source: trunk/FieldsWidgets.py @ 1242

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

Fixed #1045, spent 2h.

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