################################################################################## # Copyright (c) 2004-2009 Utah State University, All rights reserved. # Portions copyright 2009 Massachusetts Institute of Technology, All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 2. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ################################################################################## __author__ = '''Brent Lambert, David Ray, Jon Thomas''' __version__ = '$ Revision 0.0 $'[11:-2] from Products.CMFPlone.utils import _createObjectByType from StringIO import StringIO import string from zope.component import getUtility, getMultiAdapter from Products.CMFPlone.factory import addPloneSite from Products.CMFCore.utils import getToolByName from enpraxis.educommons import portlet from collective.imstransport.utilities.interfaces import IIMSTransportUtility from plone.portlets.interfaces import IPortletManager, IPortletAssignmentMapping from Products.CMFDynamicViewFTI.fti import DynamicViewTypeInformation from Products.CMFCore.ActionInformation import Action def migrate(portal_setup): """ Migration from eduCommons 3.0.2 to 3.1.0 """ portal_url = getToolByName(portal_setup, 'portal_url') portal= portal_url.getPortalObject() updateTransforms(portal) updateKupu(portal) createAccessibilityGuidelines(portal) updateSettings(portal) updateActions(portal) updateCourses(portal) disableKSS(portal) def updateTransforms(portal): """ Update safe_html portal_transform """ from Products.CMFDefault.utils import VALID_TAGS from Products.CMFDefault.utils import NASTY_TAGS valid_tags = VALID_TAGS.copy() nasty_tags = NASTY_TAGS.copy() nasty_tags.pop('applet') nasty_tags.pop('embed') nasty_tags.pop('object') nasty_tags.pop('script') valid_tags['applet'] = 1 valid_tags['embed'] = 1 valid_tags['object'] = 1 valid_tags['thead'] = 1 valid_tags['tfoot'] = 1 valid_tags['param'] = 0 kwargs = {'nasty_tags': nasty_tags, 'valid_tags': valid_tags, 'remove_javascript': 0} transform = getattr(getToolByName(portal, 'portal_transforms'), 'safe_html') for k in list(kwargs): if isinstance(kwargs[k], dict): v = kwargs[k] kwargs[k+'_key'] = v.keys() kwargs[k+'_value'] = [str(s) for s in v.values()] del kwargs[k] transform.set_parameters(**kwargs) def updateKupu(portal): """ update kupu default settings """ kupu_tool = getToolByName(portal, 'kupu_library_tool') combos = [] #enable original size images kupu_tool.allowOriginalImageSize = True #remove default stripped combo in kupu kupu_tool.set_stripped_combinations(combos) #link using UIDs kupu_tool.linkbyuid = True #Allow captioned images kupu_tool.captioning = True #Add eduCommons documentTable style kupu_tool.table_classnames += ['documentTable|eduCommons Content'] #Make undo/redo buttons visible kupu_tool._setToolbarFilters([{'id': 'bg-undo', 'override': '', 'visible': 1}, {'id': 'undo-button', 'override': '', 'visible': 1}, {'id': 'redo-button', 'override': '', 'visible': 1}, {'id': 'embed-tab', 'override': '', 'visible': 1},], '') #Remove image_preview class from embedded images kupu_tool.updatePreviewActions([{'classes': '', 'defscale': '', 'expression': 'string:${object_url}/image_thumb', 'marker': 'x', 'mediatype': 'image', 'normal': '', 'portal_type': 'Image', 'scalefield': 'image'}, {'classes': '', 'defscale': 'image_preview', 'expression': 'string:${object_url}/image_thumb', 'marker': 'x', 'mediatype': 'image', 'normal': '', 'portal_type': 'News Item', 'scalefield': 'image'}, {'classes': '', 'defscale': 'image_preview', 'expression': '', 'marker': '-', 'mediatype': 'image', 'normal': '', 'portal_type': '', 'scalefield': 'image'}]) #remove cellpadding, cellspacing, bgcolor from sripped attrs list attrs_to_remove = ['cellpadding', 'cellspacing', 'bgcolor'] stripped_attributes = kupu_tool.get_stripped_attributes() for attr in attrs_to_remove: if attr in stripped_attributes: stripped_attributes.remove(attr) kupu_tool.set_stripped_attributes(stripped_attributes) def createAccessibilityGuidelines(portal): """ create accessibility guidelines page """ if hasattr(portal, 'help'): help = getattr(portal, 'help') _createObjectByType('Document', help, id="accessibility-guidelines", title="Accessibility Guidelines", description='Guidelines to help determine if content meets accessibility standards.') context = getattr(portal.help, 'accessibility-guidelines') publishObject(context) template = '@@accessibilityguidelines_view' template = context.restrictedTraverse(str(template)) text = template(context) context.setText(text) def updateSettings(portal): """ update various portal settings """ #Check Mail Host Title if '' == portal.MailHost.title: portal.MailHost = 'Plone Mail Host' #Fix RSS max_items portal.portal_syndication.max_items = 999 #update courselist RSS max_items if hasattr(portal, 'courselist'): portal.courselist.syndication_information.max_items = 999 #update max_items on each Department div_brains = portal.portal_catalog(path= {'query':'/'.join(portal.getPhysicalPath())+'/'}, portal_type='Division') for div in div_brains: div = div.getObject() if hasattr(div, 'syndication_information'): div.syndication_information.max_items = 999 #update max_items on each Course course_brains = portal.portal_catalog(path= {'query':'/'.join(portal.getPhysicalPath())+'/'}, portal_type='Course') for course in course_brains: course = course.getObject() if hasattr(course, 'syndication_information'): course.syndication_information.max_items = 999 #set portal to openOCW in educommons_properties ec_props = portal.portal_properties.educommons_properties ec_props.manage_addProperty(id='reusecourse_enabled', type='boolean', value='True') ec_props.manage_addProperty(id='reusecourse_instance', type='string', value='http://openocw.org') #create FSSFile Type types_tool = portal.portal_types types_tool.manage_addTypeInformation(DynamicViewTypeInformation.meta_type, id='FSSFile', typeinfo_name='FSSFile') FSSFile = types_tool.FSSFile FSSFile.description = 'An external file uploaded to the site which uses FileSystemStorage' FSSFile.title = 'FSSFile' FSSFile.content_icon = 'file_icon.gif' FSSFile.content_meta_type = 'FSSFile' FSSFile.product = 'Products.eduCommons' FSSFile.factory = 'addFSSFile' FSSFile.immediate_view = 'file_view' FSSFile.default_view = 'file_view' FSSFile.view_methods = ('file_view',) wf_tool = portal.portal_workflow wf_tool.setChainForPortalTypes(('FSSFile',), 'content_workflow') #allow FSSFile for courses portal.portal_types.Course.manage_changeProperties(allowed_content_types=('File', 'FSSFile', 'Image', 'Document', 'Link', 'Folder')) def updateActions(portal): """ Add the Package Course folder_button action """ at = portal.portal_actions pkg_action = Action('package_course', title = 'Package Course', descriptiong = '', url_expr = 'string:@@package_course_view:method', available_expr = "python:object.Type() == 'Course' and object.portal_workflow.getInfoFor(object, 'review_state') == 'Published'", permissions = 'Modify portal content', visible = 1) folder_buttons = at['folder_buttons'] folder_buttons._setObject('package_course', pkg_action) def updateCourses(portal): """ Migrate current courses in ZODB """ course_brains = portal.portal_catalog(path= {'query':'/'.join(portal.getPhysicalPath())+'/'}, portal_type='Course') for course in course_brains: course = course.getObject() #add reuse course portlet to existent courses rightColumn = getUtility(IPortletManager, name=u'plone.rightcolumn', context=course) right = getMultiAdapter((portal, rightColumn), IPortletAssignmentMapping, context=course) if u'Reuse Course' not in right.keys(): right[u'Reuse Course'] = portlet.reusecourseportlet.Assignment() #add crosslisting field to existent courses crosslisting = course.Schema()['crosslisting'] crosslisting.set(course, ('')) #repackage Download this Course object for existent courses zip_exists = 0 for object in course.listFolderContents(): if 'download this course' == string.lower(object.Title()): #delete object, repackage as FSS based IMS package zip_exists = 1 course.manage_delObjects([object.getId()]) #commenting out for now, too fragile for all potential courses. #Will have to hand package courses post migration if zip_exists == 1: file_id = course.id + '.zip' ims_util = getUtility(IIMSTransportUtility) data, file_id = ims_util.exportZipfile(course, file_id) course.invokeFactory("FSSFile",id=file_id, title="Download this Course") fileobj = getattr(course,file_id) publishObject(fileobj) fileobj.setTitle("Download This Course") fileobj.setExcludeFromNav(True) fileobj.setFile(data) #appendObjPosition(fileobj) course.portal_catalog.reindexObject(fileobj) def publishObject(context): """ Move an object into the published state """ wftool = getToolByName(context, 'portal_workflow') if wftool.getInfoFor(context, 'review_state') != 'Published': wftool.doActionFor(context, 'submit') wftool.doActionFor(context, 'release') wftool.doActionFor(context, 'publish') def disableKSS(context): """ Disable KSS/inline edits """ kss = context.portal_kss kss.getResource('at.kss').setEnabled(False) kss.getResource('plone.kss').setEnabled(False)