[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('&raquo;%s&laquo; is not a valid page name. Use '
+                       ' something like &raquo;WikiName&laquo; instead' \
+                       % self.context.__name__)
+            self.redirect(self.application_url())
+            return
+        if not(self.exists(self.context.__name__)):
+               self.flash('Page &raquo;%s&laquo; 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