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