This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
themis.fields/themis/fields/widgets.py

508 lines
18 KiB
Python

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