460 lines
16 KiB
Python
460 lines
16 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 zc.relation.interfaces import ICatalog
|
|
from zope.app.intid.interfaces import IIntIds
|
|
|
|
from z3c.form.converter import BaseDataConverter
|
|
|
|
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 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
|
|
|
|
@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(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) { console.log(row, idx,
|
|
count, value); return row[1]; }'''
|
|
formatResult = '''function(row, idx, count) { console.log("t2", row, idx,
|
|
count); return ""; }'''
|
|
|
|
|
|
js_template = """\
|
|
(function($) {
|
|
$().ready(function() {
|
|
$('#%(id)s-iadd').autocomplete('%(url)s', {
|
|
autoFill: false,
|
|
minChars: 2,
|
|
max: 10,
|
|
mustMatch: true,
|
|
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)
|
|
|
|
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
|
|
|
|
|
|
@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'200px'
|
|
|
|
def height(self):
|
|
if hasattr(self.field, 'height'):
|
|
return self.field.height
|
|
return u'100px'
|
|
|
|
def get_attribute_name(self):
|
|
if hasattr(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
|