[Checkins] SVN: grokapps/gbepastebin/ Initial import

Michael Haubenwallner michael at d2m.at
Sun Sep 21 12:15:11 EDT 2008


Log message for revision 91294:
  Initial import

Changed:
  A   grokapps/gbepastebin/
  A   grokapps/gbepastebin/README.txt
  A   grokapps/gbepastebin/bootstrap.py
  A   grokapps/gbepastebin/buildout.cfg
  A   grokapps/gbepastebin/setup.py
  A   grokapps/gbepastebin/src/
  A   grokapps/gbepastebin/src/gbepastebin/
  A   grokapps/gbepastebin/src/gbepastebin/__init__.py
  A   grokapps/gbepastebin/src/gbepastebin/app.py
  A   grokapps/gbepastebin/src/gbepastebin/app.txt
  A   grokapps/gbepastebin/src/gbepastebin/browser.py
  A   grokapps/gbepastebin/src/gbepastebin/browser_templates/
  A   grokapps/gbepastebin/src/gbepastebin/browser_templates/entry.pt
  A   grokapps/gbepastebin/src/gbepastebin/browser_templates/index.pt
  A   grokapps/gbepastebin/src/gbepastebin/browser_templates/manage.pt
  A   grokapps/gbepastebin/src/gbepastebin/browser_templates/master.pt
  A   grokapps/gbepastebin/src/gbepastebin/configure.zcml
  A   grokapps/gbepastebin/src/gbepastebin/error.py
  A   grokapps/gbepastebin/src/gbepastebin/error_templates/
  A   grokapps/gbepastebin/src/gbepastebin/error_templates/notfound.pt
  A   grokapps/gbepastebin/src/gbepastebin/error_templates/systemerror.pt
  A   grokapps/gbepastebin/src/gbepastebin/error_templates/usererror.pt
  A   grokapps/gbepastebin/src/gbepastebin/ftesting.zcml
  A   grokapps/gbepastebin/src/gbepastebin/paste.py
  A   grokapps/gbepastebin/src/gbepastebin/policy.py
  A   grokapps/gbepastebin/src/gbepastebin/rest.py
  A   grokapps/gbepastebin/src/gbepastebin/static/
  A   grokapps/gbepastebin/src/gbepastebin/static/styles.css
  A   grokapps/gbepastebin/src/gbepastebin/store.py
  A   grokapps/gbepastebin/src/gbepastebin/tests.py

-=-
Added: grokapps/gbepastebin/README.txt
===================================================================
--- grokapps/gbepastebin/README.txt	                        (rev 0)
+++ grokapps/gbepastebin/README.txt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,76 @@
+Grok-by-Example: Pastebin
+=========================
+
+: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 Pastebin' application [source__] ported from repoze.cluegun 
+[source__], a port of ClueBin [source__].
+
+__ http://svn.zope.org/grokapps/gbepastebin/src/gbepastebin/
+__ http://repoze.org/viewcvs/repoze.cluegun/trunk/
+__ http://pypi.python.org/pypi/ClueBin
+
+
+
+Overview
+========
+
+This is a simple Grok__ Pastebin application.
+
+Allows you to add, view and delete Pastes through a web form. 
+Pastes are formatted by the Pygments__ syntax highlighter.
+
+Can also be used as a RESTful service with JSON as response format.
+See the functional test file 'app.txt' for details.
+
+__ http://grok.zope.org
+__ http://pypi.python.org/pypi/Pygments
+
+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/gbepastebin
+	
+	# change to the newly created directory
+	cd gbepastebin
+	
+	# 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/gbepastebin/bootstrap.py
===================================================================
--- grokapps/gbepastebin/bootstrap.py	                        (rev 0)
+++ grokapps/gbepastebin/bootstrap.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -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/gbepastebin/buildout.cfg
===================================================================
--- grokapps/gbepastebin/buildout.cfg	                        (rev 0)
+++ grokapps/gbepastebin/buildout.cfg	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,77 @@
+[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
+
+[versions]
+Pygments = 0.11.1
+
+[app]
+recipe = zc.zope3recipes>=0.5.3:application
+eggs = gbepastebin
+site.zcml = <include package="gbepastebin" />
+            <include package="zope.app.twisted" />
+
+            <configure i18n_domain="gbepastebin">
+              <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 = gbepastebin
+defaults = ['--tests-pattern', '^f?tests$', '-v']
+
+# this section named so that the i18n scripts are called bin/i18n...
+[i18n]
+recipe = lovely.recipe:i18n
+package = gbepastebin
+domain = gbepastebin
+location = src/gbepastebin
+output = locales
+
+[eggbasket]
+recipe = z3c.recipe.eggbasket
+eggs = grok
+url = http://grok.zope.org/releaseinfo/grok-eggs-0.13.tgz

Added: grokapps/gbepastebin/setup.py
===================================================================
--- grokapps/gbepastebin/setup.py	                        (rev 0)
+++ grokapps/gbepastebin/setup.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,37 @@
+from setuptools import setup, find_packages
+
+version = '0.1'
+
+setup(name='gbepastebin',
+      version=version,
+      description="Grok-by-Example: PasteBin",
+      long_description="""\
+      Simple PasteBin implementation
+      porting both ClueBin and ClueGun
+""",
+      # Get strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      classifiers=['Development Status :: 4 - Beta',
+                   'Framework :: Zope3',
+                   'License :: OSI Approved :: Zope Public License',
+                   'Programming Language :: Python',
+                   'Programming Language :: Zope',
+                   ], 
+      keywords="Grok Example",
+      author="Michael Haubenwallner",
+      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',
+                        # Add extra requirements here
+                        'Pygments',
+                        ],
+      entry_points="""
+      # Add entry points here
+      """,
+      )

Added: grokapps/gbepastebin/src/gbepastebin/__init__.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/__init__.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/__init__.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1 @@
+# this directory is a package

Added: grokapps/gbepastebin/src/gbepastebin/app.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/app.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/app.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,55 @@
+from datetime import datetime
+
+import grok
+
+from gbepastebin.store import volatile_pastebin, pastebin
+
+class Application(grok.Application, grok.Container):
+    
+    display_limit=10
+    version=0.1
+    
+    def next_id(self):
+        keys=[0]
+        keylist=self.store.keys()
+        if keylist:
+            keys=[int(key) for key in keylist]
+        keys.sort()
+        return str(int(keys[-1])+1)
+
+    def get_paste(self, pasteid):
+        return self.store.get(pasteid)
+    
+    def add_paste(self, paste):
+        pasteid=self.next_id()
+        paste.pasteid=pasteid
+        self.store[pasteid]=paste
+        return pasteid
+    
+    def delete_paste(self, pasteid):
+        if self.get_paste(pasteid):
+            del self.store[pasteid]
+            return True
+        return False
+        
+    def list_pastes(self, max=display_limit):
+        keylist=self.store.keys()
+        keys=[int(key) for key in keylist]
+        keys.sort()
+        keys.reverse()
+        return [self.get_paste(str(key)) for key in keys[:max]]
+    
+    def list_pasteids(self):
+        return list(self.store.keys())
+        
+    def delete_pastes(self, pastelist):
+        success=True
+        for pasteid in pastelist:
+            delete = self.delete_paste(pasteid)
+            success=success and delete
+        return success
+
+ at grok.subscribe(Application, grok.IObjectAddedEvent)
+def handle(obj, event):
+    #obj.store=volatile_pastebin()
+    obj.store=pastebin()

Added: grokapps/gbepastebin/src/gbepastebin/app.txt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/app.txt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/app.txt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,145 @@
+Do a functional doctest test on the app.
+========================================
+
+:Test-Layer: functional
+
+
+Test the basic functionality
+----------------------------
+
+Let's first create an instance of gbepastebin at the top level:
+
+   >>> from gbepastebin.app import Application
+   >>> from gbepastebin.paste import Paste
+   >>> root = getRootFolder()
+   >>> root['app'] = app = Application()
+   
+Now test the base functionality: add 3 pastes.
+
+   >>> for paste in range(3):
+   ...     app.add_paste(Paste(author_name='name', paste='text', language='Python'))
+   '1'
+   '2'
+   '3'
+   
+List the Pastebin:
+
+   >>> def show_pastebin():
+   ...     return sorted([x.pasteid for x in app.list_pastes()])
+   >>> show_pastebin()
+   ['1', '2', '3']
+   
+Delete Paste #1, returns True if successful:
+
+   >>> app.delete_paste('1')
+   True
+   >>> app.delete_paste('1')
+   False
+   >>> show_pastebin()
+   ['2', '3']
+   
+Delete several Pastes, returns False if only one of the deletions was not 
+successful:
+
+   >>> app.delete_pastes(['1','3'])
+   False
+   >>> show_pastebin()
+   ['2']
+   
+
+
+Run tests in the testbrowser
+----------------------------
+
+Create a browser and visit the instance you just created:
+
+   >>> from zope.testbrowser.testing import Browser
+   >>> browser = Browser()
+   >>> browser.open('http://localhost/app')
+   
+Paste #2 should show in the 'Previous Pastes' listing:
+
+   >>> '<a href="http://localhost/app/2">' in browser.contents
+   True
+   
+Add another Paste, should be #3:
+
+   >>> browser.open('http://localhost/app/@@paste?author_name=name&paste=text&language=python')
+   >>> '<base href="http://localhost/app/3/@@index" />' in browser.contents
+   True
+   
+Delete Paste #3  (Deletions must be authenticated):
+
+   >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+   >>> browser.open('http://localhost/app/3/@@delete')
+   >>> '<a href="http://localhost/app/3">' in browser.contents
+   False
+   
+Management Screen, shows a list of existing Pastes:
+
+   >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+   >>> browser.open('http://localhost/app/@@manage')
+   >>> for paste in ['1','2','3']:
+   ...     ('href="http://localhost/app/%s"' % paste) in browser.contents
+   False
+   True
+   False
+
+Cleanup the Pastebin:
+
+   >>> app.delete_pastes(show_pastebin())
+   True
+   >>> show_pastebin()
+   []
+   
+   
+
+Test the REST views
+-------------------
+
+Add a few pastes and list the Pastebin contents:
+
+   >>> import simplejson
+   >>> for paste in range(3):
+   ...     browser.open('http://localhost/++rest++json/app/', 'author_name=name&paste=text&language=python')
+   >>> browser.open('http://localhost/++rest++json/app/')
+   >>> simplejson.loads(browser.contents)
+   [u'1', u'2', u'3']
+   
+Delete Paste #2 (Deletions must be authenticated):
+
+   >>> from grok.ftests.test_grok_functional import http_call
+   >>> response = http_call('DELETE', 'http://localhost/++rest++json/app/2')
+   Traceback (most recent call last):
+   ...
+   Unauthorized: (<grok.meta.JSONPaste ...>, '__call__', 'gbepastebin.manage')
+   >>> response = http_call('DELETE', 'http://localhost/++rest++json/app/2',Authorization='Basic mgr:mgrpw')
+   >>> simplejson.loads(response.getBody())
+   True
+   
+Delete it again:
+
+   >>> response = http_call('DELETE', 'http://localhost/++rest++json/app/2',Authorization='Basic mgr:mgrpw')
+   Traceback (most recent call last):
+   ...
+   NotFound: Object: <gbepastebin.app.Application object at ...>, name: u'2'
+    
+Delete all Pastes:
+
+   >>> response = http_call('DELETE', 'http://localhost/++rest++json/app/',Authorization='Basic mgr:mgrpw')
+   >>> simplejson.loads(response.getBody())
+   True
+   
+Show the Pastebin contents.
+
+   >>> browser.open('http://localhost/++rest++json/app/')
+   >>> simplejson.loads(browser.contents)
+   []
+   
+Test the 'languages' command:
+
+   >>> browser.open('http://localhost/++rest++json/app/languages')
+   >>> ['python', 'Python'] in simplejson.loads(browser.contents)
+   True
+   
+   
\ No newline at end of file

Added: grokapps/gbepastebin/src/gbepastebin/browser.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/browser.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/browser.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,124 @@
+import pygments
+from pygments import lexers
+from pygments import formatters
+from pygments import util
+from zope.interface import Interface
+from zope.app.security.interfaces import IUnauthenticatedPrincipal
+from zope.component import getUtility
+import z3c.flashmessage.interfaces
+import grok
+
+from gbepastebin.app import Application
+from gbepastebin.paste import Paste as PasteBase
+
+formatter = formatters.HtmlFormatter(linenos=True, cssclass="source")
+style_defs = formatter.get_style_defs()
+
+COOKIE_AUTHOR='gbepastebin.last_author'
+COOKIE_LANGUAGE='gbepastebin.last_language'
+
+class Master(grok.View):
+    grok.context(Interface)
+
+    def version(self):
+        return grok.getSite().version
+
+    def isManager(self):
+        return self.request.principal.id == 'zope.manager'
+    
+    def messages(self):
+        source = getUtility(
+            z3c.flashmessage.interfaces.IMessageSource, name='session')
+        for message in list(source.list('message')):
+            message.prepare(source)
+            yield message
+
+class Index(Master):
+    grok.context(Application)
+        
+    def preferred_author(self):
+        return self.request.cookies.get(COOKIE_AUTHOR,'')       
+    
+    def preferred_language(self):
+        return self.request.cookies.get(COOKIE_LANGUAGE,'')       
+  
+    def lexers(self):
+        all_lexers = list(lexers.get_all_lexers())
+        lexer_info = []
+        for name, aliases, filetypes, mimetypes_ in all_lexers:
+                lexer_info.append((name.lower(),{'alias':aliases[0], 'name':name}))
+        lexer_info.sort()
+        return [value for key, value in lexer_info]
+        
+
+class Paste(grok.View):
+    grok.context(Application)
+    
+    def update(self, author_name, language, paste):
+        paste=paste.strip()
+        if not(paste):
+            self.flash('Blank paste. Please fill in some text.')
+            return self.redirect(self.application_url())
+        self.request.response.setCookie(COOKIE_AUTHOR, author_name)
+        self.request.response.setCookie(COOKIE_LANGUAGE, language)
+        paste=PasteBase(author_name, paste, language)
+        self.pasteid=self.context.add_paste(paste)
+        
+    def render(self):
+        self.flash('Created Paste #%s' % self.pasteid)
+        return self.redirect(self.url(self.pasteid))
+
+class Delete(grok.View):
+    grok.context(PasteBase)
+    grok.require('gbepastebin.manage')
+    
+    def update(self):
+        site=grok.getSite()
+        pasteid=self.context.pasteid
+        success=site.delete_paste(pasteid)
+        if success:
+            self.flash('Deleted Paste #%s' % pasteid)
+        else:
+            self.flash('<b>Problem:</b> could not delete Paste #%s' % pasteid)
+            
+    def render(self):
+        return self.redirect(self.url(grok.getSite()))
+    
+class Manage(Master):
+    grok.context(Application)
+    grok.require('gbepastebin.manage')
+
+    def isPlural(self, pastelist):
+        if len(pastelist) > 1:
+            return 's'
+        return ''
+    
+    def update(self, delete=''):
+        if self.request.form.get('form.submitted') and delete:
+            success=self.context.delete_pastes(delete)
+            if success:
+                self.flash('Deleted Paste%s #%s' % (self.isPlural(delete) ,', '.join(delete)))
+            else:
+                self.flash('<b>Problem:</b> could not delete all Pastes requested')
+            
+class Entry(Master):
+    grok.context(PasteBase)
+    grok.name('index')
+        
+    def format(self):
+        context = self.context
+        language=''
+        try:
+            if context.language:
+                l = lexers.get_lexer_by_name(context.language)
+            else:
+                l = lexers.guess_lexer(context.paste)
+            language = l.name
+        except util.ClassNotFound, err:
+            # couldn't guess lexer
+            l = lexers.TextLexer()
+
+        formatted_paste = pygments.highlight(context.paste, l, formatter)
+        return {'language': language,
+                'formatted_paste': formatted_paste,
+                'style_defs': style_defs}

Added: grokapps/gbepastebin/src/gbepastebin/browser_templates/entry.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/browser_templates/entry.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/browser_templates/entry.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,29 @@
+<metal:block use-macro="context/@@master/macros/master">
+  <metal:block fill-slot="left">
+    <tal:block define="format view/format">
+        
+        <style tal:content="structure format/style_defs"></style>
+                
+        <dl class="previous_paste">
+          <dt>Paste Entry #<tal:block content="context/pasteid" /></dt>
+          <dd>Author: <tal:block content="context/author_name" /></dd>
+          <dd>Date: <tal:block content="python:context.date.strftime('%x at %X')" /></dd>
+          <dd>Format: <tal:block content="format/language" /></dd>
+          <dd><tal:block content="structure format/formatted_paste" /></dd>
+        </dl>
+
+    </tal:block>
+  </metal:block>
+
+  <metal:block fill-slot="userinfo">
+      <div class="userinfo">
+          <a href="..">Main Page</a>
+          <tal:block tal:condition="view/isManager"> /
+            <a tal:attributes="href string:@@delete">Delete</a> /
+            <a tal:attributes="href string:/@@logout.html">Log Out</a>
+          </tal:block>
+      </div>
+  </metal:block>
+
+</metal:block>
+

Added: grokapps/gbepastebin/src/gbepastebin/browser_templates/index.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/browser_templates/index.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/browser_templates/index.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,54 @@
+<metal:block use-macro="context/@@master/macros/master">
+  <metal:block fill-slot="left">
+        <form tal:attributes="action string:@@paste" method="POST">
+          <input type="hidden" name="form.submitted"/>
+          <fieldset>
+            <legend>Paste Info</legend>
+            <div class="field">
+              <label for="author_name">Name</label>
+              <input type="text" name="author_name" tal:attributes="value view/preferred_author|string:" />
+            </div>
+            <div class="field">
+              <label for="language">Language</label>
+              <select name="language">
+                <option value="">-- Auto detect --</option>
+                <option tal:repeat="lexer view/lexers"
+                        tal:attributes="value lexer/alias; selected python:lexer['alias']==view.preferred_language()" 
+						tal:content="lexer/name"
+                        />
+              </select>
+            </div>
+            <div class="field">
+              <label for="paste">Paste Text</label>
+              <textarea name="paste"></textarea>
+            </div>
+            <input type="submit" />
+          </fieldset>
+        </form>
+
+  </metal:block>
+	
+  <metal:block fill-slot="right">	  
+     <tal:block define="pastes context/list_pastes" condition="pastes">
+        <fieldset>
+          <legend>Previous Pastes</legend>
+          <ul>
+            <li tal:repeat="paste pastes">
+              <a tal:attributes="href string:${view/application_url}/${paste/pasteid}"
+			     tal:define="date python:paste.date.strftime('%x at %X')"
+                 tal:content="string:${paste/author_name} on ${date}" />
+            </li>
+          </ul>
+        </fieldset>
+      </tal:block>
+  </metal:block>
+  
+  <metal:block fill-slot="userinfo">
+  	  <div class="userinfo" tal:condition="view/isManager">
+          <a tal:attributes="href string:@@manage">Manage</a> /
+          <a tal:attributes="href string:/@@logout.html">Log Out</a>
+      <br />&nbsp;
+      </div>
+  </metal:block>
+  
+</metal:block>
\ No newline at end of file

Added: grokapps/gbepastebin/src/gbepastebin/browser_templates/manage.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/browser_templates/manage.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/browser_templates/manage.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,32 @@
+<metal:block use-macro="context/@@master/macros/master">
+  <metal:block fill-slot="left">       
+      <tal:block define="pastes context/list_pastes" condition="pastes">
+        <fieldset>
+          <legend>Delete Pastes</legend>
+          <form action="@@manage" method="POST">
+            <ul class="nobullet">
+              <li tal:repeat="paste pastes">
+                <input type="checkbox" name="delete:list" 
+                   tal:attributes="value paste/pasteid" />
+                <a tal:attributes="href string:${view/application_url}/${paste/pasteid}"
+				   tal:define="date python:paste.date.strftime('%x at %X')"
+                   tal:content="string:#${paste/pasteid} by ${paste/author_name} on ${date}" />
+              </li>
+            </ul>
+            <input type="submit" name="form.submitted" value="Delete"/>
+          </form>
+        </fieldset>
+      </tal:block>  
+  </metal:block>
+  
+  <metal:block fill-slot="userinfo">
+      <div class="userinfo">
+          <a href=".">Main Page</a>
+          <tal:block tal:condition="view/isManager"> /
+            <a tal:attributes="href string:/@@logout.html">Log Out</a>
+          </tal:block>
+      </div>
+	  <br />
+  </metal:block>
+
+</metal:block>

Added: grokapps/gbepastebin/src/gbepastebin/browser_templates/master.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/browser_templates/master.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/browser_templates/master.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,42 @@
+<metal:block define-macro="master">   
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <head>
+    <title>gbepastebin</title>
+    <link rel="stylesheet" type="text/css"
+          tal:attributes="href static/styles.css" />
+  </head>
+  <body>
+    <div id="main">
+
+      <div class="header">Grok-By-Example Pastebin v<tal:block replace="view/version" /> by <a
+         href="http://d2m.at" >d2m</a>
+         <br />
+         <small>based on ClueGun by <a
+         href="http://repoze.org" >Agendaless Consulting</a> based on ClueBin by <a
+         href="http://www.serverzen.com">ServerZen Software</a></small>
+      </div>
+      <div tal:condition="view/messages|nothing">
+        <div class="message" tal:repeat="message view/messages"
+		     tal:content="structure message/message">Error message</div>
+		<br />
+	  </div>
+
+      <div class="userinfo">
+<metal:block define-slot="userinfo" />   
+      </div>
+
+      <div class="left">
+<metal:block define-slot="left" />      
+      </div>
+
+      <div class="right">
+<metal:block define-slot="right" />      
+      </div>
+      
+      <div class="clear" />
+	  
+    </div>
+  </body>
+</html>
+</metal:block>
\ No newline at end of file

Added: grokapps/gbepastebin/src/gbepastebin/configure.zcml
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/configure.zcml	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/configure.zcml	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,6 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:grok="http://namespaces.zope.org/grok">
+  <include package="grok" />
+  <includeDependencies package="." />
+  <grok:grok package="." />
+</configure>

Added: grokapps/gbepastebin/src/gbepastebin/error.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/error.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/error.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,18 @@
+from zope.publisher.interfaces import INotFound
+from zope.interface.common.interfaces import IException
+from zope.exceptions.interfaces import IUserError
+
+import grok
+
+class NotFound(grok.View):
+      grok.context(INotFound)
+      grok.name('index.html')
+      
+class SystemError(grok.View):
+      grok.context(IException)
+      grok.name('index.html')
+            
+class UserError(grok.View):
+      grok.context(IUserError)
+      grok.name('index.html')
+      

Added: grokapps/gbepastebin/src/gbepastebin/error_templates/notfound.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/error_templates/notfound.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/error_templates/notfound.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,5 @@
+<html>
+  <body>
+    <b>404</b> - Page Not Found
+  </body>
+</html>

Added: grokapps/gbepastebin/src/gbepastebin/error_templates/systemerror.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/error_templates/systemerror.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/error_templates/systemerror.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,5 @@
+<html>
+  <body>
+    <b>500</b> - System Error
+  </body>
+</html>
\ No newline at end of file

Added: grokapps/gbepastebin/src/gbepastebin/error_templates/usererror.pt
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/error_templates/usererror.pt	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/error_templates/usererror.pt	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,5 @@
+<html>
+  <body>
+    <b>ERROR</b> - <tal:block content="context.__class__.__name__" />
+  </body>
+</html>

Added: grokapps/gbepastebin/src/gbepastebin/ftesting.zcml
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/ftesting.zcml	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/ftesting.zcml	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,35 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   i18n_domain="gbepastebin"
+   package="gbepastebin"
+   >
+
+  <include package="grok" />
+  <include package="gbepastebin" />
+
+  <!-- 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/gbepastebin/src/gbepastebin/paste.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/paste.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/paste.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,22 @@
+from datetime import datetime
+
+import grok
+
+class Paste(grok.Model):
+
+    def __init__(self, author_name='', paste='', language=''):
+        super(Paste, self).__init__()
+        self.author_name = author_name
+        self.paste = paste
+        self.language = language
+        self.date = datetime.now()
+        
+    def to_dict(self):
+        return {'pasteid': self.pasteid,
+                'author_name': self.author_name,
+                'language': self.language,
+                'paste': self.paste,
+                'date': str(self.date),
+                }
+
+

Added: grokapps/gbepastebin/src/gbepastebin/policy.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/policy.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/policy.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,20 @@
+import grok
+
+from gbepastebin.app import Application
+from gbepastebin.rest import Utils
+    
+class AppTraverser(grok.Traverser):
+    grok.context(Application)
+    
+    def traverse(self, name):
+        if name == 'languages':
+            if grok.IRESTLayer.providedBy(self.request):
+                return Utils()
+        paste=self.context.get_paste(name)
+        if paste:
+            return paste
+        self.request.response.setStatus(404)
+        return None
+
+class ManagePermission(grok.Permission):
+    grok.name('gbepastebin.manage')

Added: grokapps/gbepastebin/src/gbepastebin/rest.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/rest.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/rest.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,89 @@
+import sys
+import simplejson
+from pygments import lexers
+import grok
+
+from gbepastebin.app import Application
+from gbepastebin.paste import Paste as PasteBase
+
+"""
+REST Application Namespace
+==========================
+
+Request     Method                          Returns                  Role
+----------- ------- ----------------------- ------------------------ ---------            
+/           GET     list all pastes         ['pasteid', ...]         Anonymous
+/           POST    add new paste           'pasteid'                Anonymous
+/           DELETE  delete all pastes       Boolean                  Manager
+/<id>       GET     return paste (id)       Paste                    Anonymous
+/<id>       DELETE  delete paste (id)       Boolean                  Manager
+/languages  GET     list languages          [('alias', 'name'), ...] Anonymous
+
+"""
+
+class JSONLayer(grok.IRESTLayer):
+   pass
+
+class JSONProtocol(grok.RESTProtocol):
+   grok.layer(JSONLayer)
+   grok.name('json')
+
+class BaseApplication(object):
+    
+    def list_pastes(self):
+        return self.context.list_pasteids()
+    
+    def add_paste(self):
+        author_name=self.request.get('author_name')
+        paste=self.request.get('paste')
+        language=self.request.get('language')
+        paste_obj=PasteBase(author_name, paste, language)
+        return self.context.add_paste(paste_obj)
+    
+    def delete_pastes(self):
+        pastelist=self.list_pastes()
+        return self.context.delete_pastes(pastelist)
+        
+class JSONApplication(grok.REST, BaseApplication):
+    grok.context(Application)
+    grok.layer(JSONLayer)
+    
+    def GET(self):
+        return simplejson.dumps(self.list_pastes())
+    
+    def POST(self):
+        return simplejson.dumps(self.add_paste())
+
+    @grok.require('gbepastebin.manage')
+    def DELETE(self):
+        return simplejson.dumps(self.delete_pastes())
+
+class Utils(grok.Model):
+    
+    def list_languages(self):
+        all_lexers = list(lexers.get_all_lexers())
+        lexer_info = []
+        for name, aliases, filetypes, mimetypes_ in all_lexers:
+                lexer_info.append((aliases[0], name))
+        return lexer_info        
+
+class JSONLanguages(grok.REST):
+    grok.context(Utils)
+    grok.layer(JSONLayer)
+    
+    def GET(self):
+        return simplejson.dumps(self.context.list_languages())
+        
+class JSONPaste(grok.REST):
+    grok.context(PasteBase)
+    grok.layer(JSONLayer)
+
+    def GET(self):
+        return simplejson.dumps(self.context.to_dict())
+    
+    @grok.require('gbepastebin.manage')
+    def DELETE(self):
+        pasteid=self.context.pasteid
+        site=grok.getSite()
+        return simplejson.dumps(site.delete_paste(pasteid))
+    

Added: grokapps/gbepastebin/src/gbepastebin/static/styles.css
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/static/styles.css	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/static/styles.css	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,16 @@
+PRE { margin: 0; }
+.code, .linenos { font-size: 90%; }
+.source { border: 1px #999 dashed; margin: 0; padding: 1em }
+.left { width: 60%; float: left; }
+.right { margin-left: 2em; width: 35%; float: left; }
+.field { margin-bottom: 1em; }
+.field LABEL { font-weight: bold; width: 20%; display: block; float: left; }
+.field INPUT { width: 80% }
+.field TEXTAREA { width: 100%; height: 20em }
+.previous_paste DD { margin-left: 0; }
+.clear { display: block; clear: both; }
+.header { display:block; font-size: 130%; float: left; margin-bottom: 1em; margin-top: 0.5em;}
+.header small { font-size: 60%;}
+.message{ display: block; clear: both; background-color: #c3d9ff; padding: 6px; text-align: center}
+.userinfo { display: block; clear: both;}
+ul.nobullet li { list-style-type: none;}

Added: grokapps/gbepastebin/src/gbepastebin/store.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/store.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/store.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,8 @@
+import grok
+
+def volatile_pastebin():
+    return dict()
+
+def pastebin():
+    return grok.Container()
+    
\ No newline at end of file

Added: grokapps/gbepastebin/src/gbepastebin/tests.py
===================================================================
--- grokapps/gbepastebin/src/gbepastebin/tests.py	                        (rev 0)
+++ grokapps/gbepastebin/src/gbepastebin/tests.py	2008-09-21 16:15:08 UTC (rev 91294)
@@ -0,0 +1,12 @@
+import os.path
+import z3c.testsetup
+import gbepastebin
+from zope.app.testing.functional import ZCMLLayer
+
+
+ftesting_zcml = os.path.join(
+    os.path.dirname(gbepastebin.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
+                            allow_teardown=True)
+
+test_suite = z3c.testsetup.register_all_tests('gbepastebin')



More information about the Checkins mailing list