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