root/trunk/ConfigurationMethods.py

Revision 3067, 20.9 kB (checked in by jukka, 1 year ago)

Added Brazilian Portuguese to available languages.

  • Property svn:eol-style set to native
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
19 # -*- coding: iso-8859-1 -*-
20
21 from Acquisition import aq_get
22 from AccessControl import Permissions, getSecurityManager
23 from zExceptions import BadRequest
24 from OFS.PropertyManager import PropertyManager
25 from zLOG import INFO, ERROR
26 from Globals import package_home
27 from Products.ZCatalog.Catalog import CatalogError
28
29 from Products.CMFCore.utils import getToolByName
30 from Products.CMFCore.FSFile import FSFile
31
32 from Products.CMFCore.DirectoryView import registerDirectory, addDirectoryViews, registerFileExtension
33
34 from Products.CMFCore.ActionInformation import ActionInformation
35 from Products.CMFPlone.migrations.migration_util import safeEditProperty
36 from Products.CMFPlone.PloneFolder import addPloneFolder
37 from Products.CMFPlone.setup.SetupBase import SetupWidget
38 from Products.SiteErrorLog.SiteErrorLog import manage_addErrorLog
39 from Products.Archetypes.public import listTypes, registerType
40 from Products.Archetypes.Extensions.utils \
41      import installTypes, install_subskin
42 from Products.PythonScripts.PythonScript import PythonScript
43 from Products.LeMill.LatexTool import workingDir
44 from Acquisition import aq_inner, aq_parent
45
46
47 from DateTime import DateTime
48 import string, types
49 from cStringIO import StringIO
50 from os import path
51
52 from config import *
53
54
55 # All methods starting with "setup" will be used in the customization policy
56 # The methods will be sorted by ascii value, so if something  needs to happen before
57 # something else, make it so (see method setup001Dependencies).
58
59 # Note that all methods need to be repeatable - they should not fail
60 # if they are run again when all that they do has already been done.
61 # Also, the methods should be able to upgrade, meaning that older customizations
62 # should be changed to match the newest specifications.
63
64 def setup001Dependencies(self, portal):
65     """Install all necessary products into the plone instance."""
66     qi=getToolByName(portal, 'portal_quickinstaller')
67     # We need ourselves installed into the plone instance,
68     # since we provide portal tools and content
69     qi.installProduct('LeMill')
70     # LanguageTool is also necessary
71     qi.installProduct('PloneLanguageTool')
72     if FS_STORAGE:
73         qi.installProduct('FileSystemStorage')
74
75 def setupSkin(self,portal):
76     """Create new custom skin."""
77     skinsTool = getToolByName(portal,"portal_skins")
78
79     # Register our own skin folder as a viewable folder
80     try:
81         addDirectoryViews(skinsTool,SKINS_DIR,GLOBALS)
82     except:
83         # If the directory is already registered, we just continue
84         pass
85
86     # Create our new skin, making it a copy of BASE_SKIN and adding
87     # our skin layer just after "custom".
88     if SKIN_NAME not in skinsTool.getSkinSelections():
89         path = skinsTool.getSkinPath(BASE_SKIN)
90         path = map(string.strip,string.split(path,','))
91         if SKIN_NAME not in path:
92             try:
93                 path.insert(path.index('custom')+1,SKIN_NAME)
94             except ValueError:
95                 path.append(SKIN_NAME)
96         path=string.join(path,',')
97         skinsTool.addSkinSelection(SKIN_NAME,path)
98
99     # Select our first skin as default
100     skinsTool.default_skin=SKIN_NAME
101
102     # Allow fonts into skins.
103     registerFileExtension('ttf', FSFile)
104
105
106 def addObject(portal,id,type,title,desc=None):
107     """Convenience method for creating new content items."""
108     try:
109         portal.invokeFactory(id=id,type_name=type)
110     except BadRequest:
111         # If the id already existed, we'll just quietly be happy about it.
112         pass
113     finalizeObject(portal,id,title,desc)
114
115 def finalizeObject(portal,id,title,desc=None):
116     """Set content object properties in place."""
117     ob=getattr(portal,id)
118    
119     # Set basic properties of object
120     ob.setTitle(to_unicode(title))
121     if desc:
122         ob.setDescription(to_unicode(desc))
123        
124     # Publish content
125     ob.setState="public"
126
127 def setupFrontPage(self,portal):
128     """Setup the front page."""
129     # Make sure that old PythonScript for redirecting isn't there anymore
130     # the content section.
131     try:
132         portal._delObject('front-page')
133     except AttributeError:
134         pass
135
136
137 def setupLatexSupport(self, portal):
138     """Setup localfs for latex-images."""
139     if not LATEX_IMAGES_STORAGE_PATH:
140         return
141    
142     if not hasattr(portal, LATEX_IMAGES_STORAGE_PATH):
143         ob = None
144         try:
145             from Products.LocalFS.LocalFS import LocalFS
146             ob = LocalFS(LATEX_IMAGES_STORAGE_PATH, '', workingDir, None, None)
147             portal._setObject(LATEX_IMAGES_STORAGE_PATH, ob, set_owner=1)
148
149         except ImportError: # no localfs
150             pass
151
152 def setupFontsFolder(self, portal):
153     """ Setup fonts for pdf-creation """
154    
155     if not hasattr(portal, 'fonts'):
156         ob = None
157         fontpath=path.join(package_home(globals()), 'fonts/')
158         ob = None
159         try:
160             from Products.LocalFS.LocalFS import LocalFS
161             ob = LocalFS('fonts', '', fontpath, None, None)
162             portal._setObject('fonts', ob, set_owner=1)
163
164         except ImportError: # no localfs
165             pass
166
167
168
169                  
170 def setupFolders(self,portal):
171     """Setup the basic structure of the site."""
172
173     def createSection(base, section):
174         section_id=section.lower()
175         section_type=SECTION_FOLDER_TYPES[section]
176         if not hasattr(base, section_id):
177             addObject(base,section_id,section_type,section)
178         else:
179             folder = getattr(base, section_id)
180             if folder.portal_type != section_type:
181                 base._delObject(section_id)
182                 addObject(base,section_id,section_type,section)
183         folder = getattr(base,section_id)
184         folder.manage_permission(ADD_CONTENT_PERMISSION, ('Member',), acquire=1)
185         folder.manage_permission(LIST_FOLDER_CONTENTS, ('Member',), acquire=1)
186         folder.manage_permission(ADD_TOPICS, ('Member',), acquire=1)
187         return folder
188
189
190     # Loop through all main sections
191     for (section, subsections) in SECTIONS.items():
192         section=createSection(portal, section)
193         # Loop through their subsections
194         for subsection in subsections:
195             subsection=createSection(section, subsection)                           
196
197     # Setup additional Trash folder for deleted resources
198     # It might be a good idea to make any actions including View only usable by users with Manager permission
199     trash_folder = 'Trash'
200     trash_type = 'LargeTrashFolder'
201
202     if not hasattr(portal, trash_folder.lower()):
203         addObject(portal,trash_folder.lower(),trash_type,trash_folder)
204     folder = getattr(portal,trash_folder.lower())
205     folder.manage_permission(ADD_CONTENT_PERMISSION, ('Member',), acquire=1)
206     for section, toplist in SECTION_TOPLISTS.items():
207         section=getattr(portal, section)
208         for top3 in toplist:
209             if not hasattr(section, top3):
210                 section.manage_addProperty(top3, [], 'lines')
211        
212        
213
214
215 def setupReadOnlyMode(self, portal):
216     stp = getToolByName(portal,'portal_properties').site_properties
217     if not hasattr(stp, 'readonly_mode'):
218         stp.manage_addProperty('readonly_mode',False,'boolean')
219
220
221 def setupSiteSyndication(self, portal):
222     # Enable syndication
223     syn_tool = getToolByName(portal, 'portal_syndication')
224     syn_tool.editProperties(isAllowed=True)
225     #safeEditProperty(syn_tool,'isAllowed',True)
226     
227 def setupCatalog(self,portal):
228     # put 'language' and 'subject' in catalog index and retrieved metadata
229     # from http://plone.org/documentation/how-to/adding-new-fields-to-smart-folders-search
230     # these are metadata fields from basic plone objects.
231
232     class args:
233             def __init__(self, **kw):
234                 self.__dict__.update(kw)
235             def keys(self):
236                 return self.__dict__.keys()
237
238
239     catalog_tool = getToolByName(portal, 'portal_catalog')
240     # (jukka, 10.1.2008) I commented away deleting indexes before adding them, as this forces us to always update catalog after running these
241     # We can restore them if we do changes that really change probably existing indexes
242
243     #try:
244     #    catalog_tool._removeIndex("Language")
245     #except:
246     #    pass
247     #try:
248     #    catalog_tool.delColumn("Language")
249     #except:
250     #    pass
251
252     extra = args(doc_attr='Language',
253                  lexicon_id='plone_lexicon',
254                  index_type='Okapi BM25 Rank')
255     try:
256         catalog_tool.manage_addIndex("Language", "FieldIndex", extra)
257     except CatalogError:
258         pass # index already exists
259     try:
260         catalog_tool.manage_addColumn("Language")
261     except CatalogError:
262         pass # metadata already exists
263
264     #try:
265     #    catalog_tool.delColumn("sortable_title")
266     #except:
267     #    pass
268     try:
269         catalog_tool.manage_addIndex("sortable_title", "FieldIndex")   
270     except CatalogError:
271         pass # index already exists
272     try:
273         catalog_tool.manage_addColumn("sortable_title")   
274     except CatalogError:
275         pass # metadata already exists
276     #try:
277     #    catalog_tool.delColumn("UID")
278     #except:
279     #    pass
280
281     extra = args(doc_attr='UID',
282                  lexicon_id='plone_lexicon',
283                  index_type='Okapi BM25 Rank')
284     try:
285         catalog_tool.manage_addIndex("UID", "FieldIndex", extra)   
286     except CatalogError:
287         pass # index already exists
288     try:
289         catalog_tool.manage_addColumn("UID")
290     except CatalogError:
291         pass # metadata already exists
292
293     # The meta_type was gone from the catalog Metadata, we need it back
294     try:
295         catalog_tool.manage_addColumn("meta_type")
296     except CatalogError:
297         pass # metadata already exists
298
299
300     try:
301         catalog_tool.manage_addColumn("postCount")
302     except CatalogError:
303         pass # metadata already exists
304     try:
305         catalog_tool.manage_addColumn("getLastCommentDate")
306     except CatalogError:
307         pass # metadata already exists
308     
309        
310     # BAD INDEXES AND METADATA:
311 #    bads=['Description','EffectiveDate','ExpirationDate','Subject','effective','end','exclude_from_nav','expires',   
312 #'getActivity_score','getAge_group','getCategory','getDescription','getEndUserRole','getFullname','getIcon',
313 #'getLearningContext','getLearningResourceType','getLocation','getNickname','getObjSize','getRemoteUrl',
314 #'getShortDescription','getUsed_content','in_reply_to','is_default_page','is_folderish','location','start','getRawRelatedActivities', 'effectiveRange', 'getEventType']
315 #
316 #    for b in bads:
317 #        try:
318 #            catalog_tool.delColumn(b)
319 #            print 'removed metadata %s' % b
320 #        except:
321 #            pass
322 #        try:
323 #            catalog_tool._removeIndex(b)
324 #            print 'removed index %s' % b
325 #        except:
326 #            pass
327
328
329 def setupTopics(self, portal):
330     """ From v 3.0-> we don't use topics anymore. This method makes sure they are removed. :/ """
331     removed_topics = {
332         'content': ['tags','published','browse_resources','drafts','language','subject_area','target_group','browse_references','browse_pieces','recent'],
333         'methods': ['language','tags','published','browse_methods'],
334         'tools': ['language','tags','published','browse_tools'],
335         'community':['portfolio','browse_people','language','country','skills','interests','subject_area','browse_groups','tags','g_language','group_subject_area']
336     }
337     for section,topic_ids in removed_topics.items():
338         section=getattr(portal, section)
339         del_ids=[]
340         for topic_id in topic_ids:
341             topic= getattr(section, topic_id, None)
342             if topic and getattr(topic, 'meta_type','') == 'ATTopic':
343                 del_ids.append(topic_id)
344         if del_ids:
345             print 'deleting obsolete topics:', del_ids
346             section.manage_delObjects(del_ids)
347     # Also remove every Topic from catalog, there are some legacy stuff that has survived.
348     pc=portal.portal_catalog
349     for topic_md in list(pc(portal_type='Topic')): # needs to be list because lazy catalog results behave oddly if catalog objects are changed during iteration
350         topic=topic_md.getObject()
351         if topic:
352             topic_parent=topic.aq_inner.aq_parent
353             print 'deleting: ', topic.id
354             topic_parent.manage_delObjects([topic.id])
355         try:
356             pc.uncatalog_object(topic_md.data_record_id_)               
357         except KeyError:
358             print 'tried to uncatalog object but failed'
359
360
361 def setupRemoteLeMills(self, portal):
362     #If variable set, tries to add REMOTE_SERVERS to be included in LeMill searches
363     search_tool=getToolByName(portal, 'lemill_search')
364     remote_boxes=search_tool.get_remote_lemilles()
365     for box in REMOTE_SERVERS:
366         add = 1
367         for r in remote_boxes.values():
368             if box == r['URL']:
369                 add = 0
370         if add:
371             search_tool.setNewLocation(box)
372
373
374 def setupCleanDuplicateActions(self, portal):
375     # Migrating Plone or LeMill causes duplication of certain actions. This is Plone's fault.
376     # addNewActions in Plone's ConfigurationMethods creates these actions without checking if they already exist.
377     atool=getToolByName(portal, 'portal_actions')
378     acts = atool._cloneActions()
379     clean_acts=[]
380     home_check = ownership_check = rename_check = paste_check = delete_check = cut_check = copy_check = 0
381     # Go through actions and add only first instances of the following actions to the new actions list:
382     for a in acts:
383         if a.getId()=='index_html': #'Home'
384             if home_check==0:
385                 home_check=1
386                 clean_acts.append(a)
387         elif a.getId()=='change_ownership':
388             if ownership_check==0:
389                 ownership_check=1
390                 clean_acts.append(a)
391         elif a.getId()=='rename':
392             if rename_check==0:
393                 rename_check=1
394                 clean_acts.append(a)
395         elif a.getId()=='paste':
396             if paste_check==0:
397                 paste_check=1
398                 clean_acts.append(a)
399         elif a.getId()=='cut':
400             if cut_check==0:
401                 copy_check=1
402                 clean_acts.append(a)
403         elif a.getId()=='copy':
404             if copy_check==0:
405                 copy_check=1
406                 clean_acts.append(a)
407         elif a.getId()=='delete':
408             if delete_check==0:
409                 delete_check=1
410                 clean_acts.append(a)
411         else:
412             clean_acts.append(a)
413     atool._actions = tuple( clean_acts )
414    
415
416 def setupFactoryTypes(self, portal):
417     """ set types that should use portal_factory """
418     ft = getToolByName(portal, 'portal_factory')
419     types_list = ft.getFactoryTypes()
420     for t in ALL_CONTENT_TYPES:
421         types_list[t] = 1
422     ft.manage_setPortalFactoryTypes(None, types_list)
423
424
425 # Still missing: ['nl','sl']
426
427 def setupLanguageTool(self, portal):
428     """ configure plone language tool to use browser language request negotation """
429     lt = getToolByName(portal, 'portal_languages')
430     if lt is None:
431         return
432     lt.manage_setLanguageSettings('en', #default language
433             AVAILABLE_TRANSLATIONS, # supported languages (needs to be a list, not a tuple)
434             1, # use cookie negotiation
435             1, # use request negotiation
436             0, # use path negotiation
437             0, # force language URLs (content)
438             1, # allow fallback (content)
439             1, # use combined languages
440             0, # display flags
441             0, # start neutral (content)
442             )
443
444 # TODO: when we get too many translations, we'll need to shorten the primary list
445 primary_language_list = AVAILABLE_TRANSLATIONS
446
447 def setupProperties(self, portal):
448     lt = getToolByName(portal, 'lemill_tool')
449     if not hasattr(lt, 'portal_integration'):
450         safeEditProperty(lt, 'portal_integration', value=False, data_type="boolean")
451     if not hasattr(lt, 'portal_search_link'):
452         safeEditProperty(lt, 'portal_search_link', value="TODO", data_type="string")
453     if not hasattr(lt, 'primary_languages'):
454         safeEditProperty(lt, 'primary_languages', value=primary_language_list, data_type="tokens")
455     if not hasattr(lt, 'allow_banning'):
456         safeEditProperty(lt, 'allow_banning', value=False, data_type="boolean")
457     if not hasattr(lt, 'irc_link'):
458         safeEditProperty(lt, 'irc_link', value='http://neumann.uiah.fi/irc/irc.cgi', data_type="string")
459     lt.language_dict=dict(LANGUAGES)
460     safeEditProperty(portal, 'validate_email', value=False, data_type="boolean")
461    
462 def setupLeMillFAQ(self, portal):
463     # Create MultimediaMaterial resource lemill-faq based on docs/FAQ.html
464     faqid='lemill-faq'
465     context=portal.content.webpages
466     if not hasattr(context, faqid):
467         addObject(context, faqid, 'MultimediaMaterial', 'LeMill FAQ')
468     else:
469         return 1 # If there is a lemill_faq don't overwrite it, because we don't want to lose community's changes
470     faq=getattr(context, faqid)   
471     homedir=package_home(globals())
472     file=open(homedir+'/docs/FAQ.html', 'r').read()
473     title=file[file.find('<title>')+7:file.rfind('</title>')]
474     body=file[file.find('<body>')+6:file.rfind('</body>')]
475     faq.edit(title=title, bodyText=body, language='en', tags=['faq', 'lemill'])
476     faq.manage_afterAdd(faq,context)
477     faq.at_post_create_script()
478     faq.state = 'public'
479     faq.reindexObject()
480
481 def setupLeMillAbout(self, portal):
482     # Create MultimediaMaterial resuurce about-lemill based on docs/About.html
483     aboutid='about-lemill'
484     context=portal.content.webpages
485     if not hasattr(context, aboutid):
486         addObject(context, aboutid, 'MultimediaMaterial', 'About LeMill')
487     else:
488         return 1 # If there is a about-lemill don't overwrite it
489     about=getattr(context, aboutid)
490     homedir=package_home(globals())
491     file=open(homedir+'/docs/About.html', 'r').read()
492     title=file[file.find('<title>')+7:file.rfind('</title>')]
493     body=file[file.find('<body>')+6:file.rfind('</body>')]
494     about.edit(title=title, bodyText=body, language='en', tags=['about', 'lemill'])
495     about.manage_afterAdd(about,context)
496     about.at_post_create_script()
497     about.state = 'public'
498     about.reindexObject()
499     #about.manage_permission(MODIFY_CONTENT, ('Manager','Owner'), acquire=0)
500         
501 def setupUnassignedDiscussionsGroup(self, portal):
502     # Create empty group to hold discussions for items not assigned to any group.
503     
504     if not hasattr(portal, 'community'):
505         return 1
506     parent=portal.community.groups
507     if hasattr(parent, 'unassigned_discussions'):
508         return 1
509     # We force our way through stupid allowed-content-types-limitations
510     if 'Large Plone Folder' not in parent.allowed_content_types:
511         parent.allowed_content_types=parent.allowed_content_types+('Large Plone Folder',)
512     if parent.filter_content_types==True:
513         parent.filter_content_types=False
514     addObject(parent, 'unassigned_discussions', 'Large Plone Folder', 'Discussions for resources not assigned to groups')
515     obj=parent.unassigned_discussions
516     syn_tool = getToolByName(portal, 'portal_syndication', None)
517     syn_tool.enableSyndication(obj)
518     obj.manage_permission(ADD_CONTENT_PERMISSION, ('Member',), acquire=1)
519     obj.manage_permission(LIST_FOLDER_CONTENTS, ('Member',), acquire=1)
520
521    
522        
523
524 #########
525 # Collect all setup functions into a setup widget
526
527 afunctions = {}
528 for f in dir():
529     if f.startswith('setup'):
530         func = eval("%s" % f)
531         if type(func) == types.FunctionType:
532             afunctions[f] = func
533
534 class LeMillSetup(SetupWidget):
535     type = 'LeMill Setup'
536
537     description = """Customization methods needed by the LeMill Plone portal"""
538
539     functions = afunctions
540
541     def setup(self):
542         pass
543
544     def delItems(self, fns):
545         out = []
546         out.append(('Currently there is no way to remove a function', INFO))
547         return out
548
549     def addItems(self, fns):
550         """This method is called when configuration methods need to be applied.
551         fns is the list of function names that need to be executed in order."""
552         out = []
553         for fn in fns:
554             # All functions are executed with the portal as the first actual parameter
555             self.functions[fn](self, self.portal)
556             out.append(('Function %s has been applied' % fn, INFO))
557         return out
558
559     def installed(self):
560         return []
561
562     def available(self):
563         """Get a list of availabel functions."""
564         funcs = self.functions.keys()
565         # Sort, so we get a pre-determined order.
566         # The functions need to be named properly
567         funcs.sort()
568         return funcs
Note: See TracBrowser for help on using the browser.