support excluding feeds from the front page

and some style/UI tweaks
This commit is contained in:
Kyle Mahan 2015-12-07 15:46:47 +00:00
parent 190bb1eb55
commit bca07e2178
11 changed files with 77 additions and 28 deletions

View file

@ -99,7 +99,9 @@ class Subscription(db.Model):
# user-editable name of this subscribed feed # user-editable name of this subscribed feed
name = db.Column(db.String(256)) name = db.Column(db.String(256))
tags = db.Column(db.String(256)) tags = db.Column(db.String(256))
# exclude from the front page
exclude = db.Column(db.Boolean, default=False)
user = db.relationship(User, backref='subscriptions') user = db.relationship(User, backref='subscriptions')
feed = db.relationship(Feed, backref='subscriptions') feed = db.relationship(Feed, backref='subscriptions')

View file

@ -64,15 +64,16 @@ $(function(){
function attachListeners() { function attachListeners() {
$("#older-link").off('click').click(clickOlderLink); $("#older-link").off('click').click(clickOlderLink);
$(".micropub-form button[type='submit']").off('click').click(submitMicropubForm); $(".micropub-form button[type='submit']").off('click').click(submitMicropubForm);
$(".reply-area").hide(); $(".reply-area.closed").hide();
$("article").off('click').click(function(evt) { $("article").off('click').click(function(evt) {
var $target = $(evt.target); var $target = $(evt.target);
if ($target.closest("form, a, video, audio").length == 0) { if ($target.closest("form, a, video, audio").length == 0) {
$(".reply-area", this).toggleClass("closed");
$(".reply-area", this).slideToggle(200); $(".reply-area", this).slideToggle(200);
} }
}); });
} }

View file

@ -360,7 +360,8 @@ th {
/* Subtlety of Hue */ /* Subtlety of Hue */
body { body {
font: 12pt Helvetica, Arial, sans-serif; font: 12pt Helvetica, Arial, sans-serif;
background: #ecebf0; /*background: $athens-gray;*/
background: #f4f4f4;
padding-top: 1em; } padding-top: 1em; }
a { a {
@ -410,12 +411,13 @@ ul#navigation {
article { article {
margin-bottom: 2em; margin-bottom: 2em;
box-shadow: 0 0 2px #687d77; /*box-shadow: $box-shadow;
background-color: white; background-color: white;*/
padding: 0.5em; } padding: 0.5em; }
article.reply-context { article.reply-context {
margin-bottom: 0; margin-bottom: 0;
background-color: #f3f3f3; } background-color: #e0e0e0;
color: #555; }
article.reply-context .content img { article.reply-context .content img {
max-height: 240px; max-height: 240px;
max-width: 240px; } max-width: 240px; }

View file

@ -17,7 +17,8 @@ $box-shadow: 0 0 2px $sirocco;
body { body {
font: 12pt $body-font; font: 12pt $body-font;
background: $athens-gray; /*background: $athens-gray;*/
background: #f4f4f4;
padding-top: 1em; padding-top: 1em;
} }
@ -86,13 +87,14 @@ ul#navigation {
article { article {
margin-bottom: 2em; margin-bottom: 2em;
box-shadow: $box-shadow; /*box-shadow: $box-shadow;
background-color: white; background-color: white;*/
padding: 0.5em; padding: 0.5em;
&.reply-context { &.reply-context {
margin-bottom: 0; margin-bottom: 0;
background-color: #f3f3f3; background-color: #e0e0e0;
color: #555;
.content img { .content img {
max-height: 240px; max-height: 240px;
max-width: 240px; max-width: 240px;
@ -246,10 +248,10 @@ button {
.reply-area { .reply-area {
text-align: center; text-align: center;
margin-top: 0.5em; margin-top: 0.5em;
.content { .content {
height: 4em; height: 4em;
} }
.reply-link { .reply-link {
display: inline-block; display: inline-block;

View file

@ -18,8 +18,20 @@ $(function() {
}).fail(function failure() { }).fail(function failure() {
$(".save-status", form).html("Save Failed!"); $(".save-status", form).html("Save Failed!");
}); });
});
$("form.poll-now").submit(function(evt) {
var form = $(this);
evt.preventDefault();
$(".poll-status", form).html("");
$.post(form.attr('action'), form.serialize(), function success(){
$(".poll-status", form).html("Poll Requested.");
}).fail(function failure() {
$(".poll-status", form).html("Polling Failed!");
});
}); });
}); });

View file

@ -311,8 +311,13 @@ def notify_feed_updated(app, feed_id, entry_ids):
'subscription': s.id, 'subscription': s.id,
'entries': rendered, 'entries': rendered,
}) })
for topic in ('user:{}'.format(s.user.id),
'subsc:{}'.format(s.id)): topics = []
if not s.exclude:
topics.append('user:{}'.format(s.user.id))
topics.append('subsc:{}'.format(s.id))
for topic in topics:
redis.publish('woodwind_notify:{}'.format(topic), message) redis.publish('woodwind_notify:{}'.format(topic), message)

View file

@ -73,7 +73,7 @@
| {% for synd in entry.get_property('syndication') %} <a href="{{ synd }}">{{ synd | domain_for_url}}</a>{% endfor %} | {% for synd in entry.get_property('syndication') %} <a href="{{ synd }}">{{ synd | domain_for_url}}</a>{% endfor %}
{% endif %} {% endif %}
<div class="reply-area"> <div class="reply-area closed">
{% include '_reply.jinja2' with context %} {% include '_reply.jinja2' with context %}
</div> </div>
</footer> </footer>

View file

@ -8,7 +8,7 @@
<link rel="shortcut icon" href="{{ url_for('static', filename='logo.png') }}"/> <link rel="shortcut icon" href="{{ url_for('static', filename='logo.png') }}"/>
<link rel="apple-touch-icon" href="{{ url_for('static', filename='logo.png') }}"/> <link rel="apple-touch-icon" href="{{ url_for('static', filename='logo.png') }}"/>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css', version='2015-10-07') }}"/> <link rel="stylesheet" href="{{ url_for('static', filename='style.css', version='2015-11-25') }}"/>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"/> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"/>
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script> <script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="{{ url_for('static', filename='moment.min.js') }}"></script> <script src="{{ url_for('static', filename='moment.min.js') }}"></script>

View file

@ -4,7 +4,7 @@
{% if ws_topic %} {% if ws_topic %}
<script>var WS_TOPIC = "{{ ws_topic }}";</script> <script>var WS_TOPIC = "{{ ws_topic }}";</script>
{% endif %} {% endif %}
<script src="{{url_for('static', filename='feed.js', version='2015-10-08')}}"></script> <script src="{{url_for('static', filename='feed.js', version='2015-11-06')}}"></script>
{% if current_user and current_user.settings {% if current_user and current_user.settings
and current_user.settings.get('reply-method') == 'indie-config' %} and current_user.settings.get('reply-method') == 'indie-config' %}

View file

@ -38,6 +38,12 @@
<input type="text" name="tags" value="{{ s.tags or ''}}"/> <input type="text" name="tags" value="{{ s.tags or ''}}"/>
<label>URL</label> <label>URL</label>
<input type="text" name="feed" value="{{ s.feed.feed }}" readonly /> <input type="text" name="feed" value="{{ s.feed.feed }}" readonly />
<label style="display: block; font-weight: normal;">
<input type="checkbox" name="exclude" value="true" {% if s.exclude %}checked{% endif %} />
Exclude from primary feed
</label>
<button type="submit">Save Edits</button> <span class="save-status"></span> <button type="submit">Save Edits</button> <span class="save-status"></span>
</form> </form>
@ -63,14 +69,14 @@
</div> </div>
<form action="{{ url_for('.update_feed') }}" method="POST" style="display:inline"> <form class="poll-now" action="{{ url_for('.update_feed') }}" method="POST" style="display:inline">
<input type="hidden" name="id" value="{{ s.feed.id }}"/> <input type="hidden" name="id" value="{{ s.feed.id }}"/>
<button type="submit">Poll Now</button> <button type="submit">Poll Now</button> <span class="poll-status"></span>
</form> </form>
<form action="{{ url_for('.unsubscribe') }}" method="POST" style="display:inline"> <form class="unsubscribe" action="{{ url_for('.unsubscribe') }}" method="POST" style="display:inline">
<input type="hidden" name="id" value="{{ s.id }}"/> <input type="hidden" name="id" value="{{ s.id }}"/>
<button type="submit">Unsubscribe</button> <button type="submit">Unsubscribe</button> <span class="unsubscribe-status"></span>
</form> </form>

View file

@ -2,7 +2,8 @@ from . import tasks, util
from .extensions import db, login_mgr, micropub from .extensions import db, login_mgr, micropub
from .models import Feed, Entry, User, Subscription from .models import Feed, Entry, User, Subscription
import flask.ext.login as flask_login import flask.ext.login as flask_login
import binascii
import base64
import bs4 import bs4
import datetime import datetime
import feedparser import feedparser
@ -74,6 +75,7 @@ def index():
entry_query = entry_query.filter( entry_query = entry_query.filter(
sqlalchemy.sql.expression.cast(Entry.properties['jam'], sqlalchemy.TEXT) == 'true') sqlalchemy.sql.expression.cast(Entry.properties['jam'], sqlalchemy.TEXT) == 'true')
else: else:
entry_query = entry_query.filter(Subscription.exclude == False)
ws_topic = 'user:{}'.format(flask_login.current_user.id) ws_topic = 'user:{}'.format(flask_login.current_user.id)
entry_query = entry_query.order_by(Entry.retrieved.desc(), entry_query = entry_query.order_by(Entry.retrieved.desc(),
@ -226,6 +228,7 @@ def edit_subscription():
subsc.tags = ' '.join(t.strip() for t in tag_list if t.strip()) subsc.tags = ' '.join(t.strip() for t in tag_list if t.strip())
else: else:
subsc.tags = None subsc.tags = None
subsc.exclude = flask.request.form.get('exclude') == 'true'
db.session.commit() db.session.commit()
flask.flash('Edited {}'.format(subsc.name)) flask.flash('Edited {}'.format(subsc.name))
@ -609,13 +612,29 @@ def add_preview(content):
@views.app_template_filter() @views.app_template_filter()
def proxy_image(url): def proxy_image(url):
proxy_url = flask.current_app.config.get('IMAGEPROXY_URL')
proxy_key = flask.current_app.config.get('IMAGEPROXY_KEY')
if proxy_url and proxy_key:
sig = base64.urlsafe_b64encode(
hmac.new(proxy_key.encode(), url.encode(), hashlib.sha256).digest()
).decode()
return '/'.join((proxy_url.rstrip('/'), 's' + sig, url))
pilbox_url = flask.current_app.config.get('PILBOX_URL')
pilbox_key = flask.current_app.config.get('PILBOX_KEY')
if pilbox_url and pilbox_key:
query = urllib.parse.urlencode({'url': url, 'op': 'noop'})
sig = hmac.new(pilbox_key.encode(), query.encode(), hashlib.sha1).hexdigest()
query += '&sig=' + sig
return pilbox_url + '?' + query
camo_url = flask.current_app.config.get('CAMO_URL') camo_url = flask.current_app.config.get('CAMO_URL')
camo_key = flask.current_app.config.get('CAMO_KEY') camo_key = flask.current_app.config.get('CAMO_KEY')
if not camo_url or not camo_key: if camo_url and camo_key:
return url digest = hmac.new(camo_key.encode(), url.encode(), hashlib.sha1).hexdigest()
digest = hmac.new(camo_key.encode(), url.encode(), hashlib.sha1).hexdigest() return (urllib.parse.urljoin(camo_url, digest)
return (urllib.parse.urljoin(camo_url, digest) + '?url=' + urllib.parse.quote_plus(url))
+ '?url=' + urllib.parse.quote_plus(url)) return url
@views.app_template_filter() @views.app_template_filter()