source: trunk/MemberFolder.py @ 3026

Revision 3026, 31.7 KB checked in by jukka, 10 years ago (diff)

Fixed #1963. Resource types have now useful descriptions.

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 Products.Archetypes.public import *
20from Products.ATContentTypes.content.folder import ATFolder
21from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget
22from Products.CMFCore.permissions import ModifyPortalContent
23from Products.Archetypes.public import BaseFolder, BaseFolderSchema, registerType
24from Products.Archetypes.atapi import DisplayList
25from Globals import InitializeClass
26from Products.CMFCore.utils import getToolByName
27from AccessControl import ClassSecurityInfo, Unauthorized
28from config import *
29from Schemata import coverImage, score, latest_edit_schema, state
30from permissions import ModerateContent, MODIFY_CONTENT, ACCESS_CONTENT, VIEW
31from Products.PloneLanguageTool.availablelanguages import countries
32from FieldsWidgets import HTMLLinkWidget
33try:
34    from Products.PloneLanguageTool.availablelanguages import languages_english
35except ImportError:
36    from Products.PloneLanguageTool.availablelanguages import languages
37    languages_english = dict([(key, val['english']) for (key, val) in languages.iteritems()])
38    del languages
39
40from messagefactory_ import i18nme as _
41from FieldsWidgets import TagsField, TagsWidget, MessengerWidget, MobileWidget, LeTextAreaWidget, TwoColumnMultiSelectionWidget
42from CommonMixIn import CommonMixIn, CoverImageMixIn
43from LargeSectionFolder import LeMillFolder
44from Acquisition import ImplicitAcquisitionWrapper
45from DateTime import DateTime
46import datetime, time
47import random
48
49### MemberFolders should be created automatically. If users get their account from other authentication scheme, their folders can be created with methods at LargeCommunityFolder (my_page -method is run, if top-right-corner link to memberpage fails)
50
51messenger_vocabulary = [(x,x) for x in INSTANT_MESSENGERS]
52
53# if it is plausible that just this field is searched somewhere, then field can have its own index. Otherwise it should be just 'searchable=True'. Those are all kept in one big text index.
54
55memberfolder_schema = Schema((
56    StringField('title',
57        mode ='r',
58        # default_method is called only once, when first needed; after that, getXxx is used
59        widget=StringWidget(
60            label_msgid='label_title',
61            visible={'edit' : 'invisible'},           
62        ),
63    ),
64    StringField('firstname',
65        searchable = True,
66        required = True,
67        widget = StringWidget(
68             label = 'First name',
69             label_msgid = 'label_firstname',
70             i18n_domain = "lemill"
71             )
72        ),
73    StringField('lastname',
74        searchable = True,
75        required = True,
76        widget = StringWidget(
77             label = 'Last name',
78             label_msgid = 'label_lastname',
79             i18n_domain = "lemill"
80             )
81        ),
82    StringField('full_name',
83        searchable = True,
84        widget = StringWidget(
85             visible = {'view':'invisible', 'edit':'invisible'},
86             )
87        ),
88    ComputedField('nicename', # so that displayed name can be pulled from catalog metadata
89        index= 'FieldIndex:schema',
90        expression = 'here.getNicename()',
91        ),
92       
93    StringField('email',
94        searchable = True,
95        required = True,
96        index = 'FieldIndex:schema',
97        validators = 'isEmail',
98        widget = StringWidget(
99             label = 'Email address',
100             label_msgid = 'label_email',
101             i18n_domain = "lemill"
102             )
103        ),
104
105    StringField('mobile',
106        widget = MobileWidget(
107            label = 'Phone',
108            label_msgid = 'label_phone',
109            description = 'Check the box if you approve community members sending text messages.',
110            description_msgid = 'help_mobile',
111            i18n_domain ='lemill',
112            )
113    ),         
114
115    StringField('messenger1',
116        vocabulary = DisplayList(messenger_vocabulary),
117        widget = MessengerWidget(
118             label = 'Instant messengers',
119             label_msgid = 'label_messenger',
120             i18n_domain = "lemill"
121             )
122        ),
123    StringField('messenger2',
124        vocabulary = DisplayList(messenger_vocabulary),
125        widget = MessengerWidget(
126             label = ' ',
127             )
128        ),
129    StringField('messenger3',
130        vocabulary = DisplayList(messenger_vocabulary),
131        widget = MessengerWidget(
132             label = ' ',
133             )
134        ),
135    StringField('slideshare_username',
136        widget = StringWidget(
137            label = 'Slideshare user name',
138            label_msgid ='label_slideshare_user',
139            description = 'If you want to upload presentations from Slideshare, LeMill needs to know where to look for them. Username is not visible for other users in LeMill. We will never ask your password.',
140            description_msgid = 'help_slideshare_user',
141            i18n_domain = 'lemill'
142            )
143        ),
144    StringField('location_country',
145        index = 'FieldIndex:schema',
146        searchable = True,
147        vocabulary = 'getCountrylist',
148        widget = SelectionWidget(
149             label = 'Country',
150             label_msgid = 'label_country',
151             i18n_domain = "lemill"
152             )
153        ),       
154    StringField('location_area',
155        searchable = True,
156        widget = StringWidget(
157             label = 'City or area',
158             label_msgid = 'label_area',
159             i18n_domain = "lemill"
160             )
161        ),
162    StringField('home_page',
163        widget = HTMLLinkWidget(
164             label = 'Homepage',
165             label_msgid = 'label_home_page',
166             i18n_domain = "lemill",
167             validators = 'isURL'
168             )
169        ),
170    LinesField('language_skills',
171        default = [],
172        index = 'KeywordIndex:schema',
173        vocabulary = 'getLanguagelist',
174        widget = PicklistWidget(           
175             label = 'Languages',
176             label_msgid = 'label_language_skills',
177             description = "Choose languages you can use to create learning resources or communicate.",
178             description_msgid = 'help_language_skills',
179             i18n_domain = "lemill"
180             )
181        ),       
182    TagsField('skills',
183        index = 'KeywordIndex:schema',
184        widget = TagsWidget(           
185             label = 'Skills',
186             label_msgid = 'label_skills',
187             description = "Enter a list of your skills separated by commas.",
188             description_msgid = 'help_skills',
189             i18n_domain = "lemill"
190             )
191        ),
192    TagsField('interests',
193        index = 'KeywordIndex:schema',
194        widget = TagsWidget(
195             label = 'Interests',
196             label_msgid = 'label_interests',
197             description = "Enter a list of your interests separated by commas.",
198             description_msgid = 'help_interests',
199             i18n_domain = "lemill"
200             )
201        ),
202    LinesField('subject_area',
203        vocabulary = DisplayList([(x,x) for x in SUBJECT_AREAS]),
204        index = "KeywordIndex:schema",
205        multivalued=True,
206        searchable=True,
207        widget=TwoColumnMultiSelectionWidget(
208            format="checkbox",
209            label="Subject area",
210            label_msgid="label_member_subject_area",
211            description="Choose subject areas that you are teaching.",
212            description_msgid="description_member_subject_area",
213            i18n_domain="lemill"
214            )
215        ),
216    ComputedField('tags',
217        index = 'KeywordIndex:schema',
218        expression = 'here.getTags()',
219        ),
220
221    StringField('biography',
222        searchable = True,
223        widget = LeTextAreaWidget(
224            label = 'Biography',
225            label_msgid = 'label_your_biography',
226            description = "Any background information about yourself that you wish to share with others.",
227            description_msgid = 'help_your_biography',
228            i18n_domain = "lemill"
229            )
230        ),
231
232    StringField('wysiwyg_editor',
233            default='kupu',
234            mode = 'r',
235            widget = StringWidget(
236                visible = {'view':'invisible', 'edit':'invisible'},
237                ),
238            ),
239    ReferenceField('listOfContacts',
240        required = False,
241        searchable = False,
242        relationship = 'is contact of',
243        accessor = 'getListOfContacts',
244        mutator = 'addListOfContacts',
245        multiValued = True,
246        widget = ReferenceWidget(
247            visible = {'view':'invisible', 'edit':'invisible'},
248            ),
249        ),
250    LinesField('notification_properties',
251        vocabulary = DisplayList((
252            ("resource_edited", "Someone edited your resource"),
253            ("resource_added_to_collection", "Someone added your resource to a collection"),
254            ("resource_discussion_note", "Someone wrote a discussion note about your resource"),
255            ("contact_added", "Someone added you as a contact"),
256            ("contact_published_resource", "Your contact published a new resource"),
257            ("group_joined", "Someone joined your group"),
258            ("group_message_posted", "Someone posted a message in group forum"),
259            )),
260        multivalued = True,
261        default = ('resource_edited', 'resource_added_to_collection', 'resource_discussion_note', 'contact_added', 'contact_published_resource', 'group_joined', 'group_message_posted'),
262        widget = MultiSelectionWidget(
263            format = "checkbox",
264            visible = {'view':'invisible','edit':'visible'},
265            label = "E-mail announcements about changes in LeMill",
266            label_msgid = "label_notification_properties",
267            description = "The system will automatically send e-mail messages to you for all the checked events.",
268            description_msgid = "description_notification properties",
269            i18n_domain = "lemill",
270        ),
271    ),
272
273))
274
275schema = BaseFolderSchema + coverImage + score + latest_edit_schema + state + memberfolder_schema
276
277schema = schema.copy()
278schema['coverImage'].original_size=(140,120)
279
280class MemberFolder(CoverImageMixIn,CommonMixIn, BaseFolder):
281    """Member folder"""
282
283    meta_type = "MemberFolder"
284    archetype_name = "MemberFolder" 
285    typeDescription="Member folder"
286    typeDescMsgId='description_memberfolder'
287    global_allow = 1
288    portlet = 'here/portlet_member/macros/portlet'
289    default_location = 'community/people'
290
291    allowed_content_types = ('CollectionsFolder', 'Topic')
292    default_view = ('member_view')
293    filter_content_types = True
294    security = ClassSecurityInfo()
295    schema = schema
296
297    actions = (
298        {'id':'view',
299         'name':'View',
300         'action':"string:${object_url}/member_view",
301         'permission':(VIEW,),
302        },
303        {'id':'edit',
304         'name':'Edit',
305         'action':"string:${object_url}/personalize_form",
306         'permission':(MODIFY_CONTENT,),
307        },
308    )   
309    aliases = {
310        '(Default)': '',
311        'edit': 'personalize_form',
312        'view':'member_view'
313        }
314
315    def manage_afterAdd(self, item, container):
316        BaseFolder.manage_afterAdd(self, item, container)
317        # Handle case when logged in user is authenticated from an
318        # external source for the first time
319        try:
320            # This is not very nicely done, should use API methods to access what I need
321            uinfo = self.acl_users.source_users.getUserInfo(self.Creator())
322        except KeyError: # Doesn't exist yet - create
323            self.acl_users.source_users.addUser(self.Creator(),self.Creator(),'')
324            self.acl_users.portal_role_manager.assignRolesToPrincipal(('Member',),self.Creator())
325        # If we've done external authentication, then the user isn't really yet
326        # properly authenticated, so we can't create
327        # the collections and resources folders.
328        self.reindexObject()
329
330    def setPermissions(self):
331        # Setting View permission
332        self.manage_permission(VIEW, ('Manager','Owner','Member','Authenticated','Anonymous'), acquire=0)
333        # Setting Edit permission
334        self.manage_permission(MODIFY_CONTENT, ('Manager','Owner'), acquire=0)
335        # Setting Review permission
336        self.manage_permission(ModerateContent, ('Manager','Owner','Reviewer'), acquire=0)
337        # Setting Access permission
338        self.manage_permission(ACCESS_CONTENT, ('Manager','Owner','Member','Authenticated','Anonymous'), acquire=0)
339
340    def setFullname(self, value):
341        """ had to rename fullname-field, but its set method is still widely used """ 
342        self.setFull_name(value)
343
344    def getMemberId(self):
345        """ return user's id """
346        return self.Creator()
347
348    def getMetaDescription(self):
349        """ Biography is a good description, but if not filled, then skills, interests or subject areas  """
350        return self.getBiography() or ', '.join(self.getTags()) or ', '.join(self.getSubject_area())
351
352    def getLocation_country(self):
353        """ a getter to fix problem with wrapped values """
354        value= self.getField('location_country').get(self)
355        if type(value)==ImplicitAcquisitionWrapper:
356            value=value()
357        return value
358
359    def getMemberFolder(self):
360        """ return the member, useful for collections, topics etc. subobjects """
361        return self
362
363    def getLatestEditDate(self):
364        """Returns creation date, we don't want to notify about every edit"""
365        return DateTime(self.CreationDate())
366
367    def getCollectionsFolder(self):
368        """..."""
369        if not hasattr(self.aq_base, 'collections'):
370            self.invokeFactory('CollectionsFolder', id='collections')
371        return self.collections
372
373    def getCollectionsNumber(self):
374        """..."""
375        dest = self.getCollectionsFolder()
376        collections = dest.objectIds('Collection')
377        return len(collections)
378   
379    def getCollections(self, obj_id=None, sort=True, col_uid=None):
380        """Return list of user's collections."""
381        if obj_id:
382            return self.aq_parent.getCollections(obj_id)
383        collections = self.getCollectionsFolder().objectValues('Collection')
384        if col_uid:
385            collections = [c for c in collections if c.UID()!=col_uid]
386        if not sort:
387            return collections
388        sortable_collections = [(x.Title(), x) for x in collections]
389        sortable_collections.sort()
390        return [x[1] for x in sortable_collections]
391
392    security.declareProtected(MODIFY_CONTENT,'delCollection')
393    def delCollection(self, obj_id):
394        obj=self.collections.get(obj_id)
395        if obj.amIOwner():       
396            collection_resources = obj.getRelatedContent() + obj.getRelatedMethods() + obj.getRelatedTools()
397            self.collections._delObject(obj_id)
398            # when deleting collection we need to recalculate scores for collection resources
399            for r in collection_resources:
400                r.recalculateScore()
401                r.reindexObject(['getScore'])
402            msg=_(u'Collection deleted')
403        else:
404            msg=_(u'You are not allowed to delete this collection')
405        return (self.collections, msg)     
406   
407    def getCountrylist(self):
408        """ We don't need short country codes, so we make a list where fancyname=fancyname and give that as DisplayList """
409        countries_root = [('No country specified', 'No country specified')]
410        countries_list = [(x[1],x[1]) for x in countries.items()]
411        countries_list.sort()
412        countries_root= countries_root + countries_list
413        return DisplayList(tuple(countries_root)) # not sure, but DisplayList might require tuples not lists.
414
415   
416    def getLanguagelist(self):
417        languagelist=self.availableLanguages()
418        return DisplayList(languagelist[1:]) # Cut out the first, 'no language specified'-option
419
420
421    def getPortrait(self, author=None):
422        """ Gets portrait"""
423        if not author:
424            return self.getCoverImage()
425        else:
426            return self.getMemberFolderById(author).getCoverImage()
427
428    def getPortraitURL(self,author=None):
429        """ Gets portrait URL"""
430        return self.getCoverImageURL()
431
432    def getNicename(self):
433        """ First try full_name, then userid """
434        return self.getFull_name() or self.Creator()
435
436    def sortable_title(self):
437        lastname= self.getLastname()
438        #if type(lastname)== ImplicitAcquisitionWrapper:
439        #    lastname=lastname()
440        if lastname:
441            try:
442                lastname=lastname.lower()
443            except:
444                lastname=lastname()
445                print lastname, type(lastname)
446            return lastname.lower()
447        else:
448            return self.Creator().lower()
449
450    def prefill_title(self):
451        lutool = getToolByName(self, 'lemill_usertool')
452        return lutool.getAuthenticatedId()
453
454    def getFull_name(self):
455        full= self.getField('full_name').get(self)
456        if full:           
457            return full
458        else:
459            first=self.getField('firstname').get(self)
460            last=self.getField('lastname').get(self)
461            if first and last:
462                self.getField('full_name').set(self, ' '.join((first, last)))
463                return ' '.join((first, last))
464            else:
465                return ''
466
467    def setLastname(self, value):
468        self.getField('lastname').set(self, value)
469        first=self.getField('firstname').get(self)
470        if callable(first):
471            first=first()
472        if value:
473            self.getField('full_name').set(self, ' '.join((first, value)))
474       
475    def setFirstname(self, value):
476        self.getField('firstname').set(self, value)
477        last=self.getField('lastname').get(self)
478        if callable(last):
479            last=last()
480        if value:
481            self.getField('full_name').set(self, ' '.join((value, last)))
482       
483    security.declareProtected(MODIFY_CONTENT,'setCoverImage')
484    def setCoverImage(self, value, **kwargs):
485        cover=self.getField('coverImage')
486        cover.set(self,value,**kwargs)
487        has_cover=self.getField('hasCoverImage')
488        if value==None:
489            has_cover.set(self,False)
490        else:
491            has_cover.set(self,True)
492
493       
494    def getHasCoverImage(self):
495        if self.getCoverImage():
496            return True
497        return False
498
499    security.declarePrivate('at_post_edit_script')
500    def at_post_edit_script(self):
501        self.recalculateScore()
502        fullname = self.getField('full_name').get(self)
503        if fullname and fullname!=self.Title():
504            self.setTitle(fullname)
505        self.reindexObject()
506
507    def getTags(self):
508        lst = self.getField('interests').get(self) + self.getField('skills').get(self)
509        return list(lst)
510
511    def getResources(self, creator, n=False, filter=None, as_dict=False, private_materials=True):
512        """ Get resources from subject of this memberfolder by using general portfolio-topic """
513        public_results = []
514        pcatalog=getToolByName(self, 'portal_catalog')
515        lutool = getToolByName(self, 'lemill_usertool')
516        query={'Creator':creator}
517        # We also show private resources to Owner of the folder
518        if lutool.getAuthenticatedId()==creator and private_materials==True:
519            query['getState'] = ('public','draft','private')
520        else:
521            query['getState'] = ('public','draft')
522        if filter:
523            query['meta_type'] = filter
524        if as_dict and n: # fastest way to count resources
525            dict={}
526            query['meta_type'] = MATERIAL_TYPES
527            dict['Content']=len(pcatalog(query))
528            query['meta_type'] = 'Activity'
529            dict['Activities']=len(pcatalog(query))
530            query['meta_type'] = 'Tool'
531            dict['Tools']=len(pcatalog(query))
532            query['meta_type'] = 'Collection'
533            dict['Collections']=len(pcatalog(query))
534            query['meta_type'] = 'Piece'
535            dict['Pieces']=len(pcatalog(query))
536            return dict               
537
538        results=pcatalog(query)
539        if as_dict:
540            # Make a dictionary with following keys: 'Content','Activities','Tools','Collections'
541            dict={'Content':[],'Activities':[],'Tools':[],'Collections':[],'Pieces':[]}
542            for res in results:
543                if res.meta_type in MATERIAL_TYPES:
544                    dict['Content'].append(res)
545                elif res.meta_type=='Activity':
546                    dict['Activities'].append(res)
547                elif res.meta_type=='Tool':
548                    dict['Tools'].append(res)
549                elif res.meta_type=='Collection':
550                    dict['Collections'].append(res)
551                elif res.meta_type=='Piece':
552                    dict['Pieces'].append(res)
553            return dict                   
554        if n:
555            return len(results)
556        return results
557
558    def canNotify(self, prop):
559        """ checks if notification preferences allow this kind of message """
560        return prop in self.getNotification_properties()
561
562    def userPortfolioInfo(self):
563        """ """
564        amounts_dict = self.getResources(self.Creator(), n=True, as_dict=True, private_materials=False);
565        text = '<div id="portfolio_content" style="background-color: #ffffff; padding:5px; vertical-align:top;">'
566       
567        # display random user resource image if has any
568        pc = getToolByName(self, 'portal_catalog')
569        meta_types = MATERIAL_TYPES + ACTIVITY_TYPES + TOOLS_TYPES
570        results = pc({'Creator': self.Creator(), 'meta_type': meta_types, 'getState':['public', 'draft'], 'getHasCoverImage': True})
571        if results:
572            obj = random.choice(results).getObject()
573            if obj:
574                text += '<div style="display: table-cell;text-align: center;vertical-align: middle;width: 130px;height: 100px;border: 1px solid #cccccc;"><img src="%s" alt=""></img></div>' % obj.getCoverImageURL()
575       
576        here_url = self.absolute_url()
577        portal_url = getToolByName(self, 'portal_url')
578        portal_url = portal_url()
579        text += '<ul style="line-height: 1.5em;margin: 0.5em 0 0 1.5em;padding: 0;list-style-image: url(%s);list-style-type: square;font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;font-size: 13px;">' % (portal_url + '/bullet.gif')
580        text += '<li><a href="%s" target="_blank" style="color:#2299bb;">Content</a> (%s)</li>' % (here_url + '/portfolio?type=resource', amounts_dict.get('Content'))
581        text += '<li><a href="%s" target="_blank" style="color:#2299bb;">Methods</a> (%s)</li>' % (here_url + '/portfolio?type=Activity', amounts_dict.get('Activities'))
582        text += '<li><a href="%s" target="_blank" style="color:#2299bb;">Tools</a> (%s)</li>' % (here_url + '/portfolio?type=Tool', amounts_dict.get('Tools'))
583        text += '<li><a href="%s" target="_blank" style="color:#2299bb;">Collections</a> (%s)</li>' % (here_url + '/collections', amounts_dict.get('Collections'))
584        text += '<li><a href="%s" target="_blank" style="color:#2299bb;">Tag cloud</a></li>' % (here_url + '/portfolio')
585        text += '</ul></div>'
586        return text
587
588    def userPortfolio(self):
589        """ """
590        url = self.absolute_url() + '/userPortfolioInfo'
591        iframe_text = '<iframe src="%s" width="100%%" height="220px" frameborder="0" scrolling="no"></iframe>' % url
592        return iframe_text
593
594
595    def getGroups(self, objects=False):
596        """ Gets groups where member belongs"""
597        pc=getToolByName(self, 'portal_catalog')
598        objlist=pc({'getGroupMembers':self.getMemberId(), 'portal_type':'GroupBlog'})
599        if objects:
600            # getObject verified
601            return [o.getObject() for o in objlist]
602        else:
603            return objlist
604
605
606    def getSamples(self):
607        """ get n number of samples """
608        my_id=self.getMemberId()
609        all_contents = list(self.portfolio.queryCatalog({'getState':'public', 'meta_type': self.getFeaturedTypes(), 'getHasCoverImage':True, 'Creator': my_id}))
610        if len(all_contents)<3:
611            all_contents+= list(self.portfolio.queryCatalog({'getState':'public', 'meta_type': ('Piece',), 'getHasCoverImage':True, 'Creator': my_id}))
612        good_results=[]
613        while all_contents and len(good_results)<3:
614            res=all_contents.pop(random.randint(0,len(all_contents)-1))
615            if res:
616                try:               
617                    obj=res.getObject()
618                    if obj:
619                        good_results.append(obj)
620                except AttributeError:
621                    pass
622        return good_results
623       
624
625    def getDefaultIcon(self, meta_type='', obj=None):
626        """ general method for getting proper icon for object, used when only catalog-metadata is available """
627        # this combines folderish getDefaultIcon(for-this-type, object) and resource-specific object.getDefaultIcon()
628        # folderish behaviour is needed because members have these created resources-pages. 
629        if meta_type=='':
630            return  DEFAULT_ICONS['MemberFolder']
631        else:     
632            address=DEFAULT_ICONS[meta_type]
633            if address!='piece':
634                return address
635            else:
636                # getObject verified
637                obj=obj.getObject()
638                return obj.getDefaultIcon()
639   
640    def verifyMessengers(self, array):
641        """ check if there are some instant messengers to show
642            takes an array and if there is content then returns True
643        """
644        for x in array:
645            if x: return True
646        return False
647
648
649    security.declareProtected(MODIFY_CONTENT,'addToContacts')
650    def addToContacts(self,uid=None):
651        """ Add person to contacts """
652        field=self.getField('listOfContacts')
653        uidlist=field.getRaw(self)
654        if uid not in uidlist:
655            uidlist.append(uid)
656            field.set(self, uidlist)
657            lt=getToolByName(self, 'lemill_tool')
658            mf = lt.getObjectByUID(uid)
659            mf.notifyAddedAsContact()
660       
661    security.declareProtected(MODIFY_CONTENT,'removeFromContacts')
662    def removeFromContacts(self,uid=None):
663        """ Remove person from contacts """
664        field=self.getField('listOfContacts')
665        uidlist=field.getRaw(self)       
666        if uid in uidlist:
667            uidlist.remove(uid)
668            field.set(self, uidlist)
669
670    def showRemoveContactLink(self):
671        """ Determines if we show the remove link for user this is still done in reverse """
672        lutool = getToolByName(self, "lemill_usertool")
673        home=lutool.getMemberFolder()
674        field = home.getField('listOfContacts')
675        return self.UID() in field.getRaw(home)
676
677    def giveSortedListOfContacts(self):
678        """ Sorts list of contacts by sortable nicename """
679        return self.portal_catalog(UID=self.getRawListOfContacts(), sort_on='sortable_title')
680
681    def getRelatedContacts(self):
682        """ Returns all related contacts """
683        obj_uid = self.UID()
684        res = []
685        q = { 'targetUID': obj_uid }
686        qres = self.reference_catalog(q)
687        for q in qres:
688            sourceUID= self.reference_catalog.lookupObject(q.UID).sourceUID
689            sres = self.portal_catalog({'UID':sourceUID})
690            if sres and sres[0].portal_type == 'MemberFolder':
691                res.append(sres[0])
692        return res
693
694    security.declareProtected(MODIFY_CONTENT,'updateSlideshare_username')
695    def updateSlideshare_username(self, REQUEST):
696        """ """
697        new_username=REQUEST.get('new_username','')
698        if new_username:
699            self.setSlideshare_username(new_username)
700        lt=getToolByName(self, 'lemill_tool')
701        lt.addPortalMessage(_('Saved SlideShare username.'))
702        return REQUEST.RESPONSE.redirect(self.content.absolute_url()+'/add_presentation')
703
704    def recalculateScore(self):
705        """ Recalculates score for MemberFolder according to specifications """
706        score = 0
707        member = self.getMemberId()
708
709        all_resources = self.getResources(member, as_dict=True)
710        # Get pieces and filter out References
711        pieces = all_resources['Pieces']
712        pieces = [x for x in pieces if x.meta_type=='Piece']
713        # Get Material types, Methods and Tools
714        main_resources = all_resources['Content'] + all_resources['Activities'] + all_resources['Tools']
715        # Get collections and see which ones are complete
716        collections = self.getCollections()
717        stories = [c for c in collections if c.getGoodStory()]
718        # Get all posts by member
719        pcatalog=getToolByName(self, 'portal_catalog')
720        posts = pcatalog({'getState':'public', 'meta_type':'BlogPost', 'Creator':member})
721
722        # Get people that have this member as contact
723        contacts = self.getRelatedContacts()
724
725        score = score + len(pieces) # 1 point for each piece
726        score = score + len(main_resources) * 10 # 10 points for each learning resource, method and tool
727        score = score + len(stories) * 10 # 10 points for each storie
728        score = score + len(posts) # 1 point for each post
729        score = score + len(contacts) # 1 point for each contact
730
731        # Make sure that score is at least 1
732        if score<1:
733            score = 1
734        self.setScore(score)
735
736    def portfolioFilter(self):
737        """ Used for filtering member portfolio """
738        return ('Creator',self.getMemberId())
739
740    ################ Notifications ##############################
741    security.declarePrivate('notifyAddedAsContact')
742    def notifyAddedAsContact(self):
743        ltool = getToolByName(self, 'lemill_tool')
744        lutool = getToolByName(self, 'lemill_usertool')
745        auth_mf=lutool.getMemberFolder()
746        if self.canNotify('contact_added'):
747            language=lutool.getCommunicationLanguage(self)
748            dict={'name':to_unicode(auth_mf.getNicename()),
749                'url':to_unicode(self.absolute_url())}               
750            msg = self.translate("%(name)s has added you as a contact: %(url)s", domain="lemill", target_language=language) % dict
751            ltool.mailNotification(msg, recipient=self.getEmail(), language=language)   
752
753    def sendInvitationMail(self, message, invited, inviter_name):
754        utool=getToolByName(self,'portal_url')
755        lutool = getToolByName(self, 'lemill_usertool')   
756        language=lutool.getCommunicationLanguage(invited.getId())
757        dict = {'invited':to_unicode(invited.getNicename()),
758            'inviter_name':to_unicode(inviter_name),
759            'message':to_unicode(message)}
760        msg = self.translate(u"""Dear %(invited)s,
761
762%(inviter_name)s is inviting you to join the following groups in LeMill:
763
764%(message)s
765
766Best regards,
767
768LeMill
769
770--
771Do not reply to this message. You can find this person's contact information from LeMill.""", domain="lemill", target_language=language) %  dict
772       
773        try:
774            mhost=self.MailHost
775            mhost.secureSend(msg.encode('utf-8'),
776                mto=invited.getEmail(),
777                mfrom="LeMill <no-reply@lemill.net>",
778                subject="Invitation to group in %s (do not reply)" % (utool.Title()),
779                charset='utf-8')
780        except:
781            print 'Something is wrong with outgoing email. Sent message was:'
782            try:
783                print msg
784            except:
785                print '(Probably UnicodeEncodeError preventing displaying this message)'
786
787       
788registerType(MemberFolder, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.