Commit | Line | Data |
---|---|---|
6ef8ead4 | 1 | # -*- encoding: utf-8 -*- |
29e2a8fd | 2 | import operator |
23b5b3d5 | 3 | import re |
116db1fd | 4 | |
264a3210 | 5 | from django.core.urlresolvers import reverse as url |
6a6986bc | 6 | from django.db import models |
29e2a8fd | 7 | from django.db.models import Q |
5212238e | 8 | from django.db.models.query import QuerySet |
6ef8ead4 | 9 | from django.contrib import admin |
6d885e0c | 10 | from django.contrib.auth.admin import UserAdmin |
11 | from django.contrib.auth.models import User | |
6a6986bc | 12 | from django.contrib.admin.filterspecs import RelatedFilterSpec, FilterSpec |
23b5b3d5 | 13 | from django.utils.safestring import mark_safe |
6a6986bc | 14 | from django.utils.translation import ugettext as _ |
15 | from django.utils.encoding import smart_unicode, iri_to_uri | |
e3c3296e | 16 | from django.http import HttpResponseRedirect |
116db1fd | 17 | |
81fe476e PP |
18 | from models import SourceActualite, Actualite, ActualiteVoir, Discipline, \ |
19 | Evenement, EvenementVoir, Record, RecordCategorie, \ | |
20 | ListSet, HarvestLog, Profile, PageStatique | |
21 | ||
23b5b3d5 | 22 | from savoirs.globals import META |
6ef8ead4 | 23 | |
6a6986bc | 24 | class ListSetFilterSpec(RelatedFilterSpec): |
25 | """ | |
26 | Filtre custom automatiquement lié à un field nommé 'listsets'. Il a pour but de s'afficher | |
27 | lorsqu'un server a déjà été présélectionné. Dans ce cas, il affiche une liste qui contient les | |
28 | listsets de ce server. | |
29 | """ | |
30 | def __init__(self, f, request, params, model, model_admin): | |
31 | super(ListSetFilterSpec, self).__init__(f, request, params, model, model_admin) | |
32 | self.server_name = request.GET.get('server', None) | |
33 | ||
34 | def has_output(self): | |
35 | return self.server_name is not None | |
36 | ||
6a6986bc | 37 | FilterSpec.filter_specs.insert(0, (lambda f: f.name == 'listsets', ListSetFilterSpec)) |
38 | ||
23b5b3d5 | 39 | # Ces deux classes permettent d'implémenter la possibilité d'avoir un champs readonly_fields |
40 | # dans l.administration. | |
41 | # Ce champs est devenu natif à partir de la version 1.2 | |
42 | # http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields | |
43 | from django import forms | |
44 | class ReadOnlyWidget(forms.Widget): | |
45 | def __init__(self, original_value, display_value): | |
46 | self.original_value = original_value | |
47 | self.display_value = display_value | |
48 | ||
49 | super(ReadOnlyWidget, self).__init__() | |
50 | ||
51 | def render(self, name, value, attrs=None): | |
52 | if self.display_value is not None: | |
53 | output = self.display_value | |
54 | else: | |
55 | output = unicode(self.original_value) | |
56 | ||
d972b61d | 57 | # pour les relations |
58 | try: | |
59 | output = ", ".join([ls.name for ls in self.original_value.get_query_set()]) | |
60 | except: | |
61 | pass | |
62 | ||
23b5b3d5 | 63 | is_url = re.match('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', output) |
64 | if is_url: | |
65 | output = "<a target='_blank' href='%s'>%s</a>" % (output, output) | |
d972b61d | 66 | |
23b5b3d5 | 67 | return mark_safe(output) |
68 | ||
69 | def value_from_datadict(self, data, files, name): | |
70 | return self.original_value | |
71 | ||
72 | class ReadOnlyAdminFields(object): | |
73 | def get_form(self, request, obj=None): | |
74 | form = super(ReadOnlyAdminFields, self).get_form(request, obj) | |
75 | ||
76 | if hasattr(self, 'readonly_fields'): | |
77 | for field_name in self.readonly_fields: | |
78 | if field_name in form.base_fields: | |
79 | ||
80 | if hasattr(obj, 'get_%s_display' % field_name): | |
81 | display_value = getattr(obj, 'get_%s_display' % field_name)() | |
82 | else: | |
83 | display_value = None | |
84 | ||
85 | form.base_fields[field_name].widget = ReadOnlyWidget(getattr(obj, field_name, ''), display_value) | |
86 | form.base_fields[field_name].required = False | |
87 | return form | |
88 | ||
5212238e EMS |
89 | class RecordAdminQuerySet(QuerySet): |
90 | ||
91 | def filter(self, *args, **kwargs): | |
92 | """Gère des filtres supplémentaires pour l'admin. | |
93 | ||
94 | C'est la seule façon que j'ai trouvée de contourner les mécanismes | |
95 | de recherche de l'admin.""" | |
96 | search = kwargs.pop('admin_search', None) | |
97 | search_titre = kwargs.pop('admin_search_titre', None) | |
98 | search_sujet = kwargs.pop('admin_search_sujet', None) | |
99 | search_description = kwargs.pop('admin_search_description', None) | |
100 | search_auteur = kwargs.pop('admin_search_auteur', None) | |
101 | ||
102 | if search: | |
103 | qs = self | |
104 | search_all = not (search_titre or search_description or search_sujet or search_auteur) | |
105 | fields = [] | |
106 | if search_titre or search_all: | |
107 | fields += ['title', 'alt_title'] | |
108 | if search_description or search_all: | |
109 | fields += ['description', 'abstract'] | |
110 | if search_sujet or search_all: | |
111 | fields += ['subject'] | |
112 | if search_auteur or search_all: | |
113 | fields += ['creator', 'contributor'] | |
114 | ||
115 | for bit in search.split(): | |
116 | or_queries = [Q(**{field + '__icontains': bit}) for field in fields] | |
117 | qs = qs.filter(reduce(operator.or_, or_queries)) | |
118 | ||
119 | if args or kwargs: | |
120 | qs = super(RecordAdminQuerySet, qs).filter(*args, **kwargs) | |
121 | return qs | |
122 | else: | |
123 | return super(RecordAdminQuerySet, self).filter(*args, **kwargs) | |
23b5b3d5 | 124 | |
125 | class RecordAdmin(ReadOnlyAdminFields, admin.ModelAdmin): | |
a5f76eb4 EMS |
126 | fields = ['server', 'title', 'creator', 'description', 'modified', |
127 | 'identifier', 'uri', 'source', 'contributor', 'publisher', | |
656b9c0f PP |
128 | 'type', 'format', 'language', 'categorie', 'disciplines', |
129 | 'thematiques','pays', 'regions', 'validated'] | |
23b5b3d5 | 130 | |
23b5b3d5 | 131 | readonly_fields = [] |
da9020f3 | 132 | |
a5f76eb4 | 133 | list_filter = ('validated', 'server', 'listsets', 'pays', 'regions', |
656b9c0f | 134 | 'disciplines', 'thematiques', 'categorie') |
18dbd2cf | 135 | list_display = ('title', 'subject', 'uri_display', 'creator', |
656b9c0f | 136 | 'categorie', 'est_complet', 'validated',) |
18dbd2cf EMS |
137 | list_editable = ('validated',) |
138 | list_per_page = 25 | |
139 | ||
140 | actions = ['assigner_pays', 'assigner_regions', 'assigner_disciplines', | |
656b9c0f | 141 | 'assigner_thematiques', 'assigner_categorie'] |
da9020f3 | 142 | |
23b5b3d5 | 143 | def __init__(self, *args, **kwargs): |
144 | """Surcharge l'initialisation pour définir les champs de recherche dynamiquement, | |
145 | et les champs en lecture seule uniquement.""" | |
23b5b3d5 | 146 | self.readonly_fields = META.keys() |
d972b61d | 147 | self.readonly_fields.append('listsets') |
23b5b3d5 | 148 | super(RecordAdmin, self).__init__(*args, **kwargs) |
6d885e0c | 149 | |
1719bf4e | 150 | def queryset(self, request): |
5212238e | 151 | return RecordAdminQuerySet(Record) |
77b0fac0 EMS |
152 | |
153 | # Présentation de l'information | |
18dbd2cf | 154 | |
6d885e0c | 155 | def est_complet(self, obj): |
6d885e0c | 156 | v = obj.est_complet() |
157 | return '<img src="/admin_media/img/admin/icon-%s.gif" alt="%d"/>' % (('no','yes')[v], v) | |
158 | est_complet.allow_tags = True | |
18dbd2cf | 159 | est_complet.short_description = u'complet' |
23b5b3d5 | 160 | |
18dbd2cf | 161 | def uri_display(self, obj): |
da9020f3 | 162 | return "<a target='_blank' href='%s'>%s</a>" % (obj.uri, obj.uri) |
18dbd2cf EMS |
163 | uri_display.allow_tags = True |
164 | uri_display.short_description = u'lien' | |
da9020f3 | 165 | |
18dbd2cf | 166 | def description_display(self, obj): |
23b5b3d5 | 167 | max = 140 |
168 | if obj.description is not None and len(obj.description) > max: | |
169 | return "%s..." % obj.description[:max] | |
170 | else: | |
171 | return obj.description | |
18dbd2cf | 172 | description_display.short_description = u'description' |
da9020f3 | 173 | |
18dbd2cf | 174 | # Actions |
2be148fe | 175 | |
e3c3296e | 176 | def assigner_pays(self, request, queryset): |
b07400fb PP |
177 | selected = queryset.values_list('id', flat=True) |
178 | selected = ",".join("%s" % val for val in selected) | |
179 | return HttpResponseRedirect("/admin/assigner_%s?ids=%s" % ('pays', selected)) | |
18dbd2cf | 180 | assigner_pays.short_description = u'Assigner des pays' |
e3c3296e | 181 | |
182 | def assigner_regions(self, request, queryset): | |
b07400fb PP |
183 | selected = queryset.values_list('id', flat=True) |
184 | selected = ",".join("%s" % val for val in selected) | |
185 | return HttpResponseRedirect(url('assigner_regions', kwargs=dict(app_name='savoirs', model_name='record')) + '?ids=' + selected) | |
264a3210 | 186 | assigner_regions.short_description = u'Assigner des régions' |
e3c3296e | 187 | |
188 | def assigner_thematiques(self, request, queryset): | |
b07400fb PP |
189 | selected = queryset.values_list('id', flat=True) |
190 | selected = ",".join("%s" % val for val in selected) | |
191 | return HttpResponseRedirect("/admin/assigner_%s?ids=%s" % ('thematiques', selected)) | |
18dbd2cf | 192 | assigner_thematiques.short_description = u'Assigner des thématiques' |
e3c3296e | 193 | |
194 | def assigner_disciplines(self, request, queryset): | |
b07400fb PP |
195 | selected = queryset.values_list('id', flat=True) |
196 | selected = ",".join("%s" % val for val in selected) | |
197 | return HttpResponseRedirect(url('assigner_disciplines', kwargs=dict(app_name='savoirs', model_name='record')) + '?ids=' + selected) | |
264a3210 | 198 | assigner_disciplines.short_description = u'Assigner des disciplines' |
116db1fd | 199 | |
656b9c0f | 200 | def assigner_categorie(self, request, queryset): |
b07400fb PP |
201 | selected = queryset.values_list('id', flat=True) |
202 | selected = ",".join("%s" % val for val in selected) | |
203 | return HttpResponseRedirect("/admin/assigner_%s?ids=%s" % ('categorie', selected)) | |
656b9c0f PP |
204 | assigner_categorie.short_description = u'Assigner une catégorie' |
205 | ||
da9020f3 | 206 | admin.site.register(Record, RecordAdmin) |
207 | ||
d972b61d | 208 | class ListSetAdmin(ReadOnlyAdminFields, admin.ModelAdmin): |
9eda5d6c | 209 | fields = ['spec', 'name', 'server', 'validated' ] |
d972b61d | 210 | list_display = fields |
211 | readonly_fields = ['spec', 'name', 'server',] | |
212 | list_filter = ('server',) | |
213 | ||
214 | admin.site.register(ListSet, ListSetAdmin) | |
215 | ||
23b5b3d5 | 216 | class HarvestLogAdmin(ReadOnlyAdminFields, admin.ModelAdmin): |
a85ba76e | 217 | fields = ['context', 'name', 'added', 'updated', 'processed', 'record'] |
23b5b3d5 | 218 | list_display = fields + ['date'] |
219 | admin_order_fields = ['date'] | |
19c4ac32 | 220 | search_fields = ['context', 'name', 'added', 'updated', 'processed', 'record__title'] |
23b5b3d5 | 221 | readonly_fields = fields |
222 | list_filter = ('context',) | |
223 | ||
224 | admin.site.register(HarvestLog, HarvestLogAdmin) | |
6d885e0c | 225 | |
226 | class ProfileInline(admin.TabularInline): | |
227 | model = Profile | |
228 | fk_name = 'user' | |
229 | max_num = 1 | |
230 | ||
231 | class UserProfileAdmin(UserAdmin): | |
232 | inlines = [ProfileInline, ] | |
233 | ||
234 | admin.site.unregister(User) | |
235 | admin.site.register(User, UserProfileAdmin) | |
236 | ||
f554ef70 | 237 | class ActualiteAdmin(admin.ModelAdmin): |
a5f76eb4 | 238 | list_filter = ('visible', 'disciplines', 'regions') |
ccbc4363 | 239 | list_display = ('titre', 'source', 'date', 'visible') |
011804bb | 240 | list_editable = ['visible'] |
264a3210 | 241 | actions = ['rendre_visible', 'rendre_invisible', 'assigner_regions', 'assigner_disciplines'] |
f554ef70 | 242 | |
1719bf4e | 243 | def queryset(self, request): |
29e2a8fd | 244 | return Actualite.all_objects.get_query_set() |
1719bf4e | 245 | |
f554ef70 | 246 | # actions |
247 | def rendre_visible(self, request, queryset): | |
b07400fb PP |
248 | selected = queryset.values_list('id', flat=True) |
249 | selected = ",".join("%s" % val for val in selected) | |
250 | return HttpResponseRedirect("/admin/confirmation/%s/%s?ids=%s" % ('actualite', 'visible', selected)) | |
f554ef70 | 251 | |
252 | def rendre_invisible(self, request, queryset): | |
b07400fb PP |
253 | selected = queryset.values_list('id', flat=True) |
254 | selected = ",".join("%s" % val for val in selected) | |
255 | return HttpResponseRedirect("/admin/confirmation/%s/%s?ids=%s" % ('actualite', 'invisible', selected)) | |
f554ef70 | 256 | |
264a3210 | 257 | def assigner_regions(self, request, queryset): |
b07400fb PP |
258 | selected = queryset.values_list('id', flat=True) |
259 | selected = ",".join("%s" % val for val in selected) | |
260 | return HttpResponseRedirect(url('assigner_regions', kwargs=dict(app_name='savoirs', model_name='actualite')) + '?ids=' + selected) | |
264a3210 EMS |
261 | assigner_regions.short_description = u'Assigner des régions' |
262 | ||
263 | def assigner_disciplines(self, request, queryset): | |
b07400fb PP |
264 | selected = queryset.values_list('id', flat=True) |
265 | selected = ",".join("%s" % val for val in selected) | |
266 | return HttpResponseRedirect(url('assigner_disciplines', kwargs=dict(app_name='savoirs', model_name='actualite')) + '?ids=' + selected) | |
264a3210 EMS |
267 | assigner_disciplines.short_description = u'Assigner des disciplines' |
268 | ||
f554ef70 | 269 | admin.site.register(Actualite, ActualiteAdmin) |
b7a741ad | 270 | |
81fe476e PP |
271 | class ActualiteVoirAdmin(ActualiteAdmin): |
272 | ||
273 | actions = None | |
274 | list_editable = [] | |
275 | fields = ['titre', 'texte', 'url', 'date', 'visible', 'ancienid', 'source', 'disciplines', 'regions'] | |
276 | ||
277 | def __init__(self, model, admin_site): | |
278 | super(ActualiteVoirAdmin, self).__init__(model, admin_site) | |
279 | ||
280 | self.readonly_fields = self.fields | |
281 | ||
282 | ||
283 | admin.site.register(ActualiteVoir, ActualiteVoirAdmin) | |
284 | ||
db2999fa EMS |
285 | class SourceActualiteAdmin(admin.ModelAdmin): |
286 | actions = ['update_sources'] | |
011804bb EMS |
287 | list_display = ['nom', 'url', 'type'] |
288 | list_filter = ['type'] | |
db2999fa EMS |
289 | |
290 | def update_sources(self, request, queryset): | |
291 | for source in queryset: | |
292 | source.update() | |
293 | update_sources.short_description = u'Mettre à jour les fils sélectionnés' | |
294 | ||
295 | admin.site.register(SourceActualite, SourceActualiteAdmin) | |
b7a741ad | 296 | |
73309469 | 297 | class EvenementAdminForm(forms.ModelForm): |
86983865 | 298 | mots_cles = forms.CharField(label='Mots-clés', required=False) |
86983865 | 299 | |
73309469 | 300 | class Meta: |
301 | model = Evenement | |
302 | ||
303 | def clean(self,): | |
304 | cleaned_data = self.cleaned_data | |
305 | debut = cleaned_data.get("debut") | |
306 | fin = cleaned_data.get("fin") | |
7495bc13 | 307 | if debut and fin and debut > fin: |
73309469 | 308 | raise forms.ValidationError("La date de fin ne doit pas être antérieure à la date de début") |
309 | return cleaned_data | |
310 | ||
b7a741ad | 311 | class EvenementAdmin(admin.ModelAdmin): |
73309469 | 312 | form = EvenementAdminForm |
a5f76eb4 | 313 | list_filter = ('approuve', 'regions', 'discipline', 'discipline_secondaire') |
731ef7ab | 314 | list_display = ('titre', 'debut', 'fin', 'ville', 'pays', 'approuve') |
264a3210 | 315 | fields = ['titre', 'discipline', 'discipline_secondaire', 'mots_cles', |
731ef7ab EMS |
316 | 'type', 'adresse', 'ville', 'pays', 'fuseau', 'debut', 'fin', 'piece_jointe', 'regions', |
317 | 'description', 'prenom', 'nom', 'courriel', 'url', 'approuve'] | |
264a3210 EMS |
318 | actions = ['assigner_regions', 'assigner_disciplines'] |
319 | ||
1719bf4e | 320 | def queryset(self, request): |
29e2a8fd | 321 | return Evenement.all_objects.get_query_set() |
1719bf4e | 322 | |
264a3210 | 323 | def assigner_regions(self, request, queryset): |
b07400fb PP |
324 | selected = queryset.values_list('id', flat=True) |
325 | selected = ",".join("%s" % val for val in selected) | |
326 | return HttpResponseRedirect(url('assigner_regions', kwargs=dict(app_name='savoirs', model_name='evenement')) + '?ids=' + selected) | |
264a3210 EMS |
327 | assigner_regions.short_description = u'Assigner des régions' |
328 | ||
329 | def assigner_disciplines(self, request, queryset): | |
b07400fb PP |
330 | selected = queryset.values_list('id', flat=True) |
331 | selected = ",".join("%s" % val for val in selected) | |
332 | return HttpResponseRedirect(url('assigner_disciplines', kwargs=dict(app_name='savoirs', model_name='evenement')) + '?ids=' + selected) | |
264a3210 | 333 | assigner_disciplines.short_description = u'Assigner des disciplines' |
b7a741ad | 334 | |
335 | admin.site.register(Evenement, EvenementAdmin) | |
336 | ||
81fe476e PP |
337 | class EvenementVoirAdmin(EvenementAdmin): |
338 | ||
339 | actions = None | |
340 | list_editable = [] | |
341 | ||
342 | def __init__(self, model, admin_site): | |
343 | super(EvenementVoirAdmin, self).__init__(model, admin_site) | |
344 | ||
345 | self.readonly_fields = self.fields | |
346 | ||
347 | ||
348 | admin.site.register(EvenementVoir, EvenementVoirAdmin) | |
349 | ||
f09bc1c6 EMS |
350 | class PageStatiqueAdmin(admin.ModelAdmin): |
351 | list_display = ['titre', 'id'] | |
352 | list_display_links = ['titre', 'id'] | |
b7a741ad | 353 | |
f09bc1c6 EMS |
354 | class Media: |
355 | js = ['js/tiny_mce/tiny_mce.js', 'js/tiny_mce_textareas.js'] | |
356 | ||
357 | admin.site.register(PageStatique, PageStatiqueAdmin) | |
b7a741ad | 358 | |
656b9c0f PP |
359 | |
360 | admin.site.register(RecordCategorie) |