source: trunk/PILOTMaterial.py @ 2417

Revision 2410, 10.8 KB checked in by anonymous, 11 years ago (diff)

Portlets are much more efficient and simpler now and work independent of Plone's portlet system. The logic is in Resources/getPortletDetails, portlets itself just either show or not show their parts.

  • 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=[('', 'image_piece'),('','audio_piece'),(['','',''],'pilot_keywords'),('', 'image_piece'),('','audio_piece'),(['','','','','','',''],'pilot_questions'),],
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 + latest_edit_schema + score + state
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
106    def getOnlyText(self):
107        """ bodyTexts index_method: goes through all text fields in body text and concatenates them to a mass of text suitable for indexing """
108        field=self.getField('bodyText')
109        values = field.get(self)
110        reslist=[]
111        if not values:
112            return ''
113        if type(values)==list:
114            if type(values[0])==tuple:       
115                for (chap, chaptype) 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            else:
122                for chap in values:
123                    if chap==None:
124                        continue
125                    if type(chap)==list or type(chap)==tuple:
126                        chap=to_unicode('\n'.join(chap))
127                    reslist.append(chap)                               
128        else:
129            return values
130
131        return u'\n'.join(reslist)
132
133    def getQuestions(self):
134        """ Research questions are the last part of the bodytext, returned as a list """
135        questions = self.getRawBodyText()
136        questions = questions[-1][0]
137        return questions
138
139
140    def buildXML(self, only_validate=False):
141        """ builds the xml-blueprint from bodytext for flash engine """
142
143        def kw_cutter(k,maxlength):
144            """ method to add linebreaks to long keywords """
145            if len(k)>maxlength:
146                cutend=maxlength
147                cutbegin=0
148                resl=[]
149                while cutend<len(k):
150                    while k[cutend] not in ' +-':
151                        cutend=cutend-1
152                    resl.append(k[cutbegin:cutend])
153                    cutbegin=cutend
154                    cutend=cutend+maxlength
155                resl.append(k[cutbegin:])
156                k=' &amp;&amp;'.join(resl)
157            return k
158
159        plone_utils=getToolByName(self, 'plone_utils')
160
161        bodyfield=self.getField('bodyText')
162        bodytext=self.getRawBodyText()
163        title=self.title
164        failure=''
165        def setFailure(new_failure, old_failure):
166            if (new_failure == 'fatal' and old_failure in ('soft', '')) or (new_failure == 'soft' and old_failure == ''):
167                return new_failure
168            return old_failure
169        voice_urls=[]
170        bg_url=[]
171        kw=[]
172        has_title=0
173        title_begin=''
174        maxlength=40 # how many characters of keyword fits on one line
175        msg=''
176        f=-1
177        ### Collect and validate values ###
178        for (chapter, chapter_type) in bodytext:
179            f+=1
180            if chapter_type == 'image_piece':
181                # backgrounds
182                obj=bodyfield.getObjectByUID(self, chapter)
183                url='missing'
184                if obj!=None:
185                    if obj.isImage():
186                        url='/'.join((obj.absolute_url(),obj.getJpegVarName()))
187                    else:
188                        msg=msg+'Background image %s is missing, using blank instead.' % str((f/3)+1)
189                        failure = setFailure('fatal', failure)
190                else:
191                    msg=msg+'Background image %s is missing, using blank instead.' % str((f/3)+1)                   
192                    failure = setFailure('fatal', failure)
193                bg_url.append(url)
194
195            elif chapter_type == 'audio_piece':
196                # voiceovers
197                obj=bodyfield.getObjectByUID(self, chapter)
198                url='missing'
199                length=30
200                if obj!=None:
201                    if obj.isAudio():
202                        url=obj.absolute_url()+'/file'
203                        length=obj.getLength()
204                    else:
205                        msg=msg+'Voiceover for scene %s is missing, using 15 seconds of ambient noise instead.' % str((f/3)+1)
206                        failure = setFailure('soft', failure)
207                else:
208                    msg=msg+'Voiceover for scene %s is missing, using 15 seconds of ambient noise instead.' % str((f/3)+1)                   
209                    failure = setFailure('soft', failure)
210                voice_urls.append((url,length))
211           
212            elif chapter_type == 'pilot_keywords' or chapter_type == 'pilot_questions':
213                # keywords
214                tkw=[to_unicode(x) for x in chapter]
215                if len(tkw)>3 and f<len(bodytext)-2:
216                    tkw=tkw[:3] # three keywords max unless final scene
217                if tkw==[]:
218                    tkw=['']
219                    msg=msg+'Keywords for scene %s are missing' % str((f/3)+1)
220                    failure = setFailure('soft', failure)
221                mplength=voice_urls[-1][1] # length of latest voiceover
222                if f==2:
223                    has_title=1
224                cliplen = mplength/(len(tkw)+has_title)  # time reserved for each keyword
225                i=0
226                newlist=[]
227                if has_title==1:
228                    i=1
229                    title_begin='1'
230                    title_end=str(cliplen-2)
231                    has_title=0                   
232                for x in tkw:
233                    if len(x)>maxlength:
234                        x=kw_cutter(x,maxlength)
235                    newlist.append((x, i*cliplen+1, (i+1)*cliplen-2))
236                    i=i+1
237                kw.append(newlist)
238                # cliplen = time allocated for each keyword
239                # kw[n]=(keyword, startpoint, endpoint)
240
241        if len(voice_urls)!=len(bg_url) or len(bg_url)!=len(kw):
242            failure = setFailure('fatal', failure)
243        if title_begin=='':
244            msg=msg+"Scene doesn't have enough components, keywords are missing."
245            failure = setFailure('fatal', failure)
246
247        if only_validate==True:
248            return (failure,
249                bg_url,
250                [x[0] for x in voice_urls],
251                [x[0][0] for x in kw])
252
253        if failure=='fatal':
254            print 'fatal failure'
255            print msg
256            plone_utils.addPortalMessage(PMF(u'Building flash failed. PILOT'))
257            #plone_utils.addPortalMessage(PMF(msg))
258            return ''
259
260        ### Build xml ###
261
262        scene_type=' type="title"'
263        xml=u'<?xml version="1.0" encoding="utf-8"?>\n<pilot>\n'
264        xml=xml+u'<title begin="'+title_begin+u'" end="'+title_end+u'">'+title+u'</title>\n'
265        for i in range(len(bg_url)):
266            xml=xml+u'<scene'+scene_type+u'>\n'
267            xml=xml+u'   <voiceover src="'+voice_urls[i][0]+u'" len="'+str(voice_urls[i][1])+u'"/>\n'
268            xml=xml+u'   <backgroundimage src="'+bg_url[i]+'" />\n'
269            for k in range(len(kw[i])):           
270                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'
271            xml=xml+u'</scene> \n'
272            if i < len(bg_url)-2:
273                scene_type=''
274            else:
275                scene_type=u' type="questions"'
276        xml=xml+u'</pilot>'
277        # We use UTF-16 because that way we get the BOM, which Flash XML parser requires.
278        return xml.encode('utf-16')
279
280
281registerType(PILOTMaterial, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.