Example #1
0
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')
Example #2
0
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())))
Example #3
0
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"
        )
Example #4
0
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")
Example #5
0
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')
Example #6
0
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())))
Example #7
0
    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)
Example #9
0
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')
Example #10
0
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')
Example #11
0
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')
Example #12
0
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
Example #13
0
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
Example #14
0
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')
Example #15
0
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)
Example #16
0
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")
Example #17
0
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)
Example #18
0
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.')
Example #19
0
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.')
Example #20
0
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")
Example #21
0
def cook_css_registry(context):
    """ Apply upgrade profile """
    css_registry = getToolByName(context, 'portal_css')
    css_registry.cookResources()
    logger.info('CSS registry refreshed')
Example #22
0
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')
Example #23
0
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')
Example #24
0
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')
Example #25
0
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')
Example #26
0
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')
Example #27
0
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")
Example #28
0
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')
Example #29
0
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.')
Example #30
0
def cook_css_registry(context):
    """ Apply upgrade profile """
    css_registry = getToolByName(context, 'portal_css')
    css_registry.cookResources()
    logger.info('CSS registry refreshed')
Example #31
0
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')
Example #32
0
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')
Example #33
0
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')