[Checkins] SVN: grokapps/gbewiki/ Initial import
Michael Haubenwallner
michael at d2m.at
Sun Aug 24 14:40:07 EDT 2008
Log message for revision 90178:
Initial import
Changed:
A grokapps/gbewiki/
A grokapps/gbewiki/README.txt
A grokapps/gbewiki/bootstrap.py
A grokapps/gbewiki/buildout.cfg
A grokapps/gbewiki/setup.py
A grokapps/gbewiki/src/
A grokapps/gbewiki/src/gbewiki/
A grokapps/gbewiki/src/gbewiki/__init__.py
A grokapps/gbewiki/src/gbewiki/app.py
A grokapps/gbewiki/src/gbewiki/app.txt
A grokapps/gbewiki/src/gbewiki/configure.zcml
A grokapps/gbewiki/src/gbewiki/ftesting.zcml
A grokapps/gbewiki/src/gbewiki/interface.py
A grokapps/gbewiki/src/gbewiki/interface_templates/
A grokapps/gbewiki/src/gbewiki/interface_templates/login.pt
A grokapps/gbewiki/src/gbewiki/interface_templates/master.pt
A grokapps/gbewiki/src/gbewiki/page.py
A grokapps/gbewiki/src/gbewiki/page_templates/
A grokapps/gbewiki/src/gbewiki/page_templates/edit.pt
A grokapps/gbewiki/src/gbewiki/page_templates/index.pt
A grokapps/gbewiki/src/gbewiki/static/
A grokapps/gbewiki/src/gbewiki/static/README.txt
A grokapps/gbewiki/src/gbewiki/static/button-background.gif
A grokapps/gbewiki/src/gbewiki/static/external.png
A grokapps/gbewiki/src/gbewiki/static/logo.png
A grokapps/gbewiki/src/gbewiki/static/style.css
A grokapps/gbewiki/src/gbewiki/tests.py
A grokapps/gbewiki/src/gbewiki/utils.py
-=-
Added: grokapps/gbewiki/README.txt
===================================================================
--- grokapps/gbewiki/README.txt (rev 0)
+++ grokapps/gbewiki/README.txt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,70 @@
+Grok-by-Example: Wiki
+=====================
+
+:Author: d2m (michael at d2m.at)
+:Motivation: look at the original source and the Grok code side-by-side
+ and deduce from both
+
+A basic 'Grok Wiki' application [source__] ported from a
+Google Appengine [GAE] example application [source__].
+
+__ http://svn.zope.org/grokapps/gbewiki/src/gbewiki/
+__ http://code.google.com/p/google-app-engine-samples/source/browse/trunk/cccwiki/wiki.py
+
+
+Overview
+========
+
+A simple Grok wiki application.
+
+Editing is in a WYSIWYG editor (TinyMCE) rather than a text editor with special
+syntax. Users need to create an account and authenticate to edit pages.
+WikiName linking and auto-linking to plain text URLs is supported.
+
+
+Usage
+-----
+
+This example is a complete Grok app on its own. Here is how to use it::
+
+ # checkout the example to your harddisk
+ svn co svn://svn.zope.org/repos/main/grokapps/gbewiki
+
+ # change to the newly created directory
+ cd gbewiki
+
+ # make it a virtualenv
+ virtualenv --no-site-packages .
+
+ # activate the virtualenv
+ source bin/activate
+
+ # bootstrap the buildout environment
+ bin/python bootstrap.py
+
+ # run the buildout
+ bin/buildout
+
+ # test the example app
+ bin/test
+
+ # run the example app
+ bin/zopectl fg
+
+ # point your browser to
+ http://localhost:8080
+
+ # login
+ username: grok
+ password: grok
+
+ # create an instance of the registered grok app
+ # and use it
+
+That's it!
+
+Need help? There is the Grok Users mailinglist at grok-dev at zope.org
+(http://mail.zope.org/mailman/listinfo/grok-dev),
+the Grok IRC channel at irc.freenode.net/#grok
+and the Grok website at http://grok.zope.org
+
Added: grokapps/gbewiki/bootstrap.py
===================================================================
--- grokapps/gbewiki/bootstrap.py (rev 0)
+++ grokapps/gbewiki/bootstrap.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 85041 2008-03-31 15:57:30Z andreasjung $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+try:
+ import pkg_resources
+except ImportError:
+ ez = {}
+ exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+ import pkg_resources
+
+if sys.platform == 'win32':
+ def quote(c):
+ if ' ' in c:
+ return '"%s"' % c # work around spawn lamosity on windows
+ else:
+ return c
+else:
+ def quote (c):
+ return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, quote (sys.executable),
+ '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
+
+# grokproject specific addition to standard bootstrap.py:
+# Install eggbasket too.
+zc.buildout.buildout.main(sys.argv[1:] + ['install', 'eggbasket'])
Added: grokapps/gbewiki/buildout.cfg
===================================================================
--- grokapps/gbewiki/buildout.cfg (rev 0)
+++ grokapps/gbewiki/buildout.cfg 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,74 @@
+[buildout]
+develop = .
+parts = eggbasket app data zopectl i18n test
+newest = false
+extends = http://grok.zope.org/releaseinfo/grok-0.13.cfg
+# eggs will be installed in the default buildout location
+# (see .buildout/default.cfg in your home directory)
+# unless you specify an eggs-directory option here.
+
+versions = versions
+
+[app]
+recipe = zc.zope3recipes>=0.5.3:application
+eggs = gbewiki
+site.zcml = <include package="gbewiki" />
+ <include package="zope.app.twisted" />
+
+ <configure i18n_domain="gbewiki">
+ <unauthenticatedPrincipal id="zope.anybody"
+ title="Unauthenticated User" />
+ <unauthenticatedGroup id="zope.Anybody"
+ title="Unauthenticated Users" />
+ <authenticatedGroup id="zope.Authenticated"
+ title="Authenticated Users" />
+ <everybodyGroup id="zope.Everybody"
+ title="All Users" />
+ <principal id="zope.manager"
+ title="Manager"
+ login="grok"
+ password_manager="Plain Text"
+ password="grok"
+ />
+
+ <!-- Replace the following directive if you do not want
+ public access -->
+ <grant permission="zope.View"
+ principal="zope.Anybody" />
+ <grant permission="zope.app.dublincore.view"
+ principal="zope.Anybody" />
+
+ <role id="zope.Manager" title="Site Manager" />
+ <role id="zope.Member" title="Site Member" />
+ <grantAll role="zope.Manager" />
+ <grant role="zope.Manager"
+ principal="zope.manager" />
+ </configure>
+
+[data]
+recipe = zc.recipe.filestorage
+
+# this section named so that the start/stop script is called bin/zopectl
+[zopectl]
+recipe = zc.zope3recipes:instance
+application = app
+zope.conf = ${data:zconfig}
+ devmode on
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = gbewiki
+defaults = ['--tests-pattern', '^f?tests$', '-v']
+
+# this section named so that the i18n scripts are called bin/i18n...
+[i18n]
+recipe = lovely.recipe:i18n
+package = gbewiki
+domain = gbewiki
+location = src/gbewiki
+output = locales
+
+[eggbasket]
+recipe = z3c.recipe.eggbasket
+eggs = grok
+url = http://grok.zope.org/releaseinfo/grok-eggs-0.13.tgz
Added: grokapps/gbewiki/setup.py
===================================================================
--- grokapps/gbewiki/setup.py (rev 0)
+++ grokapps/gbewiki/setup.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,31 @@
+from setuptools import setup, find_packages
+
+version = '0.1'
+
+setup(name='gbewiki',
+ version=version,
+ description="Grok-by-Example: Wiki",
+ long_description="""\
+ ported from
+http://code.google.com/p/google-app-engine-samples/source/browse/trunk/cccwiki/
+""",
+ # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+ classifiers=[],
+ keywords="grok example",
+ author="d2m",
+ author_email="michael at d2m.at",
+ url="http://blog.d2m.at",
+ license="ZPL2",
+ package_dir={'': 'src'},
+ packages=find_packages('src'),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=['setuptools',
+ 'grok',
+ 'z3c.testsetup',
+ 'megrok.tinymce',
+ ],
+ entry_points="""
+ # Add entry points here
+ """,
+ )
Added: grokapps/gbewiki/src/gbewiki/__init__.py
===================================================================
--- grokapps/gbewiki/src/gbewiki/__init__.py (rev 0)
+++ grokapps/gbewiki/src/gbewiki/__init__.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1 @@
+# this directory is a package
Added: grokapps/gbewiki/src/gbewiki/app.py
===================================================================
--- grokapps/gbewiki/src/gbewiki/app.py (rev 0)
+++ grokapps/gbewiki/src/gbewiki/app.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,58 @@
+import grok
+from zope.app.authentication import PluggableAuthentication
+from zope.app.authentication.principalfolder import PrincipalFolder
+from zope.app.authentication.session import SessionCredentialsPlugin
+from zope.app.security.interfaces import IAuthentication
+
+from gbewiki.page import Page, default_page_name
+from gbewiki.utils import WikiWords, AutoLink, ListOfPages, ITransform
+
+def setup_pau_principal(pau):
+ """Callback to setup the Pluggable Authentication Utility """
+ pau['principals'] = PrincipalFolder()
+ pau.authenticatorPlugins = ('principals',)
+ pau['session'] = session = SessionCredentialsPlugin()
+ session.loginpagename = 'login'
+ pau.credentialsPlugins = ('No Challenge if Authenticated', 'session',)
+
+class PermissionEditPage(grok.Permission):
+ """Permission to edit a Page """
+ grok.name('wiki.EditPage')
+
+class PermissionAddPage(grok.Permission):
+ """Permission to add a Page """
+ grok.name('wiki.AddPage')
+
+class WikiPage(grok.Application, grok.Container):
+ grok.local_utility(PluggableAuthentication, IAuthentication,
+ setup=setup_pau_principal)
+
+ def traverse(self, page_name=default_page_name):
+ """default page name is MainPage
+ create a new page object if page_name does not exist already """
+ page=self.get(page_name)
+ if page is None:
+ page=Page(page_name)
+ return page
+
+class WikiPageIndex(grok.View):
+ """default application view """
+ grok.name('index')
+
+ def render(self):
+ self.redirect('%s/%s' % (self.application_url(),default_page_name))
+ return
+
+class Add(grok.View):
+ grok.require('wiki.AddPage')
+
+ def render(self):
+ name=self.request['name']
+ page=Page(name)
+ page.content=self.request['content']
+ page.editor=self.request.principal.title
+ self.context[name]=page
+ grok.notify(grok.ObjectCreatedEvent(page))
+ self.flash('Page was added')
+ self.redirect(self.url(page))
+
Added: grokapps/gbewiki/src/gbewiki/app.txt
===================================================================
--- grokapps/gbewiki/src/gbewiki/app.txt (rev 0)
+++ grokapps/gbewiki/src/gbewiki/app.txt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,31 @@
+Do a functional doctest test on the app.
+========================================
+
+:Test-Layer: functional
+
+Let's first create an instance of gbewiki at the top level:
+
+ >>> from gbewiki.app import WikiPage
+ >>> root = getRootFolder()
+ >>> root['app'] = WikiPage()
+
+Run tests in the testbrowser
+----------------------------
+
+The zope.testbrowser.browser module exposes a Browser class that
+simulates a web browser similar to Mozilla Firefox or IE. We use that
+to test how our application behaves in a browser. For more
+information, see http://pypi.python.org/pypi/zope.testbrowser.
+
+Create a browser and visit the instance you just created:
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.open('http://localhost/app/MainPage')
+
+Check some basic information about the page you visit:
+
+ >>> browser.url
+ 'http://localhost/app/MainPage'
+ >>> browser.headers.get('Status').upper()
+ '200 OK'
Added: grokapps/gbewiki/src/gbewiki/configure.zcml
===================================================================
--- grokapps/gbewiki/src/gbewiki/configure.zcml (rev 0)
+++ grokapps/gbewiki/src/gbewiki/configure.zcml 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,7 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:grok="http://namespaces.zope.org/grok">
+ <include package="grok" />
+ <includeDependencies package="." />
+ <grok:grok package="." />
+</configure>
Added: grokapps/gbewiki/src/gbewiki/ftesting.zcml
===================================================================
--- grokapps/gbewiki/src/gbewiki/ftesting.zcml (rev 0)
+++ grokapps/gbewiki/src/gbewiki/ftesting.zcml 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,37 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:grok="http://namespaces.zope.org/grok"
+ i18n_domain="gbewiki"
+ package="gbewiki"
+ >
+
+ <include package="grok" />
+ <include package="gbewiki" />
+ <grok:grok package="gbewiki" />
+
+ <!-- Typical functional testing security setup -->
+ <securityPolicy
+ component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+ />
+
+ <unauthenticatedPrincipal
+ id="zope.anybody"
+ title="Unauthenticated User"
+ />
+ <grant
+ permission="zope.View"
+ principal="zope.anybody"
+ />
+
+ <principal
+ id="zope.mgr"
+ title="Manager"
+ login="mgr"
+ password="mgrpw"
+ />
+
+ <role id="zope.Manager" title="Site Manager" />
+ <grantAll role="zope.Manager" />
+ <grant role="zope.Manager" principal="zope.mgr" />
+
+</configure>
Added: grokapps/gbewiki/src/gbewiki/interface.py
===================================================================
--- grokapps/gbewiki/src/gbewiki/interface.py (rev 0)
+++ grokapps/gbewiki/src/gbewiki/interface.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,124 @@
+import re
+
+import grok
+from zope.interface import Interface
+from zope.component import getUtility
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.app.authentication.principalfolder import InternalPrincipal
+from zope.app.security.interfaces import IAuthentication
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
+from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager
+import z3c.flashmessage.interfaces
+
+grok.context(Interface)
+
+class Master(grok.View):
+ """The master page template macro """
+
+ def logged_in(self):
+ return not IUnauthenticatedPrincipal.providedBy(self.request.principal)
+
+ def user(self):
+ if not self.logged_in():
+ return None
+ else:
+ return self.request.principal.title
+
+ def isManager(self):
+ return self.request.principal.id == 'zope.manager'
+
+ def exists(self,name=''):
+ if not(name):
+ if self.context.__class__.__name__ == 'Page':
+ parent=self.context.__parent__
+ name=self.context.__name__
+ else:
+ parent=self.context
+ else:
+ parent=self.context.__parent__
+ return name in parent.keys()
+
+ def isWikiName(self, name=''):
+ regexp = re.compile(r'[A-Z][a-z]+([A-Z][a-z]+)+')
+ return list(regexp.finditer(name))
+
+ def dc(self):
+ return IZopeDublinCore(self.context)
+
+ def editor(self):
+ if self.context.__class__.__name__ == 'Page':
+ if self.exists():
+ return self.context.editor
+ return None
+
+ def messages(self):
+ source = getUtility(
+ z3c.flashmessage.interfaces.IMessageSource, name='session')
+ for message in list(source.list('message')):
+ message.prepare(source)
+ yield message
+
+ def js_tiny(self):
+ out="""
+tinyMCE.init({
+mode: "textareas",
+theme: "advanced",
+content_css: "%s",
+auto_focus: "mce_editor_0"
+});
+""" % self.static['style.css']()
+ return out
+
+ def js_newpage(self):
+ out="""
+function NewPage() {
+ var pageName = window.prompt("Enter the WikiName of your new page:");
+ if (pageName) {
+ location.href = '%s/' + pageName;
+ }
+}
+""" % self.application_url()
+ return out
+
+class Login(Master):
+ """Login form and handler."""
+ def update(self, login_submit=None):
+ if login_submit is not None:
+ if self.logged_in():
+ dest = self.request.get('camefrom', self.application_url())
+ self.flash('Welcome back, %s. You are now logged in' % \
+ self.request.principal.id)
+ self.redirect(dest)
+ else:
+ # create a new principal from the request data
+ if not (self.request.get('login') in self.members()):
+ login=self.request.get('login')
+ password=self.request.get('password')
+ pau = getUtility(IAuthentication)
+ principals = pau['principals']
+ principal = InternalPrincipal(login, password, login,
+ passwordManagerName='SHA1')
+ principals[login] = principal
+ permission_mngr = IPrincipalPermissionManager(grok.getSite())
+ permission_mngr.grantPermissionToPrincipal(
+ 'wiki.AddPage', principals.prefix + login)
+ permission_mngr.grantPermissionToPrincipal(
+ 'wiki.EditPage', principals.prefix + login)
+ dest = self.request.get('camefrom', self.application_url())
+ self.flash('Your account has been created. You are now \
+ logged in')
+ self.redirect(dest)
+
+ def members(self):
+ pau = getUtility(IAuthentication)
+ principals = pau['principals']
+ return list(sorted(principals.keys()))
+
+class Logout(grok.View):
+ """Logout handler."""
+ def render(self):
+ session = getUtility(IAuthentication)['session']
+ session.logout(self.request)
+ self.flash('You are now logged out')
+ self.redirect(self.application_url())
+
\ No newline at end of file
Added: grokapps/gbewiki/src/gbewiki/interface_templates/login.pt
===================================================================
--- grokapps/gbewiki/src/gbewiki/interface_templates/login.pt (rev 0)
+++ grokapps/gbewiki/src/gbewiki/interface_templates/login.pt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,46 @@
+<html metal:use-macro="context/@@master/macros/page">
+ <body>
+ <div metal:fill-slot="body">
+ <h1>Login</h1>
+
+ <form metal:define-macro="loginform" action="@@login" method="post">
+ <input type="hidden" name="camefrom"
+ tal:condition="exists:request/camefrom"
+ tal:attributes="value request/camefrom" />
+
+ <table>
+ <tr>
+ <th>Login *</th>
+ <td>
+ <input type="text" name="login" id="login"
+ tal:attributes="value request/login|nothing"/>
+ </td>
+ </tr>
+ <tr>
+ <th>Password</th>
+ <td>
+ <input type="password" name="password" id="password" />
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td>
+ <input type="submit" name="login_submit" value="Log in" />
+ </td>
+ </tr>
+ </table>
+
+ <div>
+ <br>
+ * if you are not a registered editor already, a new account will be
+ created for you with the login/password you entered
+ <br>
+ </div>
+ <div id="toc" tal:condition="view/members">
+ <b>Editors so far:</b>
+ <span tal:content="structure python:', '.join(view.members())" />
+ </div>
+ </form>
+ </div>
+ </body>
+</html>
\ No newline at end of file
Added: grokapps/gbewiki/src/gbewiki/interface_templates/master.pt
===================================================================
--- grokapps/gbewiki/src/gbewiki/interface_templates/master.pt (rev 0)
+++ grokapps/gbewiki/src/gbewiki/interface_templates/master.pt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,58 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ metal:define-macro="page">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+ <title metal:define-slot="title" tal:content="python:context.__parent__.__name__"></title>
+ <link tal:attributes="href static/style.css" rel="stylesheet" type="text/css"/>
+ <script type="text/javascript" tal:content="view/js_newpage"></script>
+ <metal:block define-slot="head" />
+ </head>
+ <body>
+ <div id="header">
+ <div class="top">
+ <div class="login">
+ <span class="item nickname" tal:content="view/user|nothing"/>
+ <tal:block tal:condition="not:view/isManager">
+ <tal:block tal:condition="view/user">
+ |
+ <span class="item"><a tal:attributes="href python:view.url('@@logout')">Sign out</a></span>
+ </tal:block>
+ <tal:block tal:condition="not:view/user">
+ <span class="item"><a tal:attributes="href python:view.url('@@login')">Sign in</a></span>
+ </tal:block>
+ </tal:block>
+ </div>
+ <div class="title">
+ <a href="/" tal:attributes="href python:view.application_url()"><img
+ tal:attributes="src static/logo.png" alt="Google Wiki"/></a>
+ </div>
+ </div>
+ <div class="messages">
+ <tal:block tal:repeat="message view/messages">
+ <div class="message" tal:condition="repeat/message/start"
+ tal:content="structure message/message" />
+ </tal:block>
+ </div>
+ <div class="bottom">
+ <div class="attribution">
+ <tal:block condition="view/exists|nothing">
+ Edited on <span tal:replace="python:view.dc().modified.strftime('%a, %b %d, %Y \t %I:%M %p')" /> by
+ <tal:block tal:condition="view/editor" tal:replace="view/editor" />
+ </tal:block>
+ </div>
+ <div class="buttons">
+ <span class="item">
+ <input type="button"
+ value="New Page" onclick="NewPage()"/>
+ </span><metal:block define-slot="buttons" />
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ </div>
+ <div id="body"><metal:block define-slot="body" /></div>
+ <div id="footer"></div>
+ </body>
+</html>
\ No newline at end of file
Added: grokapps/gbewiki/src/gbewiki/page.py
===================================================================
--- grokapps/gbewiki/src/gbewiki/page.py (rev 0)
+++ grokapps/gbewiki/src/gbewiki/page.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,70 @@
+import grok
+from zope.component import getAdapter
+
+from gbewiki.interface import Master
+from gbewiki.utils import ITransform
+
+default_page_name = 'MainPage'
+
+class Page(grok.Model):
+ """Page object, holds the Wikipage data
+ """
+
+ def __init__(self, name=''):
+ super(Page, self).__init__()
+ self.name=name
+ self.content = '<h1>%s</h1>' % name
+ self.editor=None
+
+class Index(Master):
+
+ def wikified_content(self):
+ self.content = self.context.content
+ self.default_page_name = default_page_name
+ self.page_name=self.context.__name__
+ self.wordlist=self.list_of_pages()
+
+ transforms = [
+ 'wiki.AutoLink',
+ 'wiki.ListOfPages',
+ 'wiki.WikiWords',
+ ]
+
+ for transform in transforms:
+ self.content = getAdapter(self, ITransform, transform).run()
+ return self.content
+
+ def list_of_pages(self):
+ parent=self.context.__parent__
+ return parent.keys()
+
+ def update(self):
+ if not(self.isWikiName(self.context.__name__)):
+ self.flash('»%s« is not a valid page name. Use '
+ ' something like »WikiName« instead' \
+ % self.context.__name__)
+ self.redirect(self.application_url())
+ return
+ if not(self.exists(self.context.__name__)):
+ self.flash('Page »%s« created. You need to edit and \
+ save the page to '
+ 'store it to the database' % self.context.__name__)
+
+class Edit(Master):
+ grok.require('wiki.EditPage')
+
+ def update(self):
+ if not self.exists(self.context.__name__):
+ self.action='%s/@@add' % self.application_url()
+ else:
+ self.action='%s/%s/@@save' % (self.application_url(),self.context.name)
+
+class Save(grok.View):
+ grok.require('wiki.EditPage')
+
+ def render(self):
+ self.context.content=self.request['content']
+ self.context.editor=self.request.principal.title
+ grok.notify(grok.ObjectModifiedEvent(self.context))
+ self.flash('Page is saved')
+ self.redirect('%s/%s' % (self.application_url(),self.context.name))
Added: grokapps/gbewiki/src/gbewiki/page_templates/edit.pt
===================================================================
--- grokapps/gbewiki/src/gbewiki/page_templates/edit.pt (rev 0)
+++ grokapps/gbewiki/src/gbewiki/page_templates/edit.pt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,36 @@
+<html metal:use-macro="context/@@master/macros/page">
+ <title metal:fill-slot="title">
+ <span tal:replace="python:context.__parent__.__name__" /> -
+ <span tal:replace="python:context.__name__" />
+ </title>
+ <metal:block metal:fill-slot="head">
+
+ <script type="text/javascript"
+ tal:attributes="src context/++resource++TinyMCE/tiny_mce.js"></script>
+ <script type="text/javascript" tal:content="view/js_tiny"></script>
+ <style type="text/css">
+ #body{margin:29px;margin-top:19px}
+ #body .buttons{margin-top:0.75em}
+ #body .buttons .item{margin-right: 0.5em}
+ </style>
+ </metal:block>
+
+ <metal:block metal:fill-slot="body">
+ <form tal:attributes="action view/action" method="post">
+ <input type="hidden" name="name" tal:attributes="value context/name">
+ <div>
+ <textarea rows="10" cols="50" id="content"
+ style="width: 100%; height: 400px"
+ name="content" tal:content="context/content"/>
+ </div>
+ <div class="buttons">
+ <span class="item"><input style="font-weight: bold" type="submit"
+ value="Save Changes"/></span>
+ <span class="item"><input type="button"
+ tal:attributes="onclick python:'location.href=\'%s\'' % view.url('@@index')"
+ value="Cancel"/></span>
+ </div>
+ </form>
+ </metal:block>
+
+</html>
\ No newline at end of file
Added: grokapps/gbewiki/src/gbewiki/page_templates/index.pt
===================================================================
--- grokapps/gbewiki/src/gbewiki/page_templates/index.pt (rev 0)
+++ grokapps/gbewiki/src/gbewiki/page_templates/index.pt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,23 @@
+<html metal:use-macro="context/@@master/macros/page">
+ <head>
+ <title metal:fill-slot="title">
+ <span tal:replace="python:context.__parent__.__name__" /> -
+ <span tal:replace="python:context.__name__" />
+ </title>
+ </head>
+ <body>
+
+ <metal:block metal:fill-slot="buttons">
+ <span class="item">
+ <input type="button"
+ tal:attributes="onclick python:'location.href=\'%s\'' % view.url('@@edit')"
+ value="Edit This Page"/>
+ </span>
+ </metal:block>
+
+ <metal:block metal:fill-slot="body">
+ <tal:block tal:content="structure view/wikified_content" />
+ </metal:block>
+
+ </body>
+</html>
\ No newline at end of file
Added: grokapps/gbewiki/src/gbewiki/static/README.txt
===================================================================
--- grokapps/gbewiki/src/gbewiki/static/README.txt (rev 0)
+++ grokapps/gbewiki/src/gbewiki/static/README.txt 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,2 @@
+Put static files here, like javascript and css. They will be
+available as static/<filename> in views.
Added: grokapps/gbewiki/src/gbewiki/static/button-background.gif
===================================================================
(Binary files differ)
Property changes on: grokapps/gbewiki/src/gbewiki/static/button-background.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: grokapps/gbewiki/src/gbewiki/static/external.png
===================================================================
(Binary files differ)
Property changes on: grokapps/gbewiki/src/gbewiki/static/external.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: grokapps/gbewiki/src/gbewiki/static/logo.png
===================================================================
(Binary files differ)
Property changes on: grokapps/gbewiki/src/gbewiki/static/logo.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: grokapps/gbewiki/src/gbewiki/static/style.css
===================================================================
--- grokapps/gbewiki/src/gbewiki/static/style.css (rev 0)
+++ grokapps/gbewiki/src/gbewiki/static/style.css 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,24 @@
+#body{clear:both;margin:20px 30px 30px;position:relative}
+#body a[href ^="http"]{background:url(external.png) no-repeat center right;color:#36b;padding-right:13px}
+#body h1 a.wikiword,#body h1 a.wikiword:visited{color:#000;text-decoration:none}
+#header .bottom{background:#FFD700;clear:both;padding:3px 7px 3px 5px}
+#header .bottom .attribution{float:right;padding-right:4px;padding-top:3px}
+#header .bottom .buttons .item{margin-right:4px}
+#header .top{height:33px;position:relative}
+#header .top .login{position:absolute;right:7px;top:7px}
+#header .top .login .nickname{font-weight:700}
+#header .top .title{left:6px;position:absolute;top:5px}
+#header .top .title img{border:0}
+#toc{border-top:1px solid #aaa;color:#000;font-weight:400;margin-top:4em;padding-bottom:.5em;padding-top:.17em}
+.message{background:#c3d9ff;font-weight:700;padding:6px;text-align:center}
+a.button,input[type^="submit"],input[type^="button"]{background:url("button-background.gif") repeat-x #ddd;border:1px solid #aaa;color:#222;cursor:default;font-family:Arial, Sans-serif;font-size:12px;margin:0;padding:2px 5px;text-decoration:none}
+a.button:hover,input[type^="submit"]:hover,input[type^="button"]:hover{border-color:#9cf #69e #69e #7af}
+body{font-family:Arial;font-size:10pt;margin:0}
+h1{font-size:188%}
+h1,h2,h3,h4,h5,h6{border-bottom:1px solid #aaa;color:#000;font-weight:400;margin:0;padding-bottom:.17em;padding-top:.5em}
+h2{font-size:150%}
+h3{font-size:132%}
+h3,h4,h5,h6{border-bottom:none;font-weight:700}
+li{margin-bottom:.25em}
+pre{background:#f5f5f5;border:1px solid silver;margin:2em;overflow:auto;padding:.5em}
+pre,code{color:#007000;font-family:monospace;font-size:10pt}
\ No newline at end of file
Added: grokapps/gbewiki/src/gbewiki/tests.py
===================================================================
--- grokapps/gbewiki/src/gbewiki/tests.py (rev 0)
+++ grokapps/gbewiki/src/gbewiki/tests.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,12 @@
+import os.path
+import z3c.testsetup
+import gbewiki
+from zope.app.testing.functional import ZCMLLayer
+
+
+ftesting_zcml = os.path.join(
+ os.path.dirname(gbewiki.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
+ allow_teardown=True)
+
+test_suite = z3c.testsetup.register_all_tests('gbewiki')
Added: grokapps/gbewiki/src/gbewiki/utils.py
===================================================================
--- grokapps/gbewiki/src/gbewiki/utils.py (rev 0)
+++ grokapps/gbewiki/src/gbewiki/utils.py 2008-08-24 18:40:06 UTC (rev 90178)
@@ -0,0 +1,59 @@
+import re
+import urlparse
+
+import grok
+from zope.interface import Interface
+
+class ITransform(Interface):
+ pass
+
+class WikiWords(grok.Adapter):
+ """Translates WikiWords to links.
+ We look up all words, and we only link those words that currently exist.
+ """
+ grok.implements(ITransform)
+ grok.name('wiki.WikiWords')
+ grok.context(grok.View)
+
+ def run(self):
+ regexp = re.compile(r'[A-Z][a-z]+([A-Z][a-z]+)+')
+ return regexp.sub(self.replace, self.context.content)
+
+ def replace(self, match):
+ wikiword = match.group(0)
+ if wikiword in self.context.wordlist:
+ return '<a class="wikiword" href="%s/%s">%s</a>' % \
+ (urlparse.urlparse(self.context.application_url())[2],wikiword, wikiword)
+ else:
+ return wikiword
+
+
+class AutoLink(grok.Adapter):
+ """A transform that auto-links URLs."""
+ grok.implements(ITransform)
+ grok.name('wiki.AutoLink')
+ grok.context(grok.View)
+
+ def run(self):
+ regexp = re.compile(r'([^"])\b((http|https)://[^ \t\n\r<>\(\)&"]+' \
+ r'[^ \t\n\r<>\(\)&"\.])')
+ return regexp.sub(self.replace, self.context.content)
+
+ def replace(self, match):
+ url = match.group(2)
+ return '<a class="autourl" href="%s">%s</a>' % (url, url)
+
+
+class ListOfPages(grok.Adapter):
+ """A transform that adds a TOC list to the default page"""
+ grok.implements(ITransform)
+ grok.name('wiki.ListOfPages')
+ grok.context(grok.View)
+
+ def run(self):
+ if self.context.page_name == self.context.default_page_name:
+ if self.context.wordlist:
+ return self.context.content + '<div id="toc"><b>Available Pages:\
+ </b> %s</div>' % ', '.join(self.context.wordlist)
+ return self.context.content
+
More information about the Checkins
mailing list