Skip to content

Commit

Permalink
Remove redundant metadata from Twitter plugin as it can use Open Grap…
Browse files Browse the repository at this point in the history
…h properties as fall back. (#131)

The following changes were also implemented:

* widgets now respect users' privacy via implementation of the 'twitter:dnt' property.

* Tweet Button code was updated to use current best practices.

* plugin code was cleaned up and refactored; it must be faster now and consume less memory.

Note that 'data-lang' attribute was removed as Twitter must be able to discover the content language automatically.

More information:
* https://dev.twitter.com/web/tweet-button
* https://dev.twitter.com/web/overview/privacy
  • Loading branch information
hvelarde committed Sep 4, 2017
1 parent c9b6cd9 commit 5a46dc2
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 113 deletions.
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ There's a frood who really knows where his towel is.
2.10.2 (unreleased)
^^^^^^^^^^^^^^^^^^^

- Update Tweet Button code; code clean up and refactor.
[hvelarde]

- Twitter widgets now respect users' privacy.
[hvelarde]

- Remove redundant metadata as Twitter can use Open Graph properties as fall back (closes `#112 <https://github.com/collective/sc.social.like/issues/112>`_).
[hvelarde]

- Remove useless scale caching on the request as it seems to be causing colateral issues (closes `#109 <https://github.com/collective/sc.social.like/issues/109>`_).
[rodfersou]

Expand Down
84 changes: 32 additions & 52 deletions sc/social/like/plugins/twitter/browser.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,63 @@
# -*- coding:utf-8 -*-
"""Helper view to generate the Tweet Button widget.
More information:
* https://dev.twitter.com/web/tweet-button
* https://dev.twitter.com/web/overview/privacy
"""
from Acquisition import aq_inner
from plone import api
from plone.api.exc import InvalidParameterError
from Products.CMFPlone.utils import safe_unicode
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.PythonScripts.standard import url_quote
from sc.social.like.behaviors import ISocialMedia
from sc.social.like.config import IS_PLONE_5
from sc.social.like.interfaces import ISocialLikeSettings
from sc.social.like.utils import get_content_image
from sc.social.like.utils import get_language
from urllib import urlencode
from zope.component import getMultiAdapter


class PluginView(BrowserView):

twitter_enabled = False
language = 'en'
"""Helper view to generate the Tweet Button widget."""

metadata = ViewPageTemplateFile('templates/metadata.pt')
plugin = ViewPageTemplateFile('templates/plugin.pt')
link = ViewPageTemplateFile('templates/link.pt')

def __init__(self, context, request):
self.context = context
self.title = context.title
self.description = context.Description()
self.context = aq_inner(context)
self.request = request
# FIXME: the following could rise unexpected exceptions
# move it to a new setup() method
# see: http://docs.plone.org/develop/plone/views/browserviews.html#creating-a-view
self.portal_state = getMultiAdapter((self.context, self.request),
name=u'plone_portal_state')
self.portal = self.portal_state.portal()
self.site_url = self.portal_state.portal_url()
self.portal_title = self.portal_state.portal_title()
self.url = context.absolute_url()
self.language = get_language(context)
self.image = get_content_image(context)
self.urlnoscript = (
u'http://twitter.com/home?status=' +
url_quote(u'{0} - {1} via {2}'.format(
safe_unicode(self.context.title),
self.context.absolute_url(),
self.via)
)
)

@property
def is_plone_5(self):
return IS_PLONE_5

@property
def typebutton(self):
record = ISocialLikeSettings.__identifier__ + '.typebutton'
try:
return api.portal.get_registry_record(record)
except InvalidParameterError:
return ''
def portal_url(self):
portal = api.portal.get()
return portal.absolute_url()

def canonical_url(self):
"""Return canonical URL if available; otherwise, context URL."""
if ISocialMedia.providedBy(self.context):
return self.context.canonical_url
else:
return self.context.absolute_url()

@property
def via(self):
record = ISocialLikeSettings.__identifier__ + '.twitter_username'
try:
return api.portal.get_registry_record(record)
except InvalidParameterError:
return ''
return api.portal.get_registry_record(record, default='')

def share_link(self):
params = dict(
text=safe_unicode(self.context.Title()).encode('utf-8'),
url=self.context.absolute_url(),
)
if self.via:
params['via'] = self.via
params = {
'text': self.context.Title(),
'url': self.context.absolute_url(),
}

via = self.via()
if via:
params['via'] = via

url = 'https://twitter.com/intent/tweet?' + urlencode(params)
return url

def image_url(self):
"""Return image URL."""
return self.image.url if self.image else None
def dnt(self):
record = ISocialLikeSettings.__identifier__ + '.do_not_track'
return api.portal.get_registry_record(record, default=False)
27 changes: 13 additions & 14 deletions sc/social/like/plugins/twitter/templates/link.pt
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<tal:twitter i18n:domain="sc.social.like">
<!-- Twitter -->
<a href="https://twitter.com/intent/tweet"
class="slPrivacy"
title="Tweet it! (open in new window)"
tal:attributes="href view/share_link"
onclick="javascript:window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;"
i18n:attributes="title">
<img src="share-linkedin.png"
alt="Tweet it!"
i18n:attributes="alt"
tal:attributes="src string:${view/site_url}/++resource++sl_images/share-twitter.png" />
</a>
</tal:twitter>
<!-- Twitter -->
<a class="slPrivacy"
href="https://twitter.com/intent/tweet"
title="Tweet (opens in new window)"
tal:attributes="href view/share_link"
onclick="javascript:window.open(this.href, '', 'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=600,width=600');return false;"
i18n:domain="sc.social.like"
i18n:attributes="title">
<img src="share-twitter.png"
alt="Tweet"
i18n:attributes="alt"
tal:attributes="src string:${view/portal_url}/++resource++sl_images/share-twitter.png" />
</a>
4 changes: 1 addition & 3 deletions sc/social/like/plugins/twitter/templates/metadata.pt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
</tal:comment>
<tal:twitter condition="not:view/is_plone_5">
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" tal:condition="view/image_url" tal:attributes="content view/image_url" />
<meta name="twitter:site" tal:attributes="content string:@${view/via}" />
<meta name="twitter:title" tal:attributes="content view/title" />
<meta name="twitter:description" tal:attributes="content view/description" />
<meta tal:condition="view/dnt" name="twitter:dnt" content="on">
</tal:twitter>
21 changes: 7 additions & 14 deletions sc/social/like/plugins/twitter/templates/plugin.pt
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
<tal:twitter>
<!-- Twitter -->
<a href="https://twitter.com/share"
class="twitter-share-button"
tal:attributes="data-count view/typebutton;
data-via view/via;
data-url view/url;
data-lang view/language;
data-text here/Title;">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
<noscript>
<a tal:attributes="href view/urlnoscript">Tweet</a>
</noscript>
</tal:twitter>
<!-- Twitter -->
<a class="twitter-share-button"
href="https://twitter.com/intent/tweet"
tal:attributes="data-text here/Title;
data-url view/canonical_url;
data-via view/via">Tweet</a>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
43 changes: 13 additions & 30 deletions sc/social/like/tests/test_plugin_twitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,24 @@ def setUp(self):
def test_plugin_view_metadata(self):

def get_meta_content(name):
"""Return the content attribute of the meta tag specified by name."""
return html.find('*/meta[@name="{0}"]'.format(name)).attrib['content']

"""Return the content attribute of the meta tag specified."""
meta = html.find('*/meta[@name="{0}"]'.format(name))
if meta is not None:
return meta.attrib['content']
view = self.newsitem.restrictedTraverse(self.plugin.view())
record = ISocialLikeSettings.__identifier__ + '.twitter_username'
api.portal.set_registry_record(record, 'plone')

from lxml import etree
html = etree.HTML(view.metadata())
self.assertEqual(get_meta_content('twitter:card'), 'summary_large_image')
expected = r'http://nohost/plone/lorem-ipsum/@@images/[0-9a-f--]+.png'
self.assertRegexpMatches(get_meta_content('twitter:image'), expected)
self.assertEqual(get_meta_content('twitter:site'), '@plone')
self.assertEqual(get_meta_content('twitter:title'), 'Lorem Ipsum')
self.assertEqual(get_meta_content('twitter:description'), 'Neque Porro')

# privacy settings
self.assertIsNone(get_meta_content('twitter:dnt'))
self.settings.do_not_track = True
html = etree.HTML(view.metadata())
self.assertEqual(get_meta_content('twitter:dnt'), 'on')

def test_plugin_view_html(self):
view = self.newsitem.restrictedTraverse(self.plugin.view())
Expand All @@ -99,33 +102,13 @@ def test_privacy_plugin_view_html(self):

view = self.portal.restrictedTraverse(self.plugin.view())
html = view.link()
self.assertIn('Tweet it!', html)
self.assertIn('Tweet', html)

def test_plugin_twitter_username(self):
self.settings.twitter_username = '@simplesconsult'

view = self.newsitem.restrictedTraverse(self.plugin.view())
html = view.plugin()
self.assertIn('data-via="@simplesconsult"', html)

def test_plugin_urlnoscript_encoding(self):
self.newsitem.setTitle(u'Notícia')
self.settings.twitter_username = '@simplesconsult'

view = self.newsitem.restrictedTraverse(self.plugin.view())
html = view.plugin()
self.assertIn('%20via%20%40simplesconsult">Tweet', html)

def test_plugin_language(self):
self.newsitem.setLanguage('pt-br')
view = self.newsitem.restrictedTraverse(self.plugin.view())
html = view.plugin()
self.assertIn('data-lang="pt-br"', html)

self.newsitem.setLanguage('en')
self.settings.twitter_username = 'simplesconsult'
view = self.newsitem.restrictedTraverse(self.plugin.view())
html = view.plugin()
self.assertIn('data-lang="en"', html)
self.assertIn('data-via="simplesconsult"', html)

def test_share_link(self):
view = self.newsitem.restrictedTraverse(self.plugin.view())
Expand Down

0 comments on commit 5a46dc2

Please sign in to comment.