summaryrefslogtreecommitdiffstats
path: root/themis/fields/widgets.py
blob: a642fa7b23bb9bb8cd95d5cc07c01938d132176c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
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