| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
from Products.Archetypes.public import * |
|---|
| 20 |
from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget |
|---|
| 21 |
from Products.CMFCore.permissions import ModifyPortalContent |
|---|
| 22 |
from Products.Archetypes.atapi import DisplayList |
|---|
| 23 |
from Globals import InitializeClass |
|---|
| 24 |
from Products.CMFCore.utils import getToolByName |
|---|
| 25 |
from AccessControl import ClassSecurityInfo, Unauthorized |
|---|
| 26 |
from LargeSectionFolder import LeMillFolder |
|---|
| 27 |
from Products.ZCatalog.CatalogBrains import AbstractCatalogBrain |
|---|
| 28 |
from Acquisition import aq_base, aq_inner, aq_parent |
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
from config import PROJECTNAME, ALL_CONTENT_TYPES, CONTENT_TYPES, DEFAULT_ICONS, ACTIVITY_TYPES, TOOLS_TYPES, REPOSITORY, TTF_FONTS, to_unicode |
|---|
| 32 |
from Resource import Resource |
|---|
| 33 |
from Schemata import resource_schema |
|---|
| 34 |
from permissions import ModerateContent, MODIFY_CONTENT, ACCESS_CONTENT, VIEW |
|---|
| 35 |
from FieldsWidgets import WYSIWYMField, LeVisualWidget |
|---|
| 36 |
from cStringIO import StringIO |
|---|
| 37 |
import os, urllib, shutil, sys, zipfile, sre, random, time |
|---|
| 38 |
import urlparse, tempfile, urllib2 |
|---|
| 39 |
from types import ListType, TupleType |
|---|
| 40 |
|
|---|
| 41 |
try: |
|---|
| 42 |
import ho.pisa as pisa |
|---|
| 43 |
from reportlab.pdfbase import pdfmetrics, ttfonts |
|---|
| 44 |
pdf_enabled=True |
|---|
| 45 |
except ImportError: |
|---|
| 46 |
pdf_enabled=False |
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
collection_schema = Schema(( |
|---|
| 50 |
WYSIWYMField('description', |
|---|
| 51 |
accessor='Description', |
|---|
| 52 |
widget=LeVisualWidget( |
|---|
| 53 |
label="Learning and teaching story", |
|---|
| 54 |
description="", |
|---|
| 55 |
label_msgid='label_learning_story', |
|---|
| 56 |
description_msgid='description_description', |
|---|
| 57 |
i18n_domain = "lemill", |
|---|
| 58 |
visible={'view':'invisible','edit':'visible'}, |
|---|
| 59 |
), |
|---|
| 60 |
), |
|---|
| 61 |
|
|---|
| 62 |
ReferenceField('relatedContent', |
|---|
| 63 |
relationship = 'relatesToContent', |
|---|
| 64 |
multiValued = True, |
|---|
| 65 |
isMetadata = True, |
|---|
| 66 |
languageIndependent = False, |
|---|
| 67 |
index = None, |
|---|
| 68 |
write_permission = ModifyPortalContent, |
|---|
| 69 |
allowed_types= ALL_CONTENT_TYPES, |
|---|
| 70 |
widget = ReferenceBrowserWidget( |
|---|
| 71 |
allow_search = True, |
|---|
| 72 |
allow_browse = True, |
|---|
| 73 |
show_indexes = False, |
|---|
| 74 |
force_close_on_insert = True, |
|---|
| 75 |
startup_directory = "content", |
|---|
| 76 |
size = 4, |
|---|
| 77 |
i18n_domain = "lemill", |
|---|
| 78 |
label = "Related Content", |
|---|
| 79 |
label_msgid = "label_related_content", |
|---|
| 80 |
description = "", |
|---|
| 81 |
description_msgid = "help_story_related_content", |
|---|
| 82 |
visible = {'edit' : 'invisible', 'view' : 'invisible' } |
|---|
| 83 |
) |
|---|
| 84 |
), |
|---|
| 85 |
ReferenceField('relatedMethods', |
|---|
| 86 |
relationship = 'relatesToMethods', |
|---|
| 87 |
multiValued = True, |
|---|
| 88 |
isMetadata = True, |
|---|
| 89 |
languageIndependent = False, |
|---|
| 90 |
index = None, |
|---|
| 91 |
write_permission = ModifyPortalContent, |
|---|
| 92 |
allowed_types=('Document','Activity',), |
|---|
| 93 |
widget = ReferenceBrowserWidget( |
|---|
| 94 |
allow_search = True, |
|---|
| 95 |
allow_browse = True, |
|---|
| 96 |
show_indexes = False, |
|---|
| 97 |
force_close_on_insert = True, |
|---|
| 98 |
startup_directory = "methods", |
|---|
| 99 |
size = 4, |
|---|
| 100 |
i18n_domain = "lemill", |
|---|
| 101 |
label = "Related Methods", |
|---|
| 102 |
label_msgid = "label_related_methods", |
|---|
| 103 |
description = "", |
|---|
| 104 |
description_msgid = "help_story_related_methods", |
|---|
| 105 |
visible = {'edit' : 'invisible', 'view' : 'invisible' } |
|---|
| 106 |
) |
|---|
| 107 |
), |
|---|
| 108 |
ReferenceField('relatedTools', |
|---|
| 109 |
relationship = 'relatesToTools', |
|---|
| 110 |
multiValued = True, |
|---|
| 111 |
isMetadata = True, |
|---|
| 112 |
languageIndependent = False, |
|---|
| 113 |
index = None, |
|---|
| 114 |
write_permission = ModifyPortalContent, |
|---|
| 115 |
allowed_types=('Document','Tool',), |
|---|
| 116 |
widget = ReferenceBrowserWidget( |
|---|
| 117 |
allow_search = True, |
|---|
| 118 |
allow_browse = True, |
|---|
| 119 |
show_indexes = False, |
|---|
| 120 |
force_close_on_insert = True, |
|---|
| 121 |
startup_directory = "tools", |
|---|
| 122 |
size = 4, |
|---|
| 123 |
|
|---|
| 124 |
i18n_domain = "lemill", |
|---|
| 125 |
label = "Related Tools", |
|---|
| 126 |
label_msgid = "label_related_tools", |
|---|
| 127 |
description = "", |
|---|
| 128 |
description_msgid = "help_story_related_tools", |
|---|
| 129 |
visible = {'edit' : 'invisible', 'view' : 'invisible' } |
|---|
| 130 |
) |
|---|
| 131 |
), |
|---|
| 132 |
ReferenceField('relatedCollections', |
|---|
| 133 |
relationship = 'relatesToCollections', |
|---|
| 134 |
multiValued = True, |
|---|
| 135 |
isMetadata = True, |
|---|
| 136 |
languageIndependent = False, |
|---|
| 137 |
index = None, |
|---|
| 138 |
write_permission = ModifyPortalContent, |
|---|
| 139 |
allowed_types=('Document','Collection',), |
|---|
| 140 |
widget = ReferenceBrowserWidget( |
|---|
| 141 |
allow_search = True, |
|---|
| 142 |
allow_browse = True, |
|---|
| 143 |
show_indexes = False, |
|---|
| 144 |
force_close_on_insert = True, |
|---|
| 145 |
startup_directory = "community", |
|---|
| 146 |
size = 4, |
|---|
| 147 |
i18n_domain = "lemill", |
|---|
| 148 |
label = "Related Collections", |
|---|
| 149 |
description = "", |
|---|
| 150 |
visible = {'edit' : 'invisible', 'view' : 'invisible' } |
|---|
| 151 |
) |
|---|
| 152 |
), |
|---|
| 153 |
BooleanField('goodStory', |
|---|
| 154 |
index = 'FieldIndex', |
|---|
| 155 |
default=False, |
|---|
| 156 |
isMetadata = True |
|---|
| 157 |
), |
|---|
| 158 |
ComputedField('language', |
|---|
| 159 |
index='FieldIndex:schema', |
|---|
| 160 |
accessor='Language', |
|---|
| 161 |
expression = 'here.combineCollectionLanguage()', |
|---|
| 162 |
isMetadata = True |
|---|
| 163 |
) |
|---|
| 164 |
)) |
|---|
| 165 |
|
|---|
| 166 |
schema = resource_schema + collection_schema |
|---|
| 167 |
|
|---|
| 168 |
schema = schema.copy() |
|---|
| 169 |
|
|---|
| 170 |
class Collection(Resource): |
|---|
| 171 |
"""Collection""" |
|---|
| 172 |
|
|---|
| 173 |
schema = schema |
|---|
| 174 |
actions= ( |
|---|
| 175 |
{ |
|---|
| 176 |
'id':'view', |
|---|
| 177 |
'name':'view', |
|---|
| 178 |
'action':'string:${object_url}/collection_view', |
|---|
| 179 |
'permission':('View',), |
|---|
| 180 |
}, |
|---|
| 181 |
{ |
|---|
| 182 |
'id':'edit', |
|---|
| 183 |
'name':'Edit', |
|---|
| 184 |
'action':'string:${object_url}/base_edit', |
|---|
| 185 |
'permission':('View',), |
|---|
| 186 |
}, |
|---|
| 187 |
) |
|---|
| 188 |
meta_type = "Collection" |
|---|
| 189 |
archetype_name = "Collection" |
|---|
| 190 |
typeDescription="Collection of resources." |
|---|
| 191 |
typeDescMsgId='description_collection' |
|---|
| 192 |
global_allow = 1 |
|---|
| 193 |
portlet = 'here/portlet_collection_actions/macros/portlet' |
|---|
| 194 |
security = ClassSecurityInfo() |
|---|
| 195 |
_at_rename_after_creation = True |
|---|
| 196 |
|
|---|
| 197 |
def at_post_create_script(self): |
|---|
| 198 |
self.at_post_edit_script() |
|---|
| 199 |
|
|---|
| 200 |
def at_post_edit_script(self): |
|---|
| 201 |
self._renameAfterCreation() |
|---|
| 202 |
self. updateStory() |
|---|
| 203 |
|
|---|
| 204 |
def after_add_rename(self): |
|---|
| 205 |
self._renameAfterCreation() |
|---|
| 206 |
|
|---|
| 207 |
|
|---|
| 208 |
security.declarePrivate('_processForm') |
|---|
| 209 |
def _processForm(self, data=1, metadata=None, REQUEST=None, values=None): |
|---|
| 210 |
request = REQUEST or self.REQUEST |
|---|
| 211 |
if values: |
|---|
| 212 |
form = values |
|---|
| 213 |
else: |
|---|
| 214 |
form = request.form |
|---|
| 215 |
|
|---|
| 216 |
|
|---|
| 217 |
title_field=self.getField('title') |
|---|
| 218 |
try: |
|---|
| 219 |
title, other = title_field.widget.process_form(self, title_field, form,validating=False) |
|---|
| 220 |
except TypeError: |
|---|
| 221 |
title, other = title_field.widget.process_form(self, title_field, form) |
|---|
| 222 |
|
|---|
| 223 |
self.setTitle(title) |
|---|
| 224 |
desc_field=self.getField('description') |
|---|
| 225 |
try: |
|---|
| 226 |
desc, other = desc_field.widget.process_form(self, desc_field, form,validating=False) |
|---|
| 227 |
except TypeError: |
|---|
| 228 |
desc, other = desc_field.widget.process_form(self, desc_field, form) |
|---|
| 229 |
self.setDescription(desc) |
|---|
| 230 |
|
|---|
| 231 |
for (reftype,ordered_by) in [('relatedContent','order_'), ('relatedMethods',''), ('relatedTools',''),('relatedCollections','col_order_')]: |
|---|
| 232 |
sorted=self.getSortedList(reftype) |
|---|
| 233 |
if ordered_by: |
|---|
| 234 |
new_list=[] |
|---|
| 235 |
for index, item in enumerate(sorted): |
|---|
| 236 |
order=form.get(ordered_by+str(index), 100) |
|---|
| 237 |
new_list.append((order, item)) |
|---|
| 238 |
new_list.sort() |
|---|
| 239 |
new_list=[item.targetUID for (order, item) in new_list] |
|---|
| 240 |
self._setOrderedReferences(new_list, reftype) |
|---|
| 241 |
|
|---|
| 242 |
for item in sorted: |
|---|
| 243 |
delete_flag=int(form.get('%s_deleted' % item.targetUID, 0)) |
|---|
| 244 |
if delete_flag: |
|---|
| 245 |
self._delete(item.targetUID, reftype) |
|---|
| 246 |
self.reindexObject() |
|---|
| 247 |
|
|---|
| 248 |
security.declarePrivate('_delete') |
|---|
| 249 |
def _delete(self, uid, reftype): |
|---|
| 250 |
field=self.getField(reftype) |
|---|
| 251 |
newlist = [u for u in field.getRaw(self) if u!=uid] |
|---|
| 252 |
field.set(self, newlist) |
|---|
| 253 |
ltool=getToolByName(self, 'lemill_tool') |
|---|
| 254 |
obj=ltool.getObjectByUID(uid) |
|---|
| 255 |
if obj: |
|---|
| 256 |
obj.recalculateScore() |
|---|
| 257 |
obj.reindexObject() |
|---|
| 258 |
|
|---|
| 259 |
def setPermissions(self): |
|---|
| 260 |
|
|---|
| 261 |
self.manage_permission(VIEW, ('Manager','Owner','Member','Authenticated','Anonymous'), acquire=0) |
|---|
| 262 |
|
|---|
| 263 |
self.manage_permission(MODIFY_CONTENT, ('Manager','Owner'), acquire=0) |
|---|
| 264 |
|
|---|
| 265 |
self.manage_permission(ModerateContent, ('Manager','Owner','Reviewer'), acquire=0) |
|---|
| 266 |
|
|---|
| 267 |
self.manage_permission(ACCESS_CONTENT, ('Manager','Owner','Member','Authenticated','Anonymous'), acquire=0) |
|---|
| 268 |
|
|---|
| 269 |
def detectLanguage(self): |
|---|
| 270 |
""" Returns the most used language in content objects of this collection """ |
|---|
| 271 |
languages={} |
|---|
| 272 |
for obj in self.getRelatedContent(): |
|---|
| 273 |
this_l=obj.Language() |
|---|
| 274 |
if this_l: |
|---|
| 275 |
languages[this_l]=languages.get(this_l,0)+1 |
|---|
| 276 |
maxval=max(languages.values()) |
|---|
| 277 |
for key in languages.keys(): |
|---|
| 278 |
if languages[key]==maxval: |
|---|
| 279 |
return key |
|---|
| 280 |
return None |
|---|
| 281 |
|
|---|
| 282 |
def getMetaDescription(self): |
|---|
| 283 |
""" Biography is a good description, but if not filled, then skills, interests or subject areas """ |
|---|
| 284 |
desc= self.Description() |
|---|
| 285 |
if desc: |
|---|
| 286 |
desc_len=0 |
|---|
| 287 |
desc_result=[] |
|---|
| 288 |
ltool = getToolByName(self, 'lemill_tool') |
|---|
| 289 |
desc=ltool.stripHTML(desc) |
|---|
| 290 |
for p in desc.split('\n'): |
|---|
| 291 |
if p: |
|---|
| 292 |
desc_len+=len(p) |
|---|
| 293 |
desc_result.append(p) |
|---|
| 294 |
if desc_len > 140: |
|---|
| 295 |
break |
|---|
| 296 |
return '\n'.join(desc_result) |
|---|
| 297 |
lutool = getToolByName(self, 'lemill_usertool') |
|---|
| 298 |
return "%s's collection of LeMill resources" % lutool.linkTo(self.Creator(), disabled=True) |
|---|
| 299 |
|
|---|
| 300 |
|
|---|
| 301 |
def getPathInsideCollection(self, obj): |
|---|
| 302 |
""" Returns a path in form portal_url/member/collection/content_section/content_subfolder/content_id """ |
|---|
| 303 |
if isinstance(obj, AbstractCatalogBrain): |
|---|
| 304 |
obj_url=obj.getURL().split('/') |
|---|
| 305 |
coll_url=self.absolute_url().split('/') |
|---|
| 306 |
portal_url=getToolByName(self, 'portal_url')().split('/') |
|---|
| 307 |
return '/'.join(coll_url+obj_url[len(portal_url):]) |
|---|
| 308 |
|
|---|
| 309 |
path= getattr(obj.__class__, 'default_location','') |
|---|
| 310 |
if path: |
|---|
| 311 |
return '/'.join((self.absolute_url(), path, obj.id)) |
|---|
| 312 |
else: |
|---|
| 313 |
return '/'.join((self.absolute_url, obj.id)) |
|---|
| 314 |
|
|---|
| 315 |
|
|---|
| 316 |
def getReferenceType(self, object): |
|---|
| 317 |
""" Which reference should be used for this object? Returns string. """ |
|---|
| 318 |
objtype=object.portal_type |
|---|
| 319 |
if objtype in CONTENT_TYPES: |
|---|
| 320 |
return 'relatedContent' |
|---|
| 321 |
elif objtype in ACTIVITY_TYPES: |
|---|
| 322 |
return 'relatedMethods' |
|---|
| 323 |
elif objtype in TOOLS_TYPES: |
|---|
| 324 |
return 'relatedTools' |
|---|
| 325 |
elif objtype == 'Collection': |
|---|
| 326 |
return 'relatedCollections' |
|---|
| 327 |
else: |
|---|
| 328 |
return 'refsToResources' |
|---|
| 329 |
|
|---|
| 330 |
|
|---|
| 331 |
def getEmbedCode(self): |
|---|
| 332 |
""" Builds a nice embed code """ |
|---|
| 333 |
return '''<iframe src="%s/collection_clean_view" width="100%%" height="550px" scrolling="auto" frameborder="1"></iframe>''' % self.absolute_url() |
|---|
| 334 |
|
|---|
| 335 |
|
|---|
| 336 |
security.declareProtected(MODIFY_CONTENT, 'add') |
|---|
| 337 |
def add(self, obj): |
|---|
| 338 |
""" Add object to collection """ |
|---|
| 339 |
if type(obj) == ListType or type(obj) == TupleType: |
|---|
| 340 |
for x in obj: |
|---|
| 341 |
self.add(x) |
|---|
| 342 |
return None |
|---|
| 343 |
UID=obj.UID() |
|---|
| 344 |
reftype=self.getReferenceType(obj) |
|---|
| 345 |
field = self.Schema().get(reftype) |
|---|
| 346 |
value = field.getRaw(self) |
|---|
| 347 |
value.append(UID) |
|---|
| 348 |
self._setOrderedReferences(value, reftype) |
|---|
| 349 |
self.updateStory() |
|---|
| 350 |
obj.recalculateScore() |
|---|
| 351 |
obj.reindexObject() |
|---|
| 352 |
self.notifyCollectionAdd(obj) |
|---|
| 353 |
|
|---|
| 354 |
def updateStory(self): |
|---|
| 355 |
""" Recalculate goodStory-field, changed to be done manually because |
|---|
| 356 |
a) to avoid infinite looping |
|---|
| 357 |
b) to avoid unnecessary and costly updating of this field """ |
|---|
| 358 |
old=self.getGoodStory() |
|---|
| 359 |
new=self.isThisGoodStory() |
|---|
| 360 |
if old != new: |
|---|
| 361 |
self.setGoodStory(new) |
|---|
| 362 |
self.reindexObject(['getGoodStory']) |
|---|
| 363 |
for obj in self.getContent()+self.getMethods()+self.getTools(): |
|---|
| 364 |
obj.recalculateScore() |
|---|
| 365 |
obj.reindexObject(['getScore']) |
|---|
| 366 |
|
|---|
| 367 |
|
|---|
| 368 |
def isThisGoodStory(self): |
|---|
| 369 |
""" Check if there is content,method,tool and description """ |
|---|
| 370 |
if self.state=='deleted': |
|---|
| 371 |
return False |
|---|
| 372 |
if len(self.Description())<200: |
|---|
| 373 |
return False |
|---|
| 374 |
|
|---|
| 375 |
good_content = False |
|---|
| 376 |
good_methods = False |
|---|
| 377 |
good_tools = False |
|---|
| 378 |
c=self.getRelatedContent() |
|---|
| 379 |
for obj in c: |
|---|
| 380 |
if obj.getHasCoverImage() and obj.state == 'public': |
|---|
| 381 |
good_content = True |
|---|
| 382 |
if not good_content: |
|---|
| 383 |
return False |
|---|
| 384 |
m=self.getRelatedMethods() |
|---|
| 385 |
for obj in m: |
|---|
| 386 |
if obj.getHasCoverImage() and obj.state == 'public': |
|---|
| 387 |
good_methods = True |
|---|
| 388 |
if not good_methods: |
|---|
| 389 |
return False |
|---|
| 390 |
t=self.getRelatedTools() |
|---|
| 391 |
for obj in t: |
|---|
| 392 |
if obj.getHasCoverImage() and obj.state == 'public': |
|---|
| 393 |
good_tools = True |
|---|
| 394 |
return good_tools |
|---|
| 395 |
|
|---|
| 396 |
def getItemCount(self): |
|---|
| 397 |
""" return how many items are in collection """ |
|---|
| 398 |
return len(self.getContent()+self.getMethods()+self.getTools()+self.getCollections()) |
|---|
| 399 |
|
|---|
| 400 |
|
|---|
| 401 |
def getContent(self): |
|---|
| 402 |
""" make sure that related content comes in correct order even when programmer forgets to ask for it""" |
|---|
| 403 |
return self.getResources('relatedContent') |
|---|
| 404 |
|
|---|
| 405 |
def getMethods(self): |
|---|
| 406 |
""" don't get deleted methods """ |
|---|
| 407 |
return self.getResources('relatedMethods') |
|---|
| 408 |
|
|---|
| 409 |
def getTools(self): |
|---|
| 410 |
""" don't get deleted tools """ |
|---|
| 411 |
return self.getResources('relatedTools') |
|---|
| 412 |
|
|---|
| 413 |
def getCollections(self): |
|---|
| 414 |
""" don't get deleted collections """ |
|---|
| 415 |
return self.getResources('relatedCollections') |
|---|
| 416 |
|
|---|
| 417 |
def getSortedList(self, reftype='relatedContent'): |
|---|
| 418 |
field = self.Schema().get(reftype) |
|---|
| 419 |
if reftype == 'relatedMethods' or reftype=='relatedTools': |
|---|
| 420 |
sortable = [(ref.targetId(), ref) for ref in self.getReferenceImpl(relationship=field.relationship)] |
|---|
| 421 |
else: |
|---|
| 422 |
sortable = [(getattr(ref,'collection_position',100), ref) for ref in self.getReferenceImpl(relationship=field.relationship)] |
|---|
| 423 |
sortable.sort() |
|---|
| 424 |
return [ref for i, ref in sortable if ref] |
|---|
| 425 |
|
|---|
| 426 |
def getBlurp(self): |
|---|
| 427 |
""" 200-300 character version of description """ |
|---|
| 428 |
desc=self.Description() |
|---|
| 429 |
if desc: |
|---|
| 430 |
lt = getToolByName(self, 'lemill_tool') |
|---|
| 431 |
return lt.cropText(lt.parse_text(desc)) |
|---|
| 432 |
else: |
|---|
| 433 |
return '' |
|---|
| 434 |
|
|---|
| 435 |
def getResources(self, reftype='relatedContent', private_materials=True, pdf_types=False): |
|---|
| 436 |
""" ... """ |
|---|
| 437 |
user = getToolByName(self, 'lemill_usertool').getAuthenticatedId() |
|---|
| 438 |
manager=self.amIManager() |
|---|
| 439 |
resources = [] |
|---|
| 440 |
for ref in self.getSortedList(reftype): |
|---|
| 441 |
print 'ref:', ref |
|---|
| 442 |
target = ref.getTargetObject() |
|---|
| 443 |
if not target: |
|---|
| 444 |
continue |
|---|
| 445 |
if pdf_types and target.meta_type not in ['MultimediaMaterial', 'ExerciseMaterial', 'LessonPlan', 'SchoolProjectMaterial', 'LeMillReference', 'Activity', 'Tool']: |
|---|
| 446 |
continue |
|---|
| 447 |
if target.state=='draft' or target.state=='public': |
|---|
| 448 |
resources.append(target) |
|---|
| 449 |
elif private_materials and target.state=='private' and (user==target.Creator() or manager): |
|---|
| 450 |
resources.append(target) |
|---|
| 451 |
return resources |
|---|
| 452 |
|
|---|
| 453 |
|
|---|
| 454 |
def getResourcesMetadata(self, reftype='relatedContent'): |
|---|
| 455 |
""" ... """ |
|---|
| 456 |
pc=getToolByName(self, 'portal_catalog') |
|---|
| 457 |
user = getToolByName(self, 'lemill_usertool').getAuthenticatedId() |
|---|
| 458 |
manager=self.amIManager() |
|---|
| 459 |
results=[] |
|---|
| 460 |
for ref in self.getSortedList(reftype): |
|---|
| 461 |
md=pc(UID=ref.targetUID) |
|---|
| 462 |
if md: |
|---|
| 463 |
md=md[0] |
|---|
| 464 |
else: |
|---|
| 465 |
continue |
|---|
| 466 |
if md.getState=='draft' or md.getState=='public': |
|---|
| 467 |
results.append(md) |
|---|
| 468 |
elif md.getState=='private' and (md.Creator==user or manager): |
|---|
| 469 |
results.append(md) |
|---|
| 470 |
return results |
|---|
| 471 |
|
|---|
| 472 |
def isResourceInCollection(self, resource): |
|---|
| 473 |
""" Returns True if the resource belongs to this collection, False otherwise. """ |
|---|
| 474 |
objects = self.getRelatedContent() + self.getRelatedMethods() + self.getRelatedTools() + self.getRelatedCollections() |
|---|
| 475 |
return resource in objects |
|---|
| 476 |
|
|---|
| 477 |
|
|---|
| 478 |
|
|---|
| 479 |
def getCollectionContent(self): |
|---|
| 480 |
""" Returns a list of content, methods, tools and collections for RSS """ |
|---|
| 481 |
objects=self.getContent()+self.getMethods()+self.getTools()+self.getCollections() |
|---|
| 482 |
return objects |
|---|
| 483 |
|
|---|
| 484 |
|
|---|
| 485 |
def _setOrderedReferences(self, ordered_list, reftype='relatedContent'): |
|---|
| 486 |
""" setter that stores the index of each element in list with reference object. ordered_list = list of uids """ |
|---|
| 487 |
field = self.getField(reftype) |
|---|
| 488 |
field.set(self, ordered_list) |
|---|
| 489 |
refs = self.getReferenceImpl(relationship=field.relationship) |
|---|
| 490 |
for ref in refs: |
|---|
| 491 |
i = ordered_list.index(ref.targetUID) |
|---|
| 492 |
ref.collection_position=i |
|---|
| 493 |
|
|---|
| 494 |
def getLearningStoryText(self): |
|---|
| 495 |
""" will return the nice version for the story text """ |
|---|
| 496 |
lt = getToolByName(self, 'lemill_tool') |
|---|
| 497 |
return lt.parse_text(self.Description()) |
|---|
| 498 |
|
|---|
| 499 |
|
|---|
| 500 |
|
|---|
| 501 |
def getDefaultIcon(self, meta_type='', obj=None): |
|---|
| 502 |
""" Will return a portrait of the creator or his default icon """ |
|---|
| 503 |
try: |
|---|
| 504 |
mf=self.getMemberFolder() |
|---|
| 505 |
except AttributeError: |
|---|
| 506 |
owner=self.Creator |
|---|
| 507 |
lutool = getToolByName(self, 'lemill_usertool') |
|---|
| 508 |
mf=lutool.getMemberFolder(owner) |
|---|
| 509 |
return mf.getCoverImageURL() |
|---|
| 510 |
|
|---|
| 511 |
def getCollection(self): |
|---|
| 512 |
return self |
|---|
| 513 |
|
|---|
| 514 |
def isCollection(self): |
|---|
| 515 |
return True |
|---|
| 516 |
|
|---|
| 517 |
def getLatestModificationDate(self): |
|---|
| 518 |
""" Looks to latest modification date of all resources """ |
|---|
| 519 |
return max([x.ModificationDate for x in self.getAllRelatedResources()]+[self.ModificationDate()]) |
|---|
| 520 |
|
|---|
| 521 |
def getAllRelatedResources(self): |
|---|
| 522 |
return self.getResourcesMetadata(reftype='relatedContent') + self.getResourcesMetadata(reftype='relatedMethods') + self.getResourcesMetadata(reftype='relatedTools')+self.getResourcesMetadata(reftype='relatedCollections') |
|---|
| 523 |
|
|---|
| 524 |
def getNextAndPrevLinks(self, item): |
|---|
| 525 |
""" Returns a tuple where (prev, next) are urls to next and previous items in collection """ |
|---|
| 526 |
prev='' |
|---|
| 527 |
next='' |
|---|
| 528 |
resources = self.getAllRelatedResources() |
|---|
| 529 |
resources_ids=[r.getId for r in resources] |
|---|
| 530 |
try: |
|---|
| 531 |
i = resources_ids.index(item.getId()) |
|---|
| 532 |
except ValueError: |
|---|
| 533 |
return (prev,next) |
|---|
| 534 |
if i>0: |
|---|
| 535 |
prev=resources[i-1] |
|---|
| 536 |
prev=self.getPathInsideCollection(prev) |
|---|
| 537 |
if i<len(resources)-1: |
|---|
| 538 |
next=resources[i+1] |
|---|
| 539 |
next=self.getPathInsideCollection(next) |
|---|
| 540 |
return (prev, next) |
|---|
| 541 |
|
|---|
| 542 |
def getRelatedContentMetadata(self, **kw): |
|---|
| 543 |
""" Returns catalog objects """ |
|---|
| 544 |
pc = getToolByName(self, 'portal_catalog') |
|---|
| 545 |
uids = self.getRawRelatedContent() |
|---|
| 546 |
query = {'UID':uids} |
|---|
| 547 |
query.update(kw) |
|---|
| 548 |
objlist = pc(query) |
|---|
| 549 |
return objlist |
|---|
| 550 |
|
|---|
| 551 |
def getRelatedMethodsMetadata(self, **kw): |
|---|
| 552 |
""" Returns catalog objects """ |
|---|
| 553 |
pc = getToolByName(self, 'portal_catalog') |
|---|
| 554 |
uids = self.getRawRelatedMethods() |
|---|
| 555 |
query = {'UID':uids} |
|---|
| 556 |
query.update(kw) |
|---|
| 557 |
objlist = pc(query) |
|---|
| 558 |
return objlist |
|---|
| 559 |
|
|---|
| 560 |
def getRelatedToolsMetadata(self, **kw): |
|---|
| 561 |
""" Returns catalog objects """ |
|---|
| 562 |
pc = getToolByName(self, 'portal_catalog') |
|---|
| 563 |
uids = self.getRawRelatedTools() |
|---|
| 564 |
query = {'UID':uids} |
|---|
| 565 |
query.update(kw) |
|---|
| 566 |
objlist = pc(query) |
|---|
| 567 |
return objlist |
|---|
| 568 |
|
|---|
| 569 |
def getRelatedCollectionsMetadata(self, **kw): |
|---|
| 570 |
""" Returns catalog objects """ |
|---|
| 571 |
pc = getToolByName(self, 'portal_catalog') |
|---|
| 572 |
uids = self.getRawRelatedCollections() |
|---|
| 573 |
query = {'UID':uids} |
|---|
| 574 |
query.update(kw) |
|---|
| 575 |
objlist = pc(query) |
|---|
| 576 |
return objlist |
|---|
| 577 |
|
|---|
| 578 |
def combineCollectionLanguage(self): |
|---|
| 579 |
""" Returns the list of languages used by resourcs in collection""" |
|---|
| 580 |
pc=getToolByName(self, 'portal_catalog') |
|---|
| 581 |
languages = {} |
|---|
| 582 |
language = 'en' |
|---|
| 583 |
objectUIDs = self.getRawRelatedContent() + self.getRawRelatedMethods() + self.getRawRelatedTools() |
|---|
| 584 |
objects = pc({'UID':objectUIDs}) |
|---|
| 585 |
for object in objects: |
|---|
| 586 |
|
|---|
| 587 |
if object.Language != '': |
|---|
| 588 |
if object.Language not in languages.keys(): |
|---|
| 589 |
languages[object.Language] = 1 |
|---|
| 590 |
else: |
|---|
| 591 |
languages[object.Language] = languages[object.Language] + 1 |
|---|
| 592 |
if languages: |
|---|
| 593 |
sorter = [] |
|---|
| 594 |
for k in languages.keys(): |
|---|
| 595 |
sorter.append([languages[k], k]) |
|---|
| 596 |
sorter.sort(cmp=lambda x,y: cmp(x[0], y[0])) |
|---|
| 597 |
sorter.reverse() |
|---|
| 598 |
language = sorter[0][1] |
|---|
| 599 |
return language |
|---|
| 600 |
|
|---|
| 601 |
|
|---|
| 602 |
|
|---|
| 603 |
|
|---|
| 604 |
|
|---|
| 605 |
def getRandomObject(self): |
|---|
| 606 |
""" Chooses one object to represent the whole collection """ |
|---|
| 607 |
objectUIDs = self.getRawRelatedContent() + self.getRawRelatedMethods() + self.getRawRelatedTools() |
|---|
| 608 |
pc = getToolByName(self, 'portal_catalog') |
|---|
| 609 |
results = pc({'UID':objectUIDs, 'getState':['public', 'draft'], 'getHasCoverImage': True}) |
|---|
| 610 |
if results: |
|---|
| 611 |
return random.choice(results).getObject() |
|---|
| 612 |
else: |
|---|
| 613 |
return None |
|---|
| 614 |
|
|---|
| 615 |
def getCollectionInfo(self): |
|---|
| 616 |
""" Provides data for embedded collection """ |
|---|
| 617 |
d={} |
|---|
| 618 |
obj=self.getRandomObject() |
|---|
| 619 |
if obj: |
|---|
| 620 |
d['coverUrl'] = obj.getCoverImageURL() |
|---|
| 621 |
else: |
|---|
| 622 |
d['coverUrl'] = '' |
|---|
| 623 |
d['collectionUrl'] = self.absolute_url() |
|---|
| 624 |
pu = getToolByName(self,'portal_url') |
|---|
| 625 |
d['listStyle'] = "line-height: 1.5em;margin: 0.5em 0 0 1.5em;padding: 0;list-style-image: url(%s/bullet.gif);list-style-type: square" % pu() |
|---|
| 626 |
d['collectionTitle'] = self.Title() |
|---|
| 627 |
d['content_n'] = len(self.getResources(reftype='relatedContent', private_materials=False)) |
|---|
| 628 |
d['methods_n'] = len(self.getRelatedMethods()) |
|---|
| 629 |
d['tools_n'] = len(self.getRelatedTools()) |
|---|
| 630 |
d['collections_n'] = len(self.getRawRelatedCollections()) |
|---|
| 631 |
short_desc=self.getBlurp() |
|---|
| 632 |
if short_desc: |
|---|
| 633 |
if short_desc.endswith('</p>'): |
|---|
| 634 |
short_desc = short_desc[:-4] |
|---|
| 635 |
if short_desc.startswith('<p>'): |
|---|
| 636 |
short_desc = short_desc[3:] |
|---|
| 637 |
d['collectionDescription'] = short_desc or '' |
|---|
| 638 |
return d |
|---|
| 639 |
|
|---|
| 640 |
|
|---|
| 641 |
|
|---|
| 642 |
|
|---|
| 643 |
|
|---|
| 644 |
security.declarePrivate('notifyCollectionAdd') |
|---|
| 645 |
def notifyCollectionAdd(self, obj): |
|---|
| 646 |
lutool = getToolByName(self, 'lemill_usertool') |
|---|
| 647 |
auth_mf = lutool.getMemberFolder() |
|---|
| 648 |
if obj.Creator() != lutool.getAuthenticatedId() and obj.meta_type in self.getFeaturedTypes(): |
|---|
| 649 |
mf = lutool.getMemberFolder(obj.Creator()) |
|---|
| 650 |
|
|---|
| 651 |
if mf and mf.canNotify('resource_added_to_collection'): |
|---|
| 652 |
language=lutool.getCommunicationLanguage(mf) |
|---|
| 653 |
dict={'name':to_unicode(auth_mf.getNicename()), |
|---|
| 654 |
'title':to_unicode(obj.Title()), |
|---|
| 655 |
'url':to_unicode(self.absolute_url())} |
|---|
| 656 |
msg = self.translate("%(name)s has added your resource '%(title)s' to a collection: %(url)s",domain="lemill",target_language=language) % dict |
|---|
| 657 |
ltool = getToolByName(self, 'lemill_tool') |
|---|
| 658 |
ltool.mailNotification(msg=msg, recipient=mf.getEmail(), language=language) |
|---|
| 659 |
|
|---|
| 660 |
|
|---|
| 661 |
|
|---|
| 662 |
|
|---|
| 663 |
def isPDFEnabled(self): |
|---|
| 664 |
""" Do we want to show link to pdf download? """ |
|---|
| 665 |
return pdf_enabled |
|---|
| 666 |
|
|---|
| 667 |
def downloadPage(self): |
|---|
| 668 |
""" Just returns collection_pdf -page. We don't have links to this template to avoid bots""" |
|---|
| 669 |
return self.collection_page() |
|---|
| 670 |
|
|---|
| 671 |
|
|---|
| 672 |
def downloadPDF(self): |
|---|
| 673 |
""" This method is the front-end for pisa, PDF file generator """ |
|---|
| 674 |
if not pdf_enabled: |
|---|
| 675 |
return None |
|---|
| 676 |
image_cache={} |
|---|
| 677 |
portal=self.portal_url |
|---|
| 678 |
portal_url=self.portal_url() |
|---|
| 679 |
|
|---|
| 680 |
def getFSSPath(piece, size_name): |
|---|
| 681 |
"""Get path to actual file""" |
|---|
| 682 |
field=piece.getField('image') |
|---|
| 683 |
stor=field.getStorage(piece) |
|---|
| 684 |
info = stor.getFSSInfo(size_name, piece) |
|---|
| 685 |
if info is None: |
|---|
| 686 |
return '' |
|---|
| 687 |
strategy = stor.getStorageStrategy(size_name, piece) |
|---|
| 688 |
props = stor.getStorageStrategyProperties(size_name, piece, info) |
|---|
| 689 |
path = strategy.getValueFilePath(**props) |
|---|
| 690 |
return path |
|---|
| 691 |
|
|---|
| 692 |
|
|---|
| 693 |
class Callback(pisa.pisaLinkLoader): |
|---|
| 694 |
""" Our own version of pisa's callback method. If links are local, get them from zope and if they use |
|---|
| 695 |
FileSystemStorage, point to that file instead of building a temp.""" |
|---|
| 696 |
def __del__(self): |
|---|
| 697 |
|
|---|
| 698 |
for path in self.tfileList: |
|---|
| 699 |
if path.split('/')[-1].startswith('pisa-'): |
|---|
| 700 |
os.remove(path) |
|---|
| 701 |
|
|---|
| 702 |
|
|---|
| 703 |
def getFileName(self, name, relative=None): |
|---|
| 704 |
name=name.replace("\n","") |
|---|
| 705 |
name=name.replace(" ", "%20") |
|---|
| 706 |
if name.startswith('fonts/'): |
|---|
| 707 |
return os.path.join(TTF_FONTS, name.split('/')[-1]) |
|---|
| 708 |
if name in image_cache: |
|---|
| 709 |
return image_cache[name] |
|---|
| 710 |
url = urlparse.urljoin(relative or self.src, name) |
|---|
| 711 |
url_path=url.split('/') |
|---|
| 712 |
if url_path[-1] in ['image_large', 'image_small','image_slide'] and url.startswith(portal_url): |
|---|
| 713 |
portal_path=portal_url.split('/') |
|---|
| 714 |
url_path=url_path[len(portal_path):] |
|---|
| 715 |
obj=portal.unrestrictedTraverse(url_path) |
|---|
| 716 |
piece=portal.unrestrictedTraverse(url_path[:-1]) |
|---|
| 717 |
size_name=url_path[-1] |
|---|
| 718 |
path=getFSSPath(piece, size_name) |
|---|
| 719 |
image_cache[name]=path |
|---|
| 720 |
self.tfileList.append(path) |
|---|
| 721 |
return path |
|---|
| 722 |
path = urlparse.urlsplit(url)[2] |
|---|
| 723 |
suffix = "" |
|---|
| 724 |
if "." in path: |
|---|
| 725 |
new_suffix = "." + path.split(".")[-1].lower() |
|---|
| 726 |
if new_suffix in (".css", ".gif", ".jpg", ".png"): |
|---|
| 727 |
suffix = new_suffix |
|---|
| 728 |
path = tempfile.mktemp(prefix="pisa-", suffix = suffix) |
|---|
| 729 |
tfile = file(path, "wb") |
|---|
| 730 |
if url.startswith(portal_url): |
|---|
| 731 |
portal_path=portal_url.split('/') |
|---|
| 732 |
url_path=url_path[len(portal_path):] |
|---|
| 733 |
obj=portal.unrestrictedTraverse(url_path) |
|---|
| 734 |
if hasattr(obj, 'data') and obj.data: |
|---|
| 735 |
data=StringIO(str(obj.data)) |
|---|
| 736 |
elif hasattr(obj, '_data') and obj._data: |
|---|
| 737 |
data=StringIO(str(obj._data)) |
|---|
| 738 |
else: |
|---|
| 739 |
data=StringIO(str(obj)) |
|---|
| 740 |
while True: |
|---|
| 741 |
d=data.read(1024) |
|---|
| 742 |
if not d: |
|---|
| 743 |
break |
|---|
| 744 |
tfile.write(d) |
|---|
| 745 |
data.close() |
|---|
| 746 |
tfile.close() |
|---|
| 747 |
else: |
|---|
| 748 |
ufile = urllib.urlopen(url) |
|---|
| 749 |
while True: |
|---|
| 750 |
data = ufile.read(1024) |
|---|
| 751 |
if not data: |
|---|
| 752 |
break |
|---|
| 753 |
tfile.write(data) |
|---|
| 754 |
ufile.close() |
|---|
| 755 |
tfile.close() |
|---|
| 756 |
self.tfileList.append(path) |
|---|
| 757 |
image_cache[name]=path |
|---|
| 758 |
return path |
|---|
| 759 |
|
|---|
| 760 |
|
|---|
| 761 |
filename = ".".join((self.id,'pdf')) |
|---|
| 762 |
portal_url=self.portal_url() |
|---|
| 763 |
portal=self.portal_url |
|---|
| 764 |
result_stringio = StringIO() |
|---|
| 765 |
url= '/'.join((self.absolute_url(), 'collection_pdf')) |
|---|
| 766 |
source_text=self.collection_pdf() |
|---|
| 767 |
pdf = pisa.CreatePDF( |
|---|
| 768 |
src=source_text, |
|---|
| 769 |
dest=result_stringio, |
|---|
| 770 |
path=url, |
|---|
| 771 |
link_callback=Callback(url).getFileName, |
|---|
| 772 |
quiet=True, |
|---|
| 773 |
|
|---|
| 774 |
|
|---|
| 775 |
) |
|---|
| 776 |
|
|---|
| 777 |
if not pdf.err: |
|---|
| 778 |
response = self.REQUEST.RESPONSE |
|---|
| 779 |
response.setHeader('Content-Type', 'application/pdf') |
|---|
| 780 |
response.setHeader('Content-Disposition', 'attachment; filename=%s' % filename) |
|---|
| 781 |
result_stringio.seek(0) |
|---|
| 782 |
return result_stringio.read() |
|---|
| 783 |
else: |
|---|
| 784 |
ltool = getToolByName(self, "lemill_tool") |
|---|
| 785 |
ltool.addPortalMessage("A problem occured, PDF could not be generated.", type="stop") |
|---|
| 786 |
return self.REQUEST.RESPONSE.redirect(self.absolute_url()) |
|---|
| 787 |
|
|---|
| 788 |
|
|---|
| 789 |
|
|---|
| 790 |
|
|---|
| 791 |
def download(self, PACKAGE_TYPE="HTML"): |
|---|
| 792 |
""" Builds a zip. Set PACKAGE_TYPE to SCORM to generate an imsmanifest.xml file. """ |
|---|
| 793 |
|
|---|
| 794 |
if PACKAGE_TYPE == "PDF": |
|---|
| 795 |
return self.downloadPDF() |
|---|
| 796 |
|
|---|
| 797 |
elif PACKAGE_TYPE == "PAGE": |
|---|
| 798 |
return self.downloadPage() |
|---|
| 799 |
|
|---|
| 800 |
else: |
|---|
| 801 |
|
|---|
| 802 |
return |
|---|
| 803 |
|
|---|
| 804 |
def splitHTML(htmlPage, pageURL): |
|---|
| 805 |
|
|---|
| 806 |
def flatten(seq): |
|---|
| 807 |
res = [] |
|---|
| 808 |
for item in seq: |
|---|
| 809 |
if isinstance(item, (list, tuple)): |
|---|
| 810 |
res.extend(flatten(item)) |
|---|
| 811 |
else: |
|---|
| 812 |
res.append(item) |
|---|
| 813 |
return res |
|---|
| 814 |
|
|---|
| 815 |
def combineNones(inputList, infoList): |
|---|
| 816 |
previousInfo = '' |
|---|
| 817 |
text = '' |
|---|
| 818 |
list = range(len(infoList)) |
|---|
| 819 |
list.reverse() |
|---|
| 820 |
for i in list: |
|---|
| 821 |
if infoList[i] == None: |
|---|
| 822 |
if previousInfo == None: |
|---|
| 823 |
text = inputList[i] + text |
|---|
| 824 |
else: |
|---|
| 825 |
previousInfo = None |
|---|
| 826 |
text = inputList[i] |
|---|
| 827 |
del infoList[i] |
|---|
| 828 |
del inputList[i] |
|---|
| 829 |
elif previousInfo == None: |
|---|
| 830 |
previousInfo = '' |
|---|
| 831 |
infoList.insert(i + 1, None) |
|---|
| 832 |
inputList.insert(i + 1, text) |
|---|
| 833 |
if previousInfo == None: |
|---|
| 834 |
infoList.insert(0, None) |
|---|
| 835 |
inputList.insert(0, text) |
|---|
| 836 |
|
|---|
| 837 |
def recursiveSplit(inputList, infoList, info, reg, first, next, delete = -1): |
|---|
| 838 |
if isinstance(inputList, (list, tuple)): |
|---|
| 839 |
for i in range(len(inputList)): |
|---|
| 840 |
if not isinstance(infoList[i], str): |
|---|
| 841 |
inputList[i], infoList[i] = recursiveSplit(inputList[i], infoList[i], info, reg, first, next, delete) |
|---|
| 842 |
else: |
|---|
| 843 |
reg = sre.compile(reg, sre.DOTALL | sre.IGNORECASE) |
|---|
| 844 |
inputList = sre.split(reg, inputList) |
|---|
| 845 |
if len(inputList) > 1: |
|---|
| 846 |
infoList = [] |
|---|
| 847 |
for i in range(len(inputList)): |
|---|
| 848 |
mod = i % next |
|---|
| 849 |
if mod == first: |
|---|
| 850 |
infoList.append(info) |
|---|
| 851 |
else: |
|---|
| 852 |
if mod == delete: |
|---|
| 853 |
inputList[i] = '' |
|---|
| 854 |
infoList.append(None) |
|---|
| 855 |
combineNones(inputList, infoList) |
|---|
| 856 |
else: |
|---|
| 857 |
infoList = [infoList] |
|---|
| 858 |
return inputList, infoList |
|---|
| 859 |
|
|---|
| 860 |
splitHtml, infoList = recursiveSplit(htmlPage, None, 'base', '<base href="(.*?)".*?/>', 1, 2) |
|---|
| 861 |
if len(splitHtml) > 1: |
|---|
| 862 |
baseURL = splitHtml[-2] |
|---|
| 863 |
del splitHtml[1::2] |
|---|
| 864 |
del infoList[1::2] |
|---|
| 865 |
else: |
|---|
| 866 |
baseURL = pageURL |
|---|
| 867 |
if not baseURL.endswith('/'): |
|---|
| 868 |
baseURL += '/' |
|---|
| 869 |
|
|---|
| 870 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'src', '(<link[^>]+href=")(.*?)("[^>]+rel="stylesheet")', 2, 4) |
|---|
| 871 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'href', '(<a[^>]+)href="(.*?)"', 2, 3) |
|---|
| 872 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'fvars', '(<object.*?<embed[^>]*?src="(mp3player.swf|FlowPlayer.swf|player.swf)"[^>]*?flashvars=")(.*?)(".*?</object>)', 3, 5, 2) |
|---|
| 873 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'XMLmp3', '(<voiceover[^>]+src=")(.*?)(")', 2, 4) |
|---|
| 874 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'src', '(<[^>]+src=")(.*?)(")', 2, 4) |
|---|
| 875 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'src', '(<param.+?name="movie".*?value=")(.*?)(")', 2, 4) |
|---|
| 876 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'fvars', r"(<script.*?>.*?AC_FL_RunContent\(.*?'flashvars', ')(.*?)((?<!\\)'.*?</script>)", 2, 4) |
|---|
| 877 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'jsfname', "(<script.*?>.*?AC_FL_RunContent\(.*?'movie', ')(.*?)(')", 2, 4) |
|---|
| 878 |
splitHtml, infoList = recursiveSplit(splitHtml, infoList, 'fvars', '(<param.+?name="flashvars".*?value=")(.*?)(")', 2, 4) |
|---|
| 879 |
return flatten(splitHtml), flatten(infoList), baseURL |
|---|
| 880 |
|
|---|
| 881 |
def getAbsoluteURL(url, info, baseURL=''): |
|---|
| 882 |
flash = None |
|---|
| 883 |
if info in ('src', 'jsfname', 'XMLmp3', 'href'): |
|---|
| 884 |
if not url.startswith('http://'): |
|---|
| 885 |
url = baseURL + url |
|---|
| 886 |
elif info == 'fvars': |
|---|
| 887 |
if url.startswith('file='): |
|---|
| 888 |
url = url[5:-5] |
|---|
| 889 |
flash = 'mp3' |
|---|
| 890 |
elif url.startswith('xml='): |
|---|
| 891 |
url = url[4:] |
|---|
| 892 |
flash = 'pilot' |
|---|
| 893 |
elif url.startswith("config={videoFile: '"): |
|---|
| 894 |
url = url[20:-2] |
|---|
| 895 |
flash = 'flv1' |
|---|
| 896 |
elif url.startswith("config={videoFile: \\'"): |
|---|
| 897 |
url = url[21:-3] |
|---|
| 898 |
flash = 'flv2' |
|---|
| 899 |
url = url.replace('\\', '/') |
|---|
| 900 |
url = url.replace('at_download/', '') |
|---|
| 901 |
return url, flash |
|---|
| 902 |
|
|---|
| 903 |
def splitFileName(fileName): |
|---|
| 904 |
splitted = fileName.split('.') |
|---|
| 905 |
if len(splitted) > 1: |
|---|
| 906 |
return '.'.join(splitted[:-1]), '.' + splitted[-1] |
|---|
| 907 |
return fileName, '' |
|---|
| 908 |
|
|---|
| 909 |
def hasFileNameInSrcs(fileName, srcs): |
|---|
| 910 |
for absURL in srcs: |
|---|
| 911 |
src = srcs[absURL] |
|---|
| 912 |
if src.has_key('fileName') and src['fileName'] == fileName: |
|---|
| 913 |
return src |
|---|
| 914 |
return None |
|---|
| 915 |
|
|---|
| 916 |
def findUniqueFileName(absURL, srcs): |
|---|
| 917 |
fileName = absURL.split('?')[0].split('/')[-1] |
|---|
| 918 |
src2 = hasFileNameInSrcs(fileName, srcs) |
|---|
| 919 |
if src2 == None: |
|---|
| 920 |
return fileName |
|---|
| 921 |
else: |
|---|
| 922 |
src2['counter'] += 1 |
|---|
| 923 |
fileName, extension = splitFileName(fileName) |
|---|
| 924 |
return findUniqueFileName('%s(%d)%s' % (fileName, src2['counter'], extension), srcs) |
|---|
| 925 |
|
|---|
| 926 |
def processHtml(splitHtml, infoList, srcs, hrefs, baseURL, htmlIndex): |
|---|
| 927 |
for i in range(len(infoList)): |
|---|
| 928 |
if infoList[i] in ('src', 'jsfname', 'fvars', 'XMLmp3', 'href'): |
|---|
| 929 |
absURL, flash = getAbsoluteURL(splitHtml[i], infoList[i], baseURL) |
|---|
| 930 |
if infoList[i] != 'href' or splitHtml[i].find('at_download') != -1: |
|---|
| 931 |
if not absURL.startswith('http://') or absURL.startswith(self.portal_url()): |
|---|
| 932 |
if srcs.has_key(absURL): |
|---|
| 933 |
srcs[absURL]['usedBy'].add(htmlIndex) |
|---|
| 934 |
else: |
|---|
| 935 |
srcs[absURL] = {} |
|---|
| 936 |
src = srcs[absURL] |
|---|
| 937 |
src['fileName'] = findUniqueFileName(absURL, srcs) |
|---|
| 938 |
src['finalURL'] = '' |
|---|
| 939 |
src['usedBy'] = set([htmlIndex]) |
|---|
| 940 |
src['counter'] = 0 |
|---|
| 941 |
if flash == 'mp3': |
|---|
| 942 |
src['extension'] = 'mp3' |
|---|
| 943 |
elif flash == 'pilot': |
|---|
| 944 |
src['extension'] = 'xml' |
|---|
| 945 |
elif flash in ('flv1', 'flv2'): |
|---|
| 946 |
src['extension'] = 'flv' |
|---|
| 947 |
elif infoList[i] == 'XMLmp3': |
|---|
| 948 |
src['extension'] = 'mp3' |
|---|
| 949 |
else: |
|---|
| 950 |
src['extension'] = '' |
|---|
| 951 |
else: |
|---|
| 952 |
infoList[i] = None |
|---|
| 953 |
if infoList[i] == 'href': |
|---|
| 954 |
absURL, flash = getAbsoluteURL(splitHtml[i], infoList[i], baseURL) |
|---|
| 955 |
if hrefs.has_key(splitHtml[i]): |
|---|
| 956 |
hrefs[absURL]['usedBy'].add(htmlIndex) |
|---|
| 957 |
else: |
|---|
| 958 |
hrefs[absURL] = {} |
|---|
| 959 |
href = hrefs[absURL] |
|---|
| 960 |
href['finalURL'] = '' |
|---|
| 961 |
href['type'] = 0 |
|---|
| 962 |
href['usedBy'] = set([htmlIndex]) |
|---|
| 963 |
|
|---|
| 964 |
def processPilotXMLs(srcs, baseURLs, baseDirs): |
|---|
| 965 |
xmlDatas = {} |
|---|
| 966 |
xmlList = [absURL for absURL in srcs if srcs[absURL]['extension'] == 'xml'] |
|---|
| 967 |
for absURL in xmlList: |
|---|
| 968 |
src = srcs[absURL] |
|---|
| 969 |
xml = self.aq_parent.restrictedTraverse(absURL.split(self.portal_url() + '/')[1])().decode('utf_16').encode('utf_8') |
|---|
| 970 |
xmlDatas[absURL]={} |
|---|
| 971 |
xmlData = xmlDatas[absURL] |
|---|
| 972 |
xmlData['split'], xmlData['info'], baseURL = splitHTML(xml, '') |
|---|
| 973 |
xmlData['fileName'] = src['fileName'] |
|---|
| 974 |
xmlData['baseDirs'] = [] |
|---|
| 975 |
for htmlIndex in src['usedBy']: |
|---|
| 976 |
processHtml(xmlData['split'], xmlData['info'], srcs, {}, baseURLs[htmlIndex], htmlIndex) |
|---|
| 977 |
xmlData['baseDirs'].append(baseDirs[htmlIndex]) |
|---|
| 978 |
return xmlDatas |
|---|
| 979 |
|
|---|
| 980 |
def downloadFiles(srcs, zip, baseDirs = []): |
|---|
| 981 |
def addExtension(fileName, extension): |
|---|
| 982 |
name, ext = splitFileName(fileName) |
|---|
| 983 |
if ext == '' and extension != '': |
|---|
| 984 |
fileName = '%s.%s' % (fileName, extension) |
|---|
| 985 |
return fileName |
|---|
| 986 |
for absURL in srcs: |
|---|
| 987 |
src = srcs[absURL] |
|---|
| 988 |
data = '' |
|---|
| 989 |
mediaType = '' |
|---|
| 990 |
subType = '' |
|---|
| 991 |
try: |
|---|
| 992 |
if src['extension'] != 'xml': |
|---|
| 993 |
dataObj = self.aq_parent.restrictedTraverse(absURL.split(self.portal_url() + '/')[1].split('?')[0]) |
|---|
| 994 |
mediaType, subType = dataObj.getContentType().split('/') |
|---|
| 995 |
subType = subType.split(';')[0] |
|---|
| 996 |
if mediaType == 'image': |
|---|
| 997 |
data = dataObj.data.__str__() |
|---|
| 998 |
if subType in ('jpeg', 'pjpeg'): |
|---|
| 999 |
src['extension'] = 'jpg' |
|---|
| 1000 |
elif subType == 'x-ms-bmp': |
|---|
| 1001 |
src['extension'] = 'bmp' |
|---|
| 1002 |
elif subType in ('png', 'gif'): |
|---|
| 1003 |
src['extension'] = subType |
|---|
| 1004 |
elif subType == 'x-png': |
|---|
| 1005 |
src['extension'] = 'png' |
|---|
| 1006 |
else: |
|---|
| 1007 |
data = str(dataObj) |
|---|
| 1008 |
if mediaType == 'application' and subType == 'x-shockwave-flash': |
|---|
| 1009 |
src['extension'] = 'swf' |
|---|
| 1010 |
elif mediaType == 'video': |
|---|
| 1011 |
if subType in ('x-msvideo', 'avi'): |
|---|
| 1012 |
src['extension'] = 'avi' |
|---|
| 1013 |
elif subType == 'x-ms-wmv': |
|---|
| 1014 |
src['extension'] = 'wmv' |
|---|
| 1015 |
elif subType == 'mpeg': |
|---|
| 1016 |
src['extension'] = 'mpg' |
|---|
| 1017 |
elif subType == 'mp4': |
|---|
| 1018 |
src['extension'] = subType |
|---|
| 1019 |
elif subType == 'quicktime': |
|---|
| 1020 |
src['extension'] = 'mov' |
|---|
| 1021 |
elif mediaType == 'audio' and subType == 'mpeg': |
|---|
| 1022 |
src['extension'] = 'mp3' |
|---|
| 1023 |
except (AttributeError): |
|---|
| 1024 |
|
|---|
| 1025 |
|
|---|
| 1026 |
try: |
|---|
| 1027 |
portal_url = getToolByName(self, 'portal_url') |
|---|
| 1028 |
portal_url = portal_url() |
|---|
| 1029 |
import urllib2 |
|---|
| 1030 |
|
|---|
| 1031 |
if absURL.lower().startswith(portal_url): |
|---|
| 1032 |
|
|---|
| 1033 |
dataObj = urllib2.urlopen(absURL) |
|---|
| 1034 |
data = dataObj.read() |
|---|
| 1035 |
|
|---|
| 1036 |
|
|---|
| 1037 |
subType = absURL.split('/')[-1:][0].split('.')[-1:][0] |
|---|
| 1038 |
src['extension'] = subType |
|---|
| 1039 |
else: |
|---|
| 1040 |
|
|---|
| 1041 |
print "restrictedTraverse error: " + absURL |
|---|
| 1042 |
except (AttributeError): |
|---|
| 1043 |
|
|---|
| 1044 |
print "restrictedTraverse error: " + absURL |
|---|
| 1045 |
|
|---|
| 1046 |
fileName = addExtension(src['fileName'], src['extension']) |
|---|
| 1047 |
src['finalURL'] = '_Files/' + fileName |
|---|
| 1048 |
if src['extension'] != 'xml': |
|---|
| 1049 |
for i in src['usedBy']: |
|---|
| 1050 |
zip.writestr(baseDirs[i] + src['finalURL'], data) |
|---|
| 1051 |
|
|---|
| 1052 |
def processHrefs(hrefs, srcs, htmlURLs, baseDirs, portalURL): |
|---|
| 1053 |
def checkURLCases(absURL, portalURL): |
|---|
| 1054 |
|
|---|
| 1055 |
places = ['/content/pieces/', '/content/references/', '/content/printresources/', '/content/lessonplans/', '/content/pilots/', '/content/presentations/', '/content/schoolprojects/', '/content/webpages/', '/content/exercises/'] + ['/methods/', '/tools/'] |
|---|
| 1056 |
for place in places: |
|---|
| 1057 |
if absURL.count(portalURL + place) > 0: |
|---|
| 1058 |
return True |
|---|
| 1059 |
return False |
|---|
| 1060 |
downloadURLs = [] |
|---|
| 1061 |
for absURL in srcs: |
|---|
| 1062 |
fileName = absURL.split('?')[0].split('/') |
|---|
| 1063 |
domain = '/'.join(fileName[:-1]) |
|---|
| 1064 |
fileName = fileName[-1] |
|---|
| 1065 |
downloadURLs.append('%s/at_download/%s' % (domain, fileName)) |
|---|
| 1066 |
for absURL in hrefs: |
|---|
| 1067 |
href = hrefs[absURL] |
|---|
| 1068 |
if absURL in htmlURLs: |
|---|
| 1069 |
href['finalURL'] = 'href="%sindex.html"' % baseDirs[htmlURLs.index(absURL)] |
|---|
| 1070 |
elif absURL + '/' in htmlURLs: |
|---|
| 1071 |
href['finalURL'] = 'href="%sindex.html"' % baseDirs[htmlURLs.index(absURL + '/')] |
|---|
| 1072 |
elif absURL in srcs: |
|---|
| 1073 |
href['finalURL'] = srcs[absURL]['finalURL'] |
|---|
| 1074 |
href['type'] = 1 |
|---|
| 1075 |
elif absURL in downloadURLs: |
|---|
| 1076 |
href['finalURL'] = srcs[absURL.replace('/at_download', '')]['finalURL'] |
|---|
| 1077 |
href['type'] = 1 |
|---|
| 1078 |
elif absURL.startswith(portalURL) and not (absURL.endswith('/CollectionRSS') or checkURLCases(absURL, portalURL)): |
|---|
| 1079 |
href['finalURL'] = '' |
|---|
| 1080 |
else: |
|---|
| 1081 |
href['finalURL'] = 'href="%s"' % absURL |
|---|
| 1082 |
|
|---|
| 1083 |
def updateHtmls(splitHtmls, infoLists, baseURLs, baseDirs, srcs, hrefs): |
|---|
| 1084 |
for i in range(len(splitHtmls)): |
|---|
| 1085 |
splitHtml = splitHtmls[i] |
|---|
| 1086 |
infoList = infoLists[i] |
|---|
| 1087 |
for j in range(len(splitHtml)): |
|---|
| 1088 |
if infoList[j] in ('src', 'jsfname', 'fvars'): |
|---|
| 1089 |
absURL, flash = getAbsoluteURL(splitHtml[j], infoList[j], baseURLs[i]) |
|---|
| 1090 |
src = srcs[absURL] |
|---|
| 1091 |
finalURL = src['finalURL'] |
|---|
| 1092 |
if infoList[j] == 'src': |
|---|
| 1093 |
splitHtml[j] = finalURL |
|---|
| 1094 |
elif infoList[j] == 'jsfname': |
|---|
| 1095 |
splitHtml[j] = finalURL |
|---|
| 1096 |
elif infoList[j] == 'fvars': |
|---|
| 1097 |
if flash == 'mp3': |
|---|
| 1098 |
splitHtml[j] = 'file=%s' % finalURL |
|---|
| 1099 |
elif flash == 'pilot': |
|---|
| 1100 |
splitHtml[j] = 'xml=%s' % finalURL |
|---|
| 1101 |
elif flash == 'flv1': |
|---|
| 1102 |
splitHtml[j] = "config={videoFile: '../%s'}" % finalURL |
|---|
| 1103 |
elif flash == 'flv2': |
|---|
| 1104 |
splitHtml[j] = "config={videoFile: \\'../%s\\'}" % finalURL |
|---|
| 1105 |
elif infoList[j] == 'href': |
|---|
| 1106 |
absURL, flash = getAbsoluteURL(splitHtml[j], infoList[j], baseURLs[i]) |
|---|
| 1107 |
href = hrefs[absURL] |
|---|
| 1108 |
if href['type'] == 1: |
|---|
| 1109 |
finalURL = 'href="%s"' % href['finalURL'] |
|---|
| 1110 |
else: |
|---|
| 1111 |
finalURL = href['finalURL'] |
|---|
| 1112 |
splitHtml[j] = finalURL |
|---|
| 1113 |
|
|---|
| 1114 |
def updatePilotXMLs(xmlDatas, baseURLs, srcs): |
|---|
| 1115 |
for absURL in xmlDatas: |
|---|
| 1116 |
splitXML = xmlDatas[absURL]['split'] |
|---|
| 1117 |
xmlInfoList = xmlDatas[absURL]['info'] |
|---|
| 1118 |
for j in range(len(splitXML)): |
|---|
| 1119 |
if xmlInfoList[j] in ('src', 'XMLmp3'): |
|---|
| 1120 |
absURL2, flash = getAbsoluteURL(splitXML[j], xmlInfoList[j]) |
|---|
| 1121 |
src = srcs[absURL2] |
|---|
| 1122 |
splitXML[j] = src['finalURL'] |
|---|
| 1123 |
|
|---|
| 1124 |
def addHtmlsToZip(splitHtmls, zip, baseDirs = None): |
|---|
| 1125 |
i = 0 |
|---|
| 1126 |
baseDir = '' |
|---|
| 1127 |
for splitHtml in splitHtmls: |
|---|
| 1128 |
if isinstance(baseDirs, (list, tuple)): |
|---|
| 1129 |
baseDir = baseDirs[i] |
|---|
| 1130 |
zip.writestr(baseDir + 'index.html', ''.join(splitHtml)) |
|---|
| 1131 |
i += 1 |
|---|
| 1132 |
|
|---|
| 1133 |
def addPilotXMLsToZip(xmlDatas, srcs, zip): |
|---|
| 1134 |
for absURL in xmlDatas: |
|---|
| 1135 |
xmlData = xmlDatas[absURL] |
|---|
| 1136 |
xml = ''.join(xmlData['split']) |
|---|
| 1137 |
for baseDir in xmlData['baseDirs']: |
|---|
| 1138 |
zip.writestr(baseDir + srcs[absURL]['finalURL'], xml.decode('utf_8').encode('utf_16')) |
|---|
| 1139 |
break |
|---|
| 1140 |
|
|---|
| 1141 |
def addSCORMFiles(version, htmlList, srcs, zip): |
|---|
| 1142 |
def addMetadata(manifest, obj, tabs): |
|---|
| 1143 |
language = obj.Language() |
|---|
| 1144 |
if language == '': |
|---|
| 1145 |
language = 'en' |
|---|
| 1146 |
tab_str=''.join(['\t']*tabs) |
|---|
| 1147 |
manifest.append(''.join((tab_str,'<metadata>'))) |
|---|
| 1148 |
manifest.append(''.join((tab_str,'\t<schema>ADL SCORM</schema>'))) |
|---|
| 1149 |
manifest.append(''.join((tab_str,'\t<schemaversion>1.2</schemaversion>'))) |
|---|
| 1150 |
manifest.append(''.join((tab_str,'\t<imsmd:lom>'))) |
|---|
| 1151 |
manifest.append(''.join((tab_str,'\t\t<general>'))) |
|---|
| 1152 |
manifest.append(''.join((tab_str,'\t\t\t<title>'))) |
|---|
| 1153 |
manifest.append(''.join((tab_str,'\t\t\t\t<langstring xml:lang="',language,'">',obj.TitleOrId(),'</langstring>'))) |
|---|
| 1154 |
manifest.append(''.join((tab_str,'\t\t\t</title>'))) |
|---|
| 1155 |
manifest.append(''.join((tab_str,'\t\t\t<language>',language,'</language>'))) |
|---|
| 1156 |
tags = obj.getTags() |
|---|
| 1157 |
for tag in tags: |
|---|
| 1158 |
manifest.append(''.join((tab_str,'\t\t\t<keyword>'))) |
|---|
| 1159 |
manifest.append(''.join((tab_str,'\t\t\t\t<langstring xml:lang="',language,'">',tag,'</langstring>'))) |
|---|
| 1160 |
manifest.append(''.join((tab_str,'\t\t\t</keyword>'))) |
|---|
| 1161 |
manifest.append(''.join((tab_str,'\t\t</general>'))) |
|---|
| 1162 |
manifest.append(''.join((tab_str,'\t\t<lifecycle>'))) |
|---|
| 1163 |
manifest.append(''.join((tab_str,'\t\t\t<version>'))) |
|---|
| 1164 |
manifest.append(''.join((tab_str,'\t\t\t\t<langstring xml:lang="',language,'">',str(obj.getLatestEditDate()),'</langstring>'))) |
|---|
| 1165 |
manifest.append(''.join((tab_str,'\t\t\t</version>'))) |
|---|
| 1166 |
manifest.append(''.join((tab_str,'\t\t</lifecycle>'))) |
|---|
| 1167 |
manifest.append(''.join((tab_str,'\t</imsmd:lom>'))) |
|---|
| 1168 |
manifest.append(''.join((tab_str,'</metadata>'))) |
|---|
| 1169 |
return manifest |
|---|
| 1170 |
|
|---|
| 1171 |
manifest=[] |
|---|
| 1172 |
manifest.append('<?xml version="1.0" encoding="utf-8"?>') |
|---|
| 1173 |
manifest.append('<manifest identifier="%s" version="%s" xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_rootv1p2p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" xsi:schemaLocation="http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd">' % (htmlList[0][0].getId(), version)) |
|---|
| 1174 |
manifest = addMetadata(manifest, self, 1) |
|---|
| 1175 |
manifest.append('\t<organizations default="ORGANIZATION_%s">' % htmlList[0][0].getId()) |
|---|
| 1176 |
manifest.append('\t\t<title>%s</title>' % htmlList[0][0].TitleOrId()) |
|---|
| 1177 |
manifest.append('\t\t<organization identifier="ORGANIZATION_%s" structure="linear">' % htmlList[0][0].getId()) |
|---|
| 1178 |
for obj in htmlList: |
|---|
| 1179 |
manifest.append('\t\t\t<item identifier="ITEM_%s" identifierref="RESOURCE_%s" isvisible="true">' % (obj[0].getId(), obj[0].getId())) |
|---|
| 1180 |
manifest.append('\t\t\t\t<title>%s</title>' % obj[0].TitleOrId()) |
|---|
| 1181 |
manifest = addMetadata(manifest, obj[0], 4) |
|---|
| 1182 |
manifest.append('\t\t\t</item>') |
|---|
| 1183 |
manifest.append('\t\t</organization>') |
|---|
| 1184 |
manifest.append('\t</organizations>') |
|---|
| 1185 |
manifest.append('\t<resources>') |
|---|
| 1186 |
for i in range(len(htmlList)): |
|---|
| 1187 |
manifest.append('\t\t<resource identifier="RESOURCE_%s" type="webcontent" adlcp:scormtype="sco" href="%sindex.html">' % (htmlList[i][0].getId(), htmlList[i][1])) |
|---|
| 1188 |
manifest.append('\t\t\t<file href="%sindex.html" />' % htmlList[i][1]) |
|---|
| 1189 |
for absURL in srcs: |
|---|
| 1190 |
src = srcs[absURL] |
|---|
| 1191 |
if i in src['usedBy']: |
|---|
| 1192 |
finalURL = htmlList[i][1] + src['finalURL'] |
|---|
| 1193 |
manifest.append('\t\t\t<file href="%s" />' % finalURL) |
|---|
| 1194 |
manifest.append('\t\t</resource>') |
|---|
| 1195 |
manifest.append('\t</resources>') |
|---|
| 1196 |
manifest.append('</manifest>') |
|---|
| 1197 |
zip.writestr('imsmanifest.xml', '\n'.join(manifest)) |
|---|
| 1198 |
for file in ('adlcp_rootv1p2.xsd', 'ims_xml.xsd', 'imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd'): |
|---|
| 1199 |
f = open(REPOSITORY + file, 'r') |
|---|
| 1200 |
zip.writestr(file, f.read()) |
|---|
| 1201 |
f.close() |
|---|
| 1202 |
|
|---|
| 1203 |
splitHtmls = [] |
|---|
| 1204 |
infoLists = [] |
|---|
| 1205 |
baseURLs = [] |
|---|
| 1206 |
baseDirs = [] |
|---|
| 1207 |
htmlURLs = [] |
|---|
| 1208 |
srcs = {} |
|---|
| 1209 |
hrefs = {} |
|---|
| 1210 |
times = [] |
|---|
| 1211 |
htmlList = [(self, '')] |
|---|
| 1212 |
htmlIndex = 0 |
|---|
| 1213 |
|
|---|
| 1214 |
content_subtypes = {'Piece':'pieces','LeMillReference':'references','PresentationMaterial':'presentations','PILOTMaterial':'pilots','MultimediaMaterial':'webpages','ExerciseMaterial':'exercises','LessonPlan':'lessonplans','SchoolProjectMaterial':'schoolprojects','LeMillPrintResource':'printresources'} |
|---|
| 1215 |
for resourceType in ('Content', 'Methods', 'Tools'): |
|---|
| 1216 |
for resource in self.getResources(reftype = 'related' + resourceType): |
|---|
| 1217 |
if resourceType == 'Content': |
|---|
| 1218 |
if resource.meta_type in content_subtypes.keys(): |
|---|
| 1219 |
htmlList.append((resource, '/'.join((resourceType.lower(), content_subtypes[resource.meta_type], resource.getId(),'')))) |
|---|
| 1220 |
else: |
|---|
| 1221 |
htmlList.append((resource, '/'.join((resourceType.lower(), resource.getId(),'')))) |
|---|
| 1222 |
times.append(resource.getLatestEditDate()) |
|---|
| 1223 |
|
|---|
| 1224 |
for html in htmlList: |
|---|
| 1225 |
splitHtml, infoList, baseURL = splitHTML(html[0].standalone_view(), self.absolute_url()) |
|---|
| 1226 |
splitHtmls.append(splitHtml) |
|---|
| 1227 |
infoLists.append(infoList) |
|---|
| 1228 |
baseURLs.append(baseURL) |
|---|
| 1229 |
baseDirs.append(html[1]) |
|---|
| 1230 |
htmlURLs.append('/'.join((self.absolute_url(), html[1]))) |
|---|
| 1231 |
processHtml(splitHtml, infoList, srcs, hrefs, baseURL, htmlIndex) |
|---|
| 1232 |
htmlIndex += 1 |
|---|
| 1233 |
|
|---|
| 1234 |
xmlDatas = processPilotXMLs(srcs, baseURLs, baseDirs) |
|---|
| 1235 |
zipStr = StringIO() |
|---|
| 1236 |
zip = zipfile.ZipFile(zipStr, 'w', compression = zipfile.ZIP_DEFLATED) |
|---|
| 1237 |
downloadFiles(srcs, zip, baseDirs) |
|---|
| 1238 |
processHrefs(hrefs, srcs, htmlURLs, baseDirs, self.portal_url()) |
|---|
| 1239 |
updateHtmls(splitHtmls, infoLists, baseURLs, baseDirs, srcs, hrefs) |
|---|
| 1240 |
updatePilotXMLs(xmlDatas, baseURLs, srcs) |
|---|
| 1241 |
addHtmlsToZip(splitHtmls, zip, baseDirs) |
|---|
| 1242 |
addPilotXMLsToZip(xmlDatas, srcs, zip) |
|---|
| 1243 |
if PACKAGE_TYPE == 'SCORM': |
|---|
| 1244 |
addSCORMFiles(str(max(times)), htmlList, srcs, zip) |
|---|
| 1245 |
zip.close() |
|---|
| 1246 |
response = self.REQUEST.RESPONSE |
|---|
| 1247 |
response.setHeader('Content-Type', 'application/zip') |
|---|
| 1248 |
response.setHeader('Content-Disposition', 'attachment; filename="%s.zip"' % self.getId()) |
|---|
| 1249 |
return zipStr.getvalue() |
|---|
| 1250 |
|
|---|
| 1251 |
|
|---|
| 1252 |
|
|---|
| 1253 |
registerType(Collection, PROJECTNAME) |
|---|
| 1254 |
|
|---|
| 1255 |
class CollectionsFolder(LeMillFolder, BaseFolder): |
|---|
| 1256 |
""" container for collection """ |
|---|
| 1257 |
schema = BaseSchema |
|---|
| 1258 |
id = "collections" |
|---|
| 1259 |
meta_type = "CollectionsFolder" |
|---|
| 1260 |
archetype_name = "CollectionsFolder" |
|---|
| 1261 |
|
|---|
| 1262 |
actions= ( |
|---|
| 1263 |
{ |
|---|
| 1264 |
'id':'view', |
|---|
| 1265 |
'name':'view', |
|---|
| 1266 |
'action':'string:${object_url}/collections_list', |
|---|
| 1267 |
'permission':('View',), |
|---|
| 1268 |
}, |
|---|
| 1269 |
) |
|---|
| 1270 |
|
|---|
| 1271 |
aliases = { |
|---|
| 1272 |
'(Default)' : 'collections_list', |
|---|
| 1273 |
'view' : 'collections_list', |
|---|
| 1274 |
'edit' : 'collections_list' |
|---|
| 1275 |
} |
|---|
| 1276 |
|
|---|
| 1277 |
registerType(CollectionsFolder, PROJECTNAME) |
|---|