Commit dc7b6e2e authored by Jean-Paul Smets's avatar Jean-Paul Smets

Initial revision


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@2 20353a03-c40f-0410-a6d1-a30d3c3de9de
parents

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
__version__ = "$Revision$"[11:-2]
__doc__ = "This product provides the basic behaviour to CMF object which need\
translation"
from OFS.Folder import Folder
from Products.CMFCore import CMFCorePermissions
from Products.CMFCore.PortalContent import PortalContent
from Products.CMFCore.PortalFolder import PortalFolder
from AccessControl import ClassSecurityInfo
from Products.Localizer.Utils import lang_negotiator
from utils import _translate_stx
from zLOG import LOG
class Base18(PortalContent):
""" Base18 implements the basic translation tactics"""
# Declarative security
security = ClassSecurityInfo()
### Delegate to central repository
def findMessageCatalog(self):
return self.portal_translations.findMessageCatalog(self)
### Common translation methods
security.declareProtected(CMFCorePermissions.View, 'TranslatedDescription')
def TranslatedDescription(self):
"""
Returns translated desc
"""
return self.findMessageCatalog().gettext(self.Description())
security.declareProtected(CMFCorePermissions.View, 'getTranslatedDescription')
getTranslatedDescription = TranslatedDescription
security.declareProtected(CMFCorePermissions.View, 'TranslatedTitle')
def TranslatedTitle(self):
"""
Returns a translated title
"""
#LOG('TranslatedTitle',0,str(self))
return self.findMessageCatalog().gettext(self.Title())
security.declareProtected(CMFCorePermissions.View, 'getTranslatedTitle')
getTranslatedTitle = TranslatedTitle
security.declareProtected(CMFCorePermissions.View, 'TranslatedTitle_or_id')
def TranslatedTitle_or_id(self):
"""
Returns a translated title if title exists
or id otherwise
"""
title=self.title
if callable(title):
title=title()
if title: return self.findMessageCatalog().gettext(title)
return self.getId()
security.declareProtected(CMFCorePermissions.View, 'getTranslatedTitle_or_id')
getTranslatedTitle_or_id = TranslatedTitle_or_id
security.declareProtected(CMFCorePermissions.View, 'getNegotiatedLanguage')
def getNegotiatedLanguage(self,md=None):
if md is None: md = self.findMessageCatalog()
available_languages = list(md._languages)
# Get the language!
lang = lang_negotiator(available_languages)
# Is it None? use the default
if lang is None:
lang = md._default_language
return lang
### Generic Translation methods
security.declareProtected(CMFCorePermissions.View, 'TranslatedBody')
def TranslatedBody(self, stx_level=None, setlevel=0, lang=None, md=None):
"""\
Equivalent to CookedBody but returns a translated version thanks
to the use of message catalog
"""
if md is None: md = self.findMessageCatalog()
if stx_level is None:
stx_level = self._stx_level
cooked = _translate_stx(self.text, md, stx_level, lang)
return cooked
security.declareProtected(CMFCorePermissions.View, 'getTranslatedBody')
getTranslatedBody = TranslatedBody
# Catalog Related Method (required for ERP5)
def getObject(self, REQUEST=None):
"""Returns self - useful for ListBox"""
return self
# For Compatibility Purpose
PortalContent.getTranslatedTitle = Base18.TranslatedTitle
PortalContent.TranslatedTitle = Base18.TranslatedTitle
PortalContent.getTranslatedTitle_or_id = Base18.TranslatedTitle_or_id
PortalContent.TranslatedTitle_or_id = Base18.TranslatedTitle_or_id
PortalFolder.getTranslatedTitle = PortalContent.title_or_id
PortalFolder.TranslatedTitle = PortalContent.title_or_id
PortalFolder.getTranslatedTitle_or_id = PortalFolder.title_or_id
PortalFolder.TranslatedTitle_or_id = PortalFolder.title_or_id
Folder.getTranslatedTitle = Folder.title_or_id
Folder.TranslatedTitle = Folder.title_or_id
Folder.getTranslatedTitle_or_id = Folder.title_or_id
Folder.TranslatedTitle_or_id = Folder.title_or_id
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the Zope Public License (ZPL) Version 2.0
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
##############################################################################
from Products.CMFCore.CookieCrumbler import CookieCrumbler
from urllib import quote
from AccessControl import ClassSecurityInfo
class Base18CookieCrumbler(CookieCrumbler):
# Dynamic Patch Class
# Declarative security
security = ClassSecurityInfo()
security.declarePublic('getLoginURL')
def getLoginURL(self):
"""
Redirects to the login page.
XXX Take from CookieCrumbler -> license should be ZPL
"""
if self.auto_login_page:
req = self.REQUEST
resp = req['RESPONSE']
iself = getattr(self, 'aq_inner', self)
parent = getattr(iself, 'aq_parent', None)
page = getattr(parent, self.auto_login_page, None)
if page is not None:
retry = getattr(resp, '_auth', 0) and '1' or ''
came_from = req.get('came_from', None)
if came_from is None:
came_from = req['URL']
came_from_list = list(came_from.split('/'))
# Find the object where we came form
# And forget about trailing URL
try:
came_from_object = parent.restrictedTraverse(came_from_list[3:-1])
url = '%s?came_from=%s&retry=%s&disable_cookie_login__=1' % (
came_from_object.local_absolute_url(target=page), quote(came_from), retry)
return url
except:
try:
url = '%s?came_from=%s&retry=%s&disable_cookie_login__=1' % (
self.local_absolute_url(), quote(came_from), retry)
#self.local_absolute_url(target=page), quote(came_from), retry)
except:
relative_url = self.portal_url.getRelativeUrl(self)
absolute_url=self.portal_url.getPortalObject().absolute_url()
local_absolute_url='%s/%s' % (absolute_url,relative_url)
url = '%s?came_from=%s&retry=%s&disable_cookie_login__=1' % (
local_absolute_url, quote(came_from), retry)
return url
return None
# Dynamic Patch
CookieCrumbler.getLoginURL = Base18CookieCrumbler.getLoginURL
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
__version__ = "$Revision$"[11:-2]
__doc__ = "This product provides multilingual capabilities to the CMFDefault \
Document"
from utils import _translate_stx, _translate_html
from Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo
from Products.CMFCore import CMFCorePermissions
from Products.Localizer.MessageCatalog import MessageCatalog
from Products.CMFDefault.Document import Document
from Base18 import Base18
#import zLOG
# Content Creator
def addDocument18(self, id, title='', description='', text_format='',
text=''):
""" Add a Multilingual Document """
o = Document18(id, title, description, text_format, text)
self._setObject(id,o)
# Content Class
class Document18(Document,Base18):
""" A Multilingual Document - Handles both StructuredText and HTML
and translates sentences through a portal_translations tool
"""
meta_type = 'Base18 Document'
portal_type = 'Document'
isPortalContent = 1
# Default values
cached_translations = None
# Declarative security
security = ClassSecurityInfo()
# CMF Factory Type Information
factory_type_information = ( { 'id' : portal_type
, 'meta_type' : meta_type
, 'description' : """\
Document can contain text that can be formatted using 'Structured Text.\
Text can be automatically translated through the use of message catalogs.'"""
, 'icon' : 'document_icon.gif'
, 'product' : 'Base18'
, 'factory' : 'addDocument18'
, 'immediate_view' : 'metadata_edit_form'
, 'actions' :
( { 'id' : 'view'
, 'name' : 'View'
, 'action' : 'document18_view'
, 'permissions' : (
CMFCorePermissions.View, )
}
, { 'id' : 'print'
, 'name' : 'Print'
, 'action' : 'document18_print'
, 'permissions' : (
CMFCorePermissions.View, )
}
, { 'id' : 'edit'
, 'name' : 'Edit'
, 'action' : 'document_edit_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'metadata'
, 'name' : 'Metadata'
, 'action' : 'metadata_edit_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'translate'
, 'name' : 'Translate'
, 'action' : 'translation_template'
, 'permissions' : (
CMFCorePermissions.View, )
}
)
}
,
)
### Content accessor methods
security.declareProtected(CMFCorePermissions.View, 'SearchableText')
def SearchableText(self, md=None):
"""\
Used by the catalog for basic full text indexing
We are going to concatenate all available translations
"""
if md is None: md = self.findMessageCatalog()
searchable_text = ""
for lang in md.get_languages():
searchable_text = searchable_text + "%s %s %s" % (
md.gettext(self.Title(),lang)
, md.gettext(self.Description(),lang)
, self.TranslatedBody(lang=lang)
)
return searchable_text
# Source Text access
security.declareProtected(CMFCorePermissions.View, 'getText')
def getText(self):
"""
Returns source text
"""
return self.text
# Specific Translation methods
security.declareProtected(CMFCorePermissions.View, 'TranslatedBody')
def TranslatedBody(self, stx_level=None, setlevel=0, lang=None, md=None):
"""\
Equivalent to CookedBody but returns a translated version thanks
to the use of message catalog
"""
if md is None: md = self.findMessageCatalog()
if stx_level is None: stx_level = self._stx_level
if lang is None: lang = self.getNegotiatedLanguage(md)
#zLOG.LOG('Language',0,lang)
# Create a translation cache if necessary
if self.cached_translations is None:
self.cached_translations = PersistentMapping()
if not self.cached_translations.has_key(lang) or \
stx_level != self._stx_level:
if self.text_format == 'html':
cooked = _translate_html(self.text, md, stx_level, lang)
else:
cooked = _translate_stx(self.text, md, stx_level, lang)
self.cached_translations[lang] = \
(md.bobobase_modification_time(),cooked)
elif self.cached_translations[lang][0] != \
md.bobobase_modification_time() or \
self.cached_translations[lang][1] != \
self.bobobase_modification_time():
if self.text_format == 'html':
cooked = _translate_html(self.text, md, stx_level, lang)
else:
cooked = _translate_stx(self.text, md, stx_level, lang)
self.cached_translations[lang] = (md.bobobase_modification_time(),
self.bobobase_modification_time(),cooked)
else:
cooked = self.cached_translations[lang][2]
return cooked
security.declareProtected(CMFCorePermissions.View, 'TranslationTemplate')
def TranslationTemplate(self):
"""\
This method allows to produce of .pot file for this document
"""
# Create a new catalog
md = MessageCatalog('temp', 'Temporary Message Catalog',
self.findMessageCatalog().get_languages())
# Do some dirty acquisition trick
md.aq_base = self
# Run the md on the text body, title and description
cooked = _translate_stx(self.text, md, self._stx_level, None)
md.gettext(self.Title())
md.gettext(self.Description())
# And return the template
return md.manage_export('locale.pt')
InitializeClass(Document18)
Document = Document18
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the Zope Public License (ZPL) Version 2.0
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Converts instances of Products.CMFDefault.<content> to
instances of Products.Base18.<content>.
"""
# Source
import Products.CMFDefault.NewsItem
import Products.CMFDefault.Link
import Products.CMFDefault.Document
import Products.CMFDefault.File
import Products.CMFDefault.Image
import Products.CMFDefault.DiscussionItem
import Products.CMFDefault.Favorite
import Products.CMFCore.PortalFolder
# Destination
import Products.Base18.NewsItem
import Products.Base18.Replica
import Products.Base18.Document
#
import re
from zLOG import LOG
migrations = {
Products.CMFDefault.Document.Document :\
Products.Base18.Document.Document18,
Products.CMFDefault.NewsItem.NewsItem :\
Products.Base18.NewsItem.NewsItem18,
Products.CMFDefault.Link.Link :\
Products.Base18.Replica.Link18,
Products.CMFDefault.File.File :\
Products.Base18.Replica.File18,
Products.CMFDefault.Image.Image :\
Products.Base18.Replica.Image18,
Products.CMFDefault.DiscussionItem.DiscussionItem :\
Products.Base18.Replica.DiscussionItem18,
Products.CMFDefault.Favorite.Favorite :\
Products.Base18.Replica.Favorite18,
Products.CMFCore.PortalFolder.PortalFolder :\
Products.Base18.Replica.SkinnedFolder18,
Products.CMFWiki.CMFWikiPage.CMFWikiPage :\
Products.Base18.Wiki.CMFWikiPage18,
Products.CMFWiki.CMFWikiPage.CMFWikiFolder :\
Products.Base18.Wiki.CMFWikiFolder18
}
def update_folder(obj):
"""
Folder needs to be updated in order to take into account
changes of classes and in particular meta_type
"""
obase = getattr(obj, 'aq_base', obj)
new_objects =[]
if not obase.__dict__.has_key('_objects'):
return
for dict in obase.__dict__['_objects']:
new_objects.append({'id':dict['id'],
'meta_type': obj._getOb(dict['id']).meta_type})
obase.__dict__['_objects'] = tuple(new_objects)
def migrate_branches(migrations, branch, migrated, visited):
base = getattr(branch, 'aq_base', branch)
if base in visited:
# Don't visit again!
return
visited.append(base)
try: changed = branch._p_changed
except: changed = 1
for id in branch.objectIds():
obj = branch._getOb(id)
LOG("Here",0,id)
obase = getattr(obj, 'aq_base', obj)
klass = obase.__class__
if migrations.has_key(klass):
# Replace this object.
changed = 1
try:
newob = migrations[klass](obase.id)
newob.id = obase.id # This line activates obase.
except:
newob = migrations[klass](id)
newob.id = id
keys = obase.__dict__.keys()
for k in keys:
if k is not 'id':
migrated.append(" %s" % k)
setattr(newob,k,obase.__dict__[k])
#obase = getattr(newob, 'aq_base', obj)
#obase.__dict__.update(obase.__dict__)
#newob.portal_type =
# migrations[klass].factory_type_information[0].id
setattr(branch, id, newob)
#obj.__class__ = migrations[klass]
migrated.append(obj.absolute_url())
# Keep on processing if necessary
obj = branch._getOb(id)
obase = getattr(obj, 'aq_base', obj)
if hasattr(obase, 'objectIds') and \
re.search('Folder',obase.meta_type) is not None:
# Enter a sub-branch.
migrate_branches(migrations, obj, migrated, visited)
# Update meta_type
update_folder(obj)
elif hasattr(obase, 'objectIds') and \
re.search('Folder',obase.meta_type) is not None:
# Enter a sub-branch.
migrate_branches(migrations, obj, migrated, visited)
# Update meta_type
update_folder(obj)
else:
# Unload this object if it has not been changed.
try:
if obj._p_changed is None:
obj._p_deactivate()
except: pass
if changed is None:
# Unload this branch.
object._p_deactivate()
del visited[-1] # ????
# Main Script
def main(REQUEST):
container = REQUEST.PARENTS[0]
pt = container.portal_types
ps = container.portal_skins
# Add FTI
ptlist = ['Document','Image','File','Link','News Item','Folder',
'Discussion Item','Favorite','Translation','Wiki', 'Wiki Page']
for oid in ptlist:
try: pt.manage_delObjects((oid,))
except: pass
pt.manage_addTypeInformation(id=oid,typeinfo_name="Base18: Base18 %s"
% oid)
# Add Skin
try:
from Products.CMFCore.DirectoryView import createDirectoryView
createDirectoryView(ps,'Products/Base18/skins/content18',
id='content18')
except: pass
# Add Translation and Membership Tools
try: container.manage_delObjects(('portal_membership',))
except: pass
addBase18Tool = container.manage_addProduct['Base18'].manage_addTool
try: addBase18Tool('Base18 Translation Tool', None)
except: pass
try: addBase18Tool('Base18 Membership Tool', None)
except: pass
# Eventually keep folder on preferences here
# Add Message Catalog
from Products.Localizer.MessageCatalog import manage_addMessageCatalog
try: manage_addMessageCatalog(container, 'gettext', '',('en',))
except: pass
# Update Content
visited = []
migrated = []
migrate_branches(migrations, container, migrated, visited)
update_folder(container)
from string import join
return 'Converted:\n%s\n\nDone.' % join(migrated, '\n')
# Show Attributes
def showDict(self):
ob = getattr(self,'aq_base')
return "%s \n %s" % (str(ob.__class__) , ob.__dict__)
#return str(ob.__class__) + '\n' + str(ob.meta_type) + '\n' +
# str(ob.portal_type) + '\n'
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from Replica import SkinnedFolder18
Folder18 = SkinnedFolder18
Folder = SkinnedFolder18
\ No newline at end of file
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
Warning
This product is a proof of concept for hard core Zope developers.
Wait version 0.2 to get something usable
Requirements
You will need a Zope system with "CMF":http://cmf.zope.org
and with "Localizer":http://www.nuxeo.org/localizer
Mail list
You may want to subscribe to the Base18 mail list::
http://mail.freesoftware.fsf.org/mailman/listinfo/erp5-base18
erp5-base18@mail.freesoftware.fsf.org
Installation Instructions
Download the Base18 tarball::
http://www.zope.org/Members/nexedi/Base18/Base18-0.1.tar.gz
or the latest CVS::
https://savannah.gnu.org/cgi-bin/viewcvs/erp5/Base18/
Install it in your local Zope product directory (ex. /var/lib/zope/Products on
Debian systems). Untar it.
Copy the files translation_workflow.zexp and migrate_content.zexp in the
Base18 directory into to your Zope import directory (ex. /var/lib/zope/import
on a Debian system).
Copy the file Extensions/MigrateContent.py to your Zope
Extensions directory (ex. /var/lib/zope/Extensions
on a Debian system).
Create a CMF site.
Import migrate_content.zexp at the root of your CMF site.
Import translation_workflow.zexp the content of your portal_worflow.
Execute the migrate_content external method at the root of your CMF
site (click on it and then on the Test button).
This will modify the default portal types and register the Base18 portal
types. You may at this point eventually need to reinder your CMF. The
migrate_content external method can also be used to migrate an entire site.
At this step, you need to modifiy the portal_workflow workflow associations
in order to associate the Translation portal type to the translation_workflow
workflow
Demos
There are two ways to use Base18.
For sites which do not contain much content and which are centralized,
it is sufficient to manage translations through a single gettext message
catalog. Thanks to Base18, content is automatically cut into small paragraphs
or sentences. It is then possible to export sentences with the Localizer
export feature into .pot or .po files. Such files can then be translated
mith toolds such as KBabel (see kbabel.png).
This first approach is used on "Storever":http://www.storever.com for
example
For sites which contain many documents for which different people
may want to translate single documents in multiple languages,
it is then recommended to proceed in 3 steps:
- Use the *Translate* menu of the document you want to translate
in order to download a .pot file which can be edited with kbabel
- Create a new *Translation* document in your personal folder
(My Stuff) within the CMF and upload the .pot file which
you translated at the previous step. At this step, you
must define the target language and the target document
of your translation
- Submit this translation to the portal reviewers so that
they can register your translation for that document
(use the menu on the left)
This process involves two new portal types
- a Translation portal type which is used to store
translations (ie. .po files for 1 language for 1 document)
- a portal_translations which registers the relations
between documents and translations (this tool also
implements the heuristics to find the most appropriate
translation for a given document and a given language)
The whole system allows to split the work of translation
accross all members of a given portal.
This second approach is being tested on the "ERP5 portal":http://www.erp5.org
We recommend to become member of ERP5 (a project to design a Zope based ERP)
in order to try Base18 first.
Localizer Patch
We patched Localizer in order to improve its operation with KBabel and
with cookies.
MessageCatalog.py
  - improved export procedure
    so that it generates multi-line strings properly
    (all lines are exported)
  - improved export procedure
    to escape quotes
        " -> \"
  Exported messages can now be read without errror by KBabel
__init__.py
  - changed language value from string type to tuple type
    so that it is now consistent with AcceptLanguage
    list-based algorithm
zgettext.py
  - improved import procedure so that it handles
    multi-line strings (all lines are now
    read into the message catalog)
  - improved export procedure
    to unescape quotes
        \" -> \
  KBabel .po files can now be read
Les sous-répertoires ./help et /home/jp/Localizer/help sont identiques.
Les sous-répertoires ./img et /home/jp/Localizer/img sont identiques.
Les sous-répertoires ./locale et /home/jp/Localizer/locale sont identiques.
Les sous-répertoires ./tests et /home/jp/Localizer/tests sont identiques.
Les sous-répertoires ./ui et /home/jp/Localizer/ui sont identiques.
diff -u ./Utils.py /home/jp/Localizer/Utils.py
--- ./Utils.py 2003-06-07 03:31:54.000000000 +0200
+++ /home/jp/Localizer/Utils.py 2003-06-07 03:32:44.000000000 +0200
@@ -54,7 +54,8 @@
request = get_request()
- lang = request.AcceptLanguage.select_language(available_languages)
+ return getattr(request.PARENTS[0],'FOLDER_LANGUAGE','en')
+ #lang = request.AcceptLanguage.select_language(available_languages)
# XXX Here we should set the Vary header, but, which value should it have??
@@ -62,7 +63,7 @@
## response.setHeader('Vary', 'accept-language')
## response.setHeader('Vary', '*')
- return lang
+ #return lang
# Defines strings that must be internationalized
Seulement dans ./: Utils.py.orig
Seulement dans /home/jp/Localizer: Utils.py.rej
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the Zope Public License (ZPL) Version 2.0
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
##############################################################################
__version__ = "$Revision$"[11:-2]
__doc__ = "This product provides the basic behaviour to CMF object which need\
translation"
"""\
Base18 portal_membership tool.
"""
from Globals import InitializeClass, DTMLFile
from AccessControl import ClassSecurityInfo
from Products.CMFCore.CMFCorePermissions import ManagePortal
from Products.CMFDefault.MembershipTool import MembershipTool
import Document
default_member_content = '''Default page for %s
This is the default document created for you when
you joined this community.
To change the content just select "Edit"
in the Tool Box on the left.
'''
class MembershipTool18( MembershipTool ):
meta_type = 'Base18 Membership Tool'
security = ClassSecurityInfo()
security.declareProtected(ManagePortal, 'createMemberarea')
def createMemberarea(self, member_id):
"""
create a member area
"""
parent = self.aq_inner.aq_parent
members = getattr(parent, 'Members', None)
if members is not None and not hasattr(members, member_id):
f_title = "%s's Home" % member_id
members.manage_addPortalFolder( id=member_id, title=f_title )
#members.invokeFactory(type_name="Folder", id=member_id)
f=getattr(members, member_id)
#f.setTitle(f_title)
# Grant ownership to Member
acl_users = self.__getPUS()
user = acl_users.getUser(member_id).__of__(acl_users)
f.changeOwnership(user)
f.manage_setLocalRoles(member_id, ['Owner'])
# Create Member's home page.
# default_member_content ought to be configurable per
# instance of MembershipTool.
#f.invokeFactory(type_name="Document", id='index_html')
def __getPUS(self):
# Gets something we can call getUsers() and getUserNames() on.
acl_users = self.acl_users
if hasattr(acl_users, 'getUsers'):
return acl_users
else:
# This hack works around the absence of getUsers() in LoginManager.
# Gets the PersistentUserSource object that stores our users
for us in acl_users.UserSourcesGroup.objectValues():
if us.meta_type == 'Persistent User Source':
return us.__of__(acl_users)
InitializeClass( MembershipTool18 )
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
__version__ = "$Revision$"[11:-2]
__doc__ = "This product provides multilingual capabilities to the CMFDefault NewsItem"
from utils import _translate_stx
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFCore import CMFCorePermissions
from Products.Localizer.MessageCatalog import MessageCatalog
from Products.CMFDefault.NewsItem import NewsItem
from Base18 import Base18
from Document import Document18
from zLOG import LOG
import commands
# Content Creator
def addNewsItem18( self
, id
, title=''
, description=''
, text=''
, text_format='html'
):
"""
Add a Multilingual NewsItem
"""
o=NewsItem18( id=id
, title=title
, description=description
, text=text
, text_format=text_format
)
self._setObject(id, o)
# Content Class
class NewsItem18(NewsItem, Document18):
""" A Multilingual NewsItem - Handles both StructuredText and HTML
and translates sentences through a portal_translations tool
"""
meta_type = 'Base18 News Item'
portal_type = 'News Item'
# Declarative security (replaces __ac_permissions__)
security = ClassSecurityInfo()
# CMF Factory Type Information
factory_type_information = ( { 'id' : portal_type
, 'meta_type' : meta_type
, 'description' : """\
News Items contain short text articles and carry a title as well as an optional description.\
Text can be automatically translated through the use of message catalogs."""
, 'icon' : 'newsitem_icon.gif'
, 'product' : 'Base18'
, 'factory' : 'addNewsItem18'
, 'immediate_view' : 'metadata_edit_form'
, 'actions' :
( { 'id' : 'view'
, 'name' : 'View'
, 'action' : 'newsitem18_view'
, 'permissions' : (
CMFCorePermissions.View, )
}
, { 'id' : 'print'
, 'name' : 'Print'
, 'action' : 'newsitem18_print'
, 'permissions' : (
CMFCorePermissions.View, )
}
, { 'id' : 'mail'
, 'name' : 'Mail'
, 'action' : 'newsitem_mail_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'edit'
, 'name' : 'Edit'
, 'action' : 'newsitem_edit_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'metadata'
, 'name' : 'Metadata'
, 'action' : 'metadata_edit_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'translate'
, 'name' : 'Translate'
, 'action' : 'translation_template'
, 'permissions' : (
CMFCorePermissions.View, )
}
)
}
,
)
security.declareProtected(CMFCorePermissions.View, 'PreformattedView')
def PreformattedView(self):
"""
Return a preformatted rendering of this news
Useful to send by email
"""
LOG('Pref',0, self.local_absolute_url())
result = commands.getoutput("lynx -dump %s/newsitem18_print" % self.local_absolute_url())
LOG('Pref',0,str(result))
return result
InitializeClass( NewsItem18 )
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
Base18 is a Zope product which allows to build multilingual portals. It uses
Localizer and extends the Zope CMFDefault product to provide a way
to translate documents at a sentence per sentence level.
It implements a new kind of CMF document: Translations. Translations allow to
store a .po file which allows to translate a given document. This approach
was inspired by the poxml approach used by the "KDE":http://www.kde.org project
to translate its documentation.
Because it works at the sentence or paragraph level, it is possible to use
the fuzzy feature of gettext to find similar translations for a given sentence
and accelerate the translation process.
Base18 also includes a translation workflow which allows to keep track
of translations and their association to documents in a portal.
Base18 is currently much of a "proof-of-concept" which needs to be extended.
Future versions will include greater flexibility and will implement relations.
More information can be found on the "Nexedi":http://www.nexedi.org/software
site::
http://www.nexedi.org/software
This diff is collapsed.
TODO:
Version 0.1 (Done)
- proof of concept
Version 1.0
- Release
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
__version__ = "$Revision$"[11:-2]
__doc__ = "This product provides multilingual capabilities to the CMFDefault \
Document"
from utils import _translate_stx
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFCore import CMFCorePermissions, PortalContent
from Products.Localizer.MessageCatalog import MessageCatalog
from Document import Document18
import string
# Content Creator
def addTranslation(self, id, title='', description=''):
""" Add a Translation Document """
o = Translation(id, title, description)
self._setObject(id,o)
# Content Class
class Translation(Document18):
""" A User Translation Document managed by the CMF
Actually, just a wrapper arround Localize to
make it editable under the CMF
"""
meta_type = 'Base18 Translation'
# Declarative security
security = ClassSecurityInfo()
# CMF Factory Type Information
factory_type_information = ( { 'id' : 'Translation'
, 'meta_type' : 'Base18 Translation'
, 'description' : """\
Translations can contain a list of translation messages formatted according\
to the .po standard. Translations can be registered through a translation\
workflow and provide a translation to an existing document in a portal'"""
, 'icon' : 'document_icon.gif'
, 'product' : 'Base18'
, 'factory' : 'addTranslation'
, 'immediate_view' : 'metadata_edit_form'
, 'actions' : ( { 'id' : 'view'
, 'name' : 'View'
, 'action' : 'translation_view'
, 'permissions' : (CMFCorePermissions.View,)
}
, { 'id' : 'edit'
, 'name' : 'Edit'
, 'action' : 'translation_edit_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'metadata'
, 'name' : 'Metadata'
, 'action' : 'metadata_edit_form'
, 'permissions' : (
CMFCorePermissions.ModifyPortalContent, )
}
, { 'id' : 'template'
, 'name' : 'Translation Template'
, 'action' : 'translation_template'
, 'permissions' : (
CMFCorePermissions.View, )
}
)
}
,
)
### Default values
targetContent = ()
targetLanguage = ('en')
messageCatalog = None
### Edit method
security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'edit' )
def edit( self
, text_format
, text
, file=''
, safety_belt=''
, target_language='en'
, target_content=None
):
"""
*used to be WorkflowAction(_edit)
To add webDav support, we need to check if the content is locked, and if
so return ResourceLockedError if not, call _edit.
Note that this method expects to be called from a web form, and so
disables header processing
"""
self.targetLanguage = target_language
if target_content is not None:
self.targetContent = target_content.getPhysicalPath()
else:
self.targetContent = ('a')
Document18.edit(self, text_format, text, file, safety_belt)
self.messageCatalog = MessageCatalog("mc", "Message Catalog",
(self.targetLanguage,))
self.messageCatalog.manage_import(self.targetLanguage, self.text)
def _edit(self, text, text_format='', safety_belt=''):
""" Edit the Document - Parses headers and cooks the body"""
headers = {}
if not safety_belt:
safety_belt = headers.get('SafetyBelt', '')
if not self._safety_belt_update(safety_belt=safety_belt):
msg = ("Intervening changes from elsewhere detected."
" Please refetch the document and reapply your changes."
" (You may be able to recover your version using the"
" browser 'back' button, but will have to apply them"
" to a freshly fetched copy.)")
raise 'EditingConflict', msg
self.text = self.cooked_text = text
### Content accessor methods
security.declareProtected(CMFCorePermissions.View, 'SearchableText')
def SearchableText(self, md=None):
"""\
Used by the catalog for basic full text indexing
We are going to concatenate all available translations
"""
if md is None: md = self.findMessageCatalog()
searchable_text = self.text
for lang in md.get_languages():
searchable_text = searchable_text + "%s %s" % (
md.gettext(self.Title(),lang)
, md.gettext(self.Description(),lang)
)
return searchable_text
# Translation methods
security.declareProtected(CMFCorePermissions.View, 'TranslatedBody')
def TranslatedBody(self, stx_level=None, setlevel=0, lang=None, md=None):
"""\
Equivalent to CookedBody but returns a translated version thanks
to the use of message catalog
"""
if md is None: md = self.find_md()
if stx_level is None: stx_level = self._stx_level
cooked = _translate_stx(self.text, md, stx_level, lang)
return cooked
security.declareProtected(CMFCorePermissions.View, 'TranslationTemplate')
def TranslationTemplate(self):
"""\
This method allows to produce of .pot file for this document
"""
# Create a new catalog
md = MessageCatalog('temp', 'Temporary Message Catalog',
self.find_md().get_languages())
# Do some dirty acquisition trick
md.aq_base = self
# Run the md on the text body
cooked = _translate_stx(self.text, md, self._stx_level, None)
md.gettext(self.Title())
md.gettext(self.Description())
# And return the template
return md.manage_export('locale.pt')
# Implementation with Message Catalogs
security.declareProtected(CMFCorePermissions.View, 'messageCatalog')
def getMessageCatalog(self):
return self.messageCatalog
security.declareProtected(CMFCorePermissions.View, 'targetContentPath')
def targetContentPath(self):
return string.join(self.targetContent,'/')
InitializeClass(Translation)
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
__version__ = "$Revision$"[11:-2]
__doc__ = "This product provides the basic behaviour to CMF object which need\
translation"
"""\
Base18 portal_translation tool.
"""
from OFS.SimpleItem import SimpleItem
from Products.CMFCore.utils import UniqueObject
from Globals import InitializeClass, DTMLFile, PersistentMapping
from AccessControl import ClassSecurityInfo, getSecurityManager
from Products.CMFCore import CMFCorePermissions
from utils import _dtmldir
from Products.Localizer.Utils import lang_negotiator
#import zLOG
class TranslationError( Exception ):
pass
class TranslationTool( UniqueObject, SimpleItem ):
id = 'portal_translations'
meta_type = 'Base18 Translation Tool'
security = ClassSecurityInfo()
#
# Default values.
#
registered_translations = None
def __init__( self ):
self.registered_translations = PersistentMapping()
#
# ZMI methods
#
manage_options = ( ( { 'label' : 'Overview'
, 'action' : 'manage_overview'
}
, { 'label' : 'Properties'
, 'action' : 'propertiesForm'
}
)
+ SimpleItem.manage_options
)
security.declareProtected( CMFCorePermissions.ManagePortal
, 'manage_overview' )
manage_overview = DTMLFile( 'explainTranslationTool', _dtmldir )
security.declareProtected( CMFCorePermissions.ManagePortal
, 'propertiesForm' )
propertiesForm = DTMLFile( 'translationProperties', _dtmldir )
security.declareProtected( CMFCorePermissions.ManagePortal
, 'editProperties' )
def editProperties( self
, publisher=None
, REQUEST=None
):
"""
Form handler for "tool-wide" properties (including list of
metadata elements).
"""
if publisher is not None:
self.publisher = publisher
if REQUEST is not None:
REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
+ '/propertiesForm'
+ '?manage_tabs_message=Tool+updated.'
)
#
# 'portal_translations' interface
#
security.declarePublic( 'findMessageCatalog' )
def findMessageCatalog(self, content, language=None):
"""
Return the default message catalog for
an object to translate
For now, look in the same directory
or in the acquisition path either for a message catalog for
that object or for a message more global catalog
In the future, we will try to look at message catalogs provided by
users.
A workklow will be used to manage the status of translations
"""
translation = self.findRegisteredTranslation(content)
if translation is not None:
return translation.getMessageCatalog()
else:
return getattr(content.aq_parent, str(content.id) + '.msg' ,
content.gettext)
# Translation registration catalog
# Can be improved a lot (of course)
security.declarePublic( 'registerTranslation' )
def registerTranslation(self, content, translation, language=None):
"""
This function allows to register a user translation
"""
# If language not supplied, look up languages in the user translation
if language is None:
languages = translation.getMessageCatalog().get_languages()
else:
languages=(language,)
# Get the path of content and translation
content_path = content.getPhysicalPath()
translation_path = translation.getPhysicalPath()
# Register the path for content and translation for each available
# language
if not self.registered_translations.has_key(content_path):
self.registered_translations[content_path] = PersistentMapping()
for lang in languages:
self.registered_translations[content_path][lang] = translation_path
security.declarePublic( 'unregisterTranslation' )
def unregisterTranslation(self, content, translation=None, language=None):
"""
This function allows to register a user translation
"""
content_path = content.getPhysicalPath()
# Is content registered ?
if self.registered_translations.has_key(content_path):
if language is None:
languages = self.registered_translations[content_path].keys()
else:
if self.registered_translations[content_path].has_key(language):
languages = (language,)
else:
languages = []
if translation is not None:
translation_path = translation.getPhysicalPath()
for lang in languages:
# If no translation provided, then remove default translation
if translation is None:
del self.registered_translations[content_path][lang]
# if translation is provided, remove that translation only
else:
if self.registered_translations[content_path][lang] == \
translation_path:
del self.registered_translations[content_path][lang]
else:
pass
security.declarePublic( 'findRegisteredTranslation' )
def findRegisteredTranslation(self, content, language=None):
"""
This function allows to find if a translation has been registered
"""
content_path = content.getPhysicalPath()
if self.registered_translations.has_key(content_path):
# Find negociated language if necessary
# zLOG.LOG('Registered Translation Found',0,content_path)
if language is None:
language = lang_negotiator([content.language] +
self.registered_translations[content_path].keys())
# zLOG.LOG('Neg. Language',0,self.language + ',' + language)
if language is None:
return None
translation_path = \
self.registered_translations[content_path].get(language, None)
if translation_path is None:
# zLOG.LOG('No Translation Path Found',0,translation_path)
return None
else:
# zLOG.LOG('Translation Path Found',0,translation_path)
try: return self.restrictedTraverse(translation_path)
except: return None
else:
# zLOG.LOG('No Registered Translation Found',0,content_path)
return None
InitializeClass( TranslationTool )
This diff is collapsed.
Zope Public License (ZPL) Version 2.0
-----------------------------------------------
This software is Copyright (c) Zope Corporation (tm) and
Contributors. All rights reserved.
This license has been certified as open source. It has also
been designated as GPL compatible by the Free Software
Foundation (FSF).
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions in source code must retain the above
copyright notice, this list of conditions, and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions, and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
3. The name Zope Corporation (tm) must not be used to
endorse or promote products derived from this software
without prior written permission from Zope Corporation.
4. The right to distribute this software or to use it for
any purpose does not give you the right to use Servicemarks
(sm) or Trademarks (tm) of Zope Corporation. Use of them is
covered in a separate agreement (see
http://www.zope.com/Marks).
5. If any files are modified, you must cause the modified
files to carry prominent notices stating that you changed
the files and the date of any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
This software consists of contributions made by Zope
Corporation and many individuals on behalf of Zope
Corporation. Specific attributions are listed in the
accompanying credits file.
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# TODO:
# - Implement Vary so that cache can take into account translated version
# - Implement language caching....
#
##############################################################################
"""
"""
ADD_CONTENT_PERMISSION = 'Add portal content'
import Document, NewsItem, Replica, Translation, Wiki
import TranslationTool, MembershipTool, CookieCrumbler
from Products.CMFCore import utils
import Products.CMFCore
from Products.CMFCore.DirectoryView import registerDirectory
contentClasses = ( Document.Document18
, NewsItem.NewsItem18
, Replica.File18
, Replica.Link18
, Replica.Image18
, Replica.Favorite18
, Replica.DiscussionItem18
, Replica.SkinnedFolder18
, Translation.Translation
, Wiki.CMFWikiPage18
, Wiki.CMFWikiFolder18
)
contentConstructors = ( Document.addDocument18
, NewsItem.addNewsItem18
, Replica.addFile18
, Replica.addLink18
, Replica.addImage18
, Replica.addFavorite18
, Replica.addDiscussionItem18
, Replica.addSkinnedFolder18
, Translation.addTranslation
, Wiki.addCMFWikiPage18
, Wiki.addCMFWikiFolder18
)
contentFactoryTypeInformations = []
for content in contentClasses:
if type(content.factory_type_information) == type({}):
contentFactoryTypeInformations.append(content.factory_type_information)
else:
contentFactoryTypeInformations.append(content.factory_type_information[0])
tools = ( TranslationTool.TranslationTool
, MembershipTool.MembershipTool18
)
bases = contentClasses
import sys
this_module = sys.modules[ __name__ ]
z_bases = utils.initializeBasesPhase1( bases, this_module )
z_tool_bases = utils.initializeBasesPhase1( tools, this_module )
base18_globals=globals()
# Make the skins available as DirectoryViews.
registerDirectory('skins', globals())
registerDirectory('help', globals())
def initialize( context ):
utils.initializeBasesPhase2( z_bases, context )
utils.initializeBasesPhase2( z_tool_bases, context )
utils.ToolInit('Base18 Tool', tools=tools,
product_name='Base18', icon='tool.png',
).initialize( context )
utils.ContentInit( 'Base18 Content'
, content_types=contentClasses
, permission=ADD_CONTENT_PERMISSION
, extra_constructors=contentConstructors
, fti=contentFactoryTypeInformations
).initialize( context )
context.registerHelp()
context.registerHelpTitle('Base18 Help')
Welcome to !CMFWiki, this is a !CMFWikiWeb.
For general Wiki help instructions, see the "Help":WikiHelp .
To see the table of contents or recent changes, or to search this
!CMFWikiWeb, use the "folder contents":folder_contents view
and look for the appropriate actions in the action box.
To experiment, use the SandBox page.
This page is for experimenting.
Use the "Edit" or "Comment" links on the sidebar to fool around.
<dtml-comment>
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets <jp@nexedi.com>
WARNING: This program as such is intended to be used by professional
programmers who take the whole responsability of assessing all potential
consequences resulting from its eventual inadequacies and bugs
This program as such is not intended to be used by end users. End
users who are looking for a ready-to-use solution with commercial
garantees and support are strongly adviced to contract a Free Software
Service Company
This program is Free Software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</dtml-comment>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<h3> <code>portal_metadata</code> Tool </h3>
<p> This tool embodies site-wide policies concerning content translation.
The default heuristics consists in searching first for a message
catalog next to the document, then a translation created by a member
and in the end a global message catalog.
</p>
<dtml-var manage_page_footer>
Updating an existing Zope CMF
Export your CMF as XML
First use the update-products script on the XML file
Beware : buggy, will destroy some non CMF types !!!
Update portal types (! local roles)
Add
gettext
portal_translations
portal_membership
\ No newline at end of file
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""\
Declare interface for Translatable content
"""
import Interface
class Translatable(Interface.Base):
"""\
Returns back a list of objects which implements the Translatable interface.
"""
def TranslationTemplate(self):
"""
Returns a list of results which is to be Syndicated. For example, the normal call
contentValues (on PortalFolders) returns a list of subObjects of the current object
(i.e. objectValues with filtering applied). For the case of a Topic, one would
return a sequence of objects from a catalog query, not the subObjects of the Topic.
What is returned must implement the DublinCore.
"""
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""\
Loads interface names into the package.
"""
from Translatable import Translatable
? AcceptLanguage.pyc
? Gettext.pyc
? GettextTag.pyc
? LanguageManager.pyc
? LocalAttributes.pyc
? LocalContent.pyc
? LocalFiles.pyc
? LocalFolder.pyc
? LocalPropertyManager.pyc
? Localizer.pyc
? MessageCatalog.pyc
? Translator.py.org
? Utils.pyc
? __init__.pyc
? jps.diff
? refresh.txt
? zgettext.pyc
? locale/ca.mo
? locale/de.mo
? locale/en.mo
? locale/es.mo
? locale/eu.mo
? locale/fr.mo
? locale/hu.mo
Index: AcceptLanguage.py
===================================================================
RCS file: /cvsroot/lleu/Localizer/AcceptLanguage.py,v
retrieving revision 1.2
diff -u -r1.2 AcceptLanguage.py
--- AcceptLanguage.py 21 Feb 2002 10:53:54 -0000 1.2
+++ AcceptLanguage.py 16 Apr 2002 08:07:44 -0000
@@ -23,7 +23,6 @@
#from UserDict import UserDict
from types import StringType
-
class AcceptLanguageNode:
"""
This class is a recursive representation of a tree.
Index: Localizer.py
===================================================================
RCS file: /cvsroot/lleu/Localizer/Localizer.py,v
retrieving revision 1.53
diff -u -r1.53 Localizer.py
--- Localizer.py 25 Feb 2002 09:46:40 -0000 1.53
+++ Localizer.py 16 Apr 2002 08:07:45 -0000
@@ -215,17 +215,17 @@
stack.append(self.id)
-## # Changing the language, useful snippets
-## security.declarePublic('changeLanguage')
-## changeLanguageForm = LocalDTMLFile('ui/changeLanguageForm', globals())
-## def changeLanguage(self, REQUEST, RESPONSE):
-## """ """
-## lang = REQUEST['lang']
+ # Changing the language, useful snippets
+ security.declarePublic('changeLanguage')
+ changeLanguageForm = LocalDTMLFile('ui/changeLanguageForm', globals())
+ def changeLanguage(self, REQUEST, RESPONSE):
+ """ """
+ lang = REQUEST['lang']
-## path = self.absolute_url()[len(REQUEST['SERVER_URL']):] or '/'
-## RESPONSE.setCookie('LOCALIZER_LANGUAGE', lang, path=path)
+ path = self.absolute_url()[len(REQUEST['SERVER_URL']):] or '/'
+ RESPONSE.setCookie('LOCALIZER_LANGUAGE', lang, path=path)
-## RESPONSE.redirect(REQUEST['HTTP_REFERER'])
+ RESPONSE.redirect(REQUEST['HTTP_REFERER'])
# Upgrading..
security.declarePublic('need_upgrade')
Index: MessageCatalog.py
===================================================================
RCS file: /cvsroot/lleu/Localizer/MessageCatalog.py,v
retrieving revision 1.17
diff -u -r1.17 MessageCatalog.py
--- MessageCatalog.py 25 Feb 2002 09:46:40 -0000 1.17
+++ MessageCatalog.py 16 Apr 2002 08:07:46 -0000
@@ -379,9 +379,14 @@
d[k] = ""
# Generate the file
+ quote_esc = re.compile(r'"')
for k, v in d.items():
- r.append('msgid "%s"' % k)
- r.append('msgstr "%s"' % v)
+ r.append('msgid ""')
+ for line in k.split('\n'):
+ r.append( '"%s"' % quote_esc.sub('\\"',line))
+ r.append('msgstr ""')
+ for line in v.split('\n'):
+ r.append( '"%s"' % quote_esc.sub('\\"',line))
r.append('')
@@ -405,10 +410,29 @@
k, k, k, d = parse_po_file(content)
for k, v in d.items():
- k = k[0]
+ if len(k) == 1:
+ # single line msgid
+ k = k[0]
+ else:
+ # multiline msgid
+ if k[0] == '':
+ k = '\n'.join(k[1:])
+ else:
+ k = '\n'.join(k)
if not messages.has_key(k):
messages[k] = PersistentMapping()
- messages[k][lang] = v[1][0]
+ # Take the commentaries only
+ v = v[1]
+ if len(v) == 1:
+ # single line msgstr
+ v = v[0]
+ else:
+ # multiline msgstr
+ if v[0] == '':
+ v = '\n'.join(v[1:])
+ else:
+ v = '\n'.join(v)
+ messages[k][lang] = v
if REQUEST is not None:
return self.manage_messages(self, REQUEST)
Index: __init__.py
===================================================================
RCS file: /cvsroot/lleu/Localizer/__init__.py,v
retrieving revision 1.35
diff -u -r1.35 __init__.py
--- __init__.py 25 Feb 2002 09:46:40 -0000 1.35
+++ __init__.py 16 Apr 2002 08:07:46 -0000
@@ -115,12 +115,12 @@
# Add the language from the form
lang = request.form.get('LOCALIZER_LANGUAGE', None)
if lang is not None:
- accept_language[lang] = 3.0
+ accept_language[lang.split('-',1)] = 3.0
# Add the language from the cookies
lang = request.cookies.get('LOCALIZER_LANGUAGE', None)
if lang is not None:
- accept_language[lang] = 2.0
+ accept_language[lang.split('-',1)] = 2.0
self.other['USER_PREF_LANGUAGES'] = accept_language
@@ -193,7 +193,7 @@
context.registerClass(
LocalContent.LocalContent,
constructors = (LocalContent.manage_addLocalContentForm,
- LocalContent.manage_addLocalContent),
+ LocalContent.manage_addLocalContent),
icon='img/local_content.gif')
# Register MessageCatalog
Index: zgettext.py
===================================================================
RCS file: /cvsroot/lleu/Localizer/zgettext.py,v
retrieving revision 1.15
diff -u -r1.15 zgettext.py
--- zgettext.py 25 Feb 2002 09:46:40 -0000 1.15
+++ zgettext.py 16 Apr 2002 08:07:47 -0000
@@ -39,10 +39,11 @@
def parse_po_file(content):
# The regular expressions
com = re.compile('^#.*')
- msgid = re.compile(r'^ *msgid *"(.*?[^\\]*)"')
- msgstr = re.compile(r'^ *msgstr *"(.*?[^\\]*)"')
- re_str = re.compile(r'^ *"(.*?[^\\])"')
+ msgid = re.compile(r'^ *msgid *"(.*[^\\]*)"')
+ msgstr = re.compile(r'^ *msgstr *"(.*[^\\]*)"')
+ re_str = re.compile(r'^ *"(.*[^\\])"')
blank = re.compile(r'^\s*$')
+ quote_esc = re.compile(r'\\"')
trans = {}
pointer = 0
@@ -59,7 +60,8 @@
state = 1
pointer = pointer + 1
elif msgid.match(line):
- MSGID.append(msgid.match(line).group(1))
+ line = msgid.match(line).group(1)
+ MSGID.append(quote_esc.sub('"',line))
state = 2
pointer = pointer + 1
elif blank.match(line):
@@ -72,7 +74,8 @@
state = 1
pointer = pointer + 1
elif msgid.match(line):
- MSGID.append(msgid.match(line).group(1))
+ line = msgid.match(line).group(1)
+ MSGID.append(quote_esc.sub('"',line))
state = 2
pointer = pointer + 1
elif blank.match(line):
@@ -85,11 +88,13 @@
state = 2
pointer = pointer + 1
elif re_str.match(line):
- MSGID.append(re_str.match(line).group(1))
+ line = re_str.match(line).group(1)
+ MSGID.append(quote_esc.sub('"',line))
state = 2
pointer = pointer + 1
elif msgstr.match(line):
- MSGSTR.append(msgstr.match(line).group(1))
+ line = msgstr.match(line).group(1)
+ MSGSTR.append(quote_esc.sub('"',line))
state = 3
pointer = pointer + 1
elif blank.match(line):
@@ -102,7 +107,8 @@
trans[tuple(MSGID)] = (COM, MSGSTR)
state = 0
elif re_str.match(line):
- MSGSTR.append(re_str.match(line).group(1))
+ line = re_str.match(line).group(1)
+ MSGSTR.append(quote_esc.sub('"',line))
state = 3
pointer = pointer + 1
elif blank.match(line):
## Script (Python) "TranslatedBody"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Modify the status of a content object
##
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
return context.CookedBody()
\ No newline at end of file
## Script (Python) "TranslatedDescription"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Modify the status of a content object
##
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
Description = context.Description
if callable(Description):
Description = Description()
if Description: return Description
return ''
## Script (Python) "TranslatedTitle"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Modify the status of a content object
##
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
title=context.title
if callable(title):
title=title()
if title: return context.gettext(title)
return ''
## Script (Python) "TranslatedTitle_or_id"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Modify the status of a content object
##
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
title=context.title
if callable(title):
title=title()
if title: return title
return context.getId()
<dtml-let relative_to_content="1">
<dtml-var standard_html_header>
</dtml-let>
<div class="Desktop">
<div class="Document">
<dtml-var content_byline>
<div class="Discussion">
<dtml-var aboveInThread>
</div>
<h1> &dtml-TranslatedTitle; </h1>
<dtml-var TranslatedBody>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-let relative_to_content="1">
<dtml-var standard_html_header>
</dtml-let>
<div class="Desktop">
<div class="Document">
<dtml-var content_byline>
<dtml-var TranslatedBody>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-var standard_html_header>
<div class="Desktop">
<div class="Link">
<dtml-var content_byline>
<p> <a href="&dtml-getRemoteUrl;">"><dtml-var getRemoteUrl></a> </p>
<dtml-var TranslatedDescription>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-var standard_html_header>
<div class="Desktop">
<div class="File">
<dtml-var content_byline>
<p>
<b>Filename</b>: <dtml-var getId><br>
<b>Size</b>: <dtml-var size><br>
<b>Content type</b>: <dtml-var content_type><br>
<b>Description</b>: <dtml-var name="description" newline_to_br>
</p>
<a href="&dtml-absolute_url;">Download &dtml-title;</a>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-var standard_html_header>
<h2><dtml-var title_or_id></h2>
<dtml-let folder_url=absolute_url>
<dtml-in expr="objectValues( [ 'Document', 'News Item', 'Portal Image', 'Portal File', 'Base18 Document', 'Base18 News Item', 'Base18 File', 'Base18 Image' ] )"
skip_unauthorized>
<dtml-if sequence-start>
<h3> Documents, Images, and Files </h3>
<ul>
</dtml-if>
<li> <a href="&dtml-id;"> <dtml-var TranslatedTitle> </a>
<dtml-if name="Description"><blockquote>&dtml-TranslatedDescription;</blockquote></dtml-if></li>
<dtml-if sequence-end>
</ul>
</dtml-if>
</dtml-in>
<dtml-in expr="objectValues( [ 'Link', 'Favorite', 'Base18 Link','Base18 Favorite' ] )" skip_unauthorized>
<dtml-if sequence-start>
<h3> Links </h3>
<ul>
</dtml-if>
<li> <a href="&dtml-getRemoteUrl;"> <dtml-var TranslatedTitle> </a>
<dtml-if name="Description"><blockquote>&dtml-TranslatedDescription;</blockquote></dtml-if></li>
<dtml-if sequence-end>
</ul>
</dtml-if>
</dtml-in>
<dtml-in expr="objectValues( [ 'Folder', 'Portal Folder', 'Skinned Folder' , 'Base18 Folder'] )" skip_unauthorized>
<dtml-if sequence-start>
<h3> Folders </h3>
<ul>
</dtml-if>
<li> <a href="&dtml-id;"> <dtml-var TranslatedTitle_or_id> </a>
<dtml-if name="Description"><blockquote>&dtml-TranslatedDescription;</blockquote></dtml-if></li>
<dtml-if sequence-end>
</ul>
</dtml-if>
</dtml-in>
</dtml-let>
<dtml-var standard_html_footer>
<dtml-comment>
folderfilter cookie maintenance.
</dtml-comment>
<dtml-in filterCookie>
</dtml-in>
<dtml-comment>
Folder contents display.
</dtml-comment>
<dtml-call "REQUEST.set('isDesktop', 1)">
<dtml-var standard_html_header>
<dtml-if "not portal_membership.checkPermission('List folder contents'
, this())">
<dtml-call "RESPONSE.redirect(absolute_url())">
</dtml-if>
<!-- This is the desktop area -->
<div class="Desktop">
<h1> Desktop </h1>
<form action="&dtml-absolute_url;" method="POST">
<table class="FormLayout">
<tr>
<td valign="top">
<table class="ContentsList">
<dtml-if "portal_membership.checkPermission( 'List folder contents'
, this(), 'aq_parent')">
<dtml-let upNav="_.hasattr(this().aq_parent, 'portal_url')"
upID="this().aq_parent.getId()"
>
<tr valign="top">
<td width="16"><br></td>
<td>
<dtml-if upNav>
<a href="../folder_contents"
><img src="&dtml-portal_url;/UpFolder_icon.gif"
alt="[Link]" border="0"></a>
</dtml-if>
</td>
<td>
<dtml-if upNav>
Up to <a href="../folder_contents"><dtml-var upID></a>
<dtml-else>
<span class="mild">Root</span>
</dtml-if>
</td>
</tr>
</dtml-let>
</dtml-if>
<dtml-let filterString="REQUEST.get( 'folderfilter', '' )"
filter="decodeFolderFilter( filterString )"
>
<dtml-in expr="listFolderContents( contentFilter=filter )"
sort="getId" size="19" start=qs skip_unauthorized>
<dtml-if next-sequence>
<dtml-call "REQUEST.set('next-sequence'
, _['next-sequence-start-number'])">
</dtml-if>
<dtml-if previous-sequence>
<dtml-call "REQUEST.set('previous-sequence'
,_['previous-sequence-start-number'])">
</dtml-if>
<dtml-let obj="_.getitem('sequence-item', 0 )"
folderish=isPrincipiaFolderish
portalish="_.hasattr( obj, 'isPortalContent' )
and obj.isPortalContent"
methodID="folderish and '/folder_contents' or ( portalish
and '/view' or '' )"
getIcon="_.hasattr(obj, 'getIcon') and obj.getIcon()"
icon="getIcon or _.getattr(obj, 'icon', '')"
>
<tr valign="top">
<td>
<input type="checkbox" name="ids:list" value="&dtml-getId;"
id="cb_&dtml-getId;" />
</td>
<td>
<dtml-if icon>
<a href="&dtml.url_quote-getId;&dtml-methodID;"
><img src="&dtml-portal_url;/&dtml-icon;"
alt="&dtml-Type;" border="0"></a>
</dtml-if>
</td>
<td>
<a href="&dtml.url_quote-getId;&dtml-methodID;"
>&dtml-getId;<dtml-if title> (&dtml-title;)</dtml-if></a>
</td>
</tr>
<dtml-if qs>
<dtml-if "_['sequence-index'] - _.int(qs) == 7">
</table><dtml-comment> End of first column </dtml-comment>
</td>
<td>
<table class="ContentsList">
</dtml-if>
<dtml-else> <dtml-comment> No batch </dtml-comment>
<dtml-if "_['sequence-index'] == 8">
</table><dtml-comment> End of first column </dtml-comment>
</td>
<td valign="top">
<table class="ContentsList">
</dtml-if>
</dtml-if>
</dtml-let>
</dtml-in>
</dtml-let>
</table>
</td>
</tr>
<tr>
<td align="right">
<dtml-if previous-sequence>
<a href="folder_contents?qs=&dtml-previous-sequence;">Previous items</a>
<dtml-else>
<br>
</dtml-if>
</td>
<td align="left">
<dtml-if next-sequence>
<a href="folder_contents?qs=&dtml-next-sequence;">Next items</a>
<dtml-else>
<br>
</dtml-if>
</td>
</tr>
</table><dtml-comment> End of listing table </dtml-comment>
<table border="0" cellspacing="0" cellpadding="2">
<tr>
<td align="left" valign="top" width="16"></td>
<td align="left" valign="top">
<dtml-if expr="portal_membership.checkPermission('Add portal content'
, this())">
<input type="submit" name="folder_factories:method" value="New...">
</dtml-if>
<dtml-if expr="portal_membership.checkPermission('View management screens'
, this())">
<input type="submit" name="folder_rename_form:method" value="Rename">
<input type="submit" name="folder_cut:method" value="Cut">
<input type="submit" name="folder_copy:method" value="Copy">
<dtml-if cb_dataValid>
<input type="submit" name="folder_paste:method" value="Paste">
</dtml-if>
</dtml-if>
<dtml-if expr="portal_membership.checkPermission('Delete objects', this())">
<input type="submit" name="folder_delete:method" value="Delete">
</dtml-if>
</td>
</tr>
</table>
</form>
<dtml-var folder_filter_form>
</div>
<dtml-var standard_html_footer>
return context.TranslatedBody()
\ No newline at end of file
return context.TranslatedDescription()
\ No newline at end of file
return context.TranslatedTitle()
\ No newline at end of file
return context.TranslatedTitle_or_id()
\ No newline at end of file
<dtml-var standard_html_header>
<div class="Desktop">
<div class="Image">
<dtml-var tag>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-var standard_html_header>
<div class="Desktop">
<div class="Link">
<dtml-var content_byline>
<p>
<a href="<dtml-var remote_url>"><dtml-var remote_url></a></p>
</p>
<dtml-var TranslatedDescription>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-let relative_to_content="1">
<dtml-var standard_html_header>
</dtml-let>
<div class="Desktop">
<div class="NewsItem">
<blockquote class="Description">
<dtml-var TranslatedDescription fmt="structured-text">
</blockquote>
<dtml-var content_byline>
<dtml-var TranslatedBody>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
## Script (Python) "translation_edit"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=target_content, target_language, text, file='', SafetyBelt='', choice=' Change '
##title=Edit a document
##
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
#try:
if 1:
target_content = context.restrictedTraverse(target_content, None)
context.edit( ''
, text
, file
, safety_belt=SafetyBelt
, target_language=target_language
, target_content=target_content
)
qst='portal_status_message=Document+changed.'
if choice == ' Change and View ':
target_action = context.getTypeInfo().getActionById( 'view' )
else:
target_action = context.getTypeInfo().getActionById( 'edit' )
context.REQUEST.RESPONSE.redirect( '%s/%s?%s' % ( context.absolute_url()
, target_action
, qst
) )
#except Exception, msg:
# target_action = context.getTypeInfo().getActionById( 'edit' )
# context.REQUEST.RESPONSE.redirect('%s/%s?portal_status_message=%s' % (
# context.absolute_url()
# , target_action
# , msg
# ))
<dtml-var standard_html_header>
<div class="Desktop">
<dtml-if message>
<p>&dtml-message;</p>
<hr>
</dtml-if>
<div class="Document">
<h2>Edit &dtml-getId;</h2>
<form action="translation_edit" method="post" enctype="multipart/form-data">
<input type="hidden" name="SafetyBelt" value="&dtml-SafetyBelt;">
<table class="FormLayout">
<tr>
<th>
Title
</th>
<td>
<dtml-var Title>
</td>
</tr>
<tr>
<th>
Description
</th>
<td>
<dtml-var description>
</td>
</tr>
<tr>
<th>
Target
</th>
<td>
<input type="text" name="target_content:string" value="&dtml-targetContentPath;" />
</td>
</tr>
<tr>
<th>
Language
</th>
<td>
<input type="text" name="target_language:string" value="&dtml-targetLanguage;" />
</td>
</tr>
<tr>
<th> Upload </th>
<td>
<input type="file" name="file" size="25">
</td>
</tr>
<tr>
<th class="TextField"> Edit </th>
<td class="TextField">
<textarea name="text:text"
rows="20" cols="80"><dtml-var EditableBody html_quote></textarea>
</td>
</tr>
<tr>
<td> <br> </td>
<td>
<input type="submit" name="choice" value=" Change ">
<input type="submit" name="choice" value=" Change and View ">
</td>
</tr>
</table>
</form>
</div>
</div>
<dtml-var standard_html_footer>
\ No newline at end of file
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
translation_state="portal_workflow.getInfoFor(this(), 'translation_state')"
translation_history="portal_workflow.getInfoFor(this(), 'translation_history')"
url="portal_workflow.getInfoFor(this(), 'url')">
<div class="Desktop">
<h1> Register a Translation </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p>To make use of a translation, it has to be reviewed by one of the site's reviewers.
A <b>registered</b> translation becomes the default translation for a given document
to the general member base and anonymous visitors.</p>
<form method="post" action="translation_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-translation_state;</b> status.
<input type="hidden" name="workflow_action" value="register">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Register Translation "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
\ No newline at end of file
## Script (Python) "translation_status_modify"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=workflow_action, comment='', url=''
##title=Modify the status of a content object
##
##############################################################################
#
# Base18: a Zope product which provides multilingual services for CMF Default
# documents.
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
#
# This program as such is not intended to be used by end users. End
# users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
context.portal_workflow.doActionFor(
context,
workflow_action,
comment=comment)
content = context.restrictedTraverse(context.targetContent)
language = context.targetLanguage
translation = context.restrictedTraverse(context.id)
if workflow_action == 'unregister':
context.portal_translations.unregisterTranslation(content,translation,language=language)
redirect_url = '%s/view?%s' % ( context.absolute_url()
, 'portal_status_message=Status+changed.'
)
elif workflow_action == 'register':
context.portal_translations.registerTranslation(content,translation,language=language)
redirect_url = '%s/view?%s' % ( context.absolute_url()
, 'portal_status_message=Status+changed.'
)
else:
redirect_url = '%s/view?%s' % ( context.absolute_url()
, 'portal_status_message=Status+changed.'
)
context.REQUEST[ 'RESPONSE' ].redirect( redirect_url )
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
translation_state="portal_workflow.getInfoFor(this(), 'translation_state')"
translation_history="portal_workflow.getInfoFor(this(), 'translation_history')"
url="portal_workflow.getInfoFor(this(), 'url')">
<div class="Desktop">
<h1> Submit a Translation for Review </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p>To make use of a translation, it has to be reviewed by one of the site's reviewers.
A <b>registered</b> translation becomes the default translation for a given document
to the general member base and anonymous visitors.</p>
<form method="post" action="translation_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-translation_state;</b> status.
<input type="hidden" name="workflow_action" value="submit">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Submit Translation "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
\ No newline at end of file
<dtml-var standard_html_header>
<div class="Desktop">
<div class="File">
<dtml-var content_byline>
<p>
<b>Document</b>: <dtml-var getId><br>
<b>Description</b>: <dtml-var name="description" newline_to_br>
</p>
<a href="&dtml-absolute_url;/TranslationTemplate">Download &dtml-title;</a>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
<dtml-let relative_to_content="1">
<dtml-var standard_html_header>
</dtml-let>
<div class="Desktop">
<div class="Document">
<dtml-var content_byline>
<dtml-var CookedBody>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
</div>
</div>
<dtml-var standard_html_footer>
## Script (Python) "change_language"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Modify the language cookie
##
request = context.REQUEST
PARENTS = request.PARENTS
portal_root = context.portal_url.getPortalObject()
portal_root_path = portal_root.getPhysicalPath()
lang_list = context.gettext.get_available_languages()
if len(PARENTS) >= (1 + len(portal_root_path)):
section = PARENTS[len(PARENTS) - 1
- len(portal_root.getPhysicalPath())]
if section.id in lang_list:
if len(PARENTS) >= (2 + len(portal_root_path)):
section = PARENTS[len(PARENTS) - 2
- len(portal_root_path)]
else:
section = context
else:
section = context
return section
## Script (Python) "change_language"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Modify the language cookie
##
request = context.REQUEST
PARENTS = request.PARENTS
portal_root = context.portal_url.getPortalObject()
portal_root_path = portal_root.getPhysicalPath()
lang_list = context.gettext.get_available_languages()
if len(PARENTS) >= (2 + len(portal_root_path)):
section = PARENTS[len(PARENTS) - 1
- len(portal_root.getPhysicalPath())]
subsection = PARENTS[len(PARENTS) - 2
- len(portal_root.getPhysicalPath())]
if section.id in lang_list:
if len(PARENTS) >= (3 + len(portal_root_path)):
subsection = PARENTS[len(PARENTS) - 3
- len(portal_root_path)]
else:
subsection = context
else:
subsection = context
return subsection
\ No newline at end of file
TODO
- make sure permission to see private folder is OK
\ No newline at end of file
<dtml-let portal_obj="portal_url.getPortalObject()">
<dtml-if "portal_obj != this()">
<a href="&dtml-portal_url;"><dtml-var "portal_obj.Title()"></a>&nbsp;<dtml-in "REQUEST.PARENTS[0:-(_.len(portal_obj.getPhysicalPath()))]" reverse>&gt;&nbsp;<a href="<dtml-var local_absolute_url>"><dtml-var TranslatedTitle_or_id></a>&nbsp;</dtml-in>
<dtml-if "REQUEST.PARENTS[0] != this()">&gt;&nbsp;<a href="<dtml-var local_absolute_url>"><dtml-var TranslatedTitle_or_id></a></dtml-if>
</dtml-if>
</dtml-let>
## Script (Python) "change_language"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=REQUEST
##title=Modify the language cookie
##
lang = REQUEST['lang']
path = context.absolute_url()[len(REQUEST['SERVER_URL']):] or '/'
REQUEST.RESPONSE.setCookie('LOCALIZER_LANGUAGE', lang, path=path)
REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER'])
## Script (Python) "combined_subjects"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Combine all possible subjects and sub-subjects
##
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solane <jp@nexedi.com>
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
def buildCombinedSubjects(topic):
myquery = topic.buildQuery()
if myquery.has_key('Subject'):
subjects = [myquery['Subject']]
else:
subjects = []
for subtopic in topic.listSubtopics():
subjects = subjects + buildCombinedSubjects(subtopic)
return subjects
return buildCombinedSubjects(context)
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Hide Item </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>A <b>private</b> item is only available to its owner.</p>
<p>Another way to control the visibility of an item is with its <b>effective
date</b>. An item is not publicly available before its effective date,
<em>even if its status is <b>published</b></em>.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="hide">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Hide this Item "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Publish Item </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>A <b>published</b> item is available to the general
member base and anonymous visitors.</p>
<p>Another way to control the visibility of an item is with its <b>effective
date</b>. An item is not publicly available before its effective date,
<em>even if its status is <b>published</b></em>.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="publish">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Publish this Item "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Reject Item </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>Use this form to reject the publication of a content item and set its
status to <b>Private</b>, thereby making it unavailable to
other portal members and visitors.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="reject">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Reject this Item "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Release Item </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>A <b>published</b> item is available to the general
member base but not to anonymous visitors.</p>
<p>Another way to control the visibility of an item is with its <b>effective
date</b>. An item is not publicly available before its effective date,
<em>even if its status is <b>published</b></em>.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="publish">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Release this Item "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Retract Item </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>Use this form to retract a content item by setting its
status to <b>Private</b>, thereby making it unavailable to
other portal members and visitors.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="retract">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Retract this Item "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Show Item </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>A <b>restricted</b> item is available to your partners ie. Members which
have a Partner local role.</p>
<p>Another way to control the visibility of an item is with its <b>effective
date</b>. An item is not publicly available before its effective date,
<em>even if its status is <b>published</b></em>.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="show">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Show this Item to Partners "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
<dtml-comment>
Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.0 (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
</dtml-comment>
<dtml-var standard_html_header>
<dtml-let member="portal_membership.getAuthenticatedMember()"
review_state="portal_workflow.getInfoFor(this(), 'review_state')"
review_history="portal_workflow.getInfoFor(this(), 'review_history')">
<div class="Desktop">
<h1> Submit Item for Review </h1>
<dtml-if message>
<h2 class="DesktopTitle">&dtml-message;</h2>
</dtml-if>
<p align="center"><img src="corporate_workflow.png" /></p>
<p>To make an item published, it
has to be reviewed by one of the site's reviewers.
A <b>published</b> item is available to the general
member base and eventually to anonymous visitors.</p>
<p>Another way to control the visibility of an item is with its <b>effective
date</b>. An item is not publicly available before its effective date,
<em>even if its status is <b>published</b></em>.</p>
<form method="post" action="content_status_modify">
<table class="FormLayout">
<tr>
<td valign=top align=left>
<strong>Status</strong>
</td>
<td valign=top align=left>
This item is currently in <b>&dtml-review_state;</b> status.
<input type="hidden" name="workflow_action" value="submit">
</td>
</tr>
<tr>
<td valign=top align=left colspan=2>
<strong><em>Comments</em></strong><br>
<textarea name="comment" cols="60" rows="5" wrap="soft"
style="width: 100%"></textarea>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value=" Submit Item "></td>
</tr>
</table>
</form>
<dtml-if review_history>
<p><strong>Reviewing history</strong><br>
<dtml-in review_history mapping reverse>
<dtml-var time fmt="aCommon"> &dtml-action;
<dtml-if effective_date>
(effective: <dtml-var effective_date fmt="aCommon">)
</dtml-if>
by &dtml-actor;<br>
<dtml-if "_['sequence-item']['comments']">
</p><dtml-var "_['sequence-item']['comments']" fmt="structured-text"><p>
</dtml-if>
</dtml-in>
</p>
</dtml-if>
</dtml-let>
</div>
<dtml-var standard_html_footer>
This diff is collapsed.
<dtml-comment>
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets <jp@nexedi.com>
WARNING: This program as such is intended to be used by professional
programmers who take the whole responsability of assessing all potential
consequences resulting from its eventual inadequacies and bugs
This program as such is not intended to be used by end users. End
users who are looking for a ready-to-use solution with commercial
garantees and support are strongly adviced to contract a Free Software
Service Company
This program is Free Software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</dtml-comment>
<dtml-let relative_to_content="1">
<dtml-var printable_html_header>
</dtml-let>
<div class="Document">
<p class="title">&dtml-TranslatedTitle;</h1>
<dtml-var TranslatedBody>
</div>
<dtml-var printable_html_footer>
<dtml-comment>
Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets <jp@nexedi.com>
WARNING: This program as such is intended to be used by professional
programmers who take the whole responsability of assessing all potential
consequences resulting from its eventual inadequacies and bugs
This program as such is not intended to be used by end users. End
users who are looking for a ready-to-use solution with commercial
garantees and support are strongly adviced to contract a Free Software
Service Company
This program is Free Software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
</dtml-comment>
<dtml-let relative_to_content="1">
<dtml-var printable_html_header>
</dtml-let>
<div class="Document">
<p class="title">&dtml-TranslatedTitle;</h1>
<p class="summary"><dtml-var TranslatedDescription></p>
<dtml-var TranslatedBody>
</div>
<dtml-var printable_html_footer>
<dtml-let relative_to_content="1">
<dtml-var standard_html_header>
</dtml-let>
<div class="Document">
<dtml-var TranslatedBody>
</div>
<dtml-if "not portal_membership.isAnonymousUser()">
<div class="ContentBy"><dtml-var content_byline></div>
</dtml-if>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
<dtml-var standard_html_footer>
This diff is collapsed.
<dtml-let relative_to_content="1">
<dtml-var standard_html_header>
</dtml-let>
<div class="Document">
<dtml-var CookedBody>
</div>
<dtml-if "not portal_membership.isAnonymousUser()">
<div class="ContentBy"><dtml-var content_byline></div>
</dtml-if>
<div class="Discussion">
<dtml-var viewThreadsAtBottom>
</div>
<dtml-var standard_html_footer>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<ul tal:condition="python:here.public_subtopics is not []">
<span tal:repeat="item here/public_subtopics">
<li ><a tal:content="item/title_or_id" tal:attributes="href item/absolute_url">Title</a></li>
<span tal:replace="structure item/list_subtopics" />
</span>
</ul>
\ No newline at end of file
<dtml-if expr="_.has_key('root_topic')"><dtml-var root_topic>/<dtml-var id>
<dtml-let root_topic="root_topic + '/' + id"><dtml-in public_subtopics><dtml-var
list_topic_vocabulary></dtml-in></dtml-let>
<dtml-else><dtml-var id>
<dtml-let root_topic="id"><dtml-in public_subtopics><dtml-var
list_topic_vocabulary></dtml-in></dtml-let></dtml-if>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment