source: trunk/FieldsWidgets.py @ 1252

Revision 1252, 39.0 KB checked in by jukka, 13 years ago (diff)

Fixed #1055, spent 1.5h.

  • 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.config import REFERENCE_CATALOG
21from Products.Archetypes.Registry import registerField, registerWidget
22from Products.Archetypes.Field import StringField, LinesField, ReferenceField, ObjectField, FileField
23from Products.Archetypes.Widget import TextAreaWidget, StringWidget, SelectionWidget, TypesWidget, MultiSelectionWidget, VisualWidget, FileWidget
24from Products.Archetypes.ReferenceEngine import Reference
25from config import to_unicode
26from Products.Archetypes import config
27from string import letters, punctuation
28from Products.CMFCore.utils import getToolByName
29from ZODB.PersistentMapping import PersistentMapping
30from Products.Archetypes.utils import shasattr
31from Acquisition import aq_base
32from mp3tool import get_length
33
34from types import ListType, TupleType, StringType, UnicodeType, InstanceType
35
36import re
37
38STRING_TYPES = [StringType, UnicodeType]
39
40
41#StringWidget and TextAreaWidget need to update their macro, otherwise they try to use archetypes/skins/widgets -path only
42#Unfortunately this can't be done but by making new Widgets: LeStringWidget and LeTextAreaWidget
43
44class LeVisualWidget(VisualWidget):
45    _properties = VisualWidget._properties.copy()
46    _properties.update({
47        'macro' : 'widget_visual',
48        'rows'  : 18,      #rows of TextArea if VE is not available
49        'cols'  : 80,      #same for cols
50        'width' : '507px', #width of VE frame (if VE is avalilable)
51        'height': '260px', #same for height
52    })
53    # material types use versions of chapter_widgets and are not affected by this
54
55registerWidget(LeVisualWidget,
56    title='Visual Widget',
57    description='Visual Widget'
58)
59   
60
61class LeStringWidget(StringWidget):
62    _properties = StringWidget._properties.copy()
63    _properties.update({
64        'macro' : 'widget_string'
65    })
66
67registerWidget(LeStringWidget,
68    title='String Widget',
69    description='String Widget'
70)
71
72
73class LeTextAreaWidget(TextAreaWidget):
74    _properties = TextAreaWidget._properties.copy()
75    _properties.update({
76        'macro' : 'widget_textarea'
77    })
78
79#    security = ClassSecurityInfo()
80#
81#    # Copy-pasted from Archetypes/Widget.py/TextAreaWidget
82#    # removed a big chunks for supporting append_only and different text formats. We can take them back from original if needed.
83#    security.declarePublic('process_form')
84#    def process_form(self, instance, field, form, empty_marker=None,
85#                     emptyReturnsMarker=False):
86#        value = None
87#        # text field
88#        value = form.get(field.getName(), empty_marker)
89#
90#        if value is empty_marker:
91#            return empty_marker
92#
93#        if emptyReturnsMarker and value == '':
94#            return empty_marker
95#
96#        kwargs = {} # there be formats
97#
98#        #convert line breaks to <br />:s
99#        value= value.replace('\n','<br />')
100#
101#        return value, kwargs
102
103
104
105registerWidget(LeTextAreaWidget,
106    title='Textarea Widget',
107    description='Textarea Widget'
108)
109
110
111
112class WYSIWYMWidget(TextAreaWidget):
113    _properties = TextAreaWidget._properties.copy()
114    _properties.update({
115        'macro' : 'widget_wysiwym',
116        'helper_js' : ('js_helpers.js',),
117        'helper_css' : (),
118        'cols' : 40,
119        'rows' : 10,
120        'no_label' : True,
121    })
122
123registerWidget(WYSIWYMWidget,
124    title='WYSIWYM Widget',
125    description='WYSIWYM Widget',
126    used_for=('Products.LeMill.FieldsWidgets.WYSIWYMField',)
127)
128
129
130class TagsWidget(StringWidget):
131    _properties = StringWidget._properties.copy()
132    _properties.update({
133        'macro' : 'widget_tags',
134    })
135   
136registerWidget(TagsWidget,
137    title='Tags Widget',
138    description='Tags Widget',
139    used_for=('Products.LeMill.TagsField.TagsField',)
140)
141
142class HTMLLinkWidget(StringWidget):
143    _properties = StringWidget._properties.copy()
144    _properties.update({
145        'macro' : 'widget_htmllink',
146    })
147   
148registerWidget(HTMLLinkWidget,
149    title='HTML Link Widget',
150    description='Generated <a> tag Widget',
151    used_for=('Products.LeMill.FieldsWidgets.HTMLLinkWidget',)
152)
153
154class MessengerWidget(TypesWidget):
155    _properties = TypesWidget._properties.copy()
156    _properties.update({
157        'macro' : 'widget_messenger',
158    },)
159
160    security = ClassSecurityInfo()
161
162    security.declarePublic('process_form')
163    def process_form(self, instance, field, form, empty_marker=None,
164                     emptyReturnsMarker=False):
165        """Concatenates address + messenger type, but if address already contains something like prefix (word:) then removes it.
166        """
167        name = field.getName()
168        otherName = "%s_other" % name
169        value = form.get(name, empty_marker)
170        othervalue = form.get(otherName, empty_marker)
171        if (not value or value == empty_marker):
172            return empty_marker
173        if not othervalue == empty_marker:
174            value = "%s:%s" % (othervalue,value)
175        return value, {}
176
177
178registerWidget(MessengerWidget,
179    title='Messenger Widget',
180    description='Messenger Widget',
181    used_for=('Products.Archetypes.Field.StringField',)
182)
183
184
185class MobileWidget(StringWidget):
186    _properties = StringWidget._properties.copy()
187    _properties.update({
188        'macro' : 'widget_mobile',
189    })
190
191    security = ClassSecurityInfo()
192
193    security.declarePublic('process_form')
194    def process_form(self, instance, field, form, empty_marker=None,
195                     emptyReturnsMarker=False):
196        name = field.getName()
197        otherName = "%s_checkbox" % name
198        value = form.get(name, empty_marker)
199        othervalue = form.get(otherName, empty_marker)
200        if value == empty_marker and emptyReturnsMarker:
201            return empty_marker
202        if othervalue=="1":
203            value = "*SMS*%s" % value
204        return value, {}
205
206   
207registerWidget(MobileWidget,
208    title='Mobile Widget',
209    description='Mobile Widget',
210    used_for=('Products.Archetypes.Field.StringField',)
211)
212
213
214class CopyrightWidget(SelectionWidget):
215    _properties = SelectionWidget._properties.copy()
216    _properties.update({
217        'macro' : 'widget_copyright',
218    })
219   
220registerWidget(CopyrightWidget,
221    title='Copyright Widget',
222    description='Copyright selection Widget',
223    used_for=('Products.LeMill.StringField.StringField',)
224)
225
226class TwoColumnMultiSelectionWidget(MultiSelectionWidget):
227    _properties = MultiSelectionWidget._properties.copy()
228    _properties.update({
229        'macro' : 'widget_twocolumn',
230    })
231   
232registerWidget(TwoColumnMultiSelectionWidget,
233    title='Two column widget',
234    description='Two column multiselection widget',
235    used_for=('Products.Archetypes.Field.MultiSelectionField',)
236)
237
238
239class LinkLinesWidget(StringWidget):
240    _properties = StringWidget._properties.copy()
241    _properties.update({
242        'macro' : 'widget_linklines',
243        'empty_links' : 5
244    })
245   
246    security = ClassSecurityInfo()
247
248    security.declarePublic('process_form')
249    def process_form(self, instance, field, form, empty_marker=None,
250                     emptyReturnsMarker=False):
251        namebase = field.getName()
252        length = form.get("%s_last" % namebase, empty_marker)
253        if length==None or length==[]: length=4
254        result=[]
255        for n in range(0,int(length)):
256            url=form.get("%s%s_url" % (namebase, n))
257            text=form.get("%s%s" % (namebase, n))
258            if (url!='' and url!='http://'):
259                result.append((url,text))
260        return result, {}     
261
262class PieceWidget(SelectionWidget):
263    _properties = SelectionWidget._properties.copy()
264    _properties.update({
265        'macro' : 'widget_pieceselector',
266        'collections_only' : False,
267        'helper_js' : ('js_helpers.js',),
268        'fieldsToQuery' : ['material-piece-', 'material-file-'],
269        'show_added' : True,
270    })
271 
272    security = ClassSecurityInfo()
273
274    security.declarePublic('process_form')
275    def process_form(self, instance, field, form, empty_marker=None,
276            emptyReturnsMarker=False):
277        """ goes through the form and tries to find all references to pieces. """
278        value = []
279        pieces = []
280        for i in range(0,99):
281            tmp = []
282            for f in self._properties['fieldsToQuery']:
283                one = form.get(f+str(i), empty_marker)
284                if one and one is not empty_marker:
285                    if str(one) != '0':
286                        tmp.append(one)
287            if len(tmp)>0:
288                pieces.append(tmp)
289            tmp = []
290        for p in pieces:
291            uid = p[0]
292            if len(p)>1: # we have both uid and FileUpload instance. we're taking FilUpload
293                # upload piece, get uid
294                pass
295            value.append(uid)
296        if value == []:
297            return empty_marker
298        return value, {}
299
300registerWidget(PieceWidget,
301    title='Piece Widget',
302    description='Piece selection Widget',
303    used_for=('Products.LeMill.PieceField',)
304)
305
306class GroupWidget(SelectionWidget):
307    _properties = SelectionWidget._properties.copy()
308    _properties.update({
309        'macro' : 'widget_group',
310        'format' : 'radio',
311    })
312
313
314class refImagesWidget(SelectionWidget):
315    _properties = SelectionWidget._properties.copy()
316    _properties.update({
317        'macro' : 'widget_imageselection',
318        'helper_js' : ('js_helpers.js',),
319    })     
320
321class ImageSelectorWidget(SelectionWidget):
322    _properties = SelectionWidget._properties.copy()
323    _properties.update({
324        'macro' : 'widget_imageselector',
325        'helper_js' : ('js_helpers.js',),
326    })
327
328
329class ChapterWidget(SelectionWidget):
330    _properties = SelectionWidget._properties.copy()
331    _properties.update({
332        'macro' : 'widget_chapter',
333        'collections_only' : False,
334        'helper_js' : ('js_helpers.js',),
335        'show_added' : True,
336        'chapter_count' : 1,
337    })
338 
339    security = ClassSecurityInfo()
340
341    security.declarePublic('process_form')
342    def process_form(self, instance, field, form, empty_marker=None,
343            emptyReturnsMarker=False):
344        """ goes through the form and tries to find all texts and references to pieces. """
345        value = []
346        fieldname=field.getName()
347        file=form.get('%s_file' % fieldname)       
348        upload_pos=int(form.get('%s_uploaded' % fieldname))
349        for i in range(0,int(form.get('%s_count' % fieldname, 0))):
350            text= form.get('%s_%s' % (fieldname, i))
351            value.append(text)
352
353        if file and hasattr(file, 'filename') and upload_pos>-1:
354            value[upload_pos]=file
355        if value == []:
356            return empty_marker
357        return value, {}
358
359registerWidget(ChapterWidget,
360    title='Chapter Widget',
361    description='Chapter editor Widget',
362    used_for=('Products.LeMill.ChapterField',)
363)
364
365class SlideWidget(ChapterWidget):
366    _properties = ChapterWidget._properties.copy()
367    _properties.update({
368        'macro' : 'widget_slides',
369        'collections_only' : False,
370        'helper_js' : ('js_helpers.js',),
371        'show_added' : True,
372        'chapter_count' : 5,
373    })
374
375
376    security = ClassSecurityInfo()
377
378    security.declarePublic('process_form')
379    def process_form(self, instance, field, form, empty_marker=None,
380            emptyReturnsMarker=False):
381        """ goes through the form and tries to find all texts and references to pieces. """
382        fieldname=field.getName()
383        value = form.get(fieldname, empty_marker) # allow tests to create presentations from strings
384        if type(value)==str:
385            return['thisisamediapiecethisisamediapie', value], {} # simplest possible presentation
386        value = []
387        for i in range(0,int(form.get('%s_count' % fieldname, 1))):
388            text= form.get('%s_%s' % (fieldname, i))
389            file=form.get('%s_file_%s' % (fieldname, i))       
390            value.append(text)
391            if file and hasattr(file, 'filename'):
392                value[i]=file
393
394        if value == []:
395            return empty_marker
396        return value, {'allowed_types_sequence':['image','string']}
397
398
399registerWidget(SlideWidget,
400    title='Slide Widget',
401    description='Slide and caption editor Widget',
402    used_for=('Products.LeMill.ChapterField',)
403)
404
405class PilotWidget(ChapterWidget):
406    _properties = ChapterWidget._properties.copy()
407    _properties.update({
408        'macro' : 'widget_pilot',
409        'collections_only' : False,
410        'helper_js' : ('js_helpers.js',),
411        'show_added' : True,
412        'chapter_count' : 3,
413    })
414
415
416    security = ClassSecurityInfo()
417
418    security.declarePublic('process_form')
419    def process_form(self, instance, field, form, empty_marker=None,
420            emptyReturnsMarker=False):
421        """ goes through the form and tries to merge all text inputs to lists, find references to pieces and if file objects are uploaded, just send them forward to field. """
422        fieldname=field.getName()
423        value = []
424        edited = int(form.get('%s_last_edited' % fieldname, 1))
425        count= int(form.get('%s_count' % fieldname, 1))
426        y=3
427        for i in range(0,count):
428            text= form.get('%s_%s' % (fieldname, i))
429            if text!="" and text!=None:
430                if text.startswith('#keywords#!#'):
431                    text=text.split('#!#')[1:]
432                value.append(text)
433
434            if i >= edited and i<edited+3: 
435                # files
436                if i%3 != 2:
437                    file=form.get('%s_file_%s' % (fieldname, i))   
438                    if file and hasattr(file, 'filename'):
439                        value[i]=file
440                        continue
441                # keywords and questions
442                if i%3==2:               
443                    kw_counter=0
444                    kw=''
445                    target_len=3
446                    keywordlist=[]
447                    while(kw!=None and kw_counter<8):
448                        kw=form.get('%s_keyword_%s_%s' % (fieldname,i,kw_counter), None)
449                        if kw!=None:                     
450                            keywordlist.append(kw)
451                        kw_counter=kw_counter+1
452                    if i==count-1:
453                        target_len=7
454                    while len(keywordlist)<target_len:
455                        keywordlist.append('')
456                    value[i]=keywordlist
457        if value == []:
458            return empty_marker
459        return value, {'allowed_types_sequence':['image','audio','list']}
460
461    def merge_keywords(self, value):
462        """ allow lists to be stored in forms as hidden fields """
463        if type(value)==list or type(value)==tuple:
464            value='#!#'.join(value)
465            return ''.join(('#keywords#!#',value))
466         
467
468
469registerWidget(PilotWidget,
470    title='Pilot Widget',
471    description='Scene editor Widget',
472    used_for=('Products.LeMill.ChapterField',)
473)
474
475
476class AudioWidget(FileWidget):
477    _properties = FileWidget._properties.copy()
478    _properties.update({
479    'macro' : 'widget_audio',
480    })
481
482registerWidget(AudioWidget,
483    title='Audio Widget',
484    description='Widget that uses flashplayer for mp3:s',
485    used_for=('Products.LeMill.ChapterField',)
486)
487
488
489
490
491####                            ####
492####            FIELDS          ####
493####                            ####
494
495
496class PieceField(ReferenceField):
497    """ A field that stores references to pieces. Can be limited to images only etc. """
498    __implements__ = ReferenceField.__implements__
499    _properties = ReferenceField._properties.copy()
500    _properties.update({
501        'widget' : PieceWidget,
502        'multiValued' : True,
503        'piece_types' : 'all',
504        'allowed_types':('Piece',),
505       
506    })
507
508    security = ClassSecurityInfo()
509
510    security.declarePrivate('set')
511    def set(self, instance, value, **kwargs):
512        allowed = None
513        try:
514            allowed=kwargs[piece_types]
515        except NameError:
516            allowed = self._properties['piece_types']
517        # this is mostly copied from Archetypes.Field.ReferenceField.set with some additional checks and some shortcuts
518
519        # locating instances and creating pieces
520        new_values = []
521        for v in value:
522            if type(v) != InstanceType:
523                new_values.append(v)
524                continue
525            if v and v.filename:
526                id = instance.generateUniqueId('Piece')
527                portal_url = getToolByName(instance, 'portal_url')
528                new_id = portal_url.getPortalObject().content.invokeFactory('Piece', id, title=instance.Title()+'-image')
529                new_piece = getattr(instance.content, new_id)
530                uid = new_piece.UID()
531                new_piece.edit(image=v, file=v, language='')
532                new_values.append(uid)
533               
534                req = instance.REQUEST
535                ff = instance.getField('bodyText')
536                edit_chapter = req.get('edit_chapter', 1001)
537                ff.setPieces(instance, uid, int(edit_chapter))
538        old_values = value
539        value = new_values
540
541        tool = getToolByName(instance, 'reference_catalog')
542        targetUIDs = [ref.targetUID for ref in
543                      tool.getReferences(instance, self.relationship)]
544
545        if value is None:
546            value = ()
547
548        if not isinstance(value, (ListType, TupleType)):
549            value = value,
550        elif not self.multiValued and len(value) > 1:
551            value = value[0],
552
553        #convert objects to uids if necessary and check if they are correct subtype
554        uids = []
555        for v in value:
556            if type(v) in [StringType, UnicodeType]:
557                vobj=tool(UID=v)
558                # AttributeError: LazyCat instance has no attribute 'getObject'
559                #vobj=vobj.getObject()
560                if allowed=='all' or (allowed=='images' and vobj.isImage()):                 
561                    uids.append(v)
562            else:
563                if allowed=='all' or (allowed=='images' and v.isImage()): 
564                    uids.append(v.UID())
565
566        add = [v for v in uids if v and v not in targetUIDs]
567        sub = [t for t in targetUIDs if t not in uids]
568
569        # tweak keyword arguments for addReference
570        addRef_kw = kwargs.copy()
571        addRef_kw.setdefault('referenceClass', self.referenceClass)
572        if addRef_kw.has_key('schema'): del addRef_kw['schema']
573
574        for uid in add:
575            __traceback_info__ = (instance, uid, value, targetUIDs)
576            # throws IndexError if uid is invalid
577            tool.addReference(instance, uid, self.relationship, **addRef_kw)
578
579        for uid in sub:
580            tool.deleteReference(instance, uid, self.relationship)
581
582        if self.callStorageOnSet:
583            #if this option is set the reference fields's values get written
584            #to the storage even if the reference field never use the storage
585            #e.g. if i want to store the reference UIDs into an SQL field
586            ObjectField.set(self, instance, self.getRaw(instance), **kwargs)
587
588registerField(PieceField,
589               title='Piece Field',
590               description=('References to Pieces'),
591               )
592
593
594
595class TagsField(LinesField):
596    """ A field that stores tags """
597    __implements__ = LinesField.__implements__
598    _properties = LinesField._properties.copy()
599    _properties.update({
600        'widget' : TagsWidget,
601    })
602
603    security = ClassSecurityInfo()
604
605    def set(self, instance, value, **kwargs):
606        tags = []
607        tags_dirty = []
608    if isinstance(value,str):
609            value = value.lower()
610            tags_dirty = value.split(',')
611            value = tags_dirty
612            tags = []
613        if isinstance(value, tuple) or isinstance(value, list):
614            tags_dirty = value
615            [ tags.append(t.strip().lower()) for t in tags_dirty if t.strip().lower() not in tags ]
616            value = ','.join(tags)
617    if isinstance(value,str):
618        value = value.replace(',','\n')
619        LinesField.set(self, instance, value, **kwargs)
620                       
621    def getRaw(self, instance, **kwargs):
622        value = self.get(instance, **kwargs)
623        return ', '.join(value)
624
625    def get(self, instance, **kwargs):
626        return LinesField.get(self, instance, **kwargs)
627
628registerField(TagsField,
629               title='Tags Field',
630               description=('Tags Field'),
631               )
632
633class WYSIWYMField(StringField):
634    """A field that stores WYSIWYM strings"""
635    _properties = StringField._properties.copy()
636    _properties.update({
637        'widget' : WYSIWYMWidget,
638        'edit_accessor' :  "getRaw",
639        'default_output_type' : 'text/x-html-captioned',
640        'default_content_type' : 'text/html',
641    })   
642   
643    security = ClassSecurityInfo()
644   
645   
646    def getRaw(self, instance, **kwargs):
647        value = self.get_historical(instance,**kwargs)
648        return value
649   
650    def get_historical(self, instance, **kwargs):
651        """Special get method to handle history views as well."""
652        version = instance.REQUEST.get('version',None)
653        if not version:
654            return StringField.get(self,instance,**kwargs)
655        # Get old data from history
656        #fields = instance.getHistoricalFields(version)
657        value = instance.getFieldHistory(self.getName(), version)
658        #try:
659        #    value = fields[self.getName()]
660        #except KeyError:
661        #    value = ''
662        return value
663
664    def get(self, instance, **kwargs):
665        ltool = getToolByName(instance, 'lemill_tool')
666        value = self.get_historical(instance,**kwargs)
667        return ltool.render(value)
668
669    # render -method has moved to LeMillTools, because it is needed also outside the wysiwym field.
670
671registerField(WYSIWYMField,
672               title='WYSIWYM Field',
673               description=('WYSIWYM Field'),
674               )
675
676class LinkLinesField(LinesField):
677    """ handles lists of links and their texts """
678    _properties = LinesField._properties.copy()
679    _properties.update({
680        'widget' : LinkLinesWidget,
681    })
682
683    security = ClassSecurityInfo()
684   
685    def get(self, instance, **kwargs):
686        """ Get a list of tuples, where [0] is url and [1] text """
687        values = LinesField.get(self, instance, **kwargs)
688        values = [tuple(x.split('###',1)) for x in values]
689        return values
690
691
692    security.declarePrivate('set')
693    def set(self, instance, value, **kwargs):
694        value= ['###'.join(x) for x in value]
695        LinesField.set(self, instance, value, **kwargs)
696
697registerField(LinkLinesField,
698               title='Link Lines Field',
699               description=('List of links'),
700               )
701
702
703
704class ChapterField(ObjectField):
705    """ chapters are stored as lists, where chapter can either be a string or UID. If UID, view widget should fetch the object. """
706    _properties = ObjectField._properties.copy()
707    _properties.update({
708        'widget' : ChapterWidget,
709        'relationship' : 'uses', # required
710        'referenceClass' : Reference,
711        'referenceReferences' : False,
712        'deleteEmptyChapters' : True
713    })
714   
715    security = ClassSecurityInfo()
716
717    def get(self, instance, **kwargs):
718        """Special get method to handle history views as well."""
719        version = instance.REQUEST.get('version',None)
720        if not version:
721            return ObjectField.get(self,instance,**kwargs)
722        # Get old data from history
723        #fields = instance.getHistoricalFields(version)
724        value = instance.getFieldHistory(self.getName(), version)
725        #value = fields[self.getName()]
726        if type(value) != type([]):
727            return [value,]
728        else:
729            return value
730
731    def set(self, instance, value, **kwargs):
732        tool = getToolByName(instance, REFERENCE_CATALOG)
733        targetUIDs = tool.getReferences(instance, self.relationship)
734        finalvalues=[]
735        allowed_types_sequence=['']
736        sequence_index=0
737        # widget cannot test file types with ease,
738        # so it sends requirement sequence as list f.ex ['image','string'] for Presentation 
739        if kwargs.has_key('allowed_types_sequence'):
740            allowed_types_sequence=kwargs['allowed_types_sequence']           
741        sequence_length=len(allowed_types_sequence)
742        ignore_type= allowed_types_sequence==['']
743        uids=[]
744        if value==None: value=['']
745        if type(value)==str: value=[value]       
746       
747        # tweak keyword arguments for addReference
748        addRef_kw = kwargs.copy()
749        addRef_kw.setdefault('referenceClass', self.referenceClass)
750        if addRef_kw.has_key('schema'): del addRef_kw['schema']
751
752        #assume that values are given in list   
753        for chapter in value:
754            if chapter and hasattr(chapter, 'filename'):
755                # This is a file, so create a piece from it, get its uid and give that to be the content of this chapter
756                id = instance.generateUniqueId('Piece')
757                portal_url = getToolByName(instance, 'portal_url')
758                new_id = portal_url.getPortalObject().content.invokeFactory('Piece', id, title='-'.join((instance.Title(),chapter.filename)))
759                new_piece = getattr(instance.content, new_id)
760                new_piece.edit(file=chapter)
761               
762                # file is uploaded and piece is made, but is it a piece of correct type?               
763                if new_piece.isImage() and (allowed_types_sequence[sequence_index]=='image' or ignore_type):
764                    new_piece.edit(image=chapter)
765                    chapter = new_piece.UID()
766                    fine_piece = True             
767                elif new_piece.isAudio() and (allowed_types_sequence[sequence_index]=='audio' or ignore_type):
768                    chapter = new_piece.UID()
769                    fine_piece = True               
770                else:
771                    fine_piece = False
772                    if allowed_types_sequence[sequence_index]=='image':
773                        chapter = 'thisisamediapiecethisisamediapie'
774                    elif allowed_types_sequence[sequence_index]=='audio':
775                        chapter = 'thisisaudiopiecethisisaudiopiece'
776                    else:
777                        chapter = None
778
779                # store UID in session so that we can edit these new pieces
780                if fine_piece:
781                    session=instance.REQUEST.SESSION
782                    if session.has_key('new_pieces'):
783                        session['new_pieces'].append(new_piece.UID())
784                    else:
785                        session['new_pieces']=[new_piece.UID()]
786
787            #try to set references
788            if type(chapter)==str:
789                if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's a UID!
790                    uids.append(chapter) # collect list of good uids that should be there                 
791                    if chapter not in targetUIDs and chapter not in ['thisisamediapiecethisisamediapie', 'thisisaudiopiecethisisaudiopiece']: # if not there, add it
792                        __traceback_info__ = (instance, chapter, targetUIDs)
793                        tool.addReference(instance, chapter, self.relationship, **addRef_kw)
794
795            if chapter!=None or self.deleteEmptyChapters==False:
796                finalvalues.append(chapter)
797            sequence_index=sequence_index+1
798            if sequence_index==sequence_length:
799                sequence_index=0
800
801
802        for uid in targetUIDs: # delete bad references
803            if uid not in uids:
804                tool.deleteReference(instance, uid, self.relationship)           
805        # finally get it done
806        ObjectField.set(self, instance, finalvalues, **kwargs)
807
808
809    def add_new_chapter(self,instance, insert_point=-1):
810        """ adds new field/chapter """
811        value=ChapterField.get(self,instance)
812        value_end=[]
813        if insert_point!=-1:
814            value_end=value[insert_point:]
815            value=value[:insert_point]       
816        value.append((''))
817        value=value+value_end
818        ObjectField.set(self, instance, value)
819        if insert_point==-1:       
820            return len(value)-1
821        else:
822            return insert_point
823
824    def add_new_mediapiece(self,instance, insert_point=-1):
825        """ adds new field/mediapiece """
826        value=ChapterField.get(self,instance)
827        value_end=[]
828        if insert_point!=-1:
829            value_end=value[insert_point:]
830            value=value[:insert_point]           
831        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
832        value=value+value_end
833        ObjectField.set(self, instance, value)
834        if insert_point==-1:       
835            return len(value)-1
836        else:
837            return insert_point
838
839    def add_new_slide(self, instance, insert_point=-1):
840        """ adds slide and caption (2 objects) to list """
841        value=ChapterField.get(self,instance)
842        value_end=[]
843        if insert_point!=-1:
844            value_end=value[insert_point:]
845            value=value[:insert_point]           
846        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
847        value.append((''))
848        value=value+value_end
849        ObjectField.set(self, instance, value)
850        if insert_point==-1:       
851            return len(value)-2
852        else:
853            return insert_point
854
855    def add_new_scene(self, instance, insert_point=-1):
856        """ adds slide, voiceover and keywords (PILOT)"""
857        value=ChapterField.get(self,instance)
858        if insert_point==-1:
859            insert_point=len(value)-3
860        value_end=[]       
861        value_end=value[insert_point:]
862        value=value[:insert_point]           
863        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
864        value.append(('thisisaudiopiecethisisaudiopiece'))
865        value.append(['','',''])
866        value=value+value_end
867        ObjectField.set(self, instance, value)
868        return insert_point
869       
870    def isUid(self, chapter):
871        """ Checks if chapter seems, smells and feels like UID """
872        if type(chapter) in STRING_TYPES:
873            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
874                return chapter
875        elif type(chapter) in (list, tuple, int) or hasattr(chapter, 'filename'):
876            return False
877        else:
878#            try:
879                chapter=chapter.UID()
880                return chapter
881#            except:
882                return False
883               
884    def isAudioUID(self, chapter):
885        """ True if UID is audio placeholder """
886        if chapter=='thisisaudiopiecethisisaudiopiece':
887            return True
888
889    def isImageUID(self, chapter):
890        """ True if UID is audio placeholder """
891        if chapter=='thisisamediapiecethisisamediapie':
892            return True
893
894    def isKeywords(self, chapter):
895        """Keywords are lists of three strings, used in PILOT """
896        if type(chapter)==list and len(chapter)==3:
897            return True
898
899    def isQuestions(self, chapter):
900        """Questions are lists of seven strings, used in PILOT """
901        if type(chapter)==list and len(chapter)==7:
902            return True           
903               
904    def getObjectByUID(self, instance, uid):
905        """ Helper method for fetching mediapieces from references """
906        uid_catalog = getToolByName(instance, 'uid_catalog')
907        if uid=='thisisamediapiecethisisamediapie' or uid=='thisisaudiopiecethisisaudiopiece': return None # this is just a newly created placeholder
908        results=uid_catalog(UID=uid)
909        return results[0].getObject()
910
911    def getChapters(self, instance):
912        """ get a list of chapters with text. returning id's of chapters... eg. [1001, 1101, 1201] """
913        value=ChapterField.get(self,instance)
914        result=[]
915        for n in range(len(value)):
916            chapter=value[n]
917            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
918                pass
919            else:
920                result.append(n)
921        return result
922
923    def getChapter(self, instance, chap_nr):
924        """ get a content of one chapter """
925        value=ChapterField.get(self, instance)
926        if len(value)<chap_nr:
927            return ''
928        else:
929            return value[chap_nr]
930
931    def getPieces(self, instance, chap_nr, as_objects=False):
932        """ return a list of pieces; uids or object list """
933        value=ChapterField.get(self,instance)
934        result=[]
935        for chapter in value:
936            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
937                result.append(chapter)
938        if not as_objects:
939            return result
940        res = []
941        new_value = value
942        removed=[]
943        tool = getToolByName(instance, 'reference_catalog')
944        for uid in result:
945                v = tool.lookupObject(uid)
946                if not v:
947                    removed.append(uid)
948                else:
949                    res.append(v)
950        for chapclip in removed:
951            new_value.remove(chapclip)
952        if new_value!=value:
953            ObjectField.set(self, instance, new_value)
954        return res
955
956    def getFirstChapter(self, instance):
957        """ return first chapter """
958        return 0
959
960    def setChapter(self, instance, value, chap_nr):
961        """ set a chapter """
962        values=ChapterField.get(self,instance)
963        values[chap_nr]=value
964        ObjectField.set(self, instance, values)
965
966    def delChapter(self, instance, chap_nr):
967        """ delete chapter """
968        value=ChapterField.get(self,instance)
969        del value[chap_nr]
970        ObjectField.set(self, instance, value)       
971
972    def setPieces(self, instance, value, chap_nr):
973        """ set uids """
974        values=ChapterField.get(self,instance)
975        if type(value) not in STRING_TYPES:
976            value=value.UID()                   
977        values[chap_nr]=value
978        ObjectField.set(self, instance, values)
979
980    def delPiece(self, instance, chap_nr):
981        """ delete reference to piece """
982        values=ChapterField.get(self,instance)
983        tool = getToolByName(instance, REFERENCE_CATALOG)
984        tool.deleteReference(instance, values[chap_nr], self.relationship)
985        del values[chap_nr]
986        ObjectField.set(self, instance, values)       
987       
988    def delSlide(self, instance, chap_nr):
989        """ delete both chapter and caption """
990        values=ChapterField.get(self,instance)
991        tool = getToolByName(instance, REFERENCE_CATALOG)
992        tool.deleteReference(instance, values[chap_nr], self.relationship)
993        del values[chap_nr]
994        if len(values)>chap_nr:
995            del values[chap_nr]
996        ObjectField.set(self, instance, values)       
997
998    def delScene(self, instance, chap_nr):
999        """ delete image, audio and keywords/questions (PILOT)"""
1000        values=ChapterField.get(self,instance)
1001        tool = getToolByName(instance, REFERENCE_CATALOG)
1002        tool.deleteReference(instance, values[chap_nr], self.relationship)
1003        tool.deleteReference(instance, values[chap_nr+1], self.relationship)
1004        for a in [1,2,3]:
1005            if len(values)>chap_nr:
1006                del values[chap_nr]
1007        ObjectField.set(self, instance, values)
1008       
1009    def moveChapterUp(self, instance, chap_nr, granularity):
1010        """ Move chapter chap_nr, chunk size is defined by int granularity """
1011        values=ChapterField.get(self,instance)
1012        if chap_nr-granularity<0:
1013            return 0       
1014        begin = values[:chap_nr-granularity] # minus the chunk before current chapter
1015        moving_part = values[chap_nr-granularity:chap_nr] # the chunk before current chapter
1016        end = values[chap_nr+granularity:]
1017        chapter = values[chap_nr:chap_nr+granularity]
1018        ObjectField.set(self, instance, begin+chapter+moving_part+end)
1019       
1020    def moveChapterDown(self, instance, chap_nr, granularity):
1021        """ Move chapter chap_nr, chunk size is defined by int granularity """
1022        values=ChapterField.get(self,instance)
1023        if chap_nr+2*granularity>len(values):
1024            return 0     
1025        begin = values[:chap_nr]
1026        chapter = values[chap_nr:chap_nr+granularity]
1027        moving_part = values[chap_nr+granularity:chap_nr+granularity*2] # the chunk after current chapter
1028        end = values[chap_nr+granularity*2:] # the rest after current chapter and chunk after it
1029        ObjectField.set(self, instance, begin+moving_part+chapter+end)
1030
1031
1032    def getLength(self, piece):
1033        as_time=piece.getLength()
1034        return {'hour':as_time/3600,'minute':(as_time % 3600)/60,'second':as_time % 60}           
1035
1036
1037registerField(ChapterField,
1038    title='Chapter field',
1039    description=('Chapter field'),
1040)
1041
1042
1043class DictField(LinesField):
1044    """ dictionary field """
1045    _properties = LinesField._properties.copy()
1046    _properties.update({
1047        'widget' : StringField,
1048    })
1049
1050    security = ClassSecurityInfo()
1051    def setValue(self, instance, key, value):
1052        i = aq_base(instance)
1053        if not shasattr(i, '_ivalues'):
1054            i._ivalues = PersistentMapping()
1055            i._p_changed = 1
1056        i._ivalues[key] = value
1057        i._ivalues._p_changed = 1
1058        i._p_changed = 1
1059
1060    def getKeys(self, instance):
1061        i = aq_base(instance)
1062        if not shasattr(i, '_ivalues'):
1063            return []
1064        else:
1065            return i._ivalues.keys()
1066
1067    def getValue(self, instance, key):
1068        i = aq_base(instance)
1069        if not shasattr(i, '_ivalues'):
1070            i._ivalues = PersistentMapping()
1071            i._p_changed = 1
1072            return ""
1073        if not i._ivalues.has_key(key):
1074            return ""
1075        return i._ivalues[key]
1076
1077class AudioField(FileField):
1078    """ field for mp3:s """
1079    _properties = FileField._properties.copy()
1080    _properties.update({
1081        'widget' : AudioWidget,
1082    })
1083
1084    def getLength(self, instance):
1085        i = aq_base(instance)
1086        file = AudioField.get(self,instance)
1087        if not shasattr(i, '_mp3length'):
1088                instance._mp3length=get_length(str(file))
1089        print instance._mp3length
1090        if instance._mp3length==0:
1091                instance._mp3length=get_length(str(file))
1092        return {'hour':instance._mp3length/3600,'minute':(instance._mp3length % 3600)/60,'second':instance._mp3length % 60}
1093           
1094    def set(self, instance, value, **kwargs):
1095        """ allow only audio here """
1096        FileField.set(self, instance, value, **kwargs)
1097        file=self.get(instance)
1098        if self.getContentType(instance).startswith('audio/'):
1099            instance._mp3length=get_length(str(file))
1100        elif str(file)!='':
1101            FileField.set(self, instance, "DELETE_FILE", **kwargs)
1102           
1103
1104registerField(AudioField,
1105    title='Audio field',
1106    description=('Field that accepts only mp3:s'),
1107)
Note: See TracBrowser for help on using the repository browser.