source: trunk/MemberFolder.py @ 490

Revision 490, 19.7 KB checked in by tarmo, 13 years ago (diff)

More fixes to i18n strings.

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 SharedMetadata import age_group, description, tags
20from Products.Archetypes.public import *
21from Products.ATContentTypes.content.folder import ATFolder
22from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget
23from Products.CMFCore.permissions import ModifyPortalContent
24from Products.Archetypes.public import BaseFolder, BaseFolderSchema, registerType
25from Products.Archetypes.atapi import DisplayList
26from Globals import InitializeClass
27from Products.CMFCore.utils import getToolByName
28from AccessControl import ClassSecurityInfo, Unauthorized
29from config import PROJECTNAME, MODIFY_CONTENT, VIEW
30from Products.PloneLanguageTool.availablelanguages import countries
31try:
32    from Products.PloneLanguageTool.availablelanguages import languages_english
33except ImportError:
34    from Products.PloneLanguageTool.availablelanguages import languages
35    languages_english = dict([(key, val['english']) for (key, val) in languages.iteritems()])
36    del languages
37
38from FieldsWidgets import TagsField, TagsWidget, MessengerWidget, MobileWidget
39from DateTime import DateTime
40import datetime
41
42
43messenger_vocabulary = (
44                 ('aimprefix','AIM'),
45                 ('googletalkprefix','Google Talk'),
46                 ('icq','ICQ'),
47                 ('msn','MSN'),
48                 ('skype','Skype'),
49                 ('yahoo','Yahoo'),
50                 )
51
52# 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.
53
54schema = BaseFolderSchema + Schema((
55    StringField('title',
56        mode ='r',
57        # default_method is called only once, when first needed; after that, getXxx is used
58        default_method = 'prefill_title',
59        widget=StringWidget(
60            label_msgid='label_title',
61            visible={'edit' : 'invisible'},           
62        ),
63    ),
64    TextField('firstname',
65        default_method = 'prefill_firstname',
66        widget = StringWidget(
67             label = 'First name',
68             label_msgid = 'label_firstname',
69             description = 'Your first name',
70             description_msgid = 'help_firstname',
71             i18n_domain = "lemill"
72             )
73        ),
74    TextField('lastname',
75        index = 'FieldIndex:schema',
76        default_method = 'prefill_lastname',
77        widget = StringWidget(
78             label = 'Last name',
79             label_msgid = 'label_lastname',
80             description = 'Your last name',
81             description_msgid = 'help_lastname',
82             i18n_domain = "lemill"
83             )
84        ),
85    ComputedField('fullname',
86        index = 'FieldIndex:schema',
87        searchable = True,
88        expression = 'here.getFullname()',
89        default_method = 'prefill_fullname',
90        ),
91    TextField('nickname',
92        index = 'FieldIndex:schema',
93        searchable = True,
94        widget = StringWidget(
95             label = 'Nick name',
96             label_msgid = 'label_nickname',
97             description = 'If you enter a nick name, it will be visible instead of your full name in LeMill community.',
98             description_msgid = 'help_nickname',
99             i18n_domain = "lemill"
100             )
101        ),
102    ComputedField('nicename', # so that displayed name can be pulled from catalog metadata
103        index= 'FieldIndex:schema',
104        expression = 'here.getNicename()',
105        default_method = 'prefill_fullname',
106        ),
107    ComputedField('sortable_nicename', # so that displayed names can be ordered alphabetically
108        index= 'FieldIndex:schema',
109        expression = 'here.getSortable_nicename',
110        default_method = 'prefill_lastname',
111        ),
112       
113    TextField('email',
114        searchable = True,
115        index = 'FieldIndex:schema',
116        default_method = 'prefill_email',
117        validators = 'isEmail',
118        widget = StringWidget(
119             label = 'Email address',
120             label_msgid = 'label_email',
121             description = 'Your email address.',
122             description_msgid = 'help_email',
123             i18n_domain = "lemill"
124             )
125        ),
126
127    TextField('phone',
128        widget = StringWidget(
129            label = 'Phone',
130            label_msgid = 'label_phone',
131            description = 'Your phone number if you want it to be available for community',
132            description_msgid = 'help_phone',
133            i18n_domain ='lemill',
134            )
135    ),     
136    TextField('mobile',
137        widget = MobileWidget(
138            label = 'Mobile',
139            label_msgid = 'label_mobile',
140            description = 'Check the box if you approve community members sending text messages.',
141            description_msgid = 'help_mobile',
142            i18n_domain ='lemill',
143            )
144    ),         
145
146    TextField('messenger1',
147        vocabulary = DisplayList(messenger_vocabulary),
148        widget = MessengerWidget(
149             label = 'Instant messengers',
150             label_msgid = 'label_messenger',
151             description = 'You may enter up to three instant messenger, skype or gsm-contacts',
152             description_msgid = 'help_messenger',
153             i18n_domain = "lemill"
154             )
155        ),
156    TextField('messenger2',
157        vocabulary = DisplayList(messenger_vocabulary),
158        widget = MessengerWidget(
159             )
160        ),
161    TextField('messenger3',
162        vocabulary = DisplayList(messenger_vocabulary),
163        widget = MessengerWidget(
164             )
165        ),
166    TextField('location_country',
167        index = 'FieldIndex:schema',
168        searchable = True,
169        vocabulary = 'getCountrylist',
170        widget = SelectionWidget(
171             label = 'Country',
172             label_msgid = 'label_country',
173             description = "Country where you're living",
174             description_msgid = 'help_country',
175             i18n_domain = "lemill"
176             )
177        ),       
178    TextField('location_area',
179        searchable = True,
180        index = 'FieldIndex:schema',
181        widget = StringWidget(
182             label = 'City or area',
183             label_msgid = 'label_area',
184             description = "City or area where you're living",
185             description_msgid = 'help_area',
186             i18n_domain = "lemill"
187             )
188        ),
189    TextField('home_page',
190        widget = StringWidget(
191             label = 'Homepage',
192             label_msgid = 'label_home_page',
193             description = "Your homepage or blog",
194             description_msgid = 'help_home_page',
195             i18n_domain = "lemill",
196             validators = 'isURL'
197             )
198        ),
199    LinesField('language_skills',
200        default = [],
201        index = 'KeywordIndex:schema',
202        vocabulary = 'getLanguagelist',
203        widget = PicklistWidget(           
204             label = 'Languages',
205             label_msgid = 'label_language_skills',
206             description = "Choose languages you can use to create learning material or communicate.",
207             description_msgid = 'help_language_skills',
208             i18n_domain = "lemill"
209             )
210        ),       
211    TagsField('skills',
212        index = 'KeywordIndex:schema',
213        widget = TagsWidget(           
214             label = 'Skills',
215             label_msgid = 'label_skills',
216             description = "List skills that can be useful for community.",
217             description_msgid = 'help_skills',
218             i18n_domain = "lemill"
219             )
220        ),
221    TagsField('interests',
222        index = 'KeywordIndex:schema',
223        widget = TagsWidget(
224             label = 'Interests',
225             label_msgid = 'label_interests',
226             description = "List of interests that you might share with others.",
227             description_msgid = 'help_interests',
228             i18n_domain = "lemill"
229             )
230        ),
231    ComputedField('tags',
232        index = 'KeywordIndex:schema',
233        expression = 'here.getTags()',
234        ),
235
236    LinesField('recent_activity',
237        default= [],
238        widget = LinesWidget(
239            visible = {'view':'invisible', 'edit':'invisible'},
240            ),
241        ),
242
243    IntegerField('activity_score',
244        index = 'FieldIndex:schema',
245        default = 0,
246        widget = IntegerWidget(
247                 visible = {'view':'invisible', 'edit':'invisible'},
248             ),
249        ),
250
251    TextField('biography',
252        searchable = True,
253        widget = TextAreaWidget(
254            label = 'Biography',
255            label_msgid = 'label_biography',
256            description = "If you want to write something about yourself.",
257            description_msgid = 'help_biography',
258            i18n_domain = "lemill"
259            )
260        ),
261
262    LinesField('used_content',
263        mode = 'r',
264        index = 'KeywordIndex:schema',
265        widget = LinesWidget(
266                 visible = {'view':'invisible', 'edit':'invisible'},
267             )
268        ),
269    BooleanField('hasCoverImage',
270                default=False,
271                index="FieldIndex:schema",
272                accessor='getHasCoverImage',
273                mutator='setHasCoverImage',
274                widget = BooleanWidget(
275                    visible = {'view':'invisible', 'edit':'invisible'},
276                    ),
277                )
278
279
280))
281
282# This lists the fields that also exist in
283# the memberdata tool
284# TODO: this could be automatically generated from the schema and member property list
285DUPLICATED_FIELDS = ('firstname','lastname','email')
286
287class MemberFolder(BaseFolder):
288    """Member folder"""
289
290    meta_type = "MemberFolder"
291    archetype_name = "MemberFolder" 
292    typeDescription="Member folder"
293    typeDescMsgId='description_memberfolder'
294    global_allow = 1
295
296    allowed_content_types = ('Story','CollectionsFolder')
297    default_view = ('member_view')
298    filter_content_types = True
299    security = ClassSecurityInfo()
300    schema = schema
301
302    actions = (
303        {'id':'view',
304         'name':'view',
305         'action':"string:${object_url}/member_view",
306         'permission':(VIEW,),
307        },
308        {'id':'edit',
309         'name':'edit',
310         'action':"string:${object_url}/personalize_form",
311         'permission':(MODIFY_CONTENT,),
312        },
313    )   
314    aliases = {
315        '(Default)': '',
316        'edit': 'personalize_form',
317        'view':'member_view'
318        }
319
320    def manage_afterAdd(self, item, container):
321        """ add collections folder """
322        BaseFolder.manage_afterAdd(self, item, container)
323        if not hasattr(item.aq_base, 'collections'):
324            item.invokeFactory('CollectionsFolder', id='collections')
325
326    def getCollectionsFolder(self):
327        """
328        return collections folder.
329        XXX: 'if' can be removed later.
330        """
331        if not hasattr(self.aq_base, 'collections'):
332            self.invokeFactory('CollectionsFolder', id='collections')
333        return self.collections
334   
335    def whatamI(self):
336        # Because my dynamically created methods confuse my author
337        print dir(self)
338        return dir(self)       
339
340    def computeTags(self):
341        return self.skills + self.interests
342
343    def getCountrylist(self):
344        """ We don't need short country codes, so we make a list where fancyname=fancyname and give that as DisplayList """
345        countries_root = [('No country specified', 'No country specified')]
346        countries_list = [(x[1],x[1]) for x in countries.items()]
347        countries_list.sort()
348        countries_root= countries_root + countries_list
349        return DisplayList(tuple(countries_root)) # not sure, but DisplayList might require tuples not lists.
350
351   
352    def getLanguagelist(self):
353        languagelist=self.availableLanguages()
354        return DisplayList(languagelist[1:]) # Cut out the first, 'no language specified'-option
355
356    def getPortraitURL(self,author=None):
357        """ Gets portrait from portal_membership """
358        mtool   = getToolByName(self, 'portal_membership')
359        if author:
360            portrait = mtool.getPersonalPortrait(author)
361        else:
362            portrait = mtool.getPersonalPortrait(self.getId())
363        return portrait.absolute_url()
364
365    def getCoverImage(self):
366        """ This makes the method work similarly with people and objects """
367        mtool   = getToolByName(self, 'portal_membership')
368        portrait = mtool.getPersonalPortrait(self.getId())
369        return portrait
370     
371    def NiceName(self):
372        """ Nickname if exists, fullname if not """
373        if self.getNickname()!='':
374            return self.getNickname()
375        elif self.getFullname()!='':
376            return self.getFullname()
377        else:
378            return self.getId()   
379
380    def getNicename(self):
381        return self.NiceName()
382
383    def getSortable_nicename(self):
384        name = self.NiceName()
385        if name == self.getFullname():
386            name=self.getLastname()
387        return name.lower()
388
389    def prefill_title(self):
390        mdatatool = getToolByName(self, 'portal_memberdata')
391        value = mdatatool.getMemberId()
392        return value
393
394    def __getMemberProperty(self,key):
395        memberid = self.getId()
396        mtool = getToolByName(self, 'portal_membership')
397        member = mtool.getMemberById(memberid)
398        if member:
399            return member.getProperty(key)
400        else:
401            return ''
402
403    def debugProps(self):
404        """..."""
405        mtool = getToolByName(self, 'portal_memberdata')
406        raise 'FOO',str(mtool.propdict())
407       
408
409    def prefill_email(self):
410        """ When memberfolder is created first time we need to get some values right, as they will be used in templates"""
411        return self.__getMemberProperty('email')
412       
413    def prefill_firstname(self):
414        return self.__getMemberProperty('firstname')
415
416    def prefill_lastname(self):
417        return self.__getMemberProperty('lastname')
418
419    # Both the default_method and normal getter for fullname field
420    def getFullname(self):
421        first = str(self.__getMemberProperty('firstname'))
422        last = str(self.__getMemberProperty('lastname'))
423        if first=='None' or first=='':
424            first=''
425        if last=='None' or last=='':
426            last=''           
427        if first=='' and last=='':
428            return ''
429        else:
430            return " ".join((first, last))       
431
432    def setFullname(self,value):
433        names=value.split(' ')
434        self.setFirstname(names[0])
435        self.setLastname(names[1])
436
437
438
439    def flagCoverImageOn(self):
440        self.getField('hasCoverImage').set(self, True)
441
442    def flagCoverImageOff(self):
443        self.getField('hasCoverImage').set(self, False)
444
445
446    def at_post_edit_script(self):
447        putil = getToolByName(self,'plone_utils')
448        pmem = getToolByName(self,'portal_membership')
449        user = pmem.getMemberById(self.getId())
450        for field in DUPLICATED_FIELDS:
451            value = self.getField(field).get(self)
452            if value != user.getProperty(field):
453                user.setMemberProperties({field:value})
454
455    def getTags(self):
456        lst = self.getField('interests').get(self) + self.getField('skills').get(self)
457        return list(lst)
458
459    def getCollections(self):
460        return self.collections.objectValues('Collection')
461
462    def getStories(self):
463        # please change the path from collections to stories when folder for them gets created.
464        return self.collections.objectValues('Story')
465
466    def note_action(self, obj_uid, portal_type, sender):
467        """ Calculate activity score and add to recent activities """       
468        acts=self.getRecent_activity()
469        uids=[x[0] for x in acts]
470        score=self.getActivity_score()
471        if score==None: score=0
472        if sender=='post_edit':
473            msg='Modify %s' % portal_type
474        elif sender=='afterAdd':
475            msg='Create %s' % portal_type           
476        else:
477            msg='?? %s' % portal_type
478       
479        if not obj_uid in uids:
480            if sender=='afterAdd':
481                if portal_type=='Piece' or portal_type=='GroupBlog':
482                    score=score+3
483                elif 'Material' in portal_type or portal_type=='Activity' or portal_type=='Tool':
484                    score=score+5
485                elif portal_type=='BlogPost':
486                    score=score+2       
487            if sender=='post_edit':
488                if 'Material' in portal_type or portal_type=='Activity' or portal_type=='Tool':
489                    score=score+5           
490            self.setActivity_score(score)
491            self.reindexObject()
492        self.addRecent_activity(obj_uid, msg)
493
494    def getRecent_activity(self):
495        """ Linesfield stores tuple of strings and we need list of tuples. So... """
496        src=list(self.getField('recent_activity').get(self))
497        return [tuple(x.split(',')) for x in src]
498       
499       
500    def addRecent_activity(self, obj_uid, act_type):
501        """ Recent activity is a list of (obj_UID, date, activity type {'modified piece', 'created' etc.})
502         that tracks member activities for all kinds of calculations """
503
504        current_date = DateTime()
505        acts= self.getRecent_activity()
506        acts=[(obj_uid,current_date,act_type)]+acts
507        # pop out old collaboration proposals from tail of the list
508        while acts[-1][1] < (current_date-31):
509            acts.pop()
510        acts=[','.join((obj_uid, str(current_date), act_type)) for (obj_uid,current_date,act_type) in acts]
511        acts_field=self.getField('recent_activity')
512        acts_field.set(self, acts)
513
514       
515    def my_works(self, stay=False, REQUEST=None):
516        """ Search for content-like created by member. """
517        if not stay:
518            return REQUEST.RESPONSE.redirect('/'.join((self.absolute_url(),'search?portal_type=Material&portal_type=Piece&portal_type=Activity&portal_type=Tool&portal_type=BlogPost&Creator=%s' % self.getId())))
519           
520        types=['Material','Piece','Activity','Tool','BlogPost']
521        pc = getToolByName(self, 'portal_catalog')
522        results=pc.searchResults(Creator=self.id)
523        results = filter(lambda x: x.portal_type in types and x.review_state!='hidden', results)
524        return results
525
526    def resize_portrait(self, portrait, member_id):
527        """ resize user portrait if it is larger than 160x120 """
528        try:
529            from PIL import Image
530        except ImportError:
531            return portrait
532        import cStringIO
533        s = cStringIO.StringIO(portrait.read())
534        im = Image.open(s)
535        (width, height) = im.size
536        if width > 160:
537            mod = float(160)/float(height)
538            width = width*mod
539            height = height*mod
540            im = im.resize((int(width),int(height)))
541        if height > 120:
542            u_w = float(120)/float(width)
543            width = width*u_w
544            height = height*u_w
545            im = im.resize((int(width),int(height)))
546        s = cStringIO.StringIO()
547        im.save(s, "JPEG", quality=100)
548        s.seek(0)
549       
550        from OFS.Image import Image
551        pr = Image(id=member_id, file=portrait, title='')
552        membertool   = getToolByName(self, 'portal_memberdata')
553        membertool._setPortrait(pr, member_id)
554        return 1
555       
556registerType(MemberFolder, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.