source: trunk/LargeSectionFolder.py @ 1354

Revision 1354, 42.7 KB checked in by jukka, 13 years ago (diff)

Worked on Title clouds, spent 5 hours. Sizes are calculated right but lots of small stuff is still needed.

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