source: trunk/SectionFolder.py @ 1302

Revision 1302, 39.8 KB checked in by pjotr, 13 years ago (diff)

Fixes #1103, someone check out if that works for you

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