# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
+
+#############################################################################
+# from: http://djangosnippets.org/snippets/243/
+
+import base64
+
+from django.http import HttpResponse
+from django.contrib.auth import authenticate, login
+
+#
+def view_or_basicauth(view, request, test_func, realm = "", *args, **kwargs):
+ """
+ This is a helper function used by 'logged_in_or_basicauth' and
+ 'has_perm_or_basicauth' (deleted) that does the nitty of determining if they
+ are already logged in or if they have provided proper http-authorization
+ and returning the view if all goes well, otherwise responding with a 401.
+ """
+ if test_func(request.user):
+ # Already logged in, just return the view.
+ #
+ return view(request, *args, **kwargs)
+
+ # They are not logged in. See if they provided login credentials
+ #
+ if 'HTTP_AUTHORIZATION' in request.META:
+ auth = request.META['HTTP_AUTHORIZATION'].split()
+ if len(auth) == 2:
+ # NOTE: We are only support basic authentication for now.
+ #
+ if auth[0].lower() == "basic":
+ uname, passwd = base64.b64decode(auth[1]).split(':')
+ user = authenticate(username=uname, password=passwd)
+ if user is not None:
+ if user.is_active:
+ login(request, user)
+ request.user = user
+ return view(request, *args, **kwargs)
+
+ # Either they did not provide an authorization header or
+ # something in the authorization attempt failed. Send a 401
+ # back to them to ask them to authenticate.
+ #
+ response = HttpResponse()
+ response.status_code = 401
+ response['WWW-Authenticate'] = 'Basic realm="%s"' % realm
+ return response
+
+
+#
+def logged_in_or_basicauth(realm = ""):
+ """
+ A simple decorator that requires a user to be logged in. If they are not
+ logged in the request is examined for a 'authorization' header.
+
+ If the header is present it is tested for basic authentication and
+ the user is logged in with the provided credentials.
+
+ If the header is not present a http 401 is sent back to the
+ requestor to provide credentials.
+
+ The purpose of this is that in several django projects I have needed
+ several specific views that need to support basic authentication, yet the
+ web site as a whole used django's provided authentication.
+
+ The uses for this are for urls that are access programmatically such as
+ by rss feed readers, yet the view requires a user to be logged in. Many rss
+ readers support supplying the authentication credentials via http basic
+ auth (and they do NOT support a redirect to a form where they post a
+ username/password.)
+
+ Use is simple:
+
+ @logged_in_or_basicauth
+ def your_view:
+ ...
+
+ You can provide the name of the realm to ask for authentication within.
+ """
+ def view_decorator(func):
+ def wrapper(request, *args, **kwargs):
+ return view_or_basicauth(func, request,
+ lambda u: u.is_authenticated(),
+ realm, *args, **kwargs)
+ return wrapper
+ return view_decorator
+
+
+#############################################################################
+
+
from base64 import b64encode
import os.path
_root_feeds = (
- {u"category": u"author", u"title": u"Autorzy", u"description": u"Utwory wg autorów"},
- {u"category": u"kind", u"title": u"Rodzaje", u"description": u"Utwory wg rodzajów"},
- {u"category": u"genre", u"title": u"Gatunki", u"description": u"Utwory wg gatunków"},
- {u"category": u"epoch", u"title": u"Epoki", u"description": u"Utwory wg epok"},
+ {
+ u"category": u"",
+ u"link": u"opds_user",
+ u"link_args": [],
+ u"title": u"Moje półki",
+ u"description": u"Półki użytkownika dostępne po zalogowaniu"
+ },
+ {
+ u"category": u"author",
+ u"link": u"opds_by_category",
+ u"link_args": [u"author"],
+ u"title": u"Autorzy",
+ u"description": u"Utwory wg autorów"
+ },
+ {
+ u"category": u"kind",
+ u"link": u"opds_by_category",
+ u"link_args": [u"kind"],
+ u"title": u"Rodzaje",
+ u"description": u"Utwory wg rodzajów"
+ },
+ {
+ u"category": u"genre",
+ u"link": u"opds_by_category",
+ u"link_args": [u"kind"],
+ u"title": u"Gatunki",
+ u"description": u"Utwory wg gatunków"
+ },
+ {
+ u"category": u"epoch",
+ u"link": u"opds_by_category",
+ u"link_args": [u"epoch"],
+ u"title": u"Epoki",
+ u"description": u"Utwory wg epok"
+ },
)
+def factory_decorator(decorator):
+ """ generates a decorator for a function factory class
+ if A(*) == f, factory_decorator(D)(A)(*) == D(f)
+ """
+ def fac_dec(func):
+ def wrapper(*args, **kwargs):
+ return decorator(func(*args, **kwargs))
+ return wrapper
+ return fac_dec
+
+
class OPDSFeed(Atom1Feed):
link_rel = u"subsection"
link_type = u"application/atom+xml"
+ _book_parent_img = "http://%s%s" % (Site.objects.get_current().domain, os.path.join(settings.STATIC_URL, "img/book-parent.png"))
try:
- with open(os.path.join(settings.STATIC_ROOT, "img/book-par ent.png")) as f:
- t = f.read()
- _book_parent_img_size = len(t)
- _book_parent_img = b64encode(t)
+ _book_parent_img_size = unicode(os.path.getsize(os.path.join(settings.STATIC_ROOT, "img/book-parent.png")))
except:
- _book_parent_img = _book_parent_img_size = ''
+ _book_parent_img_size = ''
+ _book_img = "http://%s%s" % (Site.objects.get_current().domain, os.path.join(settings.STATIC_URL, "img/book.png"))
try:
- with open(os.path.join(settings.STATIC_ROOT, "img/bo ok.png")) as f:
- t = f.read()
- _book_img_size = len(t)
- _book_img = b64encode(t)
+ _book_img_size = unicode(os.path.getsize(os.path.join(settings.STATIC_ROOT, "img/book.png")))
except:
- _book_img = _book_img_size = ''
+ _book_img_size = ''
def add_root_elements(self, handler):
super(OPDSFeed, self).add_root_elements(handler)
# add a "green book" icon
handler.addQuickElement(u"link", '',
{u"rel": u"http://opds-spec.org/thumbnail",
- u"href": u"data:image/png;base64,%s" % self._book_parent_img,
- u"length": unicode(self._book_parent_img_size),
+ u"href": self._book_parent_img,
+ u"length": self._book_parent_img_size,
u"type": u"image/png"})
if item['pubdate'] is not None:
handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('utf-8'))
# add a "red book" icon
handler.addQuickElement(u"link", '',
{u"rel": u"http://opds-spec.org/thumbnail",
- u"href": u"data:image/png;base64,%s" % self._book_img,
- u"length": unicode(self._book_img_size),
+ u"href": self._book_img,
+ u"length": self._book_img_size,
u"type": u"image/png"})
# Categories.
return item['title']
def item_link(self, item):
- return reverse(u"opds_by_category", args=[item['category']])
+ return reverse(item['link'], args=item['link_args'])
def item_description(self, item):
return item['description']
return u''
def item_enclosure_url(self, book):
- return "http://%s%s" % (Site.objects.get_current().domain, book.epub_file.url)
+ return "http://%s%s" % (Site.objects.get_current().domain, book.root_ancestor.epub_file.url)
def item_enclosure_length(self, book):
- return book.epub_file.size
+ return book.root_ancestor.epub_file.size
+
+
+@factory_decorator(logged_in_or_basicauth())
+class UserFeed(Feed):
+ feed_type = OPDSFeed
+ link = u'http://www.wolnelektury.pl/'
+ description = u"Półki użytkownika na stronie http://WolneLektury.pl"
+ author_name = u"Wolne Lektury"
+ author_link = u"http://www.wolnelektury.pl/"
+
+ def get_object(self, request):
+ return request.user
+
+ def title(self, user):
+ return u"Półki użytkownika %s" % user.username
+
+ def items(self, user):
+ return (tag for tag in Tag.objects.filter(category='set', user=user) if tag.get_count() > 0)
+
+ def item_title(self, item):
+ return item.name
+
+ def item_link(self, item):
+ return reverse("opds_user_set", args=[item.slug])
+
+ def item_description(self):
+ return u''
+
+
+@factory_decorator(logged_in_or_basicauth())
+class UserSetFeed(Feed):
+ feed_type = OPDSFeed
+ link = u'http://www.wolnelektury.pl/'
+ item_enclosure_mime_type = "application/epub+zip"
+ author_name = u"Wolne Lektury"
+ author_link = u"http://www.wolnelektury.pl/"
+
+ def link(self, tag):
+ return tag.get_absolute_url()
+
+ def title(self, tag):
+ return tag.name
+
+ def description(self, tag):
+ return u"Spis utworów na stronie http://WolneLektury.pl"
+
+ def get_object(self, request, slug):
+ return get_object_or_404(Tag, category='set', slug=slug, user=request.user)
+
+ def items(self, tag):
+ return Book.tagged.with_any([tag])
+
+ def item_title(self, book):
+ return book.title
+
+ def item_description(self):
+ return u''
+
+ def item_link(self, book):
+ return book.get_absolute_url()
+
+ def item_author_name(self, book):
+ try:
+ return book.tags.filter(category='author')[0].name
+ except KeyError:
+ return u''
+
+ def item_author_link(self, book):
+ try:
+ return book.tags.filter(category='author')[0].get_absolute_url()
+ except KeyError:
+ return u''
+
+ def item_enclosure_url(self, book):
+ return "http://%s%s" % (Site.objects.get_current().domain, book.root_ancestor.epub_file.url)
+
+ def item_enclosure_length(self, book):
+ return book.root_ancestor.epub_file.size
+
+@logged_in_or_basicauth()
+def user_set_feed(request):
+ return UserSetFeed()(request)
+