source: trunk/PILOTMaterial.py @ 3010

Revision 3010, 11.4 KB checked in by jukka, 10 years ago (diff)

PILOTs can now be embedded. Unified to use same portlet macro as embedding collections.

  • Property svn:keywords set to Id Revision
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-1
18
19from AccessControl import ClassSecurityInfo
20from Products.Archetypes.public import *
21from Products.CMFCore.utils import getToolByName
22from Schemata import material_schema, community_editing_schema, draft_schema
23from FieldsWidgets import ChapterField, LeTextAreaWidget, PilotWidget
24from config import PROJECTNAME, MODIFY_CONTENT, VIEW, to_unicode
25from Products.CMFPlone import PloneMessageFactory as PMF
26from Material import Material
27from Branchable import Branchable
28
29pilotmaterial_schema = Schema((
30    StringField('shortDescription',
31        index=None,
32        searchable=True,
33        schemata = 'default',
34        widget = LeTextAreaWidget(
35            rows = 3,
36            label = 'Short description',
37            label_msgid = 'label_short_description',
38            i18n_domain="lemill",
39        ),
40    ),
41    StringField('description',
42        index=None,
43        searchable=True,
44        schemata="metadata",
45        widget=LeTextAreaWidget(
46            rows = 8,
47            label="Full description",
48            label_msgid="label_full_description",
49            i18n_domain="lemill"
50        ),
51    ),
52    ChapterField('bodyText',
53        accessor = 'getBodyText',
54        edit_accessor = 'getRawBodyText',
55        schemata = 'scenes',
56        mutator = "setBodyText",
57        index='ZCTextIndex',
58        index_method = 'getOnlyText',
59        copied_in_translation=True,
60        searchable = True,
61        allowable_content_types = ['text/html',],
62        allow_file_upload = False,
63        default_output_type = 'text/x-html-captioned',
64        default_content_type = 'text/html',
65        default=[('', 'image_piece'),('','audio_piece'),(['','',''],'pilot_keywords'),('', 'image_piece'),('','audio_piece'),(['','','','','','',''],'pilot_questions'),],
66        deleteEmptyChapters = False,
67        widget=PilotWidget(label = "",
68            label_msgid = "",
69            description = "Here you can compose a flash movie from images and audio pieces.",
70            description_msgid = "help_pilot_body_text",
71            i18n_domain = "lemill",
72            ),
73    ),
74))
75
76schema = material_schema + community_editing_schema + draft_schema + pilotmaterial_schema
77
78schema = schema.copy()
79schema['description'].isMetadata = False
80schema['description'].schemata = 'default'
81schema.moveField('shortDescription', after='bodyText')
82schema.moveField('description', after='shortDescription')
83schema['title'].required = True
84schema.moveField('rights', pos='bottom')
85schema.moveField('hideDrafts', before='rights')
86schema.moveField('editingMode', before='hideDrafts')
87
88
89class PILOTMaterial(Branchable, Material):
90    """ PILOT Material """
91
92    schema = schema
93   
94    meta_type = 'PILOTMaterial'
95    archetype_name = 'PILOTMaterial'
96    default_location = 'content/pilots'
97   
98    security = ClassSecurityInfo()
99    security.declareObjectPublic()
100
101    security.declarePrivate('manage_afterAdd')
102    def manage_afterAdd(self, item, container):
103        Material.manage_afterAdd(self, item, container)
104   
105    def isEmbeddable(self):
106        """ PILOTs are embeddable """
107        return True
108
109    def getOnlyText(self):
110        """ bodyTexts index_method: goes through all text fields in body text and concatenates them to a mass of text suitable for indexing """
111        field=self.getField('bodyText')
112        values = field.get(self)
113        reslist=[]
114        if not values:
115            return ''
116        if type(values)==list:
117            if type(values[0])==tuple:       
118                for (chap, chaptype) in values:
119                    if chap==None:
120                        continue
121                    if type(chap)==list or type(chap)==tuple:
122                        chap=to_unicode('\n'.join(chap))
123                    reslist.append(chap)
124            else:
125                for chap in values:
126                    if chap==None:
127                        continue
128                    if type(chap)==list or type(chap)==tuple:
129                        chap=to_unicode('\n'.join(chap))
130                    reslist.append(chap)                               
131        else:
132            return values
133
134        return u'\n'.join(reslist)
135
136    def getQuestions(self):
137        """ Research questions are the last part of the bodytext, returned as a list """
138        for chapter, chapter_type  in self.getRawBodyText():
139            if chapter_type=='pilot_questions':
140                return chapter
141        else:
142            return ['']*7
143
144
145    def getEmbedCode(self):
146        """ Builds a nice embed code """
147        portal_url=getToolByName(self, 'portal_url')
148        return """<embed src="%s/player.swf" loop="false" quality="high" bgcolor="#000000" width="500" height="400" name="player" align="middle" allowScriptAccess="sameDomain" allowFullScreen="true" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="xml=%s/buildXML" />""" % (portal_url(), self.absolute_url())
149
150    def buildXML(self, only_validate=False):
151        """ builds the xml-blueprint from bodytext for flash engine """
152
153        def kw_cutter(k,maxlength):
154            """ method to add linebreaks to long keywords """
155            if len(k)>maxlength:
156                cutend=maxlength
157                cutbegin=0
158                resl=[]
159                while cutend<len(k):
160                    while k[cutend] not in ' +-':
161                        cutend=cutend-1
162                    resl.append(k[cutbegin:cutend])
163                    cutbegin=cutend
164                    cutend=cutend+maxlength
165                resl.append(k[cutbegin:])
166                k=' &amp;&amp;'.join(resl)
167            return k
168
169        bodyfield=self.getField('bodyText')
170        bodytext=self.getRawBodyText()
171        title=self.title
172        failure=''
173        def setFailure(new_failure, old_failure):
174            if (new_failure == 'fatal' and old_failure in ('soft', '')) or (new_failure == 'soft' and old_failure == ''):
175                return new_failure
176            return old_failure
177        voice_urls=[]
178        bg_url=[]
179        kw=[]
180        has_title=0
181        title_begin=''
182        maxlength=40 # how many characters of keyword fits on one line
183        msg=''
184        f=-1
185        ### Collect and validate values ###
186        for (chapter, chapter_type) in bodytext:
187            f+=1
188            if chapter_type == 'image_piece':
189                # backgrounds
190                obj=bodyfield.getObjectByUID(self, chapter)
191                url='missing'
192                if obj!=None:
193                    if obj.isImage():
194                        url='/'.join((obj.absolute_url(),'image_large'))
195                    else:
196                        msg=msg+'Background image %s is missing, using blank instead.' % str((f/3)+1)
197                        failure = setFailure('fatal', failure)
198                else:
199                    msg=msg+'Background image %s is missing, using blank instead.' % str((f/3)+1)                   
200                    failure = setFailure('fatal', failure)
201                bg_url.append(url)
202
203            elif chapter_type == 'audio_piece':
204                # voiceovers
205                obj=bodyfield.getObjectByUID(self, chapter)
206                url='missing'
207                length=30
208                if obj!=None:
209                    if obj.isAudio():
210                        url=obj.absolute_url()+'/file'
211                        length=obj.getLength()
212                    else:
213                        msg=msg+'Voiceover for scene %s is missing, using 15 seconds of ambient noise instead.' % str((f/3)+1)
214                        failure = setFailure('soft', failure)
215                else:
216                    msg=msg+'Voiceover for scene %s is missing, using 15 seconds of ambient noise instead.' % str((f/3)+1)                   
217                    failure = setFailure('soft', failure)
218                voice_urls.append((url,length))
219           
220            elif chapter_type == 'pilot_keywords' or chapter_type == 'pilot_questions':
221                # keywords
222                tkw=[to_unicode(x) for x in chapter]
223                if len(tkw)>3 and f<len(bodytext)-2:
224                    tkw=tkw[:3] # three keywords max unless final scene
225                if tkw==[]:
226                    tkw=['']
227                    msg=msg+'Keywords for scene %s are missing' % str((f/3)+1)
228                    failure = setFailure('soft', failure)
229                if voice_urls:                   
230                    mplength=voice_urls[-1][1] # length of latest voiceover
231                else:
232                    mplength=15
233                if f==2:
234                    has_title=1
235                cliplen = mplength/(len(tkw)+has_title)  # time reserved for each keyword
236                i=0
237                newlist=[]
238                if has_title==1:
239                    i=1
240                    title_begin='1'
241                    title_end=str(cliplen-2)
242                    has_title=0                   
243                for x in tkw:
244                    newlist.append((x, i*cliplen+1, (i+1)*cliplen-2))
245                    i=i+1
246                kw.append(newlist)
247                # cliplen = time allocated for each keyword
248                # kw[n]=(keyword, startpoint, endpoint)
249
250        if len(voice_urls)!=len(bg_url) or len(bg_url)!=len(kw):
251            failure = setFailure('fatal', failure)
252        if title_begin=='':
253            msg=msg+"Scene doesn't have enough components, keywords are missing."
254            failure = setFailure('fatal', failure)
255
256        if only_validate==True:
257            return (failure,
258                bg_url,
259                [x[0] for x in voice_urls],
260                [x[0][0] for x in kw])
261
262        if failure=='fatal':
263            print 'fatal failure'
264            print msg
265            lt = getToolByName(self, 'lemill_tool')
266            lt.addPortalMessage(_('Building flash file for PILOT failed. '))
267            return ''
268
269        ### Build xml ###
270
271        scene_type=' type="title"'
272        xml=u'<?xml version="1.0" encoding="utf-8"?>\n<pilot>\n'
273        xml=xml+u'<title begin="'+title_begin+u'" end="'+title_end+u'">'+title+u'</title>\n'
274        for i in range(len(bg_url)):
275            xml=xml+u'<scene'+scene_type+u'>\n'
276            xml=xml+u'   <voiceover src="'+voice_urls[i][0]+u'" len="'+str(voice_urls[i][1])+u'"/>\n'
277            xml=xml+u'   <backgroundimage src="'+bg_url[i]+'" />\n'
278            for k in range(len(kw[i])):           
279                xml=xml+u'   <keyword begin="'+str(kw[i][k][1])+u'" end="'+str(kw[i][k][2])+u'">'+kw[i][k][0]+u'</keyword>\n'
280            xml=xml+u'</scene> \n'
281            if i < len(bg_url)-2:
282                scene_type=''
283            else:
284                scene_type=u' type="questions"'
285        xml=xml+u'</pilot>'
286        # We use UTF-16 because that way we get the BOM, which Flash XML parser requires.
287        return xml.encode('utf-16')
288
289
290registerType(PILOTMaterial, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.