diff --git a/woodwind/models.py b/woodwind/models.py
index 297264b..d7ef6bc 100644
--- a/woodwind/models.py
+++ b/woodwind/models.py
@@ -1,4 +1,5 @@
import bleach
+import json
from .extensions import db
@@ -12,6 +13,24 @@ bleach.ALLOWED_ATTRIBUTES.update({
})
+class JsonType(db.TypeDecorator):
+ """Represents an immutable structure as a json-encoded string.
+ http://docs.sqlalchemy.org/en/rel_0_9/core/types.html#marshal-json-strings
+ """
+ impl = db.Text
+
+ def process_bind_param(self, value, dialect):
+ if value is not None:
+ value = json.dumps(value)
+
+ return value
+
+ def process_result_value(self, value, dialect):
+ if value is not None:
+ value = json.loads(value)
+ return value
+
+
users_to_feeds = db.Table(
'users_to_feeds', db.Model.metadata,
db.Column('user_id', db.Integer, db.ForeignKey('user.id'), index=True),
@@ -24,6 +43,7 @@ class User(db.Model):
domain = db.Column(db.String(256))
micropub_endpoint = db.Column(db.String(512))
access_token = db.Column(db.String(512))
+ settings = db.Column(JsonType)
# Flask-Login integration
def is_authenticated(self):
diff --git a/woodwind/static/indieconfig.js b/woodwind/static/indieconfig.js
new file mode 100644
index 0000000..dedce86
--- /dev/null
+++ b/woodwind/static/indieconfig.js
@@ -0,0 +1,70 @@
+/*jslint browser: true, plusplus: true, vars: true, indent: 2 */
+window.loadIndieConfig = (function () {
+ 'use strict';
+
+ // Indie-Config Loading script
+ // by Pelle Wessman, voxpelli.com
+ // MIT-licensed
+ // http://indiewebcamp.com/indie-config
+
+ var config, configFrame, configTimeout,
+ callbacks = [],
+ handleConfig, parseConfig;
+
+ // When the configuration has been loaded – deregister all loading mechanics and call all callbacks
+ handleConfig = function () {
+ config = config || {};
+
+ configFrame.parentNode.removeChild(configFrame);
+ configFrame = undefined;
+
+ window.removeEventListener('message', parseConfig);
+
+ clearTimeout(configTimeout);
+
+ while (callbacks[0]) {
+ callbacks.shift()(config);
+ }
+ };
+
+ // When we receive a message, check if the source is right and try to parse it
+ parseConfig = function (message) {
+ var correctSource = (configFrame && message.source === configFrame.contentWindow);
+
+ if (correctSource && config === undefined) {
+ try {
+ config = JSON.parse(message.data);
+ } catch (ignore) {}
+
+ handleConfig();
+ }
+ };
+
+ return function (callback) {
+ // If the config is already loaded, call callback right away
+ if (config) {
+ callback(config);
+ return;
+ }
+
+ // Otherwise add the callback to the queue
+ callbacks.push(callback);
+
+ // Are we already trying to load the Indie-Config, then wait
+ if (configFrame) {
+ return;
+ }
+
+ // Create the iframe that will load the Indie-Config
+ configFrame = document.createElement('iframe');
+ configFrame.src = 'web+action:load';
+ document.getElementsByTagName('body')[0].appendChild(configFrame);
+ configFrame.style.display = 'none';
+
+ // Listen for messages so we will catch the Indie-Config message
+ window.addEventListener('message', parseConfig);
+
+ // And if no such Indie-Config message has been loaded in a while, abort the loading
+ configTimeout = setTimeout(handleConfig, 3000);
+ };
+}());
diff --git a/woodwind/static/style.css b/woodwind/static/style.css
index 616b108..8e24e34 100644
--- a/woodwind/static/style.css
+++ b/woodwind/static/style.css
@@ -415,11 +415,32 @@ article {
label {
font-weight: bold;
- display: block; }
+ display: inline-block;
+ margin-bottom: 5px;
+ max-width: 100%; }
textarea, input[type="text"], input[type="url"] {
width: 100%;
margin: 0.25em 0; }
+ textarea.input-25, input[type="text"].input-25, input[type="url"].input-25 {
+ width: 23%; }
+ textarea.input-50, input[type="text"].input-50, input[type="url"].input-50 {
+ width: 48%; }
+ textarea.input-75, input[type="text"].input-75, input[type="url"].input-75 {
+ width: 73%; }
+
+.reply-area {
+ text-align: center; }
+ .reply-area a {
+ display: inline-block;
+ padding: 0.2em;
+ border: 1px solid #687D77;
+ border-radius: 4px;
+ background-color: #ECEBF0;
+ text-decoration: none;
+ color: #484A47;
+ min-width: 50px;
+ text-align: center; }
button {
padding: 0.25em; }
diff --git a/woodwind/static/style.css.map b/woodwind/static/style.css.map
index 2c84d0c..b0f74fb 100644
--- a/woodwind/static/style.css.map
+++ b/woodwind/static/style.css.map
@@ -1,6 +1,6 @@
{
"version": 3,
-"mappings": "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",
+"mappings": "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",
"sources": ["normalize.scss","style.scss"],
"names": [],
"file": "style.css"
diff --git a/woodwind/static/style.scss b/woodwind/static/style.scss
index ff1894c..87fb9bc 100644
--- a/woodwind/static/style.scss
+++ b/woodwind/static/style.scss
@@ -92,12 +92,39 @@ article {
label {
font-weight: bold;
- display: block;
+ display: inline-block;
+ margin-bottom: 5px;
+ max-width: 100%;
}
textarea, input[type="text"], input[type="url"] {
width: 100%;
- margin: 0.25em 0 ;
+ margin: 0.25em 0;
+
+ &.input-25 {
+ width: 23%;
+ }
+ &.input-50 {
+ width: 48%;
+ }
+ &.input-75 {
+ width: 73%;
+ }
+}
+
+.reply-area {
+ text-align: center;
+ a {
+ display: inline-block;
+ padding: 0.2em;
+ border: 1px solid $sirocco;
+ border-radius: 4px;
+ background-color: $athens-gray;
+ text-decoration: none;
+ color: $lunar-green;
+ min-width: 50px;
+ text-align: center;
+ }
}
button {
diff --git a/woodwind/static/webaction.js b/woodwind/static/webaction.js
new file mode 100644
index 0000000..262ad91
--- /dev/null
+++ b/woodwind/static/webaction.js
@@ -0,0 +1,63 @@
+/*jslint browser: true, plusplus: true, vars: true, indent: 2 */
+(function () {
+ 'use strict';
+
+ var loadingClassRegexp = /(^|\s)indieconfig-loading(\s|$)/;
+
+ var doTheAction = function (indieConfig) {
+ var href, action, anchors;
+
+ // Don't block the tag anymore as the queued action is now handled
+ this.className = this.className.replace(loadingClassRegexp, ' ');
+
+ // Pick the correct endpoint for the correct action
+ action = this.getAttribute('do');
+ href = indieConfig[action];
+
+ // If no endpoint is found, try the URL of the first a-tag within it
+ if (!href) {
+ anchors = this.getElementsByTagName('a');
+ if (anchors[0]) {
+ href = anchors[0].href;
+ }
+ }
+
+ // We have found an endpoint!
+ if (href) {
+ //Resolve a relative target
+ var target = document.createElement('a');
+ target.href = this.getAttribute('with');
+ target = target.href;
+
+ // Insert the target into the endpoint
+ href = href.replace('{url}', encodeURIComponent(target || window.location.href));
+
+ // And redirect to it
+ window.open( href, '_blank');
+ }
+ };
+
+ // Event handler for a click on an indie-action tag
+ var handleTheAction = function (e) {
+ // Prevent the default of eg. any a-tag fallback within the indie-action tag
+ e.preventDefault();
+
+ // Make sure this tag hasn't already been queued for the indieconfig-load
+ if (!loadingClassRegexp.test(this.className)) {
+ this.className += ' indieconfig-loading';
+ // Set "doTheAction" to be called when the indie-config has been loaded
+ window.loadIndieConfig(doTheAction.bind(this));
+ }
+ };
+
+ // Once the page is loased add click event listeners to all indie-action tags
+ window.addEventListener('DOMContentLoaded', function () {
+ var actions = document.querySelectorAll('indie-action'),
+ i,
+ length = actions.length;
+
+ for (i = 0; i < length; i++) {
+ actions[i].addEventListener('click', handleTheAction);
+ }
+ });
+}());
diff --git a/woodwind/templates/_reply.jinja2 b/woodwind/templates/_reply.jinja2
new file mode 100644
index 0000000..13b0fa4
--- /dev/null
+++ b/woodwind/templates/_reply.jinja2
@@ -0,0 +1,34 @@
+{% set settings = current_user.settings or {} %}
+{% set replyMethod = settings.get('reply-method') %}
+
+{% if replyMethod == 'micropub' and current_user.micropub_endpoint %}
+
+
+
+
+
+
+{% elif replyMethod == 'indie-config' %}
+ {% for action in settings.get('indie-config-actions', []) %}
+
+ {{ action | capitalize }}
+
+ {% endfor %}
+{% elif replyMethod == 'action-urls' %}
+ {% for action, url in settings.get('action-urls', []) %}
+ {{ action | capitalize }}
+ {% endfor %}
+{% endif %}
diff --git a/woodwind/templates/feed.jinja2 b/woodwind/templates/feed.jinja2
index a4014e0..fca5dbd 100644
--- a/woodwind/templates/feed.jinja2
+++ b/woodwind/templates/feed.jinja2
@@ -1,6 +1,13 @@
{% extends "base.jinja2" %}
{% block head %}
-
+
+
+ {% if current_user and current_user.settings
+ and current_user.settings.get('reply-method') == 'indie-config' %}
+
+
+ {% endif %}
+
{% endblock head %}
{% block header %}
@@ -34,28 +41,9 @@
{% endif %}
{% endfor %}
diff --git a/woodwind/templates/login.jinja2 b/woodwind/templates/login.jinja2
deleted file mode 100644
index 4e20abb..0000000
--- a/woodwind/templates/login.jinja2
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "base.jinja2" %}
-{% block body %}
-
-{% endblock %}
diff --git a/woodwind/templates/settings.jinja2 b/woodwind/templates/settings.jinja2
index 329b491..ad8704d 100644
--- a/woodwind/templates/settings.jinja2
+++ b/woodwind/templates/settings.jinja2
@@ -2,45 +2,123 @@
{% block body %}
-
+
-
-
-
- {% endif %}
+
-
+
+
+
+
Indie-Config
+
+ Select indie-config actions.
+
+ {% set selectedActions = settings.get('indie-config-actions', []) %}
-
+ {% for action in ['like', 'favorite', 'reply', 'repost', 'bookmark'] %}
+
+ {% endfor %}
+
+
+
+
+
Action URLs
+
+ {% set actionUrls = settings.get('action-urls', {}) %}
+ {% for action, url in actionUrls %}
+
+
+ {% endfor %}
+
+
+
+
+
+
+
+
{% endblock body %}
diff --git a/woodwind/views.py b/woodwind/views.py
index d7c3f05..6ac6543 100644
--- a/woodwind/views.py
+++ b/woodwind/views.py
@@ -43,10 +43,31 @@ def feeds():
return flask.render_template('feeds.jinja2', feeds=sorted_feeds)
-@views.route('/settings')
+@views.route('/settings', methods=['GET', 'POST'])
@flask_login.login_required
def settings():
- return flask.render_template('settings.jinja2')
+ settings = flask_login.current_user.settings or {}
+ if flask.request.method == 'GET':
+ return flask.render_template('settings.jinja2', settings=settings)
+
+ settings = dict(settings)
+ reply_method = flask.request.form.get('reply-method')
+ settings['reply-method'] = reply_method
+
+ if reply_method == 'micropub':
+ pass
+ elif reply_method == 'indie-config':
+ settings['indie-config-actions'] = flask.request.form.getlist(
+ 'indie-config-action')
+ elif reply_method == 'action-urls':
+ zipped = zip(
+ flask.request.form.getlist('action'),
+ flask.request.form.getlist('action-url'))
+ settings['action-urls'] = [[k, v] for k, v in zipped if k and v]
+
+ flask_login.current_user.settings = settings
+ db.session.commit()
+ return flask.render_template('settings.jinja2', settings=settings)
@views.route('/update_feed')
@@ -103,7 +124,7 @@ def login():
def login_callback(resp):
if not resp.me:
flask.flash(cgi.escape('Login error: ' + resp.error))
- return flask.redirect(flask.url_for('.login'))
+ return flask.redirect(flask.url_for('.index'))
if resp.error:
flask.flash(cgi.escape('Warning: ' + resp.error))
@@ -121,12 +142,12 @@ def login_callback(resp):
return flask.redirect(resp.next_url or flask.url_for('.index'))
-@views.route('/authorize', methods=['POST'])
+@views.route('/authorize')
@flask_login.login_required
def authorize():
return micropub.authorize(
me=flask_login.current_user.url,
- next_url=flask.request.form.get('next'),
+ next_url=flask.request.args.get('next'),
scope='post')
@@ -135,13 +156,13 @@ def authorize():
def micropub_callback(resp):
if not resp.me or resp.error:
flask.flash(cgi.escape('Authorize error: ' + resp.error))
- return flask.redirect(flask.url_for('.login'))
+ return flask.redirect(flask.url_for('.index'))
domain = urllib.parse.urlparse(resp.me).netloc
user = load_user(domain)
if not user:
flask.flash(cgi.escape('Unknown user for domain: ' + domain))
- return flask.redirect(flask.url_for('.login'))
+ return flask.redirect(flask.url_for('.index'))
user.micropub_endpoint = resp.micropub_endpoint
user.access_token = resp.access_token
@@ -149,13 +170,13 @@ def micropub_callback(resp):
return flask.redirect(resp.next_url or flask.url_for('.index'))
-@views.route('/deauthorize', methods=['POST'])
+@views.route('/deauthorize')
@flask_login.login_required
def deauthorize():
flask_login.current_user.micropub_endpoint = None
flask_login.current_user.access_token = None
db.session.commit()
- return flask.redirect(flask.request.form.get('next')
+ return flask.redirect(flask.request.args.get('next')
or flask.url_for('.index'))