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
Raw Normal View History

from AccessControl import getSecurityManager
from AccessControl import ClassSecurityInfo
2011-06-07 21:56:13 +02:00
from Acquisition import ImplicitAcquisitionWrapper
from Acquisition.interfaces import IAcquirer
2011-06-07 21:56:13 +02:00
from zope import component
from zope.interface import implements, implementsOnly, implementer
2011-06-07 21:56:13 +02:00
from zope.component import adapts, adapter
from Products.CMFCore.utils import getToolByName
2011-09-28 08:27:23 +02:00
from z3c.form.interfaces import IFormLayer, IFieldWidget, NOVALUE
from z3c.form.widget import Widget, FieldWidget
2011-06-13 21:38:31 +02:00
from collective.z3cform.datetimewidget import DateWidget
2011-06-07 21:56:13 +02:00
2011-09-16 14:51:31 +02:00
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
2011-09-16 14:51:31 +02:00
2011-09-28 08:27:23 +02:00
from z3c.form.converter import BaseDataConverter
2011-10-30 15:42:27 +01:00
from Products.statusmessages.interfaces import IStatusMessage
2011-09-16 14:51:31 +02:00
2011-06-26 13:19:38 +02:00
from themis.fields.interfaces import ICommission, ICommissions
from zope.schema.interfaces import IDate, IContextSourceBinder, ISource
2011-06-26 13:19:38 +02:00
from themis.fields.interfaces import IDateOnly
2011-06-26 13:43:33 +02:00
from themis.fields.interfaces import IRadioChoice
2011-06-26 13:19:38 +02:00
from themis.fields.interfaces import ISubjects
from themis.fields.interfaces import ILegisSession
2011-06-26 13:19:38 +02:00
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
2011-10-24 21:35:19 +02:00
from themis.fields.interfaces import ISpeakers
2011-06-26 13:19:38 +02:00
from themis.fields.interfaces import IOrderedSelectAndAddWidget
2011-06-30 19:21:44 +02:00
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
2011-09-28 09:41:52 +02:00
from themis.fields.interfaces import IPreviewDoc, IPreviewDocWidget
2011-09-16 14:51:31 +02:00
from themis.fields.interfaces import IRelatedDocWidget, IRelatedDocsWidget
2011-09-28 08:27:23 +02:00
from themis.fields.interfaces import IDocHistoLineWidget, IDocHistoLine
from themis.fields.interfaces import IPointingDocs, IPointingDocsWidget
2011-06-07 21:56:13 +02:00
from plone.formwidget.autocomplete.interfaces import IAutocompleteWidget
from plone.formwidget.autocomplete.widget import AutocompleteSelectionWidget, AutocompleteMultiSelectionWidget
2011-06-07 21:56:13 +02:00
2011-06-13 21:38:31 +02:00
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
2011-06-26 13:43:33 +02:00
from z3c.form.browser.radio import RadioWidget
from plone.formwidget.contenttree.widget import ContentTreeWidget, MultiContentTreeWidget
2011-06-07 21:56:13 +02:00
2011-10-30 15:46:25 +01:00
from interfaces import _
2011-06-07 21:56:13 +02:00
@adapter(ICommission, IFormLayer)
@implementer(IFieldWidget)
def CommissionFieldWidget(field, request):
"""IFieldWidget factory for CommissionWidget."""
return FieldWidget(field, SelectWidget(request))
2011-06-13 21:38:31 +02:00
@adapter(ISubjects, IFormLayer)
@implementer(IFieldWidget)
def SubjectsFieldWidget(field, request):
"""IFieldWidget factory for SelectWidget."""
return FieldWidget(field, CheckBoxWidget(request))
2011-06-13 23:20:20 +02:00
@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))
2011-10-24 21:35:19 +02:00
@adapter(ISpeakers, IFormLayer)
@implementer(IFieldWidget)
def SpeakersFieldWidget(field, request):
"""IFieldWidget factory for SpeakersWidget."""
return FieldWidget(field, OrderedSelectWidget(request))
@adapter(ILegisSession, IFormLayer)
2011-06-14 09:37:06 +02:00
@implementer(IFieldWidget)
def LegisSessionFieldWidget(field, request):
"""IFieldWidget factory for LegisSessionWidget."""
2011-06-14 09:37:06 +02:00
return FieldWidget(field, SelectWidget(request))
2011-06-15 14:33:43 +02:00
@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))
2011-06-26 13:43:33 +02:00
@adapter(IRadioChoice, IFormLayer)
@implementer(IFieldWidget)
def RadioChoiceFieldWidget(field, request):
'''IFieldWidget factory for RadioChoiceWidget'''
return FieldWidget(field, RadioWidget(request))
class OrderedSelectAndAddWidget(OrderedSelectWidget):
implementsOnly(IOrderedSelectAndAddWidget)
2011-12-05 10:58:07 +01:00
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
2011-09-16 14:51:31 +02:00
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]
2011-06-30 19:21:44 +02:00
@adapter(IRelatedDoc, IFormLayer)
@implementer(IFieldWidget)
def RelatedDocFieldWidget(field, request):
2011-09-16 14:51:31 +02:00
'''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
2011-06-30 19:21:44 +02:00
2011-09-16 14:51:31 +02:00
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'''
2011-09-16 14:51:31 +02:00
return FieldWidget(field, RelatedDocsWidget(request))
return FieldWidget(field, AutocompleteMultiSelectionWidget(request))
2011-09-16 14:51:31 +02:00
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))
2011-09-28 08:27:23 +02:00
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
2011-09-28 09:41:52 +02:00
@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'
2011-09-28 09:41:52 +02:00
def href(self):
if not hasattr(self.context, self.get_attribute_name()):
2011-09-28 09:41:52 +02:00
return None
form_url = self.request.getURL()
return "%s/++widget++%s/@@file" % (form_url, self.name)
2011-09-28 09:41:52 +02:00
def filename(self):
if not hasattr(self.context, self.get_attribute_name()):
2011-09-28 09:41:52 +02:00
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
2011-09-28 09:41:52 +02:00
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