check and update tags on save in editor
[redakcja.git] / apps / catalogue / forms.py
1 # -*- coding: utf-8 -*-
2 #
3 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 #
6 from django.utils.encoding import force_text
7 from django.utils.html import format_html
8 from django.utils.safestring import mark_safe
9
10 from catalogue.models import Category
11 from catalogue.models import Tag
12 from django import forms
13 from django.utils.translation import ugettext_lazy as _
14
15 from catalogue.constants import MASTERS
16
17
18 def tag_field(category_tag, required=True):
19     category = Category.objects.get(dc_tag=category_tag)
20     return forms.ModelMultipleChoiceField(queryset=category.tag_set.all(), required=required)
21
22
23 class DocumentCreateForm(forms.Form):
24     """
25         Form used for creating new documents.
26     """
27     owner_organization = forms.CharField(required=False)
28     title = forms.CharField()
29     publisher = forms.CharField(required=False)
30     description = forms.CharField(required=False)
31     cover = forms.FileField(required=False)
32
33     def clean_cover(self):
34         cover = self.cleaned_data['cover']
35         if cover and cover.name.rsplit('.', 1)[-1].lower() not in ('jpg', 'jpeg', 'png', 'gif', 'svg'):
36             raise forms.ValidationError(_('The cover should be an image file (jpg/png/gif)'))
37         return file
38
39
40 class TagForm(forms.Form):
41     def __init__(self, category, instance=None, tutorial_no=None, *args, **kwargs):
42         super(TagForm, self).__init__(*args, **kwargs)
43         self.category = category
44         self.instance = instance
45         self.field().queryset = Tag.objects.filter(category=self.category)
46         self.field().label = self.category.label.capitalize()
47         if tutorial_no and category.tutorial:
48             self.field().widget.attrs.update({
49                 'data-toggle': 'tutorial',
50                 'data-tutorial': str(tutorial_no),
51                 'data-placement': 'bottom',
52                 'data-content': category.tutorial,
53             })
54         if self.instance:
55             self.field().initial = self.initial()
56
57     def save(self, instance=None):
58         instance = instance or self.instance
59         assert instance, 'No instance provided'
60         self.category.set_tags_for(instance, self.cleaned_tags())
61
62     def field(self):
63         raise NotImplementedError
64
65     def initial(self):
66         raise NotImplementedError
67
68     def cleaned_tags(self):
69         raise NotImplementedError
70
71     def metadata_rows(self):
72         return '\n'.join(
73             '<dc:%(name)s>%(value)s</dc:%(name)s>' % {'name': tag.category.dc_tag, 'value': tag.dc_value}
74             for tag in self.cleaned_tags())
75
76
77 class TagSelect(forms.Select):
78     def render_option(self, selected_choices, option_value, option_label):
79         if option_value is None:
80             option_value = ''
81         help_html = ''
82         if option_value:
83             tag = Tag.objects.get(id=int(option_value))
84             if tag.help_text:
85                 help_html = mark_safe(' data-help="%s"' % tag.help_text)
86         option_value = force_text(option_value)
87         if option_value in selected_choices:
88             selected_html = mark_safe(' selected="selected"')
89             if not self.allow_multiple_selected:
90                 # Only allow for a single selection.
91                 selected_choices.remove(option_value)
92         else:
93             selected_html = ''
94         return format_html(
95             u'<option value="{}"{}{}>{}</option>',
96             option_value, selected_html, help_html, force_text(option_label))
97
98
99 class TagSingleForm(TagForm):
100     tag = forms.ModelChoiceField(
101         Tag.objects.none(),
102         widget=TagSelect(attrs={
103             'class': 'form-control',
104         })
105     )
106
107     def field(self):
108         return self.fields['tag']
109
110     def initial(self):
111         return self.instance.tags.get(category=self.category)
112
113     def cleaned_tags(self):
114         return [self.cleaned_data['tag']]
115
116
117 class TagMultipleForm(TagForm):
118     tags = forms.ModelMultipleChoiceField(
119         Tag.objects.none(), required=False,
120         widget=forms.SelectMultiple(attrs={
121             'class': 'chosen-select',
122             'data-placeholder': _('Choose'),
123         }))
124
125     def field(self):
126         return self.fields['tags']
127
128     def initial(self):
129         return self.instance.tags.filter(category=self.category)
130
131     def cleaned_tags(self):
132         return self.cleaned_data['tags']
133
134
135 class DocumentsUploadForm(forms.Form):
136     """
137         Form used for uploading new documents.
138     """
139     file = forms.FileField(required=True, label=_('ZIP file'))
140     dirs = forms.BooleanField(
141         label=_('Directories are documents in chunks'),
142         widget=forms.CheckboxInput(attrs={'disabled': 'disabled'}))
143
144     def clean(self):
145         file = self.cleaned_data['file']
146
147         import zipfile
148         try:
149             z = self.cleaned_data['zip'] = zipfile.ZipFile(file)
150         except zipfile.BadZipfile:
151             raise forms.ValidationError("Should be a ZIP file.")
152         if z.testzip():
153             raise forms.ValidationError("ZIP file corrupt.")
154
155         return self.cleaned_data
156
157
158 class ChooseMasterForm(forms.Form):
159     """
160         Form used for fixing the chunks in a book.
161     """
162
163     master = forms.ChoiceField(choices=((m, m) for m in MASTERS))
164
165
166 class DocumentForkForm(forms.Form):
167     """
168         Form used for forking documents.
169     """
170     owner_organization = forms.CharField(required=False)