<?xml version="1.0" encoding="UTF-8" ?>
<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><Project><name>z3ext.formatter</name>
<shortdesc>Extensible TALES fomratter expression.</shortdesc>
<description>Detailed Dcoumentation
======================


===============
z3ext.formatter
===============

This package adds extensible tales expression for various formatters.
You can change formatter setting per site basis (z3ext.controlpanel).

For configure default settings, add following code to zope.conf

&lt;product-config z3ext.formatter&gt;
  timezone UTC
  timezoneFormat 2
  principalTimezone true
&lt;/product-config&gt;

Values for timezoneFormat are:
    1: No timezone
    2: Number of hours
    3: Timezone name

We need register controlpanel configlet

  &gt;&gt;&gt; from zope.configuration import xmlconfig
  &gt;&gt;&gt; context = xmlconfig.string("""
  ... &lt;configure xmlns:z3ext="http://namespaces.zope.org/z3ext"
  ...      i18n_domain="z3ext.formatter"&gt;
  ...    &lt;include package="z3ext.controlpanel" file="meta.zcml" /&gt;
  ...  &lt;z3ext:configlet
  ...     name="formatter"
  ...     schema="z3ext.formatter.interfaces.IFormatterConfiglet"
  ...     title="Portal formatters"
  ...     description="Configure portal formatters."/&gt;
  ... &lt;/configure&gt;""")

We'll try emulate &lt;product-config z3ext.formatter&gt;

  &gt;&gt;&gt; from zope.app.appsetup import product
  &gt;&gt;&gt; product._configs['z3ext.formatter'] = {
  ...   'timezone': u'UTC', 'timezoneFormat': '2', 'principalTimezone': 'true'}

Let's check this

   &gt;&gt;&gt; product.getProductConfiguration('z3ext.formatter')
   {'timezone': u'UTC', 'timezoneFormat': '2', 'principalTimezone': 'true'}


Usually initFormatter() function is colled during IDatabaseOpenedEvent event,
we simply call it directly:

   &gt;&gt;&gt; from z3ext.formatter.config import initFormatter
   &gt;&gt;&gt; initFormatter(None)

Now we can get IFormatterConfiglet utility

   &gt;&gt;&gt; from zope.component import getUtility
   &gt;&gt;&gt; from z3ext.formatter.interfaces import IFormatterConfiglet

   &gt;&gt;&gt; configlet = getUtility(IFormatterConfiglet)

Setup request

   &gt;&gt;&gt; from zope import interface
   &gt;&gt;&gt; from zope.publisher.browser import TestRequest
   &gt;&gt;&gt; from zope.annotation.interfaces import IAttributeAnnotatable

   &gt;&gt;&gt; request = TestRequest(environ={'HTTP_ACCEPT_LANGUAGE': 'en'})
   &gt;&gt;&gt; interface.directlyProvides(request, IAttributeAnnotatable)

   &gt;&gt;&gt; from pytz import UTC
   &gt;&gt;&gt; from datetime import date, datetime, timedelta


DateTime formatter
------------------

   &gt;&gt;&gt; from z3ext.formatter.tests import ZPTPage
   &gt;&gt;&gt; page = ZPTPage()
   &gt;&gt;&gt; page.pt_edit(u'''
   ... &lt;html&gt;
   ...   &lt;body&gt;
   ...     &lt;tal:block tal:content="formatter:dateTime,short:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:dateTime,medium:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:dateTime,long:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:dateTime,full:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:dateTime:options/now" /&gt;
   ...   &lt;/body&gt;
   ... &lt;/html&gt;''', 'text/html')

   &gt;&gt;&gt; dt = datetime(2007, 1, 1, 0, 0, 0, tzinfo=UTC)
   &gt;&gt;&gt; dt
   datetime.datetime(2007, 1, 1, 0, 0, tzinfo=&lt;UTC&gt;)

By default we use UTC timezone for output:

   &gt;&gt;&gt; print page.render(request, now=dt)
   &lt;html&gt;
     &lt;body&gt;
       1/1/07 12:00 AM
       Jan 1, 2007 12:00:00 AM
       January 1, 2007 12:00:00 AM +000
       Monday, January 1, 2007 12:00:00 AM +000
       Jan 1, 2007 12:00:00 AM
     &lt;/body&gt;
   &lt;/html&gt;


If datetime object doesn't contain timezone information, UTC is used

   &gt;&gt;&gt; print page.render(request, now=datetime(2007, 1, 1, 0, 0))
   &lt;html&gt;
     &lt;body&gt;
       1/1/07 12:00 AM
       Jan 1, 2007 12:00:00 AM
       January 1, 2007 12:00:00 AM +000
       Monday, January 1, 2007 12:00:00 AM +000
       Jan 1, 2007 12:00:00 AM
     &lt;/body&gt;
   &lt;/html&gt;


Now let's chane timezone to US/Pacific, we change only time zone 
not datetime value

   &gt;&gt;&gt; configlet.timezone = 'US/Pacific'

   &gt;&gt;&gt; print page.render(request, now=dt)
   &lt;html&gt;
     &lt;body&gt;
       12/31/06 4:00 PM
       Dec 31, 2006 4:00:00 PM
       December 31, 2006 4:00:00 PM -800
       Sunday, December 31, 2006 4:00:00 PM -800
       Dec 31, 2006 4:00:00 PM
     &lt;/body&gt;
   &lt;/html&gt;


Now we can change timezone format to 3 (Timezone name)

   &gt;&gt;&gt; configlet.timezoneFormat = 3
   
   &gt;&gt;&gt; print page.render(request, now=dt)
   &lt;html&gt;
     &lt;body&gt;
       12/31/06 4:00 PM
       Dec 31, 2006 4:00:00 PM US/Pacific
       December 31, 2006 4:00:00 PM -800
       Sunday, December 31, 2006 4:00:00 PM US/Pacific
       Dec 31, 2006 4:00:00 PM US/Pacific
     &lt;/body&gt;
   &lt;/html&gt;


We also can redefine timezone for principal if we use principalTimezone true

   &gt;&gt;&gt; from pytz import timezone
   &gt;&gt;&gt; from zope import interface, component
   &gt;&gt;&gt; class IPrincipal(interface.Interface):
   ...   pass

   &gt;&gt;&gt; class Principal:
   ...     interface.implements(IPrincipal)
   ...
   ...     def __init__(self, id):
   ...         self.id = id
   ...         self.groups = []

   &gt;&gt;&gt; @component.adapter(IPrincipal)
   ... @interface.implementer(interface.common.idatetime.ITZInfo)
   ... def getTimezone(prin):
   ...   if prin.id == 'user1':
   ...     return timezone('Europe/Paris')
   ...   elif prin.id == 'user2':
   ...     return timezone('Asia/Almaty')
   &gt;&gt;&gt; component.provideAdapter(getTimezone)

   &gt;&gt;&gt; request.setPrincipal(Principal('user1'))
   &gt;&gt;&gt; print page.render(request, now=dt)
   &lt;html&gt;
     &lt;body&gt;
       1/1/07 1:00 AM
       Jan 1, 2007 1:00:00 AM Europe/Paris
       January 1, 2007 1:00:00 AM +100
       Monday, January 1, 2007 1:00:00 AM Europe/Paris
       Jan 1, 2007 1:00:00 AM Europe/Paris
     &lt;/body&gt;
   &lt;/html&gt;

   &gt;&gt;&gt; request.setPrincipal(Principal('user2'))
   &gt;&gt;&gt; print page.render(request, now=dt)
   &lt;html&gt;
     &lt;body&gt;
       1/1/07 6:00 AM
       Jan 1, 2007 6:00:00 AM Asia/Almaty
       January 1, 2007 6:00:00 AM +600
       Monday, January 1, 2007 6:00:00 AM Asia/Almaty
       Jan 1, 2007 6:00:00 AM Asia/Almaty
     &lt;/body&gt;
   &lt;/html&gt;

   &gt;&gt;&gt; request.setPrincipal(None)


fancyDatetime formatter
-----------------------

   &gt;&gt;&gt; now = datetime.now(UTC)

   &gt;&gt;&gt; fpage = ZPTPage()
   &gt;&gt;&gt; fpage.pt_edit(u'''
   ... &lt;html&gt;
   ...   &lt;body&gt;
   ...     &lt;tal:block tal:content="formatter:fancyDatetime:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:fancyDatetime,short:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:fancyDatetime,medium:options/now" /&gt;
   ...     &lt;tal:block tal:content="formatter:fancyDatetime,full:options/now" /&gt;
   ...   &lt;/body&gt;
   ... &lt;/html&gt;''', 'text/html')

Today's datetime

   &gt;&gt;&gt; today = now - timedelta(hours=1)

   &gt;&gt;&gt; print fpage.render(request, now=today)
   &lt;html&gt;
     &lt;body&gt;
       Today at ...
       Today at ...
       Today at ...
       Today at ...
     &lt;/body&gt;
   &lt;/html&gt;


Yesterday's datetime

   &gt;&gt;&gt; yesterday = now - timedelta(hours=25)

   &gt;&gt;&gt; print fpage.render(request, now=yesterday)
   &lt;html&gt;
     &lt;body&gt;
       Yesterday at ...
       Yesterday at ...
       Yesterday at ...
       Yesterday at ...
     &lt;/body&gt;
   &lt;/html&gt;


Default timezone is UTC

   &gt;&gt;&gt; now = datetime.now(UTC)
   &gt;&gt;&gt; print fpage.render(request, now=now)
   &lt;html&gt;
     &lt;body&gt;
       Today at ...
       Today at ...
       Today at ...
       Today at ...
     &lt;/body&gt;
   &lt;/html&gt;


Date formatter
--------------

   &gt;&gt;&gt; datepage = ZPTPage()
   &gt;&gt;&gt; datepage.pt_edit(u'''
   ... &lt;html&gt;
   ...   &lt;body&gt;
   ...     &lt;tal:block tal:content="formatter:date:options/today" /&gt;
   ...     &lt;tal:block tal:content="formatter:date,short:options/today" /&gt;
   ...   &lt;/body&gt;
   ... &lt;/html&gt;''', 'text/html')

   &gt;&gt;&gt; d = date(2007, 1, 1)
   &gt;&gt;&gt; d
   datetime.date(2007, 1, 1)

   &gt;&gt;&gt; print datepage.render(request, today=d)
   &lt;html&gt;
     &lt;body&gt;
       Jan 1, 2007
       1/1/07
     &lt;/body&gt;
   &lt;/html&gt;


Also you can get formatter from python code

   &gt;&gt;&gt; from z3ext.formatter.utils import getFormatter
   &gt;&gt;&gt; formatter = getFormatter(request, 'dateTime', 'full')
   &gt;&gt;&gt; formatter.format(dt)
   u'Sunday, December 31, 2006 4:00:00 PM US/Pacific'

We will get FormatterNotDefined if formatter is unknown

   &gt;&gt;&gt; getFormatter(request, 'unknown')
   Traceback (most recent call last):
   ...
   FormatterNotDefined: ...

Wrong format, we should add path expression

   &gt;&gt;&gt; errpage = ZPTPage()
   &gt;&gt;&gt; errpage.pt_edit(u'''
   ...     &lt;tal:block tal:content="formatter:unknown" /&gt;''', 'text/html')
   &gt;&gt;&gt; print errpage.render(request)
   Traceback (most recent call last):
   ...
   PTRuntimeError: ...

Unknown formatter

   &gt;&gt;&gt; errpage = ZPTPage()
   &gt;&gt;&gt; errpage.pt_edit(u'''
   ...     &lt;tal:block tal:content="formatter:unknown:opitons/now" /&gt;''', 'text/html')
   &gt;&gt;&gt; print errpage.render(request)
   Traceback (most recent call last):
   ...
   FormatterNotDefined: unknown


Custom formatter
================

We should define formatter factory and formatter itself
Let's implement formatter that accept string and currency name and 
format as currency. Format of TALES expression whould be as 
'formatter:&lt;formatter name&gt;,&lt;formatter var1&gt;,&lt;formatter var2&gt;,...:&lt;path expression&gt;'
&lt;formatter name&gt; is name of adapter that adapts IHTTPRequest to IFormatterFactory
also expression will pass &lt;formatter var[1-...]&gt; as args to factory.

   &gt;&gt;&gt; from z3ext.formatter.interfaces import IFormatter, IFormatterFactory

Here code of formatter:

   &gt;&gt;&gt; class MyFormatter(object):
   ...    interface.implements(IFormatter)
   ...
   ...    currencies = {'usd': '$', 'euro': 'Eur'}
   ...
   ...    def __init__(self, request, *args):
   ...       self.request = request
   ...       self.currency = self.currencies[args[0]]
   ...
   ...    def format(self, value):
   ...       return '%s %s'%(value, self.currency)

Now we need formatter factory:

   &gt;&gt;&gt; class MyFormatterFactory(object):
   ...    interface.implements(IFormatterFactory)
   ...
   ...    def __init__(self, request):
   ...        self.request = request
   ...
   ...    def __call__(self, *args, **kw):
   ...        return MyFormatter(self.request, *args)

Now we need register factory as named adapter for IHTTPRequest

   &gt;&gt;&gt; from zope.component import provideAdapter
   &gt;&gt;&gt; from zope.publisher.interfaces.http import IHTTPRequest

   &gt;&gt;&gt; provideAdapter(MyFormatterFactory, \
   ...   (IHTTPRequest,), IFormatterFactory, name='currency')

Now we can use formatter

   &gt;&gt;&gt; page = ZPTPage()
   &gt;&gt;&gt; page.pt_edit(u'''&lt;tal:block tal:define="value python:121.04"&gt;
   ... &lt;tal:block tal:content="formatter:currency,usd:value" /&gt;
   ... &lt;tal:block tal:content="formatter:currency,euro:value" /&gt;
   ... &lt;/tal:block&gt;''', 'text/html')

   &gt;&gt;&gt; print page.render(request)
   121.04 $
   121.04 Eur


=======
CHANGES
=======

1.3.0 (2009-07-05)
------------------

- Added chameleon support

- Added 'formatter' chameleon expression


1.2.7 (2009-04-??)
------------------

- Do not use z3c.autoinclude


1.2.6 (2009-03-11)
------------------

- Added 'human' datetime formatter (XX minute(s) ago)

- Do not show seconds for fancyDatetime fomratter

- Fixed multple usage of one fancyDatetime formatter


1.2.5 (2008-11-23)
------------------

- z3ext.controlpanel is optional

- Added buildout for testing against zope3.4

1.2.4 (2008-10-21)
------------------

- Fixed russian translation

- nl translation updated


1.2.3 (2008-10-20)
------------------

- Added translations: nl, ru


1.2.2 (2008-10-10)
------------------

- Use new i18n domain z3ext.formatter


1.2.1 (2008-05-16)
------------------

- Replace 'autoinclude' with 'includeDependendcies'


1.2.0 (2008-03-25)
------------------

- Use z3c.autoinclude

- Code moved to svn.zope.org


1.1.2 (2008-03-06)
------------------

- Fixed bug in fancyDatetime formatter


1.1.1 (2008-02-05)
------------------

- Added 'Timezones' vocabulary

- Cleanup code


1.1.0 (2007-12-21)
------------------

- Added controlpanel configlet


1.0.0 (2007-12-07)
------------------

- Initial release.</description>
<homepage rdf:resource="http://z3ext.net/" />
<maintainer><foaf:Person><foaf:name>Nikolay Kim</foaf:name>
<foaf:mbox_sha1sum>d2ae7e827893a4b491ceed859306c037152b761c</foaf:mbox_sha1sum></foaf:Person></maintainer>
<release><Version><revision>1.3.0</revision></Version></release>
</Project></rdf:RDF>