from AccessControl import getSecurityManager from AccessControl import ClassSecurityInfo from Acquisition import ImplicitAcquisitionWrapper from Acquisition.interfaces import IAcquirer from zope import component from zope.interface import implements, implementsOnly, implementer from zope.component import adapts, adapter from Products.CMFCore.utils import getToolByName from z3c.form.interfaces import IFormLayer, IFieldWidget, NOVALUE from z3c.form.widget import Widget, FieldWidget from collective.z3cform.datetimewidget import DateWidget from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.Five.browser import BrowserView from plone.dexterity.factory import DexterityFactory from zc.relation.interfaces import ICatalog from zope.app.intid.interfaces import IIntIds from z3c.form.converter import BaseDataConverter from Products.statusmessages.interfaces import IStatusMessage from themis.fields.interfaces import ICommission, ICommissions from zope.schema.interfaces import IDate, IContextSourceBinder, ISource from themis.fields.interfaces import IDateOnly from themis.fields.interfaces import IRadioChoice from themis.fields.interfaces import ISubjects from themis.fields.interfaces import ILegisSession from themis.fields.interfaces import IContact, IContacts from themis.fields.interfaces import IDeputy, IDeputies from themis.fields.interfaces import IMinistry, IMinistries from themis.fields.interfaces import IDeputyOrMinistry, IDeputiesOrMinistries from themis.fields.interfaces import ISpeakers from themis.fields.interfaces import IOrderedSelectAndAddWidget from themis.fields.interfaces import IRelatedDoc from themis.fields.interfaces import IRelatedDocs from themis.fields.interfaces import IMailId, IMailIdWidget from themis.fields.interfaces import IMailRefId, IMailRefIdWidget from themis.fields.interfaces import IPreviewDoc, IPreviewDocWidget from themis.fields.interfaces import IRelatedDocWidget, IRelatedDocsWidget from themis.fields.interfaces import IDocHistoLineWidget, IDocHistoLine from themis.fields.interfaces import IPointingDocs, IPointingDocsWidget from plone.formwidget.autocomplete.interfaces import IAutocompleteWidget from plone.formwidget.autocomplete.widget import AutocompleteSelectionWidget, AutocompleteMultiSelectionWidget from z3c.form.browser.orderedselect import OrderedSelectWidget from z3c.form.browser.checkbox import CheckBoxWidget from z3c.form.browser.select import SelectWidget from z3c.form.browser.text import TextWidget from z3c.form.browser.radio import RadioWidget from plone.formwidget.contenttree.widget import ContentTreeWidget, MultiContentTreeWidget from interfaces import _ @adapter(ICommission, IFormLayer) @implementer(IFieldWidget) def CommissionFieldWidget(field, request): """IFieldWidget factory for CommissionWidget.""" return FieldWidget(field, SelectWidget(request)) @adapter(ISubjects, IFormLayer) @implementer(IFieldWidget) def SubjectsFieldWidget(field, request): """IFieldWidget factory for SelectWidget.""" return FieldWidget(field, CheckBoxWidget(request)) @adapter(ICommissions, IFormLayer) @implementer(IFieldWidget) def CommissionsFieldWidget(field, request): """IFieldWidget factory for SelectWidget.""" return FieldWidget(field, CheckBoxWidget(request)) @adapter(IDeputy, IFormLayer) @implementer(IFieldWidget) def DeputyFieldWidget(field, request): """IFieldWidget factory for DeputyWidget.""" return FieldWidget(field, SelectWidget(request)) #return FieldWidget(field, AutocompleteSelectionWidget(request)) @adapter(IMinistry, IFormLayer) @implementer(IFieldWidget) def MinistryFieldWidget(field, request): """IFieldWidget factory for MinistryWidget.""" return FieldWidget(field, SelectWidget(request)) @adapter(IDeputyOrMinistry, IFormLayer) @implementer(IFieldWidget) def DeputyOrMinistryFieldWidget(field, request): """IFieldWidget factory for DeputyOrMinistryWidget.""" return FieldWidget(field, SelectWidget(request)) @adapter(IContact, IFormLayer) @implementer(IFieldWidget) def ContactFieldWidget(field, request): """IFieldWidget factory for ContactWidget.""" return FieldWidget(field, AutocompleteSelectionWidget(request)) @adapter(IDeputies, IFormLayer) @implementer(IFieldWidget) def DeputiesFieldWidget(field, request): """IFieldWidget factory for DeputiesWidget.""" return FieldWidget(field, OrderedSelectWidget(request)) @adapter(IMinistries, IFormLayer) @implementer(IFieldWidget) def MinistriesFieldWidget(field, request): """IFieldWidget factory for MinistriesWidget.""" return FieldWidget(field, OrderedSelectWidget(request)) @adapter(IDeputiesOrMinistries, IFormLayer) @implementer(IFieldWidget) def DeputiesOrMinistriesFieldWidget(field, request): """IFieldWidget factory for DeputiesOrMinistriesWidget.""" return FieldWidget(field, OrderedSelectWidget(request)) @adapter(IContacts, IFormLayer) @implementer(IFieldWidget) def ContactsFieldWidget(field, request): """IFieldWidget factory for ContactsWidget.""" return FieldWidget(field, OrderedSelectAndAddWidget(request)) @adapter(ISpeakers, IFormLayer) @implementer(IFieldWidget) def SpeakersFieldWidget(field, request): """IFieldWidget factory for SpeakersWidget.""" return FieldWidget(field, OrderedSelectWidget(request)) @adapter(ILegisSession, IFormLayer) @implementer(IFieldWidget) def LegisSessionFieldWidget(field, request): """IFieldWidget factory for LegisSessionWidget.""" return FieldWidget(field, SelectWidget(request)) @adapter(IDateOnly, IFormLayer) @implementer(IFieldWidget) def DateOnlyFieldWidget(field, request): '''IFieldWidget factory for DateOnlyWidget.''' return FieldWidget(field, DateWidget(request)) @adapter(IDate, IFormLayer) @implementer(IFieldWidget) def DateFieldWidget(field, request): '''IFieldWidget factory for DateOnlyWidget.''' return FieldWidget(field, DateWidget(request)) @adapter(IRadioChoice, IFormLayer) @implementer(IFieldWidget) def RadioChoiceFieldWidget(field, request): '''IFieldWidget factory for RadioChoiceWidget''' return FieldWidget(field, RadioWidget(request)) class OrderedSelectAndAddWidget(OrderedSelectWidget): implementsOnly(IOrderedSelectAndAddWidget) formatItem = '''function(row, idx, count, value) { return row[1]; }''' formatResult = '''function(row, idx, count) { return ""; }''' js_template = """\ (function($) { $().ready(function() { $('#%(id)s-iadd').autocomplete('%(url)s', { autoFill: false, minChars: 2, max: 10, mustMatch: false, matchContains: true, formatItem: %(formatItem)s, formatResult: %(formatResult)s }).result(%(js_callback)s); }); })(jQuery); """ def autocomplete_url(self): """Generate the URL that returns autocomplete results for this form """ form_url = self.request.getURL() return "%s/++widget++%s/@@autocomplete-search" % ( form_url, self.name ) def js(self): # Use a template if it exists, in case anything overrode this interface js_callback = 'formwidgetadd_autocomplete_ready' return self.js_template % dict(id=self.id, url=self.autocomplete_url(), formatItem=self.formatItem, formatResult=self.formatResult, klass=self.klass, title=self.title, js_callback=js_callback) @property def source(self): """We need to bind the field to the context so that vocabularies appear as sources""" return self.field.bind(self.context).value_type.source _bound_source = None @property def bound_source(self): if self._bound_source is None: source = self.source if IContextSourceBinder.providedBy(source): source = source(self.context) assert ISource.providedBy(source) self._bound_source = source return self._bound_source def deselect(self): selecteditems = [] notselecteditems = [] for selecteditem in self.selectedItems: selecteditems.append(selecteditem['content']) for item in self.items: if not item['content'] in selecteditems: if item['value'].startswith('contact:'): # skip simple contacts, to avoid the list growing too large # ideally we would have something like "favourite" contacts # populated automatically on usage frequence... continue notselecteditems.append(item) return notselecteditems class RelatedDocWidget(AutocompleteSelectionWidget): implements(IRelatedDocWidget) display_template = ViewPageTemplateFile('relateddoc_display.pt') maxResults = 50 def get_url(self, v): return v def get_label(self, v): term = self.terms.getTermByToken(v) return term.title def tuples(self): return [(self.get_url(x), self.get_label(x)) for x in self.value] @adapter(IRelatedDoc, IFormLayer) @implementer(IFieldWidget) def RelatedDocFieldWidget(field, request): '''IFieldWidget factory for RelatedDocWidget''' return FieldWidget(field, RelatedDocWidget(request)) class RelatedDocsWidget(AutocompleteMultiSelectionWidget): implements(IRelatedDocsWidget) display_template = ViewPageTemplateFile('relateddoc_display.pt') maxResults = 50 def get_url(self, v): return v def get_label(self, v): term = self.terms.getTermByToken(v) return term.title def tuples(self): return [(self.get_url(x), self.get_label(x)) for x in self.value] @adapter(IRelatedDocs, IFormLayer) @implementer(IFieldWidget) def RelatedDocsFieldWidget(field, request): '''IFieldWidget factory for RelatedDocsWidget''' return FieldWidget(field, RelatedDocsWidget(request)) return FieldWidget(field, AutocompleteMultiSelectionWidget(request)) return FieldWidget(field, MultiContentTreeWidget(request)) class MailIdWidget(TextWidget): implements(IMailIdWidget) security = ClassSecurityInfo() security.declareObjectPublic() def get_matching_object(self): catalog = getToolByName(self.context, 'portal_catalog') results = catalog(themis_mail_ref=self.value) objects = [x.getObject() for x in results] if len(objects) == 0: return None if len(objects) > 1: pass # should we display a warning? return objects[0] def href(self): object = self.get_matching_object() if object: return object.absolute_url() return None def create_href(self): form_url = self.request.getURL() from themis.config.utils import get_document_type_for_mail document_type = get_document_type_for_mail(self.context) if not document_type: return None return "%s/++widget++%s/@@createDoc" % (form_url, self.name) class MailIdCreateDoc(BrowserView): def __call__(self): from themis.config.utils import get_document_type_for_mail, get_document_location_for_mail mail = self.context.context document_type = get_document_type_for_mail(mail) if document_type is None: IStatusMessage(self.request).add(_(u"No associated document type"), 'warning') return self.request.response.redirect(mail.absolute_url()) document_location = get_document_location_for_mail(mail) if not document_location: IStatusMessage(self.request).add(_(u"No associated document location"), 'warning') return self.request.response.redirect(mail.absolute_url()) folder = getToolByName(self.context, 'portal_url').getPortalObject() for part in document_location.split('/'): if not part: continue folder = getattr(folder, part) folder.invokeFactory(document_type, id=mail.id, title=mail.title, mail_ref_id=mail.numero_courrier) document_object = getattr(folder, mail.id) return self.request.response.redirect(document_object.absolute_url()) @adapter(IMailId, IFormLayer) @implementer(IFieldWidget) def MailIdFieldWidget(field, request): """IFieldWidget factory for MailId""" return FieldWidget(field, MailIdWidget(request)) class MailRefIdWidget(TextWidget): implements(IMailRefIdWidget) def href(self): catalog = getToolByName(self.context, 'portal_catalog') results = catalog(themis_mail_number=self.value) objects = [x.getObject() for x in results] if len(objects) == 0: return '' if len(objects) > 1: pass # should we display a warning? return objects[0].absolute_url() @adapter(IMailRefId, IFormLayer) @implementer(IFieldWidget) def MailRefIdFieldWidget(field, request): """IFieldWidget factory for MailRefId""" return FieldWidget(field, MailRefIdWidget(request)) class DocHistoLineWidget(Widget): implements(IDocHistoLineWidget) def extract(self, default=NOVALUE): from themis.fields import DocHistoLine if not (self.name + '.comment') in self.request.form: return NOVALUE dochistoline = DocHistoLine() has_value = None for attr in ('comment', ): setattr(dochistoline, attr, self.request.get(self.name + '.' + attr)) has_value = has_value or getattr(dochistoline, attr) if not has_value: return NOVALUE return dochistoline @adapter(IDocHistoLine, IFormLayer) @implementer(IFieldWidget) def DocHistoLineFieldWidget(field, request): return FieldWidget(field, DocHistoLineWidget(request)) class DocHistoLineConverter(BaseDataConverter): adapts(IDocHistoLine, IDocHistoLineWidget) def toWidgetValue(self, value): print 'XXX, histoline converter, to widget value!' from themis.fields import DocHistoLine if value is None: return DocHistoLine() return value def toFieldValue(self, value): print 'XXX, to field value' return value @adapter(IPreviewDoc, IFormLayer) @implementer(IFieldWidget) def PreviewDocFieldWidget(field, request): """IFieldWidget factory for PreviewDoc""" return FieldWidget(field, PreviewDocWidget(request)) class PreviewDocWidget(Widget): implements(IPreviewDocWidget) security = ClassSecurityInfo() security.declareObjectPublic() def width(self): if hasattr(self.field, 'width'): return self.field.width return u'710px' def height(self): if hasattr(self.field, 'height'): return self.field.height return u'910px' def get_attribute_name(self): if hasattr(self.field, 'attribute_name') and self.field.attribute_name: return self.field.attribute_name else: return 'fichier' def href(self): if not hasattr(self.context, self.get_attribute_name()): return None form_url = self.request.getURL() return "%s/++widget++%s/@@file" % (form_url, self.name) def filename(self): if not hasattr(self.context, self.get_attribute_name()): return '' if getattr(self.context, self.get_attribute_name()): if self.form.widgets[self.get_attribute_name()].filename_encoded: return self.form.widgets[self.get_attribute_name()].filename_encoded return 'unknown.pdf' return '' class PreviewDocFile(BrowserView): def validate_access(self): # copied from autocompletewidget content = self.context.form.context # If the object is not wrapped in an acquisition chain # we cannot check any permission. if not IAcquirer.providedBy(content): return url = self.request.getURL() view_name = url[len(content.absolute_url()):].split('/')[1] # May raise Unauthorized # If the view is 'edit', then traversal prefers the view and # restrictedTraverse prefers the edit() method present on most CMF # content. Sigh... if not view_name.startswith('@@') and not view_name.startswith('++'): view_name = '@@' + view_name view_instance = content.restrictedTraverse(view_name) sm = getSecurityManager() sm.validate(content, content, view_name, view_instance) def __call__(self): self.validate_access() file = getattr(self.context.context, self.context.get_attribute_name()) if file.contentType: self.request.response.setHeader('Content-type', file.contentType) else: self.request.response.setHeader('Content-type', 'application/octet-stream') return file.data @adapter(IPointingDocs, IFormLayer) @implementer(IFieldWidget) def PointingDocsFieldWidget(field, request): """IFieldWidget factory for PointingDocs""" return FieldWidget(field, PointingDocsWidget(request)) class PointingDocsWidget(Widget): implements(IPointingDocsWidget) def pointing_docs(self): from plone.dexterity.content import Item if not isinstance(self.context, Item): # we do not want to call intids.getId on TypeSchemaContext (which # is passed as self.context when viewing the type in dexterity # through-the-web edit schema page) as this will get some deep part # of zope zodb/transaction layer in trouble. return [] intids = component.getUtility(IIntIds) catalog = component.getUtility(ICatalog) try: doc_intid = intids.getId(self.context) except KeyError: return [] pointing_docs = [x for x in catalog.findRelations({'to_id': doc_intid})] return pointing_docs