diff --git a/article/admin.py b/article/admin.py index ff178cee..29b59839 100644 --- a/article/admin.py +++ b/article/admin.py @@ -125,12 +125,14 @@ def article_change(request, url): return render_to_response('article_add.html', args) #----------------------- -from django.views.generic import ListView, FormView +from django.views.generic import FormView +from functions.custom_views import ListView from forms import BlogForm class BlogList(ListView): model = Article - template_name = 'article/article_admin_list.html' + template_name = 'admin/article/article_admin_list.html' + paginate_by = settings.ADMIN_PAGINATION def get_queryset(self): return self.model.objects.blogs() @@ -140,6 +142,7 @@ class BlogList(ListView): context['blog_flag'] = True return context + class BlogView(FormView): form_class = BlogForm template_name = 'article/blog_form.html' @@ -160,7 +163,6 @@ class BlogView(FormView): form.save(author, article=self.obj) return HttpResponseRedirect(self.success_url) - def get_form(self, form_class): if self.request.POST: return super(BlogView, self).get_form(form_class) @@ -168,7 +170,8 @@ class BlogView(FormView): if self.obj: article = self.obj data = {} - data['theme'] = [item.id for item in article.theme.all()] + data['slug'] = article.slug + data['theme'] = [item.id for item in article.blog_theme.all()] if article.exposition: data['exposition'] = article.exposition.id if article.conference: diff --git a/article/forms.py b/article/forms.py index 8810abe7..e287fd45 100644 --- a/article/forms.py +++ b/article/forms.py @@ -12,18 +12,20 @@ from functions.form_check import translit_with_separator #models from models import Article from accounts.models import User -from theme.models import Theme, Tag +from theme.models import Theme, Tag, ThemeBlog from exposition.models import Exposition from conference.models import Conference class BlogForm(forms.Form): type = Article.blog - theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=Theme.objects.exclude(article__id=None), required=False, + theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=ThemeBlog.objects.all(), required=False, widget=forms.SelectMultiple(attrs={'style':'width: 550px'})) + slug = forms.SlugField(label=u'URL', max_length=255, min_length=1) publish_date = forms.DateField(label=u'Дата публикации', input_formats=['%Y-%m-%d', '%d.%m.%Y'], required=False) tag = forms.CharField(label=u'Теги', widget=forms.HiddenInput(), required=False) logo = forms.ImageField(label=u'Лого', required=False) + def __init__(self, *args, **kwargs): """ create dynamical translated fields fields @@ -49,21 +51,26 @@ class BlogForm(forms.Form): def save(self, author, article=None): data = self.cleaned_data - #create new Article object or get exists + # create new Article object or get exists if not article: article = Article() article.author = author article.type = self.type + article.slug = data.get('slug') if data['logo']: article.logo = data['logo'] article.publish_date = data['publish_date'] # fill translated fields and save object fill_with_signal(Article, article, data) # fill manytomany fields - article.theme.clear() + if self.type == Article.blog: + article.blog_theme.clear() + article.blog_theme.add(*ThemeBlog.objects.filter(id__in=data['theme'])) + else: + article.theme.clear() + article.theme.add(*Theme.objects.filter(id__in=data['theme'])) article.tag.clear() - article.theme.add(*Theme.objects.filter(id__in=data['theme'])) article.tag.add(*Tag.objects.filter(id__in=data['tag'])) #for item in data['theme']: # article.theme.add(item.id)#.id cause select uses queryset @@ -92,7 +99,8 @@ class NewsForm(BlogForm): type = Article.news exposition = forms.CharField(label=u'Выставка', widget=forms.HiddenInput(), required=False) conference = forms.CharField(label=u'Конференция', widget=forms.HiddenInput(), required=False) - + theme = forms.ModelMultipleChoiceField(label='Тематики', queryset=Theme.objects.all(), required=False, + widget=forms.SelectMultiple(attrs={'style':'width: 550px'})) #exposition = forms.ModelChoiceField(label = u'Выставка', required=False, queryset=Exposition.objects.all()) #conference = forms.ModelChoiceField(label = u'Конференция', required=False, queryset=Conference.objects.all()) @@ -258,7 +266,7 @@ class BlogForm(forms.ModelForm): class ArticleFilterForm(forms.Form): theme = forms.MultipleChoiceField(label=_(u'Тематика:'), required=False, - choices=[(item.id, item.name) for item in Theme.objects.language().filter(article__type=1).exclude(article__id=None).distinct()]) + choices=[(item.id, item.name) for item in ThemeBlog.objects.language().distinct()]) tag = forms.CharField(label=_(u'Теги:'), widget=forms.HiddenInput(), required=False) ''' @@ -284,7 +292,7 @@ class BlogFilterForm(forms.Form): super(BlogFilterForm, self).__init__(*args, **kwargs) ids = [item['theme'] for item in list(Article.objects.blogs().values('theme').distinct())] self.fields['theme'] = forms.MultipleChoiceField(label=_(u'Тематика:'), required=False, - choices=[(item.id, item.name) for item in Theme.objects.language().filter(id__in=ids)]) + choices=[(item.id, item.name) for item in ThemeBlog.objects.language().filter(id__in=ids)]) class NewsFilterForm(forms.Form): diff --git a/article/models.py b/article/models.py index aa252baf..f2cf06dc 100644 --- a/article/models.py +++ b/article/models.py @@ -14,7 +14,6 @@ from functions.form_check import translit_with_separator from django.core.cache import cache - class ArticleManager(TranslationManager): cache_time = 60 def safe_get(self, **kwargs): @@ -60,7 +59,6 @@ class ArticleManager(TranslationManager): cache.set(key, blogs, self.cache_time) return blogs - return list(self.blogs().filter(publish_date__isnull=False).order_by('-main_page', '-publish_date')[:3]) class Article(TranslatableModel): """ @@ -81,6 +79,7 @@ class Article(TranslatableModel): old_id = models.IntegerField(blank=True, null=True) logo = ImageField(upload_to='articles_preview', blank=True) theme = models.ManyToManyField('theme.Theme') + blog_theme = models.ManyToManyField('theme.ThemeBlog') tag = models.ManyToManyField('theme.Tag', blank=True, null=True) author = models.ForeignKey('accounts.User', verbose_name='Автор', on_delete=models.PROTECT, related_name='articles') @@ -148,15 +147,12 @@ class Article(TranslatableModel): return self.conference return None - def save(self, *args, **kwargs): - # If no slug is provided, generates one before saving. if not self.slug: self.slug = self.generate_unique_slug() - - #Set the description field on save. - #if self.gen_description: + # Set the description field on save. + # if self.gen_description: # self.description = strip_tags(self.description_from_content()) super(Article, self).save(*args, **kwargs) @@ -189,7 +185,6 @@ class Article(TranslatableModel): #print self.lazy_translation_getter('main_title', self.pk) return u'%s'%self.lazy_translation_getter('main_title', self.pk) - def _get_next_or_previous_by_publish_date(self, is_next, **kwargs): """ Retrieves next or previous object by publish date. We implement diff --git a/article/views.py b/article/views.py index 7b5c68ec..06932d0b 100644 --- a/article/views.py +++ b/article/views.py @@ -5,7 +5,7 @@ from functions.custom_views import ListView from django.http import HttpResponse from models import Article from forms import ArticleFilterForm -from theme.models import Tag, Theme +from theme.models import Tag, Theme, ThemeBlog from meta.views import MetadataMixin @@ -71,7 +71,7 @@ class BlogList(MetadataMixin, ListView): themes = self.request.GET.getlist('theme') if themes: - qs = qs.filter(theme__id__in=themes) + qs = qs.filter(blog_theme__id__in=themes) tags = self.request.GET.getlist('tag') if u'' in tags: @@ -180,10 +180,10 @@ class BlogsFilterCatalog(MetadataMixin, ListView): self.filter_object = tag qs = Article.objects.blogs().filter(tag=tag) else: - theme = get_object_or_404(Theme, url=slug) + theme = get_object_or_404(ThemeBlog, url=slug) self.kwargs['theme'] = theme self.filter_object = theme - qs = Article.objects.blogs().filter(theme = theme) + qs = Article.objects.blogs().filter(blog_theme = theme) year = self.kwargs.get('year') if year: diff --git a/conference/admin.py b/conference/admin.py index af2f95c5..7e22fc61 100644 --- a/conference/admin.py +++ b/conference/admin.py @@ -159,7 +159,7 @@ def conference_change(request, url): #initial StatisticFormSet StatisticFormSet = modelformset_factory(Statistic, form=StatisticForm, exclude=('conference',)) #fill form with data from database - data = {'web_page':conference.web_page, 'foundation_year': conference.foundation_year, + data = {'web_page':conference.web_page, 'place_alt': conference.place_alt, 'foundation_year': conference.foundation_year, 'data_begin':conference.data_begin, 'data_end':conference.data_end, 'currency':conference.currency, 'tax':conference.tax, 'min_price':conference.min_price, 'max_price':conference.max_price, 'link':conference.link, 'conference_id':conference.id, 'expohit': conference.expohit, @@ -233,7 +233,7 @@ class ConferenceView(AdminView): return super(ConferenceView, self).get_form(form_class) obj = self.set_obj() if obj: - data = {'web_page':obj.web_page, 'foundation_year': obj.foundation_year, + data = {'web_page':obj.web_page, 'place_alt':obj.place_alt, 'foundation_year': obj.foundation_year, 'data_begin':obj.data_begin, 'data_end':obj.data_end, 'currency':obj.currency, 'tax':obj.tax, 'min_price':obj.min_price, 'max_price':obj.max_price, 'link':obj.link, 'conference_id':obj.id, 'expohit': obj.expohit, 'periodic':obj.periodic, diff --git a/conference/forms.py b/conference/forms.py index 897f441b..4cba3c0b 100644 --- a/conference/forms.py +++ b/conference/forms.py @@ -56,6 +56,7 @@ class ConferenceCreateForm(forms.Form): place = forms.ChoiceField(label=u'Место проведения', required=False, choices=places) + place_alt = forms.CharField(label = u"Альтернативное название места", required=False) #creates select input with empty choices cause it will be filled with ajax city = forms.CharField(label=u'Город', widget=forms.HiddenInput()) @@ -160,6 +161,7 @@ class ConferenceCreateForm(forms.Form): conference.canceled = data['canceled'] conference.moved = data['moved'] conference.periodic = data['periodic'] + conference.place_alt = data['place_alt'] # generates bitfield flag = 0 if data['quality_label']: diff --git a/emencia/django/newsletter/forms.py b/emencia/django/newsletter/forms.py index 6c212211..250a388b 100644 --- a/emencia/django/newsletter/forms.py +++ b/emencia/django/newsletter/forms.py @@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _ from emencia.django.newsletter.models import Contact, ContactSettings from emencia.django.newsletter.models import MailingList from theme.models import Theme +from country.models import Country, Area class MailingListSubscriptionForm(forms.ModelForm): @@ -64,6 +65,7 @@ class ContactForm(forms.ModelForm): model = Contact fields = ('email', 'first_name', ) + class ContactSettingsForm(forms.ModelForm): theme = forms.MultipleChoiceField(choices=[(str(item.id), item.name) for item in list(Theme.objects.language().all())], widget=forms.CheckboxSelectMultiple(attrs={'class': 'pr-checkbox'}), required=False) @@ -76,4 +78,10 @@ class ContactSettingsForm(forms.ModelForm): if theme: return Theme.objects.filter(id__in=theme) else: - return Theme.objects.none() \ No newline at end of file + return Theme.objects.none() + +class ContactFilterForm(forms.Form): + email = forms.EmailField(label=_("Email"), max_length=255) + theme = forms.ChoiceField(label=_("Тематика"), choices = [(t.id, t.name) for t in Theme.objects.language()]) + country = forms.ChoiceField(label=_("Страна"), choices = [(c.id, c.name) for c in Country.objects.language().distinct()]) + area = forms.ChoiceField(label=_("Страна"), choices = [(c.id, c.name) for c in Country.objects.language().distinct()]) diff --git a/emencia/django/newsletter/views/admin_views.py b/emencia/django/newsletter/views/admin_views.py index 6c04b220..8ba3c9af 100644 --- a/emencia/django/newsletter/views/admin_views.py +++ b/emencia/django/newsletter/views/admin_views.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -from django.views.generic import TemplateView, CreateView, ListView, UpdateView, DetailView +from django.views.generic import TemplateView, CreateView, ListView, UpdateView, DetailView, FormView +from django.views.generic.list import MultipleObjectMixin from django.conf import settings from django.http import HttpResponseRedirect from django.forms.formsets import BaseFormSet, formset_factory @@ -7,12 +8,61 @@ from django.shortcuts import get_object_or_404 from django.core.urlresolvers import reverse from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter, Attachment from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm, AttachmentForm +from emencia.django.newsletter.models import Contact, ContactSettings, MailingList, Newsletter +from emencia.django.newsletter.admin_forms import ContactSettingsForm, MailingListForm, NewsletterForm +from ..forms import ContactFilterForm +from functions.admin_views import paginate_results -class ContactList(ListView): +class ContactList(FormView): paginate_by = settings.ADMIN_PAGINATION model = Contact template_name = 'admin/newsletters/contact_list.html' + form_class = ContactFilterForm + queryset = Contact.objects.all() + + def get_form(self, form_class): + + if self.request.GET: + return form_class(self.request.GET) + else: + return form_class(**self.get_form_kwargs()) + + def get(self, request, *args, **kwargs): + + if request.GET: + form_class = self.get_form_class() + form = self.get_form(form_class) + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + else: + return super(ContactList, self).get(request, *args, **kwargs) + + def form_valid(self, form): + """ + + filtering queryset and return paginated results + + """ + qs = form.filter() + result = paginate_results(qs, page=self.request.GET.get('page')) + context = self.get_context_data(form=form) + context.update({'object_list': result}) + return self.render_to_response(context) + + def get_context_data(self, **kwargs): + context = super(ContactList, self).get_context_data(**kwargs) + qs = self.model.objects.all() + result = paginate_results(qs, page=self.request.GET.get('page')) + context['object_list'] = result + return context + + + + + class UpdateContact(UpdateView): diff --git a/service/admin.py b/service/admin.py index 91ae029d..16bb9707 100644 --- a/service/admin.py +++ b/service/admin.py @@ -141,4 +141,65 @@ class ServiceControl(FormMixin, DetailView): def get_context_data(self, **kwargs): context = super(ServiceControl, self).get_context_data(**kwargs) context['form'] = self.get_form(self.form_class) - return context \ No newline at end of file + return context + + + +from django.shortcuts import get_object_or_404 +from django.views.generic import UpdateView, ListView, DeleteView +from .forms import LinkedServiceForm +from .models import LinkedService + + + + +class LinkedServiceUpdateView(UpdateView): + form_class = LinkedServiceForm + model = LinkedService + template_name = "admin/service/linked_service.html" + success_url = "/admin/service/test/all/" + + def get_object(self, queryset=None): + url = self.kwargs['url'] + service = get_object_or_404(Service, url=url) + obj = LinkedService.objects.get(service=service) + return obj + + def get_initial(self): + types = {0:[], 1:['expo'], 2:['conference'], 3:['expo', 'conference']} + initial = { + 'expositions': ",".join("%s:%s"%(item.id, item.name) for item in self.object.expositions.all()), + 'conferences': ",".join("%s:%s"%(item.id, item.name) for item in self.object.conferences.all()), + 'type': types[self.object.service.type.mask] + } + return initial + + def get_success_url(self): + return self.success_url + + def form_valid(self, form): + obj = form.save() + obj.countries = form.cleaned_data['countries'] + obj.expositions = form.cleaned_data['expositions'] + obj.conferences = form.cleaned_data['conferences'] + obj.save() + obj.update_all_flags() + return HttpResponseRedirect(self.get_success_url()) + + +class LinkedServiceList(ListView): + model = LinkedService + template_name = 'admin/service/linked_service_list.html' + + +class LinkedServiceDeleteView(DeleteView): + model = LinkedService + template_name = 'admin/service/linked_service_confirm_delete.html' + success_url = '/admin/service/test/all' + slug_url_kwarg = 'url' + + def get_object(self, queryset=None): + url = self.kwargs['url'] + service = get_object_or_404(Service, url=url) + obj = LinkedService.objects.get(service=service) + return obj diff --git a/service/admin_urls.py b/service/admin_urls.py index 4dda045f..f1816405 100644 --- a/service/admin_urls.py +++ b/service/admin_urls.py @@ -2,7 +2,7 @@ from django.conf.urls import patterns, include, url from views import CallBackListView, VisitListView, TranslationListView, AdvertisingListView, \ ParticipationListView, RemoteListView,TicketsListView -from service.admin import ServiceControlList, ServiceControl +from service.admin import ServiceControlList, ServiceControl, LinkedServiceList, LinkedServiceUpdateView, LinkedServiceDeleteView urlpatterns = patterns('service.admin', url(r'^control/list/$', ServiceControlList.as_view()), @@ -18,8 +18,14 @@ urlpatterns = patterns('service.admin', url(r'order/participation/$', ParticipationListView.as_view()), url(r'order/remote/$', RemoteListView.as_view()), url(r'order/tickets/$', TicketsListView.as_view()), + url('^test/delete/(?P[a-z]*)/', LinkedServiceDeleteView.as_view(), name='linked_service_delete'), + url('^test/all/', LinkedServiceList.as_view(), name = 'linked_service_all'), + url('^test/(?P[a-z]*)/', LinkedServiceUpdateView.as_view(), name= 'linked_service_update'), + #ajax url(r'^get_city/$', 'get_city'), + + #url(r'^get_country/$', 'get_country'), ) diff --git a/service/forms.py b/service/forms.py index 946b0d64..011e6a1c 100644 --- a/service/forms.py +++ b/service/forms.py @@ -146,4 +146,48 @@ class ServiceControlForm(forms.Form): self.fields['event_type'] = forms.MultipleChoiceField(required=False, label = 'Тип события', widget=forms.CheckboxSelectMultiple(), choices=[(item['service_bit'], item['verbose']) - for item in self.event]) \ No newline at end of file + for item in self.event]) + + +from .models import LinkedService +from django.db.models.query import EmptyQuerySet + + +class LinkedServiceForm(forms.ModelForm): + type = forms.MultipleChoiceField(choices = [(x, x) for x in list(Service.type)], widget = forms.CheckboxSelectMultiple, required=False) + expositions = forms.CharField(widget=forms.HiddenInput,required=False) + conferences = forms.CharField(widget=forms.HiddenInput, required=False) + + class Meta: + model = LinkedService + fields = ['countries', 'exclude_countries', 'expositions', 'conferences'] + help_text = { + 'exclude_countries': u'При отсутствии стран этот флаг значит ВСЕ страны!' + } + + def clean_countries(self): + countries = Country.objects.language().filter(id__in=self.cleaned_data['countries']) + return countries + + def clean_expositions(self): + expositions = EmptyQuerySet() + if self.cleaned_data.get('expositions'): + expositions = Exposition.objects.language().filter(id__in=list(set(self.cleaned_data['expositions'].split(',')))) + return expositions + + def clean_conferences(self): + conferences = EmptyQuerySet() + if self.cleaned_data.get('conferences'): + conferences = Conference.objects.language().filter(id__in=list(set(self.cleaned_data['conferences'].split(',')))) + return conferences + + def save(self, commit=True): + obj = super(LinkedServiceForm, self).save(commit=True) + data = self.cleaned_data + # manage service type bit field + types = data['type'] + obj.service.type = 0 + for type in types: + obj.service.type = obj.service.type | getattr(Service.type, type) + obj.service.save() + return obj diff --git a/service/management/__init__.py b/service/management/__init__.py new file mode 100644 index 00000000..7ff70582 --- /dev/null +++ b/service/management/__init__.py @@ -0,0 +1 @@ +__author__ = 'dev' diff --git a/service/management/commands/__init__.py b/service/management/commands/__init__.py new file mode 100644 index 00000000..7ff70582 --- /dev/null +++ b/service/management/commands/__init__.py @@ -0,0 +1 @@ +__author__ = 'dev' diff --git a/service/management/commands/create_linked_services.py b/service/management/commands/create_linked_services.py new file mode 100644 index 00000000..681d9f46 --- /dev/null +++ b/service/management/commands/create_linked_services.py @@ -0,0 +1,24 @@ +from django.core.management.base import BaseCommand +from django.utils.translation import activate +from service.models import Service,LinkedService +from country.models import Country +from exposition.models import Exposition +from conference.models import Conference + + +class Command(BaseCommand): + def handle(self, *args, **options): + activate('ru') + LinkedService.objects.all().delete() + services = Service.objects.all() + for service in services: + linked = LinkedService() + linked.service = service + linked.save() + if Country.objects.language().filter(services=getattr(Country.services, service.url)).count() > 180: + linked.exclude_countries = True + linked.countries = Country.objects.language().exclude(services=getattr(Country.services, service.url)) + else: + linked.countries = Country.objects.language().filter(services=getattr(Country.services, service.url)) + linked.save() + diff --git a/service/models.py b/service/models.py index d0d711f3..4549f10e 100644 --- a/service/models.py +++ b/service/models.py @@ -198,4 +198,45 @@ class CallBack(models.Model): viewed = models.DateTimeField(null=True, blank=True) class Meta: - ordering = ['-created'] \ No newline at end of file + ordering = ['-created'] + + +from country.models import Country +from exposition.models import Exposition +from conference.models import Conference +from django.db.models import F + + +class LinkedService(models.Model): + service = models.ForeignKey(Service, blank=False) + countries = models.ManyToManyField(Country, blank=True, verbose_name=u"Страны") + exclude_countries = models.BooleanField(default=False, verbose_name=u"Исключить страны") + expositions = models.ManyToManyField(Exposition, blank=True, verbose_name= u"Выставки") + conferences = models.ManyToManyField(Conference, blank=True, verbose_name=u'Конференции') + + def update_countries_flag(self): + if self.exclude_countries: + 'filter all countries except selected and set flag to true' + Country.objects.language().exclude(id__in=[c.id for c in self.countries.all()]).update(services=F('services').bitor(getattr(Country.services, self.service.url))) + 'set another flags to false' + Country.objects.language().filter(id__in=[c.id for c in self.countries.all()]).update(services=F('services').bitand(~getattr(Country.services, self.service.url))) + else: + 'if not exclude, filter all selected countries and set flag to true' + self.countries.update(services=F('services').bitor(getattr(Country.services, self.service.url))) + Country.objects.exclude(id__in=[c.id for c in self.countries.all()]).update(services=F('services').bitand(~getattr(Country.services, self.service.url))) + + def update_expositions_flag(self): + self.expositions.update(services=F('services').bitor(getattr(Exposition.services, self.service.url))) + Exposition.objects.exclude(id__in=[c.id for c in self.expositions.all()]).update(services=F('services').bitand(~getattr(Exposition.services, self.service.url))) + + def update_conferences_flag(self): + self.conferences.update(services=F('services').bitor(getattr(Conference.services, self.service.url))) + Conference.objects.exclude(id__in=[c.id for c in self.conferences.all()]).update(services=F('services').bitand(~getattr(Conference.services, self.service.url))) + + def update_all_flags(self): + self.update_countries_flag() + self.update_expositions_flag() + self.update_conferences_flag() + + def __unicode__(self): + return u'Linked service for %s'%self.service.url diff --git a/static/js/select/select2.min.js b/static/js/select/select2.min.js deleted file mode 100644 index 1537edca..00000000 --- a/static/js/select/select2.min.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2012 Igor Vaynberg - -Version: 3.4.1 Timestamp: Thu Jun 27 18:02:10 PDT 2013 - -This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU -General Public License version 2 (the "GPL License"). You may choose either license to govern your -use of this software only upon the condition that you accept all of the terms of either the Apache -License or the GPL License. - -You may obtain a copy of the Apache License and the GPL License at: - -http://www.apache.org/licenses/LICENSE-2.0 -http://www.gnu.org/licenses/gpl-2.0.html - -Unless required by applicable law or agreed to in writing, software distributed under the Apache License -or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -either express or implied. See the Apache License and the GPL License for the specific language governing -permissions and limitations under the Apache License and the GPL License. -*/ -(function(a){a.fn.each2===void 0&&a.fn.extend({each2:function(b){for(var c=a([0]),d=-1,e=this.length;e>++d&&(c.context=c[0]=this[d])&&b.call(c[0],d,c)!==!1;);return this}})})(jQuery),function(a,b){"use strict";function m(a,b){for(var c=0,d=b.length;d>c;c+=1)if(o(a,b[c]))return c;return-1}function n(){var b=a(l);b.appendTo("body");var c={width:b.width()-b[0].clientWidth,height:b.height()-b[0].clientHeight};return b.remove(),c}function o(a,c){return a===c?!0:a===b||c===b?!1:null===a||null===c?!1:a.constructor===String?a+""==c+"":c.constructor===String?c+""==a+"":!1}function p(b,c){var d,e,f;if(null===b||1>b.length)return[];for(d=b.split(c),e=0,f=d.length;f>e;e+=1)d[e]=a.trim(d[e]);return d}function q(a){return a.outerWidth(!1)-a.width()}function r(c){var d="keyup-change-value";c.on("keydown",function(){a.data(c,d)===b&&a.data(c,d,c.val())}),c.on("keyup",function(){var e=a.data(c,d);e!==b&&c.val()!==e&&(a.removeData(c,d),c.trigger("keyup-change"))})}function s(c){c.on("mousemove",function(c){var d=i;(d===b||d.x!==c.pageX||d.y!==c.pageY)&&a(c.target).trigger("mousemove-filtered",c)})}function t(a,c,d){d=d||b;var e;return function(){var b=arguments;window.clearTimeout(e),e=window.setTimeout(function(){c.apply(d,b)},a)}}function u(a){var c,b=!1;return function(){return b===!1&&(c=a(),b=!0),c}}function v(a,b){var c=t(a,function(a){b.trigger("scroll-debounced",a)});b.on("scroll",function(a){m(a.target,b.get())>=0&&c(a)})}function w(a){a[0]!==document.activeElement&&window.setTimeout(function(){var d,b=a[0],c=a.val().length;a.focus(),a.is(":visible")&&b===document.activeElement&&(b.setSelectionRange?b.setSelectionRange(c,c):b.createTextRange&&(d=b.createTextRange(),d.collapse(!1),d.select()))},0)}function x(b){b=a(b)[0];var c=0,d=0;if("selectionStart"in b)c=b.selectionStart,d=b.selectionEnd-c;else if("selection"in document){b.focus();var e=document.selection.createRange();d=document.selection.createRange().text.length,e.moveStart("character",-b.value.length),c=e.text.length-d}return{offset:c,length:d}}function y(a){a.preventDefault(),a.stopPropagation()}function z(a){a.preventDefault(),a.stopImmediatePropagation()}function A(b){if(!h){var c=b[0].currentStyle||window.getComputedStyle(b[0],null);h=a(document.createElement("div")).css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),h.attr("class","select2-sizer"),a("body").append(h)}return h.text(b.val()),h.width()}function B(b,c,d){var e,g,f=[];e=b.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0===this.indexOf("select2-")&&f.push(this)})),e=c.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0!==this.indexOf("select2-")&&(g=d(this),g&&f.push(this))})),b.attr("class",f.join(" "))}function C(a,c,d,e){var f=a.toUpperCase().indexOf(c.toUpperCase()),g=c.length;return 0>f?(d.push(e(a)),b):(d.push(e(a.substring(0,f))),d.push(""),d.push(e(a.substring(f,f+g))),d.push(""),d.push(e(a.substring(f+g,a.length))),b)}function D(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return(a+"").replace(/[&<>"'\/\\]/g,function(a){return b[a]})}function E(c){var d,e=0,f=null,g=c.quietMillis||100,h=c.url,i=this;return function(j){window.clearTimeout(d),d=window.setTimeout(function(){e+=1;var d=e,g=c.data,k=h,l=c.transport||a.fn.select2.ajaxDefaults.transport,m={type:c.type||"GET",cache:c.cache||!1,jsonpCallback:c.jsonpCallback||b,dataType:c.dataType||"json"},n=a.extend({},a.fn.select2.ajaxDefaults.params,m);g=g?g.call(i,j.term,j.page,j.context):null,k="function"==typeof k?k.call(i,j.term,j.page,j.context):k,f&&f.abort(),c.params&&(a.isFunction(c.params)?a.extend(n,c.params.call(i)):a.extend(n,c.params)),a.extend(n,{url:k,dataType:c.dataType,data:g,success:function(a){if(!(e>d)){var b=c.results(a,j.page);j.callback(b)}}}),f=l.call(i,n)},g)}}function F(c){var e,f,d=c,g=function(a){return""+a.text};a.isArray(d)&&(f=d,d={results:f}),a.isFunction(d)===!1&&(f=d,d=function(){return f});var h=d();return h.text&&(g=h.text,a.isFunction(g)||(e=h.text,g=function(a){return a[e]})),function(c){var h,e=c.term,f={results:[]};return""===e?(c.callback(d()),b):(h=function(b,d){var f,i;if(b=b[0],b.children){f={};for(i in b)b.hasOwnProperty(i)&&(f[i]=b[i]);f.children=[],a(b.children).each2(function(a,b){h(b,f.children)}),(f.children.length||c.matcher(e,g(f),b))&&d.push(f)}else c.matcher(e,g(b),b)&&d.push(b)},a(d().results).each2(function(a,b){h(b,f.results)}),c.callback(f),b)}}function G(c){var d=a.isFunction(c);return function(e){var f=e.term,g={results:[]};a(d?c():c).each(function(){var a=this.text!==b,c=a?this.text:this;(""===f||e.matcher(f,c))&&g.results.push(a?this:{id:this,text:this})}),e.callback(g)}}function H(b,c){if(a.isFunction(b))return!0;if(!b)return!1;throw Error(c+" must be a function or a falsy value")}function I(b){return a.isFunction(b)?b():b}function J(b){var c=0;return a.each(b,function(a,b){b.children?c+=J(b.children):c++}),c}function K(a,c,d,e){var h,i,j,k,l,f=a,g=!1;if(!e.createSearchChoice||!e.tokenSeparators||1>e.tokenSeparators.length)return b;for(;;){for(i=-1,j=0,k=e.tokenSeparators.length;k>j&&(l=e.tokenSeparators[j],i=a.indexOf(l),!(i>=0));j++);if(0>i)break;if(h=a.substring(0,i),a=a.substring(i+l.length),h.length>0&&(h=e.createSearchChoice.call(this,h,c),h!==b&&null!==h&&e.id(h)!==b&&null!==e.id(h))){for(g=!1,j=0,k=c.length;k>j;j++)if(o(e.id(h),e.id(c[j]))){g=!0;break}g||d(h)}}return f!==a?a:b}function L(b,c){var d=function(){};return d.prototype=new b,d.prototype.constructor=d,d.prototype.parent=b.prototype,d.prototype=a.extend(d.prototype,c),d}if(window.Select2===b){var c,d,e,f,g,h,j,k,i={x:0,y:0},c={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case c.LEFT:case c.RIGHT:case c.UP:case c.DOWN:return!0}return!1},isControl:function(a){var b=a.which;switch(b){case c.SHIFT:case c.CTRL:case c.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){return a=a.which?a.which:a,a>=112&&123>=a}},l="
";j=a(document),g=function(){var a=1;return function(){return a++}}(),j.on("mousemove",function(a){i.x=a.pageX,i.y=a.pageY}),d=L(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(c){var d,e,h,i,f=".select2-results";this.opts=c=this.prepareOpts(c),this.id=c.id,c.element.data("select2")!==b&&null!==c.element.data("select2")&&c.element.data("select2").destroy(),this.container=this.createContainer(),this.containerId="s2id_"+(c.element.attr("id")||"autogen"+g()),this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1"),this.container.attr("id",this.containerId),this.body=u(function(){return c.element.closest("body")}),B(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.css(I(c.containerCss)),this.container.addClass(I(c.containerCssClass)),this.elementTabIndex=this.opts.element.attr("tabindex"),this.opts.element.data("select2",this).attr("tabindex","-1").before(this.container),this.container.data("select2",this),this.dropdown=this.container.find(".select2-drop"),this.dropdown.addClass(I(c.dropdownCssClass)),this.dropdown.data("select2",this),this.results=d=this.container.find(f),this.search=e=this.container.find("input.select2-input"),this.resultsPage=0,this.context=null,this.initContainer(),s(this.results),this.dropdown.on("mousemove-filtered touchstart touchmove touchend",f,this.bind(this.highlightUnderEvent)),v(80,this.results),this.dropdown.on("scroll-debounced",f,this.bind(this.loadMoreIfNeeded)),a(this.container).on("change",".select2-input",function(a){a.stopPropagation()}),a(this.dropdown).on("change",".select2-input",function(a){a.stopPropagation()}),a.fn.mousewheel&&d.mousewheel(function(a,b,c,e){var f=d.scrollTop();e>0&&0>=f-e?(d.scrollTop(0),y(a)):0>e&&d.get(0).scrollHeight-d.scrollTop()+e<=d.height()&&(d.scrollTop(d.get(0).scrollHeight-d.height()),y(a))}),r(e),e.on("keyup-change input paste",this.bind(this.updateResults)),e.on("focus",function(){e.addClass("select2-focused")}),e.on("blur",function(){e.removeClass("select2-focused")}),this.dropdown.on("mouseup",f,this.bind(function(b){a(b.target).closest(".select2-result-selectable").length>0&&(this.highlightUnderEvent(b),this.selectHighlighted(b))})),this.dropdown.on("click mouseup mousedown",function(a){a.stopPropagation()}),a.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource()),null!==c.maximumInputLength&&this.search.attr("maxlength",c.maximumInputLength);var h=c.element.prop("disabled");h===b&&(h=!1),this.enable(!h);var i=c.element.prop("readonly");i===b&&(i=!1),this.readonly(i),k=k||n(),this.autofocus=c.element.prop("autofocus"),c.element.prop("autofocus",!1),this.autofocus&&this.focus()},destroy:function(){var a=this.opts.element,c=a.data("select2");this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),c!==b&&(c.container.remove(),c.dropdown.remove(),a.removeClass("select2-offscreen").removeData("select2").off(".select2").prop("autofocus",this.autofocus||!1),this.elementTabIndex?a.attr({tabindex:this.elementTabIndex}):a.removeAttr("tabindex"),a.show())},optionToData:function(a){return a.is("option")?{id:a.prop("value"),text:a.text(),element:a.get(),css:a.attr("class"),disabled:a.prop("disabled"),locked:o(a.attr("locked"),"locked")||o(a.data("locked"),!0)}:a.is("optgroup")?{text:a.attr("label"),children:[],element:a.get(),css:a.attr("class")}:b},prepareOpts:function(c){var d,e,f,g,h=this;if(d=c.element,"select"===d.get(0).tagName.toLowerCase()&&(this.select=e=c.element),e&&a.each(["id","multiple","ajax","query","createSearchChoice","initSelection","data","tags"],function(){if(this in c)throw Error("Option '"+this+"' is not allowed for Select2 when attached to a ","
"," ","
    ","
","
"].join(""));return b},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.focusser.prop("disabled",!this.isInterfaceEnabled())},opening:function(){var b,c,d;this.opts.minimumResultsForSearch>=0&&this.showSearch(!0),this.parent.opening.apply(this,arguments),this.showSearchInput!==!1&&this.search.val(this.focusser.val()),this.search.focus(),b=this.search.get(0),b.createTextRange?(c=b.createTextRange(),c.collapse(!1),c.select()):b.setSelectionRange&&(d=this.search.val().length,b.setSelectionRange(d,d)),this.focusser.prop("disabled",!0).val(""),this.updateResults(!0),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.focusser.removeAttr("disabled"),this.focusser.focus())},focus:function(){this.opened()?this.close():(this.focusser.removeAttr("disabled"),this.focusser.focus())},isFocused:function(){return this.container.hasClass("select2-container-active")},cancel:function(){this.parent.cancel.apply(this,arguments),this.focusser.removeAttr("disabled"),this.focusser.focus()},initContainer:function(){var d,e=this.container,f=this.dropdown;0>this.opts.minimumResultsForSearch?this.showSearch(!1):this.showSearch(!0),this.selection=d=e.find(".select2-choice"),this.focusser=e.find(".select2-focusser"),this.focusser.attr("id","s2id_autogen"+g()),a("label[for='"+this.opts.element.attr("id")+"']").attr("for",this.focusser.attr("id")),this.focusser.attr("tabindex",this.elementTabIndex),this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){if(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)return y(a),b;switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),y(a),b;case c.ENTER:return this.selectHighlighted(),y(a),b;case c.TAB:return this.selectHighlighted({noFocus:!0}),b;case c.ESC:return this.cancel(a),y(a),b}}})),this.search.on("blur",this.bind(function(){document.activeElement===this.body().get(0)&&window.setTimeout(this.bind(function(){this.search.focus()}),0)})),this.focusser.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.ESC){if(this.opts.openOnEnter===!1&&a.which===c.ENTER)return y(a),b;if(a.which==c.DOWN||a.which==c.UP||a.which==c.ENTER&&this.opts.openOnEnter){if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return;return this.open(),y(a),b}return a.which==c.DELETE||a.which==c.BACKSPACE?(this.opts.allowClear&&this.clear(),y(a),b):b}})),r(this.focusser),this.focusser.on("keyup-change input",this.bind(function(a){if(this.opts.minimumResultsForSearch>=0){if(a.stopPropagation(),this.opened())return;this.open()}})),d.on("mousedown","abbr",this.bind(function(a){this.isInterfaceEnabled()&&(this.clear(),z(a),this.close(),this.selection.focus())})),d.on("mousedown",this.bind(function(b){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.opened()?this.close():this.isInterfaceEnabled()&&this.open(),y(b)})),f.on("mousedown",this.bind(function(){this.search.focus()})),d.on("focus",this.bind(function(a){y(a)})),this.focusser.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})).on("blur",this.bind(function(){this.opened()||(this.container.removeClass("select2-container-active"),this.opts.element.trigger(a.Event("select2-blur")))})),this.search.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.setPlaceholder()},clear:function(a){var b=this.selection.data("select2-data");if(b){var c=this.getPlaceholderOption();this.opts.element.val(c?c.val():""),this.selection.find(".select2-chosen").empty(),this.selection.removeData("select2-data"),this.setPlaceholder(),a!==!1&&(this.opts.element.trigger({type:"select2-removed",val:this.id(b),choice:b}),this.triggerChange({removed:b}))}},initSelection:function(){if(this.isPlaceholderOptionSelected())this.updateSelection([]),this.close(),this.setPlaceholder();else{var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.setPlaceholder())})}},isPlaceholderOptionSelected:function(){var a;return(a=this.getPlaceholderOption())!==b&&a.is(":selected")||""===this.opts.element.val()||this.opts.element.val()===b||null===this.opts.element.val()},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=a.find(":selected");b(c.optionToData(d))}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=c.val(),f=null;b.query({matcher:function(a,c,d){var g=o(e,b.id(d));return g&&(f=d),g},callback:a.isFunction(d)?function(){d(f)}:a.noop})}),b},getPlaceholder:function(){return this.select&&this.getPlaceholderOption()===b?b:this.parent.getPlaceholder.apply(this,arguments)},setPlaceholder:function(){var a=this.getPlaceholder();if(this.isPlaceholderOptionSelected()&&a!==b){if(this.select&&this.getPlaceholderOption()===b)return;this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.container.removeClass("select2-allowclear")}},postprocessResults:function(a,c,d){var e=0,f=this;if(this.findHighlightableChoices().each2(function(a,c){return o(f.id(c.data("select2-data")),f.opts.element.val())?(e=a,!1):b}),d!==!1&&(c===!0&&e>=0?this.highlight(e):this.highlight(0)),c===!0){var h=this.opts.minimumResultsForSearch;h>=0&&this.showSearch(J(a.results)>=h)}},showSearch:function(b){this.showSearchInput!==b&&(this.showSearchInput=b,this.dropdown.find(".select2-search").toggleClass("select2-search-hidden",!b),this.dropdown.find(".select2-search").toggleClass("select2-offscreen",!b),a(this.dropdown,this.container).toggleClass("select2-with-searchbox",b))},onSelect:function(a,b){if(this.triggerSelect(a)){var c=this.opts.element.val(),d=this.data();this.opts.element.val(this.id(a)),this.updateSelection(a),this.opts.element.trigger({type:"select2-selected",val:this.id(a),choice:a}),this.close(),b&&b.noFocus||this.selection.focus(),o(c,this.id(a))||this.triggerChange({added:a,removed:d})}},updateSelection:function(a){var d,e,c=this.selection.find(".select2-chosen");this.selection.data("select2-data",a),c.empty(),d=this.opts.formatSelection(a,c,this.opts.escapeMarkup),d!==b&&c.append(d),e=this.opts.formatSelectionCssClass(a,c),e!==b&&c.addClass(e),this.selection.removeClass("select2-default"),this.opts.allowClear&&this.getPlaceholder()!==b&&this.container.addClass("select2-allowclear") -},val:function(){var a,c=!1,d=null,e=this,f=this.data();if(0===arguments.length)return this.opts.element.val();if(a=arguments[0],arguments.length>1&&(c=arguments[1]),this.select)this.select.val(a).find(":selected").each2(function(a,b){return d=e.optionToData(b),!1}),this.updateSelection(d),this.setPlaceholder(),c&&this.triggerChange({added:d,removed:f});else{if(!a&&0!==a)return this.clear(c),b;if(this.opts.initSelection===b)throw Error("cannot call val() if initSelection() is not defined");this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){e.opts.element.val(a?e.id(a):""),e.updateSelection(a),e.setPlaceholder(),c&&e.triggerChange({added:a,removed:f})})}},clearSearch:function(){this.search.val(""),this.focusser.val("")},data:function(a,c){var d;return 0===arguments.length?(d=this.selection.data("select2-data"),d==b&&(d=null),d):(a&&""!==a?(d=this.data(),this.opts.element.val(a?this.id(a):""),this.updateSelection(a),c&&this.triggerChange({added:a,removed:d})):this.clear(c),b)}}),f=L(d,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container select2-container-multi"}).html(["","
","
    ","
","
"].join(""));return b},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=[];a.find(":selected").each2(function(a,b){d.push(c.optionToData(b))}),b(d)}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=p(c.val(),b.separator),f=[];b.query({matcher:function(c,d,g){var h=a.grep(e,function(a){return o(a,b.id(g))}).length;return h&&f.push(g),h},callback:a.isFunction(d)?function(){for(var a=[],c=0;e.length>c;c++)for(var g=e[c],h=0;f.length>h;h++){var i=f[h];if(o(g,b.id(i))){a.push(i),f.splice(h,1);break}}d(a)}:a.noop})}),b},selectChoice:function(a){var b=this.container.find(".select2-search-choice-focus");b.length&&a&&a[0]==b[0]||(b.length&&this.opts.element.trigger("choice-deselected",b),b.removeClass("select2-search-choice-focus"),a&&a.length&&(this.close(),a.addClass("select2-search-choice-focus"),this.opts.element.trigger("choice-selected",a)))},initContainer:function(){var e,d=".select2-choices";this.searchContainer=this.container.find(".select2-search-field"),this.selection=e=this.container.find(d);var f=this;this.selection.on("mousedown",".select2-search-choice",function(){f.search[0].focus(),f.selectChoice(a(this))}),this.search.attr("id","s2id_autogen"+g()),a("label[for='"+this.opts.element.attr("id")+"']").attr("for",this.search.attr("id")),this.search.on("input paste",this.bind(function(){this.isInterfaceEnabled()&&(this.opened()||this.open())})),this.search.attr("tabindex",this.elementTabIndex),this.keydowns=0,this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){++this.keydowns;var d=e.find(".select2-search-choice-focus"),f=d.prev(".select2-search-choice:not(.select2-locked)"),g=d.next(".select2-search-choice:not(.select2-locked)"),h=x(this.search);if(d.length&&(a.which==c.LEFT||a.which==c.RIGHT||a.which==c.BACKSPACE||a.which==c.DELETE||a.which==c.ENTER)){var i=d;return a.which==c.LEFT&&f.length?i=f:a.which==c.RIGHT?i=g.length?g:null:a.which===c.BACKSPACE?(this.unselect(d.first()),this.search.width(10),i=f.length?f:g):a.which==c.DELETE?(this.unselect(d.first()),this.search.width(10),i=g.length?g:null):a.which==c.ENTER&&(i=null),this.selectChoice(i),y(a),i&&i.length||this.open(),b}if((a.which===c.BACKSPACE&&1==this.keydowns||a.which==c.LEFT)&&0==h.offset&&!h.length)return this.selectChoice(e.find(".select2-search-choice:not(.select2-locked)").last()),y(a),b;if(this.selectChoice(null),this.opened())switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),y(a),b;case c.ENTER:return this.selectHighlighted(),y(a),b;case c.TAB:return this.selectHighlighted({noFocus:!0}),this.close(),b;case c.ESC:return this.cancel(a),y(a),b}if(a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.BACKSPACE&&a.which!==c.ESC){if(a.which===c.ENTER){if(this.opts.openOnEnter===!1)return;if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return}this.open(),(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)&&y(a),a.which===c.ENTER&&y(a)}}})),this.search.on("keyup",this.bind(function(){this.keydowns=0,this.resizeSearch()})),this.search.on("blur",this.bind(function(b){this.container.removeClass("select2-container-active"),this.search.removeClass("select2-focused"),this.selectChoice(null),this.opened()||this.clearSearch(),b.stopImmediatePropagation(),this.opts.element.trigger(a.Event("select2-blur"))})),this.container.on("click",d,this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").length>0||(this.selectChoice(null),this.clearPlaceholder(),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.open(),this.focusSearch(),b.preventDefault()))})),this.container.on("focus",d,this.bind(function(){this.isInterfaceEnabled()&&(this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.clearSearch()},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.search.prop("disabled",!this.isInterfaceEnabled())},initSelection:function(){if(""===this.opts.element.val()&&""===this.opts.element.text()&&(this.updateSelection([]),this.close(),this.clearSearch()),this.select||""!==this.opts.element.val()){var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.clearSearch())})}},clearSearch:function(){var a=this.getPlaceholder(),c=this.getMaxSearchWidth();a!==b&&0===this.getVal().length&&this.search.hasClass("select2-focused")===!1?(this.search.val(a).addClass("select2-default"),this.search.width(c>0?c:this.container.css("width"))):this.search.val("").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")&&this.search.val("").removeClass("select2-default")},opening:function(){this.clearPlaceholder(),this.resizeSearch(),this.parent.opening.apply(this,arguments),this.focusSearch(),this.updateResults(!0),this.search.focus(),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&this.parent.close.apply(this,arguments)},focus:function(){this.close(),this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(b){var c=[],d=[],e=this;a(b).each(function(){0>m(e.id(this),c)&&(c.push(e.id(this)),d.push(this))}),b=d,this.selection.find(".select2-search-choice").remove(),a(b).each(function(){e.addSelectedChoice(this)}),e.postprocessResults()},tokenize:function(){var a=this.search.val();a=this.opts.tokenizer.call(this,a,this.data(),this.bind(this.onSelect),this.opts),null!=a&&a!=b&&(this.search.val(a),a.length>0&&this.open())},onSelect:function(a,b){this.triggerSelect(a)&&(this.addSelectedChoice(a),this.opts.element.trigger({type:"selected",val:this.id(a),choice:a}),(this.select||!this.opts.closeOnSelect)&&this.postprocessResults(),this.opts.closeOnSelect?(this.close(),this.search.width(10)):this.countSelectableResults()>0?(this.search.width(10),this.resizeSearch(),this.getMaximumSelectionSize()>0&&this.val().length>=this.getMaximumSelectionSize()&&this.updateResults(!0),this.positionDropdown()):(this.close(),this.search.width(10)),this.triggerChange({added:a}),b&&b.noFocus||this.focusSearch())},cancel:function(){this.close(),this.focusSearch()},addSelectedChoice:function(c){var j,k,d=!c.locked,e=a("
  • "),f=a("
  • "),g=d?e:f,h=this.id(c),i=this.getVal();j=this.opts.formatSelection(c,g.find("div"),this.opts.escapeMarkup),j!=b&&g.find("div").replaceWith("
    "+j+"
    "),k=this.opts.formatSelectionCssClass(c,g.find("div")),k!=b&&g.addClass(k),d&&g.find(".select2-search-choice-close").on("mousedown",y).on("click dblclick",this.bind(function(b){this.isInterfaceEnabled()&&(a(b.target).closest(".select2-search-choice").fadeOut("fast",this.bind(function(){this.unselect(a(b.target)),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"),this.close(),this.focusSearch()})).dequeue(),y(b))})).on("focus",this.bind(function(){this.isInterfaceEnabled()&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))})),g.data("select2-data",c),g.insertBefore(this.searchContainer),i.push(h),this.setVal(i)},unselect:function(a){var c,d,b=this.getVal();if(a=a.closest(".select2-search-choice"),0===a.length)throw"Invalid argument: "+a+". Must be .select2-search-choice";c=a.data("select2-data"),c&&(d=m(this.id(c),b),d>=0&&(b.splice(d,1),this.setVal(b),this.select&&this.postprocessResults()),a.remove(),this.opts.element.trigger({type:"removed",val:this.id(c),choice:c}),this.triggerChange({removed:c}))},postprocessResults:function(a,b,c){var d=this.getVal(),e=this.results.find(".select2-result"),f=this.results.find(".select2-result-with-children"),g=this;e.each2(function(a,b){var c=g.id(b.data("select2-data"));m(c,d)>=0&&(b.addClass("select2-selected"),b.find(".select2-result-selectable").addClass("select2-selected"))}),f.each2(function(a,b){b.is(".select2-result-selectable")||0!==b.find(".select2-result-selectable:not(.select2-selected)").length||b.addClass("select2-selected")}),-1==this.highlight()&&c!==!1&&g.highlight(0),!this.opts.createSearchChoice&&!e.filter(".select2-result:not(.select2-selected)").length>0&&(!a||a&&!a.more&&0===this.results.find(".select2-no-results").length)&&H(g.opts.formatNoMatches,"formatNoMatches")&&this.results.append("
  • "+g.opts.formatNoMatches(g.search.val())+"
  • ")},getMaxSearchWidth:function(){return this.selection.width()-q(this.search)},resizeSearch:function(){var a,b,c,d,e,f=q(this.search);a=A(this.search)+10,b=this.search.offset().left,c=this.selection.width(),d=this.selection.offset().left,e=c-(b-d)-f,a>e&&(e=c-f),40>e&&(e=c-f),0>=e&&(e=a),this.search.width(e)},getVal:function(){var a;return this.select?(a=this.select.val(),null===a?[]:a):(a=this.opts.element.val(),p(a,this.opts.separator))},setVal:function(b){var c;this.select?this.select.val(b):(c=[],a(b).each(function(){0>m(this,c)&&c.push(this)}),this.opts.element.val(0===c.length?"":c.join(this.opts.separator)))},buildChangeDetails:function(a,b){for(var b=b.slice(0),a=a.slice(0),c=0;b.length>c;c++)for(var d=0;a.length>d;d++)o(this.opts.id(b[c]),this.opts.id(a[d]))&&(b.splice(c,1),c--,a.splice(d,1),d--);return{added:b,removed:a}},val:function(c,d){var e,f=this;if(0===arguments.length)return this.getVal();if(e=this.data(),e.length||(e=[]),!c&&0!==c)return this.opts.element.val(""),this.updateSelection([]),this.clearSearch(),d&&this.triggerChange({added:this.data(),removed:e}),b;if(this.setVal(c),this.select)this.opts.initSelection(this.select,this.bind(this.updateSelection)),d&&this.triggerChange(this.buildChangeDetails(e,this.data()));else{if(this.opts.initSelection===b)throw Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,function(b){var c=a.map(b,f.id);f.setVal(c),f.updateSelection(b),f.clearSearch(),d&&f.triggerChange(this.buildChangeDetails(e,this.data()))})}this.clearSearch()},onSortStart:function(){if(this.select)throw Error("Sorting of elements is not supported when attached to instead.");this.search.width(0),this.searchContainer.hide()},onSortEnd:function(){var b=[],c=this;this.searchContainer.show(),this.searchContainer.appendTo(this.searchContainer.parent()),this.resizeSearch(),this.selection.find(".select2-search-choice").each(function(){b.push(c.opts.id(a(this).data("select2-data")))}),this.setVal(b),this.triggerChange()},data:function(c,d){var f,g,e=this;return 0===arguments.length?this.selection.find(".select2-search-choice").map(function(){return a(this).data("select2-data")}).get():(g=this.data(),c||(c=[]),f=a.map(c,function(a){return e.opts.id(a)}),this.setVal(f),this.updateSelection(c),this.clearSearch(),d&&this.triggerChange(this.buildChangeDetails(g,this.data())),b)}}),a.fn.select2=function(){var d,g,h,i,j,c=Array.prototype.slice.call(arguments,0),k=["val","destroy","opened","open","close","focus","isFocused","container","dropdown","onSortStart","onSortEnd","enable","readonly","positionDropdown","data","search"],l=["val","opened","isFocused","container","data"],n={search:"externalSearch"};return this.each(function(){if(0===c.length||"object"==typeof c[0])d=0===c.length?{}:a.extend({},c[0]),d.element=a(this),"select"===d.element.get(0).tagName.toLowerCase()?j=d.element.prop("multiple"):(j=d.multiple||!1,"tags"in d&&(d.multiple=j=!0)),g=j?new f:new e,g.init(d);else{if("string"!=typeof c[0])throw"Invalid arguments to select2 plugin: "+c;if(0>m(c[0],k))throw"Unknown method: "+c[0];if(i=b,g=a(this).data("select2"),g===b)return;if(h=c[0],"container"===h?i=g.container:"dropdown"===h?i=g.dropdown:(n[h]&&(h=n[h]),i=g[h].apply(g,c.slice(1))),m(c[0],l)>=0)return!1}}),i===b?this:i},a.fn.select2.defaults={width:"copy",loadMorePadding:0,closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c,d){var e=[];return C(a.text,c.term,e,d),e.join("")},formatSelection:function(a,c,d){return a?d(a.text):b},sortResults:function(a){return a},formatResultCssClass:function(){return b},formatSelectionCssClass:function(){return b},formatNoMatches:function(){return"No matches found"},formatInputTooShort:function(a,b){var c=b-a.length;return"Please enter "+c+" more character"+(1==c?"":"s")},formatInputTooLong:function(a,b){var c=a.length-b;return"Please delete "+c+" character"+(1==c?"":"s")},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results..."},formatSearching:function(){return"Searching..."},minimumResultsForSearch:0,minimumInputLength:0,maximumInputLength:null,maximumSelectionSize:0,id:function(a){return a.id},matcher:function(a,b){return(""+b).toUpperCase().indexOf((""+a).toUpperCase())>=0},separator:",",tokenSeparators:[],tokenizer:K,escapeMarkup:D,blurOnChange:!1,selectOnBlur:!1,adaptContainerCssClass:function(a){return a},adaptDropdownCssClass:function(){return null}},a.fn.select2.ajaxDefaults={transport:a.ajax,params:{type:"GET",cache:!1,dataType:"json"}},window.Select2={query:{ajax:E,local:F,tags:G},util:{debounce:t,markMatch:C,escapeMarkup:D},"class":{"abstract":d,single:e,multi:f}}}}(jQuery); \ No newline at end of file diff --git a/templates/admin/article/blog_form.html b/templates/admin/article/blog_form.html index 16a15c83..057b565d 100644 --- a/templates/admin/article/blog_form.html +++ b/templates/admin/article/blog_form.html @@ -51,7 +51,15 @@ {{ form.publish_date.errors }} - + {% if not article %} +
    + +
    + {{ form.slug }} + {{ form.slug.errors }} +
    +
    + {% endif %} {# theme #}
    diff --git a/templates/admin/conference/conference.html b/templates/admin/conference/conference.html index 51811f7f..8d6a7f6b 100644 --- a/templates/admin/conference/conference.html +++ b/templates/admin/conference/conference.html @@ -134,6 +134,13 @@ {{ form.place.errors }}
    + {# place_alt #} +
    + +
    {{ form.place_alt }} + {{ form.place_alt.errors }} +
    +
    {# theme #}
    diff --git a/templates/admin/conference/conference_add.html b/templates/admin/conference/conference_add.html index efe93065..39fb4a90 100644 --- a/templates/admin/conference/conference_add.html +++ b/templates/admin/conference/conference_add.html @@ -47,7 +47,7 @@
    -

    Основная информация

    +

    Основная информаdция

    {# Hidden inputs uses for comparing with TmpFile objects #} @@ -99,6 +99,14 @@ {{ form.place.errors }}
    + + {# place #} +
    + +
    {{ form.place_alt }} + {{ form.place_alt.errors }} +
    +
    {# theme #}
    diff --git a/templates/admin/includes/admin_nav.html b/templates/admin/includes/admin_nav.html index 25226d51..f70cb26a 100644 --- a/templates/admin/includes/admin_nav.html +++ b/templates/admin/includes/admin_nav.html @@ -40,6 +40,7 @@
  • Страна
  • Город
  • Тематики
  • +
  • Тематики для блогов
  • Теги
  • Услуги
  • Главная страница
  • diff --git a/templates/admin/newsletters/contact_list.html b/templates/admin/newsletters/contact_list.html index d65cb1f7..ce58dc06 100644 --- a/templates/admin/newsletters/contact_list.html +++ b/templates/admin/newsletters/contact_list.html @@ -2,13 +2,70 @@ {% block body %} -
    -
    -

    Список контактов

    -
    -
    +
    +
    +

    Список контактов

    +
    +
    +
    +
    + + + Filter subscribers + + +
    + + +
    +{# #} + {{ form.email }} + +
    +
    + + +
    + + +
    + {{ form.theme }} +
    +
    + + +
    + + +
    + {{ form.country }} +
    +
    + + +
    + + +
    + {{ form.area }} +
    +
    + + +
    + + +
    + +
    +
    + +
    +
    + - +
    diff --git a/templates/admin/service/linked_service.html b/templates/admin/service/linked_service.html new file mode 100644 index 00000000..fd007a7c --- /dev/null +++ b/templates/admin/service/linked_service.html @@ -0,0 +1,142 @@ +{% extends 'base.html' %} +{% load static %} + +{% block scripts %} + + {# selects #} + + + + + + +{% endblock %} + +{% block body %} + + {# Uses multilang.html template for translated fields #} + {% csrf_token %} +
    +
    +

    Основная информация

    +
    + + {% for f in form %} +
    + +
    {{ f }} + {{ f.errors }} +
    +
    + {% endfor %} +
    + + +
    +
    + + + +{% endblock %} \ No newline at end of file diff --git a/templates/admin/service/linked_service_confirm_delete.html b/templates/admin/service/linked_service_confirm_delete.html new file mode 100644 index 00000000..21cc73d0 --- /dev/null +++ b/templates/admin/service/linked_service_confirm_delete.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% block sidebar %}{% endblock %} +{% block body %} +{% csrf_token %} +
    +

    Вы точно хотите удалить настройку для сервиса "{{ object.service.name }}" ?

    + + Нет +
    + +{% endblock %} \ No newline at end of file diff --git a/templates/admin/service/linked_service_list.html b/templates/admin/service/linked_service_list.html new file mode 100644 index 00000000..eb4af0cb --- /dev/null +++ b/templates/admin/service/linked_service_list.html @@ -0,0 +1,48 @@ +{% extends 'base.html' %} + +{% block body %} + +
    +
    +

    Настройки услуг

    +
    +
    +
    + + + + + + + + + + + + + {{ object }} + {% for item in object_list %} + + + + + + + + + + {% endfor %} + +
    idНазвание услугиТипСтранВиставокКонференций 
    {{ item.id }}{{ item.service.name }}{% if item.service.type.mask == 1 %} expo {% elif item.service.type.mask == 2%} conf {% elif item.service.type.mask == 3 %} expo, conf{% endif %}{{ item.countries.count }}{{ item.expositions.count }}{{ item.conferences.count }} + + Изменить + + + Удалить + +
    +
    + +
    + +{% endblock %} diff --git a/templates/admin/service/service_add.html b/templates/admin/service/service_add.html index ce487c45..9c5b2403 100644 --- a/templates/admin/service/service_add.html +++ b/templates/admin/service/service_add.html @@ -48,7 +48,7 @@ {% include 'admin/forms/multilang.html' %} {% endwith %} {# url #} -
    +
    {{ form.url }} diff --git a/templates/admin/theme/theme_blog_confirm_delete.html b/templates/admin/theme/theme_blog_confirm_delete.html new file mode 100644 index 00000000..ec7b8c9f --- /dev/null +++ b/templates/admin/theme/theme_blog_confirm_delete.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% block sidebar %}{% endblock %} +{% block body %} +
    {% csrf_token %} +
    +

    Вы точно хотите удалить "{{ object.name }}" ?

    + + Нет +
    +
    +{% endblock %} \ No newline at end of file diff --git a/templates/admin/theme/theme_blog_list.html b/templates/admin/theme/theme_blog_list.html new file mode 100644 index 00000000..ad6a7dcd --- /dev/null +++ b/templates/admin/theme/theme_blog_list.html @@ -0,0 +1,65 @@ +{% extends 'admin_list.html' %} + +{% block body %} + +{#
    #} +{#
    #} +{#

    Фильтры

    #} +{#
    #} +{#
    #} +{#
    #} +{# {{ form }}#} +{##} +{# #} +{#
    #} +{#
    #} +{##} +{#
    #} + +
    +
    +

    Список тем

    +
    +
    + + + + + + + + + + + + + + + + + {% for item in object_list %} + + + + + + + + + {% endfor %} + +
    idНазвание  
    {{ item.id }}{{ item.name }} + + Изменить + + + + Удалить + +
    + Добавить тематику +
    + {# pagination #} + {% include 'admin/includes/admin_pagination.html' with page_obj=object_list %} +
    +{% endblock %} \ No newline at end of file diff --git a/templates/admin/theme/theme_blog_new.html b/templates/admin/theme/theme_blog_new.html new file mode 100644 index 00000000..fe5406b8 --- /dev/null +++ b/templates/admin/theme/theme_blog_new.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} +{% load static %} + +{% block scripts %} + + + {# selects #} + + + + + + +{% endblock %} + +{% block body %} + +
    {% csrf_token %}{{ form.errors }} +
    + {% if object %} Изменить {% else %} Добавить {% endif %}тему +
    +
    +

    Информация

    +
    +
    + {% for field in form %} + + + + {% endfor %} +
    +
    + + +
    + + +
    + +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/templates/client/article/article.html b/templates/client/article/article.html index e36596ee..505714e5 100644 --- a/templates/client/article/article.html +++ b/templates/client/article/article.html @@ -19,7 +19,7 @@ {% include 'client/includes/article/article_logo.html' with obj=object %}

    {{ object.main_title }}

    - {{ object.publish_date|date:"d E Y" }}{% if object.theme.all.exists %}{% include 'includes/article_theme.html' with obj=object %}{% endif %} + {{ object.publish_date|date:"d E Y" }}{% if object.blog_theme.all.exists %}{% include 'client/includes/article_theme.html' with obj=object %}{% endif %} {% if request.user.is_admin %} {% trans 'изменить' %} {% endif %} @@ -53,7 +53,7 @@ {% include 'includes/show_logo.html' with obj=blog %}

    {{ blog.main_title }}

    {{ blog.preview }}

    - {{ blog.created|date:"d E Y" }}Евгения Булавина + {{ blog.publish_date|date:"d E Y" }}Евгения Булавина
    {% endfor %} diff --git a/templates/client/includes/conference/default_description.html b/templates/client/includes/conference/default_description.html index cd2633b1..2b4dade5 100644 --- a/templates/client/includes/conference/default_description.html +++ b/templates/client/includes/conference/default_description.html @@ -2,7 +2,7 @@
    {% blocktrans with name=conf.name%}

    Конференция {{name}} проходит {% endblocktrans %}{% include 'client/includes/show_date_block.html' with obj=conf %} -{% blocktrans with city=conf.name country=country.name name=conf.name id=conf.city.id code=request.LANGUAGE_CODE date1=conf.data_begin|date:'j' date2=conf.data_begin|date:'Y' date3=conf.data_begin|date:'n' date4=conf.data_end|date:'j' date5=conf.data_end|date:'Y' date6=conf.data_end|date:'n' %} +{% blocktrans with city=city.name country=country.name name=conf.name id=conf.city.id code=request.LANGUAGE_CODE date1=conf.data_begin|date:'j' date2=conf.data_begin|date:'Y' date3=conf.data_begin|date:'n' date4=conf.data_end|date:'j' date5=conf.data_end|date:'Y' date6=conf.data_end|date:'n' %} в городе {{city}}, {{country}}. Посмотреть, как проехать в место проведения конференции, можно на сайте конгрессной площадки. Деловая программа {{name}} разбита на секции по дням и размещается на сайте мероприятия с подробным списком diff --git a/theme/admin.py b/theme/admin.py index da323984..8914587f 100644 --- a/theme/admin.py +++ b/theme/admin.py @@ -3,15 +3,13 @@ from django.shortcuts import render_to_response from django.http import HttpResponseRedirect, HttpResponse from django.core.context_processors import csrf from django.conf import settings -from django.forms.formsets import BaseFormSet, formset_factory -from django.forms.models import modelformset_factory -from django.contrib.contenttypes.models import ContentType +from django.views.generic import CreateView, UpdateView, DeleteView from django.contrib.auth.decorators import login_required #forms and models from forms import ThemeForm, TagForm, ThemeDeleteForm, TagDeleteForm, TagFilterForm, ThemeFilterForm -from models import Theme, Tag +from models import Theme, Tag, ThemeBlog #custom views -from functions.custom_views import objects_list, add_object, delete_object +from functions.custom_views import objects_list, add_object, delete_object, ListView from functions.views_help import get_referer from functions.admin_views import AdminListView @@ -170,4 +168,37 @@ class ThemeListView(AdminListView): class TagListView(AdminListView): template_name = 'admin/theme/tag_list.html' form_class = TagFilterForm - model = Tag \ No newline at end of file + model = Tag + + +from functions.custom_views import ListView +from django.core.urlresolvers import reverse_lazy +from .forms import ThemeBlogForm + + +class ThemeBlogListView(ListView): + template_name = 'admin/theme/theme_blog_list.html' + model = ThemeBlog + paginate_by = settings.ADMIN_PAGINATION + + +class ThemeBlogCreateView(CreateView): + template_name = "admin/theme/theme_blog_new.html" + model = ThemeBlog + form_class = ThemeBlogForm + success_url = reverse_lazy("theme_blog_all") + + +class ThemeBlogUpdateView(UpdateView): + template_name = "admin/theme/theme_blog_new.html" + model = ThemeBlog + form_class = ThemeBlogForm + success_url = reverse_lazy("theme_blog_all") + pk_url_kwarg = "theme_id" + + +class ThemeBlogDeleteView(DeleteView): + template_name = "admin/theme/theme_blog_confirm_delete.html" + model = ThemeBlog + success_url = reverse_lazy("theme_blog_all") + pk_url_kwarg = "theme_id" diff --git a/theme/admin_urls.py b/theme/admin_urls.py index c1803d4f..0caf5c87 100644 --- a/theme/admin_urls.py +++ b/theme/admin_urls.py @@ -1,19 +1,25 @@ # -*- coding: utf-8 -*- from django.conf.urls import patterns, include, url from admin import TagListView, ThemeListView +from .admin import ThemeBlogCreateView, ThemeBlogUpdateView, ThemeBlogDeleteView, ThemeBlogListView + urlpatterns = patterns('theme.admin', url(r'^theme/add.*/$', 'theme_add'), url(r'^tag/add.*/$', 'tag_add'), + url(r'^blog_theme/add/$', ThemeBlogCreateView.as_view(), name = 'theme_blog_new'), url(r'^theme/delete/(?P\d+)/$', 'theme_delete'), url(r'^tag/delete/(?P\d+)/$', 'tag_delete'), + url(r'^blog_theme/delete/(?P\d+)/$', ThemeBlogDeleteView.as_view(), name = 'theme_blog_delete'), url(r'^theme/change/(?P\d+).*/$', 'theme_change'), + url(r'^blog_theme/change/(?P\d+).*/$', ThemeBlogUpdateView.as_view(), name= "theme_blog_change"), url(r'^tag/change/(?P\d+).*/$', 'tag_change'), url(r'^theme/copy/(?P\d+).*/$', 'theme_copy'), url(r'^tag/copy/(?P\d+).*/$', 'tag_copy'), #url(r'^theme/all/$', 'theme_all'), #url(r'^tag/all/$', 'tag_all'), url(r'^theme/all/$', ThemeListView.as_view()), + url(r'^blog_theme/all/$', ThemeBlogListView.as_view(), name="theme_blog_all"), url(r'^tag/all/$', TagListView.as_view()), url(r'^tag/search/$', 'search_tag'), url(r'^tag/search-without-theme/$', 'search2'), diff --git a/theme/forms.py b/theme/forms.py index 7cd0512f..5f166b04 100644 --- a/theme/forms.py +++ b/theme/forms.py @@ -143,4 +143,19 @@ class ThemeFilterForm(AdminFilterForm): class TagFilterForm(AdminFilterForm): - model = Tag \ No newline at end of file + model = Tag + +from hvad.forms import TranslatableModelForm +from .models import ThemeBlog + + +class ThemeBlogForm(TranslatableModelForm): + class Meta: + model = ThemeBlog + fields = ['url', 'name', 'main_title', 'description', 'inflect'] + widgets = {'url':forms.TextInput(attrs={'required':False})} + + def save(self, commit= True): + if not 'url' in self.cleaned_data: + self.cleaned_data['url'] = translit_with_separator(self.cleaned_data['name']) + return super(ThemeBlogForm, self).save(commit=True) \ No newline at end of file diff --git a/theme/models.py b/theme/models.py index 50ece870..93908fd7 100644 --- a/theme/models.py +++ b/theme/models.py @@ -133,6 +133,30 @@ class Theme(TranslatableModel): parent = {} return parent + +class ThemeBlog(TranslatableModel): + + url = models.SlugField(unique=True, max_length=255) + + translations = TranslatedFields( + name=models.CharField(max_length=255), + main_title=models.CharField(max_length=255, blank=True), + description=models.TextField(blank=True) + ) + + inflect = models.CharField(max_length=255, blank=True) + + def __unicode__(self): + return self.lazy_translation_getter('name', unicode(self.pk)) + + def get_all_names(self): + return [item['name'] for item in self.translations.all().values('name')] + + def get_index_text(self): + translation.activate('ru') + return ' '.join(self.get_all_names()) + + from django.db import IntegrityError class Tag(TranslatableModel): """ diff --git a/theme/views.py b/theme/views.py index 1deadf9b..ce83a38a 100644 --- a/theme/views.py +++ b/theme/views.py @@ -21,11 +21,8 @@ def get_tag(request): def get_article_tags(request): - themes = request.GET.getlist('themes[]') term = request.GET['term'].capitalize() qs = Tag.objects.language().exclude(article=None).filter(article__type=1).distinct() - if themes: - qs = qs.filter(theme__id__in=themes).order_by('translations__name') if term: qs = qs.filter(translations__name__contains=term) result = [{'id': tag.id, 'label': '%s (%s)'%(tag.name, tag.theme.name)} for tag in qs]