Skip to content

Commit

Permalink
Fixed #27 -- added Subtome support.
Browse files Browse the repository at this point in the history
Thanks @julien51!
  • Loading branch information
brutasse committed Apr 14, 2013
1 parent 0a9fabc commit 820e773
Show file tree
Hide file tree
Showing 21 changed files with 171 additions and 492 deletions.
6 changes: 6 additions & 0 deletions .coveragerc
@@ -0,0 +1,6 @@
[run]
branch = True
source = feedhq

[report]
omit = *migrations*
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -40,7 +40,7 @@ txpull:
@tx pull -a

coverage:
@envdir tests/envdir coverage run --source=feedhq `which django-admin.py` test
@envdir tests/envdir coverage run `which django-admin.py` test
@coverage html

.PHONY: test run db user shell dbshell updatefeeds favicons \
Expand Down
18 changes: 16 additions & 2 deletions feedhq/feeds/forms.py
Expand Up @@ -134,8 +134,22 @@ class ReadForm(forms.Form):

class SubscriptionForm(forms.Form):
subscribe = forms.BooleanField(label=_('Subscribe?'), required=False)
name = forms.CharField(label=_('Name'))
name = forms.CharField(label=_('Name'), required=False)
url = forms.URLField(label=_('URL'))
category = forms.ChoiceField(label=_('Category'))
category = forms.ChoiceField(label=_('Category'), required=False)

def clean_name(self):
return self.require_if_subscribe('name')

def clean_category(self):
return self.require_if_subscribe('category')

def require_if_subscribe(self, field_name):
if (
self.cleaned_data.get('subscribe', False) and
not self.cleaned_data[field_name]
):
raise forms.ValidationError(_('This field is required.'))
return self.cleaned_data[field_name]

SubscriptionFormSet = formset_factory(SubscriptionForm, extra=0)
23 changes: 0 additions & 23 deletions feedhq/feeds/templates/feeds/bookmarklet.html

This file was deleted.

26 changes: 0 additions & 26 deletions feedhq/feeds/templates/feeds/bookmarklet.js

This file was deleted.

30 changes: 0 additions & 30 deletions feedhq/feeds/templates/feeds/bookmarklet_subscribe.html

This file was deleted.

35 changes: 35 additions & 0 deletions feedhq/feeds/templates/feeds/subscribe.html
@@ -0,0 +1,35 @@
{% extends "base.html" %}

{% block title %}{% trans "Subscribe" %}{% endblock %}

{% block content %}
{% if form.forms %}
<h1>{% trans "Subscribe" %}</h1>
<p>{% blocktrans count counter=feed_count %}We found the following feed. Please choose a name and a category before subscribing.{% plural %}We found the following {{ feed_count }} feeds. Please uncheck those you don't want to subscribe to and select the category for each one.{% endblocktrans %}</p>
<form method="post" action="{% url "feeds:subscribe" %}" id="subscribe">
{{ form.management_form }}
{% with form as formset %}
{% for form in formset %}
<div class="subscribe_form">
<h2>#{{ forloop.counter }}</h2>
{% include "form.html" %}
</div>
{% endfor %}
{% endwith %}
<div class="submit">
<input type="submit" value="{% trans "Subscribe" %}">
</div>
</form>
{% else %}
<div id="entry">
<div class="content">
<h1>{% trans "No feed found" %}</h1>
{% if site_url %}
<p>{% blocktrans %}Sorry, it looks like there are no feeds available on <a href="{{ site_url }}">{{ site_url }}</a>.{% endblocktrans %}</p>
{% else %}
<p>{% trans "Sorry, it looks like there are no feeds available on this site." %}</p>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}
4 changes: 1 addition & 3 deletions feedhq/feeds/urls.py
Expand Up @@ -14,9 +14,7 @@
url(r'^dashboard/$', views.dashboard, name='dashboard'),

url(r'^import/$', views.import_feeds, name='import_feeds'),
url(r'^bookmarklet/$', views.bookmarklet, name='bookmarklet'),
url(r'^bookmarklet/js/$', views.bookmarklet_js, name='bookmarklet_js'),
url(r'^subscribe/$', views.subscribe, name='bookmarklet_subscribe'),
url(r'^subscribe/$', views.subscribe, name='subscribe'),

# Categories
url(r'^category/add/$', views.add_category, name='add_category'),
Expand Down
132 changes: 49 additions & 83 deletions feedhq/feeds/views.py
@@ -1,23 +1,17 @@
import lxml.html
import opml
import re
import urllib

from django.contrib import messages
from django.contrib.sites.models import RequestSite
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.core.urlresolvers import reverse, reverse_lazy
from django.db import transaction
from django.db.models import Sum
from django.http import HttpResponseNotAllowed
from django.shortcuts import get_object_or_404, redirect, render
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext as _
from django.views import generic
from django.views.decorators.csrf import csrf_exempt

from ..decorators import login_required
from ..utils import manual_csrf_check
from ..tasks import enqueue
from .models import Category, Feed, Entry
from .forms import (CategoryForm, FeedForm, OPMLImportForm, ActionForm,
Expand Down Expand Up @@ -236,6 +230,14 @@ def form_valid(self, form):
messages.success(self.request, _('%(feed)s has been successfully '
'added') % {'feed': self.object.name})
return response

def get_initial(self):
initial = super(AddFeed, self).get_initial()
if 'feed' in self.request.GET:
initial['url'] = self.request.GET['feed']
if 'name' in self.request.GET:
initial['name'] = self.request.GET['name']
return initial
add_feed = login_required(AddFeed.as_view())


Expand Down Expand Up @@ -478,86 +480,50 @@ def dashboard(request):
return render(request, 'feeds/dashboard.html', context)


def bookmarklet(request):
site = RequestSite(request)
proto = 'https' if request.is_secure() else 'http'
url = '%s://%s%s' % (proto, site.domain, reverse('feeds:bookmarklet_js'))
js_func = ("(function(){var s=document.createElement('script');"
"s.setAttribute('type','text/javascript');"
"s.setAttribute('charset','UTF-8');"
"s.setAttribute('src','%s');"
"document.documentElement.appendChild(s);})()") % url
js_func = urllib.quote(js_func)
return render(request, "feeds/bookmarklet.html",
{'js_func': js_func,
'scheme': 'https' if request.is_secure() else 'http',
'site': site})


def bookmarklet_js(request):
site = RequestSite(request)
scheme = 'https' if request.is_secure() else 'http'
response = render(request, "feeds/bookmarklet.js",
{'scheme': scheme, 'site': site})
response['Content-Type'] = 'text/javascript; charset=utf-8'
return response


@csrf_exempt
def subscribe(request):
if request.method != 'POST':
response = HttpResponseNotAllowed('Method not allowed')
response['Accept'] = 'POST'
return response

if not request.user.is_authenticated():
return redirect(reverse('login') + '?from=bookmarklet')

if 'source' in request.POST and 'html' in request.POST:

xml = lxml.html.fromstring(request.POST['html'])
xml.make_links_absolute(request.POST['source']) # lxml FTW
links = xml.xpath(('//link[@type="application/atom+xml" or '
'@type="application/rss+xml"]'))
parsed_links = []
for link in links:
parsed_links.append({
'url': link.get('href'),
'name': link.get('title'),
'subscribe': True,
})
formset = SubscriptionFormSet(initial=parsed_links)
cats = [(str(c.pk), c.name) for c in request.user.categories.all()]
class Subscribe(generic.FormView):
form_class = SubscriptionFormSet
template_name = 'feeds/subscribe.html'

def get_initial(self):
initial = []
for link in self.request.GET.get('feeds', '').split(','):
if link:
initial.append({
'url': link,
'subscribe': True,
})
self.feed_count = len(initial)
return initial

def get_form(self, form_class):
formset = super(Subscribe, self).get_form(form_class)
cats = [['', '-----']] + [
(str(c.pk), c.name) for c in self.request.user.categories.all()
]
for form in formset:
form.fields['category'].choices = cats
return render(request, 'feeds/bookmarklet_subscribe.html',
{'formset': formset, 'source': request.POST['source']})
return formset

else:
response = manual_csrf_check(request)
if response is not None:
return response
def get_context_data(self, **kwargs):
ctx = super(Subscribe, self).get_context_data(**kwargs)
ctx['site_url'] = self.request.GET.get('url')
ctx['feed_count'] = len(ctx['form'].forms)
return ctx

formset = SubscriptionFormSet(data=request.POST)
cats = [(str(c.pk), c.name) for c in request.user.categories.all()]
def form_valid(self, formset):
created = 0
for form in formset:
form.fields['category'].choices = cats
if formset.is_valid():
created = 0
for form in formset:
if form.cleaned_data['subscribe']:
category = request.user.categories.get(
pk=form.cleaned_data['category'],
)
category.feeds.create(name=form.cleaned_data['name'],
url=form.cleaned_data['url'])
created += 1
if created == 1:
message = _('1 feed has been added')
else:
message = _('%s feeds have been added') % created
messages.success(request, message)
return redirect(reverse('feeds:home'))
if form.cleaned_data['subscribe']:
category = self.request.user.categories.get(
pk=form.cleaned_data['category'],
)
category.feeds.create(name=form.cleaned_data['name'],
url=form.cleaned_data['url'])
created += 1
if created == 1:
message = _('1 feed has been added')
else:
return render(request, 'feeds/bookmarklet_subscribe.html',
{'formset': formset})
message = _('%s feeds have been added') % created
messages.success(self.request, message)
return redirect(reverse('feeds:home'))
subscribe = login_required(Subscribe.as_view())
15 changes: 15 additions & 0 deletions feedhq/profiles/templates/profiles/bookmarklet.html
@@ -0,0 +1,15 @@
{% extends "profiles/profile_base.html" %}

{% block title %}{% trans "Subscribe button" %}{% endblock %}

{% block bookmarklet_selected %}current{% endblock %}

{% block profile_content %}
<h2>{% trans "Subscribe button" %}</h2>

<p>{% blocktrans %}FeedHQ provides a convenient way to subscribe to any site that provides feeds via <a target="_blank" href="https://www.subtome.com/">Subtome</a>. You need to <a target="_blank" href="https://www.subtome.com/settings.html">add the Subtome button</a> to your bookmarks toolbar or install the Chrome extension.{% endblocktrans %}</p>

<p>{% trans "Next time you visit an interesting site, simply click on the subscribe button on your bookmarks toolbar. It'll let you easily add the new subscription to your FeedHQ account." %}</p>

<iframe style="display:none;" src="https://www.subtome.com/register.html?name=FeedHQ&url={{ scheme }}://{{ site.domain }}{% url "feeds:subscribe" %}%3Ffeeds={feeds}%26url={url}"></iframe>
{% endblock %}
1 change: 1 addition & 0 deletions feedhq/profiles/templates/profiles/profile_base.html
Expand Up @@ -8,6 +8,7 @@ <h1>{% trans "Account settings" %}</h1>
<ul>
<li><a class="{% block stats_selected %}{% endblock %}" href="{% url "stats" %}">{% trans "Stats" %}</a></li>
<li><a class="{% block profile_selected %}{% endblock %}" href="{% url "profile" %}">{% trans "Profile" %}</a></li>
<li><a class="{% block bookmarklet_selected %}{% endblock %}" href="{% url "bookmarklet" %}">{% trans "Subscribe button" %}</a></li>
<li><a class="{% block sharing_selected %}{% endblock %}" href="{% url "sharing" %}">{% trans "Sharing" %}</a></li>
<li><a class="{% block readlater_selected %}{% endblock %}" href="{% url "read_later" %}">{% trans "Read later" %}</a></li>
<li><a class="{% block password_selected %}{% endblock %}" href="{% url "password" %}">{% trans "Password" %}</a></li>
Expand Down
1 change: 1 addition & 0 deletions feedhq/profiles/urls.py
Expand Up @@ -7,6 +7,7 @@
url(r'^stats/$', views.stats, name='stats'),
url(r'^profile/$', views.profile, name='profile'),
url(r'^sharing/$', views.sharing, name='sharing'),
url(r'^bookmarklet/$', views.bookmarklet, name='bookmarklet'),
url(r'^password/$', views.password, name='password'),
url(r'^export/$', views.export, name='export'),
url(r'^export/opml/$', views.opml_export, name='opml_export'),
Expand Down
12 changes: 12 additions & 0 deletions feedhq/profiles/views.py
@@ -1,4 +1,5 @@
from django.contrib import messages
from django.contrib.sites.models import RequestSite
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect, render
from django.utils.translation import ugettext as _
Expand Down Expand Up @@ -148,3 +149,14 @@ def form_valid(self, form):
class DestroyDone(generic.TemplateView):
template_name = 'profiles/account_delete_done.html'
destroy_done = DestroyDone.as_view()


class Bookmarklet(generic.TemplateView):
template_name = 'profiles/bookmarklet.html'

def get_context_data(self, **kwargs):
ctx = super(Bookmarklet, self).get_context_data(**kwargs)
ctx['site'] = RequestSite(self.request)
ctx['scheme'] = 'https' if self.request.is_secure() else 'http'
return ctx
bookmarklet = login_required(Bookmarklet.as_view())

1 comment on commit 820e773

@julien51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3

Please sign in to comment.