source: trunk/LargeSectionFolder.py @ 1704

Revision 1704, 42.4 KB checked in by pjotr, 13 years ago (diff)

Trash folder viewable for anyone, folder view will only show stuff for managers

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
19from config import *
20from Products.Archetypes.public import *
21from Products.ATContentTypes.content.folder import ATFolder, ATBTreeFolderSchema, ATBTreeFolder
22from Products.Archetypes.public import registerType
23from Products.CMFCore.utils import getToolByName
24from Products.CMFPlone.utils import transaction_note
25from ZPublisher.HTTPRequest import FileUpload
26from DocumentTemplate import sequence
27from random import sample
28from DateTime import DateTime
29import re, datetime, urllib
30from AccessControl import ClassSecurityInfo
31from permissions import MODIFY_CONTENT, ADD_CONTENT_PERMISSION
32from Products.LeMill import LeMillMessageFactory as _
33
34communityschema= ATBTreeFolderSchema + Schema((
35    LinesField('collaboration_proposals',
36        default= [],
37        widget = LinesWidget(
38            visible = {'view':'invisible', 'edit':'invisible'},
39            )
40        )
41
42))
43
44
45
46class LargeSectionFolder(ATBTreeFolder):
47    """Section Folder"""
48       
49    archetype_name = "Large Section Folder"
50    meta_type = "Large Section Folder"
51    security = ClassSecurityInfo()
52    isAnObjectManager = 1
53    global_allow= 1
54
55    def getAllContentTypes(self):
56        """ returns a list of all content types """
57        return ALL_CONTENT_TYPES
58
59    def getMaterialTypes(self):
60        """ returns a list of material types """
61        return MATERIAL_TYPES
62
63    def getContentTypes(self):
64        """ returns a list of material types """
65        return CONTENT_TYPES
66
67    def getActivityTypes(self):
68        """ returns a list of activity types """
69        return ACTIVITY_TYPES
70
71    def getToolsTypes(self):
72        """ returns a list of activity types """
73        return TOOLS_TYPES
74
75    def getFeaturedTypes(self):
76        """ returns a list of featured types """
77        return MATERIAL_TYPES + ACTIVITY_TYPES + TOOLS_TYPES
78
79
80    def getMimetypeWhitelist(self):
81        """ returns a list of allowed filetypes """
82        return MIMETYPE_WHITELIST
83
84    def canDeleteOnCancel(self):
85        """ Should be called on resource contexts, so NO """
86        return False
87
88    # Override initializeArchetype to turn on syndication by default
89    security.declarePrivate('initializeArchetype')
90    def initializeArchetype(self, **kwargs):
91        ret_val = ATBTreeFolder.initializeArchetype(self, **kwargs)
92        # Enable topic syndication by default
93        syn_tool = getToolByName(self, 'portal_syndication', None)
94        if syn_tool is not None:
95            if syn_tool.isSiteSyndicationAllowed():
96                try:
97                    syn_tool.enableSyndication(self)
98                except: # might get 'Syndication Information Exists'
99                    pass
100        return ret_val
101
102    security.declareProtected(ADD_CONTENT_PERMISSION,'createPiece')
103    def createPiece(self, container, id=None, file=None):
104        new=self._lemill_invokeFactory(container, 'Piece', id, do_create=True)
105        new.edit(file=file.read(), language='')
106        return new
107
108    # private
109    def _lemill_invokeFactory(self, container, meta_type, id=None, do_create=False):
110        """ add new object, edit it's title and invoke _renameAfterCreation """
111        ft=getToolByName(self, 'portal_factory')
112        if id is None:
113            id = self.generateUniqueId(meta_type)
114        if ft.getFactoryTypes().has_key(meta_type):
115            o = container.restrictedTraverse('portal_factory/' + meta_type + '/' + id)
116            message = None
117            transaction_note('Initiated creation of %s with id %s in %s' % (o.getTypeInfo().getId(), id, container.absolute_url()))
118        else:
119            new_id = container.invokeFactory(id=id, type_name=meta_type)
120            if new_id is None or new_id == '':
121               new_id = id
122            o=getattr(container, new_id, None)
123            tname = o.getTypeInfo().Title()
124            message = _(u'${tname} has been created.', mapping={u'tname' : tname})
125            transaction_note('Created %s with id %s in %s' % (o.getTypeInfo().getId(), new_id, container.absolute_url()))
126        if do_create: # finalizes object
127            o=ft.doCreate(o)
128        return o
129
130
131
132    security.declareProtected(ADD_CONTENT_PERMISSION,'start_translation')
133    def start_translation(self, objId=None):
134        """
135        Start a new translation of an object. This is done by creating new object and
136        then, on second stage copying data from object to new object.
137        Second stage is done by Resources/prefill_translation because of redirect_to -activities
138        lose the data if it is given here.
139        """
140        if objId is None:
141            raise 'object id is None (create_translation)'
142        base_obj = self.restrictedTraverse(objId)
143        meta_type = base_obj.meta_type
144        new = self._lemill_invokeFactory(self, meta_type, id=meta_type.lower()+str(id(base_obj)), do_create=True)
145        # I give up, cannot handle all required references and files while keeping the object in portal_factory, do_create=True
146        return new
147
148    def analyzeRequest(self, REQUEST):
149        """ Digs searchterms and content filters out of request, returns dictionary """
150        ltool = getToolByName(self, 'lemill_tool')
151        queries=REQUEST.form
152        # HUGE kludge - no other ideas on how to fix the mysterious "-c" appearing
153        toremove=[]
154        for key in queries.keys():
155            if key.startswith('-'):
156                toremove.append(key)
157        for rem in toremove:
158            del queries[rem]
159       
160        fmds=FIELD_METHODS_DISPLAY_STRINGS
161        if queries.has_key('filter'):
162            filter=queries.pop('filter')
163        else:
164            filter=''
165        if queries.has_key('b_start'):
166            batch_start=queries.pop('b_start')
167        search_url=''
168        for (key,value) in queries.items():
169            if type(value) == list or type(value) == tuple:
170                for y in value:
171                    search_url='%s&%s=%s' % (search_url, key, y)               
172            else:
173                search_url='%s&%s=%s' % (search_url, key, value)       
174        if search_url!='':
175            cont_url=''.join(('?',search_url,'&'))
176        else:
177            cont_url=''.join((search_url,'?'))
178        searchterm_nice={}
179        value_nice={}
180        is_plural={}
181        for (key, value) in queries.items():           
182            if type(value) == list or type(value) == tuple:               
183            # Show materials where *tags* *are* *nature*, *tree*
184                if fmds.has_key(key):
185                    searchterm_nice[key]=fmds[key][1]
186                else:
187                    searchterm_nice[key]=key
188                is_plural[key]=True
189                if key=='Language' or key=='getLanguage_skills':
190                    value_nice=', '.join([lemill_tool.getPrettyLanguage(x) for x in value])                                       
191                else:
192                    value_nice=', '.join(value)                                       
193            else:
194            # Show materials where *tag* *is* *nature*
195                if fmds.has_key(key):
196                    searchterm_nice[key]=fmds[key][0]
197                else:
198                    searchterm_nice[key]=key
199                is_plural[key]=False
200                if key=='Language' or key=='getLanguage_skills':
201                    value_nice[key]=ltool.getPrettyLanguage(value)                                       
202                else:
203                    value_nice[key]=value                                       
204
205        dict={'searchterms':queries, # dictionary of searchterms got from url attributes {'searchterm' : 'value', ...}
206            'search_url':search_url,  # searchterms in url string form '&searchterm=value'
207            'cont_url':cont_url,  #searchterms in url string beginning with '?' and ending with '&'
208            'searchterm_nice':searchterm_nice, # dictionary of tuples for each searchterm, contains displayed form of searchterm f.ex 'getTags': (tag, tags)
209            'is_plural':is_plural, # dictionary of booleans, if certain searchterm has multiple values
210            'value_nice':value_nice, # dictionary of strings, if searchterm is language contains full names of languages otherwise just values for keys {'language':'Finnish'}
211            'filter':filter} # if materials / pieces filter is activated
212        return dict
213
214    def fullResultCount(self,  REQUEST=None, topic_name=None, getIndexMethod=None, request_opened=None, **kw):
215        """ Does a full catalog search with criteria from Topic (called from lemill_browse_results) """
216
217        # if the same search has just been done, return it instead of going through all of this
218#        if hasattr(REQUEST, 'SESSION'):       
219#            if REQUEST.SESSION.get('latest_st')== request_opened['searchterms']:
220#                return REQUEST.SESSION.get('results_n_dict')
221
222        pcatalog = getToolByName(self, 'portal_catalog')
223        mtool = getToolByName(self, 'portal_membership')
224
225        if request_opened.has_key('searchterms'):
226            kw=request_opened['searchterms']       
227
228        if topic_name=='published': # some topic criteria cannot be drawn from given variables and must be hardcoded
229            kw['Creator']=mtool.getAuthenticatedMember().getId()
230            kw['review_state']='public'
231        elif topic_name=='draft':
232            return {} # this shouldn't have happened
233
234        if kw=={}: return {} # don't try to display results if there isn't proper search
235
236        results = pcatalog.searchResults(REQUEST, **kw)
237        #print [(x.portal_type, x.Title, x.id) for x in results if x.portal_type in SEARCHABLE_TYPES]
238
239        if topic_name=='published':
240            del kw['review_state'] # this kw would just mess up the results
241       
242        rc={}
243        for t in SEARCHABLE_TYPES:
244            rc[t]=0
245        for r in results:
246            rpt=r.portal_type
247            if rpt in SEARCHABLE_TYPES and r.review_state != 'deleted':
248                rc[rpt]=rc[rpt]+1
249        rc['Learning resource']= sum([rc[k] for k in MATERIAL_TYPES])
250        rc['Content']=rc['Learning resource']+rc['Piece']+rc['LeMillReference']
251        for k in MATERIAL_TYPES: del rc[k]
252       
253#        if hasattr(REQUEST, 'SESSION'):       
254#            REQUEST.SESSION.set('latest_st', request_opened['searchterms'])
255#            REQUEST.SESSION.set('results_n_dict', rc)
256        #print rc
257
258        return rc
259
260    def prettyMethodNameDictionary(self):
261        """ Helper method for finding displayable names for search fields """
262        # better to do this in config..
263        return FIELD_METHODS_DISPLAY_STRINGS                     
264
265    def getBrowsedStuff(self, topic_name):
266        """ returns nice name for currently browsed content type"""
267        return self.getId()
268
269    def getNameOfResourceType(self,restype):
270        """Get human-readable name of resource - first try TYPE_NAMES, then TEMPLATES"""
271        if restype in TYPE_NAMES.keys():
272            return TYPE_NAMES[restype][0].lower()
273        else:
274            for t in TEMPLATES.values():
275                if t['meta_type'] == restype:
276                    if t['title'].isupper():
277                        return t['title']
278                    else:
279                        return t['title'].lower()
280        return ""
281
282    def getSectionFolder(self):
283        """ return this """
284        return self
285
286    def checkTitle(self, obj=None ,title='', objtype=''):
287        """ check if title is not used anywhere in not(deleted, redirector) object, return false if it is """
288        lt=getToolByName(self, 'lemill_tool')
289        return lt.checkTitle(self,obj=obj, title=title, objtype=objtype)
290       
291
292    def getTopicLinks(self, topic, here_url, rfilter, search_url):
293        """ Figures out what links should be displayed in this topic page """       
294               
295        # these dictionaries define what kinds of links there are in legend tabs (up) and to other resource types (down).
296        # type_name_key : (location of link, path of link, filter)
297        # dictionary keys should match those of TYPE_NAMES so that displayed names for links can be fetched from there
298
299        # show all major categories
300        if topic=='published':           
301            dict={'Piece':('up','content/published','pieces'),
302            'LeMillReference':('up','content/published','references'),
303            'Learning resource':('up','content/published','resources'),
304            'Activity':('down','methods/published',''),
305            'Tool':('down','tools/published','')}
306
307        # in #1173 all of the filters are removed from recent changes
308        elif topic=='recent':           
309            dict={}
310
311        # only resources can be drafts, so show nothing
312        elif topic=='draft':
313            dict={}
314
315        elif topic=='content' or topic=='content_list':           
316            dict={'Piece':('up','here','pieces'),
317            'LeMillReference':('up','here','references'),
318            'Learning resource':('up','here','resources'),
319            'All':('up','here','')}
320
321        # almost everything has tags
322        elif topic=='tags':
323            dict={'Piece':('up','here','pieces'),
324            'LeMillReference':('up','here','references'),
325            'Learning resource':('up','here','resources'),
326            'Content':('down','content/tags','resources'),
327            'Activity':('down','activities/tags',''),
328            'Tool':('down','tools/tags',''),
329            'GroupBlog':('down','community/tags','')}
330
331        # almost everything has language (except pieces)
332        elif topic=='language' or topic=='group_language':
333            dict={'LeMillReference':('up','here','references'),
334            'Learning resource':('up','here','resources'),
335            'Content':('down', 'content/language','resources'),
336            'Activity':('down','activities/language',''),
337            'Tool':('down','tools/language',''),
338            'GroupBlog':('down','community/group_language',''),
339            'MemberFolder':('down','community/language','')}
340
341        # only references and resources have these
342        elif topic=='subject_area' or topic=='target_group':
343            dict={'LeMillReference':('up','here','references'),
344            'Learning resource':('up','here','resources')}
345       
346        # portfolio should show all stuff
347        elif topic=='portfolio':
348            dict={'Piece':('up','here','pieces'),
349            'LeMillReference':('up','here','references'),
350            'Learning resource':('up','here','resources'),
351            'Content':('down','here','resources'),
352            'Activity':('down','here','activities'),
353            'Tool':('down','here','tools'),
354            #'Collection':('down','here','collections'), <-- I think these should be included, but will require some extra work, I'll wait for comments
355            }
356       
357        # these should show nothing except themselves
358        elif topic in ['activities','tools','groups','members','country','skills','interests']:
359            dict={}                   
360        else:
361            dict={}
362
363        if dict=={}:
364            return ({},{})
365
366
367        # filter conversion table - this is stupid, but has to be done.
368        # if you're using filter, you want to disable the link to that filter, how do you know which one is it?
369        filter_conversion={'pieces':'Piece',
370        'references':'LeMillReference',
371        'resources':'Learning resource',
372        'methods':'Activity',
373        'tools':'Tool',
374        'collections':'Collection',
375        'activities':'Activity', # this should be deprecated soon
376        'materials':'Learning resource' # this should be deprecated already
377        }
378
379        # mark current page disabled
380        if rfilter:
381            current_page=filter_conversion[rfilter]
382            if current_page in dict.keys():
383                (place, path, lfilter)=dict[current_page]
384                dict[current_page]=(place,'disabled', lfilter)
385
386        portal_url = getToolByName(self, 'portal_url')
387        up_dict={}
388        down_dict={}
389        base_url = self.portal_url()
390       
391        # create better dicts:
392        # find display labels from TYPE_NAMES dict,
393        # replace path with real url with filters and searchterms
394        # if linked page is current page, set link to ''
395        # example:
396        # 'Piece':(('Piece','Pieces'), 'http://www.lemill.net/content/tags?filter=pieces')
397        for (key, value) in dict.items():
398            labels=TYPE_NAMES[key]
399            linkplace=value[0]
400            path=value[1]
401            lfilter=value[2]
402            if path=='here':
403                url=here_url
404            else:
405                url='/'.join((base_url, path))
406                if url==here_url and rfilter=='':
407                    path='disabled'
408            if key=='Content' and rfilter in ['references','resources','pieces']:
409                path='disabled'
410            if key=='All' and not rfilter:
411                path='disabled'
412            if path=='disabled':
413                url=''
414            else:
415                if search_url=='' and lfilter!='':
416                  url='%s?filter=%s' % (url, lfilter)
417                elif search_url!='' and lfilter!='':
418                  url='%s?%s&filter=%s' % (url, search_url[1:], lfilter)
419                elif search_url!='' and lfilter=='':
420                  url='%s?%s' % (url, search_url[1:])
421           
422            if linkplace=='up':
423                # add variable 'order' to dictionary value tuple, so sorting can be based on something
424                order=9               
425                for c in range(len(FILTER_LINK_ORDER)):
426                    if FILTER_LINK_ORDER[c]==key:
427                        order=c
428                        continue
429                up_dict[key]=(order,labels, url)
430            elif linkplace=='down':
431                down_dict[key]=(labels, url)
432        uplinks=up_dict.values()
433        uplinks.sort()
434        return (uplinks, down_dict)
435
436    def getMetadataFieldsToShowInEditor(self, object):
437        """ gets fields which are shown in metadata edit area """
438        type = object.meta_type
439        shownFieldsList = SHOW_METADATA_FIELDS[type]
440        shownFields = []
441        fields = object.Schemata()['metadata'].fields()
442        # At this point, the method can return a list of all metadata
443        if 'all' in shownFieldsList:
444            return fields
445        else:
446            for field in fields:
447                if field.getName() in shownFieldsList:
448                    shownFields.append(field)
449            return shownFields
450
451    def amIManager(self):
452        """Check whether I'm a manager."""
453        return 'Manager' in self.portal_membership.getAuthenticatedMember().getRoles()
454
455    def canIModerate(self):
456        roles = self.portal_membership.getAuthenticatedMember().getRolesInContext(self)
457        return 'Manager' in roles or 'Reviewer' in roles
458
459    def whoami(self):
460        return self.portal_membership.getAuthenticatedMember().getId()       
461
462    def getMember(self,uname=None):
463        if not uname:
464            uname=self.whoami()
465        try:
466            return getattr(self.community,uname)
467        except AttributeError:
468            return None
469
470    def getOtherMember(self, uname):
471        # XXX This is a very bad hack, if something fails along the road - we will get a lot of errors from all over the place
472        user_folder = self.lemill_usertool.getLeMillMemberFolder(uname)
473        if user_folder:
474            try:
475                return getattr(self.community,user_folder.getId())
476            except AttributeError:
477                return None
478            except TypeError:
479                return None
480        return None
481
482    def getSamples(self, search_results):
483        """ Pick three random objects from search results that have non-default images to display in folders front pages. """
484        pic_results = [x for x in search_results if x.meta_type in FEATURED_TYPES and x.getHasCoverImage and x.review_state=='public' and x.Title]
485        n=min(3,len(pic_results))
486        samples=sample(pic_results,n)
487        return samples
488
489    def getLanguageDependantSamples(self, search_results):
490        """ Same as previous, but gives language-dependant results. """
491        ltool = getToolByName(self, 'portal_languages')
492        language_used_now = ltool.getLanguageCookie()
493        if language_used_now == None:
494            language_used_now = 'en'
495        pic_results = [x for x in search_results if x.meta_type in FEATURED_TYPES and x.getHasCoverImage and x.review_state=='public' and x.Title and x.Language == language_used_now]
496        # If 3 results or more, then return
497        if len(pic_results) >= 3:
498            n=min(3,len(pic_results))
499            samples=sample(pic_results,n)
500            return samples
501        # If less than 3 results for current language, then add english results
502        if len(pic_results) < 3:
503            additional_results_en = [x for x in search_results if x.meta_type in FEATURED_TYPES and x.getHasCoverImage and x.review_state=='public' and x.Title and x.Language == 'en']
504            returned_results_en = pic_results + additional_results_en
505            if len(returned_results_en) >= 3:
506                results_needed = 3 - len(pic_results)
507                samples = pic_results + sample(additional_results_en,results_needed)
508                return samples
509        # If english results are not enough, then add any results
510        if len(returned_results_en) < 3:
511            additional_results_all = [x for x in search_results if x.meta_type in FEATURED_TYPES and x.getHasCoverImage and x.review_state=='public' and x.Title]
512            returned_results_all = returned_results_en + additional_results_all
513            if len(returned_results_all) >=3:
514                results_needed = 3 - len(returned_results_en)
515                samples = returned_results_en + sample(additional_results_all,results_needed)
516                return samples
517        n=min(3,len(returned_results_all))
518        samples=sample(returned_results_all,n)
519        return samples
520       
521    def getTopResults(self, search_results, index_type):
522        """ Should return top three populated subgroups of search results in given index  """
523        pc = getToolByName(self, 'portal_catalog')
524        if index_type in pc.indexes() and index_type in pc.schema():
525            uniques = pc.uniqueValuesFor(index_type)
526            if uniques == ():
527                return []
528            hits={}
529            for a in uniques:
530                hits[a]=0
531            for counter in search_results:
532                if counter.review_state!='deleted':   
533                    values=getattr(counter, index_type)
534                    if isinstance(values,str):
535                         hits[values]=hits[values]+1
536                    else:
537                        try:
538                            for a in values:
539                                hits[a]=hits[a]+1
540                        except TypeError:
541                            pass # something wrong with data - let's continue without it
542            topthree= [(x[1],x[0]) for x in hits.items() if x[1]>0 and x[0]!='']
543            topthree.sort()
544            topthree.reverse()
545            topthree= topthree[:min(3,len(topthree))]
546            topthree=[x[1] for x in topthree]
547           
548            return topthree
549        else:
550            return []
551
552    def url_quote(self,word):
553        return urllib.quote(word)
554
555    def tagcloud_type(self,topic_name):
556        """ Other than community/tool/activity titles count tag cloud weights by their popularity, aka hits """
557        return 'hits'
558
559    def getTitleCloud(self, search_results, browse_type):
560        """ Simplified version of getUniques that doesn't try to weigh results, used for equal-worth ordering like with titles """
561        # uniquetuplelist contains result metadata reordered: (sort_title, count, url, indexvalue, title)
562        from math import log
563        rc=getToolByName(self, 'reference_catalog')
564        uc=getToolByName(self, 'uid_catalog')
565        pc = getToolByName(self, 'portal_catalog')
566        if browse_type=='group_titles':
567            grouptool = getToolByName(self, 'portal_groups')
568
569        def getTypeForReferenceSource(ref_catalog_item):
570            return uc({'UID':ref_catalog_item.sourceUID})[0].portal_type
571
572        def adjust(i):
573            # helper method to adjust hit count of this tag to relative size (1,...,8)
574            (a,b,c,d,e)=i
575            b=int((8*log(b,2))/log(maxscore,2))
576            if b==0:
577                b=1
578            i=(a,b,c,d,e)
579            return i       
580           
581        def calc_piece(x):
582            res=rc({'targetUID':x.UID})
583            score=len(res)
584            return score
585           
586        def calc_activity(x):
587            res=rc({'targetUID':x.UID})
588            score=0
589            scores={'Collection':1}
590            for r in res:
591                typ = getTypeForReferenceSource(r)
592                if typ in scores.keys():
593                    score+=scores[typ]
594            return score
595        calc_content=calc_activity
596        calc_tool=calc_activity
597        calc_reference=calc_activity
598        calc_material=calc_activity
599
600        def calc_member(x):
601            created=pc({'Creator':x.id})
602            score=0
603            scores={'Piece':1, 'PILOTMaterial':10, 'PresentationMaterial':10, 'MultimediaMaterial':10, 'Activity':10, 'Tool':10, 'BlogPost':1}
604            for c in created:
605                typ=c.portal_type
606                if typ in scores.keys():
607                    score+=scores[typ]
608            contacts=rc({'relationship':'is contact of', 'sourceUID':x.UID})
609            score+=len(contacts)
610            return score
611
612        def calc_group(x):
613            # slow!
614            score=0
615            materials=pc({'getGroupsShared':x.id})
616            blog_posts=x.getObject().objectValues('BlogPost')
617            group=grouptool.getGroupById(x.id)
618            if group:
619                try:
620                    members=group.getGroupMembers()
621                except:
622                    members=[]
623            else:
624                members=[]           
625            score=len(materials)+len(blog_posts)+len(members)   
626            return score         
627       
628        func = eval('calc_%s' % browse_type.split('_')[0])
629        popularity =[(func(x)+1, x.sortable_title, x) for x in search_results]
630        popularity.sort(cmp=lambda t2,t1: cmp(t1[0],t2[0]))
631        popularity=popularity[:100]
632        if browse_type=='member_titles':
633            titlecloud=[(x.getSortable_nicename,pop,x.getURL(),s_title, x.getNicename) for (pop, s_title, x) in popularity]       
634        else:
635            titlecloud=[(s_title,pop,x.getURL(),s_title, x.Title) for (pop, s_title, x) in popularity if x.Title!='']
636        if not titlecloud:
637            return []
638        titlecloud.sort()
639        maxscore=max([x[1] for x in titlecloud])
640        if maxscore>1:
641            titlecloud=map(adjust, titlecloud)
642        return titlecloud
643
644    def getUniques(self, search_results, index_type='Date', browse_type='hits'):
645        """ Should return unique values of search results in given index, results are list of index-metadata-like tuples in alphabetical order """
646
647        if search_results==[] or search_results==None:
648            return []
649        elif browse_type.endswith('_titles'):
650            return self.getTitleCloud(search_results, browse_type)
651        elif browse_type=='hits':
652            return self.getTagCloud(search_results, index_type, browse_type)
653
654
655    def getTagCloud(self, search_results, index_type, browse_type):
656        lemill_tool = getToolByName(self, 'lemill_tool')
657        pc = getToolByName(self, 'portal_catalog')
658        from math import log
659        resultlist=[]
660        maxcount=0
661        uniquevalues=[]
662
663        def adjust(i):
664            # helper method to adjust hit count of this tag to relative size (1,...,8)
665            (a,b,c,d,e)=i
666            b=int((8*log(b,2))/log(maxcount,2))
667            if b==0:
668                b=1
669            i=(a,b,c,d,e)
670            return i
671
672        # -- Step 1 -- : Make list of unique values for this index
673        uniques = pc.uniqueValuesFor(index_type)           
674        if uniques == []:
675            return []
676        hits={}
677        # -- Step 2 -- : Use that list to make a dictionary. Values of list are keys.
678        for a in uniques:
679            hits[a]=0
680        # -- Step 3 -- : Go through the search_results and every time a certain key is found from selected index, add a hit to counter under that key.
681        spent_urls=[]
682        for c_obj in search_results:
683            if c_obj.review_state!='deleted':
684                values=getattr(c_obj, index_type)
685                if values!=None:
686                    if type(values)==str or type(values)==int:
687                        hits[values]=hits[values]+1
688                    elif type(values)==list or type(values)==tuple:
689                        for a in list(values):
690                            hits[a]=hits[a]+1               
691
692        # -- Step 4 -- : Build a list from previous dictionary.
693        if hits.has_key(''):
694            del hits['']
695        resultlist=[(x[1], x[0]) for x in hits.items() if x[1]>0]
696        if not resultlist:
697            return []
698        resultlist.sort()
699        resultlist.reverse()
700        maxcount=resultlist[0][0]
701        # if the first cut score for tag is x, we want to cut off all of the tags with score x.
702        if len(resultlist)>100:
703            #cutpoint = [x[0] for x in resultlist].index(resultlist[100]) can't figure this now, fix later
704            cutpoint = 100
705            resultlist = resultlist[:cutpoint]
706        resultlist=[(x[1], x[0], '',x[1],x[1]) for x in resultlist]
707
708        # adjust to 1-8. We don't have to worry about score 0, they're already removed.
709        if maxcount>1:
710            resultlist=map(adjust, resultlist)
711        # prettify language names
712        if index_type=='Language' or index_type=='getLanguage_skills':
713            resultlist=[(x[0],x[1],x[2],x[3],lemill_tool.getPrettyLanguage(x[4])) for x in resultlist]
714           
715        resultlist.sort()
716        return resultlist
717
718    def getTagURL(self,context,category,value,filter=None, insideMemberFolder=False):
719        value=to_unicode(value)
720
721        if filter:
722            filter=''.join(('&filter=',filter))
723        else:
724            filter=''
725
726        if insideMemberFolder:
727            begin='/'.join((insideMemberFolder.absolute_url(),context.getId()))
728            filter='&Creator=%s' % insideMemberFolder.Creator()
729        else:
730            begin=context.absolute_url()
731       
732        return u"%s?%s=%s%s" % (begin,category,value, filter)
733
734    def getUrlInsideMemberFolder(self, memberfolder=None, topicid='', here_url=''):
735        """ When trying to get url for topics, absolute_url omits the memberfolder, this method puts it back in """
736        if memberfolder:
737            return '/'.join((memberfolder.absolute_url(),topicid))
738        else:
739            return here_url
740
741    def js_queryForPieces(self, keyword, audio=False):
742        """ javascript is querying for pieces that are images """
743        # when this method is called by javascript, all arguments are packed to string 'keyword'
744        # typical value for keyword: 'foobar,audio=False'
745        keywords=keyword.split(', ')
746        keyword=keywords[0]
747        audio='no'
748        for a in keywords[1:]:
749            if a=='audio=True':
750                audio='only'
751            elif a=='audio=accept':
752                audio='accept'
753
754        result = []
755        stool = getToolByName(self, 'lemill_search')
756        q = {'SearchableText': keyword,
757             'portal_type': ['Piece', ]
758        }
759        q_results = stool.local_search(q)
760        for r in q_results:
761            if r.review_state == 'deleted':
762                continue
763            if audio=='only':
764                if not r.getObject().isAudio(): continue
765            elif audio=='no':
766                if not r.getObject().isImage(): continue
767            tmp = [r.getObject().UID(), r.getId, to_unicode(r.Title).encode('iso-8859-15'), int(r.getObject().isAudio()), int(r.getHasCoverImage)]
768            result.append(tmp)
769        return str(result)
770
771    security.declarePublic('public_getSortCriterion')
772    def public_getSortCriterion(self,topic):
773        """ Topics have this useful getSortCriterion -method, but it's available only if allowed to change content."""
774        if hasattr(topic, 'hasSortCriterion'):
775            if topic.hasSortCriterion():
776                return topic.getSortCriterion().field
777        return False
778
779    def filterContent(self, results, show_what=''):
780        """ Currently only contents use this, other SectionFolders traverse here because stupid test(x,y,z) implementation """
781        return results
782
783    def getDefaultIcon(self, meta_type, obj=None):
784        """ general method for getting proper icon for object, used when only catalog-metadata is available """       
785        address=DEFAULT_ICONS[meta_type]
786        if address!='piece':
787            return address
788        else:
789            try:
790                obj=obj.getObject()
791                return obj.getDefaultIcon()
792            except (AttributeError, KeyError):
793                return DEFAULT_ICONS['Piece']
794
795    def removeBadResults(self, results, pageid):
796        """ filter out those without titles if viewing a list of titles """
797        if pageid.endswith('_list'):
798            results=[r for r in results if r.Title]
799        return results
800
801    def getTitleOfACLGroup(self, group):
802        """ gfjreih5eh """
803        return getToolByName(self, 'portal_groups').getGroupareaFolder(group.getId()).title_or_id()
804
805
806class LargeContentFolder(LargeSectionFolder):
807
808    archetype_name = "Large Content Folder"
809    meta_type = "Large Content Folder"
810
811    allowed_content_types = CONTENT_TYPES +('Topic','Redirector')
812    default_view = ('lemill_content_view')
813    filter_content_types = True
814    security = ClassSecurityInfo()
815
816
817    def getTemplates(self, aslist=False):
818        if not aslist:
819            return TEMPLATES
820        else:           
821            # as list = ordered list of dictionaries, original key is under key 'keyname'
822            rl=[(value['order'], key, value) for (key,value) in TEMPLATES.items()]
823            rl.sort()
824            resl=[]
825            for (order, key, value) in rl:
826                value['keyname']=key
827                resl.append(value)
828            return resl
829
830
831    def getTemplate(self, template_id):
832        return TEMPLATES.get(template_id, None)
833
834    def filterContent(self, results, show_what='resources'):
835        if show_what=='content':
836            allowed=CONTENT_TYPES
837        elif show_what=='activities':
838            allowed=('Activity',)
839        elif show_what=='tools':
840            allowed=('Tool',)
841        elif show_what=='resources':
842            allowed=MATERIAL_TYPES
843        elif show_what=='pieces':
844            allowed=('Piece',)
845        elif show_what=='references':
846            allowed=('LeMillReference',)
847
848        else: return results
849        return filter(lambda x: x.portal_type in allowed, results)
850
851
852
853    def tagcloud_type(self,topic_name):
854        """ There are content tag clouds by titles """
855        if topic_name=='pieces': return 'piece_titles'
856        elif topic_name=='materials': return 'material_titles'
857        elif topic_name=='references': return 'reference_titles' # I'm not sure if these are used
858        elif topic_name=='content': return 'content_titles'
859        else: return 'hits'
860
861    security.declareProtected(ADD_CONTENT_PERMISSION,'lemidlet_post')
862    def lemidlet_post(self, REQUEST):
863        """ LeMidlet will post image here...."""
864        #print REQUEST.file.read()
865        file = REQUEST.get('file')
866        if file==None:
867            return 'file not found'       
868        description = REQUEST.get('description')
869        type = 'Piece'
870        new = self._lemill_invokeFactory(self, type, id=type+str(id(self)), do_create=True)
871        new.edit(description=description, file=file.read(),language='',title=REQUEST.get('title'),tags=REQUEST.get('tags'))
872        return 0
873
874       
875class LargeActivityFolder(LargeSectionFolder):
876
877    archetype_name = "Large Activity Folder"
878    meta_type = "Large Activity Folder"
879
880    allowed_content_types = ('Activity','KB', 'Topic','Redirector')
881    default_view = ('lemill_activities_view')
882    filter_content_types = True
883
884    def tagcloud_type(self,topic_name):
885        """ Other than community tag clouds count tags by their popularity, except hits """
886        if topic_name=='methods': return 'activity_titles'
887        else: return 'hits'
888   
889class LargeToolFolder(LargeSectionFolder):
890
891    archetype_name = "Large Tool Folder"
892    meta_type = "Large Tool Folder"
893
894    allowed_content_types = ('Tool', 'Topic','Redirector')
895    default_view = ('lemill_tools_view')
896    filter_content_types = True
897
898    def tagcloud_type(self,topic_name):
899        """ Other than community tag clouds count tags by their popularity, aka hits """
900        if topic_name=='tools': return 'tool_titles'
901        else: return 'hits'
902
903
904class LargeCommunityFolder(LargeSectionFolder):
905
906    archetype_name = "Large Community Folder"
907    meta_type = "Large Community Folder"
908
909    allowed_content_types = ('Topic','Redirector','MemberFolder','GroupBlog')
910    default_view = ('lemill_community_view')
911    filter_content_types = True
912    security = ClassSecurityInfo()
913
914    schema=communityschema
915
916    ### If MemberFolder for logged in user does not exist, it is created here
917   
918    def my_page(self):
919        """ Checks if user has MemberFolder and creates one if not. Returns the folder url."""
920        mtool = getToolByName(self, "portal_membership")
921        member=mtool.getAuthenticatedMember()
922        if not member: return
923        if member.getHomeFolder()==None:
924            member.createMemberarea()           
925        folder=member.getHomeFolder()
926        if not hasattr(folder.aq_base,'collections'):
927            folder.invokeFactory('CollectionsFolder', id='collections')
928        return folder.absolute_url()   
929
930    def tagcloud_type(self,topic_name):
931        """ Community tag clouds can use people's activities or group activities as base """
932        if topic_name=='members': return 'member_titles'
933        elif topic_name=='groups': return 'group_titles'
934        else: return 'hits'
935       
936
937    def getCollaboration_proposals(self):
938        """ Because I prefer lists, not tuples """
939        return list(self.getField('collaboration_proposals').get(self))
940       
941    security.declareProtected(ADD_CONTENT_PERMISSION,'addCollaboration_proposal')
942    def addCollaboration_proposal(self, obj_uid):
943        """ Collaboration proposals are stored as list of UID:s, because then we don't have to care about group path when finding them """
944        uid_cat = getToolByName(self, "uid_catalog")
945
946        def post_date(uid):
947            post=uid_cat(UID=uid)
948            post=post[0].getObject()
949            return post.creation_date                       
950
951        current_date = DateTime()
952        cp= self.getCollaboration_proposals()
953        if obj_uid not in cp:
954            cp=[obj_uid]+cp
955        # pop out old collaboration proposals from tail of the list
956        try:
957          while post_date(cp[-1])< (current_date-31):
958            cp.pop()
959          cp=cp[:100]
960          cp_field=self.getField('collaboration_proposals')
961          cp_field.set(self, cp)
962        except IndexError:
963          pass
964
965    security.declareProtected(ADD_CONTENT_PERMISSION,'removeCollaboration_proposal')
966    def removeCollaboration_proposal(self, obj_uid):
967        cp= self.getCollaboration_proposals()
968        if obj_uid in cp:
969            cp.remove(obj_uid)
970        cp_field=self.getField('collaboration_proposals')
971        cp_field.set(self, cp)
972
973    def mergeLatestPostsInMyGroups(self):
974        mtool = getToolByName(self, "portal_membership")
975        gtool = getToolByName(self, "portal_groups")
976        member=mtool.getAuthenticatedMember()
977        memberid=member.getId()
978        glist = self.lemill_usertool.getGroupsList(memberid);
979        recents=[]
980        for group in glist:
981            gname= group.getGroupName()
982            garea= gtool.getGroupareaFolder(gname)
983            grecent= garea.getRecent_posts()
984            for postid in grecent:
985                try:
986                    post=garea._getOb(postid)
987                    recents.append((post.CreationDate(),post))
988                except AttributeError:
989                    # do some cleaning:
990                    garea.removeRecent_post(postid)
991        recents.sort()
992        recents = [x[1] for x in recents]
993        recents.reverse()
994        return recents[:5]
995                           
996    def getBrowsedStuff(self, topic_name):
997        """ returns nice name for currently browsed content type"""
998        if BROWSING_TITLE_FROM_LOCATION.has_key(topic_name):           
999            return BROWSING_TITLE_FROM_LOCATION[topic_name]
1000        else:
1001            return self.getId()
1002       
1003       
1004    def getCollections(self, obj_uid=None):
1005        """ Show collections where object is used."""
1006        if not obj_uid:
1007            return []
1008        res = []
1009        q = { 'targetUID': obj_uid }
1010        qres = self.reference_catalog(q)
1011        for q in qres:
1012            v = self.reference_catalog.lookupObject(q.UID)
1013            if v:
1014                source = v.getSourceObject()
1015                if source.meta_type == 'Collection':
1016                    res.append(source)
1017        return res
1018
1019    def filterContent(self, results, show_what='resources'):
1020        if show_what=='content':
1021            allowed=CONTENT_TYPES
1022        elif show_what=='activities':
1023            allowed=('Activity',)
1024        elif show_what=='tools':
1025            allowed=('Tool',)
1026        elif show_what=='resources':
1027            allowed=MATERIAL_TYPES
1028        elif show_what=='pieces':
1029            allowed=('Piece',)
1030        elif show_what=='references':
1031            allowed=('LeMillReference',)
1032
1033        else: return results
1034        return filter(lambda x: x.portal_type in allowed, results)
1035
1036class LargeTrashFolder(LargeSectionFolder):
1037
1038    archetype_name = "Large Trash Folder"
1039    meta_type = "Large Trash Folder"
1040
1041    allowed_content_types = ALL_CONTENT_TYPES
1042    filter_content_types = False
1043    default_view = ('lemill_trash_view')
1044    security = ClassSecurityInfo()
1045
1046       
1047registerType(LargeContentFolder, PROJECTNAME)
1048registerType(LargeActivityFolder, PROJECTNAME)
1049registerType(LargeToolFolder, PROJECTNAME)
1050registerType(LargeCommunityFolder, PROJECTNAME)
1051registerType(LargeTrashFolder, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.