[Checkins] SVN: Sandbox/darrylcousins/tfws.website/src/tfws/website/ Progressing with the site demo

Darryl Cousins darryl at darrylcousins.net.nz
Mon Jul 23 01:06:33 EDT 2007


Log message for revision 78286:
  Progressing with the site demo

Changed:
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/BROWSER.txt
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/breadcrumb.py
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/browser.py
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/formatter.py
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/index.pt
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/table_sorted_header.pt
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/catalog.py
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/configure.zcml
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/site.py
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/ascending.gif
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/buttonBG.gif
  A   Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/decending.gif
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/skin.py
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/template.pt
  U   Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/website.css

-=-
Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/BROWSER.txt
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/BROWSER.txt	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/BROWSER.txt	2007-07-23 05:06:32 UTC (rev 78286)
@@ -16,6 +16,7 @@
   Traceback (most recent call last):
   ...
   HTTPError: HTTP Error 401: Unauthorized
+  >>> user.handleErrors = False
 
 Using our authenticated browser instance we can add a website.
 
@@ -28,8 +29,15 @@
   >>> browser.getControl('Email').value = u'darry.cousins at tfws.org.nz'
   >>> browser.getControl('Add').click()
 
-We can now access the new site with our user.
+The new site is now included in the table of sites available.
 
-  >>> user.handleErrors = False
-  >>> browser.open("http://localhost/treefern")
-  >>> print browser.contents
+  >>> browser.open("http://localhost/")
+  >>> testing.printElement(browser, "//table/tbody/tr[1]/td/a/text()",
+  ...                      multiple=True, serialize=False )
+  treefern
+  Grok/Mars/Z3C Demo Site
+
+We can edit the new site.
+
+  >>> #print browser.contents
+  >>> browser.getLink('Grok/Mars/Z3C Demo Site').click()

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/breadcrumb.py
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/breadcrumb.py	                        (rev 0)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/breadcrumb.py	2007-07-23 05:06:32 UTC (rev 78286)
@@ -0,0 +1,13 @@
+import zope.interface
+
+from z3c.breadcrumb.browser import Breadcrumbs as Breadcrumbs_
+from z3c.breadcrumb.interfaces import IBreadcrumbs
+
+import grok
+
+class Breadcrumbs(grok.MultiAdapter, Breadcrumbs_):
+    """Breadcrumbs implementation using IBreadcrumb adapters."""
+    grok.name('breadcrumbs')
+    grok.context(zope.interface.Interface)
+    grok.provides(IBreadcrumbs)
+


Property changes on: Sandbox/darrylcousins/tfws.website/src/tfws/website/breadcrumb.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/browser.py
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/browser.py	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/browser.py	2007-07-23 05:06:32 UTC (rev 78286)
@@ -1,33 +1,96 @@
 from zope.traversing.browser import absoluteURL
 from zope.app.folder.interfaces import IRootFolder
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.traversing import api
 
+from z3c.form import form, field
+
+from zc.table import column
+from zc.table import table
+
 import grok
 
 import mars.layer
 import mars.view
 import mars.template
 
+from tfws.website import interfaces
+from tfws.website.browser import formatter
 from tfws.website.layer import IWebsiteLayer
+from tfws.website.i18n import MessageFactory as _
 
 mars.layer.layer(IWebsiteLayer)
 
 grok.define_permission('tfws.ManageSites')
 
+class CheckboxColumn(column.Column):
+
+    def renderCell(self, item, formatter):
+        widget = (u'<input type="checkbox" '
+                  u'name="selected:list" value="%s">')
+        return widget %api.getName(item)
+
+
+def getCreatedDate(item, formatter):
+    formatter = formatter.request.locale.dates.getFormatter('date', 'short')
+    return formatter.format(IZopeDublinCore(item).created)
+
+
+def getModifiedDate(item, formatter):
+    formatter = formatter.request.locale.dates.getFormatter('date', 'short')
+    return formatter.format(IZopeDublinCore(item).modified)
+
+
+def link(view='index', title=''):
+    def anchor(value, item, formatter):
+        url = absoluteURL(item, formatter.request) + '/' + view
+        return u'<a href="%s" title="%s">%s</a>' %(url, title, value)
+    return anchor
+
+
 class Index(mars.view.PageletView):
     grok.context(IRootFolder)
     grok.require('tfws.ManageSites')
 
-    @property
+    columns = (
+        CheckboxColumn(_('Sel')),
+        column.GetterColumn(_('Id'), 
+                    lambda item, f: api.getName(item), link('index', _('View'))),
+        column.GetterColumn(_('Title'), 
+                    lambda item, f: item.title, link('edit', _('Edit'))),
+        column.GetterColumn(_('Created On'), getCreatedDate),
+        column.GetterColumn(_('Modified On'), getModifiedDate),
+        )
+
+    status = None
+
     def sites(self):
-        for site in self.context:
-            print site
+        return [obj
+                for obj in self.context.values()
+                if interfaces.IWebSite.providedBy(obj)]
 
-    @property
-    def addurl(self):
-        url = absoluteURL(self.context, self.request)
-        return url + '/add'
+    def table(self):
+        formatter = table.AlternatingRowFormatter(
+            self.context, self.request, self.sites(), columns=self.columns)
+        formatter.widths=[25, 50, 300, 100, 100]
+        formatter.cssClasses['table'] = 'list'
+        return formatter()
 
+    def update(self):
+        if 'ADD' in self.request:
+            self.request.response.redirect('add')
+        if 'DELETE' in self.request:
+            if self.request.get('confirm_delete') != 'yes':
+                self.status = _('You did not confirm the deletion correctly.')
+                return
+            if 'selected' in self.request:
+                for id in self.request['selected']:
+                    del self.context[id]
+                self.status = _('Sites were successfully deleted.')
+            else:
+                self.status = _('No sites were selected.')
 
+
 class IndexTemplate(mars.template.TemplateFactory):
     """layout template for `home`"""
     grok.context(Index)

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/formatter.py
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/formatter.py	                        (rev 0)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/formatter.py	2007-07-23 05:06:32 UTC (rev 78286)
@@ -0,0 +1,129 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation 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.
+#
+##############################################################################
+"""List Formatter Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from xml.sax.saxutils import quoteattr
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.app.session.interfaces import ISession
+from zc.table import table, column, interfaces
+
+
+class ListFormatter(table.SortingFormatterMixin, table.AlternatingRowFormatter):
+    """Provides a width for each column."""
+
+    sortedHeaderTemplate = ViewPageTemplateFile('table_sorted_header.pt')
+
+    sortKey = 'formdemo.table.sort-on'
+    widths = None
+    columnCSS = None
+
+    def __init__(self, *args, **kw):
+        # Figure out sorting situation
+        kw['ignore_request'] = True
+        request = args[1]
+        prefix = kw.get('prefix')
+        session = ISession(request)[self.sortKey]
+        if 'sort-on' in request:
+            name = request['sort-on']
+            if prefix and name.startswith(prefix):
+                name = name[len(prefix):]
+                oldName, oldReverse = session.get(prefix, (None, None))
+                if oldName == name:
+                    session[prefix] = (name, not oldReverse)
+                else:
+                    session[prefix] = (name, False)
+        # Now get the sort-on data from the session
+        if prefix in session:
+            kw['sort_on'] = [session[prefix]]
+
+        super(ListFormatter, self).__init__(*args, **kw)
+        self.columnCSS = {}
+
+        self.sortOn = (None, None)
+        if 'sort_on' in kw:
+            for name, reverse in kw['sort_on']:
+                self.columnCSS[name] = 'sorted-on'
+            self.sortOn = kw['sort_on'][0]
+
+    def getHeader(self, column):
+        contents = column.renderHeader(self)
+        if (interfaces.ISortableColumn.providedBy(column)):
+            contents = self._wrapInSortUI(contents, column)
+        return contents
+
+    def _wrapInSortUI(self, header, column):
+        name = column.name
+        if self.prefix:
+            name = self.prefix + name
+        isSortedOn = self.sortOn[0] == column.name
+        isAscending = self.sortOn[0] == column.name and not self.sortOn[1]
+        isDecending = self.sortOn[0] == column.name and self.sortOn[1]
+        return self.sortedHeaderTemplate(
+            header=header, name=name, isSortedOn=isSortedOn,
+            isAscending=isAscending, isDecending=isDecending)
+
+    def renderContents(self):
+        """Avoid to render empty table (tr) rows."""
+        rows = self.renderRows()
+        if not rows:
+            return '  <thead%s>\n%s  </thead>\n' % (
+                self._getCSSClass('thead'), self.renderHeaderRow())
+        else:
+            return '  <thead%s>\n%s  </thead>\n  <tbody>\n%s  </tbody>\n' % (
+                self._getCSSClass('thead'), self.renderHeaderRow(),
+                rows)
+
+    def renderHeader(self, column):
+        width = ''
+        if self.widths:
+            idx = list(self.visible_columns).index(column)
+            width = ' width="%i"' %self.widths[idx]
+        klass = self.cssClasses.get('tr', '')
+        if column.name in self.columnCSS:
+            klass += klass and ' ' or '' + self.columnCSS[column.name]
+        return '      <th%s class=%s>\n        %s\n      </th>\n' % (
+            width, quoteattr(klass), self.getHeader(column))
+
+
+    def renderCell(self, item, column):
+        klass = self.cssClasses.get('tr', '')
+        if column.name in self.columnCSS:
+            klass += klass and ' ' or '' + self.columnCSS[column.name]
+        return '    <td class=%s>\n      %s\n    </td>\n' % (
+            quoteattr(klass), self.getCell(item, column))
+
+    def renderExtra(self):
+        """Avoid use of resourcelibrary in original class."""
+        return ''
+
+
+class SelectedItemFormatter(ListFormatter):
+
+    selectedItem = None
+
+    def renderRow(self, item):
+        self.row += 1
+        klass = self.cssClasses.get('tr', '')
+        if klass:
+            klass += ' '
+        if item == self.selectedItem:
+            klass += 'selected'
+        else:
+            klass += self.row_classes[self.row % 2]
+
+        return '  <tr class=%s>\n%s  </tr>\n' % (
+            quoteattr(klass), self.renderCells(item))


Property changes on: Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/formatter.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/index.pt
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/index.pt	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/index.pt	2007-07-23 05:06:32 UTC (rev 78286)
@@ -1,11 +1,38 @@
 <h1>Tree Fern Web Site Demos</h1>
+<div class="message"
+     tal:condition="view/status"
+     tal:content="view/status"
+     i18n:translate="">
+  Something happened.
+</div>
 
-<p>
-<a tal:attributes="href view/addurl" title="Add website">Add website</a>
-</p>
-<ul>
- <li tal:repeat="site view/sites">
-  <a tal:attributes="href site/url;title site/description|site/title"
-  tal:content="site/title">Site</a>
- </li>
-</ul>
+<form action="" method="post"
+      tal:attributes="action request/URL">
+
+  <tal:block
+      condition="view/sites"
+      replace="structure view/table" />
+
+  <div
+      tal:condition="not: view/sites"
+      i18n:translate="">
+    There are no sites.
+  </div>
+
+  <div id="actionsView">
+    <span class="actionButtons">
+      <input type="submit" class="button" name="ADD" value="Add"
+             i18n:attributes="value" />
+      <tal:block condition="view/sites">
+        <input type="submit" class="button" name="DELETE" value="Delete"
+               i18n:attributes="value" />
+        <input type="text" id="confirm_delete" name="confirm_delete"
+               size="5" />
+        <label for="confirm_delete" i18n:translate="">
+          (Type "yes" to delete.)
+        </label>
+    </tal:block>
+    </span>
+  </div>
+
+</form>

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/table_sorted_header.pt
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/table_sorted_header.pt	                        (rev 0)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/browser/table_sorted_header.pt	2007-07-23 05:06:32 UTC (rev 78286)
@@ -0,0 +1,19 @@
+<html tal:omit-tag="">
+  <a href=""
+     tal:attributes="href string:${request/URL}?sort-on=${options/name}"
+     tal:content="options/header" />
+  <span tal:condition="options/isSortedOn">
+  &nbsp;
+  </span>
+  <img src="" width="7" height="4" style="vertical-align: middle"
+       alt="sort ascending"
+       tal:condition="options/isAscending"
+       tal:attributes="src
+           context/++resource++images/ascending.gif" />
+  <img src="" width="7" height="4" style="vertical-align: middle"
+       alt="sort ascending"
+       tal:condition="options/isDecending"
+       tal:attributes="src
+           context/++resource++images/decending.gif" />
+</html>
+

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/catalog.py
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/catalog.py	                        (rev 0)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/catalog.py	2007-07-23 05:06:32 UTC (rev 78286)
@@ -0,0 +1,38 @@
+import zope.interface
+from zope.index.text import textindex
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.app.catalog.text import ITextIndex
+from zope.app.container import contained
+
+from zc.catalog import catalogindex
+
+from tfws.website import interfaces
+
+class TextIndex(textindex.TextIndex, contained.Contained):
+
+    # not really true but needed by query.Text
+    zope.interface.implements(ITextIndex)
+
+    def index_doc(self, docid, obj):
+        text = ''
+        if interfaces.IContent.providedBy(obj):
+            text += obj.title + ' '
+            text += obj.description + ' '
+            text += obj.keyword + ' '
+            text += obj.body
+        return super(TextIndex, self).index_doc(docid, text)
+
+    def __repr__(self):
+        return '<%s for IFeed>' %self.__class__.__name__
+
+
+def setup_catalog(catalog):
+    """Configure catalog for the site."""
+
+    # Feed text index
+    catalog['text'] = TextIndex()
+
+    # Dublin Core Indices
+    catalog['creator'] = catalogindex.SetIndex('creators', IZopeDublinCore)
+    catalog['created'] = catalogindex.DateTimeValueIndex(
+        'created', IZopeDublinCore)


Property changes on: Sandbox/darrylcousins/tfws.website/src/tfws/website/catalog.py
___________________________________________________________________
Name: svn:keywords
   + Id

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/configure.zcml
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/configure.zcml	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/configure.zcml	2007-07-23 05:06:32 UTC (rev 78286)
@@ -7,6 +7,7 @@
 
   <!-- extra package includes, check grok configure first -->
   <include package="zope.contentprovider" />
+  <include package="zope.app.session" />
 
   <include package="zope.viewlet" file="meta.zcml" />
 
@@ -17,6 +18,7 @@
   <include package="z3c.zrtresource" file="meta.zcml" />
   <include package="zc.resourcelibrary" file="meta.zcml" />
 
+  <include package="z3c.breadcrumb" />
   <include package="z3c.form" />
   <include package="z3c.formui" />
   <include package="z3c.layer.pagelet" />

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/site.py
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/site.py	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/site.py	2007-07-23 05:06:32 UTC (rev 78286)
@@ -24,7 +24,7 @@
 from z3c.authentication.simple.principal import PrincipalBase
 
 from z3c.configurator import configurator
-from z3c.form import form, field, button
+from z3c.form import form, field, button, group
 from z3c.formui import layout
 
 import grok
@@ -36,6 +36,7 @@
 
 from tfws.website import interfaces
 from tfws.website import authentication
+from tfws.website.catalog import setup_catalog
 from tfws.website.layer import IWebsiteLayer
 from tfws.website.i18n import MessageFactory as _
 
@@ -44,6 +45,8 @@
 class WebSite(grok.Application, grok.Container):
     """Mars/Grok/Z3C demo website"""
     zope.interface.implements(interfaces.IWebSite)
+    grok.local_utility(IntIds, IIntIds) # needed for the catalog
+    grok.local_utility(Catalog, ICatalog, setup=setup_catalog)
 
     title = FieldProperty(interfaces.IWebSite['title'])
     description = FieldProperty(interfaces.IWebSite['description'])
@@ -66,12 +69,24 @@
     grok.context(Index)
     grok.template('templates/index.pt')
 
-class Edit(mars.form.FormView, layout.FormLayoutSupport, form.EditForm):
+class InitialManagerGroup(group.Group):
+    label = u'Initial Manager Account'
+    fields = field.Fields(interfaces.IWebSiteMember, prefix="member").select(
+        'member.login', 'member.password', 'member.firstName', 
+        'member.lastName', 'member.email')
+
+class SiteMetaDataGroup(group.Group):
+    label = u'Site Metadata'
+    fields = field.Fields(interfaces.IWebSite).select('title', 
+                                            'description', 'keyword')
+
+class Edit(mars.form.FormView, layout.FormLayoutSupport, 
+                               group.GroupForm, form.EditForm):
     """Edit form for site"""
     grok.name('edit')
     form.extends(form.EditForm)
     label = u'Tree Fern Web Site Edit Form'
-    fields = field.Fields(interfaces.IWebSite).omit('__parent__')
+    groups = (SiteMetaDataGroup,)
 
     @button.buttonAndHandler(u'Apply and View', name='applyView')
     def handleApplyView(self, action):
@@ -80,27 +95,34 @@
             url = absoluteURL(self.context, self.request)
             self.request.response.redirect(url)
 
-class Add(mars.form.FormView, layout.AddFormLayoutSupport, form.AddForm):
+
+class Add(mars.form.FormView, layout.AddFormLayoutSupport, 
+                              group.GroupForm, form.AddForm):
     """ Add form for tfws.website."""
     grok.name('add')
     grok.context(IRootFolder)
-    grok.local_utility(IntIds, IIntIds) # needed for the catalog
-    grok.local_utility(Catalog, ICatalog, setup=setup_catalog)
 
     label = _('Add a Tree Fern WebSite')
     contentName = None
     data = None
 
-    fields = field.Fields(zope.schema.TextLine(
-                                __name__='__name__',
-                                title=_(u"name"),
-                                required=True))
+    fields = field.Fields(zope.schema.TextLine(__name__='__name__',
+                                title=_(u"name"), required=True))
 
-    fields += field.Fields(interfaces.IWebSite).select('title')
-    fields += field.Fields(interfaces.IWebSiteMember, prefix="member").select(
-        'member.login', 'member.password', 'member.firstName', 
-        'member.lastName', 'member.email')
+    groups = (SiteMetaDataGroup, InitialManagerGroup)
 
+    @button.buttonAndHandler(_('Add'), name='add')
+    def handleAdd(self, action):
+        data, errors = self.extractData()
+        if errors:
+            self.status = self.formErrorsMessage
+            return
+        obj = self.create(data)
+        zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(obj))
+        result = self.add(obj)
+        if result is not None:
+            self._finishedAdd = True
+
     def create(self, data):
         self.data = data
         # get form data
@@ -115,14 +137,14 @@
         # Add the site
         if self.context.get(self.contentName) is not None:
             self.status = _('Site with name already exist.')
-            self._finished_add = False
+            self._finishedAdd = False
             return None
         self.context[self.contentName] = obj
 
         # Configure the new site
         configurator.configure(obj, data)
 
-        self._finished_add = True
+        self._finishedAdd = True
         return obj
 
     def nextURL(self):

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/ascending.gif
===================================================================
(Binary files differ)


Property changes on: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/ascending.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/buttonBG.gif
===================================================================
(Binary files differ)


Property changes on: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/buttonBG.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/decending.gif
===================================================================
(Binary files differ)


Property changes on: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/images/decending.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/skin.py
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/skin.py	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/skin.py	2007-07-23 05:06:32 UTC (rev 78286)
@@ -1,6 +1,10 @@
+import zope.component
 import zope.interface
 from zope.viewlet.viewlet import CSSViewlet
 from zope.publisher.interfaces.browser import IBrowserPage
+from zope.publisher.interfaces.http import IHTTPRequest
+from zope.traversing.browser import absoluteURL
+from zope.app.component import hooks
 
 import z3c.formui
 
@@ -25,6 +29,11 @@
     grok.context(IBrowserPage)
     grok.template('template.pt')
 
+class NavigationManager(mars.viewlet.ViewletManager):
+    """navigation column viewletmanager"""
+    grok.name('INavigationColumn')
+    grok.context(zope.interface.Interface)
+
 class CSSManager(mars.viewlet.ViewletManager):
     """css viewletmanager"""
     zope.interface.implements(z3c.formui.interfaces.ICSS)
@@ -52,3 +61,20 @@
     """image resource directory (++resource++images)"""
     mars.resource.directory('images')
 
+class ISiteURL(zope.interface.Interface):
+    pass
+
+class SiteURL(grok.MultiAdapter):
+    """Provides siteURL to all context"""
+    grok.name('siteURL')
+    grok.context(zope.interface.Interface)
+    zope.interface.implements(ISiteURL)
+    zope.component.adapts(zope.interface.Interface, IHTTPRequest)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def __call__(self):
+        return absoluteURL(hooks.getSite(), self.request)
+

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/template.pt
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/template.pt	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/template.pt	2007-07-23 05:06:32 UTC (rev 78286)
@@ -6,20 +6,49 @@
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 <meta http-equiv="cache-control" content="no-cache" />
 <meta http-equiv="pragma" content="no-cache" />
-<script type="text/javascript"
-    tal:define="contexturl context/@@absolute_url; 
-                viewurl request/URL"
-    tal:content="string:
-	var contextURL = '${contexturl}';
-	var viewURL = '${viewurl}';"> 
-</script>
 <script tal:replace="structure provider:IJavaScript"> </script>
 <style tal:replace="structure provider:ICSS"> 
 </style>
 </head>
-<body>
-<div id="content">
-  <tal:block replace="structure provider:pagelet" />
+<body tal:define="siteURL context/@@siteURL">
+<div id="layoutWrapper">
+  <div id="layoutContainer">
+    <div id="headerContainer">
+      <div id="topLine">
+      </div>
+    </div>
+    <div id="breadCrumbContainer">
+      <b i18n:translate="">You are here:</b>
+      <tal:block tal:repeat="crumb context/@@breadcrumbs/crumbs">
+        <a href=""
+            tal:content="string:${crumb/name}"
+            tal:attributes="href string:${crumb/url}"
+            >item</a>
+        <tal:block condition="not:repeat/crumb/end">&gt;</tal:block>
+      </tal:block>
+    </div>
+    <div id="mainContainer">
+      <div id="naviContainer">
+         <tal:block replace="structure provider:INavigationColumn">
+           tools
+         </tal:block>
+      </div>
+      <div id="contentContainer">
+        <div id="content">
+          <tal:block replace="structure provider:pagelet">
+            content
+          </tal:block>
+        </div>
+      </div>
+      <div class="clear" />
+      <div id="footerContainer">
+        <div class="footer">
+          &copy; Tree Fern Web Services ----
+          <a href="http://www.tfws.org.nz/mars" target="_blank">Mars Website</a>
+        </div>
+      </div>
+    </div>
+  </div>
 </div>
 </body>
 </html>

Modified: Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/website.css
===================================================================
--- Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/website.css	2007-07-23 04:21:03 UTC (rev 78285)
+++ Sandbox/darrylcousins/tfws.website/src/tfws/website/skin/website.css	2007-07-23 05:06:32 UTC (rev 78286)
@@ -1,86 +1,344 @@
 /* zrt-replace: "./images" tal"string:${context/++resource++images}" */
-
 body {
-    font: 11px Verdana, Helvetica, Arial, sans-serif;
+    font-family: Verdana, sans-serif;
+    font-size: 100%;
     margin: 0px;
     padding: 0px;
 }
 
 /* [ html ]---------------------------------------------------------------- */
 
+img {
+    border: none;
+}
+
+a:link {
+    color: #3257C2;
+    text-decoration: none;
+}
+
+a:visited {
+    color: #3257C2;
+    text-decoration: none;
+}
+
+a:hover {
+    color: #214285;
+}
+
+h1 {
+    font-size: 150%;
+    color: #3257C2;
+    font-weight: normal;
+}
+
+h2 {
+    font-size: 125%;
+    color: #3257C2;
+    font-weight: normal;
+}
+
+h3 {
+    font-size: 100%;
+    color: #3257C2;
+    font-weight: normal;
+}
+
+h4 {
+    font-size: 85%;
+    color: #3257C2;
+}
+
+h5 {
+    font-size: 75%;
+    color: #3257C2;
+}
+
+
+h6 {
+    font-size: 75%;
+    color: #3257C2;
+}
+
+
+
+/* [ forms ]---------------------------------------------------------------- */
+
 form {
     margin: 0px;
     padding: 0px;
 }
 
-img {
-    border: 0;
+fieldset {
+  margin-top: 15px;
+  padding: 10px 15px 5px 15px;
+  -moz-border-radius: 7px;
 }
 
-a {
-    color: #D91813;
-    text-decoration: none;
+fieldset legend {
+  color: #5E4A32;
+  font-variant: small-caps;
+  font-weight: bold;
+  font-size: 110%;
+  padding: 2px 4px;
+  -moz-border-radius: 3px;
 }
 
-a:link {
-    color: #D91813;
-    text-decoration: none;
+/* [ layout ]---------------------------------------------------------------- */
+
+div#layoutWrapper {
+    font-size: 80%;
+    text-align: center;
 }
 
-a:visited {
-    color: #A68E8E;
-    text-decoration: none;
+div#layoutContainer {
+    width: 941px;
+    text-align: left;
+    margin-left: auto; 
+    margin-right: auto;
+    padding: 0px;
 }
 
-a:hover {
-    color: #8C100D;
+
+/* [ header ]-------------------------------------------------------------- */
+
+div#headerContainer {
+    width: 941px;
+    text-align: left;
+    position: relative;
+    margin: 0px;
+    padding: 0px;
+    background-color: #E1E1E1;
 }
 
-fieldset {
+#topLine {
+    height: 5px;
+    background-color: #3257C2;
+}
+
+img#logo {
+    position: absolute;
+    top: 40px;
+    left: 10px;
+    padding: 0px 0px 20px 10px;
+}
+
+div#user {
+    top: 10px;
+    right: 10px;
+    position: absolute;
+    color: white;
+    padding: 0px 10px 0px 0px;
+}
+
+div#user a {
+    font-size: 80%;
+}
+
+/*---[ main ]-----------------------------------------------------------------*/
+
+div#mainContainer {
+    padding: 10px 0px 10px 0px;
+}
+
+/*---[ navigation ]-----------------------------------------------------------*/
+
+#naviContainer {
+	width: 140px;
+	float: left;
+    background-color: white;
+	padding: 25px 20px 0px 0px;
+}
+
+#naviContainer a {
+    color: #000000;
+}
+
+div.naviBox {
     padding: 5px;
+    margin: 0px;
+    background-color: #F2F2F2;
+/*    background: url("./img/naviBG.jpg") repeat-y left top;*/
 }
 
+div.naviBox .naviBoxHeader {
+    height: 40px;
+    line-height: 40px;
+    color: #3257C2;
+    vertical-align: top;
+}
 
-h1, h2, h3, h4, h5, h6 {
-    color: Black;
-    clear: left;
-    font: 100% bold Verdana, Helvetica, Arial, sans-serif;
-    margin: 0;
-    padding-top: 0.5em;
+div.naviBox .naviBoxHeader img {
+    vertical-align: middle;
+    padding: 5px 10px 5px 0px;
 }
 
-h1 {
-    font-size: 160%;
+
+div.naviBox .naviBoxBody {
+	font-size: 90%;
+    color: #000000;
 }
 
-h2 {
-    font-size: 140%;
+
+div.naviBox .naviBoxBody div {
+	padding-bottom: 5px;
 }
 
-h3 {
-    height: 20px;
-    font-size: 120%;
-    background-color: #DDDCD0;
-    padding: 2px 0px 0px 5px;
+div.boxSpacer {
+    height: 10px;
 }
 
-h4 {
-    color: #777777;
-    font-size: 100%;
+div.naviBox #searchForm input {
+    vertical-align: middle;
+}
+
+
+/* [ breadcrumbs ]----------------------------------------------------------- */
+
+div#breadCrumbContainer {
+    height: 24px;
+    line-height: 24px;
+    color: #666666;
+    font-size: 11px;
+    padding-left: 20px;
+}
+
+#breadCrumbContainer a {
+    color: #002B8B;
     font-weight: bold;
+    text-decoration: none;
 }
 
-h5 {
-    font-size: 90%;
+#breadCrumbContainer a:hover {
+    font-weight: bold;
+    text-decoration: underline;
 }
 
-h6 {
-    font-size: 80%;
+#breadCrumbContainer a.selected {
+    font-weight: bold;
 }
 
 
-/* ---[ website layout tags ]--------------------------------------------------- */
+/*---[ content ]-------------------------------------------------------------*/
+#contentContainer {
+	float: left;
+	width: 670px;
+    margin: 0;
+    padding: 10px 10px 0px 20px;
+}
 
 #content {
-    padding: 20px;
+    padding: 15px 0px 0px 0px;
 }
+
+#content div.headline {
+    font-size: 175%;
+    color: #214285;
+    padding: 0px;
+}
+
+#content div.body {
+    padding-top: 10px;
+}
+
+/*---[ tool ]----------------------------------------------------------------*/
+
+.button, .button-field {
+  cursor: pointer;
+  border: outset 1px #ccc;
+  background: #999999;
+  color: #666666;
+  font-weight: bold;
+  padding: 1px 2px;
+  background: url(./images/buttonBG.gif) repeat-x left top;
+}
+
+.clear {
+    height: 1px;
+    clear: both;
+    margin: 0px;
+    padding: 0px;
+}
+
+/*---[ footer ]--------------------------------------------------------------*/
+
+#footerContainer {
+    padding-top: 20px;
+	text-align: center;
+}
+
+#footerContainer .footer {
+    width: 900px;
+	font-family: Arial, Helvetica, sans-serif;
+	font-size: 10px;
+	color: #999999;
+    padding-top: 20px;
+}
+
+#footerContainer a {
+	color: #999999;
+}
+
+/*---[ table ]---------------------------------------------------------------*/
+
+table.list {
+    margin: 1em 0;
+    padding: 0;
+    border: 0;
+    border-collapse: collapse;
+}
+
+table.list tr {
+    border-bottom: 1px solid #ffffff;
+}
+
+table.list tr.even {
+    background-color: #E6E1D9;
+}
+
+table.list tr.even td.sorted-on {
+    background-color: #D5CCBD;
+}
+
+table.list tr.odd {
+    background-color: #F2F0EC;
+}
+
+table.list tr.odd td.sorted-on {
+    background-color: #DFD9CE;
+}
+
+table.list tr.selected {
+    background-color: #BCAE98;
+}
+
+table.list tr.selected td.sorted-on {
+    background-color: #AE9E84;
+}
+
+table.list th {
+    background-color: #E6E1D9;
+    border-color: #ffffff;
+    border-bottom-width: 1px;
+    border-bottom-style: solid;
+    padding: 3px 4px;
+    text-align: left;
+}
+
+table.list th.sorted-on {
+    background-color: #D5CCBD;
+}
+
+table.list td {
+    vertical-align: top;
+    padding: 3px 4px;
+}
+
+table.list .centered {
+    text-align: center;
+}
+
+table.list .right {
+    text-align: right;
+}
+



More information about the Checkins mailing list