source: trunk/SectionFolder.py @ 485

Revision 485, 18.8 KB checked in by szabolcs, 13 years ago (diff)

changed "== 'Material'" to "!= 'Piece'" (#489)

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 PROJECTNAME, SHOW_METADATA_FIELDS, TEMPLATES, to_unicode, MIMETYPE_WHITELIST
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
29
30communityschema= ATFolderSchema + Schema((
31    LinesField('collaboration_proposals',
32        default= [],
33        widget = LinesWidget(
34            visible = {'view':'invisible', 'edit':'invisible'},
35            )
36        )
37
38))
39
40
41
42class SectionFolder(ATFolder):
43    """Section Folder"""
44       
45    archetype_name = "Section Folder"
46    meta_type = "Section Folder"
47
48    # Override initializeArchetype to turn on syndication by default
49    def initializeArchetype(self, **kwargs):
50        ret_val = ATFolder.initializeArchetype(self, **kwargs)
51        # Enable topic syndication by default
52        syn_tool = getToolByName(self, 'portal_syndication', None)
53        if syn_tool is not None:
54            if syn_tool.isSiteSyndicationAllowed():
55                try:
56                    syn_tool.enableSyndication(self)
57                except: # might get 'Syndication Information Exists'
58                    pass
59        return ret_val
60
61    def _lemill_invokeFactory(self, container, meta_type, id=None, title=''):
62        """ add new object, edit it's title and invoke _renameAfterCreation """
63        if id is None:
64            id = self.generateUniqueId(meta_type)
65        new_id = container.invokeFactory(meta_type, id)
66        new = getattr(container, new_id)
67        new.edit(title = title)
68        renamed = new._renameAfterCreation()
69        if not renamed:
70            renamed = new_id
71        obj = getattr(container, renamed)
72        return obj
73
74    def start_new_version(self, REQUEST, objId):
75        """
76        Start a new version of an object. This is done by creating new object and
77        then, based on schema, all data is copyied from an object to new object.
78        objId - relative URL to object like /activities/demo_activity
79                or /activities/remote/<remote_server_address>/activities/demo_activity
80        """
81        base_obj = self.restrictedTraverse(objId)
82        meta_type = base_obj.meta_type
83        folder_path = objId.split('/')
84        folder_url = '/'.join(folder_path[:-1])
85        folder = self.restrictedTraverse(folder_url)
86        #new_id = folder.invokeFactory(meta_type, id=meta_type.lower()+str(id(base_obj)))
87        #new = getattr(folder, new_id)
88        #new.edit(title=base_obj.Title())
89        new = self._lemill_invokeFactory(folder, meta_type, id=meta_type.lower()+str(id(base_obj)), title=base_obj.Title())
90        for k in base_obj.schema.keys():
91            # list all fields here that shouldn't be copyied to new object
92            if k in ['id', 'effectiveDate', 'expirationDate', 'creation_date', 'modification_date']:
93                continue
94            old_accessor = base_obj.schema[k].getEditAccessor(base_obj)
95            new_mutator = new.schema[k].getMutator(new)
96            if k == 'parentVersion':
97                new_mutator(base_obj.absolute_url())
98                continue
99            val = old_accessor()
100            new_mutator(val)
101        mess = 'New %s has been created for you!' % meta_type.lower()
102        REQUEST.set('portal_status_message', mess)
103        return new.base_edit(portal_status_message=mess)
104
105    def getMetadaFieldsToShowInEditor(self, object):
106        """ gets fields which are shown in metadata edit area """
107        type = object.meta_type
108        shownFieldsList = SHOW_METADATA_FIELDS[type]
109        shownFields = []
110        fields = object.Schemata()['metadata'].fields()
111        # At this point, the method can return a list of all metadata
112        ### HACK FIX TO GET THIS PUBLISHED TODAY ###
113        #if type=='Material':
114        #    fields = object.Schemata()['default'].fields() + object.Schemata()['metadata'].fields()
115        #    fields=filter(lambda x: str(x) not in ['<Field title(string:rw)>', '<Field bodyText(string:rw)>', '<Field file(file:rw)>', '<Field source(file:rw)>', '<Field audio(file:rw)>', '<Field refsToImages(reference:rw)>', '<Field age_group(string:rw)>'], fields)
116        #    return fields
117
118        ### HACK FIX ENDS ###
119        if 'all' in shownFieldsList:
120            return fields
121        else:
122            for field in fields:
123                if field.getName() in shownFieldsList:
124                    shownFields.append(field)
125            return shownFields
126
127    def amIManager(self):
128        """Check whether I'm a manager."""
129        return 'Manager' in self.portal_membership.getAuthenticatedMember().getRoles()
130
131    def whoami(self):
132        return self.portal_membership.getAuthenticatedMember().getId()       
133
134    def getMember(self,uname=None):
135        if not uname:
136            uname=self.whoami()
137        try:
138            return getattr(self.community,uname)
139        except AttributeError:
140            return None
141       
142    def getSamples(self, search_results):
143        """ Pick three random objects from search results that have non-default images to display in folders front pages. """
144        pic_results = [x for x in search_results if x.meta_type != 'Piece' and x.getHasCoverImage and x.review_state=='public' and x.Title]
145        n=min(3,len(pic_results))
146        samples=sample(pic_results,n)
147        return samples
148
149    def makeSortable_title(self, title):
150        """Helper method partially copied from CMFPlone.CatalogTool """
151        putils= getToolByName(self, 'plone_utils')
152        def_charset = putils.getSiteEncoding()
153        num_sort_regex = re.compile('\d+')
154        sortabletitle = title.lower().strip()
155        # Replace numbers with zero filled numbers
156        sortabletitle = num_sort_regex.sub(lambda matchobj: matchobj.group().zfill(8), sortabletitle)
157        # Truncate to prevent bloat
158        for charset in [def_charset, 'latin-1', 'utf-8']:
159            try:
160                sortabletitle = unicode(sortabletitle, charset)[:30]
161                sortabletitle = sortabletitle.encode(def_charset or 'utf-8')
162                break
163            except UnicodeError:
164                pass
165            except TypeError:
166                # If we get a TypeError if we already have a unicode string
167                sortabletitle = sortabletitle[:30]
168                break
169        return sortabletitle
170
171       
172    def getTopResults(self, search_results, index_type):
173        """ Should return top three populated subgroups of search results in given index  """
174        pc = getToolByName(self, 'portal_catalog')
175        if index_type in pc.indexes() and index_type in pc.schema():
176            uniques = pc.uniqueValuesFor(index_type)
177            if uniques == ():
178                return []
179            hits={}
180            for a in uniques:
181                hits[a]=0
182            for counter in search_results:
183                if counter.review_state!='hidden':   
184                    values=getattr(counter, index_type)
185                    if isinstance(values,str):
186                         hits[values]=hits[values]+1
187                    else:
188                        try:
189                            for a in values:
190                                hits[a]=hits[a]+1
191                        except TypeError:
192                            pass # something wrong with data - let's continue without it
193            topthree= [(x[1],x[0]) for x in hits.items() if x[1]>0]
194            topthree.sort()
195            topthree.reverse()
196            topthree= topthree[:min(3,len(topthree))]
197            topthree=[x[1] for x in topthree]
198           
199            return topthree
200        else:
201            return []
202
203    def getUniques(self, search_results, index_type):
204        """ Should return unique values of search results in given index, results are list of index-metadata-like tuples in alphabetical order """
205        pc = getToolByName(self, 'portal_catalog')
206        uniquetuplelist=[]
207        maxcount=0
208        def adjust(i):
209            # helper method to adjust hit count of this tag to relative size (1,...,8)
210            (a,b,c,d,e)=i
211            if b>1:
212                b=int((8*b)/maxcount)
213            i=(a,b,c,d,e)
214            return i
215
216        # -- Step 1 -- : Make list of unique values for this index. For members and groups this is time to collect their urls and title.
217        if index_type in pc.indexes() and index_type!='getSortable_nicename' and index_type!='sortable_title':
218            uniques = pc.uniqueValuesFor(index_type)
219        elif index_type=='getSortable_nicename':
220            people = pc.searchResults(portal_type='MemberFolder')
221            uniques = [x.id for x in people]
222        elif index_type=='sortable_title':
223            groups = pc.searchResults(portal_type='GroupBlog')
224            uniques = [x.id for x in groups]
225        if uniques == ():
226            return []
227        hits={}
228
229        # -- Step 2 -- : Use list to make a dictionary. Values of list are keys.
230        for a in uniques:
231            hits[a]=(0,'','','') # value : (hits, objectURL, sort_title, title)
232
233        # -- 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.
234        if index_type not in pc.schema() or index_type=="getSortable_nicename":
235
236            if index_type=="sortable_title":
237                # Just get activity score from group metadata
238                for group in groups:
239                    hits[group.id]=(max(1, group.getActivity_score), group.getURL(), self.makeSortable_title(group.Title), group.Title)
240
241            elif index_type=="getSortable_nicename":
242                # Just get activity score from member metadata
243                for member in people:
244                    hits[member.id]=(max(1, member.getActivity_score), member.getURL(), member.getSortable_nicename, member.getNicename)
245            else:
246                return []
247        else:
248            # Vanilla hits counter
249            spent_urls=[]
250            for counter in search_results:
251                c_url=counter.getURL()
252                if counter.review_state!='hidden' and not c_url in spent_urls:
253                    values=getattr(counter, index_type)
254                    if values!=None:
255                        if type(values)==str or type(values)==int:
256                            hits[values]=(hits[values][0]+1, c_url, values, values)
257                        else:
258                            for a in values:
259                                hits[a]=(hits[a][0]+1, c_url, a, a)
260                    spent_urls.append(c_url)
261        # OK, the story so far: hits = dictionary, where keys are the distinct values of current index, or memberid:s
262        # values of this dictionary are tuples, where:
263        # value[0]= n of objects that have this value,
264        # value[1]= url-of-object that has this value (sensible only if there is one object with this value, like names for users),
265        # value[2]= title, nicer representation of key's name (title for group names and nicename for member names)
266        # value[3]= another_title, because sortable_nicename already takes the title place for members. ugly...
267
268        # -- Step 4 -- : Build a list from previous dictionary. Objects in list are tuples. Order list alphabetically.
269        # This dictionary should be ordered alphabetically by title.
270        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]       
271        # uniquetuplelist now contains dictionary reordered: (sort_title, count, url, indexvalue, title)
272        if uniquetuplelist==[]:
273            return uniquetuplelist
274        maxcount=max(map(lambda x: x[1], uniquetuplelist))
275        uniquetuplelist=map(adjust, uniquetuplelist)
276        uniquetuplelist.sort()
277        return uniquetuplelist
278
279    def getTagURL(self,context,category,value):
280        value=to_unicode(value)
281        return u"%s?%s=%s" % (context.absolute_url(),category,value)
282
283    def js_queryForPieces(self, keyword):
284        """ javascript is querying for pieces that are images """
285        result = []
286        stool = getToolByName(self, 'lemill_search')
287        q = {'SearchableText': keyword,
288             'portal_type': ['Piece', ]
289        }
290        q_results = stool.local_search(q)
291        for r in q_results:
292            if not r.getObject().isImage(): continue
293            tmp = [r.getObject().UID(), r.getId, to_unicode(r.Title).encode('iso-8859-15')]
294            result.append(tmp)
295        return str(result)
296
297class ContentFolder(SectionFolder):
298
299    archetype_name = "Content Folder"
300    meta_type = "Content Folder"
301
302    allowed_content_types = ('Piece', 'Material', 'Topic', 'PresentationMaterial')
303    default_view = ('lemill_content_view')
304    filter_content_types = True
305
306    def askPublish(self, REQUEST):
307        """ask the user for publish the content, in the add_content_wizard"""
308        obj_id = REQUEST.get('id')
309        publish = REQUEST.get('publish')
310        keep = REQUEST.get('keep')
311        obj = getattr(self, obj_id)
312        if keep is None:
313            obj.content_status_modify(workflow_action='publish')
314            return REQUEST.RESPONSE.redirect(obj.absolute_url_path())
315        if publish is None:
316            return REQUEST.RESPONSE.redirect(obj.absolute_url_path())
317   
318    def uploadIt(self, REQUEST):
319        """ gets file from upload and makes new object.
320            also does some content-type whitelist checking here.
321        """
322        file = REQUEST.get('file')
323        user_title = REQUEST.get('user_title')
324        user_description = REQUEST.get('user_description')
325        content_type = file.headers['Content-Type']
326        type = ''
327        print content_type
328        if content_type in MIMETYPE_WHITELIST:
329            type = 'Piece'
330       
331        if not type or type != 'Piece':
332            return REQUEST.RESPONSE.redirect('lemill_explain_upload_fail')
333       
334        new = self._lemill_invokeFactory(self, type, id=type+str(id(self)), title=user_title)
335        new.edit(description=user_description, file=file.read())
336        if type=='Piece':
337            new.edit(language='')
338        new_id = new.getId()
339        return REQUEST.RESPONSE.redirect(new.absolute_url()+'/piece_edit')
340        #return REQUEST.RESPONSE.redirect(self.absolute_url()+'/lemill_check_content?id='+new_id+'&type='+type)
341
342    def getTemplates(self):
343        return TEMPLATES
344
345    def getTemplate(self, template_id):
346        return TEMPLATES.get(template_id, None)
347       
348    def hideContent(self, REQUEST):
349        """Hide the content, replacing the delete function"""
350        obj_id = REQUEST.get('obj_id')
351        obj = getattr(self, obj_id)
352        obj.content_status_modify(workflow_action='hide')
353        return REQUEST.RESPONSE.redirect(self.portal_url())
354       
355class ActivityFolder(SectionFolder):
356
357    archetype_name = "Activity Folder"
358    meta_type = "Activity Folder"
359
360    allowed_content_types = ('Activity','KB', 'Topic')
361    default_view = ('lemill_activities_view')
362    filter_content_types = True
363   
364class ToolFolder(SectionFolder):
365
366    archetype_name = "Tool Folder"
367    meta_type = "Tool Folder"
368
369    allowed_content_types = ('Tool', 'Topic')
370    default_view = ('lemill_tools_view')
371    filter_content_types = True
372
373class CommunityFolder(SectionFolder):
374
375    archetype_name = "Community Folder"
376    meta_type = "Community Folder"
377
378    allowed_content_types = ('Topic')
379    default_view = ('lemill_community_view')
380    filter_content_types = True
381
382    schema=communityschema
383   
384    def my_page(self):
385        """ Checks if user has MemberFolder and creates one if not. Returns the folder url."""
386        mtool = getToolByName(self, "portal_membership")
387        member=mtool.getAuthenticatedMember()
388        if member.getHomeFolder()==None:
389            member.createMemberarea()
390        folder=member.getHomeFolder()   
391        return folder.absolute_url()   
392
393    def getCollaboration_proposals(self):
394        """ Because I prefer lists, not tuples """
395        return list(self.getField('collaboration_proposals').get(self))
396       
397    def addCollaboration_proposal(self, obj_uid):
398        """ Collaboration proposals are stored as list of UID:s, because then we don't have to care about group path when finding them """
399        uid_cat = getToolByName(self, "uid_catalog")
400
401        def post_date(uid):
402            post=uid_cat(UID=uid)
403            post=post[0].getObject()
404            return post.creation_date                       
405
406        current_date = DateTime()
407        cp= self.getCollaboration_proposals()
408        cp=[obj_uid]+cp
409        # pop out old collaboration proposals from tail of the list
410        while post_date(cp[-1])< (current_date-31):
411            cp.pop()
412        cp=cp[:100]
413        cp_field=self.getField('collaboration_proposals')
414        cp_field.set(self, cp)
415
416    def removeCollaboration_proposal(self,obj_uid):
417        cp= self.getCollaboration_proposals()
418        if obj_uid in cp:
419            cp.remove(obj_uid)
420        cp_field=self.getField('collaboration_proposals')
421        cp_field.set(self, cp)
422
423    def mergeLatestPostsInMyGroups(self):
424        mtool = getToolByName(self, "portal_membership")
425        gtool = getToolByName(self, "portal_groups")
426        member=mtool.getAuthenticatedMember()
427        memberid=member.getId()
428        glist = self.lemill_usertool.getGroupsList(memberid);
429        recents=[]
430        for group in glist:
431            gname= group.getGroupName()
432            garea= gtool.getGroupareaFolder(gname)
433            grecent= garea.getRecent_posts()
434            for postid in grecent:
435                try:
436                    post=garea._getOb(postid)
437                    recents.append((post.Date,post))
438                except AttributeError:
439                    # do some cleaning:
440                    garea.removeRecent_post(postid)
441        recents.sort()
442        recents = [x[1] for x in recents]
443        return recents[:5]
444                           
445       
446       
447       
448registerType(ContentFolder, PROJECTNAME)
449registerType(ActivityFolder, PROJECTNAME)
450registerType(ToolFolder, PROJECTNAME)
451registerType(CommunityFolder, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.