source: trunk/FieldsWidgets.py @ 1904

Revision 1904, 34.0 KB checked in by gabor, 13 years ago (diff)

fixed #1446 spent 17h

  • 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
35from Products.LeMill import LeMillMessageFactory as _
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
56    def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False):
57        """Basic impl for form processing in a widget"""
58        value = form.get(field.getName(), empty_marker)
59        if value is empty_marker:
60            return empty_marker
61        if emptyReturnsMarker and value == '':
62            return empty_marker
63
64        if form.get('replaceNewLines') != None and field.getName() == 'bodyText':
65            lt = getToolByName(instance, 'lemill_tool')
66            value = lt.addBreaksToText(value)
67
68        return value, {}
69
70registerWidget(LeVisualWidget,
71    title='Visual Widget',
72    description='Visual Widget'
73)   
74
75class LeStringWidget(StringWidget):
76    _properties = StringWidget._properties.copy()
77    _properties.update({
78        'macro' : 'widget_string'
79    })
80
81registerWidget(LeStringWidget,
82    title='String Widget',
83    description='String Widget'
84)
85
86
87class LeTextAreaWidget(TextAreaWidget):
88    _properties = TextAreaWidget._properties.copy()
89    _properties.update({
90        'macro' : 'widget_textarea'
91    })
92
93registerWidget(LeTextAreaWidget,
94    title='Textarea Widget',
95    description='Textarea Widget'
96)
97
98
99
100class TagsWidget(StringWidget):
101    _properties = StringWidget._properties.copy()
102    _properties.update({
103        'macro' : 'widget_tags',
104    })
105   
106registerWidget(TagsWidget,
107    title='Tags Widget',
108    description='Tags Widget',
109    used_for=('Products.LeMill.TagsField.TagsField',)
110)
111
112class HTMLLinkWidget(StringWidget):
113    _properties = StringWidget._properties.copy()
114    _properties.update({
115        'macro' : 'widget_htmllink',
116    })
117
118    def getShortLinkName(self, link):
119        """Returns the shortened version of the link pointing to the reference's location. This shortened version goes into the content of the <a> tag."""     
120        lt = getToolByName(self, 'lemill_tool')
121        return lt.shorten_link_names(lt.htmlify(link))
122   
123registerWidget(HTMLLinkWidget,
124    title='HTML Link Widget',
125    description='Generated <a> tag Widget',
126    used_for=('Products.LeMill.FieldsWidgets.HTMLLinkWidget',)
127)
128
129class MessengerWidget(TypesWidget):
130    _properties = TypesWidget._properties.copy()
131    _properties.update({
132        'macro' : 'widget_messenger',
133    },)
134
135    security = ClassSecurityInfo()
136
137    security.declarePublic('process_form')
138    def process_form(self, instance, field, form, empty_marker=None,
139                     emptyReturnsMarker=False):
140        """Concatenates address + messenger type, but if address already contains something like prefix (word:) then removes it.
141        """
142        name = field.getName()
143        otherName = "%s_other" % name
144        value = form.get(name, empty_marker)
145        othervalue = form.get(otherName, empty_marker)
146        if not value:
147            return '', {}
148        if not othervalue == empty_marker:
149            value = "%s:%s" % (othervalue,value)
150        return value, {}
151
152
153registerWidget(MessengerWidget,
154    title='Messenger Widget',
155    description='Messenger Widget',
156    used_for=('Products.Archetypes.Field.StringField',)
157)
158
159
160class MobileWidget(StringWidget):
161    _properties = StringWidget._properties.copy()
162    _properties.update({
163        'macro' : 'widget_mobile',
164    })
165
166    security = ClassSecurityInfo()
167
168    security.declarePublic('process_form')
169    def process_form(self, instance, field, form, empty_marker=None,
170                     emptyReturnsMarker=False):
171        name = field.getName()
172        otherName = "%s_checkbox" % name
173        value = form.get(name, empty_marker)
174        othervalue = form.get(otherName, empty_marker)
175        if value == empty_marker and emptyReturnsMarker:
176            return empty_marker
177        if othervalue=="1":
178            value = "*SMS*%s" % value
179        return value, {}
180
181   
182registerWidget(MobileWidget,
183    title='Mobile Widget',
184    description='Mobile Widget',
185    used_for=('Products.Archetypes.Field.StringField',)
186)
187
188
189class CopyrightWidget(SelectionWidget):
190    _properties = SelectionWidget._properties.copy()
191    _properties.update({
192        'macro' : 'widget_copyright',
193    })
194   
195registerWidget(CopyrightWidget,
196    title='Copyright Widget',
197    description='Copyright selection Widget',
198    used_for=('Products.LeMill.StringField.StringField',)
199)
200
201class TwoColumnMultiSelectionWidget(MultiSelectionWidget):
202    _properties = MultiSelectionWidget._properties.copy()
203    _properties.update({
204        'macro' : 'widget_twocolumn',
205    })
206   
207registerWidget(TwoColumnMultiSelectionWidget,
208    title='Two column widget',
209    description='Two column multiselection widget',
210    used_for=('Products.Archetypes.Field.MultiSelectionField',)
211)
212
213
214class GroupWidget(SelectionWidget):
215    _properties = SelectionWidget._properties.copy()
216    _properties.update({
217        'macro' : 'widget_group',
218        'format' : 'radio',
219    })
220
221
222class refImagesWidget(SelectionWidget):
223    _properties = SelectionWidget._properties.copy()
224    _properties.update({
225        'macro' : 'widget_imageselection',
226        'helper_js' : ('js_helpers.js',),
227    })     
228
229class ImageSelectorWidget(SelectionWidget):
230    _properties = SelectionWidget._properties.copy()
231    _properties.update({
232        'macro' : 'widget_imageselector',
233        'helper_js' : ('js_helpers.js',),
234    })
235
236class ChapterWidget(SelectionWidget):
237    _properties = SelectionWidget._properties.copy()
238    _properties.update({
239        'macro' : 'widget_chapter',
240        'collections_only' : False,
241        'helper_js' : ('js_helpers.js','AC_RunActiveContent.js'),
242        'show_added' : True,
243        'chapter_count' : 1,
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        """ goes through the form and tries to find all texts and references to pieces. """
252        value = []
253        fieldname=field.getName()
254        file=form.get('%s_file' % fieldname)       
255        upload_pos=int(form.get('%s_uploaded' % fieldname))
256        for i in range(0,int(form.get('%s_count' % fieldname, 0))):
257            text= form.get('%s_%s' % (fieldname, i))
258            text = remove_invalid_html_tags(text)
259            if form.get('replaceNewLines') != None and field.getName() == 'bodyText':
260                lt = getToolByName(instance, 'lemill_tool')
261                text = lt.addBreaksToText(text)
262            value.append(text)
263
264        if file and hasattr(file, 'filename') and upload_pos>-1:
265            value[upload_pos]=file
266        if value == []:
267            return empty_marker
268        return value, {}
269
270registerWidget(ChapterWidget,
271    title='Chapter Widget',
272    description='Chapter editor Widget',
273    used_for=('Products.LeMill.ChapterField',)
274)
275
276class SlideWidget(ChapterWidget):
277    _properties = ChapterWidget._properties.copy()
278    _properties.update({
279        'macro' : 'widget_slides',
280        'collections_only' : False,
281        'helper_js' : ('js_helpers.js',),
282        'show_added' : True,
283        'chapter_count' : 5,
284    })
285
286
287    security = ClassSecurityInfo()
288
289    security.declarePublic('process_form')
290    def process_form(self, instance, field, form, empty_marker=None,
291            emptyReturnsMarker=False):
292        """ goes through the form and tries to find all texts and references to pieces. """
293        fieldname=field.getName()
294        value = form.get(fieldname, empty_marker) # allow tests to create presentations from strings
295        if type(value)==str:
296            return['thisisamediapiecethisisamediapie', value], {} # simplest possible presentation
297        value = []
298        for i in range(0,int(form.get('%s_count' % fieldname, 1))):
299            text= form.get('%s_%s' % (fieldname, i))
300            text = remove_invalid_html_tags(text)
301            file=form.get('%s_file_%s' % (fieldname, i))
302            piece_uid=form.get('piece_uid_%s' % i, '')
303            if piece_uid=='0':
304                text=''       
305            value.append(text)
306            if file and hasattr(file, 'filename'):
307                value[i]=file
308
309        if value == []:
310            return empty_marker
311        return value, {'allowed_types_sequence':['image','string']}
312
313
314registerWidget(SlideWidget,
315    title='Slide Widget',
316    description='Slide and caption editor Widget',
317    used_for=('Products.LeMill.ChapterField',)
318)
319
320class PilotWidget(ChapterWidget):
321    _properties = ChapterWidget._properties.copy()
322    _properties.update({
323        'macro' : 'widget_pilot',
324        'collections_only' : False,
325        'helper_js' : ('js_helpers.js','AC_RunActiveContent.js'),
326        'show_added' : True,
327        'chapter_count' : 3,
328    })
329
330
331    security = ClassSecurityInfo()
332
333    security.declarePublic('process_form')
334    def process_form(self, instance, field, form, empty_marker=None,
335            emptyReturnsMarker=False):
336        """ 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. """
337        fieldname=field.getName()
338        value = []
339        edited = int(form.get('%s_last_edited' % fieldname, 1))
340        count= int(form.get('%s_count' % fieldname, 1))
341        y=3
342        for i in range(0,count):
343            text= form.get('%s_%s' % (fieldname, i))
344            if text!="" and text!=None:
345                if text.startswith('#keywords#!#'):
346                    text=text.split('#!#')[1:]
347                text = remove_invalid_html_tags(text)
348                value.append(text)
349
350            if i >= edited and i<edited+3: 
351                # files
352                if i%3 != 2:
353                    file=form.get('%s_file_%s' % (fieldname, i))   
354                    if file and hasattr(file, 'filename'):
355                        value[i]=file
356                        continue
357                # keywords and questions
358                if i%3==2:               
359                    kw_counter=0
360                    kw=''
361                    target_len=3
362                    keywordlist=[]
363                    while(kw!=None and kw_counter<8):
364                        kw=form.get('%s_keyword_%s_%s' % (fieldname,i,kw_counter), None)
365                        if kw!=None:                     
366                            keywordlist.append(kw)
367                        kw_counter=kw_counter+1
368                    if i==count-1:
369                        target_len=7
370                    while len(keywordlist)<target_len:
371                        keywordlist.append('')
372                    value[i]=keywordlist
373        if value == []:
374            return empty_marker
375        return value, {'allowed_types_sequence':['image','audio','list']}
376
377    def merge_keywords(self, value):
378        """ allow lists to be stored in forms as hidden fields """
379        if type(value)==list or type(value)==tuple:
380            value='#!#'.join(value)
381            return ''.join(('#keywords#!#',value))
382         
383
384
385registerWidget(PilotWidget,
386    title='Pilot Widget',
387    description='Scene editor Widget',
388    used_for=('Products.LeMill.ChapterField',)
389)
390
391
392class AudioWidget(FileWidget):
393    _properties = FileWidget._properties.copy()
394    _properties.update({
395    'macro' : 'widget_audio',
396    'helper_js' : ('AC_RunActiveContent.js',),
397    })
398
399registerWidget(AudioWidget,
400    title='Audio Widget',
401    description='Widget that uses flashplayer for mp3:s',
402    used_for=('Products.LeMill.ChapterField',)
403)
404
405
406
407
408####                            ####
409####            FIELDS          ####
410####                            ####
411
412
413class TagsField(LinesField):
414    """ A field that stores tags """
415    __implements__ = LinesField.__implements__
416    _properties = LinesField._properties.copy()
417    _properties.update({
418        'widget' : TagsWidget,
419    })
420
421    security = ClassSecurityInfo()
422
423    def set(self, instance, value, **kwargs):
424        tags = []
425        tags_dirty = []
426    if isinstance(value,str):
427            value = value.lower()
428            tags_dirty = value.split(',')
429            value = tags_dirty
430            tags = []
431        if isinstance(value, tuple) or isinstance(value, list):
432            tags_dirty = value
433            [ tags.append(t.strip().lower()) for t in tags_dirty if t.strip().lower() not in tags ]
434            value = ','.join(tags)
435    if isinstance(value,str):
436        value = value.replace(',','\n')
437        LinesField.set(self, instance, value, **kwargs)
438                       
439    def getRaw(self, instance, **kwargs):
440        value = self.get(instance, **kwargs)
441        return ', '.join(value)
442
443    def get(self, instance, **kwargs):
444        return LinesField.get(self, instance, **kwargs)
445
446registerField(TagsField,
447               title='Tags Field',
448               description=('Tags Field'),
449               )
450
451class WYSIWYMField(StringField):
452    """A field that stores WYSIWYM strings"""
453    _properties = StringField._properties.copy()
454    _properties.update({
455        'widget' : LeTextAreaWidget,
456        'edit_accessor' :  "getRaw",
457        'default_output_type' : 'text/x-html-captioned',
458        'default_content_type' : 'text/html',
459    })   
460   
461    security = ClassSecurityInfo()
462   
463   
464    def getRaw(self, instance, **kwargs):
465        value = self.get_historical(instance,**kwargs)
466        return value
467   
468    def get_historical(self, instance, **kwargs):
469        """Special get method to handle history views as well."""
470        version = instance.REQUEST.get('version',None)
471        if not version:
472            return StringField.get(self,instance,**kwargs)
473        # Get old data from history
474        #fields = instance.getHistoricalFields(version)
475        value = instance.getFieldHistory(self.getName(), version)
476        #try:
477        #    value = fields[self.getName()]
478        #except KeyError:
479        #    value = ''
480        return value
481
482    def get(self, instance, **kwargs):
483        ltool = getToolByName(instance, 'lemill_tool')
484        value = self.get_historical(instance,**kwargs)
485        #return ltool.render(value)
486        return value
487       
488
489    # render -method has moved to LeMillTools, because it is needed also outside the wysiwym field.
490
491registerField(WYSIWYMField,
492               title='WYSIWYM Field',
493               description=('WYSIWYM Field'),
494               )
495
496
497class ChapterField(ObjectField):
498    """ chapters are stored as lists, where chapter can either be a string or UID. If UID, view widget should fetch the object. """
499    _properties = ObjectField._properties.copy()
500    _properties.update({
501        'widget' : ChapterWidget,
502        'relationship' : 'uses', # required
503        'referenceClass' : Reference,
504        'referenceReferences' : False,
505        'deleteEmptyChapters' : True
506    })
507   
508    security = ClassSecurityInfo()
509
510    def get(self, instance, **kwargs):
511        """Special get method to handle history views as well."""
512        version = instance.REQUEST.get('version',None)
513        if not version:
514            return ObjectField.get(self,instance,**kwargs)
515        # Get old data from history
516        #fields = instance.getHistoricalFields(version)
517        value = instance.getFieldHistory(self.getName(), version)
518        #value = fields[self.getName()]
519        if type(value) != type([]):
520            return [value,]
521        else:
522            return value
523
524    def getCooked(self, instance, **kwargs):
525        """Get method that parses html as well."""
526        version = instance.REQUEST.get('version',None)
527        lt=getToolByName(self,'lemill_tool')
528        if not version:
529            raw=ObjectField.get(self,instance,**kwargs)
530        else:
531            # Get old data from history
532            #fields = instance.getHistoricalFields(version)
533            raw = instance.getFieldHistory(self.getName(), version)
534            #value = fields[self.getName()]
535            if type(raw) != type([]):
536                raw=[raw,]
537        return [lt.shorten_link_names(lt.htmlify(s)) for s in raw]
538
539
540    def set(self, instance, value, **kwargs):
541        tool = getToolByName(instance, REFERENCE_CATALOG)
542        portal_url = getToolByName(instance, 'portal_url')
543        plone_utils = getToolByName(instance, 'plone_utils')
544        lt = getToolByName(instance, 'lemill_tool')
545        targetUIDs = [x.targetUID for x in tool.getReferences(instance, self.relationship)]
546        finalvalues=[]
547        allowed_types_sequence=['']
548        sequence_index=0
549        # widget cannot test file types with ease,
550        # so it sends requirement sequence as list f.ex ['image','string'] for Presentation 
551        if kwargs.has_key('allowed_types_sequence'):
552            allowed_types_sequence=kwargs['allowed_types_sequence']           
553        sequence_length=len(allowed_types_sequence)
554        ignore_type= allowed_types_sequence==['']
555        uids=[]
556        if value==None: value=['']
557        if type(value)==str: value=[value]       
558       
559        # tweak keyword arguments for addReference
560        addRef_kw = kwargs.copy()
561        addRef_kw.setdefault('referenceClass', self.referenceClass)
562        if addRef_kw.has_key('schema'): del addRef_kw['schema']
563
564        #assume that values are given in list   
565        for chapter in value:
566            if chapter and hasattr(chapter, 'filename'):
567                # This is a file, so create a piece from it, get its uid and give that to be the content of this chapter
568                if chapter.headers['Content-Type'] not in MIMETYPE_WHITELIST:
569                    fine_piece=False
570                    if allowed_types_sequence[sequence_index]=='image' or ignore_type:
571                        chapter = 'thisisamediapiecethisisamediapie'
572                    elif allowed_types_sequence[sequence_index]=='audio':
573                        chapter = 'thisisaudiopiecethisisaudiopiece'
574                    else:
575                        chapter = None
576
577                else:               
578                    id = instance.generateUniqueId('Piece')
579                    filename=chapter.filename.split('/')[-1] # Windows brings in the whole path as filename, take only last part
580                    filename=filename.split('\\')[-1] # stupid windows                   
581                    if not lt.checkTitle(title=filename): # try with filename
582                        filename='-'.join((instance.Title(),filename)) # else use longer version
583                    new_id = portal_url.getPortalObject().content.invokeFactory('Piece', id, title=filename)
584                    new_piece = getattr(instance.content, new_id)
585                    new_piece.edit(file=chapter)                       
586                   
587                    # file is uploaded and piece is made, but is it a piece of correct type?               
588                    if new_piece.isImage() and (allowed_types_sequence[sequence_index]=='image' or ignore_type):
589                        new_piece.edit(image=chapter)
590                        chapter = new_piece.UID()
591                        fine_piece = True
592                        plone_utils.addPortalMessage(_(u'Image uploaded'))             
593                    elif new_piece.isAudio() and (allowed_types_sequence[sequence_index]=='audio' or ignore_type):
594                        chapter = new_piece.UID()
595                        fine_piece = True
596                        plone_utils.addPortalMessage(_(u'Audio clip uploaded'))             
597                    else:
598                        fine_piece = False               
599
600            #try to set references
601            if type(chapter)==str:
602                if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's a UID!
603                    uids.append(chapter) # collect list of good uids that should be there                 
604                    if chapter not in targetUIDs and chapter not in ['thisisamediapiecethisisamediapie', 'thisisaudiopiecethisisaudiopiece']: # if not there, add it
605                        __traceback_info__ = (instance, chapter, targetUIDs)
606                        tool.addReference(instance, chapter, self.relationship, **addRef_kw)
607
608            if chapter!=None or self.deleteEmptyChapters==False:
609                finalvalues.append(chapter)
610            sequence_index=sequence_index+1
611            if sequence_index==sequence_length:
612                sequence_index=0
613
614
615        for uid in targetUIDs: # delete bad references       
616            if uid not in uids:
617                tool.deleteReference(instance, uid, self.relationship)           
618        # make sure there is item 0 in list
619        if finalvalues==[]:
620            if allowed_types_sequence[0]=='image':
621                finalvalues=['thisisamediapiecethisisamediapie']
622            elif allowed_types_sequence[0]=='audio':
623                finalvalues=['thisisaudiopiecethisisaudiopiece']
624            else:
625                finalvalues=['']               
626        # finally get it done
627        ObjectField.set(self, instance, finalvalues, **kwargs)
628
629
630    def add_new_chapter(self,instance, insert_point=-1):
631        """ adds new field/chapter """
632        value=ChapterField.get(self,instance)
633        value_end=[]
634        if insert_point!=-1:
635            value_end=value[insert_point:]
636            value=value[:insert_point]       
637        value.append((''))
638        value=value+value_end
639        ObjectField.set(self, instance, value)
640        if insert_point==-1:       
641            return len(value)-1
642        else:
643            return insert_point
644
645    def add_new_mediapiece(self,instance, insert_point=-1):
646        """ adds new field/mediapiece """
647        value=ChapterField.get(self,instance)
648        value_end=[]
649        if insert_point!=-1:
650            value_end=value[insert_point:]
651            value=value[:insert_point]           
652        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
653        value=value+value_end
654        ObjectField.set(self, instance, value)
655        if insert_point==-1:       
656            return len(value)-1
657        else:
658            return insert_point
659
660    def add_new_slide(self, instance, insert_point=-1):
661        """ adds slide and caption (2 objects) to list """
662        value=ChapterField.get(self,instance)
663        value_end=[]
664        if insert_point!=-1:
665            value_end=value[insert_point:]
666            value=value[:insert_point]           
667        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
668        value.append((''))
669        value=value+value_end
670        ObjectField.set(self, instance, value)
671        if insert_point==-1:       
672            return len(value)-2
673        else:
674            return insert_point
675
676    def add_new_scene(self, instance, insert_point=-1):
677        """ adds slide, voiceover and keywords (PILOT)"""
678        value=ChapterField.get(self,instance)
679        if insert_point==-1:
680            insert_point=len(value)-3
681        value_end=[]       
682        value_end=value[insert_point:]
683        value=value[:insert_point]           
684        value.append(('thisisamediapiecethisisamediapie')) # :) this looks like an UID
685        value.append(('thisisaudiopiecethisisaudiopiece'))
686        value.append(['','',''])
687        value=value+value_end
688        ObjectField.set(self, instance, value)
689        return insert_point
690       
691    def isUid(self, chapter):
692        """ Checks if chapter seems, smells and feels like UID """
693        if type(chapter) in STRING_TYPES:
694            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
695                return chapter
696        elif type(chapter) in (list, tuple, int) or hasattr(chapter, 'filename'):
697            return False
698        else:
699#            try:
700                chapter=chapter.UID()
701                return chapter
702#            except:
703                return False
704               
705    def isAudioUID(self, chapter):
706        """ True if UID is audio placeholder """
707        if chapter=='thisisaudiopiecethisisaudiopiece':
708            return True
709
710    def isImageUID(self, chapter):
711        """ True if UID is audio placeholder """
712        if chapter=='thisisamediapiecethisisamediapie':
713            return True
714
715    def isKeywords(self, chapter):
716        """Keywords are lists of three strings, used in PILOT """
717        if type(chapter)==list and len(chapter)==3:
718            return True
719
720    def isQuestions(self, chapter):
721        """Questions are lists of seven strings, used in PILOT """
722        if type(chapter)==list and len(chapter)==7:
723            return True           
724               
725    def getObjectByUID(self, instance, uid):
726        """ Helper method for fetching mediapieces from references """
727        uid_catalog = getToolByName(instance, 'uid_catalog')
728        if uid=='thisisamediapiecethisisamediapie' or uid=='thisisaudiopiecethisisaudiopiece': return None # this is just a newly created placeholder
729        results=uid_catalog(UID=uid)
730        if results:
731            return results[0].getObject()
732        else:
733            return None
734
735    def getChapters(self, instance):
736        """ get a list of chapters with text. returning id's of chapters... eg. [1001, 1101, 1201] """
737        value=ChapterField.get(self,instance)
738        result=[]
739        for n in range(len(value)):
740            chapter=value[n]
741            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
742                pass
743            else:
744                result.append(n)
745        return result
746
747    def getChapter(self, instance, chap_nr):
748        """ get a content of one chapter """
749        value=ChapterField.get(self, instance)
750        if len(value)<chap_nr:
751            return ''
752        else:
753            return value[chap_nr]
754
755    def getPieces(self, instance, chap_nr, as_objects=False):
756        """ return a list of pieces; uids or object list """
757        value=ChapterField.get(self,instance)
758        result=[]
759        for chapter in value:
760            if chapter.isalnum() and len(chapter)==32 and len(chapter)==len(chapter.strip()): # ok, it's an UID!
761                result.append(chapter)
762        if not as_objects:
763            return result
764        res = []
765        new_value = value
766        removed=[]
767        tool = getToolByName(instance, 'reference_catalog')
768        for uid in result:
769                v = tool.lookupObject(uid)
770                if not v:
771                    removed.append(uid)
772                else:
773                    res.append(v)
774        for chapclip in removed:
775            new_value.remove(chapclip)
776        if new_value!=value:
777            ObjectField.set(self, instance, new_value)
778        return res
779
780    def getFirstChapter(self, instance):
781        """ return first chapter """
782        return 0
783
784    def setChapter(self, instance, value, chap_nr):
785        """ set a chapter """
786        values=ChapterField.get(self,instance)
787        values[chap_nr]=value
788        ObjectField.set(self, instance, values)
789
790    def delChapter(self, instance, chap_nr):
791        """ delete chapter """
792        value=ChapterField.get(self,instance)
793        del value[chap_nr]
794        if value==[]: value=['']
795        ObjectField.set(self, instance, value)       
796
797    def setPieces(self, instance, value, chap_nr):
798        """ set uids """
799        values=ChapterField.get(self,instance)
800        if type(value) not in STRING_TYPES:
801            value=value.UID()                   
802        values[chap_nr]=value
803        ObjectField.set(self, instance, values)
804
805    def delPiece(self, instance, chap_nr):
806        """ delete reference to piece """
807        values=ChapterField.get(self,instance)
808        tool = getToolByName(instance, REFERENCE_CATALOG)
809        tool.deleteReference(instance, values[chap_nr], self.relationship)
810        del values[chap_nr]
811        if values==[]: values=['']
812        ObjectField.set(self, instance, values)       
813       
814    def delSlide(self, instance, chap_nr):
815        """ delete both chapter and caption """
816        values=ChapterField.get(self,instance)
817        tool = getToolByName(instance, REFERENCE_CATALOG)
818        tool.deleteReference(instance, values[chap_nr], self.relationship)
819        del values[chap_nr]
820        if len(values)>chap_nr:
821            del values[chap_nr]
822        if values==[]: values=['thisisamediapiecethisisamediapie', '']
823        ObjectField.set(self, instance, values)       
824
825    def delScene(self, instance, chap_nr):
826        """ delete image, audio and keywords/questions (PILOT)"""
827        values=ChapterField.get(self,instance)
828        tool = getToolByName(instance, REFERENCE_CATALOG)
829        tool.deleteReference(instance, values[chap_nr], self.relationship)
830        tool.deleteReference(instance, values[chap_nr+1], self.relationship)
831        for a in [1,2,3]:
832            if len(values)>chap_nr:
833                del values[chap_nr]
834        if values==[]: values=['']
835        ObjectField.set(self, instance, values)
836       
837    def moveChapterUp(self, instance, chap_nr, granularity):
838        """ Move chapter chap_nr, chunk size is defined by int granularity """
839        values=ChapterField.get(self,instance)
840        if chap_nr-granularity<0:
841            return 0       
842        begin = values[:chap_nr-granularity] # minus the chunk before current chapter
843        moving_part = values[chap_nr-granularity:chap_nr] # the chunk before current chapter
844        end = values[chap_nr+granularity:]
845        chapter = values[chap_nr:chap_nr+granularity]
846        ObjectField.set(self, instance, begin+chapter+moving_part+end)
847       
848    def moveChapterDown(self, instance, chap_nr, granularity):
849        """ Move chapter chap_nr, chunk size is defined by int granularity """
850        values=ChapterField.get(self,instance)
851        if chap_nr+2*granularity>len(values):
852            return 0     
853        begin = values[:chap_nr]
854        chapter = values[chap_nr:chap_nr+granularity]
855        moving_part = values[chap_nr+granularity:chap_nr+granularity*2] # the chunk after current chapter
856        end = values[chap_nr+granularity*2:] # the rest after current chapter and chunk after it
857        ObjectField.set(self, instance, begin+moving_part+chapter+end)
858
859
860    def getLength(self, piece):
861        as_time=piece.getLength()
862        return {'hour':as_time/3600,'minute':(as_time % 3600)/60,'second':as_time % 60}           
863
864    def getSlidesAsTuples(self, instance):
865        """ Helper method for slideshows, returns (slide, caption) -tuples """
866        chaps=ChapterField.get(self,instance)
867        chapters=[]
868        slide='thisisamediapiecethisisamediapie'
869        for c in range(len(chaps)):
870            if c%2==0:
871                # this should be UID
872                if self.isUid(chaps[c]):
873                    slide=chaps[c]       
874            else:
875                # this should be caption
876                caption=chaps[c]
877                chapters.append((slide, caption))
878                # nice side-effect is that if presentation has more captions than working slides,
879                # latest working slide is shown
880        return chapters
881
882
883
884registerField(ChapterField,
885    title='Chapter field',
886    description=('Chapter field'),
887)
888
889
890class AudioField(FileField):
891    """ field for mp3:s """
892    _properties = FileField._properties.copy()
893    _properties.update({
894        'widget' : AudioWidget,
895    })
896
897    def getLength(self, instance):
898        i = aq_base(instance)
899        file = AudioField.get(self,instance)
900        if not shasattr(i, '_mp3length'):
901                instance._mp3length=get_length(str(file))
902        print instance._mp3length
903        if instance._mp3length==0:
904                instance._mp3length=get_length(str(file))
905        return {'hour':instance._mp3length/3600,'minute':(instance._mp3length % 3600)/60,'second':instance._mp3length % 60}
906           
907    def set(self, instance, value, **kwargs):
908        """ allow only audio here """
909        FileField.set(self, instance, value, **kwargs)
910        file=self.get(instance)
911        if self.getContentType(instance).startswith('audio/'):
912            instance._mp3length=get_length(str(file))
913        elif str(file)!='':
914            FileField.set(self, instance, "DELETE_FILE", **kwargs)
915           
916
917registerField(AudioField,
918    title='Audio field',
919    description=('Field that accepts only mp3:s'),
920)
Note: See TracBrowser for help on using the repository browser.