[gen] More work ZPT->PX.
This commit is contained in:
parent
2e9a832463
commit
34e3a3083e
31 changed files with 3287 additions and 3067 deletions
292
gen/utils.py
292
gen/utils.py
|
@ -1,7 +1,6 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
import re, os, os.path
|
||||
import re, os, os.path, base64, urllib
|
||||
from appy.shared.utils import normalizeText
|
||||
from appy.px import Px
|
||||
|
||||
# Function for creating a Zope object ------------------------------------------
|
||||
def createObject(folder, id, className, appName, wf=True, noSecurity=False):
|
||||
|
@ -10,15 +9,14 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False):
|
|||
creation of the config object), computing workflow-related info is not
|
||||
possible at this time. This is why this function can be called with
|
||||
p_wf=False.'''
|
||||
exec 'from Products.%s.%s import %s as ZopeClass' % (appName, className,
|
||||
className)
|
||||
exec 'from Products.%s.%s import %s as ZopeClass' % \
|
||||
(appName, className, className)
|
||||
# Get the tool. Depends on whether p_folder is a Zope (temp) folder or not.
|
||||
isFolder = folder.meta_type.endswith('Folder')
|
||||
tool = isFolder and folder.config or folder.getTool()
|
||||
user = tool.getUser()
|
||||
if not noSecurity:
|
||||
# Check that the user can create objects of className
|
||||
if folder.meta_type.endswith('Folder'): # Folder or temp folder.
|
||||
tool = folder.config
|
||||
else:
|
||||
tool = folder.getTool()
|
||||
user = tool.getUser()
|
||||
# Check that the user can create objects of className.
|
||||
userRoles = user.getRoles()
|
||||
allowedRoles=ZopeClass.wrapperClass.getCreators(tool.getProductConfig())
|
||||
allowed = False
|
||||
|
@ -31,267 +29,23 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False):
|
|||
raise Unauthorized("User can't create instances of %s" % \
|
||||
ZopeClass.__name__)
|
||||
obj = ZopeClass(id)
|
||||
folder._objects = folder._objects + \
|
||||
({'id':id, 'meta_type':className},)
|
||||
folder._objects = folder._objects + ({'id':id, 'meta_type':className},)
|
||||
folder._setOb(id, obj)
|
||||
obj = folder._getOb(id) # Important. Else, obj is not really in the folder.
|
||||
obj.portal_type = className
|
||||
obj.id = id
|
||||
obj._at_uid = id
|
||||
user = obj.getUser()
|
||||
if not user.getId():
|
||||
if user.name == 'System Processes':
|
||||
userId = 'admin' # This is what happens when Zope is starting.
|
||||
else:
|
||||
userId = None # Anonymous.
|
||||
else:
|
||||
userId = user.getId()
|
||||
obj.creator = userId or 'Anonymous User'
|
||||
# If no user object is there, we are at startup, before default User
|
||||
# instances are created.
|
||||
userId = user and user.login or 'system'
|
||||
obj.creator = userId
|
||||
from DateTime import DateTime
|
||||
obj.created = DateTime()
|
||||
obj.modified = obj.created
|
||||
obj.__ac_local_roles__ = { userId: ['Owner'] } # userId can be None (anon).
|
||||
obj.__ac_local_roles__ = { userId: ['Owner'] }
|
||||
if wf: obj.notifyWorkflowCreated()
|
||||
return obj
|
||||
|
||||
# Classes used by edit/view PXs for accessing information ----------------------
|
||||
class Descr:
|
||||
'''Abstract class for description classes.'''
|
||||
def get(self): return self.__dict__
|
||||
|
||||
class GroupDescr(Descr):
|
||||
'''Intermediary, on-the-fly-generated data structure that groups all fields
|
||||
sharing the same appy.gen.Group instance, that some logged user can
|
||||
see.'''
|
||||
# PX that renders a group of fields
|
||||
pxView = Px('''<p>pxGroupedFields</p>''')
|
||||
|
||||
# PX that renders a group of searches
|
||||
pxViewSearches = Px('''
|
||||
<x var="expanded=req.get(field.labelId, 'collapsed') == 'expanded'">
|
||||
<!-- Group name, prefixed by the expand/collapse icon -->
|
||||
<div class="portletGroup">
|
||||
<img class="clickable" style="margin-right: 3px" align=":dleft"
|
||||
id=":'%s_img' % field.labelId"
|
||||
src=":expanded and url('collapse.gif') or url('expand.gif')"
|
||||
onclick=":'toggleCookie(%s)' % q(field.labelId)"/>
|
||||
<x if="not field.translated">:_(field.labelId)</x>
|
||||
<x if="field.translated">:field.translated</x>
|
||||
</div>
|
||||
<!-- Group content -->
|
||||
<div var="display=expanded and 'display:block' or 'display:none'"
|
||||
id=":field.labelId" style=":'padding-left: 10px; %s' % display">
|
||||
<x for="searches in field.widgets">
|
||||
<x for="elem in searches">
|
||||
<!-- An inner group within this group -->
|
||||
<x if="elem['type'] == 'group'"
|
||||
var2="field=elem">:field.pxViewSearches</x>
|
||||
<!-- A search -->
|
||||
<x if="elem['type'] != 'group'" var2="search=elem">:search.pxView</x>
|
||||
</x>
|
||||
</x>
|
||||
</div>
|
||||
</x>''')
|
||||
|
||||
def __init__(self, group, page, metaType, forSearch=False):
|
||||
self.type = 'group'
|
||||
# All p_group attributes become self attributes.
|
||||
for name, value in group.__dict__.iteritems():
|
||||
if not name.startswith('_'):
|
||||
setattr(self, name, value)
|
||||
self.columnsWidths = [col.width for col in group.columns]
|
||||
self.columnsAligns = [col.align for col in group.columns]
|
||||
# Names of i18n labels
|
||||
labelName = self.name
|
||||
prefix = metaType
|
||||
if group.label:
|
||||
if isinstance(group.label, basestring): prefix = group.label
|
||||
else: # It is a tuple (metaType, name)
|
||||
if group.label[1]: labelName = group.label[1]
|
||||
if group.label[0]: prefix = group.label[0]
|
||||
if forSearch: gp = 'searchgroup'
|
||||
else: gp = 'group'
|
||||
self.labelId = '%s_%s_%s' % (prefix, gp, labelName)
|
||||
self.descrId = self.labelId + '_descr'
|
||||
self.helpId = self.labelId + '_help'
|
||||
# The name of the page where the group lies
|
||||
self.page = page.name
|
||||
# The widgets belonging to the group that the current user may see.
|
||||
# They will be stored by m_addWidget below as a list of lists because
|
||||
# they will be rendered as a table.
|
||||
self.widgets = [[]]
|
||||
# PX to user for rendering this group.
|
||||
self.px = forSearch and self.pxViewSearches or self.pxView
|
||||
|
||||
@staticmethod
|
||||
def addWidget(groupDict, newWidget):
|
||||
'''Adds p_newWidget into p_groupDict['widgets']. We try first to add
|
||||
p_newWidget into the last widget row. If it is not possible, we
|
||||
create a new row.
|
||||
|
||||
This method is a static method taking p_groupDict as first param
|
||||
instead of being an instance method because at this time the object
|
||||
has already been converted to a dict (for being maniputated within
|
||||
ZPTs).'''
|
||||
# Get the last row
|
||||
widgetRow = groupDict['widgets'][-1]
|
||||
numberOfColumns = len(groupDict['columnsWidths'])
|
||||
# Computes the number of columns already filled by widgetRow
|
||||
rowColumns = 0
|
||||
for widget in widgetRow: rowColumns += widget['colspan']
|
||||
freeColumns = numberOfColumns - rowColumns
|
||||
if freeColumns >= newWidget['colspan']:
|
||||
# We can add the widget in the last row.
|
||||
widgetRow.append(newWidget)
|
||||
else:
|
||||
if freeColumns:
|
||||
# Terminate the current row by appending empty cells
|
||||
for i in range(freeColumns): widgetRow.append('')
|
||||
# Create a new row
|
||||
newRow = [newWidget]
|
||||
groupDict['widgets'].append(newRow)
|
||||
|
||||
class PhaseDescr(Descr):
|
||||
'''Describes a phase.'''
|
||||
|
||||
pxPhase = Px('''
|
||||
<tr var="singlePage=len(phase['pages']) == 1">
|
||||
<td var="label='%s_phase_%s' % (zobj.meta_type, phase['name'])">
|
||||
|
||||
<!-- The title of the phase -->
|
||||
<div class="portletGroup"
|
||||
if="not singlePhase and not singlePage">::_(label)</div>
|
||||
|
||||
<!-- The page(s) within the phase -->
|
||||
<x for="aPage in phase['pages']">
|
||||
<!-- First line: page name and icons -->
|
||||
<div if="not (singlePhase and singlePage)"
|
||||
class=":aPage==page and 'portletCurrent portletPage' or \
|
||||
'portletPage'">
|
||||
<a href=":zobj.getUrl(page=aPage)">::_('%s_page_%s' % \
|
||||
(zobj.meta_type, aPage))</a>
|
||||
<x var="locked=zobj.isLocked(user, aPage);
|
||||
editable=mayEdit and phase['pagesInfo'][aPage]['showOnEdit']">
|
||||
<a if="editable and not locked"
|
||||
href="zobj.getUrl(mode='edit', page=aPage)">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
||||
<a if="editable and locked">
|
||||
<img style="cursor: help"
|
||||
var="lockDate=tool.formatDate(locked[1]);
|
||||
lockMap={'user':ztool.getUserName(locked[0]), \
|
||||
'date':lockDate};
|
||||
lockMsg=_('page_locked', mapping=lockMap)"
|
||||
src=":url('locked')" title=":lockMsg"/></a>
|
||||
<a if="editable and locked and user.has_role('Manager')">
|
||||
<img class="clickable" title=":_('page_unlock')" src=":url('unlock')"
|
||||
onclick=":'onUnlockPage(%s,%s)' % \
|
||||
(q(zobj.UID()), q(aPage))"/></a>
|
||||
</x>
|
||||
</div>
|
||||
<!-- Next lines: links -->
|
||||
<x var="links=phase['pagesInfo'][aPage].get('links')" if="links">
|
||||
<div for="link in links">
|
||||
<a href=":link['url']">:link['title']</a>
|
||||
</div>
|
||||
</x>
|
||||
</x>
|
||||
</td>
|
||||
</tr>''')
|
||||
|
||||
def __init__(self, name, obj):
|
||||
self.name = name
|
||||
self.obj = obj
|
||||
# The list of names of pages in this phase
|
||||
self.pages = []
|
||||
# The list of hidden pages in this phase
|
||||
self.hiddenPages = []
|
||||
# The dict below stores infor about every page listed in self.pages.
|
||||
self.pagesInfo = {}
|
||||
self.totalNbOfPhases = None
|
||||
# The following attributes allows to browse, from a given page, to the
|
||||
# last page of the previous phase and to the first page of the following
|
||||
# phase if allowed by phase state.
|
||||
self.previousPhase = None
|
||||
self.nextPhase = None
|
||||
self.px = self.pxPhase
|
||||
|
||||
def addPageLinks(self, appyType, obj):
|
||||
'''If p_appyType is a navigable Ref, we must add, within self.pagesInfo,
|
||||
those links.'''
|
||||
if appyType.page.name in self.hiddenPages: return
|
||||
infos = []
|
||||
for obj in appyType.getValue(obj, type="zobjects"):
|
||||
infos.append({'title': obj.title, 'url':obj.absolute_url()})
|
||||
self.pagesInfo[appyType.page.name]['links'] = infos
|
||||
|
||||
def addPage(self, appyType, obj, layoutType):
|
||||
'''Adds page-related information in the phase.'''
|
||||
# If the page is already there, we have nothing more to do.
|
||||
if (appyType.page.name in self.pages) or \
|
||||
(appyType.page.name in self.hiddenPages): return
|
||||
# Add the page only if it must be shown.
|
||||
isShowableOnView = appyType.page.isShowable(obj, 'view')
|
||||
isShowableOnEdit = appyType.page.isShowable(obj, 'edit')
|
||||
if isShowableOnView or isShowableOnEdit:
|
||||
# The page must be added.
|
||||
self.pages.append(appyType.page.name)
|
||||
# Create the dict about page information and add it in self.pageInfo
|
||||
pageInfo = {'page': appyType.page,
|
||||
'showOnView': isShowableOnView,
|
||||
'showOnEdit': isShowableOnEdit}
|
||||
pageInfo.update(appyType.page.getInfo(obj, layoutType))
|
||||
self.pagesInfo[appyType.page.name] = pageInfo
|
||||
else:
|
||||
self.hiddenPages.append(appyType.page.name)
|
||||
|
||||
def computeNextPrevious(self, allPhases):
|
||||
'''This method also fills fields "previousPhase" and "nextPhase"
|
||||
if relevant, based on list of p_allPhases.'''
|
||||
# Identify previous and next phases
|
||||
for phaseInfo in allPhases:
|
||||
if phaseInfo['name'] == self.name:
|
||||
i = allPhases.index(phaseInfo)
|
||||
if i > 0:
|
||||
self.previousPhase = allPhases[i-1]
|
||||
if i < (len(allPhases)-1):
|
||||
self.nextPhase = allPhases[i+1]
|
||||
|
||||
class SearchDescr(Descr):
|
||||
'''Describes a Search.'''
|
||||
# PX for rendering a search.
|
||||
pxView = Px('''
|
||||
<div class="portletSearch">
|
||||
<a href=":'%s?className=%s&search=%s' % \
|
||||
(queryUrl, rootClass, search['name'])"
|
||||
class=":search['name'] == currentSearch and 'portletCurrent' or ''"
|
||||
title=":search['translatedDescr']">:search['translated']</a>
|
||||
</div>''')
|
||||
|
||||
def __init__(self, search, className, tool):
|
||||
self.search = search
|
||||
self.name = search.name
|
||||
self.type = 'search'
|
||||
self.colspan = search.colspan
|
||||
if search.translated:
|
||||
self.translated = search.translated
|
||||
self.translatedDescr = search.translatedDescr
|
||||
else:
|
||||
# The label may be specific in some special cases.
|
||||
labelDescr = ''
|
||||
if search.name == 'allSearch':
|
||||
label = '%s_plural' % className
|
||||
elif search.name == 'customSearch':
|
||||
label = 'search_results'
|
||||
else:
|
||||
label = '%s_search_%s' % (className, search.name)
|
||||
labelDescr = label + '_descr'
|
||||
self.translated = tool.translate(label)
|
||||
if labelDescr:
|
||||
self.translatedDescr = tool.translate(labelDescr)
|
||||
else:
|
||||
self.translatedDescr = ''
|
||||
self.px = self.pxView
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
upperLetter = re.compile('[A-Z]')
|
||||
def produceNiceMessage(msg):
|
||||
|
@ -448,4 +202,22 @@ def callMethod(obj, method, klass=None, cache=True):
|
|||
res = method(obj)
|
||||
rq.methodCache[key] = res
|
||||
return res
|
||||
|
||||
|
||||
# Functions for manipulating the authentication cookie -------------------------
|
||||
def readCookie(request):
|
||||
'''Returns the tuple (login, password) read from the authentication
|
||||
cookie received in p_request. If no user is logged, its returns
|
||||
(None, None).'''
|
||||
cookie = request.get('_appy_', None)
|
||||
if not cookie: return None, None
|
||||
cookieValue = base64.decodestring(urllib.unquote(cookie))
|
||||
if ':' in cookieValue: return cookieValue.split(':')
|
||||
return None, None
|
||||
|
||||
def writeCookie(login, password, request):
|
||||
'''Encode p_login and p_password into the cookie set in the p_request.'''
|
||||
cookieValue = base64.encodestring('%s:%s' % (login, password)).rstrip()
|
||||
cookieValue = urllib.quote(cookieValue)
|
||||
request.RESPONSE.setCookie('_appy_', cookieValue, path='/')
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue