fix data validation for archive operations
[qcg-portal.git] / filex / forms.py
1 # coding=utf-8
2 import os
3
4 from django import forms
5 from django.core.exceptions import ValidationError
6 from django.core.validators import RegexValidator
7 from django.utils.http import urlquote
8
9 from filex.models import Favorite
10
11
12 msg = u'Invalid value'
13 host_validator = RegexValidator(r'^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+'
14                                 r'(?:[a-zA-Z]{2,6}\.?|[a-zA-Z0-9-]{2,}(?<!-)\.?))(?::\d+)?$', msg)
15 path_validator = RegexValidator(r'^~?(?:/[^/\0]*)*$', msg)
16 name_validator = RegexValidator(r'^[^/\0]+$', msg)
17
18
19 def clean_path(path):
20     return urlquote(os.path.normpath(path), safe='/~')
21
22
23 class FavoriteForm(forms.ModelForm):
24     class Meta:
25         model = Favorite
26         fields = ('owner', 'host', 'path')
27         widgets = {'owner': forms.HiddenInput()}
28
29
30 class HostForm(forms.Form):
31     host = forms.CharField(label=u'Host', max_length=256, validators=[host_validator], widget=forms.HiddenInput())
32
33
34 class HostPathForm(HostForm):
35     path = forms.CharField(label=u'Ścieżka', max_length=1024, validators=[path_validator], widget=forms.HiddenInput())
36
37     def clean_path(self):
38         return clean_path(self.cleaned_data['path'])
39
40
41 class HostPathNameForm(HostPathForm):
42     name = forms.CharField(label=u'Nazwa', max_length=256, validators=[name_validator])
43
44     def clean_name(self):
45         return clean_path(self.cleaned_data['name'])
46
47
48 class HostItemsForm(HostForm):
49     dirs = forms.MultipleChoiceField(label=u'Katalogi', required=False, widget=forms.MultipleHiddenInput())
50     files = forms.MultipleChoiceField(label=u'Pliki', required=False, widget=forms.MultipleHiddenInput())
51
52     def __init__(self, data=None, *args, **kwargs):
53         super(HostItemsForm, self).__init__(data, *args, **kwargs)
54
55         if data is not None:
56             # accept user defined choices
57             self.fields['dirs'].choices += ((v, v) for v in data.getlist('dirs'))
58             self.fields['files'].choices += ((v, v) for v in data.getlist('files'))
59
60     def clean(self):
61         data = super(HostItemsForm, self).clean()
62
63         if not (data.get('dirs') or data.get('files')):
64             raise ValidationError('No items specified')
65
66         return data
67
68     @staticmethod
69     def _clean_paths(values):
70         errors, cleaned = [], []
71         for name in values:
72             try:
73                 path_validator(name)
74             except ValidationError as e:
75                 e.message += ' - ' + name
76                 errors.append(e)
77             else:
78                 cleaned.append(clean_path(name))
79         if errors:
80             raise ValidationError(errors)
81
82         return cleaned
83
84     def clean_dirs(self):
85         return self._clean_paths(self.cleaned_data['dirs'])
86
87     def clean_files(self):
88         return self._clean_paths(self.cleaned_data['files'])
89     
90     
91 class RenameForm(HostForm):
92     src = forms.CharField(label=u'Stara nazwa', max_length=1024, validators=[path_validator], widget=forms.HiddenInput())
93     dst = forms.CharField(label=u'Nowa nazwa', max_length=1024, validators=[path_validator])
94
95     def clean_src(self):
96         return clean_path(self.cleaned_data['src'])
97
98     def clean_dst(self):
99         return clean_path(self.cleaned_data['dst'])
100
101
102 class ExtractForm(HostPathForm):
103     dst = forms.CharField(label=u'Katalog docelowy', max_length=1024, validators=[path_validator])
104
105     def clean_dst(self):
106         return clean_path(self.cleaned_data['dst'])
107
108
109 class CompressForm(HostPathForm):
110     archive = forms.CharField(label=u'Nazwa', max_length=1024, validators=[path_validator])
111     files = forms.MultipleChoiceField(label=u'Pliki', widget=forms.MultipleHiddenInput())
112
113     def __init__(self, data=None, *args, **kwargs):
114         super(CompressForm, self).__init__(data, *args, **kwargs)
115
116         if data is not None:
117             # accept user defined choices
118             self.fields['files'].choices += ((v, v) for v in data.getlist('files'))
119
120     def clean_files(self):
121         errors, cleaned = [], []
122         for name in self.cleaned_data['files']:
123             try:
124                 name_validator(name)
125             except ValidationError as e:
126                 e.message += ' - ' + name
127                 errors.append(e)
128             else:
129                 cleaned.append(clean_path(name))
130         if errors:
131             raise ValidationError(errors)
132
133         return cleaned
134
135     def clean_archive(self):
136         return clean_path(self.cleaned_data['archive'])
137
138
139 class ArchiveForm(CompressForm):
140     ZIP = '.zip'
141     GZIP = '.tar.gz'
142     BZIP = '.tar.bz2'
143
144     TYPE_CHOICES = (
145         (ZIP, 'zip'),
146         (GZIP, 'tar.gz'),
147         (BZIP, 'tar.bz2'),
148     )
149
150     type = forms.ChoiceField(label=u'Typ archiwum', choices=TYPE_CHOICES, initial=ZIP)