source: trunk/PILOTMaterial.py @ 1960

Revision 1960, 10.3 KB checked in by gabor, 12 years ago (diff)

fixed #1432 spent 2h

  • 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
22
23from SharedMetadata import *
24from FieldsWidgets import *
25from config import PROJECTNAME, MODIFY_CONTENT, VIEW, to_unicode
26from Products.CMFPlone import PloneMessageFactory as PMF
27from Material import Material
28
29short_description = Schema((
30    StringField('shortDescription',
31        index="ZCTextIndex:schema",
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))
42
43full_description = Schema((
44    StringField('description',
45        index="ZCTextIndex:schema",
46        searchable=True,
47        schemata="metadata",
48        widget=LeTextAreaWidget(
49            rows = 8,
50            label="Full description",
51            label_msgid="label_full_description",
52            i18n_domain="lemill"),
53    )
54))
55
56bodyText = Schema((
57    ChapterField('bodyText',
58        accessor = 'getBodyText',
59        edit_accessor = 'getRawBodyText',
60        schemata = 'scenes',
61        mutator = "setBodyText",
62        index='ZCTextIndex:schema',
63        index_method = 'getOnlyText',
64        searchable = True,
65        allowable_content_types = ['text/html',],
66        allow_file_upload = False,
67        default_output_type = 'text/x-html-captioned',
68        default_content_type = 'text/html',
69        default=['thisisamediapiecethisisamediapie','thisisaudiopiecethisisaudiopiece',['','',''], 'thisisamediapiecethisisamediapie','thisisaudiopiecethisisaudiopiece',['','','','','','','']],
70        deleteEmptyChapters = False,
71        widget=PilotWidget(label = "",
72            label_msgid = "",
73            description = "Here you can compose a flash movie from images and audio pieces.",
74            description_msgid = "help_pilot_body_text",
75            i18n_domain = "lemill",
76            ),
77    ),
78))
79
80schema = BaseSchema + short_description + full_description + tags + group_sharing + coverImage + lemill_metadata_mods + language_schema + license_schema + author_schema + subject_area_schema + target_group_schema + deletionReason + version_schema + translation_schema + bodyText + ieee_lom_lre_ap + latest_edit_schema + score
81
82schema = schema.copy()
83schema['description'].isMetadata = False
84schema['description'].schemata = 'default'
85schema.moveField('shortDescription', after='bodyText')
86schema.moveField('description', after='shortDescription')
87schema['title'].required = True
88schema.moveField('rights', pos='bottom')
89
90
91class PILOTMaterial(Material):
92    """ PILOT Material """
93
94    schema = schema
95   
96    meta_type = 'PILOTMaterial'
97    archetype_name = 'PILOTMaterial'
98
99    security = ClassSecurityInfo()
100    security.declareObjectPublic()
101
102    security.declarePrivate('manage_afterAdd')
103    def manage_afterAdd(self, item, container):
104        Material.manage_afterAdd(self, item, container)
105        if not hasattr(item.aq_base, 'left_slots'):
106            self._setProperty('left_slots', ['here/portlet_material_actions/macros/portlet',], 'lines')
107        else:
108            self._updateProperty('left_slots', ['here/portlet_material_actions/macros/portlet',])
109
110    def getOnlyText(self):
111        """ bodyTexts index_method: goes through all text fields in body text and concatenates them to a mass of text suitable for indexing """
112        field=self.getField('bodyText')
113        values = field.get(self)
114        reslist=[]
115        for chap in values:
116            if chap==None:
117                continue
118            if type(chap)==list or type(chap)==tuple:
119                chap=to_unicode('\n'.join(chap))
120            reslist.append(chap)
121        return u'\n'.join(reslist)
122
123    def buildXML(self, only_validate=False):
124        """ builds the xml-blueprint from bodytext for flash engine """
125
126        def kw_cutter(k,maxlength):
127            """ method to add linebreaks to long keywords """
128            if len(k)>maxlength:
129                cutend=maxlength
130                cutbegin=0
131                resl=[]
132                while cutend<len(k):
133                    while k[cutend] not in ' +-':
134                        cutend=cutend-1
135                    resl.append(k[cutbegin:cutend])
136                    cutbegin=cutend
137                    cutend=cutend+maxlength
138                resl.append(k[cutbegin:])
139                k=' &amp;&amp;'.join(resl)
140            return k
141
142        plone_utils=getToolByName(self, 'plone_utils')
143
144        bodyfield=self.getField('bodyText')
145        bodytext=self.getBodyText()
146        title=self.title
147        failure=''
148        def setFailure(new_failure, old_failure):
149            if (new_failure == 'fatal' and old_failure in ('soft', '')) or (new_failure == 'soft' and old_failure == ''):
150                return new_failure
151            return old_failure
152        vc_url=[]
153        bg_url=[]
154        kw=[]
155        has_title=0
156        title_begin=''
157        maxlength=40 # how many characters of keyword fits on one line
158        msg=''
159        ### Collect and validate values ###
160        for f in range(len(bodytext)):
161            if f % 3 == 0:
162                # backgrounds
163                uid=bodytext[f]
164                obj=bodyfield.getObjectByUID(self, uid)
165                url='missing'
166                if obj!=None:
167                    if obj.isImage():
168                        url='/'.join((obj.absolute_url(),obj.getJpegVarName()))
169                    else:
170                        msg=msg+'Background image %s is missing, using blank instead.' % str((f/3)+1)
171                        failure = setFailure('fatal', failure)
172                else:
173                    msg=msg+'Background image %s is missing, using blank instead.' % str((f/3)+1)                   
174                    failure = setFailure('fatal', failure)
175                bg_url.append(url)
176
177            elif f % 3 == 1:
178                # voiceovers
179                uid=bodytext[f]
180                obj=bodyfield.getObjectByUID(self, uid)
181                url='missing'
182                length=30
183                if obj!=None:
184                    if obj.isAudio():
185                        url=obj.absolute_url()+'/file'
186                        length=obj.getLength()
187                    else:
188                        msg=msg+'Voiceover for scene %s is missing, using 15 seconds of ambient noise instead.' % str((f/3)+1)
189                        failure = setFailure('soft', failure)
190                else:
191                    msg=msg+'Voiceover for scene %s is missing, using 15 seconds of ambient noise instead.' % str((f/3)+1)                   
192                    failure = setFailure('soft', failure)
193                vc_url.append((url,length))
194           
195            elif f % 3 == 2:
196                # keywords
197                tkw=[to_unicode(x) for x in bodytext[f] if x!='']
198                if len(tkw)>3 and f<len(bodytext)-2:
199                    tkw=tkw[:3] # three keywords max unless final scene
200                if tkw==[]:
201                    tkw=['']
202                    msg=msg+'Keywords for scene %s are missing' % str((f/3)+1)
203                    failure = setFailure('soft', failure)
204                mplength=vc_url[-1][1] # length of latest voiceover
205                if f==2:
206                    has_title=1
207                cliplen = mplength/(len(tkw)+has_title)  # time reserved for each keyword
208                i=0
209                newlist=[]
210                if has_title==1:
211                    i=1
212                    title_begin='1'
213                    title_end=str(cliplen-2)
214                    has_title=0                   
215                for x in tkw:
216                    if len(x)>maxlength:
217                        x=kw_cutter(x,maxlength)
218                    newlist.append((x, i*cliplen+1, (i+1)*cliplen-2))
219                    i=i+1
220                kw.append(newlist)
221                # cliplen = time allocated for each keyword
222                # kw[n]=(keyword, startpoint, endpoint)
223
224        if len(vc_url)!=len(bg_url) or len(bg_url)!=len(kw):
225            failure = setFailure('fatal', failure)
226        if title_begin=='':
227            msg=msg+"Scene doesn't have enough components, keywords are missing."
228            failure = setFailure('fatal', failure)
229
230        if only_validate==True:
231            return (failure,
232                bg_url,
233                [x[0] for x in vc_url],
234                [x[0][0] for x in kw])
235
236        if failure=='fatal':
237            print 'fatal failure'
238            print msg
239            plone_utils.addPortalMessage(PMF(u'Building flash failed. PILOT'))
240            #plone_utils.addPortalMessage(PMF(msg))
241            return ''
242
243        ### Build xml ###
244
245        scene_type=' type="title"'
246        xml=u'<?xml version="1.0" encoding="utf-8"?>\n<pilot>\n'
247        xml=xml+u'<title begin="'+title_begin+u'" end="'+title_end+u'">'+title+u'</title>\n'
248        for i in range(len(bg_url)):
249            xml=xml+u'<scene'+scene_type+u'>\n'
250            xml=xml+u'   <voiceover src="'+vc_url[i][0]+u'" len="'+str(vc_url[i][1])+u'"/>\n'
251            xml=xml+u'   <backgroundimage src="'+bg_url[i]+'" />\n'
252            for k in range(len(kw[i])):           
253                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'
254            xml=xml+u'</scene> \n'
255            if i < len(bg_url)-2:
256                scene_type=''
257            else:
258                scene_type=u' type="questions"'
259        xml=xml+u'</pilot>'
260        # We use UTF-16 because that way we get the BOM, which Flash XML parser requires.
261        return xml.encode('utf-16')
262
263
264registerType(PILOTMaterial, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.