Back to index

plone3  3.1.7
types.py
Go to the documentation of this file.
00001 from plone.app.workflow.remap import remap_workflow
00002 from plone.memoize.instance import memoize
00003 
00004 from zope.component import getUtility
00005 from zope.i18n import translate
00006 from zope.schema.interfaces import IVocabularyFactory
00007 
00008 from Acquisition import aq_inner
00009 
00010 from Products.CMFCore.utils import getToolByName
00011 from Products.CMFPlone import PloneMessageFactory as _
00012 from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
00013 
00014 from plone.app.controlpanel.form import ControlPanelView
00015 
00016 
00017 def format_description(text, request=None):
00018     # We expect the workflow to be a text of '- ' divided bullet points.
00019     text = translate(text.strip(), domain='plone', context=request)
00020     return [s.strip() for s in text.split('- ') if s]
00021 
00022 
00023 # These are convenient / user friendly versioning policies.
00024 VERSION_POLICIES = [
00025         dict(id="off",
00026              policy=(),
00027              title=_(u"versioning_off",
00028                      default=u"No versioning")),
00029                           
00030         dict(id="manual",
00031              policy=("version_on_revert",),
00032              title=_(u"versioning_manual",
00033                      default=u"Manual")),
00034 
00035         dict(id="automatic",
00036              policy=("at_edit_autoversion", "version_on_revert"),
00037              title=_(u"versioning_automatic",
00038                      default=u"Automatic")),
00039         ]
00040 
00041 class TypesControlPanel(ControlPanelView):
00042 
00043     # Actions
00044 
00045     template = ViewPageTemplateFile('types.pt')
00046 
00047     @property
00048     @memoize
00049     def type_id(self):
00050         type_id = self.request.get('type_id', None)
00051         if type_id is None:
00052             type_id=''
00053         return type_id
00054 
00055     @property
00056     @memoize
00057     def fti(self):
00058         type_id = self.type_id
00059         portal_types = getToolByName(self.context, 'portal_types')
00060         return getattr(portal_types, type_id)
00061 
00062     def __call__(self):
00063         """Perform the update and redirect if necessary, or render the page
00064         """
00065         postback = True
00066         context = aq_inner(self.context)
00067 
00068         form = self.request.form
00069         submitted = form.get('form.submitted', False)
00070         save_button = form.get('form.button.Save', None) is not None
00071         cancel_button = form.get('form.button.Cancel', None) is not None
00072         type_id = form.get('old_type_id', None)
00073 
00074         if submitted and not cancel_button:
00075             if type_id:
00076                 portal_types = getToolByName(self.context, 'portal_types')
00077                 portal_repository = getToolByName(self.context,
00078                                                   'portal_repository')
00079                 portal_properties = getToolByName(self.context,
00080                                                   'portal_properties')
00081                 site_properties = getattr(portal_properties, 'site_properties')
00082 
00083                 fti = getattr(portal_types, type_id)
00084 
00085                 # Set FTI properties
00086 
00087                 addable = form.get('addable', False)
00088                 allow_discussion = form.get('allow_discussion', False)
00089 
00090                 fti.manage_changeProperties(global_allow = bool(addable),
00091                                             allow_discussion = bool(allow_discussion))
00092 
00093                 version_policy = form.get('versionpolicy', "off")
00094                 if version_policy!=self.current_versioning_policy():
00095                     newpolicy=[p for p in VERSION_POLICIES if p["id"]==version_policy][0]
00096 
00097 
00098                     versionable_types = list(portal_repository.getVersionableContentTypes())
00099                     if not newpolicy["policy"]:
00100                         if type_id in versionable_types:
00101                             versionable_types.remove(type_id)
00102                     else:
00103                         if type_id not in versionable_types:
00104                             versionable_types.append(type_id)
00105 
00106                     for policy in portal_repository.listPolicies():
00107                         policy_id = policy.getId()
00108                         if policy_id in newpolicy["policy"]:
00109                             portal_repository.addPolicyForContentType(type_id, policy_id)
00110                         else:
00111                             portal_repository.removePolicyFromContentType(type_id, policy_id)
00112 
00113                     portal_repository.setVersionableContentTypes(versionable_types)
00114 
00115                 searchable = form.get('searchable', False)
00116                 blacklisted = list(site_properties.getProperty('types_not_searched'))
00117                 if searchable and type_id in blacklisted:
00118                     blacklisted.remove(type_id)
00119                 elif not searchable and type_id not in blacklisted:
00120                     blacklisted.append(type_id)
00121                 site_properties.manage_changeProperties(types_not_searched = \
00122                                                         blacklisted)
00123 
00124             # Update workflow
00125             if self.have_new_workflow() and \
00126                form.get('form.workflow.submitted', False) and \
00127                save_button:
00128                 if self.new_workflow_is_different():
00129                     new_wf = self.new_workflow()
00130                     if new_wf == '[none]':
00131                         chain = ()
00132                     elif new_wf == '(Default)':
00133                         chain = new_wf
00134                     else:
00135                         chain = (new_wf,)
00136                     state_map = dict([(s['old_state'], s['new_state']) for s in \
00137                                       form.get('new_wfstates', [])])
00138                     if state_map.has_key('[none]'):
00139                         state_map[None] = state_map['[none]']
00140                         del state_map['[none]']
00141                     if type_id:
00142                         type_ids=(type_id,)
00143                     else:
00144                         wt = getToolByName(self.context, 'portal_workflow')
00145                         tt = getToolByName(self.context, 'portal_types')
00146                         nondefault = [info[0] for info in wt.listChainOverrides()]
00147                         type_ids = [type for type in tt.listContentTypes() if type not in nondefault]
00148                         wt.setDefaultChain(','.join(chain))
00149                         chain='(Default)'
00150 
00151                     remap_workflow(context, type_ids=type_ids, chain=chain,
00152                                    state_map=state_map)
00153                 else:
00154                     portal_workflow = getToolByName(context, 'portal_workflow')
00155                     if self.new_workflow()=='(Default)':
00156                         # The WorkflowTool API can not handle this sanely
00157                         cbt=portal_workflow._chains_by_type
00158                         if cbt.has_key(type_id):
00159                             del cbt[type_id]
00160                     else:
00161                         portal_workflow.setChainForPortalTypes((type_id,),
00162                                 self.new_workflow())
00163 
00164                 self.request.response.redirect('%s/@@types-controlpanel?\
00165 type_id=%s' % (context.absolute_url() , type_id))
00166                 postback = False
00167 
00168         elif cancel_button:
00169             self.request.response.redirect(self.context.absolute_url() + \
00170                                            '/plone_control_panel')
00171             postback = False
00172 
00173         if postback:
00174             return self.template()
00175 
00176     # View
00177 
00178     def versioning_policies(self):
00179         return VERSION_POLICIES
00180 
00181     @memoize
00182     def selectable_types(self):
00183         vocab_factory = getUtility(IVocabularyFactory,
00184                                    name="plone.app.vocabularies.ReallyUserFriendlyTypes")
00185         types = []
00186         for v in vocab_factory(self.context):
00187             if v.title:
00188                 title = translate(v.title, context=self.request)
00189             else:
00190                 title = translate(v.token, domain='plone', context=self.request)
00191             types.append(dict(id=v.value, title=title) )
00192         def _key(v):
00193             return v['title']
00194         types.sort(key=_key)
00195         return types
00196 
00197     def selected_type_title(self):
00198         return self.fti.Title()
00199 
00200     def selected_type_description(self):
00201         return self.fti.Description()
00202 
00203     def is_addable(self):
00204         return self.fti.getProperty('global_allow', False)
00205 
00206     def is_discussion_allowed(self):
00207         return self.fti.getProperty('allow_discussion', False)
00208 
00209     def current_versioning_policy(self):
00210         portal_repository = getToolByName(self.context, 'portal_repository')
00211         if self.type_id not in portal_repository.getVersionableContentTypes():
00212             return "off"
00213         policy = set(portal_repository.getPolicyMap().get(self.type_id, ()))
00214         for info in VERSION_POLICIES:
00215             if set(info["policy"]) == policy:
00216                 return info["id"]
00217         return None
00218 
00219     def is_searchable(self):
00220         context = aq_inner(self.context)
00221         portal_properties = getToolByName(context, 'portal_properties')
00222         blacklisted = portal_properties.site_properties.types_not_searched
00223         return (self.type_id not in blacklisted)
00224 
00225     @memoize
00226     def current_workflow(self):
00227         context = aq_inner(self.context)
00228         portal_workflow = getToolByName(context, 'portal_workflow')
00229         try:
00230             nondefault = [info[0] for info in portal_workflow.listChainOverrides()]
00231             if self.type_id in nondefault:
00232                 wf_id = portal_workflow.getChainForPortalType(self.type_id)[0]
00233             else:
00234                 default_workflow = self.default_workflow(False)
00235                 default_title = translate(default_workflow.title,
00236                                           domain='plone',
00237                                           context=self.request)
00238                 return dict(id='(Default)',
00239                         title=_(u"label_default_workflow_title",
00240                                 default=u"Default workflow (${title})",
00241                                 mapping=dict(title=default_title)),
00242                         description=format_description(default_workflow.description, self.request))
00243         except IndexError:
00244             return dict(id='[none]',
00245                     title=_(u"label_no_workflow",
00246                         default=u"No workflow"),
00247                     description=[
00248                         _(u"description_no_workflow",
00249                         default=u"This type has no workflow. The visibilty of "
00250                                 u"items of this type is determined by the "
00251                                 u"folder they are in.")])
00252         wf = getattr(portal_workflow, wf_id)
00253         title = translate(wf.title,
00254                           domain='plone',
00255                           context=self.request)        
00256         return dict(id=wf.id, title=title, description=format_description(wf.description, self.request))
00257 
00258     def available_workflows(self):
00259         vocab_factory = getUtility(IVocabularyFactory,
00260                                    name="plone.app.vocabularies.Workflows")
00261         workflows = []
00262         for v in vocab_factory(self.context):
00263             if v.title:
00264                 title = translate(v.title, context=self.request)
00265             else:
00266                 title = translate(v.token, domain='plone', context=self.request)
00267             workflows.append(dict(id=v.value, title=title) )
00268         def _key(v):
00269             return v['title']
00270         workflows.sort(key=_key)
00271         if self.type_id:
00272             # Only offer a default workflow option on a real type
00273             default_workflow = self.default_workflow(False)
00274             default_title = translate(default_workflow.title,
00275                                       domain='plone', context=self.request)
00276             workflows.insert(0, dict(id='(Default)',
00277                     title=_(u"label_default_workflow_title",
00278                             default=u"Default workflow (${title})",
00279                             mapping=dict(title=default_title)),
00280                     description=format_description(default_workflow.description, self.request)))
00281 
00282         return workflows
00283 
00284     @memoize
00285     def new_workflow(self):
00286         current_workflow = self.current_workflow()['id']
00287         if self.type_id=='':
00288             # If we are looking at the default workflow we need to show
00289             # the real workflow
00290             current_workflow=self.real_workflow(current_workflow)
00291         old_type_id = self.request.form.get('old_type_id', self.type_id)
00292         if old_type_id != self.type_id:
00293             return current_workflow
00294         else:
00295             return self.request.form.get('new_workflow', current_workflow)
00296 
00297     @memoize
00298     def have_new_workflow(self):
00299         return self.current_workflow()['id'] != self.new_workflow()
00300 
00301     @memoize
00302     def default_workflow(self, id_only=True):
00303         portal_workflow = getToolByName(self.context, 'portal_workflow')
00304         id = portal_workflow.getDefaultChain()[0]
00305         if id_only:
00306             return id
00307         else:
00308             return portal_workflow.getWorkflowById(id)
00309 
00310     @memoize
00311     def real_workflow(self, wf):
00312         if wf=='(Default)':
00313             return self.default_workflow()
00314         else:
00315             return wf
00316 
00317     @memoize
00318     def new_workflow_is_different(self):
00319         new_workflow = self.new_workflow()
00320         current_workflow = self.current_workflow()['id']
00321 
00322         return self.real_workflow(new_workflow)!=self.real_workflow(current_workflow)
00323 
00324     @memoize
00325     def new_workflow_is_none(self):
00326         return self.new_workflow() == '[none]'
00327 
00328     def new_workflow_description(self):
00329         portal_workflow = getToolByName(self.context, 'portal_workflow')
00330         current_workflow = self.current_workflow()['id']
00331         new_workflow = self.new_workflow()
00332 
00333         if self.new_workflow_is_different():
00334             if self.new_workflow_is_none():
00335                 return [_(u"description_no_workflow",
00336                     default=u"This type has no workflow. The visibilty of "
00337                             u"items of this type is determined by the "
00338                             u"folder they are in.")]
00339             new_workflow = self.real_workflow(self.new_workflow())
00340             wf = getattr(portal_workflow, new_workflow)
00341             return format_description(wf.description, self.request)
00342 
00343         return None
00344 
00345 
00346     def new_workflow_available_states(self):
00347         current_workflow = self.current_workflow()['id']
00348         if self.new_workflow_is_different():
00349             new_workflow = self.real_workflow(self.new_workflow())
00350             portal_workflow = getToolByName(self.context, 'portal_workflow')
00351             wf = getattr(portal_workflow, new_workflow)
00352             states = []
00353             for s in wf.states.objectValues():
00354                 title = translate(s.title, domain='plone', context=self.request)
00355                 states.append(dict(id=s.id, title=title))
00356             return states
00357         else:
00358             return []
00359 
00360     def suggested_state_map(self):
00361         current_workflow = self.real_workflow(self.current_workflow()['id'])
00362         new_workflow = self.real_workflow(self.new_workflow())
00363 
00364         portal_workflow = getToolByName(self.context, 'portal_workflow')
00365 
00366         if current_workflow == '[none]':
00367             new_wf = getattr(portal_workflow, new_workflow)
00368             default_state = new_wf.initial_state
00369             return [dict(old_id = '[none]',
00370                          old_title = _(u"No workflow"),
00371                          suggested_id = default_state)]
00372 
00373         elif self.new_workflow_is_different():
00374             old_wf = getattr(portal_workflow, current_workflow)
00375             new_wf = getattr(portal_workflow, new_workflow)
00376 
00377             new_states = set([s.id for s in new_wf.states.objectValues()])
00378             default_state = new_wf.initial_state
00379 
00380             states = []
00381             for old in old_wf.states.objectValues():
00382                 title = translate(old.title, domain='plone', context=self.request)
00383                 states.append(
00384                     dict(old_id = old.id,
00385                          old_title = title,
00386                          suggested_id = (old.id in new_states and \
00387                                          old.id or default_state)))
00388             return states
00389         else:
00390             return []