diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 7973c58..8cca03c 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -44,6 +44,11 @@ class ToolMixin(BaseMixin): tool = self.appy() return tool.pxHome({'self': tool}) + def query(self): + '''Returns the content of px ToolWrapper.pxQuery.''' + tool = self.appy() + return tool.pxQuery({'self': tool}) + def getHomePage(self): '''Return the home page when a user hits the app.''' # If the app defines a method "getHomePage", call it. @@ -452,9 +457,9 @@ class ToolMixin(BaseMixin): '''Guess the current layout type, according to actual URL.''' actualUrl = self.REQUEST['ACTUAL_URL'] res = '' - if actualUrl.endswith('/ui/view'): + if actualUrl.endswith('/view'): res = 'view' - elif actualUrl.endswith('/ui/edit') or actualUrl.endswith('/do'): + elif actualUrl.endswith('/edit') or actualUrl.endswith('/do'): res = 'edit' return res diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index fa2986a..61b1abd 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -203,6 +203,16 @@ class BaseMixin: obj = createObject(tool.getPath('/temp_folder'), id, className, appName) return self.goto(obj.getUrl(**urlParams)) + def view(self): + '''Returns the view PX.''' + appySelf = self.appy() + return appySelf.pxView({'self': appySelf}) + + def edit(self): + '''Returns the edit PX.''' + appySelf = self.appy() + return appySelf.pxEdit({'self': appySelf}) + def setLock(self, user, page): '''A p_user edits a given p_page on this object: we will set a lock, to prevent other users to edit this page at the same time.''' diff --git a/gen/utils.py b/gen/utils.py index 2a2aae2..8c9c267 100644 --- a/gen/utils.py +++ b/gen/utils.py @@ -1,6 +1,7 @@ # ------------------------------------------------------------------------------ import re, os, os.path 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): @@ -53,15 +54,50 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False): if wf: obj.notifyWorkflowCreated() return obj -# Classes used by edit/view templates for accessing information ---------------- +# 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 + pxGroupedFields = Px('''

pxGroupedFields

''') + + # PX that renders a group of fields + pxGroupedSearches = Px(''' + + +
+ + :_(widget['labelId']) + :widget['translated'] +
+ +
+ + + + + :widget['px'] + + + + :search['px'] + + + +
+
+ ''') + def __init__(self, group, page, metaType, forSearch=False): - '''Creates the data structure manipulated in ZPTs for p_group, the - Group instance used in the field definition.''' self.type = 'group' # All p_group attributes become self attributes. for name, value in group.__dict__.iteritems(): @@ -88,6 +124,8 @@ class GroupDescr(Descr): # 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.pxGroupedSearches or self.pxGroupedFields @staticmethod def addWidget(groupDict, newWidget): @@ -118,6 +156,55 @@ class GroupDescr(Descr): groupDict['widgets'].append(newRow) class PhaseDescr(Descr): + '''Describes a phase.''' + + pxPhase = Px(''' + + + + +
::_(label)
+ + + + +
+ ::_('%s_page_%s' % \ + (contextObj.meta_type, aPage)) + + + + + + + + + +
+ + +
+ :link['title'] +
+
+
+ + + ''') + def __init__(self, name, obj): self.name = name self.obj = obj @@ -133,6 +220,7 @@ class PhaseDescr(Descr): # 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, @@ -177,6 +265,16 @@ class PhaseDescr(Descr): class SearchDescr(Descr): '''Describes a Search.''' + # PX for rendering a search. + pxSearch = Px(''' +
+ :search['translated'] +
+ ''') + def __init__(self, search, className, tool): self.search = search self.name = search.name @@ -200,6 +298,7 @@ class SearchDescr(Descr): self.translatedDescr = tool.translate(labelDescr) else: self.translatedDescr = '' + self.px = self.pxSearch # ------------------------------------------------------------------------------ upperLetter = re.compile('[A-Z]') diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index 88bf4b8..8d7e184 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -26,8 +26,191 @@ class ToolWrapper(AbstractWrapper): ::_('front_page_text') - - ''', template=AbstractWrapper.pxTemplate, hook='content') + ''', template=AbstractWrapper.pxTemplate, hook='content') + + # Show on query list or grid, the field content for a given object. + pxQueryField = Px(''' + + + + ::obj.getSupTitle(navInfo) + :obj.Title():obj.Title()::obj.getSubTitle() + + +
+ + +
+
+
+ + + + + + +
''') + + # Show query results as a list. + pxQueryResultList = Px(''' + + + + + + + + + + + + + + + + +
+ ::ztool.truncateText(_(widget['labelId'])) + :self.pxSortAndFilter:self.pxShowDetails +
:self.pxQueryField
''') + + # Show query results as a grid. + pxQueryResultGrid = Px(''' + + + + +
+ + :self.pxQueryField + +
''') + + # Show paginated query results as a list or grid. + pxQueryResult = Px(''' +
+ + + + + + + +
+ +
+ + +

+ :searchDescr['translated'] + (:totalNumber) +  — +  :_('search_new') + +

+ + + + + + + +
+ :searchDescr['translatedDescr']
+
:self.pxAppyNavigate
+ + + + :self.pxQueryResultList + :self.pxQueryResultGrid + + + + :self.pxAppyNavigate +
+ + + :_('query_no_result')> +
+ :_('search_new')
+
+
''') + + pxQuery = Px(''' + + :self.pxPagePrologue:self.pxQueryResult + ''', template=AbstractWrapper.pxTemplate, hook='content') def validPythonWithUno(self, value): '''This method represents the validator for field unoEnabledPython.''' diff --git a/gen/wrappers/__init__.py b/gen/wrappers/__init__.py index 1470af1..274992b 100644 --- a/gen/wrappers/__init__.py +++ b/gen/wrappers/__init__.py @@ -27,8 +27,210 @@ class AbstractWrapper(object): '''Any real Appy-managed Zope object has a companion object that is an instance of this class.''' - pxPhases = Px('''

Phases

- ''') + # -------------------------------------------------------------------------- + # Navigation-related PXs + # -------------------------------------------------------------------------- + # Icon for hiding/showing details below the title. + pxShowDetails = Px(''' + + + ''') + + # Displays up/down arrows in a table header column for sorting a given + # column. Requires variables "sortable", 'filterable' and 'fieldName'. + pxSortAndFilter = Px(''' + + + + + + + + + + ''') + + # Buttons for navigating among a list of elements: next,back,first,last... + pxAppyNavigate = Px(''' +
+ + + + + + + + + + + + + + + + + +
  + :startNumber + 1 + :startNumber + len(objs) // + :totalNumber  
+
''') + + # Buttons for going to next/previous elements if this one is among bunch of + # referenced or searched objects. currentNumber starts with 1. + pxObjectNavigate = Px(''' + +
+ + + + + + + + + + + +   + :currentNumber // + :totalNumber   + + + + + + + +
+
''') + + pxNavigationStrip = Px(''' + + + + + + ''') + + # -------------------------------------------------------------------------- + # PXs for graphical elements shown on every page + # -------------------------------------------------------------------------- + # Global elements included in every page. + pxPagePrologue = Px(''' +
+ + + + + + + + + +
+ + +
+ +
+ + + +
+ +
+ + + + +
+ +
+ + + +
+ +
+ + + + + + +
+
''') + + pxPageBottom = Px(''' + ''') pxPortlet = Px(''' - :self.pxPhases + + + :phase['px'] +
+
+ if ztool.userMaySearch(rc)]"> - - ''') +
''') pxFooter = Px(''' - - ''') + ''') pxTemplate = Px(''' - + title=":_('%sTool' % appName)"> + @@ -334,9 +535,7 @@ class AbstractWrapper(object): - - - + :self.pxNavigationStrip @@ -355,8 +554,68 @@ class AbstractWrapper(object): :self.pxFooter - - ''', prologue=Px.xhtmlPrologue) + ''', prologue=Px.xhtmlPrologue) + + # PX for viewing an object ------------------------------------------------- + pxLayoutedObject = Px('''

Layouted object

''') + pxView = Px(''' + + :self.pxPagePrologue + :self.pxLayoutedObject + :self.pxPageBottom + ''', template=pxTemplate, hook='content') + + pxEdit = Px(''' + + :self.pxPagePrologue + + +
+ + + + + + :self.pxLayoutedObject +
+ + :self.pxPageBottom +
''', template=pxTemplate, hook='content') # -------------------------------------------------------------------------- # Class methods diff --git a/pod/actions.py b/pod/actions.py index 37e7fa2..38e8cca 100644 --- a/pod/actions.py +++ b/pod/actions.py @@ -175,10 +175,12 @@ class ForAction(BufferAction): # the loop # * curLoop.nb gives the index (starting at 0) if the currently # walked element. - # * curLoop.first is True if the currently walkded element is the + # * curLoop.first is True if the currently walked element is the # first one. - # * curLoop.last is True if the currently walkded element is the + # * curLoop.last is True if the currently walked element is the # last one. + # * curLoop.odd is True if the currently walked element is odd + # * curLoop.even is True if the currently walked element is even # For example, if you have a "for" statement like this: # for elem in myListOfElements # Within the part of the ODT document impacted by this statement, you @@ -231,6 +233,8 @@ class ForAction(BufferAction): loop.nb = i loop.first = i == 0 loop.last = i == (loop.length-1) + loop.even = (i%2)==0 + loop.odd = not loop.even context[self.iter] = item # Cell: add a new row if we are at the end of a row if isCell and (currentColIndex == nbOfColumns):