source: trunk/FieldsWidgets.py @ 1370

Revision 1370, 40.1 KB checked in by jukka, 13 years ago (diff)

Worked on #1146. Search for suitable existing story is done and stories can be written. Must find out why portal_factory decides to keep stories, if user cancels or wanders off from story creation. Also the search for existing stories is tricky. Current way isn't fast, but I don't know if it can be done any faster -- and it's our front page, will get lots of hits.

Well, one way would be to combine stories' uids to one superuid and try to match that. We'll see..

  • 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, MIMETYPE_WHITELIST
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 types import ListType, TupleType, StringType, UnicodeType, InstanceType
34from Products.LeMill.Validators import remove_invalid_html_tags
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
328class ChapterWidget(SelectionWidget):
329    _properties = SelectionWidget._properties.copy()
330    _properties.update({
331        'macro' : 'widget_chapter',
332        'collections_only' : False,
333        'helper_js' : ('js_helpers.js',),
334        'show_added' : True,
335        'chapter_count' : 1,
336    })
337 
338    security = ClassSecurityInfo()
339
340    security.declarePublic('process_form')
341    def process_form(self, instance, field, form, empty_marker=None,
342            emptyReturnsMarker=False):
343        """ goes through the form and tries to find all texts and references to pieces. """
344        value = []
345        fieldname=field.getName()
346        file=form.get('%s_file' % fieldname)       
347        upload_pos=int(form.get('%s_uploaded' % fieldname))
348        for i in range(0,int(form.get('%s_count' % fieldname, 0))):
349            text= form.get('%s_%s' % (fieldname, i))
350            text = remove_invalid_html_tags(text)
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            text = remove_invalid_html_tags(text)
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                text = remove_invalid_html_tags(text)
434                value.append(text)
435
436            if i >= edited and i<edited+3: 
437                # files
438                if i%3 != 2:
439                    file=form.get('%s_file_%s' % (fieldname, i))   
440                    if file and hasattr(file, 'filename'):
441                        value[i]=file
442                        continue
443                # keywords and questions
444                if i%3==2:               
445                    kw_counter=0
446                    kw=''
447                    target_len=3
448                    keywordlist=[]
449                    while(kw!=None and kw_counter<8):
450                        kw=form.get('%s_keyword_%s_%s' % (fieldname,i,kw_counter), None)
451                        if kw!=None:                     
452                            keywordlist.append(kw)
453                        kw_counter=kw_counter+1
454                    if i==count-1:
455                        target_len=7
456                    while len(keywordlist)<target_len:
457                        keywordlist.append('')
458                    value[i]=keywordlist
459        if value == []:
460            return empty_marker
461        return value, {'allowed_types_sequence':['image','audio','list']}
462
463    def merge_keywords(self, value):
464        """ allow lists to be stored in forms as hidden fields """
465        if type(value)==list or type(value)==tuple:
466            value='#!#'.join(value)
467            return ''.join(('#keywords#!#',value))
468         
469
470
471registerWidget(PilotWidget,
472    title='Pilot Widget',
473    description='Scene editor Widget',
474    used_for=('Products.LeMill.ChapterField',)
475)
476
477
478class AudioWidget(FileWidget):
479    _properties = FileWidget._properties.copy()
480    _properties.update({
481    'macro' : 'widget_audio',
482    })
483
484registerWidget(AudioWidget,
485    title='Audio Widget',
486    description='Widget that uses flashplayer for mp3:s',
487    used_for=('Products.LeMill.ChapterField',)
488)
489
490
491
492
493####                            ####
494####            FIELDS          ####
495####                            ####
496
497
498class PieceField(ReferenceField):
499    """ A field that stores references to pieces. Can be limited to images only etc. """
500    __implements__ = ReferenceField.__implements__
501    _properties = ReferenceField._properties.copy()
502    _properties.update({
503        'widget' : PieceWidget,
504        'multiValued' : True,
505        'piece_types' : 'all',
506        'allowed_types':('Piece',),
507       
508    })
509
510    security = ClassSecurityInfo()
511
512    security.declarePrivate('set')
513    def set(self, instance, value, **kwargs):
514        allowed = None
515        try:
516            allowed=kwargs[piece_types]
517        except NameError:
518            allowed = self._properties['piece_types']
519        # this is mostly copied from Archetypes.Field.ReferenceField.set with some additional checks and some shortcuts
520
521        # locating instances and creating pieces
522        new_values = []
523        for v in value:
524            if type(v) != InstanceType:
525                new_values.append(v)
526                continue
527            if v and v.filename:
528                id = instance.generateUniqueId('Piece')
529                portal_url = getToolByName(instance, 'portal_url')
530                new_id = portal_url.getPortalObject().content.invokeFactory('Piece', id, title=instance.Title()+'-image')
531                new_piece = getattr(instance.content, new_id)
532                uid = new_piece.UID()
533                new_piece.edit(image=v, file=v, language='')
534                new_values.append(uid)
535               
536                req = instance.REQUEST
537                ff = instance.getField('bodyText')
538                edit_chapter = req.get('edit_chapter', 1001)
539                ff.setPieces(instance, uid, int(edit_chapter))
540        old_values = value
541        value = new_values
542
543        tool = getToolByName(instance, 'reference_catalog')
544        targetUIDs = [ref.targetUID for ref in
545                      tool.getReferences(instance, self.relationship)]
546
547        if value is None:
548            value = ()
549
550        if not isinstance(value, (ListType, TupleType)):
551            value = value,
552        elif not self.multiValued and len(value) > 1:
553            value = value[0],
554
555        #convert objects to uids if necessary and check if they are correct subtype
556        uids = []
557        for v in value:
558            if type(v) in [StringType, UnicodeType]:
559                vobj=tool(UID=v)
560                # AttributeError: LazyCat instance has no attribute 'getObject'
561                #vobj=vobj.getObject()
562                if allowed=='all' or (allowed=='images' and vobj.isImage()):                 
563                    uids.append(v)
564            else:
565                if allowed=='all' or (allowed=='images' and v.isImage()): 
566                    uids.append(v.UID())
567
568        add = [v for v in uids if v and v not in targetUIDs]
569        sub = [t for t in targetUIDs if t not in uids]
570
571        # tweak keyword arguments for addReference
572        addRef_kw = kwargs.copy()
573        addRef_kw.setdefault('referenceClass', self.referenceClass)
574        if addRef_kw.has_key('schema'): del addRef_kw['schema']
575
576        for uid in add:
577            __traceback_info__ = (instance, uid, value, targetUIDs)
578            # throws IndexError if uid is invalid
579            tool.addReference(instance, uid, self.relationship, **addRef_kw)
580
581        for uid in sub:
582            tool.deleteReference(instance, uid, self.relationship)
583
584        if self.callStorageOnSet:
585            #if this option is set the reference fields's values get written
586            #to the storage even if the reference field never use the storage
587            #e.g. if i want to store the reference UIDs into an SQL field
588            ObjectField.set(self, instance, self.getRaw(instance), **kwargs)
589
590registerField(PieceField,
591               title='Piece Field',
592               description=('References to Pieces'),
593               )
594
595
596
597class TagsField(LinesField):
598    """ A field that stores tags """
599    __implements__ = LinesField.__implements__
600    _properties = LinesField._properties.copy()
601    _properties.update({
602        'widget' : TagsWidget,
603    })
604
605    security = ClassSecurityInfo()
606
607    def set(self, instance, value, **kwargs):
608        tags = []
609        tags_dirty = []
610    if isinstance(value,str):
611            value = value.lower()
612            tags_dirty = value.split(',')
613            value = tags_dirty
614            tags = []
615        if isinstance(value, tuple) or isinstance(value, list):
616            tags_dirty = value
617            [ tags.append(t.strip().lower()) for t in tags_dirty if t.strip().lower() not in tags ]
618            value = ','.join(tags)
619    if isinstance(value,str):
620        value = value.replace(',','\n')
621        LinesField.set(self, instance, value, **kwargs)
622                       
623    def getRaw(self, instance, **kwargs):
624        value = self.get(instance, **kwargs)
625        return ', '.join(value)
626
627    def get(self, instance, **kwargs):
628        return LinesField.get(self, instance, **kwargs)
629
630registerField(TagsField,
631               title='Tags Field',
632               description=('Tags Field'),
633               )
634
635class WYSIWYMField(StringField):
636    """A field that stores WYSIWYM strings"""
637    _properties = StringField._properties.copy()
638    _properties.update({
639        'widget' : WYSIWYMWidget,
640        'edit_accessor' :  "getRaw",
641        'default_output_type' : 'text/x-html-captioned',
642        'default_content_type' : 'text/html',
643    })   
644   
645    security = ClassSecurityInfo()
646   
647   
648    def getRaw(self, instance, **kwargs):
649        value = self.get_historical(instance,**kwargs)
650        return value
651   
652    def get_historical(self, instance, **kwargs):
653        """Special get method to handle history views as well."""
654        version = instance.REQUEST.get('version',None)
655        if not version:
656            return StringField.get(self,instance,**kwargs)
657        # Get old data from history
658        #fields = instance.getHistoricalFields(version)
659        value = instance.getFieldHistory(self.getName(), version)
660        #try:
661        #    value = fields[self.getName()]
662        #except KeyError:
663        #    value = ''
664        return value
665
666    def get(self, instance, **kwargs):
667        ltool = getToolByName(instance, 'lemill_tool')
668        value = self.get_historical(instance,**kwargs)
669        return ltool.render(value)
670
671    # render -method has moved to LeMillTools, because it is needed also outside the wysiwym field.
672
673registerField(WYSIWYMField,
674               title='WYSIWYM Field',
675               description=('WYSIWYM Field'),
676               )
677
678class LinkLinesField(LinesField):
679    """ handles lists of links and their texts """
680    _properties = LinesField._properties.copy()
681    _properties.update({
682        'widget' : LinkLinesWidget,
683    })
684
685    security = ClassSecurityInfo()
686   
687    def get(self, instance, **kwargs):
688        """ Get a list of tuples, where [0] is url and [1] text """
689        values = LinesField.get(self, instance, **kwargs)
690        values = [tuple(x.split('###',1)) for x in values]
691        return values
692
693
694    security.declarePrivate('set')
695    def set(self, instance, value, **kwargs):
696        value= ['###'.join(x) for x in value]
697        LinesField.set(self, instance, value, **kwargs)
698
699registerField(LinkLinesField,
700               title='Link Lines Field',
701               description=('List of links'),
702               )
703
704
705
706class ChapterField(ObjectField):
707    """ chapters are stored as lists, where chapter can either be a string or UID. If UID, view widget should fetch the object. """
708    _properties = ObjectField._properties.copy()
709    _properties.update({
710        'widget' : ChapterWidget,
711        'relationship' : 'uses', # required
712        'referenceClass' : Reference,
713        'referenceReferences' : False,
714        'deleteEmptyChapters' : True
715    })
716   
717    security = ClassSecurityInfo()
718
719    def get(self, instance, **kwargs):
720        """Special get method to handle history views as well."""
721        version = instance.REQUEST.get('version',None)
722        if not version:
723            return ObjectField.get(self,instance,**kwargs)
724        # Get old data from history
725        #fields = instance.getHistoricalFields(version)
726        value = instance.getFieldHistory(self.getName(), version)
727        #value = fields[self.getName()]
728        if type(value) != type([]):
729            return [value,]
730        else:
731            return value
732
733    def set(self, instance, value, **kwargs):
734        tool = getToolByName(instance, REFERENCE_CATALOG)
735        targetUIDs = tool.getReferences(instance, self.relationship)
736        finalvalues=[]
737        allowed_types_sequence=['']
738        sequence_index=0
739        # widget cannot test file types with ease,
740        # so it sends requirement sequence as list f.ex ['image','string'] for Presentation 
741        if kwargs.has_key('allowed_types_sequence'):
742            allowed_types_sequence=kwargs['allowed_types_sequence']           
743        sequence_length=len(allowed_types_sequence)
744        ignore_type= allowed_types_sequence==['']
745        uids=[]
746        if value==None: value=['']
747        if type(value)==str: value=[value]       
748       
749        # tweak keyword arguments for addReference
750        addRef_kw = kwargs.copy()
751        addRef_kw.setdefault('referenceClass', self.referenceClass)
752        if addRef_kw.has_key('schema'): del addRef_kw['schema']
753
754        #assume that values are given in list   
755        for chapter in value:
756            if chapter and hasattr(chapter, 'filename'):
757                # This is a file, so create a piece from it, get its uid and give that to be the content of this chapter
758                if chapter.headers['Content-Type'] not in MIMETYPE_WHITELIST:
759                    fine_piece=False
760                    if allowed_types_sequence[sequence_index]=='image' or ignore_type:
761                        chapter = 'thisisamediapiecethisisamediapie'
762                    elif allowed_types_sequence[sequence_index]=='audio':
763                        chapter = 'thisisaudiopiecethisisaudiopiece'
764                    else:
765                        chapter = None
766
767                else:               
768                    id = instance.generateUniqueId('Piece')
769                    portal_url = getToolByName(instance, 'portal_url')
770                    filename=chapter.filename.split('/')[-1] # Windows brings in the whole path as filename, take only last part
771   
772                    new_id = portal_url.getPortalObject().content.invokeFactory('Piece', id, title='-'.join((instance.Title(),filename)))
773                    new_piece = getattr(instance.content, new_id)
774                    new_piece.edit(file=chapter)                       
775                   
776                    # file is uploaded and piece is made, but is it a piece of correct type?               
777                    if new_piece.isImage() and (allowed_types_sequence[sequence_index]=='image' or ignore_type):
778                        new_piece.edit(image=chapter)
779                        chapter = new_piece.UID()
780                        fine_piece = True             
781                    elif new_piece.isAudio() and (allowed_types_sequence[sequence_index]=='audio' or ignore_type):
782                        chapter = new_piece.UID()
783                        fine_piece = True
784                    else:
785                        fine_piece = False               
786
787                # store UID in session so that we can edit these new pieces
788                if fine_piece:
789                    session=instance.REQUEST.SESSION
790                    if session.has_key('new_pieces'):
791                        session['new_pieces'].append(new_piece.UID())
792                    else:
793                        session['new_pieces']=[new_piece.UID()]
794
795            #try to set references
796            if type(chapter)==str:
797                if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's a UID!
798                    uids.append(chapter) # collect list of good uids that should be there                 
799                    if chapter not in targetUIDs and chapter not in ['thisisamediapiecethisisamediapie', 'thisisaudiopiecethisisaudiopiece']: # if not there, add it
800                        __traceback_info__ = (instance, chapter, targetUIDs)
801                        tool.addReference(instance, chapter, self.relationship, **addRef_kw)
802
803            if chapter!=None or self.deleteEmptyChapters==False:
804                finalvalues.append(chapter)
805            sequence_index=sequence_index+1
806            if sequence_index==sequence_length:
807                sequence_index=0
808
809
810        for uid in targetUIDs: # delete bad references
811            if uid not in uids:
812                tool.deleteReference(instance, uid, self.relationship)           
813        # make sure there is item 0 in list
814        if finalvalues==[]:
815            if allowed_types_sequence[0]=='image':
816                finalvalues=['thisisamediapiecethisisamediapie']
817            elif allowed_types_sequence[0]=='audio':
818                finalvalues=['thisisaudiopiecethisisaudiopiece']
819            else:
820                finalvalues=['']               
821        # finally get it done
822        ObjectField.set(self, instance, finalvalues, **kwargs)
823
824
825    def add_new_chapter(self,instance, insert_point=-1):
826        """ adds new field/chapter """
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((''))
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_mediapiece(self,instance, insert_point=-1):
841        """ adds new field/mediapiece """
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=value+value_end
849        ObjectField.set(self, instance, value)
850        if insert_point==-1:       
851            return len(value)-1
852        else:
853            return insert_point
854
855    def add_new_slide(self, instance, insert_point=-1):
856        """ adds slide and caption (2 objects) to list """
857        value=ChapterField.get(self,instance)
858        value_end=[]
859        if insert_point!=-1:
860            value_end=value[insert_point:]
861            value=value[:insert_point]           
862        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
863        value.append((''))
864        value=value+value_end
865        ObjectField.set(self, instance, value)
866        if insert_point==-1:       
867            return len(value)-2
868        else:
869            return insert_point
870
871    def add_new_scene(self, instance, insert_point=-1):
872        """ adds slide, voiceover and keywords (PILOT)"""
873        value=ChapterField.get(self,instance)
874        if insert_point==-1:
875            insert_point=len(value)-3
876        value_end=[]       
877        value_end=value[insert_point:]
878        value=value[:insert_point]           
879        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
880        value.append(('thisisaudiopiecethisisaudiopiece'))
881        value.append(['','',''])
882        value=value+value_end
883        ObjectField.set(self, instance, value)
884        return insert_point
885       
886    def isUid(self, chapter):
887        """ Checks if chapter seems, smells and feels like UID """
888        if type(chapter) in STRING_TYPES:
889            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
890                return chapter
891        elif type(chapter) in (list, tuple, int) or hasattr(chapter, 'filename'):
892            return False
893        else:
894#            try:
895                chapter=chapter.UID()
896                return chapter
897#            except:
898                return False
899               
900    def isAudioUID(self, chapter):
901        """ True if UID is audio placeholder """
902        if chapter=='thisisaudiopiecethisisaudiopiece':
903            return True
904
905    def isImageUID(self, chapter):
906        """ True if UID is audio placeholder """
907        if chapter=='thisisamediapiecethisisamediapie':
908            return True
909
910    def isKeywords(self, chapter):
911        """Keywords are lists of three strings, used in PILOT """
912        if type(chapter)==list and len(chapter)==3:
913            return True
914
915    def isQuestions(self, chapter):
916        """Questions are lists of seven strings, used in PILOT """
917        if type(chapter)==list and len(chapter)==7:
918            return True           
919               
920    def getObjectByUID(self, instance, uid):
921        """ Helper method for fetching mediapieces from references """
922        uid_catalog = getToolByName(instance, 'uid_catalog')
923        if uid=='thisisamediapiecethisisamediapie' or uid=='thisisaudiopiecethisisaudiopiece': return None # this is just a newly created placeholder
924        results=uid_catalog(UID=uid)
925        return results[0].getObject()
926
927    def getChapters(self, instance):
928        """ get a list of chapters with text. returning id's of chapters... eg. [1001, 1101, 1201] """
929        value=ChapterField.get(self,instance)
930        result=[]
931        for n in range(len(value)):
932            chapter=value[n]
933            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
934                pass
935            else:
936                result.append(n)
937        return result
938
939    def getChapter(self, instance, chap_nr):
940        """ get a content of one chapter """
941        value=ChapterField.get(self, instance)
942        if len(value)<chap_nr:
943            return ''
944        else:
945            return value[chap_nr]
946
947    def getPieces(self, instance, chap_nr, as_objects=False):
948        """ return a list of pieces; uids or object list """
949        value=ChapterField.get(self,instance)
950        result=[]
951        for chapter in value:
952            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
953                result.append(chapter)
954        if not as_objects:
955            return result
956        res = []
957        new_value = value
958        removed=[]
959        tool = getToolByName(instance, 'reference_catalog')
960        for uid in result:
961                v = tool.lookupObject(uid)
962                if not v:
963                    removed.append(uid)
964                else:
965                    res.append(v)
966        for chapclip in removed:
967            new_value.remove(chapclip)
968        if new_value!=value:
969            ObjectField.set(self, instance, new_value)
970        return res
971
972    def getFirstChapter(self, instance):
973        """ return first chapter """
974        return 0
975
976    def setChapter(self, instance, value, chap_nr):
977        """ set a chapter """
978        values=ChapterField.get(self,instance)
979        values[chap_nr]=value
980        ObjectField.set(self, instance, values)
981
982    def delChapter(self, instance, chap_nr):
983        """ delete chapter """
984        value=ChapterField.get(self,instance)
985        del value[chap_nr]
986        if value==[]: value=['']
987        ObjectField.set(self, instance, value)       
988
989    def setPieces(self, instance, value, chap_nr):
990        """ set uids """
991        values=ChapterField.get(self,instance)
992        if type(value) not in STRING_TYPES:
993            value=value.UID()                   
994        values[chap_nr]=value
995        ObjectField.set(self, instance, values)
996
997    def delPiece(self, instance, chap_nr):
998        """ delete reference to piece """
999        values=ChapterField.get(self,instance)
1000        tool = getToolByName(instance, REFERENCE_CATALOG)
1001        tool.deleteReference(instance, values[chap_nr], self.relationship)
1002        del values[chap_nr]
1003        if values==[]: values=['']
1004        ObjectField.set(self, instance, values)       
1005       
1006    def delSlide(self, instance, chap_nr):
1007        """ delete both chapter and caption """
1008        values=ChapterField.get(self,instance)
1009        tool = getToolByName(instance, REFERENCE_CATALOG)
1010        tool.deleteReference(instance, values[chap_nr], self.relationship)
1011        del values[chap_nr]
1012        if len(values)>chap_nr:
1013            del values[chap_nr]
1014        if values==[]: values=['']
1015        ObjectField.set(self, instance, values)       
1016
1017    def delScene(self, instance, chap_nr):
1018        """ delete image, audio and keywords/questions (PILOT)"""
1019        values=ChapterField.get(self,instance)
1020        tool = getToolByName(instance, REFERENCE_CATALOG)
1021        tool.deleteReference(instance, values[chap_nr], self.relationship)
1022        tool.deleteReference(instance, values[chap_nr+1], self.relationship)
1023        for a in [1,2,3]:
1024            if len(values)>chap_nr:
1025                del values[chap_nr]
1026        if values==[]: values=['']
1027        ObjectField.set(self, instance, values)
1028       
1029    def moveChapterUp(self, instance, chap_nr, granularity):
1030        """ Move chapter chap_nr, chunk size is defined by int granularity """
1031        values=ChapterField.get(self,instance)
1032        if chap_nr-granularity<0:
1033            return 0       
1034        begin = values[:chap_nr-granularity] # minus the chunk before current chapter
1035        moving_part = values[chap_nr-granularity:chap_nr] # the chunk before current chapter
1036        end = values[chap_nr+granularity:]
1037        chapter = values[chap_nr:chap_nr+granularity]
1038        ObjectField.set(self, instance, begin+chapter+moving_part+end)
1039       
1040    def moveChapterDown(self, instance, chap_nr, granularity):
1041        """ Move chapter chap_nr, chunk size is defined by int granularity """
1042        values=ChapterField.get(self,instance)
1043        if chap_nr+2*granularity>len(values):
1044            return 0     
1045        begin = values[:chap_nr]
1046        chapter = values[chap_nr:chap_nr+granularity]
1047        moving_part = values[chap_nr+granularity:chap_nr+granularity*2] # the chunk after current chapter
1048        end = values[chap_nr+granularity*2:] # the rest after current chapter and chunk after it
1049        ObjectField.set(self, instance, begin+moving_part+chapter+end)
1050
1051
1052    def getLength(self, piece):
1053        as_time=piece.getLength()
1054        return {'hour':as_time/3600,'minute':(as_time % 3600)/60,'second':as_time % 60}           
1055
1056
1057registerField(ChapterField,
1058    title='Chapter field',
1059    description=('Chapter field'),
1060)
1061
1062
1063class DictField(LinesField):
1064    """ dictionary field """
1065    _properties = LinesField._properties.copy()
1066    _properties.update({
1067        'widget' : StringField,
1068    })
1069
1070    security = ClassSecurityInfo()
1071    def setValue(self, instance, key, value):
1072        i = aq_base(instance)
1073        if not shasattr(i, '_ivalues'):
1074            i._ivalues = PersistentMapping()
1075            i._p_changed = 1
1076        i._ivalues[key] = value
1077        i._ivalues._p_changed = 1
1078        i._p_changed = 1
1079
1080    def getKeys(self, instance):
1081        i = aq_base(instance)
1082        if not shasattr(i, '_ivalues'):
1083            return []
1084        else:
1085            return i._ivalues.keys()
1086
1087    def getValue(self, instance, key):
1088        i = aq_base(instance)
1089        if not shasattr(i, '_ivalues'):
1090            i._ivalues = PersistentMapping()
1091            i._p_changed = 1
1092            return ""
1093        if not i._ivalues.has_key(key):
1094            return ""
1095        return i._ivalues[key]
1096
1097class AudioField(FileField):
1098    """ field for mp3:s """
1099    _properties = FileField._properties.copy()
1100    _properties.update({
1101        'widget' : AudioWidget,
1102    })
1103
1104    def getLength(self, instance):
1105        i = aq_base(instance)
1106        file = AudioField.get(self,instance)
1107        if not shasattr(i, '_mp3length'):
1108                instance._mp3length=get_length(str(file))
1109        print instance._mp3length
1110        if instance._mp3length==0:
1111                instance._mp3length=get_length(str(file))
1112        return {'hour':instance._mp3length/3600,'minute':(instance._mp3length % 3600)/60,'second':instance._mp3length % 60}
1113           
1114    def set(self, instance, value, **kwargs):
1115        """ allow only audio here """
1116        FileField.set(self, instance, value, **kwargs)
1117        file=self.get(instance)
1118        if self.getContentType(instance).startswith('audio/'):
1119            instance._mp3length=get_length(str(file))
1120        elif str(file)!='':
1121            FileField.set(self, instance, "DELETE_FILE", **kwargs)
1122           
1123
1124registerField(AudioField,
1125    title='Audio field',
1126    description=('Field that accepts only mp3:s'),
1127)
Note: See TracBrowser for help on using the repository browser.