| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
from AccessControl import ClassSecurityInfo, getSecurityManager |
|---|
| 21 |
from Products.Archetypes.public import * |
|---|
| 22 |
from Products.CMFCore.utils import getToolByName |
|---|
| 23 |
from config import PROJECTNAME,SLIDESHARE_API_KEY,SLIDESHARE_SECRET_KEY |
|---|
| 24 |
from FieldsWidgets import AudioField, ChapterField, AudioWidget, LeTextAreaWidget, SlideWidget, EmbedWidget |
|---|
| 25 |
from Schemata import material_schema, community_editing_schema, draft_schema |
|---|
| 26 |
from Material import Material |
|---|
| 27 |
from types import ListType |
|---|
| 28 |
from permissions import MODIFY_CONTENT |
|---|
| 29 |
from persistent.mapping import PersistentMapping |
|---|
| 30 |
from Products.LeMill import LeMillMessageFactory as _ |
|---|
| 31 |
from Products.CMFPlone.PloneBatch import Batch |
|---|
| 32 |
|
|---|
| 33 |
from xml.dom.minidom import parse |
|---|
| 34 |
import urllib, urllib2 |
|---|
| 35 |
import time, sha, calendar |
|---|
| 36 |
import DateTime |
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 |
presentation_schema= Schema(( |
|---|
| 40 |
AudioField('audio', |
|---|
| 41 |
accessor='getAudioFile', |
|---|
| 42 |
mutator='setAudioFile', |
|---|
| 43 |
widget=AudioWidget(label="Audio recording", |
|---|
| 44 |
label_msgid="label_audio_recording", |
|---|
| 45 |
description="An audio recording of the presentation", |
|---|
| 46 |
description_msgid="help_presentation_audio_recording", |
|---|
| 47 |
i18n_domain="lemill", |
|---|
| 48 |
visible ={'edit':'invisible', 'view':'visible'} |
|---|
| 49 |
), |
|---|
| 50 |
), |
|---|
| 51 |
StringField('description', |
|---|
| 52 |
accessor="getDescription", |
|---|
| 53 |
mutator = "setDescription", |
|---|
| 54 |
index='ZCTextIndex', |
|---|
| 55 |
schemata='default', |
|---|
| 56 |
searchable = True, |
|---|
| 57 |
widget=LeTextAreaWidget(label = "About the presentation", |
|---|
| 58 |
label_msgid = "label_presentation_about", |
|---|
| 59 |
description = "General information about the presentation such as the data, event and location.", |
|---|
| 60 |
description_msgid = "help_presentation_about", |
|---|
| 61 |
rows = 4, |
|---|
| 62 |
i18n_domain = "lemill", |
|---|
| 63 |
visible ={'edit':'visible', 'view':'visible'} |
|---|
| 64 |
), |
|---|
| 65 |
), |
|---|
| 66 |
ChapterField('bodyText', |
|---|
| 67 |
accessor = 'getBodyText', |
|---|
| 68 |
edit_accessor = 'getRawBodyText', |
|---|
| 69 |
mutator = "setBodyText", |
|---|
| 70 |
copied_in_translation=True, |
|---|
| 71 |
index='ZCTextIndex', |
|---|
| 72 |
index_method = 'getOnlyText', |
|---|
| 73 |
searchable = True, |
|---|
| 74 |
allowable_content_types = ['text/html',], |
|---|
| 75 |
allow_file_upload = False, |
|---|
| 76 |
default_output_type = 'text/x-html-captioned', |
|---|
| 77 |
default_content_type = 'text/html', |
|---|
| 78 |
default=[], |
|---|
| 79 |
deleteEmptyChapters = False, |
|---|
| 80 |
widget=SlideWidget(label = "Slides and captions", |
|---|
| 81 |
label_msgid = "label_slides_and_captions", |
|---|
| 82 |
i18n_domain = "lemill", |
|---|
| 83 |
), |
|---|
| 84 |
), |
|---|
| 85 |
StringField('ss_id', |
|---|
| 86 |
searchable = False, |
|---|
| 87 |
widget = StringWidget( |
|---|
| 88 |
visible = {'view':'invisible', 'edit':'invisible'}, |
|---|
| 89 |
), |
|---|
| 90 |
), |
|---|
| 91 |
StringField('permalink', |
|---|
| 92 |
searchable = False, |
|---|
| 93 |
widget = StringWidget( |
|---|
| 94 |
visible = {'view':'visible', 'edit':'invisible'}, |
|---|
| 95 |
), |
|---|
| 96 |
), |
|---|
| 97 |
StringField('thumbnail', |
|---|
| 98 |
searchable = False, |
|---|
| 99 |
widget = StringWidget( |
|---|
| 100 |
visible = {'view':'invisible', 'edit':'invisible'}, |
|---|
| 101 |
), |
|---|
| 102 |
), |
|---|
| 103 |
StringField('embedCode', |
|---|
| 104 |
searchable = False, |
|---|
| 105 |
widget = EmbedWidget( |
|---|
| 106 |
visible = {'view':'visible', 'edit':'visible'}, |
|---|
| 107 |
), |
|---|
| 108 |
), |
|---|
| 109 |
|
|---|
| 110 |
)) |
|---|
| 111 |
|
|---|
| 112 |
schema = material_schema + community_editing_schema + presentation_schema |
|---|
| 113 |
|
|---|
| 114 |
schema = schema.copy() |
|---|
| 115 |
schema.moveField('rights', pos='bottom') |
|---|
| 116 |
schema['title'].required = True |
|---|
| 117 |
schema['tags'].schemata='default' |
|---|
| 118 |
schema.moveField('embedCode', before='description') |
|---|
| 119 |
|
|---|
| 120 |
SIZES = (('small', (120, 120)), ('edit', (500, 500)), ('large', (700, 525))) |
|---|
| 121 |
|
|---|
| 122 |
class PresentationMaterial(Material): |
|---|
| 123 |
""" Presentation """ |
|---|
| 124 |
|
|---|
| 125 |
schema = schema |
|---|
| 126 |
|
|---|
| 127 |
meta_type = "PresentationMaterial" |
|---|
| 128 |
archetype_name = "PresentationMaterial" |
|---|
| 129 |
default_location = 'content/presentations' |
|---|
| 130 |
|
|---|
| 131 |
security = ClassSecurityInfo() |
|---|
| 132 |
security.declareObjectPublic() |
|---|
| 133 |
|
|---|
| 134 |
aliases = { |
|---|
| 135 |
'(Default)' : 'slideshow_view', |
|---|
| 136 |
'view' : 'base_view', |
|---|
| 137 |
'edit' : 'base_edit' |
|---|
| 138 |
} |
|---|
| 139 |
|
|---|
| 140 |
def manage_afterAdd(self, item, container): |
|---|
| 141 |
if not hasattr(self, 'temp_data'): |
|---|
| 142 |
print 'no temp_data, creating empty dict' |
|---|
| 143 |
self.temp_data = {} |
|---|
| 144 |
Material.manage_afterAdd(self, item, container) |
|---|
| 145 |
|
|---|
| 146 |
def hasComplexWorkflow(self): |
|---|
| 147 |
""" Can have drafts or versions """ |
|---|
| 148 |
return False |
|---|
| 149 |
|
|---|
| 150 |
def hasEditableCoverImage(self): |
|---|
| 151 |
""" Cover Image is not created automatically """ |
|---|
| 152 |
return False |
|---|
| 153 |
|
|---|
| 154 |
def getOnlyText(self): |
|---|
| 155 |
field=self.getField('bodyText') |
|---|
| 156 |
values = field.get(self) |
|---|
| 157 |
if not values: |
|---|
| 158 |
return '' |
|---|
| 159 |
if type(values[0])==tuple: |
|---|
| 160 |
dump= '\n'.join([x[0] for x in values if x[1]=='caption']) |
|---|
| 161 |
else: |
|---|
| 162 |
dump= '\n'.join([x['text'] for x in values if x['text']]) |
|---|
| 163 |
if dump.isspace(): |
|---|
| 164 |
return '' |
|---|
| 165 |
else: |
|---|
| 166 |
return dump |
|---|
| 167 |
|
|---|
| 168 |
def getOnlyRawText(self): |
|---|
| 169 |
field=self.getField('bodyText') |
|---|
| 170 |
values = field.getRaw(self) |
|---|
| 171 |
if values: |
|---|
| 172 |
return '\n'.join([x[0] for x in values if x[1]=='caption']) |
|---|
| 173 |
return '' |
|---|
| 174 |
|
|---|
| 175 |
def getSlidesAsTuples(self): |
|---|
| 176 |
""" Helper method for slideshows, returns (slide_src, thumb_src , caption) -tuples """ |
|---|
| 177 |
portal_url = getToolByName(self, 'portal_url') |
|---|
| 178 |
thelist=[] |
|---|
| 179 |
caption=slide_src=thumb_src='' |
|---|
| 180 |
for (chapter,chapter_type) in self.getRawBodyText(): |
|---|
| 181 |
if chapter_type=='caption': |
|---|
| 182 |
thelist.append((slide_src, thumb_src, chapter)) |
|---|
| 183 |
elif chapter_type=='image_piece' and self.isUid(chapter): |
|---|
| 184 |
piece=self.getObjectByUID(chapter) |
|---|
| 185 |
if piece and hasattr(piece, 'image_large') and hasattr(piece, 'image_small'): |
|---|
| 186 |
slide_src='%s/image_large' % piece.absolute_url() |
|---|
| 187 |
thumb_src='%s/image_small' % piece.absolute_url() |
|---|
| 188 |
else: |
|---|
| 189 |
slide_src='' |
|---|
| 190 |
thumb_src='' |
|---|
| 191 |
else: |
|---|
| 192 |
slide_src='' |
|---|
| 193 |
thumb_src='' |
|---|
| 194 |
return thelist |
|---|
| 195 |
|
|---|
| 196 |
def deleteResource(self, reason=''): |
|---|
| 197 |
pieces_to_delete = [] |
|---|
| 198 |
for chapter in self.getRawBodyText(): |
|---|
| 199 |
if chapter['type']=='image_piece': |
|---|
| 200 |
piece=self.getObjectByUID(chapter['uid']) |
|---|
| 201 |
if piece and len(piece.getResourcesUsingPiece())==1: |
|---|
| 202 |
pieces_to_delete.append(piece) |
|---|
| 203 |
Material.deleteResource(self) |
|---|
| 204 |
for piece in pieces_to_delete: |
|---|
| 205 |
piece.deleteResource() |
|---|
| 206 |
|
|---|
| 207 |
|
|---|
| 208 |
|
|---|
| 209 |
security.declareProtected(MODIFY_CONTENT, 'fetchAllPresentations') |
|---|
| 210 |
def fetchAllPresentations(self): |
|---|
| 211 |
""" dictionary of SlideSharePresentations for current users """ |
|---|
| 212 |
lutool=getToolByName(self, 'lemill_usertool') |
|---|
| 213 |
ss_user=lutool.getMemberFolder().getSlideshare_username() |
|---|
| 214 |
if not ss_user: |
|---|
| 215 |
return {} |
|---|
| 216 |
data=self.getSlideshowsByUser(username_for=ss_user) |
|---|
| 217 |
self.temp_data=self.parseSlideshareXML(data) |
|---|
| 218 |
|
|---|
| 219 |
def getBatchOfPresentations(self,b_start=0, b_size=10): |
|---|
| 220 |
""" Returns importable presentations in sorted and batched sets """ |
|---|
| 221 |
if not getattr(self, 'temp_data', {}): |
|---|
| 222 |
self.fetchAllPresentations() |
|---|
| 223 |
list_of_p=[(x['date'], x) for x in self.temp_data.values()] |
|---|
| 224 |
list_of_p.sort(reverse=True) |
|---|
| 225 |
batchbase=[value for (date, value) in list_of_p] |
|---|
| 226 |
batch=Batch(batchbase, b_size, int(b_start), orphan=0) |
|---|
| 227 |
return batch |
|---|
| 228 |
|
|---|
| 229 |
def uploadedPresentations(self): |
|---|
| 230 |
""" Returns a list of slideshare ids that the user has already imported """ |
|---|
| 231 |
return [] |
|---|
| 232 |
|
|---|
| 233 |
security.declareProtected(MODIFY_CONTENT, 'injectPresentationFromSlideShare') |
|---|
| 234 |
def injectPresentationFromSlideShare(self): |
|---|
| 235 |
""" modifies the current presentation with data from slideshare """ |
|---|
| 236 |
REQUEST=self.REQUEST |
|---|
| 237 |
key=REQUEST.form.get('slideshare_id','') |
|---|
| 238 |
cancel=REQUEST.form.get('form.button.cancel','') |
|---|
| 239 |
if cancel: |
|---|
| 240 |
if hasattr(self, 'temp_data'): |
|---|
| 241 |
del self.temp_data |
|---|
| 242 |
portal_url=getToolByName(self,'portal_url') |
|---|
| 243 |
lt=getToolByName(self,'lemill_tool') |
|---|
| 244 |
lt.addPortalMessage('Resource cancelled.') |
|---|
| 245 |
self.aq_parent.manage_delObjects([self.id]) |
|---|
| 246 |
return REQUEST.RESPONSE.redirect(portal_url()+'/content') |
|---|
| 247 |
if not key: |
|---|
| 248 |
return REQUEST.RESPONSE.redirect(self.absolute_url()+'/import_presentation') |
|---|
| 249 |
if not hasattr(self, 'temp_data'): |
|---|
| 250 |
lutool=getToolByName(self, 'lemill_usertool') |
|---|
| 251 |
ss_user=lutool.getMemberFolder().getSlideshare_username() |
|---|
| 252 |
self.temp_data=self.parseSlideshareXML(self.getSlideshowsByUser(ss_user)) |
|---|
| 253 |
data=self.temp_data.get(key, None) |
|---|
| 254 |
if not data: |
|---|
| 255 |
raise 'SlideShare id %s not found' % str(key) |
|---|
| 256 |
return REQUEST.RESPONSE.redirect(self.absolute_url()+'/import_presentation') |
|---|
| 257 |
self.edit(**data) |
|---|
| 258 |
|
|---|
| 259 |
this_slideshow=self.parseSlideshareXML(self.getSlideshowById(slideshow_id=key)) |
|---|
| 260 |
if this_slideshow: |
|---|
| 261 |
self.setEmbedCode(this_slideshow[key]['embedCode']) |
|---|
| 262 |
thumbnail=self.getThumbnail() |
|---|
| 263 |
if thumbnail: |
|---|
| 264 |
coverImageFile=urllib2.urlopen(thumbnail).read() |
|---|
| 265 |
if coverImageFile: |
|---|
| 266 |
self.setCoverImage(coverImageFile) |
|---|
| 267 |
self.publish() |
|---|
| 268 |
self.at_post_edit_script() |
|---|
| 269 |
del self.temp_data |
|---|
| 270 |
return REQUEST.RESPONSE.redirect(self.absolute_url()+'/base_edit') |
|---|
| 271 |
|
|---|
| 272 |
security.declarePrivate('getSlideshowsByUser') |
|---|
| 273 |
def getSlideshowsByUser(self, username_for=None): |
|---|
| 274 |
""" Fetch XML-data from SlideShare """ |
|---|
| 275 |
print '*** Fetching from SlideShare ***' |
|---|
| 276 |
ts = int(time.time()) |
|---|
| 277 |
params = {'api_key' : SLIDESHARE_API_KEY,'ts' : ts, 'hash' : sha.new(SLIDESHARE_SECRET_KEY + str(ts)).hexdigest(), 'username_for':username_for} |
|---|
| 278 |
params = urllib.urlencode(params) |
|---|
| 279 |
data=urllib2.urlopen('http://www.slideshare.net/api/1/get_slideshow_by_user', params) |
|---|
| 280 |
return data |
|---|
| 281 |
|
|---|
| 282 |
security.declarePrivate('getSlideshowById') |
|---|
| 283 |
def getSlideshowById(self, slideshow_id=None, slideshow_url=None): |
|---|
| 284 |
""" Fetch XML-data from SlideShare """ |
|---|
| 285 |
ts = int(time.time()) |
|---|
| 286 |
params = {'api_key' : SLIDESHARE_API_KEY,'ts' : ts, 'hash' : sha.new(SLIDESHARE_SECRET_KEY + str(ts)).hexdigest(), 'slideshow_id':slideshow_id, 'slideshow_url':slideshow_url or 'blank'} |
|---|
| 287 |
params = urllib.urlencode(params) |
|---|
| 288 |
data=urllib2.urlopen('http://www.slideshare.net/api/2/get_slideshow', params) |
|---|
| 289 |
return data |
|---|
| 290 |
|
|---|
| 291 |
def parseSlideshareXML(self, xml_data): |
|---|
| 292 |
""" builds a dict of useful LeMill data from SlideShare's XML """ |
|---|
| 293 |
def getTag(dom, tag): |
|---|
| 294 |
content=dom.getElementsByTagName(tag) |
|---|
| 295 |
if content: |
|---|
| 296 |
content=content[0].childNodes |
|---|
| 297 |
for node in content: |
|---|
| 298 |
if node.nodeType==node.TEXT_NODE: |
|---|
| 299 |
return node.data |
|---|
| 300 |
return '' |
|---|
| 301 |
dom=parse(xml_data) |
|---|
| 302 |
slideshows=dom.getElementsByTagName("Slideshow") |
|---|
| 303 |
dict={} |
|---|
| 304 |
for ss in slideshows: |
|---|
| 305 |
obj={} |
|---|
| 306 |
id=getTag(ss, 'ID') |
|---|
| 307 |
obj['ss_id']=id |
|---|
| 308 |
obj['title']=getTag(ss, 'Title') |
|---|
| 309 |
obj['description']=getTag(ss, 'Description') |
|---|
| 310 |
date=getTag(ss, 'Created') |
|---|
| 311 |
if date: |
|---|
| 312 |
try: |
|---|
| 313 |
date=time.strptime(date, '%a %b %d %H:%M:%S %Z %Y') |
|---|
| 314 |
date=calendar.timegm(date) |
|---|
| 315 |
date=DateTime.DateTime(date) |
|---|
| 316 |
except ValueError: |
|---|
| 317 |
date=DateTime.DateTime() |
|---|
| 318 |
obj['date']=date |
|---|
| 319 |
tags=getTag(ss, 'Tags') |
|---|
| 320 |
if tags: |
|---|
| 321 |
tags=', '.join(tags.split(' ')) |
|---|
| 322 |
obj['tags']=tags |
|---|
| 323 |
obj['permalink']=getTag(ss, 'Permalink') |
|---|
| 324 |
obj['thumbnail']=getTag(ss, 'Thumbnail') |
|---|
| 325 |
obj['embedCode']=getTag(ss,'Embed') or getTag(ss, 'EmbedCode') |
|---|
| 326 |
dict[id]=obj |
|---|
| 327 |
dom.unlink() |
|---|
| 328 |
return dict |
|---|
| 329 |
|
|---|
| 330 |
|
|---|
| 331 |
registerType(PresentationMaterial, PROJECTNAME) |
|---|