[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 />
+ </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