standarize ajaxable dialogs
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 20 Dec 2011 15:43:13 +0000 (16:43 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Tue, 20 Dec 2011 15:45:49 +0000 (16:45 +0100)
30 files changed:
apps/ajaxable/__init__.py [new file with mode: 0644]
apps/ajaxable/models.py [new file with mode: 0644]
apps/ajaxable/templates/ajaxable/form.html [new file with mode: 0755]
apps/ajaxable/templates/ajaxable/form_on_page.html [new file with mode: 0755]
apps/ajaxable/utils.py [new file with mode: 0755]
apps/catalogue/urls.py
apps/catalogue/views.py
apps/pdcounter/urls.py [deleted file]
apps/pdcounter/views.py
apps/suggest/forms.py
apps/suggest/templates/publishing_suggest.html
apps/suggest/templates/publishing_suggest_full.html [deleted file]
apps/suggest/templates/suggest.html [deleted file]
apps/suggest/urls.py
apps/suggest/views.py
wolnelektury/settings.py
wolnelektury/static/css/dialogs.css [new file with mode: 0755]
wolnelektury/static/css/main_page.css
wolnelektury/static/js/dialogs.js [new file with mode: 0755]
wolnelektury/static/js/jquery.jqmodal.js
wolnelektury/static/js/locale.js [new file with mode: 0755]
wolnelektury/static/js/pdcounter.js [new file with mode: 0755]
wolnelektury/templates/auth/login.html [deleted file]
wolnelektury/templates/base.html
wolnelektury/templates/main_page.html
wolnelektury/templates/pdcounter/author_detail.html
wolnelektury/templates/pdcounter/book_stub_detail.html
wolnelektury/templates/pdcounter/pd_counter.html [deleted file]
wolnelektury/urls.py
wolnelektury/views.py

diff --git a/apps/ajaxable/__init__.py b/apps/ajaxable/__init__.py
new file mode 100644 (file)
index 0000000..a010543
--- /dev/null
@@ -0,0 +1,4 @@
+"""
+Provides a way to create forms behaving correctly as AJAX forms
+as well as standalone forms without any Javascript.
+"""
diff --git a/apps/ajaxable/models.py b/apps/ajaxable/models.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/ajaxable/templates/ajaxable/form.html b/apps/ajaxable/templates/ajaxable/form.html
new file mode 100755 (executable)
index 0000000..d8f0036
--- /dev/null
@@ -0,0 +1,9 @@
+{% load i18n %}
+<h1>{{ title }}</h1>
+
+<form action="{{ request.get_full_path }}" method="post" accept-charset="utf-8" class="cuteform">
+<ol>
+    {{ form.as_ul }}
+    <li><input type="submit" value="{{ submit }}"/></li>
+</ol>
+</form>
\ No newline at end of file
diff --git a/apps/ajaxable/templates/ajaxable/form_on_page.html b/apps/ajaxable/templates/ajaxable/form_on_page.html
new file mode 100755 (executable)
index 0000000..e54231d
--- /dev/null
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block titleextra %}:: {{ title }}{% endblock %}
+
+{% block body %}
+
+    {% include ajax_template %}
+
+    {% if response_data.message %}
+        <p>{{ response_data.message }}</p>
+    {% endif %}
+
+{% endblock %}
diff --git a/apps/ajaxable/utils.py b/apps/ajaxable/utils.py
new file mode 100755 (executable)
index 0000000..14b5dfc
--- /dev/null
@@ -0,0 +1,82 @@
+from django.http import HttpResponse, HttpResponseRedirect
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.utils.encoding import force_unicode
+from django.utils.functional import Promise
+from django.utils.http import urlquote_plus
+from django.utils import simplejson
+from django.utils.translation import ugettext_lazy as _
+
+
+class LazyEncoder(simplejson.JSONEncoder):
+    def default(self, obj):
+        if isinstance(obj, Promise):
+            return force_unicode(obj)
+        return obj
+
+# shortcut for JSON reponses
+class JSONResponse(HttpResponse):
+    def __init__(self, data={}, callback=None, **kwargs):
+        # get rid of mimetype
+        kwargs.pop('mimetype', None)
+        data = simplejson.dumps(data)
+        if callback:
+            data = callback + "(" + data + ");" 
+        super(JSONResponse, self).__init__(data, mimetype="application/json", **kwargs)
+
+
+
+class AjaxableFormView(object):
+    """Subclass this to create an ajaxable view for any form.
+
+    In the subclass, provide at least form_class.
+
+    """
+    form_class = None
+    # override to customize form look
+    template = "ajaxable/form.html"
+    # set to redirect after succesful ajax-less post
+    submit = _('Send')
+    redirect = None
+    title = ''
+    success_message = ''
+    formname = "form"
+    full_template = "ajaxable/form_on_page.html"
+
+    def __call__(self, request):
+        """A view displaying a form, or JSON if `ajax' GET param is set."""
+        ajax = request.GET.get('ajax', False)
+        if request.method == "POST":
+            form = self.form_class(data=request.POST)
+            if form.is_valid():
+                self.success(form, request)
+                redirect = request.GET.get('next')
+                if not ajax and redirect is not None:
+                    return HttpResponseRedirect(urlquote_plus(
+                                redirect, safe='/?='))
+                response_data = {'success': True, 'message': self.success_message}
+            else:
+                response_data = {'success': False, 'errors': form.errors}
+            if ajax:
+                return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
+        else:
+            form = self.form_class()
+            response_data = None
+
+        template = self.template if ajax else self.full_template
+        return render_to_response(template, {
+                self.formname: form, 
+                "title": self.title,
+                "submit": self.submit,
+                "response_data": response_data,
+                "ajax_template": self.template,
+            },
+            context_instance=RequestContext(request))
+
+    def success(self, form, request):
+        """What to do when the form is valid.
+        
+        By default, just save the form.
+
+        """
+        return form.save(request)
index bb1b960..0e62c30 100644 (file)
@@ -29,9 +29,6 @@ urlpatterns = patterns('catalogue.views',
     #url(r'^zip/mobi\.zip$', 'download_zip', {'format': 'mobi', 'slug': None}, 'download_zip_mobi'),
     #url(r'^zip/audiobook/(?P<book>%s)\.zip' % Book.FILEID_RE, 'download_zip', {'format': 'audiobook'}, 'download_zip_audiobook'),
 
-    # tools
-    url(r'^zegar/$', 'clock', name='clock'),
-
     # Public interface. Do not change this URLs.
     url(r'^lektura/(?P<book>%s)\.html$' % Book.FILEID_RE, 'book_text', name='book_text'),
     url(r'^lektura/(?P<book>%s)/$' % Book.URLID_RE, 'book_detail', name='book_detail'),
index 13bb5ac..8bf5681 100644 (file)
@@ -17,15 +17,13 @@ from django.utils.datastructures import SortedDict
 from django.views.decorators.http import require_POST
 from django.contrib import auth
 from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
-from django.utils import simplejson
-from django.utils.functional import Promise
-from django.utils.encoding import force_unicode
 from django.utils.http import urlquote_plus
 from django.views.decorators import cache
 from django.utils import translation
 from django.utils.translation import ugettext as _
 from django.views.generic.list_detail import object_list
 
+from ajaxable.utils import LazyEncoder, JSONResponse
 from catalogue import models
 from catalogue import forms
 from catalogue.utils import split_tags, AttachmentHttpResponse, async_build_pdf
@@ -39,23 +37,6 @@ from os import path
 staff_required = user_passes_test(lambda user: user.is_staff)
 
 
-class LazyEncoder(simplejson.JSONEncoder):
-    def default(self, obj):
-        if isinstance(obj, Promise):
-            return force_unicode(obj)
-        return obj
-
-# shortcut for JSON reponses
-class JSONResponse(HttpResponse):
-    def __init__(self, data={}, callback=None, **kwargs):
-        # get rid of mimetype
-        kwargs.pop('mimetype', None)
-        data = simplejson.dumps(data)
-        if callback:
-            data = callback + "(" + data + ");" 
-        super(JSONResponse, self).__init__(data, mimetype="application/json", **kwargs)
-
-
 def catalogue(request):
     tags = models.Tag.objects.exclude(
         category__in=('set', 'book')).exclude(book_count=0)
@@ -663,45 +644,6 @@ def delete_shelf(request, slug):
         return HttpResponseRedirect('/')
 
 
-# ==================
-# = Authentication =
-# ==================
-@require_POST
-@cache.never_cache
-def login(request):
-    form = AuthenticationForm(data=request.POST, prefix='login')
-    if form.is_valid():
-        auth.login(request, form.get_user())
-        response_data = {'success': True, 'errors': {}}
-    else:
-        response_data = {'success': False, 'errors': form.errors}
-    return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
-
-
-@require_POST
-@cache.never_cache
-def register(request):
-    registration_form = UserCreationForm(request.POST, prefix='registration')
-    if registration_form.is_valid():
-        user = registration_form.save()
-        user = auth.authenticate(
-            username=registration_form.cleaned_data['username'],
-            password=registration_form.cleaned_data['password1']
-        )
-        auth.login(request, user)
-        response_data = {'success': True, 'errors': {}}
-    else:
-        response_data = {'success': False, 'errors': registration_form.errors}
-    return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
-
-
-@cache.never_cache
-def logout_then_redirect(request):
-    auth.logout(request)
-    return HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
-
-
-
 # =========
 # = Admin =
 # =========
@@ -726,14 +668,6 @@ def import_book(request):
         return HttpResponse(_("Error importing file: %r") % book_import_form.errors)
 
 
-
-def clock(request):
-    """ Provides server time for jquery.countdown,
-    in a format suitable for Date.parse()
-    """
-    return HttpResponse(datetime.now().strftime('%Y/%m/%d %H:%M:%S'))
-
-
 # info views for API
 
 def book_info(request, id, lang='pl'):
diff --git a/apps/pdcounter/urls.py b/apps/pdcounter/urls.py
deleted file mode 100644 (file)
index 2325137..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
-# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
-#
-from django.conf.urls.defaults import *
-
-
-urlpatterns = patterns('catalogue.views',
-    url(r'^$', 'main_page', name='main_page'),
-    url(r'^polki/(?P<shelf>[a-zA-Z0-9-]+)/formaty/$', 'shelf_book_formats', name='shelf_book_formats'),
-    url(r'^polki/(?P<shelf>[a-zA-Z0-9-]+)/(?P<book>[a-zA-Z0-9-0-]+)/usun$', 'remove_from_shelf', name='remove_from_shelf'),
-    url(r'^polki/$', 'user_shelves', name='user_shelves'),
-    url(r'^polki/(?P<slug>[a-zA-Z0-9-]+)/usun/$', 'delete_shelf', name='delete_shelf'),
-    url(r'^polki/(?P<slug>[a-zA-Z0-9-]+)\.zip$', 'download_shelf', name='download_shelf'),
-    url(r'^lektury/', 'book_list', name='book_list'),
-    url(r'^audiobooki/', 'audiobook_list', name='audiobook_list'),
-    url(r'^daisy/', 'daisy_list', name='daisy_list'),
-    url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)/polki/', 'book_sets', name='book_shelves'),
-    url(r'^polki/nowa/$', 'new_set', name='new_set'),
-    url(r'^tags/$', 'tags_starting_with', name='hint'),
-    url(r'^jtags/$', 'json_tags_starting_with', name='jhint'),
-    url(r'^szukaj/$', 'search', name='search'),
-
-    # tools
-    url(r'^zegar/$', 'clock', name='clock'),
-    url(r'^xmls.zip$', 'xmls', name='xmls'),
-    url(r'^epubs.tar$', 'epubs', name='epubs'),
-
-    # Public interface. Do not change this URLs.
-    url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)\.html$', 'book_text', name='book_text'),
-    url(r'^lektura/(?P<slug>[a-zA-Z0-9-]+)/$', 'book_detail', name='book_detail'),
-    url(r'^lektura/(?P<book_slug>[a-zA-Z0-9-]+)/motyw/(?P<theme_slug>[a-zA-Z0-9-]+)/$',
-        'book_fragments', name='book_fragments'),
-    url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'),
-)
-
index efcfe95..b07ee11 100644 (file)
@@ -7,16 +7,14 @@
 from django.template import RequestContext
 from django.shortcuts import render_to_response, get_object_or_404
 from pdcounter import models
-from catalogue import forms
 from suggest.forms import PublishingSuggestForm
 
 
 def book_stub_detail(request, slug):
     book = get_object_or_404(models.BookStub, slug=slug)
     pd_counter = book.pd
-    form = forms.SearchForm()
 
-    pubsuggest_form = PublishingSuggestForm(
+    form = PublishingSuggestForm(
             initial={"books": u"%s — %s, \n" % (book.author, book.title)})
 
     return render_to_response('pdcounter/book_stub_detail.html', locals(),
@@ -26,9 +24,8 @@ def book_stub_detail(request, slug):
 def author_detail(request, slug):
     author = get_object_or_404(models.Author, slug=slug)
     pd_counter = author.goes_to_pd()
-    form = forms.SearchForm()
 
-    pubsuggest_form = PublishingSuggestForm(initial={"books": author.name + ", \n"})
+    form = PublishingSuggestForm(initial={"books": author.name + ", \n"})
 
     return render_to_response('pdcounter/author_detail.html', locals(),
         context_instance=RequestContext(request))
index 14cec03..b7c614f 100644 (file)
@@ -9,14 +9,49 @@ from django.core.validators import email_re
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext
 
-
-from suggest.models import PublishingSuggestion
+from suggest.models import PublishingSuggestion, Suggestion
 
 
 class SuggestForm(forms.Form):
     contact = forms.CharField(label=_('Contact'), max_length=120, required=False)
     description = forms.CharField(label=_('Description'), widget=forms.Textarea, required=True)
 
+    def save(self, request):
+        contact = self.cleaned_data['contact']
+        description = self.cleaned_data['description']
+
+        suggestion = Suggestion(contact=contact,
+            description=description, ip=request.META['REMOTE_ADDR'])
+        if request.user.is_authenticated():
+            suggestion.user = request.user
+        suggestion.save()
+
+        mail_managers(u'Nowa sugestia na stronie WolneLektury.pl', u'''\
+Zgłoszono nową sugestię w serwisie WolneLektury.pl.
+http://%(site)s%(url)s
+
+Użytkownik: %(user)s
+Kontakt: %(contact)s
+
+%(description)s''' % {
+            'site': Site.objects.get_current().domain,
+            'url': reverse('admin:suggest_suggestion_change', args=[suggestion.id]),
+            'user': str(request.user) if request.user.is_authenticated() else '',
+            'contact': contact,
+            'description': description,
+            }, fail_silently=True)
+
+        if email_re.match(contact):
+            send_mail(u'[WolneLektury] ' + _(u'Thank you for your suggestion.'),
+                    _(u"""\
+Thank you for your comment on WolneLektury.pl.
+The suggestion has been referred to the project coordinator.""") +
+u"""
+
+-- 
+""" + _(u'''Message sent automatically. Please do not reply.'''),
+                    'no-reply@wolnelektury.pl', [contact], fail_silently=True)
+
 
 class PublishingSuggestForm(forms.Form):
     contact = forms.CharField(label=_('Contact'), max_length=120, required=False)
index a6ed283..3e71000 100755 (executable)
@@ -1,14 +1,16 @@
 {% load i18n %}
-<h2>{% trans "Didn't find a book? Make a suggestion." %}</h2>
+<h1>{% trans "Didn't find a book? Make a suggestion." %}</h1>
+
 <form id='suggest-publishing-form' action="{% url suggest_publishing %}" method="post" accept-charset="utf-8" class="cuteform">
+{% csrf_token %}
 <ol>
-    <li><span class="error">{{ pubsuggest_form.contact.errors }}</span><label for="id_contact">{{ pubsuggest_form.contact.label }}</label> {{ pubsuggest_form.contact }}</li>
+    <li><span class="error">{{ form.contact.errors }}</span><label for="id_contact">{{ form.contact.label }}</label> {{ form.contact }}</li>
 
     <li>{% trans "I'd like to find in WolneLektury.pl these…" %}</li>
 
-    <li><span class="error">{{ pubsuggest_form.books.errors }}</span><label for="id_books">{{ pubsuggest_form.books.label }}:</label> {{ pubsuggest_form.books }}</li>
+    <li><span class="error">{{ form.books.errors }}</span><label for="id_books">{{ form.books.label }}:</label> {{ form.books }}</li>
 
-    <li><span class="error">{{ pubsuggest_form.audiobooks.errors }}</span><label for="id_audiobooks">{{ pubsuggest_form.audiobooks.label }}:</label> {{ pubsuggest_form.audiobooks }}</li>
+    <li><span class="error">{{ form.audiobooks.errors }}</span><label for="id_audiobooks">{{ form.audiobooks.label }}:</label> {{ form.audiobooks }}</li>
 
     <li><input type="submit" value="{% trans "Send report" %}"/></li>
 </ol>
diff --git a/apps/suggest/templates/publishing_suggest_full.html b/apps/suggest/templates/publishing_suggest_full.html
deleted file mode 100755 (executable)
index c5d8c28..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block title %}Sugestia do planu wydawniczego w WolneLektury.pl{% endblock %}
-
-{% block metadescription %}Sugestia do planu wydawniczego.{% endblock %}
-
-{% block bodyid %}suggest-publishing{% endblock %}
-
-{% block body %}
-    <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
-    </form>
-
-    <div id="books-list">
-    </div>
-
-    <div class="column-right block-form">
-        {% include "publishing_suggest.html" %}
-        {% if response_data.message %}
-            <p>{{ response_data.message }}</p>
-        {% endif %}
-    </div>
-{% endblock %}
diff --git a/apps/suggest/templates/suggest.html b/apps/suggest/templates/suggest.html
deleted file mode 100644 (file)
index c7fdd81..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{% load i18n %}
-<h2>{% trans "Report a bug or suggestion" %}</h2>
-<form id='suggest-form' action="{% url suggest.views.report %}" method="post" accept-charset="utf-8" class="cuteform">
-<ol>
-    <li><label for="id_contact">{{ form.contact.label }}</label> {{ form.contact }}</li>
-       <li><label for="id_description">{{ form.description.label }}</label> {{ form.description }}</li>
-    <li><input type="submit" value="{% trans "Send report" %}"/></li>
-</ol>
-</form>
\ No newline at end of file
index b769e49..ae4ac15 100644 (file)
@@ -3,21 +3,10 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 from django.conf.urls.defaults import *
-from django.views.generic.simple import direct_to_template
-from suggest.forms import SuggestForm, PublishingSuggestForm
-from suggest.views import PublishingSuggestionFormView
+from suggest import views
 
 urlpatterns = patterns('',
-    url(r'^$', 'django.views.generic.simple.direct_to_template',
-        {'template': 'suggest.html', 'extra_context': {'form': SuggestForm }}, name='suggest'),
-    url(r'^wyslij/$', 'suggest.views.report', name='report'),
-
-    #url(r'^plan/$', 'suggest.views.publishing', name='suggest_publishing'),
-    url(r'^plan/$', PublishingSuggestionFormView(), name='suggest_publishing'),
-    #url(r'^plan_block/$', 'django.views.generic.simple.direct_to_template',
-    #    {'template': 'publishing_suggest.html', 
-    #            'extra_context': {'pubsuggest_form': PublishingSuggestForm }},
-    #    name='suggest_publishing'),
-    #url(r'^plan/wyslij/$', 'suggest.views.publishing_commit', name='suggest_publishing_commit'),
+    url(r'^$', views.SuggestionFormView(), name='suggest'),
+    url(r'^plan/$', views.PublishingSuggestionFormView(), name='suggest_publishing'),
 )
 
index 24ee12c..15b65f2 100644 (file)
 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
-from django.core.mail import send_mail, mail_managers
-from django.core.urlresolvers import reverse
-from django.core.validators import email_re
-from django.http import HttpResponse, HttpResponseRedirect
-from django.utils.translation import ugettext as _
-from django.views.decorators import cache
-from django.views.decorators.http import require_POST
-from django.contrib.sites.models import Site
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.utils.translation import ugettext_lazy as _
 
-from catalogue.forms import SearchForm
+from ajaxable.utils import AjaxableFormView
 from suggest import forms
 from suggest.models import Suggestion, PublishingSuggestion
 
 
-# FIXME - shouldn't be in catalogue
-from catalogue.views import LazyEncoder
-
-
-class AjaxableFormView(object):
-    formClass = None
-    template = None
-    ajax_template = None
-    formname = None
-
-    def __call__(self, request):
-        """
-            A view displaying a form, or JSON if `ajax' GET param is set.
-        """
-        ajax = request.GET.get('ajax', False)
-        if request.method == "POST":
-            form = self.formClass(request.POST)
-            if form.is_valid():
-                form.save(request)
-                response_data = {'success': True, 'message': _('Report was sent successfully.')}
-            else:
-                response_data = {'success': False, 'errors': form.errors}
-            if ajax:
-                return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
-        else:
-            form = self.formClass()
-            response_data = None
-
-        template = self.ajax_template if ajax else self.template
-        return render_to_response(template, {
-                self.formname: form, 
-                "form": SearchForm(),
-                "response_data": response_data,
-            },
-            context_instance=RequestContext(request))
-
-
 class PublishingSuggestionFormView(AjaxableFormView):
-    formClass = forms.PublishingSuggestForm
-    ajax_template = "publishing_suggest.html"
-    template = "publishing_suggest_full.html"
-    formname = "pubsuggest_form"
-
-
-@require_POST
-@cache.never_cache
-def report(request):
-    suggest_form = forms.SuggestForm(request.POST)
-    if suggest_form.is_valid():
-        contact = suggest_form.cleaned_data['contact']
-        description = suggest_form.cleaned_data['description']
-
-        suggestion = Suggestion(contact=contact,
-            description=description, ip=request.META['REMOTE_ADDR'])
-        if request.user.is_authenticated():
-            suggestion.user = request.user
-        suggestion.save()
-
-        mail_managers(u'Nowa sugestia na stronie WolneLektury.pl', u'''\
-Zgłoszono nową sugestię w serwisie WolneLektury.pl.
-http://%(site)s%(url)s
-
-Użytkownik: %(user)s
-Kontakt: %(contact)s
-
-%(description)s''' % {
-            'site': Site.objects.get_current().domain,
-            'url': reverse('admin:suggest_suggestion_change', args=[suggestion.id]),
-            'user': str(request.user) if request.user.is_authenticated() else '',
-            'contact': contact,
-            'description': description,
-            }, fail_silently=True)
-
-        if email_re.match(contact):
-            send_mail(u'[WolneLektury] ' + _(u'Thank you for your suggestion.'),
-                    _(u"""\
-Thank you for your comment on WolneLektury.pl.
-The suggestion has been referred to the project coordinator.""") +
-u"""
+    form_class = forms.PublishingSuggestForm
+    title = _('Report a bug or suggestion')
+    template = "publishing_suggest.html"
+    success_message = _('Report was sent successfully.')
 
--- 
-""" + _(u'''Message sent automatically. Please do not reply.'''),
-                    'no-reply@wolnelektury.pl', [contact], fail_silently=True)
 
-        response_data = {'success': True, 'message': _('Report was sent successfully.')}
-    else:
-        response_data = {'success': False, 'errors': suggest_form.errors}
-    return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
+class SuggestionFormView(AjaxableFormView):
+    form_class = forms.SuggestForm
+    title = _('Report a bug or suggestion')
+    submit = _('Send report')
+    success_message = _('Report was sent successfully.')
index 91254ab..40b45be 100644 (file)
@@ -110,7 +110,7 @@ TEMPLATE_DIRS = [
     path.join(PROJECT_DIR, 'templates'),
 ]
 
-LOGIN_URL = '/uzytkownicy/login/'
+LOGIN_URL = '/uzytkownicy/zaloguj/'
 
 LOGIN_REDIRECT_URL = '/'
 
@@ -137,6 +137,7 @@ INSTALLED_APPS = [
     'modeltranslation',
 
     # our
+    'ajaxable',
     'api',
     'catalogue',
     'chunks',
@@ -161,11 +162,14 @@ CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True
 # CSS and JavaScript file groups
 COMPRESS_CSS = {
     'all': {
-        #'source_filenames': ('css/master.css', 'css/jquery.autocomplete.css', 'css/jquery.countdown.css', 'css/master.plain.css', 'css/sponsors.css', 'css/facelist_2-0.css',),
+        #'source_filenames': ('css/master.css', 'css/jquery.autocomplete.css', 'css/master.plain.css', 'css/facelist_2-0.css',),
         'source_filenames': [
+            'css/jquery.countdown.css', 
+
             'css/base.css',
             'css/header.css',
             'css/main_page.css',
+            'css/dialogs.css',
             'css/book_box.css',
             'css/sponsors.css',
         ],
@@ -185,15 +189,20 @@ COMPRESS_JS = {
     'base': {
         'source_filenames': (
             'js/jquery.cycle.min.js',
-
+            'js/jquery.jqmodal.js',
+            'js/jquery.form.js',
+            'js/jquery.countdown.js', 'js/jquery.countdown-pl.js',
+            'js/jquery.countdown-de.js', 'js/jquery.countdown-uk.js',
+            'js/jquery.countdown-es.js', 'js/jquery.countdown-lt.js',
+            'js/jquery.countdown-ru.js', 'js/jquery.countdown-fr.js',
+
+            'js/locale.js',
+            'js/dialogs.js',
             'js/sponsors.js',
-        
-            #~ 'js/jquery.autocomplete.js', 'js/jquery.form.js',
-            #~ 'js/jquery.countdown.js', 'js/jquery.countdown-pl.js',
-            #~ 'js/jquery.countdown-de.js', 'js/jquery.countdown-uk.js',
-            #~ 'js/jquery.countdown-es.js', 'js/jquery.countdown-lt.js',
-            #~ 'js/jquery.countdown-ru.js', 'js/jquery.countdown-fr.js',
-            #~ 'js/jquery.jqmodal.js', 'js/jquery.labelify.js', 'js/catalogue.js',
+            'js/pdcounter.js',
+
+            #~ 'js/jquery.autocomplete.js',
+            #~ 'js/jquery.labelify.js', 'js/catalogue.js',
             ),
         'output_filename': 'js/base?.min.js',
     },
diff --git a/wolnelektury/static/css/dialogs.css b/wolnelektury/static/css/dialogs.css
new file mode 100755 (executable)
index 0000000..dc76e6c
--- /dev/null
@@ -0,0 +1,87 @@
+.cuteform ol, .cuteform ul {
+    padding: 0;
+    margin: 0;
+    list-style: none;
+}
+
+.cuteform ol li, .cuteform ul li {
+    margin-top: 0.7em;
+}
+
+.cuteform label {
+    display: block;
+}
+
+.cuteform span.help-text {
+    display: block;
+    font-size: 0.8em;
+    color: #999;
+}
+
+.cuteform .error {
+    color: #BF3024;
+    display: block;
+}
+
+
+.jqmOverlay { background-color: #000; }
+
+
+.dialog-window {
+    position: absolute;
+    display: none;
+    background-color: transparent;
+    margin-top: -0.5em;
+    margin-left: 1em;
+}
+
+.dialog-window div.header {
+    width: 4em;
+    background-color: #FFF;
+    border-right: 0.3em solid #DDD;
+    padding: 0.5em 1em 0.5em 1em;
+    right: 0;
+    left: auto;
+    float: right;
+    text-align: center;
+}
+
+
+.dialog-window div.target {
+    background-color: #FFF;
+    color: black;
+    border-right: 0.3em solid #DDD;
+    border-bottom: 0.3em solid #DDD;
+    padding: 1em;
+    clear: both;
+}
+
+.dialog-window h1 {
+    font-size: 1.2em;
+}
+
+
+#login-window {
+    width: 24em;
+}
+#register-window {
+    width: 24em;
+}
+
+#suggest-window {
+    width: 24em;
+}
+
+#suggest-window textarea {
+    width: 19em;
+    height: 6em;
+}
+
+#suggest-publishing-window {
+    width: 26em;
+}
+
+#suggest-publishing-window textarea {
+       width: 21em;
+    height: 3em;
+}
index 28a1148..94401cb 100755 (executable)
@@ -1,6 +1,6 @@
 #big-cite {
     background-color: white;
-    padding: 8em;
+    padding: 4em 12em;
     margin: 0;
 }
 
diff --git a/wolnelektury/static/js/dialogs.js b/wolnelektury/static/js/dialogs.js
new file mode 100755 (executable)
index 0000000..0793a7f
--- /dev/null
@@ -0,0 +1,56 @@
+(function($) {
+    $(function() {
+
+        // create containers for all ajaxable form links
+        $('.ajaxable').each(function() {
+            var $window = $("#ajaxable-window").clone();
+            $window.attr("id", this.id + "-window");
+            $('body').append($window);
+
+            var trigger = '#' + this.id;
+
+            var href = $(this).attr('href');
+            if (href.search('\\?') != -1)
+                href += '&ajax=1';
+            else href += '?ajax=1';
+
+            $window.jqm({
+                ajax: href,
+                ajaxText: '<p><img src="' + STATIC_URL + 'img/indicator.gif" alt="*"/> ' + gettext("Loading") + '</p>',
+                target: $('.target', $window)[0],
+                overlay: 60,
+                trigger: trigger,
+                onShow: function(hash) {
+                    var offset = $(hash.t).offset();
+                    hash.w.css({position: 'absolute', left: offset.left - hash.w.width() + $(hash.t).width(), top: offset.top});
+                    $('.header', hash.w).css({width: $(hash.t).width()});
+                    hash.w.show();
+                },
+                onLoad: function(hash) {
+                    $('form', hash.w).each(function() {this.action += '?ajax=1';});
+                    $('form', hash.w).ajaxForm({
+                        dataType: 'json',
+                        target: $('.target', $window),
+                        success: function(response) {
+                            if (response.success) {
+                                $('.target', $window).text(response.message);
+                                setTimeout(function() { $window.jqmHide() }, 1000)
+                            }
+                            else {
+                                $('.error', $window).remove();
+                                $.each(response.errors, function(id, errors) {
+                                    $('#id_' + id, $window).before('<span class="error">' + errors[0] + '</span>');
+                                });
+                                $('input[type=submit]', $window).removeAttr('disabled');
+                                return false;
+                            }
+                        }
+                    });
+                }
+            });
+        });
+
+
+    });
+})(jQuery)
+
index 248bb19..3aac816 100644 (file)
 /*
  * jqModal - Minimalist Modaling with jQuery
+ *   (http://dev.iceburg.net/jquery/jqModal/)
  *
- * Copyright (c) 2007 Brice Burgess <bhb@iceburg.net>, http://www.iceburg.net
- * Licensed under the MIT License:
- * http://www.opensource.org/licenses/mit-license.php
- *
- * $Version: 2007.??.?? +r12 beta
- * Requires: jQuery 1.1.3+
+ * Copyright (c) 2007,2008 Brice Burgess <bhb@iceburg.net>
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ * 
+ * $Version: 03/01/2009 +r14
  */
 (function($) {
-/**
- * Initialize a set of elements as "modals". Modals typically are popup dialogs,
- * notices, modal windows, and image containers. An expando ("_jqm") containing
- * the UUID or "serial" of the modal is added to each element. This expando helps
- * reference the modal's settings in the jqModal Hash Object (jQuery.jqm.hash)
- *
- * Accepts a parameter object with the following modal settings;
- *
- * (Integer) zIndex - Desired z-Index of the modal. This setting does not override (has no effect on) preexisting z-Index styling (set via CSS or inline style).
- * (Integer) overlay - [0-100] Translucency percentage (opacity) of the body covering overlay. Set to 0 for NO overlay, and up to 100 for a 100% opaque overlay.
- * (String) overlayClass - This class is applied to the body covering overlay. Allows CSS control of the overlay look (tint, background image, etc.).
- * (String) closeClass - A close trigger is added to all elements matching this class within the modal.
- * (Mixed) trigger - An open trigger is added to all matching elements within the DOM. Trigger can be a selector String, a jQuery collection of elements, a DOM element, or a False boolean.
- * (Mixed) ajax - If not false; The URL (string) to load content from via an AJAX request.
- *                If ajax begins with a "@", the URL is extracted from the requested attribute of the triggering element.
- * (Mixed) target - If not false; The element within the modal to load the ajax response (content) into. Allows retention of modal design (e.g. framing and close elements are not overwritten by the AJAX response).
- *                  Target may be a selector string, jQuery collection of elements, or a DOM element -- but MUST exist within (as a child of) the modal.
- * (Boolean) modal - If true, user interactivity will be locked to the modal window until closed.
- * (Boolean) toTop - If true, modal will be posistioned as a first child of the BODY element when opened, and its DOM posistion restored when closed. This aids in overcoming z-Index stacking order/containment issues where overlay covers whole page *including* modal.
- * (Mixed) onShow - User defined callback function fired when modal opened.
- * (Mixed) onHide - User defined callback function fired when modal closed.
- * (Mixed) onLoad - User defined callback function fired when ajax content loads.
- *
- * @name jqm
- * @param Map options User defined settings for the modal(s).
- * @type jQuery
- * @cat Plugins/jqModal
- */
-$.fn.jqm=function(p){
-var o = {
-zIndex: 3000,
+$.fn.jqm=function(o){
+var p={
 overlay: 50,
 overlayClass: 'jqmOverlay',
 closeClass: 'jqmClose',
 trigger: '.jqModal',
-ajax: false,
-target: false,
-modal: false,
-toTop: false,
-onShow: false,
-onHide: false,
-onLoad: false
+ajax: F,
+ajaxText: '',
+target: F,
+modal: F,
+toTop: F,
+onShow: F,
+onHide: F,
+onLoad: F
 };
-
-// For each element (aka "modal") $.jqm() has been called on;
-//  IF the _jqm expando exists, return (do nothing)
-//  ELSE increment serials and add _jqm expando to element ("serialization")
-//    *AND*...
-return this.each(function(){if(this._jqm)return;s++;this._jqm=s;
-
-// ... Add this element's serial to the jqModal Hash Object
-//  Hash is globally accessible via jQuery.jqm.hash. It consists of;
-//   c: {obj} config/options
-//   a: {bool} active state (true: active/visible, false: inactive/hidden)
-//   w: {JQ DOM Element} The modal element (window/dialog/notice/etc. container)
-//   s: {int} The serial number of this modal (same as "H[s].w[0]._jqm")
-//   t: {DOM Element} The triggering element
-// *AND* ...
-H[s]={c:$.extend(o,p),a:false,w:$(this).addClass('jqmID'+s),s:s};
-
-// ... Attach events to trigger showing of this modal
-o.trigger&&$(this).jqmAddTrigger(o.trigger);
+return this.each(function(){if(this._jqm)return H[this._jqm].c=$.extend({},H[this._jqm].c,o);s++;this._jqm=s;
+H[s]={c:$.extend(p,$.jqm.params,o),a:F,w:$(this).addClass('jqmID'+s),s:s};
+if(p.trigger)$(this).jqmAddTrigger(p.trigger);
 });};
 
-// Adds behavior to triggering elements via the hide-show (HS) function.
-//
-$.fn.jqmAddClose=function(e){return HS(this,e,'jqmHide');};
-$.fn.jqmAddTrigger=function(e){return HS(this,e,'jqmShow');};
-
-// Hide/Show a modal -- first check if it is already shown or hidden via the toggle state (H[{modal serial}].a)
-$.fn.jqmShow=function(t){return this.each(function(){!H[this._jqm].a&&$.jqm.open(this._jqm,t)});};
-$.fn.jqmHide=function(t){return this.each(function(){H[this._jqm].a&&$.jqm.close(this._jqm,t)});};
+$.fn.jqmAddClose=function(e){return hs(this,e,'jqmHide');};
+$.fn.jqmAddTrigger=function(e){return hs(this,e,'jqmShow');};
+$.fn.jqmShow=function(t){return this.each(function(){t=t||window.event;$.jqm.open(this._jqm,t);});};
+$.fn.jqmHide=function(t){return this.each(function(){t=t||window.event;$.jqm.close(this._jqm,t)});};
 
 $.jqm = {
 hash:{},
-
-// Function is executed by $.jqmShow to show a modal
-// s: {INT} serial of modal
-// t: {DOM Element} the triggering element
-
-// set local shortcuts
-//  h: {obj} this Modal's "hash"
-//  c: {obj} (h.c) config/options
-//  cc: {STR} closing class ('.'+h.c.closeClass)
-//  z: {INT} z-Index of Modal. If the Modal (h.w) has the z-index style set it will use this value before defaulting to the one passed in the config (h.c.zIndex)
-//  o: The overlay object
-// mark this modal as active (h.a === true)
-// set the triggering object (h.t) and the modal's z-Index.
-open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=/^\d+$/.test(h.w.css('z-index'))&&h.w.css('z-index')||c.zIndex,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});h.t=t;h.a=true;h.w.css('z-index',z);
-
- // IF the modal argument was passed as true;
- //    Bind the Keep Focus Function if no other Modals are open (!A[0]),
- //    Add this modal to the opened modals stack (A) for nested modal support,
- //    and Mark overlay to show wait cursor when mouse hovers over it.
- if(c.modal) {!A[0]&&F('bind');A.push(s);o.css('cursor','wait');}
-
- // ELSE IF an overlay was requested (translucency set greater than 0);
- //    Attach a Close event to overlay to hide modal when overlay is clicked.
+open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=(parseInt(h.w.css('z-index'))),z=(z>0)?z:3000,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});if(h.a)return F;h.t=t;h.a=true;h.w.css('z-index',z);
+ if(c.modal) {if(!A[0])L('bind');A.push(s);}
  else if(c.overlay > 0)h.w.jqmAddClose(o);
+ else o=F;
 
- // ELSE disable the overlay
- else o=false;
+ h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):F;
+ if(ie6){$('html,body').css({height:'100%',width:'100%'});if(o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}}
 
- // Add the Overlay to BODY if not disabled.
- h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):false;
-
- // IF IE6;
- //  Set the Overlay to 100% height/width, and fix-position it via JS workaround
- if(ie6&&$('html,body').css({height:'100%',width:'100%'})&&o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}
-
- // IF the modal's content is to be loaded via ajax;
- //  determine the target element {JQ} to recieve content (r),
- //  determine the URL {STR} to load content from (u)
  if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
+  r.html(c.ajaxText).load(u,function(){if(c.onLoad)c.onLoad.call(this,h);if(cc)h.w.jqmAddClose($(cc,h.w));e(h);});}
+ else if(cc)h.w.jqmAddClose($(cc,h.w));
 
-  // Load the Content (and once loaded);
-   // Fire the onLoad callback (if exists),
-   // Attach closing events to elements inside the modal that match the closingClass,
-   // and Execute the jqModal default Open Callback
-  r.load(u,function(){c.onLoad&&c.onLoad.call(this,h);cc&&h.w.jqmAddClose($(cc,h.w));O(h);});}
-
- // ELSE the modal content is NOT to be loaded via ajax;
- //  Attach closing events to elements inside the modal that match the closingClass
- else cc&&h.w.jqmAddClose($(cc,h.w));
-
- // IF toTop was passed and an overlay exists;
- //  Remember the DOM posistion of the modal by inserting a tagged (matching serial) <SPAN> before the modal
- //  Move the Modal from its current position to a first child of the body tag (after the overlay)
- c.toTop&&h.o&&h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);
-
- // Execute user defined onShow callback, or else show (make visible) the modal.
- // Execute the jqModal default Open Callback.
- // Return false to prevent trigger click from being followed.
- (c.onShow)?c.onShow(h):h.w.show();O(h);return false;
-
+ if(c.toTop&&h.o)h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);        
+ (c.onShow)?c.onShow(h):h.w.show();e(h);return F;
 },
-
-// Function is executed by $.jqmHide to hide a modal
-  // mark this modal as inactive (h.a === false)
-close:function(s){var h=H[s];h.a=false;
- // If modal, remove from modal stack.
-   // If no modals in modal stack, unbind the Keep Focus Function
- if(h.c.modal){A.pop();!A[0]&&F('unbind');}
-
- // IF toTop was passed and an overlay exists;
- //  Move modal back to its previous ("remembered") position.
- h.c.toTop&&h.o&&$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
-
- // Execute user defined onHide callback, or else hide (make invisible) the modal and remove the overlay.
- if(h.c.onHide)h.c.onHide(h);else{h.w.hide()&&h.o&&h.o.remove()}return false;
-}};
-
-// set jqModal scope shortcuts;
-//  s: {INT} serials placeholder
-//  H: {HASH} shortcut to jqModal Hash Object
-//  A: {ARRAY} Array of active/visible modals
-//  ie6: {bool} True if client browser is Internet Explorer 6
-//  i: {JQ, DOM Element} iframe placeholder used to prevent active-x bleedthrough in IE6
-//    NOTE: It is important to include the iframe styling (iframe.jqm) in your CSS!
-//     *AND* ...
-var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
-
-//  O: The jqModal default Open Callback;
-//    IF ie6; Add the iframe to the overlay (if overlay exists) OR to the modal (if an iframe doesn't already exist from a previous opening)
-//    Execute the Modal Focus Function
-O=function(h){if(ie6)h.o&&h.o.html('<p style="width:100%;height:100%"/>').prepend(i)||(!$('iframe.jqm',h.w)[0]&&h.w.prepend(i)); f(h);},
-
-//  f: The Modal Focus Function;
-//    Attempt to focus the first visible input within the modal
-f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(e){}},
-
-//  F: The Keep Focus Function;
-//    Binds or Unbinds (t) the Focus Examination Function to keypresses and clicks
-F=function(t){$()[t]("keypress",x)[t]("keydown",x)[t]("mousedown",x);},
-
-//  x: The Focus Examination Function;
-//    Fetch the current modal's Hash as h (supports nested modals)
-//    Determine if the click/press falls within the modal. If not (r===true);
-//      call the Modal Focus Function and prevent click/press follow-through (return false [!true])
-//      ELSE if so (r===false); follow event (return true [!false])
-x=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);r&&f(h);return !r;},
-
-// hide-show function; assigns click events to trigger elements that
-//   hide, show, or hide AND show modals.
-
-// Expandos (jqmShow and/or jqmHide) are added to all trigger elements.
-// These Expandos hold an array of modal serials {INT} to show or hide.
-
-//  w: {DOM Element} The modal element (window/dialog/notice/etc. container)
-//  e: {DOM Elemet||jQ Selector String} The triggering element
-//  y: {String} Type (jqmHide||jqmShow)
-
-//  s: {array} the serial number of passed modals, calculated below;
-HS=function(w,e,y){var s=[];w.each(function(){s.push(this._jqm)});
-
-// for each triggering element attach the jqmHide or jqmShow expando (y)
-//  or else expand the expando with the current serial array
- $(e).each(function(){if(this[y])$.extend(this[y],s);
-
- // Assign a click event on the trigger element which examines the element's
- //  jqmHide/Show expandos and attempts to execute $.jqmHide/Show on matching modals
- else{this[y]=s;$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return false;});}});return w;};
+close:function(s){var h=H[s];if(!h.a)return F;h.a=F;
+ if(A[0]){A.pop();if(!A[0])L('unbind');}
+ if(h.c.toTop&&h.o)$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
+ if(h.c.onHide)h.c.onHide(h);else{h.w.hide();if(h.o)h.o.remove();} return F;
+},
+params:{}};
+var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),F=false,
+i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
+e=function(h){if(ie6)if(h.o)h.o.html('<p style="width:100%;height:100%"/>').prepend(i);else if(!$('iframe.jqm',h.w)[0])h.w.prepend(i); f(h);},
+f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(_){}},
+L=function(t){$()[t]("keypress",m)[t]("keydown",m)[t]("mousedown",m);},
+m=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);if(r)f(h);return !r;},
+hs=function(w,t,c){return w.each(function(){var s=this._jqm;$(t).each(function() {
+ if(!this[c]){this[c]=[];$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return F;});}this[c].push(s);});});};
 })(jQuery);
\ No newline at end of file
diff --git a/wolnelektury/static/js/locale.js b/wolnelektury/static/js/locale.js
new file mode 100755 (executable)
index 0000000..e765548
--- /dev/null
@@ -0,0 +1,29 @@
+var LOCALE_TEXTS = {
+    "pl": {
+        "Loading": "Ładowanie"
+    },
+    "de": {
+        "Loading": "Herunterladen"
+    },
+    "fr": {
+        "Loading": "Chargement"
+    },
+    "en": {
+        "Loading": "Loading"
+    },
+    "ru": {
+        "Loading": "Загрузка"
+    },
+    "es": {
+        "Loading": "Cargando"
+    },
+    "lt":{
+        "Loading": "Krovimas"
+    },
+    "uk":{
+        "Loading": "Завантажується"
+    }
+}
+function gettext(text) {
+    return LOCALE_TEXTS[LANGUAGE_CODE][text];
+}
\ No newline at end of file
diff --git a/wolnelektury/static/js/pdcounter.js b/wolnelektury/static/js/pdcounter.js
new file mode 100755 (executable)
index 0000000..00c0742
--- /dev/null
@@ -0,0 +1,36 @@
+(function($) {
+    $(function() {
+
+
+        $('#countdown').each(function() {
+            var $this = $(this);
+
+            var serverTime = function() {
+                var time = null;
+                $.ajax({url: '/zegar/',
+                    async: false, dataType: 'text',
+                    success: function(text) {
+                        time = new Date(text);
+                    }, error: function(http, message, exc) {
+                        time = new Date();
+                }});
+                return time;
+            }
+
+            if (LANGUAGE_CODE != 'en') {
+                $.countdown.setDefaults($.countdown.regional[LANGUAGE_CODE]);
+            }
+            else {
+                $.countdown.setDefaults($.countdown.regional['']);
+            }
+
+            var d = new Date($this.attr('data-year'), 0, 1);
+            function re() {location.reload()};
+            $this.countdown({until: d, format: 'ydHMS', serverSync: serverTime,
+                onExpiry: re, alwaysExpire: true});
+
+        });
+
+
+    });
+})(jQuery)
\ No newline at end of file
diff --git a/wolnelektury/templates/auth/login.html b/wolnelektury/templates/auth/login.html
deleted file mode 100644 (file)
index a168ff7..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block title %}{% trans "Sign in" %} / {% trans "Register on"%} WolneLektury.pl{% endblock %}
-
-{% block body %}
-    <h1>{% trans "Sign in" %} / {% trans "Register"%}</h1>
-    <form action="." method="get" accept-charset="utf-8" id="search-form">
-        <p><li>{{ search_form.q }} <input type="submit" value="{% trans "Search" %}" /></li> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
-    </form>
-    <form method="post" action="." id="login-form" class="cuteform">
-        <h2>{% trans "Sign in" %}</h2>
-        <ol>
-            {{ form.as_ul }}
-            <li><input type="submit" value="{% trans "Sign in" %}" /></li>
-        </ol>
-        <p><input type="hidden" name="next" value="{{ next }}" /></p>
-    </form>
-
-    <form action="." method="post" accept-charset="utf-8" id="registration-form">
-        <h2>{% trans "Register" %}</h2>
-
-        <p><input type="submit" value="{% trans "Register" %}"/></p>
-    </form>
-{% endblock %}
index b7f6993..b53bab7 100644 (file)
                     {% endif %}
                     | <a href="{% url logout %}?next={{ request.get_full_path }}">{% trans "Logout" %}</a>
                 {% else %}
-                    <a href="{% url login %}" class="login-register-link">{% trans "Sign in" %} / {% trans "Register" %}</a>
+                    <a href="{% url login %}?next={{ request.path }}"
+                        id="login" class="ajaxable">
+                            {% trans "Sign in" %}</a>
+                    /
+                    <a href="{% url register %}?next={{ request.path }}"
+                        id="register" class="ajaxable">
+                            {% trans "Register" %}</a>
                 {% endif %}
             </p>
 
         </div>{# end main-content #}
 
 
-        <!--div id="login-register-window">
-            <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
+        {# template #}
+        <div id="ajaxable-window" class='dialog-window'>
+            <div class="header mono"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
             <div class="target">
-                <form method="post" action="{% url login %}" id="login-form" class="cuteform">
-                    <h2>{% trans "Sign in" %} / <a href="#" id="show-registration-form" style="font-size: 0.85em; font-weight: normal">{% trans "Register" %}</a></h2>
-                    <p><span id="id_login-__all__"></span></p>
-                    <ol>
-                        {% authentication_form %}
-                        <li><input type="submit" value="{% trans "Sign in" %}" /></li>
-                    </ol>
-                </form>
-                <form method="post" action="{% url register %}" id="registration-form" class="cuteform" style="display: none;">
-                    <h2><a href="#" id="show-login-form" style="font-size: 0.85em; font-weight: normal">{% trans "Sign in" %}</a> / {% trans "Register" %}</h2>
-                    <p><span id="id_registration-__all__"></span></p>
-                    <ol>
-                        {% user_creation_form %}
-                        <li><input type="submit" value="{% trans "Register" %}" /></li>
-                    </ol>
-                </form>
+                <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
             </div>
-        </div-->
+        </div>
 
 
         {% endblock bodycontent %}
 
 
         <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
-        <script type="text/javascript">var LANGUAGE_CODE = "{{ LANGUAGE_CODE }}";</script>
+        <script type="text/javascript">
+            var LANGUAGE_CODE = "{{ LANGUAGE_CODE }}";
+            var STATIC_URL = "{{ STATIC_URL }}";
+        </script>
         {% compressed_js "base" %}
 
         <!--{{ piwik_tag|safe }}
index 56dc7e1..4ac5c84 100755 (executable)
         <h2 class="grid-line"><span class='mono'>Narzędzia</span></h2>
 
         <ul>
-            <li><a href="{% url suggest %}" id="suggest-link">{% trans "Report a bug" %}</a></li>
+            <li><a href="{% url suggest %}" id="suggest" class="ajaxable">{% trans "Report a bug or suggestion" %}</a></li>
             <li><a href="http://turniej.wolnelektury.pl">Turniej Elektrybałtów</a></li>
             <li><a href="{% url lesmianator %}">Leśmianator</a></li>
             <li><a href="">{% trans "Mobile app" %}</a></li>
             <li><a href="{% url infopage "widget" %}">{% trans "Widget" %}</a></li>
             <li><a href="">{% trans "Public domain counter" %}</a></li>
-            <li><a href="{% url suggest_publishing %}">{% trans "Missing a book?" %}</a></li>
+            <li><a href="{% url suggest_publishing %}" id="suggest-publishing" class="ajaxable">{% trans "Missing a book?" %}</a></li>
         </ul>
     </div>
 
 
     {% endspaceless %}
 
-
 {% endblock %}
-
-
-
-
-
-
-
-            {# publication plan consultations - form link #}
-            <div style="clear:right;float:right" class="big-top-link">
-                <a href="{% url suggest_publishing %}" data-ajax="{% url suggest_publishing %}?ajax=1" id="suggest-publishing-link">
-                    {% trans "Didn't find a book? Make a suggestion." %}
-                </a>
-            </div>
-
-
-
-
-        <div id="suggest-window">
-            <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
-            <div class="target">
-                <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
-            </div>
-        </div>
-        <div id="suggest-publishing-window">
-            <div class="header"><a href="#" class="jqmClose">{% trans "Close" %}</a></div>
-            <div class="target">
-                <p><img src="{{ STATIC_URL }}img/indicator.gif" alt="*"/> {% trans "Loading" %}</p>
-            </div>
-        </div>
index 1fd0985..aa51718 100644 (file)
@@ -1,7 +1,7 @@
 {% extends "base.html" %}
 {% load i18n %}
 
-{% block title %}{{ author.name }} w WolneLektury.pl{% endblock %}
+{% block titleextra %}:: {{ author.name }}{% endblock %}
 
 {% block metadescription %}Licznik domeny publicznej: {{author.name}}.{% endblock %}
 
@@ -9,9 +9,6 @@
 
 {% block body %}
     <h1>{{ author.name }}</h1>
-    <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
-    </form>
 
     <div id="books-list">
         {% if author.has_description %}
@@ -42,7 +39,7 @@
             {% else %}
                 <div>
                     <p>{% trans "This author's works will become part of public domain and will be allowed to be published without restrictions in" %}</p>
-                    {% include "pdcounter/pd_counter.html" %}
+                    <div id='countdown' data-year='{{ pd_counter }}'></div>
                     <p>{% trans "<a href='http://domenapubliczna.org/co-to-jest-domena-publiczna/'>Find out</a> why Internet libraries can't publish this author's works." %}</p>
                 </div>
             {% endif %}
index 78b04d6..30a6911 100644 (file)
@@ -1,7 +1,7 @@
 {% extends "base.html" %}
 {% load i18n %}
 
-{% block title %}{{ book.title }} w WolneLektury.pl{% endblock %}
+{% block titleextra %}:: {{ book.title }}{% endblock %}
 
 {% block metadescription %}Licznik domeny publicznej: {{ book.title }}.{% endblock %}
 
@@ -9,9 +9,6 @@
 
 {% block body %}
     <h1>{{ book.author }}, {{ book.title }}</h1>
-    <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
-        <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
-    </form>
 
     <div id="books-list">
     {% if book.in_pd %}
@@ -19,7 +16,7 @@
        {% else %}
            {% if book.pd %}
                        <p>{% trans "This work will become part of public domain and will be allowed to be published without restrictions in" %}</p>
-                   {% include "pdcounter/pd_counter.html" %}
+            <div id='countdown' data-year='{{ pd_counter }}'></div>
                        <p>{% trans "<a href='http://domenapubliczna.org/co-to-jest-domena-publiczna/'>Find out</a> why Internet libraries can't publish this work." %}</p>
                {% else %}
                    <p>{% trans "This work is copyrighted." %}
diff --git a/wolnelektury/templates/pdcounter/pd_counter.html b/wolnelektury/templates/pdcounter/pd_counter.html
deleted file mode 100644 (file)
index fd157ad..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-<div id='countdown'></div>
-<script>
-{% if LANGUAGE_CODE != 'en' %}
-    $.countdown.setDefaults($.countdown.regional['{{ LANGUAGE_CODE }}']);
-{% else %}
-    $.countdown.setDefaults($.countdown.regional['']);
-{% endif %}
-d = new Date({{ pd_counter }}, 0, 1);
-function re() {location.reload()};
-$('#countdown').countdown({until: d, format: 'ydHMS', serverSync: serverTime,
-onExpiry: re, alwaysExpire: true});
-</script>
index 307f3fa..05d8e23 100644 (file)
@@ -4,12 +4,20 @@ import os
 from django.conf.urls.defaults import *
 from django.conf import settings
 from django.contrib import admin
+import views
 
 
 admin.autodiscover()
 
 urlpatterns = patterns('wolnelektury.views',
     url(r'^$', 'main_page', name='main_page'),
+
+    url(r'^zegar/$', 'clock', name='clock'),
+
+    # Authentication
+    url(r'^uzytkownicy/zaloguj/$', views.LoginFormView(), name='login'),
+    url(r'^uzytkownicy/utworz/$', views.RegisterFormView(), name='register'),
+    url(r'^uzytkownicy/wyloguj/$', 'logout_then_redirect', name='logout'),
 )
 
 urlpatterns += patterns('',
@@ -27,12 +35,6 @@ urlpatterns += patterns('',
     url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
     url(r'^admin/', include(admin.site.urls)),
 
-    # Authentication
-    url(r'^uzytkownicy/zaloguj/$', 'catalogue.views.login', name='login'),
-    url(r'^uzytkownicy/wyloguj/$', 'catalogue.views.logout_then_redirect', name='logout'),
-    url(r'^uzytkownicy/utworz/$', 'catalogue.views.register', name='register'),
-    url(r'^uzytkownicy/login/$', 'django.contrib.auth.views.login', name='simple_login'),
-
     # API
     (r'^api/', include('api.urls')),
 
index 808a0fe..0af07f4 100755 (executable)
@@ -1,5 +1,15 @@
-from django.template import RequestContext
+from datetime import datetime
+
+from django.contrib import auth
+from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
+from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import render_to_response
+from django.template import RequestContext
+from django.utils.http import urlquote_plus
+from django.utils.translation import ugettext_lazy as _
+from django.views.decorators import cache
+
+from ajaxable.utils import AjaxableFormView
 from catalogue.models import Book
 
 
@@ -7,4 +17,52 @@ def main_page(request):
     last_published = Book.objects.exclude(html_file='').order_by('-created_at')[:4]
 
     return render_to_response("main_page.html", locals(),
-        context_instance=RequestContext(request))
\ No newline at end of file
+        context_instance=RequestContext(request))
+
+
+class LoginFormView(AjaxableFormView):
+    form_class = AuthenticationForm
+    #template = "auth/login.html"
+    title = _('Sign in')
+    submit = _('Sign in')
+
+    def __call__(self, request):
+        if request.user.is_authenticated():
+            return HttpResponseRedirect('/')
+        return super(LoginFormView, self).__call__(request)
+
+    def success(self, form, request):
+        auth.login(request, form.get_user())
+
+
+class RegisterFormView(AjaxableFormView):
+    form_class = UserCreationForm
+    #template = "auth/register.html"
+    title = _('Register')
+    submit = _('Register')
+
+    def __call__(self, request):
+        if request.user.is_authenticated():
+            return HttpResponseRedirect('/')
+        return super(RegisterFormView, self).__call__(request)
+
+    def success(self, form, request):
+        user = form.save()
+        user = auth.authenticate(
+            username=form.cleaned_data['username'],
+            password=form.cleaned_data['password1']
+        )
+        auth.login(request, user)
+
+
+@cache.never_cache
+def logout_then_redirect(request):
+    auth.logout(request)
+    return HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?='))
+
+
+def clock(request):
+    """ Provides server time for jquery.countdown,
+    in a format suitable for Date.parse()
+    """
+    return HttpResponse(datetime.now().strftime('%Y/%m/%d %H:%M:%S'))