def serializeSchema(schema): """ Finds the FTI and model associated with a schema, and synchronizes the schema to the FTI model_source attribute. This method only works for schemas that were created from an FTI's model_source property BBB - deprecated """ # determine portal_type try: prefix, portal_type, schemaName = splitSchemaName(schema.__name__) except ValueError: # not a dexterity schema return # find the FTI and model fti = queryUtility(IDexterityFTI, name=portal_type) if fti.model_source: model = fti.lookupModel() # synchronize changes to the model syncSchema(schema, model.schemata[schemaName], overwrite=True) fti.model_source = serializeModel(model) else: raise TypeError("Changes to non-dynamic schemata not yet supported.")
def __call__(self): interface = self.interface filename = interface.queryTaggedValue(FILENAME_KEY, None) if filename is None: return schema = interface.queryTaggedValue(SCHEMA_NAME_KEY, u"") moduleName = interface.__module__ module = sys.modules.get(moduleName, None) directory = moduleName if hasattr(module, '__path__'): directory = module.__path__[0] else: while "." in moduleName: moduleName, _ = moduleName.rsplit('.', 1) module = sys.modules.get(moduleName, None) if hasattr(module, '__path__'): directory = module.__path__[0] break directory = os.path.abspath(directory) # Let / act as path separator on all platforms filename = filename.replace('/', os.path.sep) filename = os.path.abspath(os.path.join(directory, filename)) model = loadFile(filename) if schema not in model.schemata: raise ValueError( u"Schema '%s' specified for interface %s does not exist in %s." % (schema, interface.__identifier__, filename,)) syncSchema(model.schemata[schema], interface, overwrite=False)
def ftiModified(object, event): """When an FTI is modified, re-sync and invalidate the schema, if necessary. """ if not IDexterityFTI.providedBy(event.object): return fti = event.object portal_type = fti.getId() mod = {} for desc in event.descriptions: if IDexterityFTIModificationDescription.providedBy(desc): mod[desc.attribute] = desc.oldValue # If the factory utility name was modified, we may get an orphan if one # was registered as a local utility to begin with. If so, remove the # orphan. if 'factory' in mod: old_factory = mod['factory'] site_manager = getGlobalSiteManager() # Remove previously registered factory, if no other type uses it. unregister_factory(old_factory, site_manager) # Register a new local factory if one doesn't exist already new_factory_utility = queryUtility(IFactory, name=fti.factory) if new_factory_utility is None: site_manager.registerUtility(DexterityFactory(portal_type), IFactory, fti.factory, info='plone.dexterity.dynamic') # Determine if we need to invalidate the schema at all if 'behaviors' in mod \ or 'schema' in mod \ or 'model_source' in mod \ or 'model_file' in mod \ or 'schema_policy' in mod: # Determine if we need to re-sync a dynamic schema if ((fti.model_source or fti.model_file) and ('model_source' in mod or 'model_file' in mod or 'schema_policy' in mod)): schemaName = portalTypeToSchemaName(portal_type) schema = getattr(plone.dexterity.schema.generated, schemaName) model = fti.lookupModel() sync_bases = 'schema_policy' in mod syncSchema(model.schema, schema, overwrite=True, sync_bases=sync_bases) notify(SchemaInvalidatedEvent(portal_type))
def ftiModified(object, event): """When an FTI is modified, re-sync and invalidate the schema, if necessary. """ if not IDexterityFTI.providedBy(event.object): return fti = event.object portal_type = fti.getId() mod = {} for desc in event.descriptions: if IDexterityFTIModificationDescription.providedBy(desc): mod[desc.attribute] = desc.oldValue # If the factory utility name was modified, we may get an orphan if one # was registered as a local utility to begin with. If so, remove the # orphan. if 'factory' in mod: old_factory = mod['factory'] site = getUtility(ISiteRoot) site_manager = getSiteManager(site) # Remove previously registered factory, if no other type uses it. unregister_factory(old_factory, site_manager) # Register a new local factory if one doesn't exist already new_factory_utility = queryUtility(IFactory, name=fti.factory) if new_factory_utility is None: site_manager.registerUtility( DexterityFactory(portal_type), IFactory, fti.factory, info='plone.dexterity.dynamic' ) # Determine if we need to invalidate the schema at all if 'behaviors' in mod \ or 'schema' in mod \ or 'model_source' in mod \ or 'model_file' in mod \ or 'schema_policy' in mod: # Determine if we need to re-sync a dynamic schema if (fti.model_source or fti.model_file) \ and ('model_source' in mod or 'model_file' in mod or 'schema_policy' in mod): schemaName = portalTypeToSchemaName(portal_type) schema = getattr(plone.dexterity.schema.generated, schemaName) model = fti.lookupModel() sync_bases = 'schema_policy' in mod syncSchema(model.schema, schema, overwrite=True, sync_bases=sync_bases) notify(SchemaInvalidatedEvent(portal_type))
def __call__(self, name, module): """Someone tried to load a dynamic interface that has not yet been created yet. We will attempt to load it from the FTI if we can. If the FTI doesn't exist, create a temporary marker interface that we can fill later. The goal here is to ensure that we create exactly one interface instance for each name. If we can't find an FTI, we'll cache the interface so that we don't get a new one with a different id later. This cache is global, so we synchronise the method with a thread lock. Once we have a properly populated interface, we set it onto the module using setattr(). This means that the factory will not be invoked again. """ try: prefix, portal_type, schemaName = splitSchemaName(name) except ValueError: return None if name in self._transient_SCHEMA_CACHE: schema = self._transient_SCHEMA_CACHE[name] else: bases = () is_default_schema = not schemaName if is_default_schema: bases += (IDexteritySchema,) schema = InterfaceClass(name, bases, __module__=module.__name__) if is_default_schema: alsoProvides(schema, IContentType) if portal_type is not None: fti = queryUtility(IDexterityFTI, name=portal_type) else: fti = None if fti is None and name not in self._transient_SCHEMA_CACHE: self._transient_SCHEMA_CACHE[name] = schema elif fti is not None: model = fti.lookupModel() syncSchema(model.schemata[schemaName], schema, sync_bases=True) # Save this schema in the module - this factory will not be # called again for this name if name in self._transient_SCHEMA_CACHE: del self._transient_SCHEMA_CACHE[name] setattr(module, name, schema) return schema
def test_syncSchema_always_overwrites_fields_from_bases(self): class IBase(Interface): one = schema.TextLine(title=u'A') class ISource(Interface): one = schema.TextLine(title=u'B') class IDest(IBase): pass utils.syncSchema(ISource, IDest, overwrite=False) self.assertTrue(IDest['one'].interface is IDest)
def test_syncSchema_always_overwrites_fields_from_bases(self): class IBase(Interface): one = schema.TextLine(title=u"A") class ISource(Interface): one = schema.TextLine(title=u"B") class IDest(IBase): pass utils.syncSchema(ISource, IDest, overwrite=False) self.assertTrue(IDest["one"].interface is IDest)
def serializeSchemaContext(schema_context, event=None): """ Serializes the schema associated with a schema context. The serialized schema is saved to the model_source property of the FTI associated with the schema context. """ # find the FTI and model fti = schema_context.fti schemaName = schema_context.schemaName schema = schema_context.schema model = fti.lookupModel() # synchronize changes to the model syncSchema(schema, model.schemata[schemaName], overwrite=True) fti.model_source = serializeModel(model)
def test_syncSchema_overwrite_no_bases(self): class IBase(Interface): base = schema.TextLine(title=u"Base") class ISource(IBase): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") class IDest(Interface): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") utils.syncSchema(ISource, IDest, overwrite=False, sync_bases=False) self.assertEqual((Interface,), IDest.__bases__) self.assertEqual(["two", "one", "three"], getFieldNamesInOrder(IDest))
def test_syncSchema_overwrite_no_bases(self): class IBase(Interface): base = schema.TextLine(title=u"Base") class ISource(IBase): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") class IDest(Interface): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") utils.syncSchema(ISource, IDest, overwrite=False, sync_bases=False) self.assertEquals((Interface, ), IDest.__bases__) self.assertEquals(['two', 'one', 'three'], getFieldNamesInOrder(IDest))
def test_syncSchema_overwrite_with_bases(self): class IBase(Interface): base = schema.TextLine(title=u"Base") class IOtherBase(Interface): foo = schema.TextLine(title=u"Foo") class ISource(IBase): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") class IDest(IOtherBase): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") utils.syncSchema(ISource, IDest, overwrite=True, sync_bases=True) self.assertEqual((IBase, ), IDest.__bases__) self.assertEqual(['base', 'one', 'two'], getFieldNamesInOrder(IDest))
def test_syncSchema_overwrite_with_bases_and_no_overwrite_with_old_bases(self): class IBase(Interface): base = schema.TextLine(title=u"Base") class IOtherBase(Interface): foo = schema.TextLine(title=u"Foo") class ISource(IBase): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") class IDest(IOtherBase, IBase): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") utils.syncSchema(ISource, IDest, overwrite=False, sync_bases=True) self.assertEquals((IBase, IOtherBase, ), IDest.__bases__) self.assertEquals(['base', 'foo', 'two', 'one', 'three'], getFieldNamesInOrder(IDest))
def serializeSchema(schema): """Taken from plone.app.dexterity.serialize Finds the FTI and model associated with a schema, and synchronizes the schema to the FTI model_source attribute. """ # determine portal_type try: prefix, portal_type, schemaName = splitSchemaName(schema.__name__) except ValueError: # not a dexterity schema return # find the FTI and model fti = queryUtility(IDexterityFTI, name=portal_type) model = fti.lookupModel() # synchronize changes to the model syncSchema(schema, model.schemata[schemaName], overwrite=True) fti.model_source = serializeModel(model)
def test_syncSchema_with_markers_overwrite(self): class IMarker(Interface): pass class ISource(Interface): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") four = schema.Text(title=u"C") alsoProvides(ISource['one'], IMarker) alsoProvides(ISource['four'], IMarker) class IDest(Interface): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") utils.syncSchema(ISource, IDest, overwrite=True) self.failUnless(IMarker.providedBy(IDest['one'])) self.failIf(IMarker.providedBy(IDest['two'])) self.failUnless(IMarker.providedBy(IDest['four']))
def test_syncSchema_with_markers_overwrite(self): class IMarker(Interface): pass class ISource(Interface): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") four = schema.Text(title=u"C") alsoProvides(ISource["one"], IMarker) alsoProvides(ISource["four"], IMarker) class IDest(Interface): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") utils.syncSchema(ISource, IDest, overwrite=True) self.assertTrue(IMarker.providedBy(IDest["one"])) self.assertFalse(IMarker.providedBy(IDest["two"])) self.assertTrue(IMarker.providedBy(IDest["four"]))
def test_syncSchema(self): class ISource(Interface): one = schema.TextLine(title=u"A") # order: 0 two = schema.Int(title=u"B") # order: 1 class IDest(Interface): one = schema.TextLine(title=u"C") # order: 0 three = schema.Int(title=u"D") # order: 1 ISource.setTaggedValue("tag1", "tag one") ISource.setTaggedValue("tag2", "tag two") IDest.setTaggedValue("tag1", "first tag") utils.syncSchema(ISource, IDest) self.assertEqual(u"C", IDest['one'].title) self.assertEqual(['one', 'two'], getFieldNamesInOrder(ISource)) self.assertEqual(['two', 'one', 'three'], getFieldNamesInOrder(IDest)) self.assertEqual("first tag", IDest.getTaggedValue("tag1")) self.assertEqual("tag two", IDest.getTaggedValue("tag2"))
def test_syncSchema_overwrite(self): class ISource(Interface): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") class IDest(Interface): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") ISource.setTaggedValue("tag1", "tag one") ISource.setTaggedValue("tag2", "tag two") IDest.setTaggedValue("tag1", "first tag") utils.syncSchema(ISource, IDest, overwrite=True) self.assertEqual(u"A", IDest["one"].title) self.assertEqual(["one", "two"], getFieldNamesInOrder(ISource)) self.assertEqual(["one", "two"], getFieldNamesInOrder(IDest)) self.assertEqual("tag one", IDest.getTaggedValue("tag1")) self.assertEqual("tag two", IDest.getTaggedValue("tag2"))
def test_syncSchema_overwrite(self): class ISource(Interface): one = schema.TextLine(title=u"A") two = schema.Int(title=u"B") class IDest(Interface): one = schema.TextLine(title=u"C") three = schema.Int(title=u"D") ISource.setTaggedValue("tag1", "tag one") ISource.setTaggedValue("tag2", "tag two") IDest.setTaggedValue("tag1", "first tag") utils.syncSchema(ISource, IDest, overwrite=True) self.assertEquals(u"A", IDest['one'].title) self.assertEquals(['one', 'two'], getFieldNamesInOrder(ISource)) self.assertEquals(['one', 'two'], getFieldNamesInOrder(IDest)) self.assertEquals("tag one", IDest.getTaggedValue("tag1")) self.assertEquals("tag two", IDest.getTaggedValue("tag2"))
def __call__(self): interface = self.interface filename = interface.queryTaggedValue(FILENAME_KEY, None) if filename is None: return schema = interface.queryTaggedValue(SCHEMA_NAME_KEY, u"") moduleName = interface.__module__ module = sys.modules.get(moduleName, None) directory = moduleName if hasattr(module, '__path__'): directory = module.__path__[0] else: while "." in moduleName: moduleName, _ = moduleName.rsplit('.', 1) module = sys.modules.get(moduleName, None) if hasattr(module, '__path__'): directory = module.__path__[0] break directory = os.path.abspath(directory) # Let / act as path separator on all platforms filename = filename.replace('/', os.path.sep) filename = os.path.abspath(os.path.join(directory, filename)) model = loadFile(filename) if schema not in model.schemata: raise ValueError( u"Schema '{0}' specified for interface {1} does not exist " "in {2}.".format( schema, interface.__identifier__, filename, ) ) syncSchema(model.schemata[schema], interface, overwrite=False)
def test_syncSchema(self): class ISource(Interface): one = schema.TextLine(title=u"A") # order: 0 two = schema.Int(title=u"B") # order: 1 class IDest(Interface): one = schema.TextLine(title=u"C") # order: 0 three = schema.Int(title=u"D") # order: 1 ISource.setTaggedValue("tag1", "tag one") ISource.setTaggedValue("tag2", "tag two") IDest.setTaggedValue("tag1", "first tag") utils.syncSchema(ISource, IDest) self.assertEquals(u"C", IDest['one'].title) self.assertEquals(['one', 'two'], getFieldNamesInOrder(ISource)) self.assertEquals(['two', 'one', 'three'], getFieldNamesInOrder(IDest)) self.assertEquals("first tag", IDest.getTaggedValue("tag1")) self.assertEquals("tag two", IDest.getTaggedValue("tag2"))
def run(self): logger.info('Migrating to imio.dms.mail 1.0...') self.cleanRegistries() self.upgradeProfile('collective.dms.mailcontent:default') # We have to reapply type info before doing other subproducts migration self.runProfileSteps('imio.dms.mail', steps=['typeinfo']) # We have to update type schema because plone.dexterity doesn't detect schema_policy modification. BUG #44 for portal_type in ['dmsincomingmail', 'dmsoutgoingmail']: schemaName = dxutils.portalTypeToSchemaName(portal_type) schema = getattr(plone.dexterity.schema.generated, schemaName) fti = getUtility(IDexterityFTI, name=portal_type) model = fti.lookupModel() syncSchema(model.schema, schema, overwrite=True, sync_bases=True) notify(plone.dexterity.schema.SchemaInvalidatedEvent(portal_type)) self.upgradeProfile('collective.task:default') self.upgradeProfile('dexterity.localroles:default') self.upgradeProfile('dexterity.localrolesfield:default') self.upgradeProfile('collective.contact.plonegroup:default') self.runProfileSteps('imio.dms.mail', steps=['actions', 'componentregistry', 'controlpanel', 'plone.app.registry', 'portlets', 'repositorytool', 'rolemap', 'sharing', 'workflow']) self.portal.portal_workflow.updateRoleMappings() self.runProfileSteps('collective.dms.mailcontent', steps=['controlpanel']) self.runProfileSteps('collective.contact.plonegroup', steps=['controlpanel']) self.reinstall([ 'collective.messagesviewlet:messages', 'collective.querynextprev:default', 'imio.dashboard:default', ]) # set jqueryui autocomplete to False. If not, contact autocomplete doesn't work self.registry['collective.js.jqueryui.controlpanel.IJQueryUIPlugins.ui_autocomplete'] = False # delete old dmsmail portlet self.delete_portlet(self.portal, 'portlet_maindmsmail') # remove deprecated interfaces self.remove_contact_interfaces() # moved notes content to task_description catalog = api.portal.get_tool('portal_catalog') brains = catalog.searchResults(portal_type='dmsincomingmail') for brain in brains: obj = brain.getObject() if not base_hasattr(obj, 'notes') or not obj.notes: continue text = u'<p>%s</p>\r\n' % obj.notes.replace('\r\n', '<br />\r\n') obj.task_description = create_richtextval(text) delattr(obj, 'notes') # obj.reindexObject() # replace collections by Dashboard collections im_folder = self.portal['incoming-mail'] alsoProvides(im_folder, INextPrevNotNavigable) alsoProvides(im_folder, IIMDashboard) self.replaceCollections(im_folder) # apply contact faceted config reimport_faceted_config(self.portal['contacts'], 'contacts-faceted.xml') # add new indexes for dashboard addOrUpdateIndexes(self.portal, indexInfos={'mail_type': ('FieldIndex', {}), 'mail_date': ('DateIndex', {}), 'in_out_date': ('DateIndex', {}), }) # set dashboard on incoming mail configure_faceted_folder(im_folder, xml='default_dashboard_widgets.xml', default_UID=im_folder['mail-searches']['all_mails'].UID()) # set task local roles configuration configure_task_rolefields(self.portal) # update dexterity local roles configuration self.update_local_roles() # add task actionspanel config if not self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions']: self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions'] = [] self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions'] += \ ['task.back_in_created|', 'task.back_in_to_assign|', 'task.back_in_to_do|', 'task.back_in_progress|', 'task.back_in_realized|'] # activate ckeditor configure_ckeditor(self.portal, custom='ged') # Set markup allowed types adapter = MarkupControlPanelAdapter(self.portal) adapter.set_allowed_types(['text/html']) # update searchabletext self.update_dmsmainfile() self.upgradeAll() for prod in ['plone.formwidget.autocomplete', 'collective.documentviewer', 'plone.formwidget.masterselect', 'collective.contact.core', 'collective.contact.duplicated', 'collective.dms.basecontent', 'collective.dms.scanbehavior', 'collective.externaleditor', 'plone.app.collection', 'plone.app.intid', 'collective.contact.facetednav', 'plonetheme.imioapps', 'PasswordStrength', 'imio.dms.mail']: mark_last_version(self.portal, product=prod) self.portal.manage_permission('CMFEditions: Revert to previous versions', ('Manager', 'Site Administrator'), acquire=0) #self.refreshDatabase() self.finish()
def run(self): logger.info('Migrating to imio.dms.mail 1.0...') self.cleanRegistries() self.upgradeProfile('collective.dms.mailcontent:default') # We have to reapply type info before doing other subproducts migration self.runProfileSteps('imio.dms.mail', steps=['typeinfo']) # We have to update type schema because plone.dexterity doesn't detect schema_policy modification. BUG #44 for portal_type in ['dmsincomingmail', 'dmsoutgoingmail']: # i_e ok schemaName = dxutils.portalTypeToSchemaName(portal_type) schema = getattr(plone.dexterity.schema.generated, schemaName) fti = getUtility(IDexterityFTI, name=portal_type) model = fti.lookupModel() syncSchema(model.schema, schema, overwrite=True, sync_bases=True) notify(plone.dexterity.schema.SchemaInvalidatedEvent(portal_type)) self.upgradeProfile('collective.task:default') self.upgradeProfile('dexterity.localroles:default') self.upgradeProfile('dexterity.localrolesfield:default') self.upgradeProfile('collective.contact.plonegroup:default') self.runProfileSteps('imio.dms.mail', steps=[ 'actions', 'componentregistry', 'controlpanel', 'plone.app.registry', 'portlets', 'repositorytool', 'rolemap', 'sharing', 'workflow' ]) self.portal.portal_workflow.updateRoleMappings() self.runProfileSteps('collective.dms.mailcontent', steps=['controlpanel']) self.runProfileSteps('collective.contact.plonegroup', steps=['controlpanel']) self.reinstall([ 'collective.messagesviewlet:messages', 'collective.querynextprev:default', 'imio.dashboard:default', ]) # set jqueryui autocomplete to False. If not, contact autocomplete doesn't work self.registry[ 'collective.js.jqueryui.controlpanel.IJQueryUIPlugins.ui_autocomplete'] = False # delete old dmsmail portlet self.delete_portlet(self.portal, 'portlet_maindmsmail') # remove deprecated interfaces self.remove_contact_interfaces() # moved notes content to task_description catalog = api.portal.get_tool('portal_catalog') brains = catalog.searchResults(portal_type='dmsincomingmail') # i_e ok for brain in brains: obj = brain.getObject() if not base_hasattr(obj, 'notes') or not obj.notes: continue text = u'<p>%s</p>\r\n' % obj.notes.replace('\r\n', '<br />\r\n') obj.task_description = richtextval(text) delattr(obj, 'notes') # obj.reindexObject() # replace collections by Dashboard collections im_folder = self.portal['incoming-mail'] alsoProvides(im_folder, INextPrevNotNavigable) alsoProvides(im_folder, IIMDashboard) self.replaceCollections(im_folder) # apply contact faceted config reimport_faceted_config(self.portal['contacts'], 'contacts-faceted.xml') # add new indexes for dashboard addOrUpdateIndexes(self.portal, indexInfos={ 'mail_type': ('FieldIndex', {}), 'mail_date': ('DateIndex', {}), 'in_out_date': ('DateIndex', {}), }) # set dashboard on incoming mail configure_faceted_folder( im_folder, xml='default_dashboard_widgets.xml', default_UID=im_folder['mail-searches']['all_mails'].UID()) # set task local roles configuration configure_task_rolefields(self.portal) # update dexterity local roles configuration self.update_local_roles() # add task actionspanel config if not self.registry[ 'imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions']: self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions'] = [] self.registry['imio.actionspanel.browser.registry.IImioActionsPanelConfig.transitions'] += \ ['task.back_in_created|', 'task.back_in_to_assign|', 'task.back_in_to_do|', 'task.back_in_progress|', 'task.back_in_realized|'] # activate ckeditor configure_ckeditor(self.portal, custom='ged') # Set markup allowed types adapter = MarkupControlPanelAdapter(self.portal) adapter.set_allowed_types(['text/html']) # update searchabletext self.update_dmsmainfile() self.upgradeAll() for prod in [ 'plone.formwidget.autocomplete', 'collective.documentviewer', 'plone.formwidget.masterselect', 'collective.contact.core', 'collective.contact.duplicated', 'collective.dms.basecontent', 'collective.dms.scanbehavior', 'collective.externaleditor', 'plone.app.collection', 'plone.app.intid', 'collective.contact.facetednav', 'plonetheme.imioapps', 'PasswordStrength', 'imio.dms.mail' ]: mark_last_version(self.portal, product=prod) self.portal.manage_permission( 'CMFEditions: Revert to previous versions', ('Manager', 'Site Administrator'), acquire=0) #self.refreshDatabase() self.finish()