Skip to content

Commit

Permalink
switch signup buttons to use oauth-dropins's button_html()
Browse files Browse the repository at this point in the history
and other misc template HTML refactorings. lets us drop a lot of template stuff!
  • Loading branch information
snarfed committed Oct 28, 2019
1 parent bda43af commit 7ef5d09
Show file tree
Hide file tree
Showing 46 changed files with 148 additions and 310 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -147,10 +147,10 @@ You'll eventually want to send them to @snarfed and @kylewm too, but no hurry.
[`handlers.py`](https://github.com/snarfed/bridgy/blob/master/handlers.py)
(just import the module).
1. Add a 48x48 PNG icon to [`static/`](https://github.com/snarfed/bridgy/tree/master/static).
1. Add new `SILO_signup.html` and `SILO_user.html` files in
1. Add a new `SILO_user.html` file in
[`templates/`](https://github.com/snarfed/bridgy/tree/master/templates)
and add the silo to
[`listen_signup.html`](https://github.com/snarfed/bridgy/blob/master/templates/listen_signup.html).
[`index.html`](https://github.com/snarfed/bridgy/blob/master/templates/index.html).
Follow the existing examples.
1. Add the silo to
[`about.html`](https://github.com/snarfed/bridgy/blob/master/templates/about.html) and this README.
Expand Down
46 changes: 17 additions & 29 deletions app.py
Expand Up @@ -21,6 +21,7 @@
flickr as oauth_flickr,
github as oauth_github,
indieauth,
instagram as oauth_instagram,
medium as oauth_medium,
mastodon as oauth_mastodon,
tumblr as oauth_tumblr,
Expand Down Expand Up @@ -133,8 +134,9 @@ def kind_count(kind):
for property in ('sent', 'unsent', 'error', 'failed', 'skipped')}

vars = super(FrontPageHandler, self).template_vars()
vars['sources'] = models.sources

# add comma separator between thousands
# stats; add comma separator between thousands
vars.update({k: '{:,}'.format(v) for k, v in {
'users': num_users,
'responses': kind_count('Response'),
Expand Down Expand Up @@ -224,11 +226,12 @@ def headers(self):
def template_vars(self):
vars = super(UserHandler, self).template_vars()
vars.update({
'source': self.source,
'EPOCH': util.EPOCH,
'REFETCH_HFEED_TRIGGER': models.REFETCH_HFEED_TRIGGER,
'RECENT_PRIVATE_POSTS_THRESHOLD': RECENT_PRIVATE_POSTS_THRESHOLD,
})
'source': self.source,
'sources': models.sources,
'EPOCH': util.EPOCH,
'REFETCH_HFEED_TRIGGER': models.REFETCH_HFEED_TRIGGER,
'RECENT_PRIVATE_POSTS_THRESHOLD': RECENT_PRIVATE_POSTS_THRESHOLD,
})
if not self.source:
return vars

Expand All @@ -239,9 +242,6 @@ def template_vars(self):
else self.source.domain_urls[0] if self.source.domain_urls
else None)

kind = self.source.key.kind()
vars['publish_signup_ok'] = kind not in ('Instagram', 'GooglePlus')

# Blog webmention promos
if 'webmention' not in self.source.features:
if self.source.SHORT_NAME in ('blogger', 'medium', 'tumblr', 'wordpress'):
Expand Down Expand Up @@ -439,23 +439,9 @@ def template_file(self):


class DeleteStartHandler(util.Handler):
OAUTH_MODULES = {
'Blogger': oauth_blogger_v2,
'FacebookPage': oauth_facebook,
'Flickr': oauth_flickr,
'GitHub': oauth_github,
'Instagram': indieauth,
# use our StartHandler subclass since it has config constants
'Mastodon': mastodon,
'Medium': oauth_medium,
'Tumblr': oauth_tumblr,
'Twitter': oauth_twitter,
'WordPress': oauth_wordpress_rest,
}

def post(self):
source = self.load_source(param='key')
module = self.OAUTH_MODULES[source.key.kind()]
kind = source.key.kind()
feature = util.get_required_param(self, 'feature')
state = util.encode_oauth_state({
'operation': 'delete',
Expand All @@ -465,17 +451,19 @@ def post(self):
})

# Blogger don't support redirect_url() yet
if module is oauth_blogger_v2:
if kind == 'Blogger':
return self.redirect('/blogger/delete/start?state=%s' % state)

path = ('/instagram/callback' if module is indieauth
else '/wordpress/add' if module is oauth_wordpress_rest
path = ('/instagram/callback' if kind == 'Instagram'
else '/wordpress/add' if kind == 'WordPress'
else '/%s/delete/finish' % source.SHORT_NAME)
kwargs = {}
if module is oauth_twitter:
if kind == 'Twitter':
kwargs['access_type'] = 'read' if feature == 'listen' else 'write'

handler = module.StartHandler.to(path, **kwargs)(self.request, self.response)
start_handler = (indieauth.StartHandler if kind == 'Instagram'
else source.OAUTH_START_HANDLER)
handler = start_handler.to(path, **kwargs)(self.request, self.response)
try:
self.redirect(handler.redirect_url(state=state))
except Exception as e:
Expand Down
1 change: 1 addition & 0 deletions blogger.py
Expand Up @@ -56,6 +56,7 @@ class Blogger(models.Source):
The key name is the blog id.
"""
GR_CLASS = collections.namedtuple('FakeGrClass', ('NAME',))(NAME='Blogger')
OAUTH_START_HANDLER = oauth_blogger.StartHandler
SHORT_NAME = 'blogger'
PATH_BLACKLIST = (re.compile('^/search/.*'),)

Expand Down
4 changes: 2 additions & 2 deletions docs/index.rst
Expand Up @@ -177,10 +177,10 @@ it. It looks like a lot, but it’s not that bad, honest.
(just import the module).
3. Add a 48x48 PNG icon to
`static/ <https://github.com/snarfed/bridgy/tree/master/static>`__.
4. Add new ``SILO_signup.html`` and ``SILO_user.html`` files in
4. Add a new ``SILO_user.html`` file in
`templates/ <https://github.com/snarfed/bridgy/tree/master/templates>`__
and add the silo to
`listen_signup.html <https://github.com/snarfed/bridgy/blob/master/templates/listen_signup.html>`__.
`index.html <https://github.com/snarfed/bridgy/blob/master/templates/index.html>`__.
Follow the existing examples.
5. Add the silo to
`about.html <https://github.com/snarfed/bridgy/blob/master/templates/about.html>`__
Expand Down
2 changes: 1 addition & 1 deletion facebook.py
Expand Up @@ -85,8 +85,8 @@ class FacebookPage(models.Source):
The key name is the Facebook id.
"""

GR_CLASS = gr_facebook.Facebook
OAUTH_START_HANDLER = oauth_facebook.StartHandler
SHORT_NAME = 'facebook'

URL_CANONICALIZER = util.UrlCanonicalizer(
Expand Down
4 changes: 2 additions & 2 deletions flickr.py
Expand Up @@ -23,11 +23,11 @@ class Flickr(models.Source):
# Fetching comments and likes is extremely request-intensive, so let's dial
# back the frequency for now.
FAST_POLL = datetime.timedelta(minutes=60)

GR_CLASS = gr_flickr.Flickr
OAUTH_START_HANDLER = oauth_flickr.StartHandler
SHORT_NAME = 'flickr'
TRANSIENT_ERROR_HTTP_CODES = ('400',)

CAN_PUBLISH = True
URL_CANONICALIZER = util.UrlCanonicalizer(
domain=GR_CLASS.DOMAIN,
approve=r'https://www\.flickr\.com/(photos|people)/[^/?]+/([^/?]+/)?$',
Expand Down
3 changes: 2 additions & 1 deletion github.py
Expand Up @@ -42,14 +42,15 @@ class GitHub(Source):
The key name is the GitHub username.
"""
GR_CLASS = gr_github.GitHub
OAUTH_START_HANDLER = oauth_github.StartHandler
SHORT_NAME = 'github'
TYPE_LABELS = {
'post': 'issue',
'like': 'star',
}
BACKFEED_REQUIRES_SYNDICATION_LINK = True
DISABLE_HTTP_CODES = Source.DISABLE_HTTP_CODES + ('403',)

CAN_PUBLISH = True
# WARNING: see docstring
URL_CANONICALIZER = util.UrlCanonicalizer(
domain=GR_CLASS.DOMAIN,
Expand Down
11 changes: 6 additions & 5 deletions instagram.py
Expand Up @@ -46,14 +46,14 @@ class Instagram(Source):
insensitive), numbers, periods, and underscores:
https://stackoverflow.com/questions/15470180
"""

GR_CLASS = gr_instagram.Instagram
OAUTH_START_HANDLER = oauth_instagram.StartHandler
SHORT_NAME = 'instagram'
FAST_POLL = datetime.timedelta(minutes=120)
RATE_LIMITED_POLL = Source.SLOW_POLL
RATE_LIMIT_HTTP_CODES = ('401', '429', '503')
DISABLE_HTTP_CODES = ()

CAN_PUBLISH = False
URL_CANONICALIZER = util.UrlCanonicalizer(
domain=GR_CLASS.DOMAIN,
subdomain='www',
Expand All @@ -78,14 +78,11 @@ def new(handler, auth_entity=None, actor=None, **kwargs):
username = actor['username']
if not kwargs.get('features'):
kwargs['features'] = ['listen']
urls = microformats2.object_urls(actor)
return Instagram(id=username,
auth_entity=auth_entity.key,
name=actor.get('displayName'),
picture=actor.get('image', {}).get('url'),
url=gr_instagram.Instagram.user_url(username),
domain_urls=urls,
domains=[util.domain_from_link(url) for url in urls],
**kwargs)

def silo_url(self):
Expand All @@ -102,6 +99,10 @@ def label_name(self):
"""Returns the username."""
return self.key.id()

@classmethod
def button_html(cls, feature, **kwargs):
return super(cls, cls).button_html(feature, form_method='get', **kwargs)

def get_activities_response(self, *args, **kwargs):
"""Set user_id because scraping requires it."""
kwargs.setdefault('group_id', gr_source.SELF)
Expand Down
42 changes: 29 additions & 13 deletions mastodon.py
Expand Up @@ -19,13 +19,28 @@
PUBLISH_SCOPES = ('read', 'write')


class StartHandler(oauth_mastodon.StartHandler):
"""Abstract base OAuth starter class with our redirect URLs."""
APP_NAME = 'Bridgy'
APP_URL = (util.HOST_URL if appengine_config.HOST in util.OTHER_DOMAINS
else appengine_config.HOST_URL)
REDIRECT_PATHS = (
'/mastodon/callback',
'/publish/mastodon/finish',
'/mastodon/delete/finish',
'/delete/finish',
)


class Mastodon(models.Source):
"""A Mastodon account.
The key name is the fully qualified address, eg 'snarfed@mastodon.technology'.
"""
GR_CLASS = gr_mastodon.Mastodon
OAUTH_START_HANDLER = StartHandler
SHORT_NAME = 'mastodon'
CAN_PUBLISH = True
TYPE_LABELS = {
'post': 'toot',
'comment': 'reply',
Expand Down Expand Up @@ -82,6 +97,20 @@ def label_name(self):
"""Returns the username."""
return self.key.id()

@classmethod
def button_html(cls, feature, **kwargs):
"""Override oauth-dropins's button_html() to not show the instance text box."""
source = kwargs.get('source')
instance = source.instance() if source else ''
return """\
<form method="%s" action="/mastodon/start">
<input type="image" class="mastodon-button shadow" alt="Sign in with Mastodon"
src="/oauth_dropins/static/mastodon_large.png" />
<input name="feature" type="hidden" value="%s" />
<input name="instance" type="hidden" value="%s" />
</form>
""" % ('post' if instance else 'get', feature, instance)

def is_private(self):
"""Returns True if this Mastodon account is protected.
Expand All @@ -105,19 +134,6 @@ def search_for_links(self):
fetch_likes=False, fetch_shares=False)


class StartHandler(oauth_mastodon.StartHandler):
"""Abstract base OAuth starter class with our redirect URLs."""
APP_NAME = 'Bridgy'
APP_URL = (util.HOST_URL if appengine_config.HOST in util.OTHER_DOMAINS
else appengine_config.HOST_URL)
REDIRECT_PATHS = (
'/mastodon/callback',
'/publish/mastodon/finish',
'/mastodon/delete/finish',
'/delete/finish',
)


class InstanceHandler(TemplateHandler, util.Handler):
"""Serves the "Enter your instance" form page."""
def template_file(self):
Expand Down
1 change: 1 addition & 0 deletions medium.py
Expand Up @@ -34,6 +34,7 @@ class Medium(models.Source):
The key name is the username (with @ prefix) or publication name.
"""
GR_CLASS = collections.namedtuple('FakeGrClass', ('NAME',))(NAME='Medium')
OAUTH_START_HANDLER = oauth_medium.StartHandler
SHORT_NAME = 'medium'


Expand Down
36 changes: 33 additions & 3 deletions models.py
Expand Up @@ -86,7 +86,10 @@ class Source(with_metaclass(SourceMeta, StringIdModel)):
SHORT_NAME = None
# the corresponding granary class
GR_CLASS = None

# oauth-dropins StartHandler class
OAUTH_START_HANDLER = None
# whether Bridgy supports publish for this silo
CAN_PUBLISH = None
# how often to poll for responses
FAST_POLL = datetime.timedelta(minutes=30)
# how often to poll sources that have never sent a webmention
Expand Down Expand Up @@ -436,6 +439,30 @@ def edit_template_url(self):
"""
raise NotImplementedError()

@classmethod
def button_html(cls, feature, **kwargs):
"""Returns an HTML string with a login form and button for this site.
Mostly just passes through to
:meth:`oauth_dropins.handlers.StartHandler.button_html`.
Returns: string, HTML
"""
assert feature in cls.FEATURES
form_extra = (kwargs.pop('form_extra', '') +
'<input name="feature" type="hidden" value="%s" />' % feature)

source = kwargs.pop('source', None)
if source:
form_extra += ('\n<input name="id" type="hidden" value="%s" />' %
source.key.id())

return cls.OAUTH_START_HANDLER.button_html(
'/%s/start' % cls.SHORT_NAME,
form_extra=form_extra,
image_prefix='/oauth_dropins/static/',
**kwargs)

@classmethod
def create_new(cls, handler, user_url=None, **kwargs):
"""Creates and saves a new :class:`Source` and adds a poll task for it.
Expand Down Expand Up @@ -571,8 +598,11 @@ def _urls_and_domains(self, auth_entity, user_url):
Returns:
([string url, ...], [string domain, ...])
"""
actor = self.gr_source.user_to_actor(json_loads(auth_entity.user_json))
logging.debug('Converted to actor: %s', json_dumps(actor, indent=2))
user = json_loads(auth_entity.user_json)
actor = (user.get('actor') # for Instagram; its user_json is IndieAuth
or self.gr_source.user_to_actor(user))
logging.debug('Extracting URLs and domains from actor: %s',
json_dumps(actor, indent=2))

candidates = util.trim_nulls(util.uniquify(
[user_url] + microformats2.object_urls(actor)))
Expand Down
4 changes: 2 additions & 2 deletions static/style.css
Expand Up @@ -340,7 +340,7 @@ iframe#twitter-widget-0 {
text-align: right;
}

.shadow, .profile, form.signup input, #screenshot {
.shadow, .profile, #screenshot {
-moz-box-shadow: 2px 2px 4px rgba(0,0,0,0.4);
-webkit-box-shadow: 2px 2px 4px rgba(0,0,0,0.4);
/* CSS3 */
Expand All @@ -351,7 +351,7 @@ iframe#twitter-widget-0 {
filter: progid:DXImageTransform.Microsoft.Shadow(Strength=2,Direction=135,Color='#666666');
}

.shadow:hover, .profile:hover, form.signup input:hover, #screenshot:hover {
.shadow:hover, .profile:hover, #screenshot:hover {
-moz-box-shadow: 2px 2px 4px rgba(0,0,0,0.8);
-webkit-box-shadow: 2px 2px 4px rgba(0,0,0,0.8);
/* CSS3 */
Expand Down
8 changes: 3 additions & 5 deletions templates/blog_user.html
Expand Up @@ -22,9 +22,8 @@
{% if source.has_bridgy_webmention_endpoint() %}
<p class="big">You're all set! Check out your recent
<a href="#blogposts">blog posts</a> and <a href="#webmentions">webmentions</a>.
<!-- This currently 500s :/ -->
<!-- <a href="http://indiewebify.me/send-webmentions/?url={{ source.domain_url }}"> -->
<!-- Try a webmention!</a> -->
<a href="http://indiewebify.me/send-webmentions/?url={{ source.domain_urls[0] }}">
Try a webmention!</a>
</p>
{% else %}
<p class="warning">Warning: your blog's current
Expand Down Expand Up @@ -52,8 +51,7 @@
{% else %}
<div class="row big">
Click to re-enable:
{% block blog_signup %}
{% endblock %}
{{ source.button_html('webmention')|safe }}
</div>
{% endif %}

Expand Down
5 changes: 0 additions & 5 deletions templates/blogger_signup.html

This file was deleted.

0 comments on commit 7ef5d09

Please sign in to comment.