source: trunk/FieldsWidgets.py @ 518

Revision 518, 19.0 KB checked in by vahur, 13 years ago (diff)

closes #529 spent 2h
chapter-like widget. it's a bit mess but otherwise working
also added default cover images for presentation and multimedia materials

  • Property svn:eol-style set to native
Line 
1# Copyright 2006 by the LeMill Team (see AUTHORS)
2#
3# This file is part of LeMill.
4#
5# LeMill is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# LeMill is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with LeMill; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19from AccessControl import ClassSecurityInfo
20from Products.Archetypes.Registry import registerField, registerWidget
21from Products.Archetypes.Field import StringField, LinesField, ReferenceField
22from Products.Archetypes.Widget import TextAreaWidget, StringWidget, SelectionWidget, TypesWidget
23from config import to_unicode
24from Products.Archetypes import config
25from string import letters, punctuation
26from Products.CMFCore.utils import getToolByName
27from ZODB.PersistentMapping import PersistentMapping
28from Products.Archetypes.utils import shasattr
29from Acquisition import aq_base
30
31from types import ListType, TupleType, StringType, UnicodeType, InstanceType
32
33import re
34
35class WYSIWYMWidget(TextAreaWidget):
36    _properties = TextAreaWidget._properties.copy()
37    _properties.update({
38        'macro' : 'wysiwym',
39        'helper_js' : ('js_helpers.js',),
40        'helper_css' : (),
41        'cols' : 40,
42        'rows' : 10,
43        'WYSIWYMLines' : False,
44        'no_label' : True,
45    })
46
47registerWidget(WYSIWYMWidget,
48    title='WYSIWYM Widget',
49    description='WYSIWYM Widget',
50    used_for=('Products.LeMill.FieldsWidgets.WYSIWYMField',)
51)
52
53class ChapterWidget(TextAreaWidget):
54    _properties = TextAreaWidget._properties.copy()
55    _properties.update({
56        'macro' : 'widget_chapter',
57        'helper_js' : ('js_helpers.js',),
58        'helper_css' : (),
59        'cols' : 40,
60        'rows' : 10,
61        'WYSIWYMLines' : False,
62        'no_label' : True,
63    })
64
65registerWidget(ChapterWidget,
66    title='Chapter Widget',
67    description='Chapter Widget',
68    used_for=('Products.LeMill.FieldsWidgets.ChapterField',)
69)
70
71class WYSIWYMLinesWidget(WYSIWYMWidget):
72    _properties = WYSIWYMWidget._properties.copy()
73    _properties.update({
74        'rows' : 5,
75        'WYSIWYMLines' : True,
76    })
77
78registerWidget(WYSIWYMLinesWidget,
79    title='WYSIWYM Lines Widget',
80    description='WYSIWYM Lines Widget',
81    used_for=('Products.LeMill.FieldsWidgets.WYSIWYMLinesField',)
82)
83
84
85class TagsWidget(StringWidget):
86    _properties = StringWidget._properties.copy()
87    _properties.update({
88        'macro' : 'tagswidget',
89    })
90   
91registerWidget(TagsWidget,
92    title='Tags Widget',
93    description='Tags Widget',
94    used_for=('Products.LeMill.TagsField.TagsField',)
95)
96
97class MessengerWidget(TypesWidget):
98    _properties = TypesWidget._properties.copy()
99    _properties.update({
100        'macro' : 'messengerwidget',
101    },)
102
103    security = ClassSecurityInfo()
104
105    security.declarePublic('process_form')
106    def process_form(self, instance, field, form, empty_marker=None,
107                     emptyReturnsMarker=False):
108        """Concatenates address + messenger type, but if address already contains something like prefix (word:) then removes it.
109        """
110        name = field.getName()
111        otherName = "%s_other" % name
112        value = form.get(name, empty_marker)
113        othervalue = form.get(otherName, empty_marker)
114        if value is empty_marker:
115            value = form.get(name, empty_marker)
116        if (not value or value == empty_marker):
117            return empty_marker
118        if not othervalue == empty_marker:
119            if len(value.split(':'))>1:
120                value=value.split(':')
121                value=value[1]
122            value = "%s:%s" % (othervalue,value)
123        return value, {}
124
125
126registerWidget(MessengerWidget,
127    title='Messenger Widget',
128    description='Messenger Widget',
129    used_for=('Products.Archetypes.Field.StringField',)
130)
131
132class Age_rangeWidget(StringWidget):
133    _properties = StringWidget._properties.copy()
134    _properties.update({
135        'macro' : 'age_rangewidget',
136    })
137
138    security = ClassSecurityInfo()
139
140    security.declarePublic('process_form')
141    def process_form(self, instance, field, form, empty_marker=None,
142                     emptyReturnsMarker=False):
143        name = field.getName()
144        otherName = "%s_other" % name
145        value = form.get(name, empty_marker)
146        othervalue = form.get(otherName, empty_marker)
147        if value == othervalue == empty_marker and emptyReturnsMarker:
148            return empty_marker
149        # ugly validation -- can't use default validators here
150        if value != empty_marker:
151            if value.isdigit():
152                value=int(value)
153            else:
154                value=value.strip(letters+punctuation)
155                if value.isdigit():
156                    value=int(value)
157                else:
158                    value=0
159        if othervalue != empty_marker:
160            if othervalue.isdigit():
161                othervalue=int(othervalue)
162            else:
163                othervalue=othervalue.strip(letters+punctuation)
164                if othervalue.isdigit():
165                    othervalue=int(othervalue)
166                else:
167                    othervalue=0
168        if value>othervalue>0:
169            x=value
170            value=othervalue
171            othervalue=x
172        value=min(100,max(0,value))
173        othervalue=min(100,max(0,othervalue))
174        if value==othervalue==0:
175            return None, {}
176        else:
177            if value==0: value=''           
178            if othervalue==0: othervalue=''           
179            value = "%s-%s" % (value, othervalue)
180            return value, {}
181
182   
183registerWidget(Age_rangeWidget,
184    title='Age range Widget',
185    description='Age range Widget',
186    used_for=('Products.Archetypes.Field.StringField',)
187)
188
189class MobileWidget(StringWidget):
190    _properties = StringWidget._properties.copy()
191    _properties.update({
192        'macro' : 'mobilewidget',
193    })
194
195    security = ClassSecurityInfo()
196
197    security.declarePublic('process_form')
198    def process_form(self, instance, field, form, empty_marker=None,
199                     emptyReturnsMarker=False):
200        name = field.getName()
201        otherName = "%s_checkbox" % name
202        value = form.get(name, empty_marker)
203        othervalue = form.get(otherName, empty_marker)
204        if value == empty_marker and emptyReturnsMarker:
205            return empty_marker
206        if othervalue=="1":
207            value = "*SMS*%s" % value
208        return value, {}
209
210   
211registerWidget(MobileWidget,
212    title='Mobile Widget',
213    description='Mobile Widget',
214    used_for=('Products.Archetypes.Field.StringField',)
215)
216
217
218class CopyrightWidget(SelectionWidget):
219    _properties = SelectionWidget._properties.copy()
220    _properties.update({
221        'macro' : 'copyrightwidget',
222        'no_label' : True,
223    })
224   
225registerWidget(CopyrightWidget,
226    title='Copyright Widget',
227    description='Copyright selection Widget',
228    used_for=('Products.LeMill.StringField.StringField',)
229)
230
231
232
233class PieceWidget(SelectionWidget):
234    _properties = SelectionWidget._properties.copy()
235    _properties.update({
236        'macro' : 'widget_pieceselector',
237        'collections_only' : False,
238        'helper_js' : ('js_helpers.js',),
239        'fieldsToQuery' : ['material-piece-', 'material-file-'],
240        'show_added' : True,
241    })
242
243    security = ClassSecurityInfo()
244
245    security.declarePublic('process_form')
246    def process_form(self, instance, field, form, empty_marker=None,
247            emptyReturnsMarker=False):
248        """ goes through the form and tries to find all references to pieces. """
249        value = []
250        pieces = []
251        for i in range(0,99):
252            tmp = []
253            for f in self._properties['fieldsToQuery']:
254                one = form.get(f+str(i), empty_marker)
255                if one and one is not empty_marker:
256                    if str(one) != '0':
257                        tmp.append(one)
258            if len(tmp)>0:
259                pieces.append(tmp)
260            tmp = []
261        for p in pieces:
262            uid = p[0]
263            if len(p)>1: # we have both uid and FileUpload instance. we're taking FilUpload
264                # upload piece, get uid
265                pass
266            value.append(uid)
267        if value == []:
268            return empty_marker
269        return value, {}
270
271registerWidget(PieceWidget,
272    title='Piece Widget',
273    description='Piece selection Widget',
274    used_for=('Products.LeMill.PieceField',)
275)
276
277
278class refImagesWidget(SelectionWidget):
279    _properties = SelectionWidget._properties.copy()
280    _properties.update({
281        'macro' : 'imageselection',
282        'helper_js' : ('js_helpers.js',),
283    })     
284
285class ImageSelectorWidget(SelectionWidget):
286    _properties = SelectionWidget._properties.copy()
287    _properties.update({
288        'macro' : 'widget_imageselector',
289        'helper_js' : ('js_helpers.js',),
290    })
291
292class PieceField(ReferenceField):
293    """ A field that stores references to pieces. Can be limited to images only etc. """
294    __implements__ = ReferenceField.__implements__
295    _properties = ReferenceField._properties.copy()
296    _properties.update({
297        'widget' : PieceWidget,
298        'multiValued' : True,
299        'piece_types' : 'all',
300        'allowed_types':('Piece',),
301       
302    })
303
304    security = ClassSecurityInfo()
305
306    security.declarePrivate('set')
307    def set(self, instance, value, **kwargs):
308        allowed = None
309        try:
310            allowed=kwargs[piece_types]
311        except NameError:
312            allowed = self._properties['piece_types']
313        # this is mostly copied from Archetypes.Field.ReferenceField.set with some additional checks and some shortcuts
314
315        # locating instances and creating pieces
316        new_values = []
317        for v in value:
318            if type(v) != InstanceType:
319                new_values.append(v)
320                continue
321            if v and v.filename:
322                id = instance.generateUniqueId('Piece')
323                new_id = instance.content.invokeFactory('Piece', id, title=instance.Title()+'-image')
324                new_piece = getattr(instance.content, new_id)
325                uid = new_piece.UID()
326                new_piece.edit(image=v, file=v, language='')
327                new_values.append(uid)
328        old_values = value
329        value = new_values
330
331        tool = getToolByName(instance, 'reference_catalog')
332        targetUIDs = [ref.targetUID for ref in
333                      tool.getReferences(instance, self.relationship)]
334
335        if value is None:
336            value = ()
337
338        if not isinstance(value, (ListType, TupleType)):
339            value = value,
340        elif not self.multiValued and len(value) > 1:
341            value = value[0],
342
343        #convert objects to uids if necessary and check if they are correct subtype
344        uids = []
345        for v in value:
346            if type(v) in [StringType, UnicodeType]:
347                vobj=tool(UID=v)
348                # AttributeError: LazyCat instance has no attribute 'getObject'
349                #vobj=vobj.getObject()
350                if allowed=='all' or (allowed=='images' and vobj.isImage()):                 
351                    uids.append(v)
352            else:
353                if allowed=='all' or (allowed=='images' and v.isImage()): 
354                    uids.append(v.UID())
355
356        add = [v for v in uids if v and v not in targetUIDs]
357        sub = [t for t in targetUIDs if t not in uids]
358
359        # tweak keyword arguments for addReference
360        addRef_kw = kwargs.copy()
361        addRef_kw.setdefault('referenceClass', self.referenceClass)
362        if addRef_kw.has_key('schema'): del addRef_kw['schema']
363
364        for uid in add:
365            __traceback_info__ = (instance, uid, value, targetUIDs)
366            # throws IndexError if uid is invalid
367            tool.addReference(instance, uid, self.relationship, **addRef_kw)
368
369        for uid in sub:
370            tool.deleteReference(instance, uid, self.relationship)
371
372        if self.callStorageOnSet:
373            #if this option is set the reference fields's values get written
374            #to the storage even if the reference field never use the storage
375            #e.g. if i want to store the reference UIDs into an SQL field
376            ObjectField.set(self, instance, self.getRaw(instance), **kwargs)
377
378registerField(PieceField,
379               title='Piece Field',
380               description=('References to Pieces'),
381               )
382
383
384
385class TagsField(LinesField):
386    """ A field that stores tags """
387    __implements__ = LinesField.__implements__
388    _properties = LinesField._properties.copy()
389    _properties.update({
390        'widget' : TagsWidget,
391    })
392
393    security = ClassSecurityInfo()
394
395    def set(self, instance, value, **kwargs):
396        tags = []
397        tags_dirty = []
398    if isinstance(value,str):
399            value = value.lower()
400            tags_dirty = value.split(',')
401            value = tags_dirty
402            tags = []
403        if isinstance(value, tuple) or isinstance(value, list):
404            tags_dirty = value
405            [ tags.append(t.strip().lower()) for t in tags_dirty if t.strip().lower() not in tags ]
406            value = ','.join(tags)
407    if isinstance(value,str):
408        value = value.replace(',','\n')
409        LinesField.set(self, instance, value, **kwargs)
410                       
411    def getRaw(self, instance, **kwargs):
412        value = self.get(instance, **kwargs)
413        return ', '.join(value)
414
415    def get(self, instance, **kwargs):
416        return LinesField.get(self, instance, **kwargs)
417
418registerField(TagsField,
419               title='Tags Field',
420               description=('Tags Field'),
421               )
422
423class WYSIWYMField(StringField):
424    """A field that stores WYSIWYM strings"""
425    _properties = StringField._properties.copy()
426    _properties.update({
427        'widget' : WYSIWYMWidget,
428        'edit_accessor' :  "getRaw",
429        'default_output_type' : 'text/x-html-captioned',
430        'default_content_type' : 'text/html',
431    })   
432   
433    security = ClassSecurityInfo()
434   
435   
436    def getRaw(self, instance, **kwargs):
437        value = StringField.get(self, instance, **kwargs)
438        return value
439   
440    def get(self, instance, **kwargs):
441        ltool = getToolByName(instance, 'lemill_tool')
442        value = StringField.get(self, instance, **kwargs)
443        return ltool.render(value)
444
445    # render -method has moved to LeMillTools, because it is needed also outside the wysiwym field.
446
447registerField(WYSIWYMField,
448               title='WYSIWYM Field',
449               description=('WYSIWYM Field'),
450               )
451
452class WYSIWYMLinesField(LinesField):
453    """A field that stores a list of strings where every string is handled through WYSIWYM renderer """
454    _properties = LinesField._properties.copy()
455    _properties.update({
456        'widget' : WYSIWYMLinesWidget,
457        'edit_accessor' : "getRaw",
458    })
459
460    security = ClassSecurityInfo()
461
462
463    def getRaw(self, instance, **kwargs):
464        values = LinesField.get(self, instance, **kwargs)
465        return values
466   
467    def get(self, instance, **kwargs):
468        ltool = getToolByName(instance, 'lemill_tool')
469        values = LinesField.get(self, instance, **kwargs)
470        data=[ltool.listrender(value) for value in values]
471        if config.ZOPE_LINES_IS_TUPLE_TYPE:
472            return tuple(data)
473        else:
474            return data
475
476registerField(WYSIWYMLinesField,
477               title='WYSIWYM Lines Field',
478               description=('WYSIWYM Lines Field'),
479               )
480
481class WYSIWYMMultiPartField(StringField):
482    """ handles sections of text """
483    _properties = StringField._properties.copy()
484    _properties.update({
485        'widget' : WYSIWYMWidget,
486    })
487
488    security = ClassSecurityInfo()
489
490    def add_new_field(self, instance):
491        """ adds new field/chapter """
492        i = aq_base(instance)
493        if not shasattr(i, '_chapters'):
494            i._chapters = PersistentMapping()
495        ind = 1000
496        try:
497            ind = max(i._chapters.keys())
498        except ValueError:
499            pass
500        try:
501            ind += 100
502        except TypeError:
503            i._chapters = PersistentMapping()
504            ind = 1001
505        i._chapters[ind] = {'data':'', 'pieces':[]}
506        i._p_changed = 1
507        i._chapters._p_changed = 1
508        return ind
509
510    def getChapters(self, instance):
511        """ get a list of chapters. returning id's of chapters... eg. [1001, 1101, 1201] """
512        i = aq_base(instance)
513        if shasattr(i, '_chapters') and len(i._chapters.keys())>0:
514            val = i._chapters.keys()
515            val.sort()
516            return val
517        else:
518            i._chapters = PersistentMapping()
519            i._chapters[1001] = {'data':'', 'pieces':[]}
520            i._chapters._p_changed = 1
521            return i._chapters.keys()
522
523    def getChapter(self, instance, chap_nr):
524        """ get a content of one chapter """
525        i = aq_base(instance)
526        return i._chapters[chap_nr]['data']
527
528    def getPieces(self, instance, chap_nr, as_objects=False):
529        """ return a list of pieces; uids or object list """
530        i = aq_base(instance)
531        uid_list = i._chapters[chap_nr]['pieces']
532        if not as_objects:
533            return uid_list
534        res = []
535        to_remove = []
536        tool = getToolByName(instance, 'reference_catalog')
537        for uid in uid_list:
538            v = tool.lookupObject(uid)
539            if not v:
540                to_remove.append(v)
541                continue
542            res.append(v)
543        for x in to_remove:
544            try:
545                i._chapters[chap_nr]['pieces'].remove(x)
546            except ValueError:
547                pass
548        i._chapters._p_changed = 1
549        return res
550
551    def getFirstChapter(self, instance):
552        """ return first chapter """
553        i = aq_base(instance)
554        return min(i._chapters.keys())
555
556    def setChapter(self, instance, value, chap_nr):
557        """ set a chapter """
558        i = aq_base(instance)
559        i._chapters[chap_nr]['data'] = value
560        i._chapters._p_changed = 1
561
562    def delChapter(self, instance, chap_nr):
563        """ delete chapter """
564        i = aq_base(instance)
565        del i._chapters[chap_nr]
566
567    def setPieces(self, instance, value, chap_nr):
568        """ set uids """
569        i = aq_base(instance)
570        if not value:
571            return
572        res = i._chapters[chap_nr]['pieces']
573        for v in value.split(';'):
574            if v: res.append(v)
575        i._chapters[chap_nr]['pieces'] = res
576        i._p_changed = 1
577        i._chapters._p_changed = 1
578
579    def delPieces(self, instance, value, chap_nr):
580        """ delete reference to piece """
581        i = aq_base(instance)
582        try:
583            print i._chapters
584            print chap_nr
585            print value
586            i._chapters[int(chap_nr)]['pieces'].remove(value)
587        except ValueError:
588            pass
589        i._chapters._p_changed = 1
590
591registerField(WYSIWYMMultiPartField,
592    title='WYSIWYM multipart Field',
593    description=('WYSIWYM multipart Field'),
594)
Note: See TracBrowser for help on using the repository browser.