source: trunk/FieldsWidgets.py @ 581

Revision 581, 19.8 KB checked in by vahur, 13 years ago (diff)

re #613 spent 1h
tuning

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