[Checkins] SVN: Grokstar/trunk/ New viewlet-based Grokstar
Graham Stratton
gns24 at beasts.org
Tue Jun 17 18:41:29 EDT 2008
Log message for revision 87483:
New viewlet-based Grokstar
Changed:
U Grokstar/trunk/INSTALL.txt
D Grokstar/trunk/bootstrap/
A Grokstar/trunk/bootstrap.py
U Grokstar/trunk/buildout.cfg
U Grokstar/trunk/setup.py
U Grokstar/trunk/src/grokstar/__init__.py
U Grokstar/trunk/src/grokstar/blog.py
D Grokstar/trunk/src/grokstar/blog_templates/blogabout.pt
U Grokstar/trunk/src/grokstar/blog_templates/blogindex.pt
D Grokstar/trunk/src/grokstar/blog_templates/blogmacros.pt
A Grokstar/trunk/src/grokstar/blog_templates/breadcrumbs.pt
U Grokstar/trunk/src/grokstar/blog_templates/categories.pt
A Grokstar/trunk/src/grokstar/blog_templates/csshead.pt
U Grokstar/trunk/src/grokstar/blog_templates/draftsindex.pt
A Grokstar/trunk/src/grokstar/blog_templates/layout.pt
A Grokstar/trunk/src/grokstar/blog_templates/recententries.pt
U Grokstar/trunk/src/grokstar/blog_templates/search.pt
A Grokstar/trunk/src/grokstar/blog_templates/titleheader.pt
U Grokstar/trunk/src/grokstar/calendar.py
U Grokstar/trunk/src/grokstar/calendar_templates/dateindex.pt
U Grokstar/trunk/src/grokstar/configure.zcml
U Grokstar/trunk/src/grokstar/entry.py
A Grokstar/trunk/src/grokstar/entry_templates/entryindex.pt
D Grokstar/trunk/src/grokstar/entry_templates/index.pt
U Grokstar/trunk/src/grokstar/entry_templates/item.pt
U Grokstar/trunk/src/grokstar/form.py
U Grokstar/trunk/src/grokstar/form_templates/grokstaraddform.pt
U Grokstar/trunk/src/grokstar/form_templates/grokstareditform.pt
U Grokstar/trunk/src/grokstar/interfaces.py
A Grokstar/trunk/src/grokstar/mail/
A Grokstar/trunk/src/grokstar/mail/__init__.py
A Grokstar/trunk/src/grokstar/mail/configure.zcml
A Grokstar/trunk/src/grokstar/mail/notifications.txt
A Grokstar/trunk/src/grokstar/mail/tests.py
A Grokstar/trunk/src/grokstar/rss.py
A Grokstar/trunk/src/grokstar/rss_templates/
A Grokstar/trunk/src/grokstar/rss_templates/rss.pt
A Grokstar/trunk/src/grokstar/static/style.css
A Grokstar/trunk/src/grokstar/static/syntax.css
D Grokstar/trunk/src/grokstar/utils.py
U Grokstar/trunk/src/grokstar/workflow.py
-=-
Modified: Grokstar/trunk/INSTALL.txt
===================================================================
--- Grokstar/trunk/INSTALL.txt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/INSTALL.txt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,6 +1,6 @@
How to install Grokstar
-$ python2.4 bootstrap/bootstrap.py
+$ python2.4 bootstrap.py
$ bin/buildout
To run:
Added: Grokstar/trunk/bootstrap.py
===================================================================
--- Grokstar/trunk/bootstrap.py (rev 0)
+++ Grokstar/trunk/bootstrap.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# 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 69908 2006-08-31 21:53:00Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+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
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', 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)
Modified: Grokstar/trunk/buildout.cfg
===================================================================
--- Grokstar/trunk/buildout.cfg 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/buildout.cfg 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,9 +1,9 @@
[buildout]
develop = .
-parts = app data zopectl i18n test
+parts = app data zopectl test
find-links = http://download.zope.org/distribution/
newest = false
-extends= http://grok.zope.org/releaseinfo/grok-0.11.1.cfg
+extends= http://grok.zope.org/releaseinfo/grok-0.12.1.cfg
versions = versions
[data]
@@ -12,10 +12,11 @@
[app]
recipe = zc.zope3recipes>=0.5.3:application
eggs = Grokstar
-site.zcml = <include package="grokstar" />
+site.zcml = <include package="zope.sendmail" file="meta.zcml" />
+ <include package="grokstar" />
+ <include package="zope.sendmail" />
<include package="zope.app.twisted" />
- <configure i18n_domain="grokstar">
<unauthenticatedPrincipal id="zope.anybody"
title="Unauthenticated User" />
<unauthenticatedGroup id="zope.Anybody"
@@ -43,26 +44,14 @@
<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
+address = 8086
application = app
zope.conf = ${data:zconfig}
[test]
recipe = zc.recipe.testrunner
-eggs = Grokstar
+eggs = grokstar
defaults = ['--tests-pattern', '^f?tests$', '-v']
-
-# this section named so that the i18n scripts are called bin/i18n...
-[i18n]
-recipe = lovely.recipe:i18n
-package = grokstar
-domain = grokstar
-location = src/grokstar
-output = locales
Modified: Grokstar/trunk/setup.py
===================================================================
--- Grokstar/trunk/setup.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/setup.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -23,6 +23,10 @@
'zc.catalog',
'hurry.query',
'hurry.workflow',
+ 'Pygments',
+ 'zope.sendmail',
+ 'zope.app.session',
+ 'zope.app.securitypolicy',
],
entry_points="""
# -*- Entry points: -*-
Modified: Grokstar/trunk/src/grokstar/__init__.py
===================================================================
--- Grokstar/trunk/src/grokstar/__init__.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/__init__.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1 +1,35 @@
# this directory is a package
+from docutils import nodes
+from docutils.parsers.rst import directives
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter
+
+pygments_formatter = HtmlFormatter()
+
+def pygments_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ try:
+ lexer = get_lexer_by_name(arguments[0])
+ except ValueError:
+ # no lexer found - use the text one instead of an exception
+ lexer = get_lexer_by_name('text')
+ parsed = highlight(u'\n'.join(content), lexer, pygments_formatter)
+ return [nodes.raw('', parsed, format='html')]
+pygments_directive.arguments = (1, 0, 1)
+pygments_directive.content = 1
+directives.register_directive('sourcecode', pygments_directive)
+
+from grok import View, Viewlet, Application
+def application(self, name=None):
+ obj = self.context
+ while obj is not None:
+ if isinstance(obj, Application):
+ return obj
+ obj = obj.__parent__
+ raise ValueError("No application found.")
+
+
+View.application = property(application)
+Viewlet.application = property(application)
+#XXX You didn't really see me do that, did you?
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/blog.py
===================================================================
--- Grokstar/trunk/src/grokstar/blog.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -2,34 +2,77 @@
from datetime import datetime, timedelta
from itertools import islice
-from zc.catalog.catalogindex import SetIndex
from zope import schema, interface
from zope.interface import Interface
from zope.traversing.api import getParents
+from zope.component import getUtility, getMultiAdapter
from hurry.query.query import Query
from hurry import query
from hurry.query.set import AllOf
from hurry.workflow.interfaces import IWorkflowState
+
import grok
from grok import index
-from grokstar.interfaces import IRestructuredTextEntry, IBlog
-from grokstar.interfaces import PUBLISHED, CREATED
-from form import GrokstarEditForm
+from grokstar.interfaces import IEntry, IBlog, PUBLISHED, CREATED
from zope.app.catalog.interfaces import ICatalog
-from zope.component import getUtility
+grok.context(Interface)
+
+class EditArticles(grok.Permission):
+ grok.name('grokstar.Edit') # Single editing permission
+
class Blog(grok.Container, grok.Application):
interface.implements(IBlog)
+ title = 'Grokstar'
+ tagline = 'A blogging app written with Grok'
+ footer = ''
+ email = ''
def __init__(self):
super(Blog, self).__init__()
- self.title = ''
- self.tagline = ''
self['entries'] = Entries()
+
+
+
+ at grok.subscribe(Blog, grok.IObjectAddedEvent)
+def registerAsUtility(app, event):
+ app.getSiteManager().registerUtility(app, grok.interfaces.IApplication)
+
+class Index(grok.View):
+ grok.template('layout')
+
+class Edit(grok.View):
+ grok.require('grokstar.Edit')
+ grok.template('layout')
+
+class AddEntry(grok.View):
+ grok.require('grokstar.Edit')
+ grok.template('layout')
+
+
+class Head(grok.ViewletManager):
+ grok.name('head')
+
+class Main(grok.ViewletManager):
+ grok.name('main')
+
+class Right(grok.ViewletManager):
+ grok.name('right')
+
+class Top(grok.ViewletManager):
+ grok.name('top')
+
+class CssHead(grok.Viewlet):
+ grok.viewletmanager(Head)
+
+class TitleHeader(grok.Viewlet):
+ grok.viewletmanager(Top)
+
+
class EntryIndexes(grok.Indexes):
grok.site(Blog)
- grok.context(IRestructuredTextEntry)
+ grok.context(IEntry)
grok.name('entry_catalog')
title = index.Text()
@@ -49,65 +92,86 @@
class Drafts(grok.Model):
pass
-class DraftsIndex(grok.View):
+
+class Search(grok.Viewlet):
+ grok.viewletmanager(Right)
+ grok.order(-1)
+
+ def update(self):
+ if 'q' not in self.request.form:
+ return
+
+ q = self.request.form['q'].strip()
+ if not q:
+ self.results = lastEntries(10)
+ return
+
+ entries = Query().searchResults(
+ (query.Eq(('entry_catalog', 'workflow_state'), PUBLISHED) &
+ (query.Text(('entry_catalog', 'title'), q) |
+ AllOf(('entry_catalog', 'categories'), [q]) |
+ query.Text(('entry_catalog', 'content'), q))))
+ self.results = list(islice(entries, 10))
+
+
+class DraftsIndex(grok.Viewlet):
grok.context(Drafts)
- grok.name('index')
+ grok.require('grokstar.Edit')
+ grok.viewletmanager(Main)
def entries(self):
return allEntries(10)
+class Breadcrumbs(object):
+ grok.viewletmanager(Top)
+ def parents(self):
+ pl = getParents(self.context)
+ return pl
+
class Entries(grok.Container):
pass
-class BlogIndex(grok.View):
+class BlogIndex(grok.Viewlet):
grok.context(Blog)
- grok.name('index')
+ grok.viewletmanager(Main)
+ grok.view(Index)
def entries(self):
return lastEntries(10)
-class BlogMacros(grok.View):
- grok.context(Interface)
+class BlogEdit(grok.Viewlet):
+ grok.context(Blog)
+ grok.viewletmanager(Main)
+ grok.view(Edit)
+
+ def update(self):
+ self.form = getMultiAdapter((self.context, self.request),
+ name='blogeditform')
+ self.form.update_form()
-class BlogEdit(GrokstarEditForm):
+ def render(self):
+ return self.form.render()
+
+class BlogEditForm(grok.EditForm):
grok.context(Blog)
- grok.name('edit')
- title = u'Edit Blog'
-
+
@grok.action('Save changes')
def edit(self, **data):
self.applyData(self.context, **data)
self.redirect(self.url(self.context))
-class BlogAbout(grok.View):
- grok.context(Blog)
- grok.name('about')
+class EntriesIndex(grok.Viewlet):
+ grok.context(Entries)
+ grok.viewletmanager(Main)
-class Search(grok.View):
- grok.context(Blog)
+ def render(self):
+ return "Entries: %s" % ' '.join(self.context.keys())
- def update(self, q=None):
- if q is None:
- return self.redirect(self.application_url())
-
- q = q.strip()
- if not q:
- self.results = lastEntries(10)
- return
-
- entries = Query().searchResults(
- (query.Eq(('entry_catalog', 'workflow_state'), PUBLISHED) &
- (query.Text(('entry_catalog', 'title'), q) |
- AllOf(('entry_catalog', 'categories'), [q]) |
- query.Text(('entry_catalog', 'content'), q))))
- self.results = list(islice(entries, 10))
-
-class EntriesIndex(grok.View):
- grok.context(Entries)
- grok.name('index')
-
+class RecentEntries(grok.Viewlet):
+ grok.viewletmanager(Right)
+
def entries(self):
- return lastEntries(10)
+ return lastEntries(40)
def lastEntries(amount):
entries = Query().searchResults(
@@ -126,11 +190,21 @@
entries, key=lambda entry: entry.updated, reverse=True
)[:amount]
-class Categories(grok.View):
- grok.context(Blog)
- grok.name('categories')
+class Categories(grok.Viewlet):
+ grok.viewletmanager(Right)
+ c = None
def categories(self):
cat = getUtility(ICatalog, 'entry_catalog')
categories = cat['categories']
return categories.values()
+
+ def update(self):
+ if 'c' not in self.request.form:
+ return
+
+ self.c = self.request.form['c']
+
+ self.entries = Query().searchResults(
+ (query.Eq(('entry_catalog', 'workflow_state'), PUBLISHED) &
+ AllOf(('entry_catalog', 'categories'), [self.c])))
\ No newline at end of file
Deleted: Grokstar/trunk/src/grokstar/blog_templates/blogabout.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/blogabout.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog_templates/blogabout.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,11 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" metal:use-macro="context/@@blogmacros/macros/blogpage">
- <head>
- <title metal:fill-slot="title">About</title>
- </head>
- <body>
- <div metal:fill-slot="main-content">
- Me explain GrokStar!
- </div>
- </body>
-</html>
Modified: Grokstar/trunk/src/grokstar/blog_templates/blogindex.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/blogindex.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog_templates/blogindex.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,21 +1,9 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" metal:use-macro="context/@@blogmacros/macros/blogpage">
- <head>
- <title metal:fill-slot="title" tal:content="context/title"/>
- </head>
- <body>
- <div metal:fill-slot="main-content">
- <div id="blogtitle"
- tal:define="title context/title;
- tagline context/tagline">
- <h1 tal:content="python:title and title or default">Untitled</h1>
- <h2 tal:content="python:tagline and tagline or default">No tagline</h2>
- <a href="edit" tal:attributes="href python:view.url('edit')">Edit</a>
- </div>
- <ol>
- <li tal:repeat="entry view/entries"
- tal:content="structure entry/@@item" />
- </ol>
- </div>
- </body>
-</html>
+<div tal:condition="not: view/entries">
+ No published entries. <a tal:attributes="href string:${view/application/@@absolute_url}/edit">Set up Grokstar</a>, then <a tal:attributes="href string:${view/application/@@absolute_url}/addentry">add an entry</a>
+</div>
+<div class="entries">
+ <tal:block repeat="entry view/entries">
+ <tal:block content="structure entry/@@item"/>
+ <hr/>
+ </tal:block>
+</div>
Deleted: Grokstar/trunk/src/grokstar/blog_templates/blogmacros.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/blogmacros.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog_templates/blogmacros.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,50 +0,0 @@
-<html metal:define-macro="blogpage">
- <head>
- <title metal:define-slot="title" tal:content="python:view.__name__" />
- <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.4.1/build/reset-fonts-grids/reset-fonts-grids.css" />
- <link rel="stylesheet" type="text/css" href="static/grokstar.css"
- tal:attributes="href static/grokstar.css" />
- <div metal:define-slot="headers" />
- </head>
- <body id="doc">
- <div id="hd">
- <div id="logo">
- <div id="appname">GrokStar!</div>
- Me need logo!
- </div>
- <div id="menu">
- <ul>
- <li><a href="#" tal:attributes="href view/application_url">Home</a></li>
- <li><a href="#" tal:attributes="href python:view.application_url('add')">Post</a></li>
- <li><a href="#" tal:attributes="href python:view.application_url('drafts')">Drafts</a></li>
- <li><a href="#" tal:attributes="href python:view.application_url('entries')">Published</a></li>
- <li><a href="#" tal:attributes="href python:view.application_url('categories')">Categories</a></li>
- <li><a href="#" tal:attributes="href python:view.application_url('about')">About</a></li>
- </ul>
- </div>
- <div id="search">
- <form action="/search" method="get"
- tal:attributes="action python:view.application_url('search')">
- <input type="text" name="q" value="" size="15"
- tal:attributes="value request/q | default" />
- </form>
- </div>
- </div>
- <div id="bd">
- <div metal:define-slot="main-content" />
- </div>
- <div id="ft">
- <span id="num-posts"
- tal:define="utils nocall:context/@@utils">
- Me grok <tal:posts replace="utils/numPosts" /> posts!
- </span>
- <div id="external-links">
- <ul>
- <li><a href="http://grok.zope.org">GROK</a></li>
- <li><a href="http://www.zope.org">Zope</a></li>
- <li><a href="http://www.python.org">Python</a></li>
- </ul>
- </div>
- </div>
- </body>
-</html>
Added: Grokstar/trunk/src/grokstar/blog_templates/breadcrumbs.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/breadcrumbs.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/blog_templates/breadcrumbs.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,3 @@
+<span tal:repeat="obj view/parents" tal:omit-tag="">
+ <a href="" tal:attributes="href python: view.url(obj)" tal:content="obj/__name__" />
+</span>
Modified: Grokstar/trunk/src/grokstar/blog_templates/categories.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/categories.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog_templates/categories.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,16 +1,11 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" metal:use-macro="context/@@blogmacros/macros/blogpage">
- <head>
- <title metal:fill-slot="title">Categories</title>
- </head>
- <body>
- <div metal:fill-slot="main-content">
- <ul>
- <li tal:repeat="cat view/categories">
- <a tal:attributes="href string:${view/application_url}/search?q=${cat}"
- tal:content="cat" />
- </li>
- </ul>
- </div>
- </body>
-</html>
+<h4 tal:condition="view/categories">Categories</h4>
+<ul>
+ <li tal:repeat="cat view/categories">
+ <a tal:attributes="href string:${context/@@absolute_url}/?c=${cat}"
+ tal:content="cat" />
+ <ul tal:condition="python:cat==view.c">
+ <li tal:repeat="entry view/entries"><a tal:attributes="href string:${entry/@@absolute_url}?c=${view/c}"
+ tal:content="structure entry/title">An entry</a></li>
+ </ul>
+ </li>
+</ul>
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/blog_templates/csshead.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/csshead.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/blog_templates/csshead.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,3 @@
+<link rel="stylesheet" tal:attributes="href static/syntax.css">
+<link rel="stylesheet" tal:attributes="href static/style.css">
+<link rel="alternate" type="application/rss+xml" tal:attributes="title view/application/title; href string:${view/application/@@absolute_url}/feed.rss">
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/blog_templates/draftsindex.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/draftsindex.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog_templates/draftsindex.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,20 +1,6 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" metal:use-macro="context/@@blogmacros/macros/blogpage">
- <body>
- <div metal:fill-slot="main-content">
- <div class="entries"
- tal:define="entries view/entries">
- <tal:block condition="entries" repeat="entry view/entries">
- <tal:block content="structure entry/@@item"/>
- </tal:block>
- <tal:block condition="not:entries">
- No drafts.
- <a href="#"
- tal:attributes="href python:view.application_url('add')">
- Post?
- </a>
- </tal:block>
- </div>
- </div>
- </body>
-</html>
+<a href="../add">Add Blog Entry</a><br />
+<div class="entries">
+ <tal:block repeat="entry view/entries">
+ <tal:block content="structure entry/@@item"/>
+ </tal:block>
+</div>
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/blog_templates/layout.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/layout.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/blog_templates/layout.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,22 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+ <tal:head content="structure provider:head" />
+</head>
+<body>
+
+<div id="head">
+ <tal:header content="structure provider:top" />
+</div>
+<div id="right" style="float:right; width:24%;">
+ <tal:main content="structure provider:right" />
+</div>
+<div id="main" style="float:right; width:74%; padding-right:2%;">
+ <tal:left content="structure provider:main" />
+</div>
+<div style="clear:both" />
+
+<p class="footer" tal:content="view/application/footer" />
+
+</body>
+</html>
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/blog_templates/recententries.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/recententries.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/blog_templates/recententries.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,4 @@
+<h4>Recent entries</h4>
+<ul>
+<li tal:repeat="entry view/entries"><a tal:content="entry/title" tal:attributes="href entry/@@absolute_url"></a></li>
+</ul>
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/blog_templates/search.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/search.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/blog_templates/search.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,20 +1,17 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" metal:use-macro="context/@@blogmacros/macros/blogpage">
- <head>
- <title metal:fill-slot="title">Search</title>
- </head>
- <body>
- <div metal:fill-slot="main-content"
- tal:define="results view/results">
- <tal:no_results condition="not:results">
- Sorry, no results for '<span tal:replace="request/q" />'
- </tal:no_results>
- <div class="search-results" tal:condition="results">
- <ol>
- <li tal:repeat="entry results"
- tal:content="structure entry/@@item" />
- </ol>
- </div>
- </div>
- </body>
-</html>
+<form action="." method="get">
+ <p><input type="text" name="q" value="" size="15"
+ tal:attributes="value request/q | default" />
+ <input type="submit" value="Search" />
+ </p>
+</form>
+<tal:was_search condition="request/q | nothing">
+ <tal:no_results condition="not:view/results">
+ Sorry, no results for '<span tal:replace="request/q" />'
+ </tal:no_results>
+ <div class="search-results" tal:condition="view/results">
+ <ol>
+ <li tal:repeat="entry view/results"><a tal:attributes="href string:${entry/@@absolute_url}?q=${request/q}"
+ tal:content="structure entry/title">An entry</a></li>
+ </ol>
+ </div>
+</tal:was_search>
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/blog_templates/titleheader.pt
===================================================================
--- Grokstar/trunk/src/grokstar/blog_templates/titleheader.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/blog_templates/titleheader.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,4 @@
+<a tal:attributes="href view/application/@@absolute_url">
+ <h1 tal:content="view/application/title"/>
+ <h3 tal:content="view/application/tagline"/>
+</a>
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/calendar.py
===================================================================
--- Grokstar/trunk/src/grokstar/calendar.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/calendar.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -4,6 +4,7 @@
import grok
from hurry.query.query import Query
from hurry import query
+from grokstar.blog import Main, Right
from grokstar.interfaces import PUBLISHED
@@ -20,10 +21,10 @@
return None
return Month(self.year, month)
-class YearIndex(grok.View):
- grok.name('index')
+class YearIndex(grok.Viewlet):
grok.context(Year)
grok.template('dateindex')
+ grok.viewletmanager(Main)
def entries(self):
from_ = datetime(self.context.year, 1, 1)
@@ -43,10 +44,10 @@
# XXX should check whether day is acceptable
return Day(self.year, self.month, day)
-class MonthIndex(grok.View):
- grok.name('index')
+class MonthIndex(grok.Viewlet):
grok.context(Month)
grok.template('dateindex')
+ grok.viewletmanager(Main)
def entries(self):
from_ = datetime(self.context.year,
@@ -66,10 +67,10 @@
self.month = month
self.day = day
-class DayIndex(grok.View):
- grok.name('index')
+class DayIndex(grok.Viewlet):
grok.context(Day)
grok.template('dateindex')
+ grok.viewletmanager(Main)
def entries(self):
from_ = datetime(self.context.year,
Modified: Grokstar/trunk/src/grokstar/calendar_templates/dateindex.pt
===================================================================
--- Grokstar/trunk/src/grokstar/calendar_templates/dateindex.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/calendar_templates/dateindex.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,15 +1,6 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" metal:use-macro="context/@@blogmacros/macros/blogpage">
- <head>
- <title metal:fill-slot="title">blog title</title>
- </head>
- <body>
- <div metal:fill-slot="main-content">
- <h1>blog index</h1>
-
- <tal:block repeat="entry view/entries">
+<div class="entries">
+ <tal:block repeat="entry view/entries">
<tal:block content="structure entry/@@item"/>
- </tal:block>
- </div>
- </body>
-</html>
+ <hr/>
+ </tal:block>
+</div>
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/configure.zcml
===================================================================
--- Grokstar/trunk/src/grokstar/configure.zcml 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/configure.zcml 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,5 +1,6 @@
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok">
<include package="grok" />
+ <include package=".mail" />
<grok:grok package="." />
</configure>
Modified: Grokstar/trunk/src/grokstar/entry.py
===================================================================
--- Grokstar/trunk/src/grokstar/entry.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/entry.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -8,60 +8,119 @@
import grok
-from grokstar.blog import Blog
+from grokstar.blog import Blog, Index, Edit, Main, Right, AddEntry
from grokstar import interfaces
-from form import GrokstarAddForm, GrokstarEditForm
+import grokstar.form
-class Entry(grok.Model):
+from zope.component import getUtility, getMultiAdapter
+
+class Entry(grok.Container):
interface.implements(interfaces.IEntry, IAttributeAnnotatable)
- def __init__(self, title, summary, rightsinfo, categories=None):
+ def __init__(self, title, summary, categories=[]):
+ grok.Container.__init__(self)
self.title = title
- self.updated = datetime.now()
- self.published = None
+ self.updated = self.published = datetime.now()
self.summary = summary
- self.rightsinfo = rightsinfo
- if categories is None:
- self.categories = []
- else:
- self.categories = categories
+ self.categories = categories
class RestructuredTextEntry(Entry):
interface.implements(interfaces.IRestructuredTextEntry)
- def __init__(self, title, summary, rightsinfo, content, categories=None):
- super(RestructuredTextEntry, self).__init__(title, summary, rightsinfo, categories)
+ def __init__(self, title='', summary='', content='', categories=[]):
+ Entry.__init__(self, title, summary, categories)
self.content = content
+class Comment(grok.Model):
+ interface.implements(interfaces.IComment, IAttributeAnnotatable)
+ comment = u""
+ date = datetime.now()
+ author = u""
+
+ def __init__(self, comment, author):
+ self.comment = comment
+ self.author = author
+ self.date = datetime.now()
+
grok.context(RestructuredTextEntry)
-
-class Index(grok.View):
+class EntryIndex(grok.Viewlet):
+ grok.viewletmanager(Main)
+ grok.view(Index)
+
+ def update(self):
+ self.comments=sorted(self.context.values(), key=lambda c:c.date)
+
+class Item(grok.View):
pass
+class AddComment(grok.Viewlet):
+ grok.context(Entry)
+ grok.viewletmanager(Main)
+ grok.view(Index)
+ grok.order(8)
-class Item(grok.View):
- def format_published(self, published_date):
- return published_date.strftime('%Y-%m-%d')
+ def update(self):
+ self.form = getMultiAdapter((self.context, self.request),
+ name='addcommentform')
+ self.form.update_form()
+ def render(self):
+ return self.form.renderedPreview + self.form.render()
-class Add(GrokstarAddForm):
+class AddCommentForm(grokstar.form.GrokstarAddForm):
+ form_fields = grok.AutoFields(Comment).omit('date')
+ renderedPreview = ''
+
+ @grok.action('Preview')
+ def preview(self, comment='', **data):
+ self.renderedPreview = '<h2>Preview</h2><div class="comment">' + renderRest(comment) + '</div>'
+ self.form_reset = False
+
+ @grok.action('Add comment')
+ def add(self, **data):
+ new_comment = Comment(**data)
+ cid = 1
+ while str(cid) in self.context:
+ cid += 1 #Not very clever, but fine for < 10000 comments!
+ self.context[str(cid)] = new_comment
+ self.redirect(self.url(self.context))
+
+
+class AddEntryViewlet(grok.Viewlet):
+ grok.viewletmanager(Main)
+ grok.view(AddEntry)
grok.context(Blog)
- title = u'Add Entry'
- # add the url that the user wants
+
+ def update(self):
+ self.form = getMultiAdapter((self.context, self.request),
+ name='addentryform')
+ self.form.update_form()
+
+ def render(self):
+ return self.form.renderedPreview + self.form.render()
+
+class AddEntryForm(grokstar.form.GrokstarAddForm):
+ grok.context(Blog)
+
form_fields = grok.Fields(
- id=schema.TextLine(title=u"Post slug"))
- # don't show them these timestamps
+ id=schema.TextLine(title=u"id"))
form_fields += grok.AutoFields(RestructuredTextEntry).omit(
'published', 'updated')
+ renderedPreview = ''
- @grok.action('Add entry')
+ @grok.action('Preview')
+ def preview(self, content='', **data):
+ self.renderedPreview = '<h2>Preview</h2><div class="comment">' + renderRest(content) + '</div>'
+ self.form_reset = False
+
+ @grok.action('Add draft entry')
def add(self, id, **data):
new_entry = RestructuredTextEntry(**data)
self.context['entries'][id] = new_entry
IWorkflowInfo(new_entry).fireTransition('create')
self.redirect(self.url(self.context))
-
+
@grok.action('Add published entry')
def add_published(self, id, **data):
new_entry = RestructuredTextEntry(**data)
@@ -70,25 +129,51 @@
IWorkflowInfo(new_entry).fireTransitionToward(interfaces.PUBLISHED)
self.redirect(self.url(self.context))
+class EntryEdit(grok.Viewlet):
+ grok.context(Entry)
+ grok.viewletmanager(Main)
+ grok.view(Edit)
-class Edit(GrokstarEditForm):
- grok.context(RestructuredTextEntry)
- title = u'Edit Entry'
+ def update(self):
+ self.form = getMultiAdapter((self.context, self.request),
+ name='entryeditform')
+ self.form.update_form()
+
+ def render(self):
+ return self.form.renderedPreview + self.form.render()
+
+class EntryEditForm(grok.EditForm):
form_fields = grok.AutoFields(RestructuredTextEntry).omit(
'published', 'updated')
+ renderedPreview = ''
@grok.action('Save changes')
def edit(self, **data):
self.applyData(self.context, **data)
self.redirect(self.url(self.context))
+ @grok.action('Preview')
+ def preview(self, content, **data):
+ self.renderedPreview = renderRest(content)
+ self.form_reset = False
+
@grok.action('Publish')
def publish(self, **data):
self.applyData(self.context, **data)
IWorkflowInfo(self.context).fireTransitionToward(interfaces.PUBLISHED)
self.redirect(self.url(self.context))
+ @grok.action('Retract')
+ def retract(self, **data):
+ self.applyData(self.context, **data)
+ IWorkflowInfo(self.context).fireTransitionToward(interfaces.CREATED)
+ self.redirect(self.url(self.context))
+class RenderedComment(grok.View):
+ grok.context(Comment)
+ def render(self):
+ return renderRest(self.context.comment)
+
class RenderedContent(grok.View):
def render(self):
return renderRest(self.context.content)
Added: Grokstar/trunk/src/grokstar/entry_templates/entryindex.pt
===================================================================
--- Grokstar/trunk/src/grokstar/entry_templates/entryindex.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/entry_templates/entryindex.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,12 @@
+<h1 tal:content="context/title">Entry</h1>
+<span class="published" tal:content="python:context.published.strftime('%e %B %Y %H:%M')">29 February 1999</span>
+<tal:block content="structure context/@@renderedcontent" />
+
+<a name="comments"></a>
+<p tal:condition="context/values" class="commenthead">Comments</p>
+<div class="comment" tal:repeat="comment view/comments">
+ <p class="commenttitle"><span tal:replace="comment/author">Fred</span> wrote on <span tal:replace="python:comment.date.strftime('%e %B %Y')">29 February 1999</span>:</p>
+ <tal:block content="structure comment/@@renderedcomment" />
+</div>
+<a name="newcomment"></a>
+<p class="commenthead">Leave a comment</p>
Deleted: Grokstar/trunk/src/grokstar/entry_templates/index.pt
===================================================================
--- Grokstar/trunk/src/grokstar/entry_templates/index.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/entry_templates/index.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,12 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html metal:use-macro="context/@@blogmacros/macros/blogpage" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
- <head>
- <title metal:fill-slot="title" tal:content="context/title"/>
- </head>
- <body>
- <div metal:fill-slot="main-content">
- <tal:entry replace="structure context/@@item" />
- <a tal:attributes="href python:view.url('edit')">edit</a>
- </div>
- </body>
-</html>
Modified: Grokstar/trunk/src/grokstar/entry_templates/item.pt
===================================================================
--- Grokstar/trunk/src/grokstar/entry_templates/item.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/entry_templates/item.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,12 +1,10 @@
<div class="entry">
- <div class="title">
- <a tal:attributes="href python:view.url(context)"
- tal:content="context/title" />
- <tal:published define="published context/published"
- condition="published">
- (<span class="published"
- tal:content="python:view.format_published(published)"/>)
- </tal:published>
- </div>
+ <h2 class="title">
+ <a tal:attributes="href python:view.url(context)">
+ <tal:block content="context/title"/>
+ </a>
+ </h2>
+ <span class="published" tal:content="python:context.published.strftime('%e %B %Y %H:%M')">29 February 1999</span>
<tal:block content="structure context/@@renderedcontent"/>
-</div>
+ <a tal:attributes="href string:${context/@@absolute_url}#comments">Comments: <span tal:replace="python:len(context.values())">0</span></a>
+</div>
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/form.py
===================================================================
--- Grokstar/trunk/src/grokstar/form.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/form.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -7,4 +7,4 @@
class GrokstarEditForm(grok.EditForm):
pass
-grok.context(Interface)
+grok.context(Interface)
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/form_templates/grokstaraddform.pt
===================================================================
--- Grokstar/trunk/src/grokstar/form_templates/grokstaraddform.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/form_templates/grokstaraddform.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,10 +1,3 @@
-<html metal:use-macro="context/@@blogmacros/macros/blogpage" >
-<head>
- <title metal:fill-slot="title" tal:content="view/title | default">Add</title>
-</head>
-
-<body>
-<div metal:fill-slot="main-content">
<form action="." tal:attributes="action request/URL" method="post"
class="edit-form" enctype="multipart/form-data">
@@ -65,7 +58,4 @@
/>
</span>
</div>
-</form>
-</div>
-</body>
-</html>
+</form>
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/form_templates/grokstareditform.pt
===================================================================
--- Grokstar/trunk/src/grokstar/form_templates/grokstareditform.pt 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/form_templates/grokstareditform.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,72 +1,61 @@
-<html metal:use-macro="context/@@blogmacros/macros/blogpage">
-<head>
- <title metal:fill-slot="title" tal:content="view/title | default">Edit</title>
-</head>
+<form action="." tal:attributes="action request/URL" method="post"
+ class="edit-form" enctype="multipart/form-data">
-<body>
- <div metal:fill-slot="main-content">
- <form action="." tal:attributes="action request/URL" method="post"
- class="edit-form" enctype="multipart/form-data">
+ <h1 i18n:translate=""
+ tal:condition="view/label"
+ tal:content="view/label">Label</h1>
- <h1 i18n:translate=""
- tal:condition="view/label"
- tal:content="view/label">Label</h1>
+ <div class="form-status"
+ tal:define="status view/status"
+ tal:condition="status">
- <div class="form-status"
- tal:define="status view/status"
- tal:condition="status">
+ <div i18n:translate="" tal:content="view/status">
+ Form status summary
+ </div>
- <div i18n:translate="" tal:content="view/status">
- Form status summary
- </div>
+ <ul class="errors" tal:condition="view/errors">
+ <li tal:repeat="error view/error_views">
+ <span tal:replace="structure error">Error Type</span>
+ </li>
+ </ul>
+ </div>
- <ul class="errors" tal:condition="view/errors">
- <li tal:repeat="error view/error_views">
- <span tal:replace="structure error">Error Type</span>
- </li>
- </ul>
- </div>
+ <table class="form-fields">
+ <tbody>
+ <tal:block repeat="widget view/widgets">
+ <tr>
+ <td class="label" tal:define="hint widget/hint">
+ <label tal:condition="python:hint"
+ tal:attributes="for widget/name">
+ <span class="required" tal:condition="widget/required"
+ >*</span><span i18n:translate=""
+ tal:content="widget/label">label</span>
+ </label>
+ <label tal:condition="python:not hint"
+ tal:attributes="for widget/name">
+ <span class="required" tal:condition="widget/required"
+ >*</span><span i18n:translate=""
+ tal:content="widget/label">label</span>
+ </label>
+ </td>
+ <td class="field">
+ <div class="widget" tal:content="structure widget">
+ <input type="text" />
+ </div>
+ <div class="error" tal:condition="widget/error">
+ <span tal:replace="structure widget/error">error</span>
+ </div>
+ </td>
+ </tr>
+ </tal:block>
+ </tbody>
+ </table>
- <table class="form-fields">
- <tbody>
- <tal:block repeat="widget view/widgets">
- <tr>
- <td class="label" tal:define="hint widget/hint">
- <label tal:condition="python:hint"
- tal:attributes="for widget/name">
- <span class="required" tal:condition="widget/required"
- >*</span><span i18n:translate=""
- tal:content="widget/label">label</span>
- </label>
- <label tal:condition="python:not hint"
- tal:attributes="for widget/name">
- <span class="required" tal:condition="widget/required"
- >*</span><span i18n:translate=""
- tal:content="widget/label">label</span>
- </label>
- </td>
- <td class="field">
- <div class="widget" tal:content="structure widget">
- <input type="text" />
- </div>
- <div class="error" tal:condition="widget/error">
- <span tal:replace="structure widget/error">error</span>
- </div>
- </td>
- </tr>
- </tal:block>
- </tbody>
- </table>
-
- <div id="actionsView">
- <span class="actionButtons" tal:condition="view/availableActions">
- <input tal:repeat="action view/actions"
- tal:replace="structure action/render"
- />
- </span>
- </div>
- </form>
-
+ <div id="actionsView">
+ <span class="actionButtons" tal:condition="view/availableActions">
+ <input tal:repeat="action view/actions"
+ tal:replace="structure action/render"
+ />
+ </span>
</div>
-</body>
-</html>
+</form>
\ No newline at end of file
Modified: Grokstar/trunk/src/grokstar/interfaces.py
===================================================================
--- Grokstar/trunk/src/grokstar/interfaces.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/interfaces.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,13 +1,16 @@
from zope.interface import Interface
from zope import schema, interface
+from zope.app.container.interfaces import IContainer
CREATED = 0
PUBLISHED = 1
class IBlog(Interface):
- title = schema.TextLine(title=u'Title', default=u'')
- tagline = schema.TextLine(title=u'Tagline', default=u'')
+ title = schema.TextLine(title=u'Title')
+ tagline = schema.TextLine(title=u'Tagline')
+ footer = schema.TextLine(title=u'Footer', required=False)
+ email = schema.TextLine(title=u'Email')
-class IEntry(Interface):
+class IEntry(IContainer):
"""
This interface is based on the Atom entry definition, from the Atom RFC.
@@ -37,14 +40,21 @@
summary = schema.SourceText(title=u"Summary", required=False)
- # content = schema.SourceText(title=u"Content")
+ content = schema.SourceText(title=u"Content")
- rightsinfo = schema.SourceText(title=u"Rights", required=False)
+ # rightsinfo = schema.SourceText(title=u"Rights", required=False)
# source is too complicated to support for us right now
class IRestructuredTextEntry(IEntry):
content = schema.SourceText(title=u"Content")
+
+class IComment(Interface):
+ date = schema.Datetime(title=u"Date", required=True)
+
+ comment = schema.SourceText(title=u"Comment", required=True)
+
+ author = schema.TextLine(title=u"Author", required=True)
class IAtomEntry(Interface):
Property changes on: Grokstar/trunk/src/grokstar/mail
___________________________________________________________________
Name: svn:ignore
+ mail-queue
Added: Grokstar/trunk/src/grokstar/mail/__init__.py
===================================================================
--- Grokstar/trunk/src/grokstar/mail/__init__.py (rev 0)
+++ Grokstar/trunk/src/grokstar/mail/__init__.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,29 @@
+import email.Charset #See Phillip's book, p318 2nd Ed
+email.Charset.add_charset('utf-8', email.Charset.SHORTEST, None, None)
+from datetime import datetime
+from email.MIMEText import MIMEText
+
+from zope.component import getUtility, adapter
+from zope.sendmail.interfaces import IMailDelivery
+
+from zope.app.container.interfaces import IObjectAddedEvent
+
+from grok.interfaces import IApplication
+
+from grokstar.interfaces import IComment
+
+
+ at adapter(IComment, IObjectAddedEvent)
+def notifyCommentAdded(comment, event):
+ return commentAdded(comment)
+
+def commentAdded(comment):
+ message = MIMEText(comment.comment.encode('utf-8'), 'plain', 'utf-8')
+ message['Subject'] = "Comment from " + comment.author
+ recipient = getUtility(IApplication).email
+ message['From'] = recipient
+ message['To'] = recipient
+ message['Date'] = datetime.now().strftime('%a, %e %b %Y %H:%M:%S %z')
+
+ mailer = getUtility(IMailDelivery, 'grokstar')
+ mailer.send(recipient, [recipient], message.as_string())
Added: Grokstar/trunk/src/grokstar/mail/configure.zcml
===================================================================
--- Grokstar/trunk/src/grokstar/mail/configure.zcml (rev 0)
+++ Grokstar/trunk/src/grokstar/mail/configure.zcml 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,22 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:mail="http://namespaces.zope.org/mail"
+ >
+
+ <mail:smtpMailer
+ name="grokstar"
+ hostname="localhost"
+ port="25"
+ />
+
+ <mail:queuedDelivery
+ name="grokstar"
+ permission="zope.SendMail"
+ queuePath="mail-queue"
+ mailer="grokstar"
+ />
+
+ <subscriber handler=".notifyCommentAdded" />
+
+</configure>
+
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/mail/notifications.txt
===================================================================
--- Grokstar/trunk/src/grokstar/mail/notifications.txt (rev 0)
+++ Grokstar/trunk/src/grokstar/mail/notifications.txt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,23 @@
+This should be really simple.
+
+We want to create comments:
+
+>>> from grokstar.entry import Comment
+>>> comment = Comment("Good stuff", "fred")
+
+When we notify zope of the creation, a mail should be sent:
+
+>>> from zope.app.container.interfaces import IObjectAddedEvent
+>>> from zope.lifecycleevent import ObjectCreatedEvent
+>>> from zope.event import notify
+>>> notify(ObjectCreatedEvent(comment))
+
+I'm not whether we should be subscribing to ObjectCreatedEvent or ObjectAddedEvent.
+
+We need to register a dummy mailer for the test:
+
+>>> from zope.sendmail.interfaces import IMailDelivery
+>>> from zope.component import getUtility
+>>> mailer = getUtility(IMailDelivery, 'grokstar')
+
+>> mailer.send(recipient, [recipient], message.as_string())
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/mail/tests.py
===================================================================
--- Grokstar/trunk/src/grokstar/mail/tests.py (rev 0)
+++ Grokstar/trunk/src/grokstar/mail/tests.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,31 @@
+import unittest
+import doctest
+
+from zope.sendmail.interfaces import IMailDelivery
+import zope.component.testing
+import zope.component.eventtesting
+from zope.interface import implements
+
+from grokstar.mail import notifyCommentAdded
+
+class DummyMailDelivery(object):
+ implements(IMailDelivery)
+ def send(self, fromaddr, toaddr, msg):
+ print msg
+
+def setUp(test):
+ zope.component.testing.setUp(test)
+ zope.component.eventtesting.setUp(test)
+ zope.component.provideUtility(DummyMailDelivery(), name='grokstar')
+ zope.component.provideHandler(notifyCommentAdded)
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('notifications.txt',
+ setUp=setUp,
+ tearDown=zope.component.testing.tearDown,
+ optionflags=doctest.ELLIPSIS),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/rss.py
===================================================================
--- Grokstar/trunk/src/grokstar/rss.py (rev 0)
+++ Grokstar/trunk/src/grokstar/rss.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,10 @@
+import grok
+
+from grokstar.blog import Blog, lastEntries
+
+class RSS(grok.View):
+ grok.context(Blog)
+ grok.name('feed.rss')
+
+ def items(self, max=10):
+ return lastEntries(max)
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/rss_templates/rss.pt
===================================================================
--- Grokstar/trunk/src/grokstar/rss_templates/rss.pt (rev 0)
+++ Grokstar/trunk/src/grokstar/rss_templates/rss.pt 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<rss version="2.0" xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <channel>
+ <title tal:content="context/title"/>
+ <link tal:content="view/application_url"/>
+ <description tal:content="context/tagline"/>
+ <item tal:repeat="item view/items">
+ <title tal:content="item/title"/>
+ <link tal:content="item/@@absolute_url"/>
+ <description tal:content="item/summary"/>
+ <pubDate tal:content="item/updated"/>
+ </item>
+ </channel>
+</rss>
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/static/style.css
===================================================================
--- Grokstar/trunk/src/grokstar/static/style.css (rev 0)
+++ Grokstar/trunk/src/grokstar/static/style.css 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,105 @@
+body {
+ width:980px;
+ font-family:sans-serif;
+ color:#222;
+ font-size:80%;
+ line-height:140%;
+}
+
+a {
+ color:#e90;
+}
+
+#head {
+ background:#fdb;
+ padding:1em;
+ margin-bottom:0.5em;
+ text-align:center;
+}
+
+#head a {
+ text-decoration:none;
+ color:#000;
+}
+
+#search {
+ margin-left:0.5em;
+ padding:1em;
+}
+
+h1, h2, h3, h4 {
+ margin-bottom:0.2em;
+}
+
+ul {
+ margin-top:0.2em;
+ list-style-type:none;
+ padding-left:1em;
+}
+
+hr {
+ border-color:#080;
+ background-color:#080;
+ border-width:0px;
+ height:1px;
+}
+
+.document h1 {
+ font-size:130%;
+ font-weight:normal;
+}
+
+.published {
+ font-weight:bold;
+ font-size:60%;
+ color:#ccc;
+ padding-left:1em;
+ margin-top:0;
+}
+
+.commenthead {
+ background-color:#7d7;
+ border:1px solid #080;
+ font-weight:bold;
+ padding:0.5em;
+}
+
+.comment {
+ background-color:#beb;
+ padding:0.5em;
+ margin-top:0.5em;
+}
+
+.commenttitle {
+ margin:0.5em;
+ font-weight:bold;
+}
+
+.doctest-block, .highlight, .literal-block {
+ margin:1em;
+ border: 1px dashed #0b0;
+ background-color:#efe;
+ overflow:auto;
+ padding:0.5em 1em 0.5em 1em;
+ line-height:140%;
+ font-size:110%;
+}
+
+.literal-block {
+ border-color:#333;
+ background-color:#eee;
+}
+
+.footer {
+ font-size:65%;
+ font-weight:bold;
+ text-align:center;
+ color:#060;
+}
+
+cite {
+ font-family:monospace;
+ font-style:normal;
+}
+
+pre { margin:0;} /* Not 0 by default in Firefox */
\ No newline at end of file
Added: Grokstar/trunk/src/grokstar/static/syntax.css
===================================================================
--- Grokstar/trunk/src/grokstar/static/syntax.css (rev 0)
+++ Grokstar/trunk/src/grokstar/static/syntax.css 2008-06-17 22:41:28 UTC (rev 87483)
@@ -0,0 +1,59 @@
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #808080 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0040D0 } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #B00040 } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
\ No newline at end of file
Deleted: Grokstar/trunk/src/grokstar/utils.py
===================================================================
--- Grokstar/trunk/src/grokstar/utils.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/utils.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -1,21 +0,0 @@
-import grok
-from hurry.query.query import Query
-from hurry import query
-from zope.interface import Interface
-from grokstar.interfaces import IBlog
-
-class Utils(grok.View):
- """contain common view methods"""
- grok.context(Interface)
-
- def numPosts(self):
- # @@ is there a better way to get all entries through the catalog?
- obj = self.context
- while obj is not None:
- if IBlog.providedBy(obj):
- return len(obj['entries'])
- obj = obj.__parent__
- return 0
-
- def render(self):
- return ''
Modified: Grokstar/trunk/src/grokstar/workflow.py
===================================================================
--- Grokstar/trunk/src/grokstar/workflow.py 2008-06-17 22:29:27 UTC (rev 87482)
+++ Grokstar/trunk/src/grokstar/workflow.py 2008-06-17 22:41:28 UTC (rev 87483)
@@ -2,10 +2,8 @@
import grok
from grokstar.entry import Entry
-from hurry.workflow import workflow
+from hurry.workflow import workflow, interfaces
from hurry.workflow.interfaces import IWorkflow
-from hurry.workflow.interfaces import IWorkflowState
-from hurry.workflow.interfaces import IWorkflowInfo
from hurry.query.query import Query
from hurry.query import Eq
@@ -35,10 +33,16 @@
destination=PUBLISHED,
action=publish_action)
- return [create_transition, publish_transition, update_transition]
+ retract_transition = workflow.Transition(
+ transition_id='retract',
+ title='retract',
+ source=PUBLISHED,
+ destination=CREATED)
+
+ return [create_transition, publish_transition, update_transition,
+ retract_transition]
class Workflow(grok.GlobalUtility, workflow.Workflow):
- # grok.name('grokstar_workflow')
grok.provides(IWorkflow)
def __init__(self):
@@ -68,9 +72,9 @@
class WorkflowState(grok.Adapter, workflow.WorkflowState):
grok.context(Entry)
- grok.provides(IWorkflowState)
+ grok.provides(interfaces.IWorkflowState)
class WorkflowInfo(grok.Adapter, workflow.WorkflowInfo):
grok.context(Entry)
- grok.provides(IWorkflowInfo)
+ grok.provides(interfaces.IWorkflowInfo)
More information about the Checkins
mailing list