class WorkflowView(browser.BungeniBrowserView): # evoque template = z3evoque.PageViewTemplateFile("workflow.html#main") # zpt #template = ViewPageTemplateFile("templates/workflow.pt") _page_title = "Workflow" def update(self, transition=None): # set up viewlets; the view is rendered from viewlets for # historic reasons; this may be refactored anytime. self.history_viewlet = WorkflowHistoryViewlet(self.context, self.request, self, None) self.action_viewlet = WorkflowActionViewlet(self.context, self.request, self, None) # update viewlets self.history_viewlet.update() self.action_viewlet.update(transition=transition) def __call__(self): self.update() # NOTE: the success_handler for the parent form is at: # bungeni.ui.forms.workflow.TransitionHandler # To get the form data: # av = self.action_viewlet # data = {} # form.getWidgetsData(av.widgets, av.prefix, data) template = self.template() return template
class AjaxContainerListing(container.ContainerListing, browser.BungeniBrowserView): """Container listing as an HTML Page. """ formatter_factory = yuiwidget.ContainerDataTableFormatter # evoque template = z3evoque.PageViewTemplateFile("container.html#generic") def __call__(self): self.update() return self.template() @property def form_name(self): dm = self.context.domain_model return getattr(model.queryModelDescriptor(dm), "container_name", dm.__name__) @property def prefix(self): return "container_contents" @property def formatter(self): context = proxy.removeSecurityProxy(self.context) formatter = self.formatter_factory(context, self.request, (), prefix=self.prefix, columns=self.columns) formatter.cssClasses["table"] = "listing" formatter.table_id = "datacontents" return formatter
class WorkflowView(browser.BungeniBrowserView): """This view is linked to by the "workflow" context action and dislays the workflow history and the action viewlet with all possible transitions """ # evoque template = z3evoque.PageViewTemplateFile("workflow.html#main") # zpt #template = ViewPageTemplateFile("templates/workflow.pt") _page_title = "Workflow" def update(self, transition=None): # !+RENAME(mr, apr-2011) should be transition_id # # set up viewlets; the view is rendered from viewlets for # historic reasons; this may be refactored anytime. if IAuditable.providedBy(self.context): self.history_viewlet = WorkflowHistoryViewlet( self.context, self.request, self, None) self.history_viewlet.update() self.action_viewlet = WorkflowActionViewlet( self.context, self.request, self, None) self.action_viewlet.update(transition=transition) def __call__(self): self.update() template = self.template() return template
class WorkflowChangeStateView(WorkflowView): """This gets called on selection of a transition from the menu i.e. NOT: a) when clicking on one of the transition buttons in the workflow form. b) when clicking Add of an object (automatic transitions). """ # evoque ajax_template = z3evoque.PageViewTemplateFile("workflow.html#ajax") # zpt #ajax_template = ViewPageTemplateFile("templates/workflow_ajax.pt") def __call__(self, transition_id=None, headless=False): # parameters coming in via URL querystring or post vars ! method = self.request["REQUEST_METHOD"] # !+ALWAYS_POST(mr, sep-2011) requests coming from link clicks (GETs) # from the bungeni Web UI seem to always be intercepted and switched # into POSTs. workflow = interfaces.IWorkflow(self.context) require_confirmation = True if transition_id is not None: self.update(transition_id) require_confirmation = workflow.get_transition( transition_id).require_confirmation else: self.update() if get_mask( self.context) == 'manual' and not self.context.registry_number: require_confirmation = True if (not require_confirmation and method == "POST"): actions = bindTransitions(self.action_viewlet, (transition_id, ), workflow) assert len(actions) == 1 # execute action # !+ should pass self.request.form as data? e.g. value is: # {u"next_url": u"...", u"transition": u"submit_response"} result = actions[0].success({}) # !+UNUSED(mr, jun-2011) this result is never used! if headless: actions = get_actions("context_workflow", self.context, self.request) state_title = workflow.get_state(self.context.status).title result = self.ajax_template(actions=actions, state_title=state_title) if require_confirmation: self.request.response.setStatus(403) else: self.request.response.setStatus(200) self.request.response.setResult(result) self.request.response.setHeader("Content-Type", "text/xml") return result template = self.template() return template
class DisplayForm(catalyst.DisplayForm, browser.BungeniBrowserView): template = z3evoque.PageViewTemplateFile("content.html#view") #template = ViewPageTemplateFile("templates/content-view.pt") form_name = _("View") def __call__(self): return self.template()
class WorkflowChangeStateView(WorkflowView): """This gets called on selection of a transition from the menu i.e. NOT: a) when clicking on one of the trasition buttons in the workflow form. b) when clicking Add of an object (automatic transitions). """ # evoque ajax_template = z3evoque.PageViewTemplateFile("workflow.html#ajax") # zpt #ajax_template = ViewPageTemplateFile("templates/workflow_ajax.pt") def __call__(self, headless=False, transition=None): # !+RENAME(mr, apr-2011) should be transition_id method = self.request["REQUEST_METHOD"] workflow = interfaces.IWorkflow(self.context) # !+REWITE(mr, jun-2011) the following needs to be rewritten! if transition: state_transition = workflow.get_transition(transition) require_confirmation = getattr(state_transition, "require_confirmation", False) self.update(transition) else: self.update() if transition and require_confirmation is False and method == "POST": actions = bindTransitions(self.action_viewlet, (transition, ), None, workflow) assert len(actions) == 1 # execute action # !+ should pass self.request.form as data? e.g. value is: # {u"next_url": u"...", u"transition": u"submit_response"} result = actions[0].success({}) # !+REWITE(mr, jun-2011) this result is never used! if headless is True: actions = get_actions("context_workflow", self.context, self.request) state_title = workflow.get_state(self.context.status).title result = self.ajax_template(actions=actions, state_title=state_title) # !+REWITE(mr, jun-2011) require_confirmation only defined when # transition is True! if require_confirmation is True: self.request.response.setStatus(403) else: self.request.response.setStatus(200) self.request.response.setResult(result) self.request.response.setHeader("Content-Type", "text/xml") return result template = self.template() return template
class WorkspaceSectionView(browser.BungeniBrowserView): # evoque __call__ = z3evoque.PageViewTemplateFile("workspace.html#section_page") # zpt #__call__ = ViewPageTemplateFile("templates/workspace-section.pt") # set on request.layer_data user_id = None user_group_ids = None government_id = None ministries = None role_interface_mapping = { u'bungeni.Admin': interfaces.IAdministratorWorkspace, u'bungeni.Minister': interfaces.IMinisterWorkspace, u'bungeni.MP': interfaces.IMPWorkspace, u'bungeni.Speaker': interfaces.ISpeakerWorkspace, u'bungeni.Clerk': interfaces.IClerkWorkspace } def __init__(self, context, request): """self:zope.app.pagetemplate.simpleviewclass.SimpleViewClass -> templates/workspace-index.pt context:bungeni.core.content.Section """ LD = IAnnotations(request)["layer_data"] assert interfaces.IWorkspaceSectionLayer.providedBy(request) assert LD.get("workspaces") is not None super(WorkspaceSectionView, self).__init__(context, request) cls_name = self.__class__.__name__ # NOTE: Zope morphs this class's name to "SimpleViewClass from ..." log.debug("%s.__init__ %s context=%s url=%s" % (cls_name, self, self.context, request.getURL())) # transfer layer data items, for the view/template self.user_id = LD.user_id self.user_group_ids = LD.user_group_ids self.government_id = LD.government_id # may be None self.ministries = LD.ministries # may be None if self.ministries: # then, ONLY if an ancestor container is actually a Ministry, # this must be a MinisterWorkspace if misc.get_parent_with_interface(self, model_interfaces.IMinistry): interface.alsoProvides(self, interfaces.IMinisterWorkspace) # roles are function of the context, so always recalculate roles = common.get_context_roles(self.context) for role_id in roles: iface = self.role_interface_mapping.get(role_id) if iface is not None: interface.alsoProvides(self, iface) log.debug("%s.__init__ %s" % (cls_name, debug.interfaces(self)))
class WorkflowChangeStateView(WorkflowView): """This gets called on selection of a transition from the menu i.e. NOT when clicking on one of the trasition buttons in the workflow form. """ # evoque ajax_template = z3evoque.PageViewTemplateFile("workflow.html#ajax") # zpt #ajax_template = ViewPageTemplateFile("templates/workflow_ajax.pt") def __call__(self, headless=False, transition=None): method = self.request["REQUEST_METHOD"] if transition: wf = interfaces.IWorkflow(self.context) state_transition = wf.getTransitionById(transition) require_confirmation = getattr(state_transition, "require_confirmation", False) self.update(transition) else: self.update() if transition and require_confirmation is False and method == "POST": actions = bindTransitions(self.action_viewlet, (transition, ), None, interfaces.IWorkflow(self.context)) assert len(actions) == 1 # execute action # !+ should pass self.request.form as data? e.g. value is: # {u"next_url": u"...", u"transition": u"submit_response"} result = actions[0].success({}) if headless is True: actions = get_actions("context_workflow", self.context, self.request) state_title = IWorkflowInfo( self.context).workflow().workflow.states[ self.context.status].title result = self.ajax_template(actions=actions, state_title=state_title) if require_confirmation is True: self.request.response.setStatus(403) else: self.request.response.setStatus(200) self.request.response.setResult(result) self.request.response.setHeader("Content-Type", "text/xml") return result template = self.template() return template
class ChangeLog(ChangeBaseView, zope.publisher.browser.BrowserPage): """Change Log View for an object """ # evoque __call__ = z3evoque.PageViewTemplateFile("audit.html#changes") # zpt #__call__ = ViewPageTemplateFile("templates/changes.pt") _page_title = "Change Log" def __init__(self, context, request): super(ChangeLog, self).__init__(context, request) if hasattr(self.context, "short_name"): self._page_title = "%s: %s" % (_( self._page_title), _(self.context.short_name))
class ReorderForm(PageForm): """Item reordering form. We use an intermediate list of ids to represent the item order. Note that this form must be subclassed with the ``save_ordering`` method overriden. """ class IReorderForm(interface.Interface): ordering = schema.List(title=u"Ordering", value_type=schema.TextLine()) # evoque template = z3evoque.PageViewTemplateFile("form.html#page") # zpt #template = NamedTemplate("alchemist.form") form_name = _(u"Item reordering") form_fields = formlib.form.Fields(IReorderForm, render_context=True) def setUpWidgets(self, ignore_request=False): class context: ordering = list(self.context) self.adapters = { self.IReorderForm: context, } self.widgets = formlib.form.setUpWidgets(self.form_fields, self.prefix, self.context, self.request, form=self, adapters=self.adapters, ignore_request=ignore_request) def save_ordering(self, ordering): raise NotImplementedError("Must be defined by subclass") @formlib.form.action(_(u"Save")) def handle_save(self, action, data): self.save_ordering(data["ordering"])
class DeleteForm(PageForm): """Delete-form for Bungeni content. Confirmation The user is presented with a confirmation form which details the items that are going to be deleted. Subobjects Recursively, a permission check is carried out for each item that is going to be deleted. If a permission check fails, an error message is displayed to the user. Will redirect back to the container on success. """ # evoque template = z3evoque.PageViewTemplateFile("delete.html") # zpt # !+form_template(mr, jul-2010) this is unused here, but needed by # some adapter of this "object delete" view #form_template = NamedTemplate("alchemist.form") #template = ViewPageTemplateFile("templates/delete.pt") _next_url = None form_fields = formlib.form.Fields() def _can_delete_item(self, action): return True def nextURL(self): return self._next_url def update(self): self.subobjects = self.get_subobjects() super(DeleteForm, self).update() def get_subobjects(self): return () def delete_subobjects(self): return 0 @formlib.form.action(_(u"Delete"), condition=_can_delete_item) def handle_delete(self, action, data): count = self.delete_subobjects() container = self.context.__parent__ trusted = removeSecurityProxy(self.context) session = Session() session.delete(trusted) count += 1 try: session.flush() except IntegrityError, e: # this should not happen in production; it's a critical # error, because the transaction might have failed in the # second phase of the commit session.rollback() log.critical(e) self.status = _(u"Could not delete item due to " "database integrity error") return self.render() # !+SESSION_CLOSE(taras.sterch, july-2011) there is no need to close the # session. Transaction manager will take care of this. Hope it does not # brake anything. #session.close() #TODO: check that it is removed from the index! notify( ObjectRemovedEvent(self.context, oldParent=container, oldName=self.context.__name__)) # we have to switch our context here otherwise the deleted object will # be merged into the session again and reappear magically self.context = container next_url = self.nextURL() if next_url is None: next_url = url.absoluteURL(container, self.request) + \ "/?portal_status_message=%d items deleted" % count self.request.response.redirect(next_url)
class PageForm(BaseForm, formlib.form.PageForm, browser.BungeniBrowserView): #template = NamedTemplate("alchemist.form") template = z3evoque.PageViewTemplateFile("form.html#page")
class VersionLogView(browser.BungeniBrowserView, forms.common.BaseForm): class IVersionEntry(interface.Interface): commit_message = schema.Text(title=_(u"Change Message")) form_fields = formlib.form.Fields(IVersionEntry) formatter_factory = TableFormatter # evoque render = z3evoque.PageViewTemplateFile("version.html") # zpt #render = ViewPageTemplateFile("templates/version.pt") diff_view = None def __init__(self, context, request): super(VersionLogView, self).__init__(context.__parent__, request) # table to display the versions history formatter = date.getLocaleFormatter(self.request, "dateTime", "short") # !+ note this breaks the previous sort-dates-as-strings-hack of # formatting dates, for all locales, as date.strftime("%Y-%m-%d %H:%M") # that, when sorted as a string, gives correct results. self.columns = [ column.SelectionColumn(lambda item: str(item.version_id), name="selection"), column.GetterColumn(title=_(u"version"), getter=lambda i, f: '<a href="%s">%d</a>' % ("%s/versions/obj-%d" % (f.url, i.version_id), i.version_id)), column.GetterColumn(title=_(u"manual"), getter=lambda i, f: i.manual), column.GetterColumn( title=_(u"modified"), getter=lambda i, f: formatter.format(i.change.date_active)), column.GetterColumn(title=_(u"by"), getter=lambda i, f: i.change.user_id), column.GetterColumn(title=_(u"message"), getter=lambda i, f: i.change.description), ] self.selection_column = self.columns[0] def listing(self): # set up table values = list(self._versions.values()) values.sort(key=operator.attrgetter("version_id")) values.reverse() formatter = self.formatter_factory( self.context, self.request, values, prefix="results", visible_column_names=[c.name for c in self.columns], columns=self.columns) # the column getter methods expect an ``url`` attribute formatter.url = url.absoluteURL(self.context, self.request) # update and render formatter.updateBatching() return formatter() def has_write_permission(self, context): """check that the user has the rights to edit the object, if not we assume he has no rights to make a version assumption is here that if he has the rights on any of the fields he may create a version.""" trusted = removeSecurityProxy(self.context) table = orm.class_mapper(trusted.__class__).mapped_table for column in table.columns: try: if canWrite(self.context, column.name): return True except ForbiddenAttribute: pass else: return False # !+action_url(mr, jul-2010) - throughout bungeni UI, defined only here @property def action_url(self): # this avoids that "POST"ed forms get a "@@index" appended to action URL return "" # !+action_method(mr, jul-2010) - throughout bungeni UI, defined only here @property def action_method(self): # XXX - for forms that only View information, this should return "get" # e.g. business / questions / <q> / versions / Show Differences return "post" @formlib.form.action(label=_("New Version"), condition=has_write_permission) def handle_new_version(self, action, data): self._versions.create(message=data["commit_message"], manual=True) self.status = _(u"New Version Created") @formlib.form.action(label=_("Revert To"), condition=has_write_permission) def handle_revert_version(self, action, data): selected = getSelected(self.selection_column, self.request) if len(selected) != 1: self.status = _("Select one item to revert to") return version = self._versions.get(selected[0]) message = data["commit_message"] self._versions.revert(version, message) self.status = (_(u"Reverted to Previous Version %s") % (version.version_id)) @formlib.form.action(label=_("Show Differences"), name="diff", validator=lambda form, action, data: ()) def handle_diff_version(self, action, data): self.status = _("Displaying differences") selected = getSelected(self.selection_column, self.request) if len(selected) not in (1, 2): self.status = _("Select one or two items to show differences") return context = removeSecurityProxy(self.context) source = self._versions.get(selected[0]) try: target = self._versions.get(selected[1]) if source.version_id > target.version_id: t = source source = target target = t except IndexError: target = context diff_view = DiffView(source, target, self.request) self.diff_view = diff_view(*filter(IIModelInterface.providedBy, interface.providedBy(context))) log.debug("handle_diff_version: source=%s target=%s \n%s" % (source, target, self.diff_view)) def setUpWidgets(self, ignore_request=False): # setup widgets in data entry mode not bound to context actions = self.actions self.actions = [] for action in actions: if getattr(action, "condition", None): if action.condition(self, self.context): self.actions.append(action) else: self.actions.append(action) if not self.has_write_permission(self.context): self.form_fields = self.form_fields.omit("commit_message") self.adapters = {} self.widgets = formlib.form.setUpDataWidgets( self.form_fields, self.prefix, self.context, self.request, ignore_request=ignore_request) @property def _versions(self): instance = removeSecurityProxy(self.context) versions = IVersioned(instance) return versions def __call__(self): self.update() return self.render()
class PageForm(BaseForm, formlib.form.PageForm, browser.BungeniBrowserView): template = z3evoque.PageViewTemplateFile("form.html#page")