| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
from AccessControl import ClassSecurityInfo |
|---|
| 20 |
from Products.Archetypes.config import REFERENCE_CATALOG |
|---|
| 21 |
from Products.Archetypes.Registry import registerField, registerWidget |
|---|
| 22 |
from Products.Archetypes.Field import StringField, LinesField, ReferenceField, ObjectField, FileField |
|---|
| 23 |
from Products.Archetypes.Widget import TextAreaWidget, StringWidget, SelectionWidget, TypesWidget, MultiSelectionWidget, VisualWidget, FileWidget |
|---|
| 24 |
from Products.Archetypes.ReferenceEngine import Reference |
|---|
| 25 |
from config import to_unicode |
|---|
| 26 |
from Products.Archetypes import config |
|---|
| 27 |
from string import letters, punctuation |
|---|
| 28 |
from Products.CMFCore.utils import getToolByName |
|---|
| 29 |
from ZODB.PersistentMapping import PersistentMapping |
|---|
| 30 |
from Products.Archetypes.utils import shasattr |
|---|
| 31 |
from Acquisition import aq_base |
|---|
| 32 |
from mp3tool import get_length |
|---|
| 33 |
from types import ListType, TupleType, StringType, UnicodeType, InstanceType |
|---|
| 34 |
from messagefactory_ import i18nme as _ |
|---|
| 35 |
from random import shuffle |
|---|
| 36 |
from copy import copy |
|---|
| 37 |
from xml.dom.minidom import parseString as parse_xml |
|---|
| 38 |
from urlparse import urlparse |
|---|
| 39 |
from xml.sax.saxutils import unescape |
|---|
| 40 |
import re, os, shutil |
|---|
| 41 |
|
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
class LeVisualWidget(VisualWidget): |
|---|
| 46 |
_properties = VisualWidget._properties.copy() |
|---|
| 47 |
_properties.update({ |
|---|
| 48 |
'macro' : 'widget_visual', |
|---|
| 49 |
'rows' : 18, |
|---|
| 50 |
'cols' : 80, |
|---|
| 51 |
'width' : '507px', |
|---|
| 52 |
'height': '260px', |
|---|
| 53 |
}) |
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 |
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False, validating=True): |
|---|
| 57 |
"""Basic impl for form processing in a widget""" |
|---|
| 58 |
value = form.get(field.getName(), empty_marker) |
|---|
| 59 |
if value is empty_marker: |
|---|
| 60 |
return empty_marker |
|---|
| 61 |
if emptyReturnsMarker and value == '': |
|---|
| 62 |
return empty_marker |
|---|
| 63 |
return value, {} |
|---|
| 64 |
|
|---|
| 65 |
registerWidget(LeVisualWidget, |
|---|
| 66 |
title='Visual Widget', |
|---|
| 67 |
description='Visual Widget' |
|---|
| 68 |
) |
|---|
| 69 |
|
|---|
| 70 |
class LeStringWidget(StringWidget): |
|---|
| 71 |
_properties = StringWidget._properties.copy() |
|---|
| 72 |
_properties.update({ |
|---|
| 73 |
'macro' : 'widget_string' |
|---|
| 74 |
}) |
|---|
| 75 |
|
|---|
| 76 |
registerWidget(LeStringWidget, |
|---|
| 77 |
title='String Widget', |
|---|
| 78 |
description='String Widget' |
|---|
| 79 |
) |
|---|
| 80 |
|
|---|
| 81 |
|
|---|
| 82 |
class LeTextAreaWidget(TextAreaWidget): |
|---|
| 83 |
_properties = TextAreaWidget._properties.copy() |
|---|
| 84 |
_properties.update({ |
|---|
| 85 |
'macro' : 'widget_textarea' |
|---|
| 86 |
}) |
|---|
| 87 |
|
|---|
| 88 |
registerWidget(LeTextAreaWidget, |
|---|
| 89 |
title='Textarea Widget', |
|---|
| 90 |
description='Textarea Widget' |
|---|
| 91 |
) |
|---|
| 92 |
|
|---|
| 93 |
class TagsWidget(StringWidget): |
|---|
| 94 |
_properties = StringWidget._properties.copy() |
|---|
| 95 |
_properties.update({ |
|---|
| 96 |
'macro' : 'widget_tags', |
|---|
| 97 |
}) |
|---|
| 98 |
|
|---|
| 99 |
registerWidget(TagsWidget, |
|---|
| 100 |
title='Tags Widget', |
|---|
| 101 |
description='Tags Widget', |
|---|
| 102 |
used_for=('Products.LeMill.TagsField.TagsField',) |
|---|
| 103 |
) |
|---|
| 104 |
|
|---|
| 105 |
class HTMLLinkWidget(StringWidget): |
|---|
| 106 |
_properties = StringWidget._properties.copy() |
|---|
| 107 |
_properties.update({ |
|---|
| 108 |
'macro' : 'widget_htmllink', |
|---|
| 109 |
}) |
|---|
| 110 |
|
|---|
| 111 |
def getShortLinkName(self, link): |
|---|
| 112 |
"""Returns the shortened version of the link pointing to the reference's location. This shortened version goes into the content of the <a> tag.""" |
|---|
| 113 |
lt = getToolByName(self, 'lemill_tool') |
|---|
| 114 |
return lt.parse_text(link, start_with_p=False) |
|---|
| 115 |
|
|---|
| 116 |
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False, validating=True): |
|---|
| 117 |
"""Basic impl for form processing in a widget""" |
|---|
| 118 |
value = form.get(field.getName(), empty_marker) |
|---|
| 119 |
if value is empty_marker: |
|---|
| 120 |
return empty_marker |
|---|
| 121 |
if emptyReturnsMarker and value == '': |
|---|
| 122 |
return empty_marker |
|---|
| 123 |
if field.getName() == 'home_page': |
|---|
| 124 |
if not (value.lower().startswith('http://') or value.lower().startswith('https://')) and value != '': |
|---|
| 125 |
value = 'http://' + value |
|---|
| 126 |
return value, {} |
|---|
| 127 |
|
|---|
| 128 |
registerWidget(HTMLLinkWidget, |
|---|
| 129 |
title='HTML Link Widget', |
|---|
| 130 |
description='Generated <a> tag Widget', |
|---|
| 131 |
used_for=('Products.LeMill.FieldsWidgets.HTMLLinkWidget',) |
|---|
| 132 |
) |
|---|
| 133 |
|
|---|
| 134 |
class AuthorsWidget(StringWidget): |
|---|
| 135 |
_properties = StringWidget._properties.copy() |
|---|
| 136 |
_properties.update({ |
|---|
| 137 |
'macro' : 'widget_authors', |
|---|
| 138 |
}) |
|---|
| 139 |
|
|---|
| 140 |
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False, validating=True): |
|---|
| 141 |
""" Check if the field should use computed values or custom entry. If custom entry, then create the list by replacing |
|---|
| 142 |
author names with their ids if possible. |
|---|
| 143 |
|
|---|
| 144 |
Since the field cannot store extra info about if its custom or computed, first member of the field is '*!*' |
|---|
| 145 |
if the value is customized. This must be removed when displaying field values. |
|---|
| 146 |
|
|---|
| 147 |
!!! stop !!! what to do with searches and comparisons, if there is *!* messing things up? |
|---|
| 148 |
|
|---|
| 149 |
""" |
|---|
| 150 |
|
|---|
| 151 |
name=field.getName() |
|---|
| 152 |
is_computed=form.get(name+'_are_computed','')=='computed' |
|---|
| 153 |
computed_authors= instance.recalculateAuthors(set_field=False, force=True) |
|---|
| 154 |
if is_computed: |
|---|
| 155 |
authors_list=computed_authors |
|---|
| 156 |
else: |
|---|
| 157 |
value_string=form.get(name+'_custom','') |
|---|
| 158 |
names=value_string.split(',') |
|---|
| 159 |
names=[n.strip() for n in names] |
|---|
| 160 |
if names: |
|---|
| 161 |
utool=getToolByName(instance, 'lemill_usertool') |
|---|
| 162 |
|
|---|
| 163 |
authors_list=['*!*'] |
|---|
| 164 |
for name in names: |
|---|
| 165 |
user_id=utool.getMemberByFullName(name, return_id=True, candidates=computed_authors) or name |
|---|
| 166 |
authors_list.append(user_id) |
|---|
| 167 |
else: |
|---|
| 168 |
authors_list=instance.recalculateAuthors(set_field=False, force=True) |
|---|
| 169 |
return authors_list, {} |
|---|
| 170 |
|
|---|
| 171 |
def displayString(self, instance, values, as_links=False): |
|---|
| 172 |
""" Linearize list of ids to string of user names and omit custom marker """ |
|---|
| 173 |
if not values: |
|---|
| 174 |
return values |
|---|
| 175 |
utool=getToolByName(instance, 'lemill_usertool') |
|---|
| 176 |
if values[0]=='*!*': |
|---|
| 177 |
values=values[1:] |
|---|
| 178 |
names=[utool.linkTo(user_id, disabled=not as_links) for user_id in values] |
|---|
| 179 |
return ", ".join(names) |
|---|
| 180 |
|
|---|
| 181 |
def getComputedAuthors(self,instance,as_string=True, include_user=False): |
|---|
| 182 |
""" Get updated authors list to show as an option when editing authors field """ |
|---|
| 183 |
authors=instance.recalculateAuthors(set_field=False) |
|---|
| 184 |
if include_user: |
|---|
| 185 |
utool=getToolByName(instance, 'lemill_usertool') |
|---|
| 186 |
user_id=utool.getAuthenticatedId() |
|---|
| 187 |
if user_id not in authors: |
|---|
| 188 |
authors.append(user_id) |
|---|
| 189 |
if as_string: |
|---|
| 190 |
return self.displayString(instance, authors, as_links=False) |
|---|
| 191 |
else: |
|---|
| 192 |
return authors |
|---|
| 193 |
|
|---|
| 194 |
|
|---|
| 195 |
registerWidget(AuthorsWidget, |
|---|
| 196 |
title='Authors Widget', |
|---|
| 197 |
description='Authors Widget', |
|---|
| 198 |
used_for=('Products.Archetypes.Field.LinesField',) |
|---|
| 199 |
) |
|---|
| 200 |
|
|---|
| 201 |
|
|---|
| 202 |
|
|---|
| 203 |
|
|---|
| 204 |
class MessengerWidget(TypesWidget): |
|---|
| 205 |
_properties = TypesWidget._properties.copy() |
|---|
| 206 |
_properties.update({ |
|---|
| 207 |
'macro' : 'widget_messenger', |
|---|
| 208 |
},) |
|---|
| 209 |
|
|---|
| 210 |
security = ClassSecurityInfo() |
|---|
| 211 |
|
|---|
| 212 |
security.declarePublic('process_form') |
|---|
| 213 |
def process_form(self, instance, field, form, empty_marker=None, |
|---|
| 214 |
emptyReturnsMarker=False, validating=True): |
|---|
| 215 |
"""Concatenates address + messenger type, but if address already contains something like prefix (word:) then removes it. |
|---|
| 216 |
""" |
|---|
| 217 |
name = field.getName() |
|---|
| 218 |
otherName = "%s_other" % name |
|---|
| 219 |
value = form.get(name, empty_marker) |
|---|
| 220 |
othervalue = form.get(otherName, empty_marker) |
|---|
| 221 |
if not value: |
|---|
| 222 |
return '', {} |
|---|
| 223 |
if not othervalue == empty_marker: |
|---|
| 224 |
value = "%s:%s" % (othervalue,value) |
|---|
| 225 |
return value, {} |
|---|
| 226 |
|
|---|
| 227 |
|
|---|
| 228 |
registerWidget(MessengerWidget, |
|---|
| 229 |
title='Messenger Widget', |
|---|
| 230 |
description='Messenger Widget', |
|---|
| 231 |
used_for=('Products.Archetypes.Field.StringField',) |
|---|
| 232 |
) |
|---|
| 233 |
|
|---|
| 234 |
|
|---|
| 235 |
class MobileWidget(StringWidget): |
|---|
| 236 |
_properties = StringWidget._properties.copy() |
|---|
| 237 |
_properties.update({ |
|---|
| 238 |
'macro' : 'widget_mobile', |
|---|
| 239 |
}) |
|---|
| 240 |
|
|---|
| 241 |
security = ClassSecurityInfo() |
|---|
| 242 |
|
|---|
| 243 |
security.declarePublic('process_form') |
|---|
| 244 |
def process_form(self, instance, field, form, empty_marker=None, |
|---|
| 245 |
emptyReturnsMarker=False, validating=True): |
|---|
| 246 |
name = field.getName() |
|---|
| 247 |
otherName = "%s_checkbox" % name |
|---|
| 248 |
value = form.get(name, empty_marker) |
|---|
| 249 |
othervalue = form.get(otherName, empty_marker) |
|---|
| 250 |
if value == empty_marker and emptyReturnsMarker: |
|---|
| 251 |
return empty_marker |
|---|
| 252 |
if othervalue=="1": |
|---|
| 253 |
value = "*SMS*%s" % value |
|---|
| 254 |
return value, {} |
|---|
| 255 |
|
|---|
| 256 |
|
|---|
| 257 |
registerWidget(MobileWidget, |
|---|
| 258 |
title='Mobile Widget', |
|---|
| 259 |
description='Mobile Widget', |
|---|
| 260 |
used_for=('Products.Archetypes.Field.StringField',) |
|---|
| 261 |
) |
|---|
| 262 |
|
|---|
| 263 |
|
|---|
| 264 |
class CopyrightWidget(SelectionWidget): |
|---|
| 265 |
_properties = SelectionWidget._properties.copy() |
|---|
| 266 |
_properties.update({ |
|---|
| 267 |
'macro' : 'widget_copyright', |
|---|
| 268 |
}) |
|---|
| 269 |
|
|---|
| 270 |
registerWidget(CopyrightWidget, |
|---|
| 271 |
title='Copyright Widget', |
|---|
| 272 |
description='Copyright selection Widget', |
|---|
| 273 |
used_for=('Products.LeMill.StringField.StringField',) |
|---|
| 274 |
) |
|---|
| 275 |
|
|---|
| 276 |
class TwoColumnMultiSelectionWidget(MultiSelectionWidget): |
|---|
| 277 |
_properties = MultiSelectionWidget._properties.copy() |
|---|
| 278 |
_properties.update({ |
|---|
| 279 |
'macro' : 'widget_twocolumn', |
|---|
| 280 |
}) |
|---|
| 281 |
|
|---|
| 282 |
registerWidget(TwoColumnMultiSelectionWidget, |
|---|
| 283 |
title='Two column widget', |
|---|
| 284 |
description='Two column multiselection widget', |
|---|
| 285 |
used_for=('Products.Archetypes.Field.MultiSelectionField',) |
|---|
| 286 |
) |
|---|
| 287 |
|
|---|
| 288 |
|
|---|
| 289 |
class GroupWidget(SelectionWidget): |
|---|
| 290 |
_properties = SelectionWidget._properties.copy() |
|---|
| 291 |
_properties.update({ |
|---|
| 292 |
'macro' : 'widget_group', |
|---|
| 293 |
'format' : 'checkbox', |
|---|
| 294 |
}) |
|---|
| 295 |
def getGroupVocabulary(self, instance): |
|---|
| 296 |
""" this builds vocabulary tuples for groups field: |
|---|
| 297 |
[(group Title, group UID, is_checked, is_disabled), ... ] |
|---|
| 298 |
""" |
|---|
| 299 |
lutool = getToolByName(instance, 'lemill_usertool') |
|---|
| 300 |
mf = lutool.getMemberFolder() |
|---|
| 301 |
user_groups=[] |
|---|
| 302 |
if mf: |
|---|
| 303 |
user_groups=mf.getGroups() |
|---|
| 304 |
resource_groups=instance.getGroupInfo() |
|---|
| 305 |
resource_uids=[g.UID for g in resource_groups] |
|---|
| 306 |
user_uids=[g.UID for g in user_groups] |
|---|
| 307 |
disabled=[(g.Title.lower(), g.Title, g.UID, True, True) for g in resource_groups if (g.UID not in user_uids) and isinstance(g.Title, StringType)] |
|---|
| 308 |
disabled.sort() |
|---|
| 309 |
other=[(g.Title.lower(), g.Title, g.UID, g.UID in resource_uids, False) for g in user_groups if isinstance(g.Title, StringType)] |
|---|
| 310 |
other.sort() |
|---|
| 311 |
groups=[(g[1],g[2],g[3],g[4]) for g in disabled+other] |
|---|
| 312 |
return groups+[('...or create a new group:','__new_group',False,False)] |
|---|
| 313 |
|
|---|
| 314 |
def process_form(self, instance, field, form, empty_marker=[], |
|---|
| 315 |
emptyReturnsMarker=False, validating=True): |
|---|
| 316 |
"""Basic impl for form processing in a widget""" |
|---|
| 317 |
value = form.get(field.getName(), empty_marker) |
|---|
| 318 |
if value is empty_marker: |
|---|
| 319 |
return empty_marker |
|---|
| 320 |
if emptyReturnsMarker and value == '': |
|---|
| 321 |
return empty_marker |
|---|
| 322 |
if '__new_group' in value: |
|---|
| 323 |
new_group_name = form.get('new_group_name', '') |
|---|
| 324 |
if new_group_name: |
|---|
| 325 |
|
|---|
| 326 |
blog=instance.community.groups.addGroup(new_group_name) |
|---|
| 327 |
value.remove('__new_group') |
|---|
| 328 |
value.append(blog.UID()) |
|---|
| 329 |
return value, {} |
|---|
| 330 |
|
|---|
| 331 |
class TranslationWidget(SelectionWidget): |
|---|
| 332 |
_properties = SelectionWidget._properties.copy() |
|---|
| 333 |
_properties.update({ |
|---|
| 334 |
'macro' : 'widget_translation', |
|---|
| 335 |
}) |
|---|
| 336 |
def getTranslationsData(self, instance): |
|---|
| 337 |
""" Get {uid, url, title, language, pretty_language} for translations of this instance """ |
|---|
| 338 |
pc=getToolByName(instance, 'portal_catalog') |
|---|
| 339 |
ltool=getToolByName(instance, 'lemill_tool') |
|---|
| 340 |
my_uid=instance.UID() |
|---|
| 341 |
md_results=pc(getOther_languages=my_uid, getState=['public','draft']) |
|---|
| 342 |
results=[] |
|---|
| 343 |
for md in self.fastMetadata(md_results, ['getUID','rid','getNicename','Language']): |
|---|
| 344 |
|
|---|
| 345 |
data={'uid':md[0], 'url':pc.getUrlPath(md[1]), 'title':md[2], 'lang':md[3], 'pretty_lang':ltool.getPrettyLanguage(md[3])} |
|---|
| 346 |
results.append(data) |
|---|
| 347 |
|
|---|
| 348 |
|
|---|
| 349 |
results.sort(key=lambda x: x['lang']) |
|---|
| 350 |
return results |
|---|
| 351 |
|
|---|
| 352 |
def process_form(self, instance, field, form, empty_marker=[], |
|---|
| 353 |
emptyReturnsMarker=False, validating=True): |
|---|
| 354 |
"""Basic impl for form processing in a widget""" |
|---|
| 355 |
value=True |
|---|
| 356 |
uids=[] |
|---|
| 357 |
index=0 |
|---|
| 358 |
while value: |
|---|
| 359 |
value=form.get('%s_%s' % (field.getName(), index), '') |
|---|
| 360 |
if value: |
|---|
| 361 |
uids.append(value) |
|---|
| 362 |
index+=1 |
|---|
| 363 |
return value, {} |
|---|
| 364 |
|
|---|
| 365 |
class ChapterWidget(SelectionWidget): |
|---|
| 366 |
_properties = SelectionWidget._properties.copy() |
|---|
| 367 |
_properties.update({ |
|---|
| 368 |
'macro' : 'widget_chapter', |
|---|
| 369 |
'collections_only' : False, |
|---|
| 370 |
'helper_js' : ('AC_RunActiveContent.js',), |
|---|
| 371 |
'show_added' : True, |
|---|
| 372 |
'no_label' : True |
|---|
| 373 |
}) |
|---|
| 374 |
security = ClassSecurityInfo() |
|---|
| 375 |
|
|---|
| 376 |
security.declarePublic('process_form') |
|---|
| 377 |
def process_form(self, instance, field, form, empty_marker=None, |
|---|
| 378 |
emptyReturnsMarker=False, validating=True): |
|---|
| 379 |
""" goes through the form, reorganizes chapters if necessary and delivers results to field writer (as dict). """ |
|---|
| 380 |
chapter_type=form.get('chapter_type') |
|---|
| 381 |
if not chapter_type: |
|---|
| 382 |
return {},{} |
|---|
| 383 |
changes = {} |
|---|
| 384 |
changes['edited']=int(form.get('chapter_last_edited', 0)) |
|---|
| 385 |
count=int(form.get('chapter_count',0)) |
|---|
| 386 |
changes['count']=count |
|---|
| 387 |
new_order=[] |
|---|
| 388 |
reorder=False |
|---|
| 389 |
|
|---|
| 390 |
|
|---|
| 391 |
for index in range(0,count): |
|---|
| 392 |
order=int(form.get('chapter_order_%s' % index, 0)) |
|---|
| 393 |
new_order.append(order) |
|---|
| 394 |
if order!=index: |
|---|
| 395 |
reorder=True |
|---|
| 396 |
if reorder: |
|---|
| 397 |
changes['new_order']=new_order |
|---|
| 398 |
if form.has_key('delete_chapter'): |
|---|
| 399 |
changes['delete_chapter']=int(form.get('delete_chapter')) |
|---|
| 400 |
|
|---|
| 401 |
changes['chapter_type']=chapter_type |
|---|
| 402 |
if chapter_type=='media_piece': |
|---|
| 403 |
changes['uid']=form.get('piece_uid') |
|---|
| 404 |
changes['caption']=form.get('caption','') |
|---|
| 405 |
elif chapter_type=='text_block': |
|---|
| 406 |
changes['text']=form.get('chapter_textarea') |
|---|
| 407 |
elif chapter_type=='embed_block': |
|---|
| 408 |
changes['text']='' |
|---|
| 409 |
changes['embed']=form.get('embed_field','') |
|---|
| 410 |
changes['embed_old']=form.get('embed_field_old', '') |
|---|
| 411 |
|
|---|
| 412 |
if form.get('saveChapter', None): |
|---|
| 413 |
form.update({'chapter_edited':-1, 'chapter_anchor':changes['edited'], 'stay':1}) |
|---|
| 414 |
elif form.get('editChapter', None): |
|---|
| 415 |
form.update({'chapter_anchor':form['chapter_edited'], 'stay':1}) |
|---|
| 416 |
|
|---|
| 417 |
elif form.get('add_text_block', None): |
|---|
| 418 |
changes['new_chapters']=[{'text':'','type':'text_block'}] |
|---|
| 419 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 420 |
elif form.get('add_media_piece', None): |
|---|
| 421 |
changes['new_chapters']=[{'text':'','type':'media_piece','uid':''}] |
|---|
| 422 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 423 |
elif form.get('add_embed_block', None): |
|---|
| 424 |
changes['new_chapters']=[{'text':'','type':'embed_block', 'embed':''}] |
|---|
| 425 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 426 |
return changes, {} |
|---|
| 427 |
|
|---|
| 428 |
registerWidget(ChapterWidget, |
|---|
| 429 |
title='Chapter Widget', |
|---|
| 430 |
description='Chapter editor Widget', |
|---|
| 431 |
used_for=('Products.LeMill.ChapterField',) |
|---|
| 432 |
) |
|---|
| 433 |
|
|---|
| 434 |
class SlideWidget(ChapterWidget): |
|---|
| 435 |
_properties = ChapterWidget._properties.copy() |
|---|
| 436 |
_properties.update({ |
|---|
| 437 |
'macro' : 'widget_slides', |
|---|
| 438 |
'collections_only' : False, |
|---|
| 439 |
'show_added' : True, |
|---|
| 440 |
'chapter_count' : 5, |
|---|
| 441 |
}) |
|---|
| 442 |
|
|---|
| 443 |
registerWidget(SlideWidget, |
|---|
| 444 |
title='Slide Widget', |
|---|
| 445 |
description='Slide and caption viewer Widget', |
|---|
| 446 |
used_for=('Products.LeMill.ChapterField',) |
|---|
| 447 |
) |
|---|
| 448 |
|
|---|
| 449 |
class PilotWidget(ChapterWidget): |
|---|
| 450 |
_properties = ChapterWidget._properties.copy() |
|---|
| 451 |
_properties.update({ |
|---|
| 452 |
'macro' : 'widget_pilot', |
|---|
| 453 |
'collections_only' : False, |
|---|
| 454 |
'helper_js' : ('AC_RunActiveContent.js',), |
|---|
| 455 |
'show_added' : True, |
|---|
| 456 |
'chapter_count' : 3, |
|---|
| 457 |
}) |
|---|
| 458 |
security = ClassSecurityInfo() |
|---|
| 459 |
|
|---|
| 460 |
security.declarePublic('process_form') |
|---|
| 461 |
def process_form(self, instance, field, form, empty_marker=None, |
|---|
| 462 |
emptyReturnsMarker=False, validating=True): |
|---|
| 463 |
""" goes through the form and sends changes forward to field writer """ |
|---|
| 464 |
changes, empty= ChapterWidget.process_form(self, instance, field, form) |
|---|
| 465 |
if not changes: |
|---|
| 466 |
return changes,empty |
|---|
| 467 |
chapter_type=changes['chapter_type'] |
|---|
| 468 |
if chapter_type=='pilot_scene': |
|---|
| 469 |
audio_file=form.get('audio_file', None) |
|---|
| 470 |
if audio_file and hasattr(audio_file, 'filename'): |
|---|
| 471 |
changes['audio_file']=audio_file |
|---|
| 472 |
else: |
|---|
| 473 |
changes['audio_uid']=form.get('audio_uid') |
|---|
| 474 |
image_file=form.get('image_file', None) |
|---|
| 475 |
if image_file and hasattr(image_file, 'filename'): |
|---|
| 476 |
changes['image_file']=image_file |
|---|
| 477 |
else: |
|---|
| 478 |
changes['image_uid']=form.get('image_uid') |
|---|
| 479 |
kws=[] |
|---|
| 480 |
for i in range(0, 3): |
|---|
| 481 |
kw=form.get('keywords_%s' % i, '') |
|---|
| 482 |
if kw: |
|---|
| 483 |
kws.append(kw) |
|---|
| 484 |
if kws: |
|---|
| 485 |
changes['keywords']=kws |
|---|
| 486 |
qs=[] |
|---|
| 487 |
for i in range(0, 7): |
|---|
| 488 |
q=form.get('question_%s' % i, '') |
|---|
| 489 |
if q: |
|---|
| 490 |
qs.append(q) |
|---|
| 491 |
if qs: |
|---|
| 492 |
changes['questions']=qs |
|---|
| 493 |
|
|---|
| 494 |
if form.get('add_pilot_section', None): |
|---|
| 495 |
changes['new_chapters']={'text':'','type':'pilot_scene','audio_uid':'', 'image_uid':'', 'keywords':[], 'questions':[]}, |
|---|
| 496 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 497 |
return changes, {} |
|---|
| 498 |
|
|---|
| 499 |
registerWidget(PilotWidget, |
|---|
| 500 |
title='Pilot Widget', |
|---|
| 501 |
description='Scene editor Widget', |
|---|
| 502 |
used_for=('Products.LeMill.ChapterField',) |
|---|
| 503 |
) |
|---|
| 504 |
|
|---|
| 505 |
class ExerciseWidget(ChapterWidget): |
|---|
| 506 |
_properties = ChapterWidget._properties.copy() |
|---|
| 507 |
_properties.update({ |
|---|
| 508 |
'macro' : 'widget_exercise', |
|---|
| 509 |
'collections_only' : False, |
|---|
| 510 |
'helper_js' : ('AC_RunActiveContent.js',), |
|---|
| 511 |
'show_added' : True, |
|---|
| 512 |
'chapter_count' : 1, |
|---|
| 513 |
}) |
|---|
| 514 |
security = ClassSecurityInfo() |
|---|
| 515 |
|
|---|
| 516 |
security.declarePublic('process_form') |
|---|
| 517 |
def process_form(self, instance, field, form, empty_marker=None, |
|---|
| 518 |
emptyReturnsMarker=False, validating=True): |
|---|
| 519 |
""" goes through the form and tries to merge all text inputs to lists, find references to pieces and if file objects are uploaded, just send them forward to field. """ |
|---|
| 520 |
changes, empty= ChapterWidget.process_form(self, instance, field, form) |
|---|
| 521 |
if not changes: |
|---|
| 522 |
return changes,empty |
|---|
| 523 |
chapter_type=changes['chapter_type'] |
|---|
| 524 |
if chapter_type=='guidelines': |
|---|
| 525 |
|
|---|
| 526 |
|
|---|
| 527 |
changes['text']=form.get('chapter') |
|---|
| 528 |
elif chapter_type=='exercise': |
|---|
| 529 |
|
|---|
| 530 |
changes['text']='' |
|---|
| 531 |
elif chapter_type in ['choice','multiple_choices','poll']: |
|---|
| 532 |
question=form.get('choice_question') |
|---|
| 533 |
answers=[] |
|---|
| 534 |
answer_count=int(form.get('choice_answer_count',0)) |
|---|
| 535 |
for i in range(0, answer_count): |
|---|
| 536 |
answer={} |
|---|
| 537 |
answer['text']=form.get('choice_answer_%s' % i, '') |
|---|
| 538 |
answer['is_correct']= chapter_type=='poll' or int(form.get('answer_%s_correct' % i, '')) |
|---|
| 539 |
answer['order']=int(form.get('choice_order_%s' % i, 0)) |
|---|
| 540 |
if answer['text']: |
|---|
| 541 |
answers.append(answer) |
|---|
| 542 |
answers.sort(cmp=lambda x,y: cmp(x['order'], y['order'])) |
|---|
| 543 |
changes['question']=question |
|---|
| 544 |
changes['answers']=answers or [{'text':'','is_correct':1,'order':0}] |
|---|
| 545 |
try: |
|---|
| 546 |
max_points=float(form.get('max_points_mc', 1)) |
|---|
| 547 |
except ValueError: |
|---|
| 548 |
max_points=1 |
|---|
| 549 |
changes['max_points']=max_points |
|---|
| 550 |
elif chapter_type=='fill_in_the_blanks': |
|---|
| 551 |
changes['text']=form.get('fill_blanks','') |
|---|
| 552 |
try: |
|---|
| 553 |
max_points=float(form.get('max_points_fib', 1)) |
|---|
| 554 |
except ValueError: |
|---|
| 555 |
max_points=1 |
|---|
| 556 |
changes['max_points']=max_points |
|---|
| 557 |
elif chapter_type=='open_ended': |
|---|
| 558 |
changes['text']=form.get('open_field','') |
|---|
| 559 |
try: |
|---|
| 560 |
max_points=float(form.get('max_points_ff', 1)) |
|---|
| 561 |
except ValueError: |
|---|
| 562 |
max_points=1 |
|---|
| 563 |
changes['max_points']=max_points |
|---|
| 564 |
|
|---|
| 565 |
|
|---|
| 566 |
if form.get('add_exercise', None): |
|---|
| 567 |
changes['new_chapters']=[{'text':'','type':'exercise'}] |
|---|
| 568 |
count=changes['count'] |
|---|
| 569 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 570 |
elif form.get('import_potatoes', None): |
|---|
| 571 |
file=form.get('import_file', None) |
|---|
| 572 |
if file and hasattr(file, 'filename'): |
|---|
| 573 |
if file.filename.endswith('jcl'): |
|---|
| 574 |
text=self.importJClozeQuestion(file) |
|---|
| 575 |
changes['chapter_type']='fill_in_the_blanks' |
|---|
| 576 |
changes['text']=text |
|---|
| 577 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 578 |
elif file.filename.endswith('jcz'): |
|---|
| 579 |
new_chapters=self.importJQuizQuestions(file) |
|---|
| 580 |
if new_chapters: |
|---|
| 581 |
this_chapter= new_chapters.pop(0) |
|---|
| 582 |
changes['chapter_type']=this_chapter['type'] |
|---|
| 583 |
changes['answers']=this_chapter['answers'] |
|---|
| 584 |
changes['question']=this_chapter['text'] |
|---|
| 585 |
if new_chapters: |
|---|
| 586 |
changes['new_chapters']=new_chapters |
|---|
| 587 |
form.update({'chapter_edited':count, 'chapter_anchor':count, 'stay':1}) |
|---|
| 588 |
return changes, {} |
|---|
| 589 |
|
|---|
| 590 |
def importJQuizQuestions(self, file): |
|---|
| 591 |
""" import hot potatoes questions made with jquiz """ |
|---|
| 592 |
dom = parse_xml(file.read()) |
|---|
| 593 |
questions = dom.getElementsByTagName("question-record") |
|---|
| 594 |
result=[] |
|---|
| 595 |
for ques in questions: |
|---|
| 596 |
questype = int(ques.getElementsByTagName("question-type")[0].childNodes[0].data) |
|---|
| 597 |
if not (questype==1 or questype==4): |
|---|
| 598 |
return |
|---|
| 599 |
question = ques.getElementsByTagName("question")[0].childNodes[0].data.encode('utf-8') |
|---|
| 600 |
|
|---|
| 601 |
answers = ques.getElementsByTagName("answer") |
|---|
| 602 |
|
|---|
| 603 |
corrects=0 |
|---|
| 604 |
made_answers=[] |
|---|
| 605 |
for answer in answers: |
|---|
| 606 |
correct = int(answer.getElementsByTagName("correct")[0].childNodes[0].data) |
|---|
| 607 |
if correct: |
|---|
| 608 |
corrects+=1 |
|---|
| 609 |
answertext = answer.getElementsByTagName("text")[0].childNodes[0].data.encode('utf-8') |
|---|
| 610 |
if answertext: |
|---|
| 611 |
made_answers.append({'text':answertext, 'is_correct':correct}) |
|---|
| 612 |
if corrects==len(result): |
|---|
| 613 |
qtype='poll' |
|---|
| 614 |
elif corrects==1: |
|---|
| 615 |
qtype='choice' |
|---|
| 616 |
else: |
|---|
| 617 |
qtype='multiple_choices' |
|---|
| 618 |
result.append({'type':qtype,'text':question,'answers':made_answers}) |
|---|
| 619 |
dom.unlink() |
|---|
| 620 |
return result |
|---|
| 621 |
|
|---|
| 622 |
def importJClozeQuestion(self, file): |
|---|
| 623 |
""" import hot potatoes question made with JCloze """ |
|---|
| 624 |
dom = parse_xml(file.read()) |
|---|
| 625 |
gap_fill = dom.getElementsByTagName("gap-fill")[0] |
|---|
| 626 |
result=[] |
|---|
| 627 |
for node in gap_fill.childNodes: |
|---|
| 628 |
if node.nodeType==node.TEXT_NODE: |
|---|
| 629 |
result.append(node.data) |
|---|
| 630 |
else: |
|---|
| 631 |
for answer in node.getElementsByTagName("answer"): |
|---|
| 632 |
text_tag=answer.getElementsByTagName("text")[0] |
|---|
| 633 |
text=text_tag.childNodes[0].data |
|---|
| 634 |
result.append('{%s}' % text) |
|---|
| 635 |
result=u''.join(result) |
|---|
| 636 |
|
|---|
| 637 |
dom.unlink() |
|---|
| 638 |
return result |
|---|
| 639 |
|
|---|
| 640 |
registerWidget(ExerciseWidget, |
|---|
| 641 |
title='Exercise Widget', |
|---|
| 642 |
description='Exercise editor Widget', |
|---|
| 643 |
used_for=('Products.LeMill.ChapterField',) |
|---|
| 644 |
) |
|---|
| 645 |
|
|---|
| 646 |
class AudioWidget(FileWidget): |
|---|
| 647 |
_properties = FileWidget._properties.copy() |
|---|
| 648 |
_properties.update({ |
|---|
| 649 |
'macro' : 'widget_audio', |
|---|
| 650 |
'helper_js' : ('AC_RunActiveContent.js',), |
|---|
| 651 |
}) |
|---|
| 652 |
|
|---|
| 653 |
registerWidget(AudioWidget, |
|---|
| 654 |
title='Audio Widget', |
|---|
| 655 |
description='Widget that uses flashplayer for mp3:s', |
|---|
| 656 |
used_for=('Products.LeMill.ChapterField',) |
|---|
| 657 |
) |
|---|
| 658 |
|
|---|
| 659 |
class LeMillLinksWidget(LeVisualWidget): |
|---|
| 660 |
_properties = LeVisualWidget._properties.copy() |
|---|
| 661 |
_properties.update({ |
|---|
| 662 |
'macro' : 'widget_lemilllinks', |
|---|
| 663 |
}) |
|---|
| 664 |
|
|---|
| 665 |
def process_form(self, instance, field, form, empty_marker=None, |
|---|
| 666 |
emptyReturnsMarker=False, validating=True): |
|---|
| 667 |
""" Replaces internal links with {uid:UID} and external links with {url} """ |
|---|
| 668 |
fieldname = field.getName() |
|---|
| 669 |
value = form.get(fieldname, empty_marker) |
|---|
| 670 |
|
|---|
| 671 |
if value.strip() == '': |
|---|
| 672 |
return None, {} |
|---|
| 673 |
|
|---|
| 674 |
portal_url = getToolByName(instance, 'portal_url') |
|---|
| 675 |
portal_url = portal_url() |
|---|
| 676 |
pc = getToolByName(instance, 'portal_catalog') |
|---|
| 677 |
from LargeSectionFolder import DEFAULT_LOCATIONS |
|---|
| 678 |
from config import CONTENT_TYPES, ACTIVITY_TYPES, TOOLS_TYPES |
|---|
| 679 |
suitable_types = CONTENT_TYPES + ACTIVITY_TYPES + TOOLS_TYPES |
|---|
| 680 |
locations = {} |
|---|
| 681 |
|
|---|
| 682 |
for d in DEFAULT_LOCATIONS.keys(): |
|---|
| 683 |
if d in suitable_types: |
|---|
| 684 |
locations[d] = DEFAULT_LOCATIONS[d] |
|---|
| 685 |
|
|---|
| 686 |
rows = value.split('\n') |
|---|
| 687 |
|
|---|
| 688 |
data = "" |
|---|
| 689 |
for row in rows: |
|---|
| 690 |
r = row |
|---|
| 691 |
row = row.strip() |
|---|
| 692 |
words = [] |
|---|
| 693 |
if row != '': |
|---|
| 694 |
words = row.split(' ') |
|---|
| 695 |
for word in words: |
|---|
| 696 |
index = words.index(word) |
|---|
| 697 |
|
|---|
| 698 |
if (word.lower().startswith(portal_url)): |
|---|
| 699 |
isMaterial = False |
|---|
| 700 |
for location, url in locations.items(): |
|---|
| 701 |
if url in word: |
|---|
| 702 |
|
|---|
| 703 |
temp = word.split(url) |
|---|
| 704 |
temp = temp[1] |
|---|
| 705 |
temp = temp.split('/') |
|---|
| 706 |
id = temp[1] |
|---|
| 707 |
|
|---|
| 708 |
query = {'getId':id,'meta_type':location} |
|---|
| 709 |
results = pc(query) |
|---|
| 710 |
if len(results)>0: |
|---|
| 711 |
words[index] = '{uid:'+results[0].UID+'}' |
|---|
| 712 |
|
|---|
| 713 |
isMaterial = True |
|---|
| 714 |
|
|---|
| 715 |
if not isMaterial: |
|---|
| 716 |
words[index] = '{'+word+'}' |
|---|
| 717 |
|
|---|
| 718 |
elif (word.lower().startswith("http://") or word.lower().startswith("https://")): |
|---|
| 719 |
words[index] = '{'+word+'}' |
|---|
| 720 |
if words != []: |
|---|
| 721 |
data += ' '.join(words) + '\n' |
|---|
| 722 |
return data, {} |
|---|
| 723 |
|
|---|
| 724 |
registerWidget(LeMillLinksWidget, |
|---|
| 725 |
title='LeMill Links Widget', |
|---|
| 726 |
description='LeMill Links Widget', |
|---|
| 727 |
used_for=('Products.LeMill.FieldsWidgets.LeMillLinksField',) |
|---|
| 728 |
) |
|---|
| 729 |
|
|---|
| 730 |
|
|---|
| 731 |
class EmbedWidget(StringWidget): |
|---|
| 732 |
_properties = StringWidget._properties.copy() |
|---|
| 733 |
_properties.update({ |
|---|
| 734 |
'macro' : 'widget_embed' |
|---|
| 735 |
}) |
|---|
| 736 |
|
|---|
| 737 |
registerWidget(EmbedWidget, |
|---|
| 738 |
title='Embed Widget', |
|---|
| 739 |
description='Embed Widget, not modifiable by common means, only displays embed code' |
|---|
| 740 |
) |
|---|
| 741 |
|
|---|
| 742 |
|
|---|
| 743 |
|
|---|
| 744 |
|
|---|
| 745 |
|
|---|
| 746 |
|
|---|
| 747 |
|
|---|
| 748 |
class TranslationField(LinesField): |
|---|
| 749 |
""" A field that stores tags """ |
|---|
| 750 |
__implements__ = LinesField.__implements__ |
|---|
| 751 |
_properties = LinesField._properties.copy() |
|---|
| 752 |
_properties.update({ |
|---|
| 753 |
'widget' : TranslationWidget, |
|---|
| 754 |
}) |
|---|
| 755 |
|
|---|
| 756 |
security = ClassSecurityInfo() |
|---|
| 757 |
|
|---|
| 758 |
def setDisabled(self, instance, value, **kwargs): |
|---|
| 759 |
value=list(value) |
|---|
| 760 |
old_value=list(self.get(instance)) |
|---|
| 761 |
if value and value==old_value: |
|---|
| 762 |
return |
|---|
| 763 |
LinesField.set(self, instance, value, **kwargs) |
|---|
| 764 |
if kwargs.get('asymmetric', False) or True: |
|---|
| 765 |
return |
|---|
| 766 |
|
|---|
| 767 |
uc=getToolByName(instance, 'uid_catalog') |
|---|
| 768 |
my_uid=instance.UID() |
|---|
| 769 |
|
|---|
| 770 |
removed=set(old_value)-set(value) |
|---|
| 771 |
for md in uc(UID=list(removed)): |
|---|
| 772 |
obj=md.getObject() |
|---|
| 773 |
field=obj.getField(self.getName()) |
|---|
| 774 |
field.remove(my_uid) |
|---|
| 775 |
|
|---|
| 776 |
added=set(value)-set(old_value) |
|---|
| 777 |
for md in uc(UID=list(added)): |
|---|
| 778 |
obj=md.getObject() |
|---|
| 779 |
field=obj.getField(self.getName()) |
|---|
| 780 |
field.add(my_uid) |
|---|
| 781 |
|
|---|
| 782 |
def add(self, instance, items): |
|---|
| 783 |
""" local helper method, doesn't traverse to linked objects """ |
|---|
| 784 |
items=list(items) |
|---|
| 785 |
old_value=list(self.get(instance)) |
|---|
| 786 |
value=list(old_value) |
|---|
| 787 |
for item in items: |
|---|
| 788 |
if item not in value: |
|---|
| 789 |
value.append(item) |
|---|
| 790 |
if value!=old_value: |
|---|
| 791 |
LinesField.set(self, instance, value) |
|---|
| 792 |
|
|---|
| 793 |
def remove(self, instance, items): |
|---|
| 794 |
""" local helper method, doesn't traverse to linked objects """ |
|---|
| 795 |
items=list(items) |
|---|
| 796 |
old_value=list(self.get(instance)) |
|---|
| 797 |
value=list(old_value) |
|---|
| 798 |
for item in items: |
|---|
| 799 |
if item in value: |
|---|
| 800 |
value.remove(item) |
|---|
| 801 |
if value!=old_value: |
|---|
| 802 |
LinesField.set(self, instance, value) |
|---|
| 803 |
|
|---|
| 804 |
security.declarePrivate('get') |
|---|
| 805 |
def get(self, instance, **kwargs): |
|---|
| 806 |
val=LinesField.get(self, instance, **kwargs) |
|---|
| 807 |
if callable(val): |
|---|
| 808 |
val=val() |
|---|
| 809 |
return val |
|---|
| 810 |
|
|---|
| 811 |
security.declarePrivate('getRaw') |
|---|
| 812 |
def getRaw(self, instance, **kwargs): |
|---|
| 813 |
val=LinesField.getRaw(self, instance, **kwargs) |
|---|
| 814 |
if val and callable(val): |
|---|
| 815 |
val=val() |
|---|
| 816 |
return val |
|---|
| 817 |
|
|---|
| 818 |
|
|---|
| 819 |
registerField(TranslationField, |
|---|
| 820 |
title='Translation Field', |
|---|
| 821 |
description=('Translation Field'), |
|---|
| 822 |
) |
|---|
| 823 |
|
|---|
| 824 |
|
|---|
| 825 |
|
|---|
| 826 |
class TagsField(LinesField): |
|---|
| 827 |
""" A field that stores tags """ |
|---|
| 828 |
__implements__ = LinesField.__implements__ |
|---|
| 829 |
_properties = LinesField._properties.copy() |
|---|
| 830 |
_properties.update({ |
|---|
| 831 |
'widget' : TagsWidget, |
|---|
| 832 |
}) |
|---|
| 833 |
|
|---|
| 834 |
security = ClassSecurityInfo() |
|---|
| 835 |
|
|---|
| 836 |
def set(self, instance, value, **kwargs): |
|---|
| 837 |
if not value: |
|---|
| 838 |
value='' |
|---|
| 839 |
if isinstance(value,str): |
|---|
| 840 |
value = value.lower() |
|---|
| 841 |
value = value.split(',') |
|---|
| 842 |
if isinstance(value, tuple) or isinstance(value, list): |
|---|
| 843 |
value=[t.strip().lower() for t in value] |
|---|
| 844 |
value=list(set(value)) |
|---|
| 845 |
value= '\n'.join(value) |
|---|
| 846 |
LinesField.set(self, instance, value, **kwargs) |
|---|
| 847 |
|
|---|
| 848 |
def getRaw(self, instance, **kwargs): |
|---|
| 849 |
version = None |
|---|
| 850 |
try: |
|---|
| 851 |
version = instance.REQUEST.get('version',None) |
|---|
| 852 |
except AttributeError: |
|---|
| 853 |
pass |
|---|
| 854 |
if version: |
|---|
| 855 |
value = instance.getFieldHistory(self.getName(), version) |
|---|
| 856 |
if value and callable(value): |
|---|
| 857 |
value=value() |
|---|
| 858 |
value = value.split(',') |
|---|
| 859 |
else: |
|---|
| 860 |
value = LinesField.get(self, instance, **kwargs) |
|---|
| 861 |
if value and callable(value): |
|---|
| 862 |
value=value() |
|---|
| 863 |
return ', '.join(value) |
|---|
| 864 |
|
|---|
| 865 |
def get(self, instance, **kwargs): |
|---|
| 866 |
version = None |
|---|
| 867 |
try: |
|---|
| 868 |
version = instance.REQUEST.get('version', None) |
|---|
| 869 |
except AttributeError: |
|---|
| 870 |
pass |
|---|
| 871 |
if version: |
|---|
| 872 |
value = instance.getFieldHistory(self.getName(), version) or '' |
|---|
| 873 |
if value and callable(value): |
|---|
| 874 |
value=value() |
|---|
| 875 |
if value: |
|---|
| 876 |
value = value.split(',') |
|---|
| 877 |
else: |
|---|
| 878 |
value = LinesField.get(self, instance, **kwargs) |
|---|
| 879 |
if value and callable(value): |
|---|
| 880 |
value=value() |
|---|
| 881 |
return value |
|---|
| 882 |
|
|---|
| 883 |
registerField(TagsField, |
|---|
| 884 |
title='Tags Field', |
|---|
| 885 |
description=('Tags Field'), |
|---|
| 886 |
) |
|---|
| 887 |
|
|---|
| 888 |
class WYSIWYMField(StringField): |
|---|
| 889 |
"""A field that stores WYSIWYM strings""" |
|---|
| 890 |
_properties = StringField._properties.copy() |
|---|
| 891 |
_properties.update({ |
|---|
| 892 |
'widget' : LeTextAreaWidget, |
|---|
| 893 |
'default_output_type' : 'text/x-html-captioned', |
|---|
| 894 |
'default_content_type' : 'text/html', |
|---|
| 895 |
}) |
|---|
| 896 |
|
|---|
| 897 |
security = ClassSecurityInfo() |
|---|
| 898 |
|
|---|
| 899 |
|
|---|
| 900 |
def getRaw(self, instance, **kwargs): |
|---|
| 901 |
""" Gets raw version for editing """ |
|---|
| 902 |
version = None |
|---|
| 903 |
try: |
|---|
| 904 |
version = instance.REQUEST.get('version',None) |
|---|
| 905 |
except AttributeError: |
|---|
| 906 |
pass |
|---|
| 907 |
if version: |
|---|
| 908 |
value = instance.getFieldHistory(self.getName(), version) |
|---|
| 909 |
else: |
|---|
| 910 |
value = StringField.get(self, instance,**kwargs) |
|---|
| 911 |
return value |
|---|
| 912 |
|
|---|
| 913 |
def get(self, instance, **kwargs): |
|---|
| 914 |
""" Gets cleaned version for display """ |
|---|
| 915 |
version = None |
|---|
| 916 |
try: |
|---|
| 917 |
version = instance.REQUEST.get('version',None) |
|---|
| 918 |
except AttributeError: |
|---|
| 919 |
pass |
|---|
| 920 |
value = self.getRaw(instance, **kwargs) |
|---|
| 921 |
cleaned_text = getattr(instance, 'cleaned_%s' % self.getName(), None) |
|---|
| 922 |
if cleaned_text and not version: |
|---|
| 923 |
return cleaned_text |
|---|
| 924 |
ltool = getToolByName(instance, 'lemill_tool') |
|---|
| 925 |
cleaned_text = ltool.parse_text(value) |
|---|
| 926 |
if not version: |
|---|
| 927 |
setattr(instance, 'cleaned_%s' % self.getName(), cleaned_text) |
|---|
| 928 |
return cleaned_text |
|---|
| 929 |
|
|---|
| 930 |
def set(self, instance, value, **kwargs): |
|---|
| 931 |
""" update the cleaned_text and then call original set """ |
|---|
| 932 |
|
|---|
| 933 |
|
|---|
| 934 |
ltool = getToolByName(instance, 'lemill_tool') |
|---|
| 935 |
cleaned_text = ltool.parse_text(value) |
|---|
| 936 |
setattr(instance, 'cleaned_%s' % self.getName(), cleaned_text) |
|---|
| 937 |
|
|---|
| 938 |
|
|---|
| 939 |
StringField.set(self, instance, value, **kwargs) |
|---|
| 940 |
|
|---|
| 941 |
|
|---|
| 942 |
|
|---|
| 943 |
|
|---|
| 944 |
|
|---|
| 945 |
registerField(WYSIWYMField, |
|---|
| 946 |
title='WYSIWYM Field', |
|---|
| 947 |
description=('WYSIWYM Field'), |
|---|
| 948 |
) |
|---|
| 949 |
|
|---|
| 950 |
|
|---|
| 951 |
class ChapterField(ObjectField): |
|---|
| 952 |
""" chapters are stored as lists, where chapter can either be a string or UID. If UID, view widget should fetch the object. """ |
|---|
| 953 |
_properties = ObjectField._properties.copy() |
|---|
| 954 |
_properties.update({ |
|---|
| 955 |
'widget' : ChapterWidget, |
|---|
| 956 |
'relationship' : 'uses', |
|---|
| 957 |
'referenceClass' : Reference, |
|---|
| 958 |
'referenceReferences' : False, |
|---|
| 959 |
'index_method':'indexValue'}) |
|---|
| 960 |
|
|---|
| 961 |
security = ClassSecurityInfo() |
|---|
| 962 |
chapter_names={'text_block':_('text chapter'), 'media_piece':_('media piece'), 'embed_block':_('embedded content'), 'multiple_choices':_('multiple choices'), 'choices':_('choices'), 'fill_in_the_blanks':_('fill in the blanks'), 'open_ended':_('open ended question'), 'exercise':_('new exercise'), 'guidelines':_('text chapter')} |
|---|
| 963 |
|
|---|
| 964 |
def getChapterNames(self): |
|---|
| 965 |
""" Return readable versions of different chapter types """ |
|---|
| 966 |
return ChapterField.chapter_names |
|---|
| 967 |
|
|---|
| 968 |
|
|---|
| 969 |
def getRaw(self, instance, version=None, **kwargs): |
|---|
| 970 |
""" Gets raw version for editing, as list """ |
|---|
| 971 |
if version: |
|---|
| 972 |
value = instance.getFieldHistory(self.getName(), version) or [] |
|---|
| 973 |
elif '_initializing_' in kwargs: |
|---|
| 974 |
value = self.getDefault(instance) or [] |
|---|
| 975 |
else: |
|---|
| 976 |
value = ObjectField.get(self, instance, **kwargs) or [] |
|---|
| 977 |
if not isinstance(value, list): |
|---|
| 978 |
value = [] |
|---|
| 979 |
if value and callable(value): |
|---|
| 980 |
value=value() |
|---|
| 981 |
for chapter in value: |
|---|
| 982 |
if isinstance(chapter, tuple): |
|---|
| 983 |
value = self.updateChapterList(value) |
|---|
| 984 |
break |
|---|
| 985 |
elif isinstance(chapter, dict) and chapter.get('type','')=='choice' and isinstance(chapter.get('text',[]), list): |
|---|
| 986 |
value=self.fixBrokenChoice(value) |
|---|
| 987 |
break |
|---|
| 988 |
return value |
|---|
| 989 |
|
|---|
| 990 |
def get(self, instance, **kwargs): |
|---|
| 991 |
""" Get a cleaned version for display """ |
|---|
| 992 |
REQUEST=getattr(instance,'REQUEST',None) |
|---|
| 993 |
if REQUEST: |
|---|
| 994 |
version=REQUEST.get('version',None) |
|---|
| 995 |
translation=REQUEST.get('translation',None) |
|---|
| 996 |
else: |
|---|
| 997 |
version=None |
|---|
| 998 |
translation=None |
|---|
| 999 |
|
|---|
| 1000 |
fix_choice=False |
|---|
| 1001 |
convert=False |
|---|
| 1002 |
if version or translation: |
|---|
| 1003 |
value = self.getRaw(instance, version=version, **kwargs) |
|---|
| 1004 |
value=self.cleanChapters(instance, value) |
|---|
| 1005 |
if translation: |
|---|
| 1006 |
value=self.mergeTranslationSource(instance,value) |
|---|
| 1007 |
return value |
|---|
| 1008 |
cleaned_value = getattr(instance, 'cleaned_%s' % self.getName(), None) |
|---|
| 1009 |
if cleaned_value: |
|---|
| 1010 |
for chapter in cleaned_value: |
|---|
| 1011 |
if isinstance(chapter, tuple): |
|---|
| 1012 |
convert=True |
|---|
| 1013 |
break |
|---|
| 1014 |
elif isinstance(chapter, dict) and 'type' in chapter and chapter['type']=='choice' and isinstance(chapter['text'], list): |
|---|
| 1015 |
fix_choice=True |
|---|
| 1016 |
break |
|---|
| 1017 |
if convert: |
|---|
| 1018 |
cleaned_value=self.updateChapterList(cleaned_value) |
|---|
| 1019 |
cleaned_value=self.updateCleanedChapters(instance, cleaned_value) |
|---|
| 1020 |
return cleaned_value |
|---|
| 1021 |
if fix_choice: |
|---|
| 1022 |
cleaned_value=self.fixBrokenChoice(cleaned_value) |
|---|
| 1023 |
cleaned_value=self.updateCleanedChapters(instance, cleaned_value) |
|---|
| 1024 |
return cleaned_value |
|---|
| 1025 |
else: |
|---|
| 1026 |
return cleaned_value |
|---|
| 1027 |
value=self.getRaw(instance, **kwargs) |
|---|
| 1028 |
cleaned_value=self.updateCleanedChapters(instance, value) |
|---|
| 1029 |
return cleaned_value |
|---|
| 1030 |
|
|---|
| 1031 |
|
|---|
| 1032 |
def textOnly(self, instance, **kwargs): |
|---|
| 1033 |
chapters=self.get(instance) |
|---|
| 1034 |
if chapters and isinstance(chapters, list): |
|---|
| 1035 |
res=[] |
|---|
| 1036 |
for ch in chapters: |
|---|
| 1037 |
if ch: |
|---|
| 1038 |
try: |
|---|
| 1039 |
res.append(to_unicode(ch['text'])) |
|---|
| 1040 |
except UnicodeDecodeError: |
|---|
| 1041 |
pass |
|---|
| 1042 |
return u'\n'.join(res) |
|---|
| 1043 |
elif chapters: |
|---|
| 1044 |
return str(chapters) |
|---|
| 1045 |
else: |
|---|
| 1046 |
return '' |
|---|
| 1047 |
|
|---|
| 1048 |
def mergeTranslationSource(self, instance, chapter_list): |
|---|
| 1049 |
""" Adds original source texts to translated chapters, under key 'original' """ |
|---|
| 1050 |
return chapter_list |
|---|
| 1051 |
|
|---|
| 1052 |
|
|---|
| 1053 |
|
|---|
| 1054 |
|
|---|
| 1055 |
def cleanChapters(self, instance, chapters): |
|---|
| 1056 |
ltool = getToolByName(instance, 'lemill_tool') |
|---|
| 1057 |
cleaned_chapters=[] |
|---|
| 1058 |
parsed_fields=['text','keywords','questions','answers'] |
|---|
| 1059 |
for chapter in chapters: |
|---|
| 1060 |
new_chapter={} |
|---|
| 1061 |
new_chapter=copy(chapter) |
|---|
| 1062 |
for key, value in chapter.items(): |
|---|
| 1063 |
if key in parsed_fields: |
|---|
| 1064 |
new_chapter[key]=ltool.parse_text(value, start_with_p=False) |
|---|
| 1065 |
cleaned_chapters.append(new_chapter) |
|---|
| 1066 |
return cleaned_chapters |
|---|
| 1067 |
|
|---|
| 1068 |
|
|---|
| 1069 |
def updateCleanedChapters(self, instance, chapters): |
|---|
| 1070 |
cleaned_chapters=self.cleanChapters(instance, chapters) |
|---|
| 1071 |
setattr(instance, 'cleaned_%s' % self.getName(), cleaned_chapters) |
|---|
| 1072 |
return cleaned_chapters |
|---|
| 1073 |
|
|---|
| 1074 |
|
|---|
| 1075 |
def _updatePilot(self, old_values): |
|---|
| 1076 |
""" in new dictionary based chapters it makes sense to use one chapter per slide, instead of three chapters |
|---|
| 1077 |
this method converts old chapter lists into new format """ |
|---|
| 1078 |
new_scene=True |
|---|
| 1079 |
updated_values=[] |
|---|
| 1080 |
for cvalue, ctype in old_values: |
|---|
| 1081 |
if new_scene: |
|---|
| 1082 |
scene={'text':'', 'image_uid':'', 'audio_uid':'', 'keywords':[], 'questions':[], 'type':'pilot_scene'} |
|---|
| 1083 |
new_scene=False |
|---|
| 1084 |
if ctype=='audio_piece': |
|---|
| 1085 |
scene['audio_uid']=cvalue |
|---|
| 1086 |
elif ctype=='image_piece': |
|---|
| 1087 |
scene['image_uid']=cvalue |
|---|
| 1088 |
elif ctype=='pilot_keywords': |
|---|
| 1089 |
scene['keywords']=cvalue |
|---|
| 1090 |
updated_values.append(scene) |
|---|
| 1091 |
new_scene=True |
|---|
| 1092 |
elif ctype=='pilot_questions': |
|---|
| 1093 |
scene['questions']=cvalue |
|---|
| 1094 |
updated_values.append(scene) |
|---|
| 1095 |
new_scene=True |
|---|
| 1096 |
return updated_values |
|---|
| 1097 |
|
|---|
| 1098 |
def fixBrokenChoice(self, old_values): |
|---|
| 1099 |
new_values=[] |
|---|
| 1100 |
for vald in old_values: |
|---|
| 1101 |
if vald['type']=='choice' and isinstance(vald['text'], list): |
|---|
| 1102 |
|
|---|
| 1103 |
print vald |
|---|
| 1104 |
text_list=vald['text'] |
|---|
| 1105 |
vald['text']=text_list[0] |
|---|
| 1106 |
correct_choices=text_list[1] |
|---|
| 1107 |
if not isinstance(correct_choices, list): |
|---|
| 1108 |
correct_choices=[correct_choices] |
|---|
| 1109 |
wrong_choices=text_list[2] |
|---|
| 1110 |
if not isinstance(wrong_choices, list): |
|---|
| 1111 |
wrong_choices=[wrong_choices] |
|---|
| 1112 |
choices=[] |
|---|
| 1113 |
for item in correct_choices: |
|---|
| 1114 |
choices.append((item, 1)) |
|---|
| 1115 |
for item in wrong_choices: |
|---|
| 1116 |
choices.append((item, 0)) |
|---|
| 1117 |
vald['choices']=choices |
|---|
| 1118 |
vald['type']='multiple_choices' |
|---|
| 1119 |
new_values.append(vald) |
|---|
| 1120 |
return new_values |
|---|
| 1121 |
|
|---|
| 1122 |
|
|---|
| 1123 |
def updateChapterList(self, old_values): |
|---|
| 1124 |
""" Backward compatibility: |
|---|
| 1125 |
Converts a chapterlist to use dicts instead of tuples. Returns updated list """ |
|---|
| 1126 |
updated_values=[] |
|---|
| 1127 |
if 'pilot_keywords' in [ct for cv, ct in old_values]: |
|---|
| 1128 |
return self._updatePilot(old_values) |
|---|
| 1129 |
for cvalue, ctype in old_values: |
|---|
| 1130 |
if ctype in ['multiple_choices','poll','choices', 'choice']: |
|---|
| 1131 |
q,correct,wrong=cvalue |
|---|
| 1132 |
if ctype=='multiple_choices': |
|---|
| 1133 |
choices=[(c, 1) for c in correct] |
|---|
| 1134 |
choices+=[(w, 0) for w in wrong] |
|---|
| 1135 |
shuffle(choices) |
|---|
| 1136 |
elif ctype=='choices' or ctype=='choice': |
|---|
| 1137 |
if correct and isinstance(correct, list): |
|---|
| 1138 |
correct=correct[0] |
|---|
| 1139 |
choices=[(correct,1)] |
|---|
| 1140 |
choices+=[(w, 0) for w in wrong] |
|---|
| 1141 |
shuffle(choices) |
|---|
| 1142 |
ctype='multiple_choices' |
|---|
| 1143 |
elif ctype=='poll': |
|---|
| 1144 |
choices=[(c,1) for c in correct+wrong] |
|---|
| 1145 |
ctype='multiple_choices' |
|---|
| 1146 |
new_chapter={'text':q, 'type':ctype, 'choices':choices, 'max_points':1.0} |
|---|
| 1147 |
elif ctype in ['fill_in_the_blanks', 'open_ended']: |
|---|
| 1148 |
new_chapter={'text':cvalue, 'type':ctype, 'max_points':1.0} |
|---|
| 1149 |
elif ctype in ['image_piece','audio_piece','media_piece']: |
|---|
| 1150 |
new_chapter={'text':'', 'type':ctype, 'uid':cvalue} |
|---|
| 1151 |
elif ctype=='embed_block': |
|---|
| 1152 |
new_chapter={'text':'', 'type':'embed_block', 'embed':cvalue} |
|---|
| 1153 |
elif ctype=='guidelines': |
|---|
| 1154 |
new_chapter={'text':cvalue, 'type':'text_block'} |
|---|
| 1155 |
else: |
|---|
| 1156 |
new_chapter={'text':cvalue, 'type':ctype} |
|---|
| 1157 |
changed=True |
|---|
| 1158 |
updated_values.append(new_chapter) |
|---|
| 1159 |
return updated_values |
|---|
| 1160 |
|
|---|
| 1161 |
def set(self, instance, value, **kwargs): |
|---|
| 1162 |
if not value: |
|---|
| 1163 |
return |
|---|
| 1164 |
lt = getToolByName(instance, 'lemill_tool') |
|---|
| 1165 |
changed=False |
|---|
| 1166 |
if kwargs.get('_initializing_', False): |
|---|
| 1167 |
old_values=self.getDefault(instance) |
|---|
| 1168 |
changed=True |
|---|
| 1169 |
else: |
|---|
| 1170 |
old_values=self.getRaw(instance, **kwargs) |
|---|
| 1171 |
references_to_add=[] |
|---|
| 1172 |
references_to_remove=[] |
|---|
| 1173 |
|
|---|
| 1174 |
if isinstance(value, dict): |
|---|
| 1175 |
edited=value['edited'] |
|---|
| 1176 |
new_values=copy(old_values) |
|---|
| 1177 |
|
|---|
| 1178 |
if edited!=-1: |
|---|
| 1179 |
ctype=value['chapter_type'] |
|---|
| 1180 |
old_chapter=old_values[edited] |
|---|
| 1181 |
|
|---|
| 1182 |
new_chapter={'type':ctype} |
|---|
| 1183 |
if ctype in ['multiple_choices','choices','poll']: |
|---|
| 1184 |
new_chapter['text']=value['question'] |
|---|
| 1185 |
choices=[] |
|---|
| 1186 |
for answer in value['answers']: |
|---|
| 1187 |
choices.append((answer['order'], answer['text'], answer['is_correct'])) |
|---|
| 1188 |
choices.sort() |
|---|
| 1189 |
choices=[(y,z) for (x,y,z) in choices] |
|---|
| 1190 |
new_chapter['choices']=choices |
|---|
| 1191 |
new_chapter['max_points']=value.get('max_points',1.0) |
|---|
| 1192 |
elif ctype=='pilot_scene': |
|---|
| 1193 |
new_chapter['text']=u'\n'.join(value['keywords'] or value['questions']) |
|---|
| 1194 |
if value['audio_file']: |
|---|
| 1195 |
new_piece = lt.createPieceFromFile(value['audio_file'], instance) |
|---|
| 1196 |
uid=new_piece.UID() |
|---|
| 1197 |
else: |
|---|
| 1198 |
uid=value['audio_uid'] |
|---|
| 1199 |
old_uid=old_chapter.get('audio_uid','') |
|---|
| 1200 |
if old_uid: |
|---|
| 1201 |
if old_uid!=uid: |
|---|
| 1202 |
references_to_remove.append(old_uid) |
|---|
| 1203 |
references_to_add.append(uid) |
|---|
| 1204 |
else: |
|---|
| 1205 |
references_to_add.append(uid) |
|---|
| 1206 |
new_chapter['audio_uid']=uid |
|---|
| 1207 |
if value['image_file']: |
|---|
| 1208 |
new_piece = lt.createPieceFromFile(value['image_file'], instance) |
|---|
| 1209 |
uid=new_piece.UID() |
|---|
| 1210 |
else: |
|---|
| 1211 |
uid=value['image_uid'] |
|---|
| 1212 |
old_uid=old_chapter.get('image_uid','') |
|---|
| 1213 |
if old_uid: |
|---|
| 1214 |
if old_uid!=uid: |
|---|
| 1215 |
references_to_remove.append(old_uid) |
|---|
| 1216 |
references_to_add.append(uid) |
|---|
| 1217 |
else: |
|---|
| 1218 |
references_to_add.append(uid) |
|---|
| 1219 |
new_chapter['image_uid']=uid |
|---|
| 1220 |
new_chapter['questions']=value['questions'] |
|---|
| 1221 |
new_chapter['keywords']=value['keywords'] |
|---|
| 1222 |
elif ctype in ['image_piece','audio_piece','media_piece']: |
|---|
| 1223 |
|
|---|
| 1224 |
uid=value['uid'] |
|---|
| 1225 |
old_uid=old_chapter.get('uid','') |
|---|
| 1226 |
if old_uid: |
|---|
| 1227 |
if old_uid!=uid: |
|---|
| 1228 |
references_to_remove.append(old_uid) |
|---|
| 1229 |
if uid: |
|---|
| 1230 |
references_to_add.append(uid) |
|---|
| 1231 |
elif uid: |
|---|
| 1232 |
references_to_add.append(uid) |
|---|
| 1233 |
new_chapter['uid']=uid |
|---|
| 1234 |
new_chapter['text']=value.get('caption','') |
|---|
| 1235 |
elif ctype == 'embed_block': |
|---|
| 1236 |
new_chapter['text']='' |
|---|
| 1237 |
if 'embed_old' in value and value['embed_old'] and not value['embed']: |
|---|
| 1238 |
new_chapter['embed']=value['embed_old'] |
|---|
| 1239 |
else: |
|---|
| 1240 |
new_chapter['embed']=value['embed'] |
|---|
| 1241 |
elif ctype in ['fill_in_the_blanks', 'open_ended']: |
|---|
| 1242 |
new_chapter['text']=value['text'] |
|---|
| 1243 |
new_chapter['max_points']=value.get('max_points',1.0) |
|---|
| 1244 |
else: |
|---|
| 1245 |
new_chapter['text']=value['text'] |
|---|
| 1246 |
if new_chapter!=old_chapter: |
|---|
| 1247 |
changed=True |
|---|
| 1248 |
new_values[edited]=new_chapter |
|---|
| 1249 |
|
|---|
| 1250 |
delete=value.has_key('delete_chapter') |
|---|
| 1251 |
n_o=value.get('new_order',[]) |
|---|
| 1252 |
if delete: |
|---|
| 1253 |
del_i=value['delete_chapter'] |
|---|
| 1254 |
if del_i<len(new_values): |
|---|
| 1255 |
removed=new_values.pop(del_i) |
|---|
| 1256 |
if removed.get('uid',''): |
|---|
| 1257 |
references_to_remove.append(removed['uid']) |
|---|
| 1258 |
changed=True |
|---|
| 1259 |
if n_o and del_i in n_o: |
|---|
| 1260 |
n_o.pop(n_o.index(del_i)) |
|---|
| 1261 |
changed=True |
|---|
| 1262 |
|
|---|
| 1263 |
if n_o: |
|---|
| 1264 |
sortable=zip(n_o, new_values) |
|---|
| 1265 |
sortable.sort() |
|---|
| 1266 |
new_values=[val for o, val in sortable] |
|---|
| 1267 |
changed=True |
|---|
| 1268 |
|
|---|
| 1269 |
new_chapters=value.get('new_chapters',[]) |
|---|
| 1270 |
if new_chapters: |
|---|
| 1271 |
new_values+=new_chapters |
|---|
| 1272 |
changed=True |
|---|
| 1273 |
|
|---|
| 1274 |
elif isinstance(value, list): |
|---|
| 1275 |
references_new=[] |
|---|
| 1276 |
for chapter in value: |
|---|
| 1277 |
uid=chapter.get('uid','') |
|---|
| 1278 |
if uid: |
|---|
| 1279 |
references_new.append(uid) |
|---|
| 1280 |
references_old=[] |
|---|
| 1281 |
for chapter in old_values: |
|---|
| 1282 |
uid=chapter.get('uid','') |
|---|
| 1283 |
if uid: |
|---|
| 1284 |
references_old.append(uid) |
|---|
| 1285 |
references_to_add=[uid for uid in references_new if uid not in references_old] |
|---|
| 1286 |
references_to_remove=[uid for uid in references_old if uid not in references_new] |
|---|
| 1287 |
if value!=old_values: |
|---|
| 1288 |
changed=True |
|---|
| 1289 |
new_values=value |
|---|
| 1290 |
|
|---|
| 1291 |
if references_to_add or references_to_remove: |
|---|
| 1292 |
tool = instance.reference_catalog |
|---|
| 1293 |
for uid in references_to_add: |
|---|
| 1294 |
tool.addReference(instance, uid, self.relationship) |
|---|
| 1295 |
for uid in references_to_remove: |
|---|
| 1296 |
tool.deleteReference(instance, uid, self.relationship) |
|---|
| 1297 |
if changed: |
|---|
| 1298 |
|
|---|
| 1299 |
self.updateCleanedChapters(instance, new_values) |
|---|
| 1300 |
|
|---|
| 1301 |
ObjectField.set(self, instance, new_values, **kwargs) |
|---|
| 1302 |
|
|---|
| 1303 |
def getObjectByUID(self, instance, uid, catalog_only=False): |
|---|
| 1304 |
""" Helper method for fetching mediapieces from references """ |
|---|
| 1305 |
uid_catalog = getToolByName(instance, 'uid_catalog') |
|---|
| 1306 |
if not uid: |
|---|
| 1307 |
return None |
|---|
| 1308 |
results=uid_catalog(UID=uid) |
|---|
| 1309 |
try: |
|---|
| 1310 |
return results[0].getObject() |
|---|
| 1311 |
except IndexError: |
|---|
| 1312 |
return None |
|---|
| 1313 |
|
|---|
| 1314 |
|
|---|
| 1315 |
def isOEmbedChapter(self, chapter): |
|---|
| 1316 |
""" Check if this chapter should use oembed query or just paste embed code """ |
|---|
| 1317 |
if not ('type' in chapter and chapter['type']=='embed_block'): |
|---|
| 1318 |
return False |
|---|
| 1319 |
code=chapter['embed'] |
|---|
| 1320 |
try: |
|---|
| 1321 |
parsetuple=urlparse(code) |
|---|
| 1322 |
return parsetuple[0]=='http' or parsetuple[0]=='https' |
|---|
| 1323 |
except TypeError: |
|---|
| 1324 |
return False |
|---|
| 1325 |
return False |
|---|
| 1326 |
|
|---|
| 1327 |
def isGoogleDocs(self, chapter): |
|---|
| 1328 |
""" OEmbed doesn't recognize google docs, but we can fill them in manually """ |
|---|
| 1329 |
def _getParam(query, key): |
|---|
| 1330 |
query_list=query.split('&') |
|---|
| 1331 |
for pair in query_list: |
|---|
| 1332 |
k,v = pair.split('=') |
|---|
| 1333 |
if k==key: |
|---|
| 1334 |
return v |
|---|
| 1335 |
return '' |
|---|
| 1336 |
code=chapter['embed'] |
|---|
| 1337 |
try: |
|---|
| 1338 |
adscheme, network, path, params, query, frag = urlparse(code) |
|---|
| 1339 |
except TypeError: |
|---|
| 1340 |
return '' |
|---|
| 1341 |
if network=='docs.google.com' or (network.startswith('docs') and network.endswith('.google.com')): |
|---|
| 1342 |
try: |
|---|
| 1343 |
docid='' |
|---|
| 1344 |
path_list=path.split('/') |
|---|
| 1345 |
if 'present' in path_list: |
|---|
| 1346 |
docid=_getParam(query,'id') |
|---|
| 1347 |
if docid: |
|---|
| 1348 |
return '<iframe src="https://docs.google.com/present/embed?id=%s" frameborder="0" width="700" height="559"></iframe>' % docid |
|---|
| 1349 |
elif 'drawings' in path_list: |
|---|
| 1350 |
docid=_getParam(query,'id') |
|---|
| 1351 |
try: |
|---|
| 1352 |
w=int(_getParam(query, 'w') or 960) |
|---|
| 1353 |
h=int(_getParam(query, 'h') or 720) |
|---|
| 1354 |
except ValueError: |
|---|
| 1355 |
w=960 |
|---|
| 1356 |
h=720 |
|---|
| 1357 |
if docid: |
|---|
| 1358 |
return '<img src="https://docs.google.com/drawings/pub?id=%s&w=%s&h=%s">' % (docid, w,h) |
|---|
| 1359 |
elif 'Doc' in path_list: |
|---|
| 1360 |
docid=_getParam(query,'docid') |
|---|
| 1361 |
elif 'd' in path_list: |
|---|
| 1362 |
docid=path_list[2] |
|---|
| 1363 |
elif 'pub' in path_list: |
|---|
| 1364 |
docid=_getParam(query,'id') |
|---|
| 1365 |
except IndexError: |
|---|
| 1366 |
return '' |
|---|
| 1367 |
if docid: |
|---|
| 1368 |
return '<iframe src="https://docs.google.com/document/pub?id=%s&embedded=true" width="760" height="564"></iframe>' % docid |
|---|
| 1369 |
elif network=='spreadsheets.google.com' or (network.startswith('spreadsheets') and network.endswith('.google.com')): |
|---|
| 1370 |
path_list=path.split('/') |
|---|
| 1371 |
if 'viewform' in path_list: |
|---|
| 1372 |
docid=_getParam(query, 'formkey') |
|---|
| 1373 |
return '<iframe src="https://spreadsheets.google.com/embeddedform?formkey=%s" width="760" height="564" frameborder="0" marginheight="0" marginwidth="0">Loading...</iframe>' % docid |
|---|
| 1374 |
elif 'gform' in path_list: |
|---|
| 1375 |
docid=_getParam(query, 'key') |
|---|
| 1376 |
return '<iframe src="https://spreadsheets.google.com/embeddedform?formkey=%s" width="760" height="564" frameborder="0" marginheight="0" marginwidth="0">Loading...</iframe>' % docid |
|---|
| 1377 |
else: |
|---|
| 1378 |
docid=_getParam(query, 'key') |
|---|
| 1379 |
return "<iframe width='760' height='564' frameborder='0' src='https://spreadsheets.google.com/pub?key=%s&output=html&widget=true'></iframe>" % docid |
|---|
| 1380 |
return '' |
|---|
| 1381 |
|
|---|
| 1382 |
def getLength(self, piece): |
|---|
| 1383 |
as_time=piece.getLength() |
|---|
| 1384 |
return {'hour':as_time/3600,'minute':(as_time % 3600)/60,'second':as_time % 60} |
|---|
| 1385 |
|
|---|
| 1386 |
def hasOneCorrectChoice(self, chapter): |
|---|
| 1387 |
""" Check if this exercise is multiple choice (-> use checkboxes) or has only one correct answer (-> use radio buttons) """ |
|---|
| 1388 |
corrects=0 |
|---|
| 1389 |
for q,c in chapter['choices']: |
|---|
| 1390 |
if c: |
|---|
| 1391 |
corrects+=1 |
|---|
| 1392 |
return corrects==1 |
|---|
| 1393 |
|
|---|
| 1394 |
|
|---|
| 1395 |
|
|---|
| 1396 |
registerField(ChapterField, |
|---|
| 1397 |
title='Chapter field', |
|---|
| 1398 |
description=('Chapter field'), |
|---|
| 1399 |
) |
|---|
| 1400 |
|
|---|
| 1401 |
class AudioField(FileField): |
|---|
| 1402 |
""" field for mp3:s """ |
|---|
| 1403 |
_properties = FileField._properties.copy() |
|---|
| 1404 |
_properties.update({ |
|---|
| 1405 |
'widget' : AudioWidget, |
|---|
| 1406 |
}) |
|---|
| 1407 |
|
|---|
| 1408 |
def getLength(self, instance): |
|---|
| 1409 |
i = aq_base(instance) |
|---|
| 1410 |
file = AudioField.get(self,instance) |
|---|
| 1411 |
if not shasattr(i, '_mp3length'): |
|---|
| 1412 |
instance._mp3length=get_length(str(file)) |
|---|
| 1413 |
if instance._mp3length==0: |
|---|
| 1414 |
instance._mp3length=get_length(str(file)) |
|---|
| 1415 |
return {'hour':instance._mp3length/3600,'minute':(instance._mp3length % 3600)/60,'second':instance._mp3length % 60} |
|---|
| 1416 |
|
|---|
| 1417 |
def set(self, instance, value, **kwargs): |
|---|
| 1418 |
""" allow only audio here """ |
|---|
| 1419 |
FileField.set(self, instance, value, **kwargs) |
|---|
| 1420 |
file=self.get(instance) |
|---|
| 1421 |
if self.getContentType(instance).startswith('audio/'): |
|---|
| 1422 |
instance._mp3length=get_length(str(file)) |
|---|
| 1423 |
elif str(file)!='': |
|---|
| 1424 |
FileField.set(self, instance, "DELETE_FILE", **kwargs) |
|---|
| 1425 |
|
|---|
| 1426 |
registerField(AudioField, |
|---|
| 1427 |
title='Audio field', |
|---|
| 1428 |
description=('Field that accepts only mp3:s'), |
|---|
| 1429 |
) |
|---|
| 1430 |
|
|---|
| 1431 |
class LeMillLinksField(ObjectField): |
|---|
| 1432 |
""" A field that stores LeMill internal links """ |
|---|
| 1433 |
__implements__ = ObjectField.__implements__ |
|---|
| 1434 |
_properties = ObjectField._properties.copy() |
|---|
| 1435 |
_properties.update({ |
|---|
| 1436 |
'widget' : LeMillLinksWidget, |
|---|
| 1437 |
}) |
|---|
| 1438 |
|
|---|
| 1439 |
security = ClassSecurityInfo() |
|---|
| 1440 |
|
|---|
| 1441 |
def set(self, instance, value, **kwargs): |
|---|
| 1442 |
ObjectField.set(self, instance, value, **kwargs) |
|---|
| 1443 |
|
|---|
| 1444 |
def getRaw(self, instance, **kwargs): |
|---|
| 1445 |
version = None |
|---|
| 1446 |
try: |
|---|
| 1447 |
version = instance.REQUEST.get('version', None) |
|---|
| 1448 |
except AttributeError: |
|---|
| 1449 |
pass |
|---|
| 1450 |
if version: |
|---|
| 1451 |
value = instance.getFieldHistory(self.getName(), version) |
|---|
| 1452 |
else: |
|---|
| 1453 |
value = ObjectField.get(self, instance, **kwargs) |
|---|
| 1454 |
if value and callable(value): |
|---|
| 1455 |
value=value() |
|---|
| 1456 |
return value |
|---|
| 1457 |
|
|---|
| 1458 |
def getRawEdit(self, instance, **kwargs): |
|---|
| 1459 |
""" Returns a text for editing """ |
|---|
| 1460 |
pc = getToolByName(instance, 'portal_catalog') |
|---|
| 1461 |
field_data = ObjectField.get(self, instance, **kwargs) |
|---|
| 1462 |
if field_data == None: |
|---|
| 1463 |
return "" |
|---|
| 1464 |
|
|---|
| 1465 |
links = re.findall('\{.*?\}', field_data) |
|---|
| 1466 |
for link in links: |
|---|
| 1467 |
linktext = link[1:-1] |
|---|
| 1468 |
if linktext.lower().startswith('http://') or linktext.lower().startswith('https://'): |
|---|
| 1469 |
field_data = field_data.replace(link, linktext) |
|---|
| 1470 |
elif linktext.lower().startswith('uid:'): |
|---|
| 1471 |
uid = linktext[4:] |
|---|
| 1472 |
results = pc({'UID':uid}) |
|---|
| 1473 |
if len(results)>0: |
|---|
| 1474 |
field_data = field_data.replace(link, results[0].getURL()) |
|---|
| 1475 |
else: |
|---|
| 1476 |
field_data = field_data.replace(link, '') |
|---|
| 1477 |
return field_data |
|---|
| 1478 |
|
|---|
| 1479 |
def get(self, instance, **kwargs): |
|---|
| 1480 |
pc = getToolByName(instance, 'portal_catalog') |
|---|
| 1481 |
letool = getToolByName(instance,'lemill_tool') |
|---|
| 1482 |
version = None |
|---|
| 1483 |
try: |
|---|
| 1484 |
version = instance.REQUEST.get('version',None) |
|---|
| 1485 |
except AttributeError: |
|---|
| 1486 |
pass |
|---|
| 1487 |
if version: |
|---|
| 1488 |
field_data = instance.getFieldHistory(self.getName(), version) |
|---|
| 1489 |
else: |
|---|
| 1490 |
field_data = ObjectField.get(self, instance, **kwargs) |
|---|
| 1491 |
if not field_data: |
|---|
| 1492 |
return "" |
|---|
| 1493 |
if callable(field_data): |
|---|
| 1494 |
field_data=field_data() |
|---|
| 1495 |
links = re.findall('\{.*?\}', field_data) |
|---|
| 1496 |
for link in links: |
|---|
| 1497 |
linktext = link[1:-1] |
|---|
| 1498 |
if linktext.lower().startswith('http://') or linktext.lower().startswith('https://'): |
|---|
| 1499 |
repl='<a href="%s">%s</a>' % (linktext, letool.shorten_url(linktext)) |
|---|
| 1500 |
field_data = field_data.replace(link, repl) |
|---|
| 1501 |
elif linktext.lower().startswith('uid:'): |
|---|
| 1502 |
uid = linktext[4:] |
|---|
| 1503 |
results = pc({'UID':uid}) |
|---|
| 1504 |
repl='' |
|---|
| 1505 |
if results: |
|---|
| 1506 |
repl='<a href="%s">%s</a>' % (results[0].getURL(), results[0].Title) |
|---|
| 1507 |
field_data = field_data.replace(link, repl) |
|---|
| 1508 |
field_data = field_data.strip() |
|---|
| 1509 |
value = field_data.split('\n') |
|---|
| 1510 |
return value |
|---|
| 1511 |
|
|---|
| 1512 |
registerField(LeMillLinksField, |
|---|
| 1513 |
title='LeMill Links Field', |
|---|
| 1514 |
description=('LeMill Links Field'), |
|---|
| 1515 |
) |
|---|