From: Lukasz Date: Tue, 18 May 2010 08:30:32 +0000 (+0200) Subject: Merge branch 'master' of http://github.com/fnp/wolnelektury X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/735368cfb54c8954518179993dcd3bc55e3878e7?ds=inline;hp=-c Merge branch 'master' of github.com/fnp/wolnelektury Conflicts: wolnelektury/settings.py wolnelektury/templates/catalogue/book_stub_detail.html wolnelektury/templates/catalogue/search_no_hits.html wolnelektury/templates/catalogue/tagged_object_list.html --- 735368cfb54c8954518179993dcd3bc55e3878e7 diff --combined apps/catalogue/views.py index 7cbba2748,b32a310b5..07fb7d349 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@@ -7,6 -7,7 +7,7 @@@ import zipfil import sys import pprint import traceback + import re from django.conf import settings from django.template import RequestContext @@@ -22,7 -23,8 +23,9 @@@ from django.contrib.auth.forms import U 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.translation import ugettext as _ from catalogue import models from catalogue import forms @@@ -170,16 -172,24 +173,24 @@@ def book_text(request, slug) # ========== # = Search = # ========== + + def _no_diacritics_regexp(query): + """ returns a regexp for searching for a query without diacritics + + should be locale-aware """ + names = {'a':u'ą', 'c':u'ć', 'e':u'ę', 'l': u'ł', 'n':u'ń', 'o':u'ó', 's':u'ś', 'z':u'ź|ż'} + def repl(m): + l = m.group() + return "(%s|%s)" % (l, names[l]) + return re.sub('[%s]'%(''.join(names.keys())), repl, query) + def _word_starts_with(name, prefix): - """returns a Q object gettings models having `name` contain a word + """returns a Q object getting models having `name` contain a word starting with `prefix` """ kwargs = {} if settings.DATABASE_ENGINE in ('mysql', 'postgresql_psycopg2', 'postgresql'): - # we must escape `prefix` so that it only matches literally - for special in r'\^$.*+?|(){}[]': - prefix = prefix.replace(special, '\\' + special) - + prefix = _no_diacritics_regexp(re.escape(prefix)) # we could use a [[:<:]] (word start), # but we want both `xy` and `(xy` to catch `(xyz)` kwargs['%s__iregex' % name] = u"(^|[^[:alpha:]])%s" % prefix @@@ -190,6 -200,19 +201,19 @@@ return Q(**kwargs) + def _tags_exact_matches(prefix, user): + book_stubs = models.BookStub.objects.filter(title__iexact = prefix) + books = models.Book.objects.filter(title__iexact = prefix) + book_stubs = filter(lambda x: x not in books, book_stubs) + tags = models.Tag.objects.filter(name__iexact = prefix) + if user.is_authenticated(): + tags = tags.filter(~Q(category='book') & (~Q(category='set') | Q(user=user))) + else: + tags = tags.filter(~Q(category='book') & ~Q(category='set')) + + return list(books) + list(tags) + list(book_stubs) + + def _tags_starting_with(prefix, user): book_stubs = models.BookStub.objects.filter(_word_starts_with('title', prefix)) books = models.Book.objects.filter(_word_starts_with('title', prefix)) @@@ -203,6 -226,24 +227,24 @@@ return list(books) + list(tags) + list(book_stubs) + + def _get_result_link(match, tag_list): + if isinstance(match, models.Book) or isinstance(match, models.BookStub): + return match.get_absolute_url() + else: + return reverse('catalogue.views.tagged_object_list', + kwargs={'tags': '/'.join(tag.slug for tag in tag_list + [match])} + ) + + def _get_result_type(match): + if isinstance(match, models.Book) or isinstance(match, models.BookStub): + type = 'book' + else: + type = match.category + return dict(models.TAG_CATEGORIES)[type] + + + def search(request): tags = request.GET.get('tags', '') prefix = request.GET.get('q', '') @@@ -214,22 -255,25 +256,25 @@@ # Prefix must have at least 2 characters if len(prefix) < 2: - return render_to_response('catalogue/search_no_hits.html', {'query':prefix, 'tags':tag_list}, + return render_to_response('catalogue/search_too_short.html', {'tags':tag_list, 'prefix':prefix}, context_instance=RequestContext(request)) - result = _tags_starting_with(prefix, request.user) - if len(result) > 0: - tag = result[0] - if isinstance(tag, models.Book) or isinstance(tag, models.BookStub): - return HttpResponseRedirect(tag.get_absolute_url()) - else: - tag_list.append(tag) - - return HttpResponseRedirect(reverse('catalogue.views.tagged_object_list', - kwargs={'tags': '/'.join(tag.slug for tag in tag_list)} - )) + result = _tags_exact_matches(prefix, request.user) + + if len(result) > 1: + # multiple exact matches + return render_to_response('catalogue/search_multiple_hits.html', + {'tags':tag_list, 'prefix':prefix, 'results':((x, _get_result_link(x, tag_list), _get_result_type(x)) for x in result)}, + context_instance=RequestContext(request)) + + if not result: + # no exact matches + result = _tags_starting_with(prefix, request.user) + + if result: + return HttpResponseRedirect(_get_result_link(result[0], tag_list)) else: - return render_to_response('catalogue/search_no_hits.html', {'query':prefix, 'tags':tag_list}, + return render_to_response('catalogue/search_no_hits.html', {'tags':tag_list, 'prefix':prefix}, context_instance=RequestContext(request)) @@@ -260,7 -304,7 +305,7 @@@ def book_sets(request, slug) book_sets = book.tags.filter(category='set', user=request.user) if not request.user.is_authenticated(): - return HttpResponse('

Aby zarządzać swoimi półkami, musisz się zalogować.

') + return HttpResponse(_('

To maintain your shelves you need to be logged in.

')) if request.method == 'POST': form = forms.ObjectSetsForm(book, request.user, request.POST) @@@ -278,7 -322,7 +323,7 @@@ book.tags = new_shelves + list(book.tags.filter(~Q(category='set') | ~Q(user=request.user))) if request.is_ajax(): - return HttpResponse('

Półki zostały zapisane.

') + return HttpResponse(_('

Shelves were sucessfully saved.

')) else: return HttpResponseRedirect('/') else: @@@ -302,9 -346,9 +347,9 @@@ def remove_from_shelf(request, shelf, b shelf.book_count -= 1 shelf.save() - return HttpResponse('Usunięto') + return HttpResponse(_('Book was successfully removed from the shelf')) else: - return HttpResponse('Książki nie ma na półce') + return HttpResponse(_('This book is not on the shelf')) def collect_books(books): @@@ -337,7 -381,7 +382,7 @@@ def download_shelf(request, slug) formats = ['pdf', 'odt', 'txt', 'mp3', 'ogg'] # Create a ZIP archive - temp = temp = tempfile.TemporaryFile() + temp = tempfile.TemporaryFile() archive = zipfile.ZipFile(temp, 'w') for book in collect_books(models.Book.tagged.with_all(shelf)): @@@ -400,7 -444,7 +445,7 @@@ def new_set(request) new_set = new_set_form.save(request.user) if request.is_ajax(): - return HttpResponse(u'

Półka %s została utworzona

' % new_set) + return HttpResponse(_('

Shelf %s was successfully created

') % new_set) else: return HttpResponseRedirect('/') @@@ -415,7 -459,7 +460,7 @@@ def delete_shelf(request, slug) user_set.delete() if request.is_ajax(): - return HttpResponse(u'

Półka %s została usunięta

' % user_set.name) + return HttpResponse(_('

Shelf %s was successfully removed

') % user_set.name) else: return HttpResponseRedirect('/') @@@ -455,7 -499,7 +500,7 @@@ def register(request) @cache.never_cache def logout_then_redirect(request): auth.logout(request) - return HttpResponseRedirect(request.GET.get('next', '/')) + return HttpResponseRedirect(urlquote_plus(request.GET.get('next', '/'), safe='/?=')) @@@ -474,10 -518,10 +519,10 @@@ def import_book(request) info = sys.exc_info() exception = pprint.pformat(info[1]) tb = '\n'.join(traceback.format_tb(info[2])) - return HttpResponse("An error occurred: %s\n\n%s" % (exception, tb), mimetype='text/plain') - return HttpResponse("Book imported successfully") + return HttpResponse(_("An error occurred: %s\n\n%s") % (exception, tb), mimetype='text/plain') + return HttpResponse(_("Book imported successfully")) else: - return HttpResponse("Error importing file: %r" % book_import_form.errors) + return HttpResponse(_("Error importing file: %r") % book_import_form.errors) @@@ -486,4 -530,4 +531,4 @@@ def clock(request) in a format suitable for Date.parse() """ from datetime import datetime - return HttpResponse(datetime.now().strftime('%Y/%m/%d %H:%M:%S')) + return HttpResponse(datetime.now().strftime('%Y/%m/%d %H:%M:%S')) diff --combined wolnelektury/settings.py index ef6887e20,33c219843..888ea34e4 --- a/wolnelektury/settings.py +++ b/wolnelektury/settings.py @@@ -67,6 -67,7 +67,7 @@@ TEMPLATE_CONTEXT_PROCESSORS = 'django.core.context_processors.i18n', 'django.core.context_processors.media', 'django.core.context_processors.request', + 'wolnelektury.context_processors.extra_settings', ] MIDDLEWARE_CLASSES = [ @@@ -96,7 -97,7 +97,7 @@@ INSTALLED_APPS = 'django.contrib.sites', 'django.contrib.admin', 'django.contrib.admindocs', - + # external 'south', 'sorl.thumbnail', @@@ -131,11 -132,8 +132,11 @@@ COMPRESS_JS = 'output_filename': 'js/jquery.min.js', }, 'all': { - 'source_filenames': ('js/jquery.autocomplete.js', 'js/jquery.form.js', - 'js/jquery.countdown.js', 'js/jquery.countdown-pl.js', + 'source_filenames': ('js/jquery.autocomplete.js', 'js/jquery.form.js', + 'js/jquery.countdown.js', 'js/jquery.countdown-pl.js', + 'js/jquery.countdown-en.js', 'js/jquery.countdown-de.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/jquery.cookie.js',), 'output_filename': 'js/all?.min.js', diff --combined wolnelektury/static/js/catalogue.js index b356183fa,66bebcf2f..d57f74339 --- a/wolnelektury/static/js/catalogue.js +++ b/wolnelektury/static/js/catalogue.js @@@ -1,50 -1,3 +1,47 @@@ - alert(LANGUAGE_CODE); +var LOCALE_TEXTS = { + "pl": { + "DELETE_SHELF": "Czy na pewno usunąć półkę", + "HIDE_DESCRIPTION": "Zwiń opis", + "EXPAND DESCRIPTION": "Rozwiń opis", + "LOADING": "Ładowanie", + }, + "fr": { + "DELETE_SHELF": "Translate me!", + "HIDE_DESCRIPTION": "Translate me!", + "EXPAND DESCRIPTION": "Translate me!", + "LOADING": "Translate me!", + }, + "ru": { + "DELETE_SHELF": "Translate me!", + "HIDE_DESCRIPTION": "Translate me!", + "EXPAND DESCRIPTION": "Translate me!", + "LOADING": "Translate me!", + }, + "en": { + "DELETE_SHELF": "Translate me!", + "HIDE_DESCRIPTION": "Translate me!", + "EXPAND DESCRIPTION": "Translate me!", + "LOADING": "Translate me!", + }, + "ru": { + "DELETE_SHELF": "Translate me!", + "HIDE_DESCRIPTION": "Translate me!", + "EXPAND DESCRIPTION": "Translate me!", + "LOADING": "Translate me!", + }, + "es": { + "DELETE_SHELF": "Translate me!", + "HIDE_DESCRIPTION": "Translate me!", + "EXPAND DESCRIPTION": "Translate me!", + "LOADING": "Translate me!", + }, + "lt":{ + "DELETE_SHELF": "Translate me!", + "HIDE_DESCRIPTION": "Translate me!", + "EXPAND DESCRIPTION": "Translate me!", + "LOADING": "Translate me!", + } +} - - alert(LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']); var BANNER_TEXTS = [ 'Przekaż 1% żeby ukryć ten baner.', 'Jak dobrze wydać 1% swojego podatku? Poradnik dla opornych.', @@@ -126,14 -79,16 +123,16 @@@ function serverTime() function() { $(this).css({background: '#FFF'}); } ).click(function() { $(this).fadeOut(function() { - $(this).prev().fadeIn() + $(this).prev().fadeIn(); }); + return false; }) } }); $('.fragment-short-text').click(function() { $(this).fadeOut(function() { $(this).next().fadeIn() }); + return false; }).hover( function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); }, function() { $(this).css({background: '#FFF'}); } @@@ -218,7 -173,7 +217,7 @@@ $('.delete-shelf').click(function() { var link = $(this); var shelf_name = $('.visit-shelf', link.parent()).text(); - if (confirm('Czy na pewno usunąć półkę ' + shelf_name + '?')) { + if (confirm(LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']+ ' '+ shelf_name + '?')) { $.post(link.attr('href'), function(data, textStatus) { link.parent().remove(); }); @@@ -256,7 -211,7 +255,7 @@@ $('.delete-shelf').click(function() { var link = $(this); var shelf_name = $('.visit-shelf', link.parent()).text(); - if (confirm('Czy na pewno usunąć półkę ' + shelf_name + '?')) { + if (confirm(LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF'] + ' ' + shelf_name + '?')) { $.post(link.attr('href'), function(data, textStatus) { link.parent().remove(); }); @@@ -280,11 -235,11 +279,11 @@@ if ($('#description').hasClass('hidden')) { $('#description').slideDown('fast').removeClass('hidden'); $.cookie('description-state', 'opened', {path: '/', expires: 30}); - $('p', this).html('Zwiń opis ▲'); + $('p', this).html(LOCALE_TEXTS[LANGUAGE_CODE]['HIDE_DESCRIPTION'] + ' ▲'); } else { $('#description').slideUp('fast').addClass('hidden'); $.cookie('description-state', 'closed', {path: '/', expires: 30}); - $('p', this).html('Rozwiń opis ▼'); + $('p', this).html(LOCALE_TEXTS[LANGUAGE_CODE]['HIDE_DESCRIPTION'] + ' ▼'); } }); @@@ -297,7 -252,7 +296,7 @@@ trigger: 'a.jqm-trigger', onShow: function(hash) { var offset = $(hash.t).offset(); - target.html('

Ładowanie

'); + target.html('

'+LOCALE_TEXTS[LANGUAGE_CODE]['DELETE_SHELF']+'

'); hash.w.css({position: 'absolute', left: offset.left, top: offset.top}).show() }, onLoad: function(hash) { $('form', hash.w).ajaxForm({ @@@ -317,7 -272,7 +316,7 @@@ if ($.cookie('description-state') == 'closed') { $('#description').hide().addClass('hidden'); - $('#toggle-description p').html('Rozwiń opis ▼'); + $('#toggle-description p').html(LOCALE_TEXTS[LANGUAGE_CODE]['EXPAND_SHELF']+' ▼'); } $('#user-info').show(); @@@ -329,7 -284,7 +328,7 @@@ $('#download-shelf-menu').slideDown('fast'); if (!formatsDownloaded) { - // Pobierz dane o formatach + // Get info about the formats formatsDownloaded = true; $.ajax({ url: $('#download-formats-form').attr('data-formats-feed'), diff --combined wolnelektury/templates/404.html index 288e050ed,b761b4866..ebf7d155b --- a/wolnelektury/templates/404.html +++ b/wolnelektury/templates/404.html @@@ -6,15 -6,15 +6,15 @@@ 404 - {% trans "Site does not exist" %} - WolneLektury.pl - + - +

{% trans "Site does not exist" %}

-{% trans "We are sorry, but this site does not exist. Please check if you entered correct address or go to "%} {% trans "homepage" %}. +{% trans "We are sorry, but this site does not exist. Please check if you entered correct address or go to "%} {% trans "main page" %}.

{% compressed_js "jquery" %} {% compressed_js "all" %} {% block extrahead %} {% endblock %} - +
{% block bodycontent %}
{% chunk "top-message" %} @@@ -29,7 -28,7 +29,7 @@@
@@@ -69,17 -58,13 +69,17 @@@