root/trunk/LargeSectionFolder.py

Revision 3192, 36.7 kB (checked in by jukka, 2 weeks ago)

Fixed #2033. Unwanted becoming second author can be fixed manually and the formula for author order is simplified. It is just the order of editing.

Line 
1 # Copyright 2006 by the LeMill Team (see AUTHORS)
2 #
3 # This file is part of LeMill.
4 #
5 # LeMill is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # LeMill is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with LeMill; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19 from config import *
20 from Products.Archetypes.public import *
21 from Products.ATContentTypes.content.folder import ATFolder, ATBTreeFolderSchema, ATBTreeFolder
22 from Products.Archetypes.public import registerType
23 from Products.CMFDefault.exceptions import DiscussionNotAllowed
24 from Products.CMFCore.utils import getToolByName
25 from Products.CMFPlone.utils import transaction_note
26 from ZPublisher.HTTPRequest import FileUpload
27 from DocumentTemplate import sequence
28 from random import sample, shuffle
29 from DateTime import DateTime
30 import re, datetime, urllib, cgi
31 from AccessControl import ClassSecurityInfo
32 from permissions import MODIFY_CONTENT, ADD_CONTENT_PERMISSION, ModerateContent
33 from messagefactory_ import i18nme as _
34 from CommonMixIn import CommonMixIn
35 from Products.ZCatalog.Lazy import Lazy
36 from config import to_unicode
37 import xml.dom.minidom
38
39 import time,copy,logging,transaction
40
41 LOG = logging.getLogger('Zope.ZCatalog')
42
43 # This is a hack since I can't figure a way to get the class object of our content types before creating an instance of that class. So we have a dictionary we can use instead.
44 DEFAULT_LOCATIONS = {
45     'Piece':'content/pieces',
46     'LeMillReference':'content/references',
47     'LeMillPrintResource':'content/printresources',
48     'MultimediaMaterial':'content/webpages',
49     'ExerciseMaterial':'content/exercises',
50     'PILOTMaterial':'content/pilots',
51     'PresentationMaterial':'content/presentations',
52     'LessonPlan':'content/lessonplans',
53     'SchoolProjectMaterial':'content/schoolprojects',
54     'Activity':'methods',
55     'Tool':'tools',
56     'MemberFolder':'community/people',
57     'GroupBlog':'community/groups'
58     }
59
60
61 communityschema= ATBTreeFolderSchema + Schema((
62     LinesField('collaboration_proposals',
63         default= [],
64         widget = LinesWidget(
65             visible = {'view':'invisible', 'edit':'invisible'},
66             )
67         )
68
69 ))
70
71
72 class LeMillFolder:
73     """ Methods common to all folderish objects """
74
75     security = ClassSecurityInfo()
76
77     #security.declareProtected(ADD_CONTENT_PERMISSION, 'invokeFactory')
78     def invokeFactory(self, type_name, id, RESPONSE=None, *args, **kw):
79         """ Checks that the path of object creation is reasonable,
80         then invokes the portal_types tool and finally sets state.
81         """
82         pt = getToolByName(self, 'portal_types')
83         id=pt.constructContent(type_name, self, id, RESPONSE, *args, **kw)
84         if id:
85             obj= getattr(self, id)
86             if obj.hasComplexWorkflow():
87                 if obj.getHideDrafts():
88                     obj.setState('private')
89                 else:
90                     obj.setState('draft')
91             else:
92                 try:
93                     obj.setState('public')
94                 except:
95                     pass
96             # Setting permissions to newly created object
97             # This should override the ones set by workflow
98             obj.setPermissions()
99         return id
100
101     def getDefaultPathFor(self, portal_type, as_list=False):
102         """ Returns the path that this object should have if it is in its right place (not trashed) """       
103         #print 'physical: %s' % '/'.join( self.getPhysicalPath() )
104         path= DEFAULT_LOCATIONS.get(portal_type, None)
105         if not path:
106             return None
107         path='/%s/%s' % (self.getPhysicalPath()[1],path)
108         #print 'default: %s' % path
109         if as_list:
110             return path.split('/')
111         else:
112             return path
113
114     def getFolderContents(self):
115         """ This shouldn't be needed """
116         return []
117        
118
119 class LargeSectionFolder(CommonMixIn, LeMillFolder, ATBTreeFolder):
120     """Section Folder"""
121        
122     archetype_name = "Large Section Folder"
123     meta_type = "Large Section Folder"
124     security = ClassSecurityInfo()
125     isAnObjectManager = 1
126     global_allow= 1
127     portlet=''
128     subsections=[]
129
130
131
132     ############### Not found redirector #####################
133
134 #    def __bobo_traverse__(self, REQUEST, entry_name=None):
135 #        """ try looking into subfolder if traverse is otherwise failing """
136 #        try:
137 #            l = BaseObject.__bobo_traverse__(self, REQUEST, entry_name)
138 #            return l
139 #        except AttributeError, entry_name:
140 #            print 'looking for %s' % entry_name
141 #            raise AttributeError(entry_name)       
142 #
143 #
144 #
145 #    def __aabobo_traverse__(self, REQUEST, entry_name=None):
146 #        """ try looking into subfolder if traverse is otherwise failing """
147 #        try:
148 #            l = BaseObject.__bobo_traverse__(self, REQUEST, entry_name)
149 #        except AttributeError:
150 #            print 'looking for %s' % entry_name
151 #            l = self.lookInSubsections(entry_name)
152 #            if l:
153 #                l.REQUEST.RESPONSE.redirect(l.absolute_url(), status=301)
154 #            else:
155 #                raise AttributeError(entry_name)
156 #        #if hasattr(l, 'im_func'):
157 #        #    return l()
158 #        return l
159
160    
161     def lookInSubsections(self, obj_id):
162         """ Check if subsection has this object and redirect if does """
163         for sub in self.__class__.subsections:
164             if sub!=self.getId():
165                 sub=getattr(self, sub)
166                 if sub.has_key(obj_id):
167                     return getattr(sub, obj_id)
168         raise AttributeError(obj_id)
169
170
171     ############### Misc. useful getters #####################
172
173
174     def fixMissingCatalogObjects(self, portal_type, start=0, end=10000):
175         """ check if cataloged uid matches with object uid """
176         pc = getToolByName(self, 'portal_catalog')
177         results = pc({'portal_type':portal_type})
178         start=int(start)
179         end=int(end)
180         c=0
181         f=0
182         for r in list(results):
183             c=c+1
184             if c<start:
185                 continue
186             if c>end:
187                 break
188             o=None
189             try:
190                 o= r.getObject()
191             except:
192                 f=f+1
193                 print '%s failed to get object %s' % (str(c), r.getPath())
194                 pc.uncatalog_object(r.getPath())
195             if o and o.UID()!=r.UID:
196                 print "%s UID mismatch: object %s's uid is %s but metadata is %s" % (str(c), r.getId, o.UID(), r.UID)
197             else:
198                 print ".",
199         print '%s failures out of %s catalog objects' % (str(f), str(c))
200
201     def getAllContentTypes(self):
202         """ returns a list of all content types """
203         return ALL_CONTENT_TYPES
204
205     def getMaterialTypes(self, no_references=False):
206         """ returns a list of material types """
207         if no_references:
208             return [x for x in MATERIAL_TYPES if x!='LeMillReference']
209         else:
210             return MATERIAL_TYPES
211
212     def getContentTypes(self):
213         """ returns a list of material types """
214         return CONTENT_TYPES
215
216     def getActivityTypes(self):
217         """ returns a list of activity types """
218         return ACTIVITY_TYPES
219
220     def getToolsTypes(self):
221         """ returns a list of activity types """
222         return TOOLS_TYPES
223
224     def getFeaturedTypes(self):
225         """ returns a list of featured types """
226         return MATERIAL_TYPES + ACTIVITY_TYPES + TOOLS_TYPES
227
228     def canDeleteOnCancel(self):
229         """ Should be called on resource contexts, so NO """
230         return False
231
232
233     def prettyMethodNameDictionary(self):
234         """ Helper method for finding displayable names for search fields """
235         # better to do this in config..
236         return FIELD_METHODS_DISPLAY_STRINGS                     
237
238
239     def getNameOfResourceType(self,restype, lower=True):
240         """Get human-readable name of resource - first try TYPE_NAMES, then TEMPLATES"""
241         name=TYPE_NAMES.get(restype, restype)
242         if isinstance(name, tuple):
243             name=name[0]       
244         if lower:
245             return name.lower()
246         else:
247             return name
248
249     def getSectionFolder(self, bottom=False):
250         """ return this or if bottom, then tries to look if there is a section folders beyond this """
251         if not bottom:
252             return self
253         if isinstance(self.aq_parent,LargeSectionFolder):
254             return self.aq_parent.getSectionFolder(bottom=True)
255         else:
256             return self
257
258     def amIManager(self):
259         """Check whether I'm a manager."""
260         lutool=getToolByName(self, 'lemill_usertool')       
261         roles = lutool.getAuthenticatedMember().getRolesInContext(self)
262         return 'Manager' in roles
263
264     def canIModerate(self):
265         lutool=getToolByName(self, 'lemill_usertool')       
266         roles = lutool.getAuthenticatedMember().getRolesInContext(self)
267         return 'Manager' in roles or 'Reviewer' in roles
268
269     def url_quote(self,word):
270         return urllib.quote(word)
271
272
273     def getDefaultIcon(self, meta_type, obj=None):
274         """ general method for getting proper icon for object, used when only catalog-metadata is available """       
275         address=DEFAULT_ICONS[meta_type]
276         if address!='piece':
277             return address
278         else:
279             try:
280                 # getObject verified
281                 obj=obj.getObject()
282                 return obj.getDefaultIcon()
283             except (AttributeError, KeyError):
284                 return DEFAULT_ICONS['Piece']
285
286
287     def getPortletPath(self):
288         """ Return a path to portlet, defined in object's class """
289         return self.__class__.portlet
290
291
292     def getFolderContents(self):
293         """ get contents by using catalog, faster than actual listing methods """
294         catalog = getToolByName(self,'portal_catalog')
295         results = catalog.searchResults(path="/".join(self.getPhysicalPath()), sort_on='modified', sort_order='reverse', getState="public")
296         return results
297
298
299     ################# Object creation ####################
300
301
302     # Fixes unicode ids causing problems with copypaste
303     def _checkId(self, id, allow_dup=0):
304         id=str(id)
305         ATBTreeFolder._checkId(self, id, allow_dup)
306        
307
308     # Override initializeArchetype to turn on syndication by default
309     security.declarePrivate('initializeArchetype')
310     def initializeArchetype(self, **kwargs):
311         ret_val = ATBTreeFolder.initializeArchetype(self, **kwargs)
312         # Enable topic syndication by default
313         syn_tool = getToolByName(self, 'portal_syndication', None)
314         if syn_tool is not None:
315             if syn_tool.isSiteSyndicationAllowed():
316                 try:
317                     syn_tool.enableSyndication(self)
318                 except: # might get 'Syndication Information Exists'
319                     pass
320         return ret_val       
321
322     security.declareProtected(ADD_CONTENT_PERMISSION,'createPiece')
323     def createPiece(self, container, id=None, file=None):
324         new=self.lemill_invokeFactory(container, 'Piece', id, do_create=True)
325         new.edit(file=file.read(), language='')
326         return new
327
328     security.declareProtected(ADD_CONTENT_PERMISSION,'lemill_invokeFactory')
329     def lemill_invokeFactory(self, container, meta_type, id=None, do_create=False):
330         """ add new object, edit it's title and invoke _renameAfterCreation """
331         ft=getToolByName(self, 'portal_factory')
332         if id is None:
333             id = self.generateUniqueId(meta_type)
334         path_as_list=self.getDefaultPathFor(meta_type, as_list=True)
335         if path_as_list and path_as_list != list(container.getPhysicalPath()):
336             container=self.restrictedTraverse(self.getDefaultPathFor(meta_type))       
337
338         if ft.getFactoryTypes().has_key(meta_type):
339             o = container.restrictedTraverse('portal_factory/' + meta_type + '/' + id)
340             message = None
341             transaction_note('Initiated creation of %s with id %s in %s' % (o.getTypeInfo().getId(), id, container.absolute_url()))
342         else:
343             new_id = container.invokeFactory(id=id, type_name=meta_type)
344             if new_id is None or new_id == '':
345                new_id = id
346             o=getattr(container, new_id, None)
347             tname = o.getTypeInfo().Title()
348             message = _(u'text_message_object_created', u'${tname} has been created.', mapping={u'tname' : tname})
349             transaction_note('Created %s with id %s in %s' % (o.getTypeInfo().getId(), new_id, container.absolute_url()))
350         if do_create: # finalizes object
351             o=ft.doCreate(o)
352         #print 'Created %s' % o
353         return o
354
355
356     def checkTitle(self, obj=None ,title='', objtype=''):
357         """ check if title is not used anywhere in not(deleted, redirector) object, return false if it is """
358         lt=getToolByName(self, 'lemill_tool')
359         return lt.checkTitle(self,obj=obj, title=title, objtype=objtype)
360
361
362
363
364     ##############         Resource conversion      ######################
365
366     security.declareProtected(ModerateContent, 'convertResource')
367     def convertResource(self, REQUEST):
368         """ Copy values of some fields to new object and redirect old references """
369         allowed_types={'LeMillReference':['content','references'],'Tool':'tools','Activity':'methods','MultimediaMaterial':['content','webpages'], 'ExerciseMaterial':['content','exercises']}
370         lt=getToolByName(self, 'lemill_tool')
371
372         # Check if we have everything we need and raise error if not
373         from_obj_id=REQUEST.get('from_obj_id', '')
374         to_obj_type=REQUEST.get('to_obj_type', '')
375         trial_run=REQUEST.get('trial_run', True)
376
377         if trial_run:
378             if trial_run=='False' or trial_run=='0':
379                 trial_run=False
380
381         from_obj=getattr(self, from_obj_id,None)
382         if not from_obj:
383             raise "base object for resource conversion doesn't exist"
384
385         from_obj_type=from_obj.portal_type
386         if from_obj_type not in allowed_types.keys():
387             raise "cannot convert from type %s" % from_obj_type
388         if to_obj_type not in allowed_types.keys():
389             raise "cannot transfer to type %s" % to_obj_type
390
391         # Create to_obj
392         if to_obj_type in ['Tool','Activity']:
393             to_folder = getattr(self.aq_parent, allowed_types[to_obj_type])
394         else:
395             section = getattr(self.aq_parent, allowed_types[to_obj_type][0])
396             to_folder = getattr(section, allowed_types[to_obj_type][1])
397         to_obj = self.lemill_invokeFactory(to_folder, to_obj_type, do_create=True)
398
399         from_schema=from_obj.schema
400         to_schema=to_obj.schema
401          
402         import copy
403         ignored_fields=['translation', 'translation_of','id','groups']
404
405         # trial run, report fields that have content but cannot be converted
406         if trial_run:
407             problems=False
408             for k in from_schema.keys():
409                 from_accessor = from_schema[k].getEditAccessor(from_obj)
410                 if k not in ignored_fields and  k not in to_schema.keys() and from_accessor and from_accessor():
411                     #stupid exceptions
412                     if k=='address' and from_accessor()=='http://':
413                         continue
414                     if k=='address' and 'location' in to_schema.keys():
415                         continue
416                     if k=='location' and 'address' in to_schema.keys():
417                         continue
418                     if k=='video' and len(from_accessor())<5:
419                         continue
420                     problems=True
421                     lt.addPortalMessage(_('text_message_cannot_convert'), default='Cannot convert ${obj}.', mapping={'obj':k})
422             to_folder._delObject(to_obj.id)
423             if not problems:
424                 lt.addPortalMessage(_('text_message_conversion_will_be_fine'), default='Checked conversion and it will be fine. Click \'Convert resource\' to do the conversion.')
425             if problems:
426                 lt.addPortalMessage(_("text_message_conversion_will_have_problems"), default="Indicated fields have text that cannot be copied to other resource type. Click 'Convert resource' if you want to do the conversion anyway.")
427             return REQUEST.RESPONSE.redirect('%s/manage_convert?trial_run=False&from_obj_id=%s&to_obj_type=%s' % (from_obj.absolute_url(), from_obj.id, to_obj_type))
428
429
430         # Helper method for copying fields
431         def copyvalue(from_accessor, to_mutator, k):
432             if not from_accessor:
433                 return
434             val = from_accessor()
435             if k=='bodyText':
436                 if from_obj_type == 'ExerciseMaterial':
437                     if to_obj_type == 'MultimediaMaterial':
438                         new_val=[]
439                         for (chapter, c_type) in val:
440                             if c_type in ['choice','multiple_choices','fill_in_the_blanks','open_ended']:
441                                 new_val.append((chapter, 'text_block'))
442                             else:
443                                 new_val.append((chapter, c_type))
444                         val=new_val
445                     else:
446                         val=from_obj.getOnlyRawText()
447                 elif from_obj_type == 'MultimediaMaterial':
448                     if to_obj_type == 'ExerciseMaterial':
449                         if val[0][0]=='text_block':
450                             val[0][0]='guidelines'
451                     else:
452                         val=from_obj.getOnlyRawText()
453             copied_val = None
454             try:
455                 copied_val = copy.copy(val)
456             except TypeError:
457                 copied_val = copy.copy(val.aq_base)
458             to_mutator(copied_val)
459
460         # Copy fields with same name
461         for k in from_schema.keys():
462             # list all fields here that shouldn't be copyied to new object
463             if k not in to_schema.keys() or k in ignored_fields:
464                 continue
465             from_accessor = from_schema[k].getEditAccessor(from_obj)
466             to_mutator = to_schema[k].getMutator(to_obj)
467             copyvalue(from_accessor, to_mutator, k)
468
469         # Check convertable pairs of exceptions:
470         for (k,l) in [('address','location')]:
471             if k in from_schema.keys() and l in to_schema.keys():
472                 from_accessor = from_schema[k].getEditAccessor(from_obj)
473                 to_mutator = to_schema[l].getMutator(to_obj)
474                 copyvalue(from_accessor, to_mutator, k)
475             elif l in from_schema.keys() and k in to_schema.keys():
476                 from_accessor = from_schema[l].getEditAccessor(from_obj)
477                 to_mutator = to_schema[k].getMutator(to_obj)
478                 copyvalue(from_accessor, to_mutator, k)
479
480         # LeMillReferences use descriptions instead of bodytexts while they still have bodytexts:
481         if to_obj_type=='LeMillReference':
482             k='bodyText'
483             from_accessor=from_schema['bodyText'].getEditAccessor(from_obj)
484             to_mutator = to_schema['description'].getMutator(to_obj)
485             copyvalue(from_accessor, to_mutator, k)
486         if from_obj_type=='LeMillReference':
487             k='description'
488             from_accessor=from_schema['description'].getEditAccessor(from_obj)
489             to_mutator = to_schema['bodyText'].getMutator(to_obj)
490             copyvalue(from_accessor, to_mutator, k)
491
492
493         # Look for references to this object
494         rc=getToolByName(self, 'reference_catalog')
495         uc=getToolByName(self, 'uid_catalog')
496         results=rc({'targetUID':from_obj.UID()})
497
498         # Get object with certain reference and fix it
499         for refobj in results:
500             res=uc({'UID':refobj.sourceUID})
501             if res:
502                 # getObject verified
503                 obj=res[0].getObject()
504                 if refobj.relationship in ['relatesToContent', 'relatesToMethods', 'relatesToTools']:
505                     obj.delete(from_obj)
506                 if obj.portal_type=='Collection':
507                     obj.add(to_obj)
508
509         # Copy references to pieces
510         if from_obj_type in ['MultimediaMaterial','ExerciseMaterial'] and to_obj_type in ['MultimediaMaterial','ExerciseMaterial']:
511             results=rc({'sourceUID':from_obj.UID()})
512             for refobj in results:
513                 rc.addReference(to_obj, refobj.targetUID, 'uses')       
514
515         # Copy history
516         from_history=from_obj.getHistory()
517         to_obj.setHistory(copy.copy(from_history))
518
519         # Run post_edits
520         to_obj.post_edit_rename()
521         to_obj.recalculateAuthors()
522         to_obj.recalculateScore()
523         to_obj.reindexObject()
524
525         # Delete old object
526         self._delObject(from_obj_id)
527
528         # Return new object
529         lt.addPortalMessage(_('Resource converted succesfully.'))       
530         if to_obj.portal_type in MATERIAL_TYPES:
531             return REQUEST.RESPONSE.redirect('%s/view' % to_obj.absolute_url())           
532         else:
533             return REQUEST.RESPONSE.redirect(to_obj.absolute_url())           
534
535        
536
537     def wakeLazy(self, lazy):
538         new=[]
539         for l in lazy._seq:
540             if isinstance(l, Lazy):
541                 new.extend(self.wakeLazy(l))
542             else:
543                 new.append(l)
544         return new
545
546
547
548
549     ################      Featured items      ###########################
550
551     def _getSamples(self, content_types):
552         lutool=getToolByName(self, 'lemill_usertool')
553         lt=getToolByName(self,'lemill_tool')
554         pc=getToolByName(self,'portal_catalog')
555         languages=lutool.getLanguages()
556         good_results=[]
557         while languages:
558             lang=languages.pop(0)
559             results=pc(portal_type=content_types, Language=lang, getState='public', getHasCoverImage=True, sort_on='getScore', sort_order='descending')
560             results=results[:30]
561             shuffle(results)
562             if len(results)>3:
563                 return results[:4]
564             else:
565                 good_results+=results
566             if len(good_results)>3:
567                 return good_results[:4]
568         return good_results
569
570     security.declareProtected(MANAGE_PORTAL, 'rebuildTop3s')
571     def rebuildTop3s(self):
572         """ set values for sections' top 3 tags, languages etc. (do this daily, or weekly: very expensive) """
573         t=time.time()
574         section_toplists={
575             'content':['top3languages','top3subject_areas','top3target_groups','top3tags'],
576             'methods':['top3languages','top3tags'],
577             'tools':['top3languages','top3tags'],
578             'community':['top3language_skills','top3locations','top3skills','top3interests','top3subject_areas']               
579         }
580        
581         type_mapping = {'content':CONTENT_TYPES, 'methods':'Activity','tools':'Tool','community':'MemberFolder'} # Groups are handled separately         
582         category_mapping={'top3languages':'Language', 'top3subject_areas':'getSubject_area', 'top3target_groups':'getTarget_group''top3tags':'getTags', 'top3language_skills':'getLanguage_skills', 'top3locations':'getLocation_country', 'top3skills':'getSkills', 'top3interests':'getInterests', 'top3group_languages':'getLanguage_skills', 'top3group_tags':'getTags','top3group_subject_areas':'getSubject_area'}
583         pc=getToolByName(self, 'portal_catalog')
584         # Others
585         for section_name, toplist in section_toplists.items():
586             results=pc(portal_type=type_mapping[section_name], getState='public', getHasCoverImage=True)
587             results=self.wakeLazy(results)
588             section=getattr(self.aq_parent, section_name)
589             props={}
590             for top3_id in toplist:
591                 props[top3_id]=self._getTopResults(results, category_mapping[top3_id])
592             section.manage_editProperties(props)
593         # Groups
594         results=pc(portal_type='GroupBlog', getState='public')
595         results=self.wakeLazy(results)
596         for top3_id in ['top3group_languages','top3group_tags','top3group_subject_areas']:
597             props[top3_id]=self._getTopResults(results, category_mapping[top3_id])
598         self.aq_parent.community.manage_editProperties(props)
599         return str(time.time()-t)       
600
601        
602     def _getTopResults(self, results, index_type):
603         pc = getToolByName(self, 'portal_catalog')
604         if index_type not in pc.indexes() or index_type not in pc.schema():
605             return []
606         res={}
607         index=pc._catalog.getIndex(index_type)
608         r=[]
609         for value in results:
610             value=index.getEntryForObject(value, "")
611             if value and (isinstance(value, list) or isinstance(value, tuple)):
612                 for v in value:
613                     res[v]=res.get(v,0)+1
614             elif value:
615                 res[value]=res.get(value,0)+1
616         topthree= [(n,key) for key, n in res.items() if n>1 and key]       
617         topthree.sort(reverse=True)
618         topthree=[key for n,key in topthree[:5]]
619         return topthree
620
621
622     def js_queryForPieces(self, keyword='', search_type='image', user='', collection=False):
623         """ javascript is querying for pieces that are images """
624         result = []
625         q = {'portal_type':'Piece', 'getState': 'public'}
626         if not (keyword or user):
627             return []
628         if keyword:
629             q['SearchableText']=keyword
630         if user:
631             lutool=getToolByName(self, 'lemill_usertool')
632             q['listCreators']=lutool.getAuthenticatedId()
633             q['sort_on']='modified'
634             q['sort_order']='descending'
635         if search_type=='image' or search_type=='cover_image':
636             q['getPiece_type']='image'
637         elif search_type=='audio':
638             q['getPiece_type']='audio'
639
640         ltool = getToolByName(self, 'lemill_tool')
641         if collection and user:
642             query={'portal_type':'Piece', 'getState':'public', 'Creator':q['listCreators']}
643             if 'getPiece_type' in q:
644                 query['getPiece_type']=q['getPiece_type']
645             reftool = getToolByName(self, 'reference_catalog')
646             catalog = getToolByName(self, 'portal_catalog')
647             q_results=[]
648             cols=ltool.searchResultsWrapper(portal_type='Collection', getState='public', Creator=q['listCreators'])
649             for col in cols:
650                 refs=reftool(sourceUID=col.UID, relationship='relatesToContent')
651                 resource_uids=[x.targetUID for x in refs]
652                 query['UID']=resource_uids
653                 res=catalog(query)
654                 if res:
655                    q_results+=res
656         else:
657             q_results = ltool.searchResultsWrapper(q)
658         for r in q_results:
659             if not r.getPiece_type:
660                 continue
661             if r.getHasCoverImage:
662                 cover_url='%s/coverImage' % r.getURL()
663             else:
664                 if r.getPiece_type=='audio':
665                     cover_url='images/default_soundclip.png'
666                 elif r.getPiece_type=='video':
667                     cover_url='images/default_movieclip.png'
668                 else:
669                     cover_url='images/default_movieclip.png'
670             image_url=''
671             piece_type=search_type
672             if search_type!='cover_image':
673                 piece_type=r.getPiece_type
674                 if piece_type=='image':
675                     image_url='%s/image_large' % r.getURL()
676             # r.Title is utf_8-encoded str, so it is changed back to unicode
677             pseudometadata = [r.UID, cover_url, r.Title.decode('utf_8'), piece_type, image_url]
678             result.append(pseudometadata)
679         # javascript can handle unicode, but not utf_8-encoded stuff. Unicode shouldn't be preceded with u.
680         result=str(result) # when list is turned to string, unicode strings inside are left as [..., u'titleishere', ]
681         result=result.replace(" u'"," '")
682         result=result.replace(' u"',' "') # and now unicode markers "u'" are removed
683         return result 
684
685     def js_getPieceMetadata(self, uid):
686         """ Get piece data so that javascript can go and inject it to a live edit form """
687         def cl(s):
688             return cgi.escape(to_unicode(s))
689         lt=getToolByName(self,'lemill_tool')
690         piece=lt.getObjectByUID(uid)
691         doc=u"""<?xml version="1.0" encoding="utf-8"?>
692         <piece>
693             <title>%s</title>
694             <description>%s</description>
695             <rights>%s</rights>
696             <original_author>%s</original_author>
697             <tags>%s</tags>
698         </piece>
699         """ % (cl(piece.Title()), cl(piece.getDescription()), cl(piece.getRights()), cl(piece.getOriginalAuthor()), u', '.join([cl(tag) for tag in piece.getTags()]))
700         # if there are xml-problems, it's better to have them here than in javascript
701         dom = xml.dom.minidom.parseString(doc.encode('utf-8'))       
702         response = self.REQUEST.RESPONSE
703         response.setHeader('Content-Type', "text/xml")
704         return dom.toxml()         
705
706
707     def getLanguageFilter(self):
708         """ returns 'language=xx&' or '' if there is a language cookie set """
709         ilanguage=getToolByName(self, 'portal_languages').getLanguageCookie()
710         if ilanguage:
711             return 'language=%s&' % ilanguage
712         else:
713             return ''
714
715 ##################### Subclasses        ################################
716
717
718 class LargeContentFolder(LargeSectionFolder):
719
720     archetype_name = "Large Content Folder"
721     meta_type = "Large Content Folder"
722
723     allowed_content_types = CONTENT_TYPES +('Topic','Redirector')
724     default_view = ('lemill_content_view')
725     portlet='here/portlet_add_content/macros/portlet'
726     filter_content_types = True
727     subsections=['webpages','presentations','pilots','exercises','pieces','lessonplans','schoolprojects','printresources']
728     security = ClassSecurityInfo()
729
730
731     def getSamples(self):
732         """ Returns content types """
733         return self._getSamples(MATERIAL_TYPES)
734
735     def getTemplate(self, template_id):
736         return TEMPLATES.get(template_id, None)
737
738     security.declareProtected(ADD_CONTENT_PERMISSION,'lemidlet_post')
739     def lemidlet_post(self, REQUEST):
740         """ LeMidlet will post image here...."""
741         #print REQUEST.file.read()
742         file = REQUEST.get('file')
743         if file==None:
744             return 'file not found'       
745         description = REQUEST.get('description')
746         type = 'Piece'
747         new = self.lemill_invokeFactory(self, type, id=type+str(id(self)), do_create=True)
748         new.edit(description=description, file=file.read(),language='',title=REQUEST.get('title'),tags=REQUEST.get('tags'))
749         return 0
750
751        
752 class LargeActivityFolder(LargeSectionFolder):
753
754     archetype_name = "Large Activity Folder"
755     meta_type = "Large Activity Folder"
756
757     allowed_content_types = ('Activity','KB', 'Topic','Redirector')
758     default_view = ('lemill_activities_view')
759     portlet='here/portlet_add_activity/macros/portlet'
760     subsections=[]
761     filter_content_types = True
762
763     def getSamples(self):
764         """ Returns 3 methods/activities """
765         return self._getSamples('Activity')
766    
767 class LargeToolFolder(LargeSectionFolder):
768
769     archetype_name = "Large Tool Folder"
770     meta_type = "Large Tool Folder"
771
772     allowed_content_types = ('Tool', 'Topic','Redirector')
773     default_view = ('lemill_tools_view')
774     portlet='here/portlet_add_tool/macros/portlet'
775     subsections=[]
776     filter_content_types = True
777
778     def getSamples(self):
779         """ Returns 3 tools """
780         return self._getSamples('Tool')
781
782 class LargeCommunityFolder(LargeSectionFolder):
783
784     archetype_name = "Large Community Folder"
785     meta_type = "Large Community Folder"
786
787     allowed_content_types = ('Topic','Redirector','MemberFolder','GroupBlog','ATBTreeFolder','Large Plone Folder')
788     default_view = ('lemill_community_view')
789     portlet='here/portlet_add_community/macros/portlet'
790
791     filter_content_types = False
792     security = ClassSecurityInfo()
793     subsections=['people','groups']
794     schema=communityschema
795
796
797     ### If MemberFolder for logged in user does not exist, it is created here
798     
799     def my_page(self):
800         """ Checks if user has MemberFolder and creates one if not. Returns the folder url."""       
801         lutool = getToolByName(self, "lemill_usertool")
802         mf_url = lutool.getMemberFolderURL()
803         #print 'my_page called'
804         if not mf_url:
805             lutool.createMemberFolder()           
806             mf_url = lutool.getMemberFolderURL()
807         return mf_url
808
809
810     def getMemberFolderById(self,id):
811         """ sometimes it is easier to just get it directly from btreefolder """
812         if self.id=='people':
813             return getattr(self, id, None)
814         elif hasattr(self, 'people'):
815             people=self.people
816             return getattr(people, id, None)
817         else:
818             return getattr(self, id, None)
819
820     def getGroupById(self,id):
821         """ sometimes it is easier to just get it directly from btreefolder """
822         if self.id=='groups':           
823             return getattr(self, id, None)
824         elif hasattr(self, 'groups'):
825             groups=self.groups
826             return getattr(groups, id, None)
827         else:
828             return getattr(self, id, None)
829
830     def getSamples(self):
831         """ Get 4 random people """ 
832         lutool=getToolByName(self, 'lemill_usertool')
833         lt=getToolByName(self,'lemill_tool')
834         pc=getToolByName(self,'portal_catalog')
835         languages=lutool.getLanguages()
836         good_results=[]
837         while languages:
838             lang=languages.pop(0)
839             results=pc(portal_type='MemberFolder', getLanguage_skills=lang, getState='public', getHasCoverImage=True, sort_on='getScore', sort_order='descending')
840             results=results[:30]
841             shuffle(results)
842             if len(results)>3:
843                 return results[:4]
844             else:
845                 good_results+=results
846             if len(good_results)>3:
847                 return good_results[:4]
848         return good_results
849        
850
851     def mergeLatestThreadsInMyGroups(self):
852         """ Method to help displaying latest threads (BlogPost) in section front page """
853         lutool = getToolByName(self, "lemill_usertool")
854         memberfolder = lutool.getMemberFolder()
855         if not memberfolder:
856             return []
857         glist=[g.getId for g in memberfolder.getGroups(objects=False)]
858         pc=getToolByName(self, 'portal_catalog')
859         posts=pc({'getParentBlog':glist, 'getState':'public', 'sort_on':'created', 'sort_order':'descending','meta_type':'BlogPost'})
860         return posts[:20]
861                                    
862     def getCollections(self, obj_uid=None):
863         """ Show collections where object is used."""
864         if not obj_uid:
865             return []
866         res = []
867         q = { 'targetUID': obj_uid }
868         qres = self.reference_catalog(q)
869         for q in qres:
870             v = self.reference_catalog.lookupObject(q.UID)
871             if v:
872                 source = v.getSourceObject()
873                 if source.meta_type == 'Collection':
874                     res.append(source)
875         return res
876
877     def getSelectablesForPortfolio(self, results):
878         """Try to find which types are found in results and show them as selectables """
879         pc=getToolByName(self, 'portal_catalog')
880         lt=getToolByName(self, 'lemill_tool')
881         types, tags = pc.fastCount(results, ('portal_type','getTags'))
882         # Convert them to tuples where (value, display value)
883         types=zip(map(lambda x: lt.getTypeName(x), types.keys()), types.keys(), types.values())
884         tagslist=zip(tags.keys(),tags.keys(),tags.values())
885         tagslist.sort()
886         return {'type':types,'tag':tagslist}
887
888     security.declareProtected(ADD_CONTENT_PERMISSION,'addGroup')
889     def addGroup(self, title, join=True):
890         """ Create a new group. """
891         if self.getId()!='groups':
892             return self.community.groups.addGroup(title, join)
893         lt = getToolByName(self, 'lemill_tool')
894         group_id = lt.normalizeString(title)
895         group_id=self.invokeFactory('GroupBlog',group_id)
896         group=getattr(self, group_id, None)
897         group.edit(title=title, description="")
898         if join:
899             group.join_group()
900         # Group object in uid_catalog gets title
901         group.helper_updateCatalogUID()
902         # Need to manually set _at_creation_flag to False, as processForm does not get called
903         group.unmarkCreationFlag()
904         return group           
905
906
907 class LargeTrashFolder(LargeSectionFolder):
908
909     archetype_name = "Large Trash Folder"
910     meta_type = "Large Trash Folder"
911
912     allowed_content_types = ALL_CONTENT_TYPES
913     filter_content_types = False
914     default_view = ('lemill_trash_view')
915     security = ClassSecurityInfo()
916
917        
918 registerType(LargeContentFolder, PROJECTNAME)
919 registerType(LargeActivityFolder, PROJECTNAME)
920 registerType(LargeToolFolder, PROJECTNAME)
921 registerType(LargeCommunityFolder, PROJECTNAME)
922 registerType(LargeTrashFolder, PROJECTNAME)
Note: See TracBrowser for help on using the browser.