def register_cover_tiles(setup_tool): """Register collective.cover tiles.""" registered = api.portal.get_registry_record('plone.app.tiles', default=None) if registered is not None: [registered.append(t) for t in TILES if t not in registered] assert set(registered) & set(TILES) == set(TILES) logger.info('Tiles for collective.cover were registered')
def facebook_prefetching(obj, event): """Call Facebook Graph API endpoint to keep metadata of published objects always updated. """ record = ISocialLikeSettings.__identifier__ + '.facebook_prefetch_enabled' prefetch_enable = api.portal.get_registry_record(record, default=False) if not prefetch_enable: return try: review_state = api.content.get_state(obj) except WorkflowException: # images and files have no associated workflow by default review_state = 'published' if review_state not in ('published', ): return # can't prefetch non-public content url = obj.absolute_url() endpoint = 'https://graph.facebook.com/?id=' + url + '&scrape=true' try: r = requests.post(endpoint, timeout=5) except requests.exceptions.RequestException as e: logger.warn('Prefetch failure: ' + str(e)) return if r.status_code == '200': logger.info('Prefetch successful: ' + url) else: logger.warn( 'Prefetch error {code} ({reason}): {debug}'.format( code=r.status_code, reason=r.reason, debug=str(r.json())))
def assign_canonical_url(obj, event): """Assing canonical URL to the object after it is published.""" if event.status['review_state'] not in ('published', ): # don't a assign a canonical URL as this is not a public state return record = ISocialLikeSettings.__identifier__ + '.canonical_domain' try: canonical_domain = api.portal.get_registry_record(record) except api.exc.InvalidParameterError: # package is not installed or record deleted; do nothing return # we can't assign a canonical URL without a canonical domain if canonical_domain: # FIXME: we're currently ignoring the Plone site id # https://github.com/collective/sc.social.like/issues/119 path = '/'.join(obj.getPhysicalPath()[2:]) obj.canonical_url = '{0}/{1}'.format(canonical_domain, path) logger.info('canonical_url set for {0}'.format(obj.canonical_url)) else: logger.warn( 'Canonical domain not set in Social Media configlet; ' "Facebook's Open Graph canonical URL (og:orl) will not be available" )
def update_configlet_information(setup_tool): """Update configlet information.""" controlpanel = api.portal.get_tool(name="portal_controlpanel") configlet = controlpanel.getActionObject("Products/sociallikes") if configlet is not None: configlet.setActionExpression("string:${portal_url}/@@sociallike-settings") logger.info("Configlet information updated")
def migrate_settings_to_registry(setup_tool): """Migrate settings to registry.""" profile = "profile-{0}:default".format(PROJECTNAME) setup_tool.runImportStepFromProfile(profile, "plone.app.registry") portal_properties = api.portal.get_tool(name="portal_properties") if "sc_social_likes_properties" in portal_properties: old_props = portal_properties.sc_social_likes_properties registry = getUtility(IRegistry) settings = registry.forInterface(ISocialLikeSettings) # ignore types not allowed types_ = _enforce_type_constraints(old_props.enabled_portal_types) settings.enabled_portal_types = types_ settings.plugins_enabled = old_props.plugins_enabled settings.typebutton = old_props.typebutton settings.do_not_track = old_props.do_not_track # this property may have an invalid value under certain circunstances try: settings.fbaction = old_props.fbaction except ConstraintNotSatisfied: # empty string settings.fbaction = u"like" # use default settings.fbbuttons = old_props.fbbuttons # these fields are no longer TextLine but ASCIILine # we need to avoid 'None' values caused by missing defaults settings.facebook_username = str(old_props.fbadmins) if old_props.fbadmins else "" settings.facebook_app_id = str(old_props.fbapp_id) if old_props.fbapp_id else "" settings.twitter_username = str(old_props.twittvia) if old_props.twittvia else "" logger.info("Settings migrated") del portal_properties["sc_social_likes_properties"] logger.info('Property sheet "sc_social_likes_properties" removed')
def facebook_prefetching(obj, event): """Call Facebook Graph API endpoint to keep metadata of published objects always updated. """ record = ISocialLikeSettings.__identifier__ + '.facebook_prefetch_enabled' prefetch_enable = api.portal.get_registry_record(record, default=False) if not prefetch_enable: return try: review_state = api.content.get_state(obj) except WorkflowException: # images and files have no associated workflow by default review_state = 'published' if review_state not in ('published', ): return # can't prefetch non-public content url = obj.absolute_url() endpoint = 'https://graph.facebook.com/?id=' + url + '&scrape=true' try: r = requests.post(endpoint, timeout=5) except requests.exceptions.RequestException as e: logger.warn('Prefetch failure: ' + str(e)) return if r.status_code == '200': logger.info('Prefetch successful: ' + url) else: logger.warn('Prefetch error {code} ({reason}): {debug}'.format( code=r.status_code, reason=r.reason, debug=str(r.json())))
def update_canonical_url(self, data): """Update the canonical URL of all objects in the catalog providing the ISocialMedia behavior. Objects published before the specified date will be updated using the canonical domain defined in this form; objects published on or after that date will be updated using the canonical domain defined in the control panel configlet. """ old_canonical_domain = data['old_canonical_domain'] new_canonical_domain = self.canonical_domain published_before = data['published_before'].isoformat() results = api.content.find( object_provides=ISocialMedia.__identifier__, review_state='published', ) total = len(results) logger.info( u'{0} objects will have their canonical URL updated'.format(total)) for obj in get_valid_objects(results): # FIXME: we're currently ignoring the Plone site id # https://github.com/collective/sc.social.like/issues/119 path = '/'.join(obj.getPhysicalPath()[2:]) if obj.effective_date < DateTime(published_before): # use the canonical domain defined in this form obj.canonical_url = '{0}/{1}'.format(old_canonical_domain, path) elif not obj.canonical_url: # use the canonical domain defined in the configlet obj.canonical_url = '{0}/{1}'.format(new_canonical_domain, path) logger.info(u'Done.') self.status = u'Update complete; {0} items processed.'.format(total)
def update_canonical_url(self, data): """Update the canonical URL of all objects in the catalog providing the ISocialMedia behavior. Objects published before the specified date will be updated using the canonical domain defined in this form; objects published on or after that date will be updated using the canonical domain defined in the control panel configlet. """ old_canonical_domain = data['old_canonical_domain'] new_canonical_domain = self.canonical_domain published_before = data['published_before'].isoformat() results = api.content.find( object_provides=ISocialMedia.__identifier__, review_state='published', ) total = len(results) logger.info(u'{0} objects will have their canonical URL updated'.format(total)) for obj in get_valid_objects(results): # FIXME: we're currently ignoring the Plone site id # https://github.com/collective/sc.social.like/issues/119 path = '/'.join(obj.getPhysicalPath()[2:]) if obj.effective_date < DateTime(published_before): # use the canonical domain defined in this form obj.canonical_url = '{0}/{1}'.format(old_canonical_domain, path) elif not obj.canonical_url: # use the canonical domain defined in the configlet obj.canonical_url = '{0}/{1}'.format(new_canonical_domain, path) logger.info(u'Done.') self.status = u'Update complete; {0} items processed.'.format(total)
def migrate_settings_to_registry(setup_tool): """Migrate settings to registry.""" profile = 'profile-{0}:default'.format(PROJECTNAME) setup_tool.runImportStepFromProfile(profile, 'plone.app.registry') portal_properties = api.portal.get_tool(name='portal_properties') if 'sc_social_likes_properties' in portal_properties: old_props = portal_properties.sc_social_likes_properties registry = getUtility(IRegistry) settings = registry.forInterface(ISocialLikeSettings) # ignore types not allowed types_ = _enforce_type_constraints(old_props.enabled_portal_types) settings.enabled_portal_types = types_ settings.plugins_enabled = old_props.plugins_enabled settings.typebutton = old_props.typebutton settings.do_not_track = old_props.do_not_track # this property may have an invalid value under certain circunstances try: settings.fbaction = old_props.fbaction except ConstraintNotSatisfied: # empty string settings.fbaction = u'like' # use default settings.fbbuttons = old_props.fbbuttons # these fields are no longer TextLine but ASCIILine # we need to avoid 'None' values caused by missing defaults settings.facebook_username = (str(old_props.fbadmins) if old_props.fbadmins else '') settings.facebook_app_id = (str(old_props.fbapp_id) if old_props.fbapp_id else '') settings.twitter_username = (str(old_props.twittvia) if old_props.twittvia else '') logger.info('Settings migrated') del portal_properties['sc_social_likes_properties'] logger.info('Property sheet "sc_social_likes_properties" removed')
def update_configlet_information(setup_tool): """Update configlet information.""" controlpanel = api.portal.get_tool(name='portal_controlpanel') configlet = controlpanel.getActionObject('Products/sociallikes') if configlet is not None: configlet.setActionExpression( 'string:${portal_url}/@@sociallike-settings') logger.info('Configlet information updated')
def remove_actionicons(context): """ Remove registration from deprecated actionicons tool""" portal_actionicons = getToolByName(context, 'portal_actionicons') try: portal_actionicons.removeActionIcon('controlpanel', 'sociallikes') logger.info('Removed deprecated registration on portal_actionicons tool') except KeyError: pass
def remove_actionicons(context): """ Remove registration from deprecated actionicons tool""" portal_actionicons = getToolByName(context, 'portal_actionicons') try: portal_actionicons.removeActionIcon('controlpanel', 'sociallikes') logger.info( 'Removed deprecated registration on portal_actionicons tool') except KeyError: pass
def update_plugins(context): """ Apply upgrade profile """ pp = getToolByName(context, 'portal_properties') sheet = getattr(pp, 'sc_social_likes_properties', None) plugins_enabled = [] if sheet.twitter_enabled: plugins_enabled.append('Twitter') if sheet.fb_enabled: plugins_enabled.append('Facebook') if sheet.gp_enabled: plugins_enabled.append('Google+') sheet.manage_changeProperties(plugins_enabled=plugins_enabled) logger.info('Update enabled plugins list')
def enable_social_media_behavior(context): """Enable Social Media behavior.""" enabled_portal_types = api.portal.get_registry_record( name='enabled_portal_types', interface=ISocialLikeSettings) types = [] for t in enabled_portal_types: fti = queryUtility(IDexterityFTI, name=t) if fti is None: continue # not a Dexterity-based content type behaviors = list(fti.behaviors) if ISocialMedia.__identifier__ in behaviors: continue # nothing to do behaviors.append(ISocialMedia.__identifier__) fti.behaviors = tuple(behaviors) types.append(t) if types: types = ', '.join(types) msg = 'Social Media behavior was enabled for the following content types: {0}'.format(types) logger.info(msg)
def assign_canonical_url(obj, event): """Assing canonical URL to the object after it is published.""" if event.status['review_state'] not in ('published', ): # don't a assign a canonical URL as this is not a public state return record = ISocialLikeSettings.__identifier__ + '.canonical_domain' try: canonical_domain = api.portal.get_registry_record(record) except api.exc.InvalidParameterError: # package is not installed or record deleted; do nothing return # we can't assign a canonical URL without a canonical domain if canonical_domain: # FIXME: we're currently ignoring the Plone site id # https://github.com/collective/sc.social.like/issues/119 path = '/'.join(obj.getPhysicalPath()[2:]) obj.canonical_url = '{0}/{1}'.format(canonical_domain, path) logger.info('canonical_url set for {0}'.format(obj.canonical_url)) else: logger.warn( 'Canonical domain not set in Social Media configlet; ' "Facebook's Open Graph canonical URL (og:orl) will not be available")
def enable_social_media_behavior(context): """Enable Social Media behavior.""" enabled_portal_types = api.portal.get_registry_record( name='enabled_portal_types', interface=ISocialLikeSettings) types = [] for t in enabled_portal_types: fti = queryUtility(IDexterityFTI, name=t) if fti is None: continue # not a Dexterity-based content type behaviors = list(fti.behaviors) if ISocialMedia.__identifier__ in behaviors: continue # nothing to do behaviors.append(ISocialMedia.__identifier__) fti.behaviors = tuple(behaviors) types.append(t) if types: types = ', '.join(types) msg = 'Social Media behavior was enabled for the following content types: {0}'.format( types) logger.info(msg)
def reindex_catalog(setup_tool): """Reindex objects to fix interfaces on the catalog.""" test = 'test' in setup_tool.REQUEST # used to ignore transactions on tests logger.info( u'Reindexing the catalog. ' u'This process could take a long time on large sites. Be patient.') catalog = api.portal.get_tool('portal_catalog') results = catalog() logger.info(u'Found {0} objects'.format(len(results))) n = 0 for obj in get_valid_objects(results): catalog.catalog_object(obj, idxs=['object_provides'], update_metadata=False) n += 1 if n % 1000 == 0 and not test: transaction.commit() logger.info('{0} items processed.'.format(n)) if not test: transaction.commit() logger.info('Done.')
def register_cover_tiles(setup_tool): """Register collective.cover tiles.""" registered = api.portal.get_registry_record("plone.app.tiles") [registered.append(t) for t in TILES if t not in registered] assert set(registered) & set(TILES) == set(TILES) logger.info("Tiles for collective.cover were registered")
def cook_css_registry(context): """ Apply upgrade profile """ css_registry = getToolByName(context, 'portal_css') css_registry.cookResources() logger.info('CSS registry refreshed')
def migrate_settings_to_registry(setup_tool): # noqa: C901 """Migrate settings to registry.""" def filter_types(portal_types): """Return ReallyUserFriendlyTypes only. Old field values were not restricted in any way; new field values must be terms of this vocabulary. """ from zope.component import getUtility from zope.schema.interfaces import IVocabularyFactory name = 'plone.app.vocabularies.ReallyUserFriendlyTypes' friendly_types = getUtility(IVocabularyFactory, name)(None) return tuple([i for i in portal_types if i in friendly_types]) def safe_migrate(old_attr, new_attr=None, to_string=False): """Copy value for property sheet to registry record avoiding AttributeError; rename record if needed. In case of error the record must already have loaded its default value. """ try: value = getattr(old_props, old_attr) except AttributeError: pass if to_string: # convert unicode to string? # avoid 'None' on None values (missing value) value = str(value) if value else '' if new_attr is None: new_attr = old_attr setattr(settings, new_attr, value) profile = 'profile-{0}:default'.format(PROJECTNAME) setup_tool.runImportStepFromProfile(profile, 'plone.app.registry') portal_properties = api.portal.get_tool(name='portal_properties') if 'sc_social_likes_properties' not in portal_properties: logger.warn('Property sheet not found; using defaults') return old_props = portal_properties.sc_social_likes_properties registry = getUtility(IRegistry) settings = registry.forInterface(ISocialLikeSettings) # ignore types not allowed try: portal_types = old_props.enabled_portal_types except AttributeError: pass else: portal_types = filter_types(portal_types) settings.enabled_portal_types = portal_types safe_migrate('plugins_enabled') safe_migrate('typebutton') safe_migrate('do_not_track') try: safe_migrate('fbaction') except ConstraintNotSatisfied: pass # skip on empty string safe_migrate('fbbuttons') # these fields are no longer TextLine but ASCIILine # and have a different name safe_migrate('fbadmins', 'facebook_username', to_string=True) safe_migrate('fbapp_id', 'facebook_app_id', to_string=True) safe_migrate('twittvia', 'twitter_username', to_string=True) logger.info('Settings migrated') del portal_properties['sc_social_likes_properties'] logger.info('Property sheet removed')
def apply_profile(context): """ Apply upgrade profile """ profile = 'profile-sc.social.like.upgrades.v3010:default' loadMigrationProfile(context, profile) logger.info('Applied upgrade profile to version 3010')
def cook_javascript_resources(context): # pragma: no cover """Cook javascript resources.""" js_tool = api.portal.get_tool('portal_javascripts') js_tool.cookResources() logger.info('Javascript resources were cooked')
def cook_css_resources(context): # pragma: no cover """Cook css resources.""" css_tool = api.portal.get_tool('portal_css') css_tool.cookResources() logger.info('CSS resources were cooked')
def add_privacy_setting(context): """ Apply upgrade profile """ profile = 'profile-sc.social.like.upgrades.v3020:default' loadMigrationProfile(context, profile) logger.info('Applied upgrade profile to version 3020')
def add_privacy_setting(context): """ Apply upgrade profile """ profile = "profile-sc.social.like.upgrades.v3020:default" loadMigrationProfile(context, profile) logger.info("Applied upgrade profile to version 3020")
def move_mobile_detection_client_side(setup_tool): """Fix caching issues with WhatsApp button.""" profile = 'profile-{0}:default'.format(PROJECTNAME) setup_tool.runImportStepFromProfile(profile, 'jsregistry') logger.info('Client-side mobile detection now in place.')
def cook_javascript_resources(context): """Cook javascript resources.""" js_tool = api.portal.get_tool('portal_javascripts') js_tool.cookResources() logger.info('Javascript resources were cooked')
def migrate_settings_to_registry(setup_tool): # noqa: C901 """Migrate settings to registry.""" def filter_types(portal_types): """Return ReallyUserFriendlyTypes only. Old field values were not restricted in any way; new field values must be terms of this vocabulary. """ from zope.component import getUtility from zope.schema.interfaces import IVocabularyFactory name = 'plone.app.vocabularies.ReallyUserFriendlyTypes' friendly_types = getUtility(IVocabularyFactory, name)(None) return tuple([i for i in portal_types if i in friendly_types]) def safe_migrate(old_attr, new_attr=None, to_string=False): """Copy value for property sheet to registry record avoiding AttributeError; rename record if needed. In case of error the record must already have loaded its default value. """ try: value = getattr(old_props, old_attr) except AttributeError: pass if to_string: # convert unicode to string? # avoid 'None' on None values (missing value) value = str(value) if value else '' if new_attr is None: new_attr = old_attr setattr(settings, new_attr, value) profile = 'profile-{0}:default'.format(PROJECTNAME) setup_tool.runImportStepFromProfile(profile, 'plone.app.registry') portal_properties = api.portal.get_tool(name='portal_properties') if 'sc_social_likes_properties' not in portal_properties: logger.warning('Property sheet not found; using defaults') return old_props = portal_properties.sc_social_likes_properties registry = getUtility(IRegistry) settings = registry.forInterface(ISocialLikeSettings) # ignore types not allowed try: portal_types = old_props.enabled_portal_types except AttributeError: pass else: portal_types = filter_types(portal_types) settings.enabled_portal_types = portal_types safe_migrate('plugins_enabled') safe_migrate('typebutton') safe_migrate('do_not_track') try: safe_migrate('fbaction') except ConstraintNotSatisfied: pass # skip on empty string safe_migrate('fbbuttons') # these fields are no longer TextLine but ASCIILine # and have a different name safe_migrate('fbadmins', 'facebook_username', to_string=True) safe_migrate('fbapp_id', 'facebook_app_id', to_string=True) safe_migrate('twittvia', 'twitter_username', to_string=True) logger.info('Settings migrated') del portal_properties['sc_social_likes_properties'] logger.info('Property sheet removed')
def cook_css_resources(context): """Cook css resources.""" css_tool = api.portal.get_tool('portal_css') css_tool.cookResources() logger.info('CSS resources were cooked')