source: telemeta/views/base.py @ 0d2f73e

cremcrem2crem3devdev2diademsdj1.6feature/breadcrumbsfeature/ts-0.5feature/ts-0.5.4feature/writecacheformagenericinstru_searchlamlam2mapsv3mergenlivemultiprobproductionrelease/1.4.4sabiodsearchsecurityserversocialstoragetelecastertest
Last change on this file since 0d2f73e was 0d2f73e, checked in by yomguy <yomguy@…>, 3 years ago

add user revisions to user profile

  • Property mode set to 100644
File size: 67.4 KB
Line 
1# -*- coding: utf-8 -*-
2# Copyright (C) 2007-2010 Samalyse SARL
3# Copyright (C) 2010-2012 Parisson SARL
4
5# This software is a computer program whose purpose is to backup, analyse,
6# transcode and stream any audio content with its metadata over a web frontend.
7
8# This software is governed by the CeCILL  license under French law and
9# abiding by the rules of distribution of free software.  You can  use,
10# modify and/ or redistribute the software under the terms of the CeCILL
11# license as circulated by CEA, CNRS and INRIA at the following URL
12# "http://www.cecill.info".
13
14# As a counterpart to the access to the source code and  rights to copy,
15# modify and redistribute granted by the license, users are provided only
16# with a limited warranty  and the software's author,  the holder of the
17# economic rights,  and the successive licensors  have only  limited
18# liability.
19
20# In this respect, the user's attention is drawn to the risks associated
21# with loading,  using,  modifying and/or developing or reproducing the
22# software by the user in light of its specific status of free software,
23# that may mean  that it is complicated to manipulate,  and  that  also
24# therefore means  that it is reserved for developers  and  experienced
25# professionals having in-depth computer knowledge. Users are therefore
26# encouraged to load and test the software's suitability as regards their
27# requirements in conditions enabling the security of their systems and/or
28# data to be ensured and,  more generally, to use and operate it in the
29# same conditions as regards security.
30
31# The fact that you are presently reading this means that you have had
32# knowledge of the CeCILL license and that you accept its terms.
33
34# Authors: Olivier Guilyardi <olivier@samalyse.com>
35#          Guillaume Pellerin <yomguy@parisson.com>
36
37import re
38import os
39import sys
40import csv
41import time
42import random
43import datetime
44import timeside
45
46from jsonrpc import jsonrpc_method
47
48from django.utils.decorators import method_decorator
49from django.contrib.auth import authenticate, login
50from django.template import RequestContext, loader
51from django import template
52from django.http import HttpResponse, HttpResponseRedirect
53from django.http import Http404
54from django.shortcuts import render_to_response, redirect, get_object_or_404
55from django.views.generic import list_detail
56from django.views.generic import DetailView
57from django.conf import settings
58from django.contrib import auth
59from django.contrib import messages
60from django.contrib.auth.decorators import login_required, permission_required
61from django.core.context_processors import csrf
62from django.forms.models import modelformset_factory, inlineformset_factory
63from django.contrib.auth.models import User
64from django.utils.translation import ugettext
65from django.contrib.auth.forms import UserChangeForm
66from django.core.exceptions import ObjectDoesNotExist
67from django.contrib.syndication.views import Feed
68
69from telemeta.models import *
70import telemeta.models
71import telemeta.interop.oai as oai
72from telemeta.interop.oaidatasource import TelemetaOAIDataSource
73from telemeta.util.unaccent import unaccent
74from telemeta.util.unaccent import unaccent_icmp
75from telemeta.util.logger import Logger
76from telemeta.util.unicode import UnicodeWriter
77from telemeta.cache import TelemetaCache
78import telemeta.views.pages as pages
79from telemeta.forms import *
80
81# Model type definition
82mods = {'item': MediaItem, 'collection': MediaCollection,
83        'corpus': MediaCorpus, 'fonds': MediaFonds, 'marker': MediaItemMarker, }
84
85# TOOLS
86
87def render(request, template, data = None, mimetype = None):
88    return render_to_response(template, data, context_instance=RequestContext(request),
89                              mimetype=mimetype)
90
91def stream_from_processor(__decoder, __processor, __flag, metadata=None):
92    while True:
93        __frames, __eodproc = __processor.process(*__decoder.process())
94        if __eodproc or not len(__frames):
95            if metadata:
96                __processor.set_metadata(metadata)
97                __processor.write_metadata()
98            __flag.value = True
99            __flag.save()
100            break
101        yield __processor.chunk
102
103def stream_from_file(__file):
104    chunk_size = 0x10000
105    f = open(__file, 'r')
106    while True:
107        __chunk = f.read(chunk_size)
108        if not len(__chunk):
109            f.close()
110            break
111        yield __chunk
112
113def get_public_access(access, year_from=None, year_to=None):
114    # Rolling publishing date : public access is given when time between recorded year
115    # and current year is over the settings value PUBLIC_ACCESS_PERIOD
116    if year_from and not year_from == 0:
117        year = year_from
118    elif year_to and not year_to == 0:
119        year = year_to
120    else:
121        year = 0
122    if access == 'full':
123        public_access = True
124    else:
125        public_access = False
126        if year and not year == 'None':
127            year_now = datetime.datetime.now().strftime("%Y")
128            if int(year_now) - int(year) >= settings.TELEMETA_PUBLIC_ACCESS_PERIOD:
129                public_access = True
130        else:
131            public_access = False
132    return public_access
133
134def get_revisions(nb, user=None):
135    last_revisions = Revision.objects.order_by('-time')
136    if user:
137        last_revisions = last_revisions.filter(user=user)
138    last_revisions = last_revisions[0:nb]
139    revisions = []
140
141    for revision in last_revisions:
142        for type in mods.keys():
143            if revision.element_type == type:
144                try:
145                    element = mods[type].objects.get(pk=revision.element_id)
146                except:
147                    element = None
148        if not element == None:
149            revisions.append({'revision': revision, 'element': element})
150    return revisions
151
152def get_playlists(request, user=None):
153    if not user:
154        user = request.user
155    playlists = []
156    if user.is_authenticated():
157        user_playlists = Playlist.objects.filter(author=user)
158        for playlist in user_playlists:
159            playlist_resources = PlaylistResource.objects.filter(playlist=playlist)
160            resources = []
161            for resource in playlist_resources:
162                try:
163                    for type in mods.keys():
164                        if resource.resource_type == type:
165                            element = mods[type].objects.get(id=resource.resource_id)
166                except:
167                    element = None
168                resources.append({'element': element, 'type': resource.resource_type, 'public_id': resource.public_id })
169            playlists.append({'playlist': playlist, 'resources': resources})
170    return playlists
171
172def check_related_media(medias):
173    for media in medias:
174        if not media.mime_type:
175            media.set_mime_type()
176            media.save()
177        if not media.title and media.url:
178            if 'https' in media.url:
179                media.url = media.url.replace('https', 'http')
180            import lxml.etree
181            parser = lxml.etree.HTMLParser()
182            tree = lxml.etree.parse(media.url, parser)
183            try:
184                title = tree.find(".//title").text
185            except:
186                title = media.url
187            media.title = title.replace('\n', '').strip()
188            media.save()
189
190def auto_code(resources, base_code):
191    index = 1
192    while True:
193        code = base_code + '_' + str(index)
194        r = resources.filter(code=code)
195        if not r:
196            break
197        index += 1
198    return code
199
200
201class GeneralView(object):
202    """Provide general web UI methods"""
203
204    def home(self, request):
205        """Render the index page"""
206
207        template = loader.get_template('telemeta/home.html')
208
209        sound_items = MediaItem.objects.sound()
210        _sound_pub_items = []
211        for item in sound_items:
212            if get_public_access(item.public_access,  str(item.recorded_from_date).split('-')[0],
213                                            str(item.recorded_to_date).split('-')[0]):
214                _sound_pub_items.append(item)
215
216        random.shuffle(_sound_pub_items)
217        if len(_sound_pub_items) != 0:
218            sound_pub_item = _sound_pub_items[0]
219        else:
220            sound_pub_item = None
221        if len(_sound_pub_items) == 2:
222            sound_pub_items = [_sound_pub_items[1]]
223        elif len(_sound_pub_items) > 2:
224            sound_pub_items = _sound_pub_items[1:3]
225        else:
226            sound_pub_items = None
227
228        revisions = get_revisions(4)
229        context = RequestContext(request, {
230                    'page_content': pages.get_page_content(request, 'home', ignore_slash_issue=True),
231                    'revisions': revisions,  'sound_pub_items': sound_pub_items,
232                    'sound_pub_item': sound_pub_item })
233        return HttpResponse(template.render(context))
234
235    def lists(self, request):
236        """Render the home page"""
237
238        if request.user.is_authenticated():
239            template='telemeta/lists.html'
240            playlists = get_playlists(request)
241            revisions = get_revisions(100)
242            searches = Search.objects.filter(username=request.user)
243            user_revisions = get_revisions(25, request.user)
244            return render(request, template, {'playlists': playlists, 'searches': searches,
245                                              'revisions': revisions, 'user_revisions': user_revisions })
246        else:
247            template = 'telemeta/messages.html'
248            mess = ugettext('Access not allowed')
249            title = ugettext('Lists') + ' : ' + mess
250            description = ugettext('Please login or contact the website administator to get a private access.')
251            messages.error(request, title)
252            return render(request, template, {'description' : description})
253
254    def edit_search(self, request, criteria=None):
255        year_min, year_max = MediaCollection.objects.all().recording_year_range()
256        rec_years = year_min and year_max and range(year_min, year_max + 1) or []
257        year_min, year_max = MediaCollection.objects.all().publishing_year_range()
258        pub_years = year_min and year_max and range(year_min, year_max + 1) or []
259        return render(request, 'telemeta/search_criteria.html', {
260            'rec_years': rec_years,
261            'pub_years': pub_years,
262            'ethnic_groups': MediaItem.objects.all().ethnic_groups(),
263            'criteria': criteria
264        })
265
266    def handle_oai_request(self, request):
267        host = request.META['HTTP_HOST']
268        datasource  = TelemetaOAIDataSource()
269        repository_name = settings.TELEMETA_ORGANIZATION
270        url         = 'http://' + host + request.path
271        admin       = settings.ADMINS[0][1]
272        provider    = oai.DataProvider(datasource, repository_name, url, admin)
273        args        = request.GET.copy()
274        args.update(request.POST)
275        return HttpResponse(provider.handle(args), mimetype='text/xml')
276
277    def render_flatpage(self, request, path):
278        try:
279            content = pages.get_page_content(request, path)
280        except pages.MalformedPagePath:
281            return redirect(request.path + '/')
282
283        if isinstance(content, pages.PageAttachment):
284            return HttpResponse(content, content.mimetype())
285        else:
286            return render(request, 'telemeta/flatpage.html', {'page_content': content })
287
288    def logout(self, request):
289        auth.logout(request)
290        return redirect('telemeta-home')
291
292    def search(self, request, type = None):
293        """Perform a search through collections and items metadata"""
294        collections = MediaCollection.objects.enriched()
295        items = MediaItem.objects.enriched()
296        corpus = MediaCorpus.objects.all()
297        fonds  = MediaFonds.objects.all()
298        input = request.REQUEST
299        criteria = {}
300
301        switch = {
302            'pattern': lambda value: (
303                collections.quick_search(value),
304                items.quick_search(value),
305                corpus.quick_search(value),
306                fonds.quick_search(value),
307                ),
308            'title': lambda value: (
309                collections.word_search('title', value),
310                items.by_title(value),
311                corpus.word_search('title', value),
312                fonds.word_search('title', value)),
313            'location': lambda value: (
314                collections.by_location(Location.objects.get(name=value)),
315                items.by_location(Location.objects.get(name=value))),
316            'continent': lambda value: (
317                collections.by_continent(value),
318                items.filter(continent = value)),
319            'ethnic_group': lambda value: (
320                collections.by_ethnic_group(value),
321                items.filter(ethnic_group = value),
322                EthnicGroup.objects.get(pk=value)),
323            'creator': lambda value: (
324                collections.word_search('creator', value),
325                items.word_search('collection__creator', value)),
326            'collector': lambda value: (
327                collections.by_fuzzy_collector(value),
328                items.by_fuzzy_collector(value)),
329            'rec_year_from': lambda value: (
330                collections.by_recording_year(int(value), int(input.get('rec_year_to', value))),
331                items.by_recording_date(datetime.date(int(value), 1, 1),
332                                        datetime.date(int(input.get('rec_year_to', value)), 12, 31))),
333            'rec_year_to': lambda value: (collections, items),
334            'pub_year_from': lambda value: (
335                collections.by_publish_year(int(value), int(input.get('pub_year_to', value))),
336                items.by_publish_year(int(value), int(input.get('pub_year_to', value)))),
337            'pub_year_to': lambda value: (collections, items),
338            'sound': lambda value: (
339                collections.sound(),
340                items.sound()),
341        }
342
343        for key, value in input.items():
344            func = switch.get(key)
345            if func and value and value != "0":
346                try:
347                    res = func(value)
348                    if len(res)  > 4:
349                        collections, items, corpus, fonds, value = res
350                    elif len(res) == 4:
351                        collections, items, corpus, fonds = res
352                    elif len(res) == 3:
353                        collections, items, value = res
354                        corpus = corpus.none()
355                        fonds = fonds.none()
356                    else:
357                        collections, items = res
358                        corpus = corpus.none()
359                        fonds = fonds.none()
360
361                except ObjectDoesNotExist:
362                    collections = collections.none()
363                    items = items.none()
364                    corpus = corpus.none()
365                    fonds = fonds.none()
366
367                criteria[key] = value
368
369        # Save the search
370        user = request.user
371        if user:
372            if user.is_authenticated():
373                search = Search(username=user)
374                search.save()
375                if criteria:
376                    for key in criteria.keys():
377                        value = criteria[key]
378                        if key == 'ethnic_group':
379                            try:
380                                group = EthnicGroup.objects.get(value=value)
381                                value = group.id
382                            except:
383                                value = ''
384                        criter = Criteria(key=key, value=value)
385                        criter.save()
386                        search.criteria.add(criter)
387                    search.save()
388
389        if type is None:
390            if items.count():
391                type = 'items'
392            else:
393                type = 'collections'
394
395        if type == 'items':
396            objects = items
397        elif type == 'collections':
398            objects = collections
399        elif type == 'corpus':
400            objects = corpus
401        elif type == 'fonds':
402            objects = fonds
403
404        return list_detail.object_list(request, objects,
405            template_name='telemeta/search_results.html', paginate_by=20,
406            extra_context={'criteria': criteria, 'collections_num': collections.count(),
407                'items_num': items.count(), 'corpus_num': corpus.count(), 'fonds_num': fonds.count(),
408                'type' : type,})
409
410    def complete_location(self, request, with_items=True):
411        input = request.REQUEST
412
413        token = input['q']
414        limit = int(input['limit'])
415        if with_items:
416            locations = MediaItem.objects.all().locations()
417        else:
418            locations = Location.objects.all()
419
420        locations = locations.filter(name__istartswith=token).order_by('name')[:limit]
421        data = [unicode(l) + " (%d items)" % l.items().count() for l in locations]
422
423        return HttpResponse("\n".join(data))
424
425    def users(self, request):
426        users = User.objects.all()
427        return render(request, 'telemeta/users.html', {'users': users})
428
429class CollectionView(object):
430    """Provide Collections web UI methods"""
431
432    def collection_detail(self, request, public_id, template='telemeta/collection_detail.html'):
433        collection = MediaCollection.objects.get(public_id=public_id)
434        items = collection.items.enriched()
435        items = items.order_by('code', 'old_code')
436
437        if collection.public_access == 'none' and not (request.user.is_staff or request.user.is_superuser):
438            mess = ugettext('Access not allowed')
439            title = ugettext('Collection') + ' : ' + public_id + ' : ' + mess
440            description = ugettext('Please login or contact the website administator to get a private access.')
441            messages.error(request, title)
442            return render(request, 'telemeta/messages.html', {'description' : description})
443
444        public_access = get_public_access(collection.public_access, collection.recorded_from_year,
445                                                collection.recorded_to_year)
446        playlists = get_playlists(request)
447
448        related_media = MediaCollectionRelated.objects.filter(collection=collection)
449        check_related_media(related_media)
450        parents = MediaCorpus.objects.filter(children=collection)
451        revisions = Revision.objects.filter(element_type='collection',
452                                            element_id=collection.id).order_by('-time')
453        if revisions:
454            last_revision = revisions[0]
455        else:
456            last_revision = None
457
458        return render(request, template, {'collection': collection, 'playlists': playlists,
459                'public_access': public_access, 'items': items, 'related_media': related_media,
460                'parents': parents, 'last_revision': last_revision })
461
462    @method_decorator(permission_required('telemeta.change_mediacollection'))
463    def collection_edit(self, request, public_id, template='telemeta/collection_edit.html'):
464        collection = MediaCollection.objects.get(public_id=public_id)
465        if request.method == 'POST':
466            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
467            if form.is_valid():
468                code = form.cleaned_data['code']
469                if not code:
470                    code = public_id
471                form.save()
472                collection.set_revision(request.user)
473                return HttpResponseRedirect('/archives/collections/'+code)
474        else:
475            form = MediaCollectionForm(instance=collection)
476
477        return render(request, template, {'collection': collection, "form": form,})
478
479    @method_decorator(permission_required('telemeta.add_mediacollection'))
480    def collection_add(self, request, template='telemeta/collection_add.html'):
481        collection = MediaCollection()
482        if request.method == 'POST':
483            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
484            if form.is_valid():
485                code = form.cleaned_data['code']
486                if not code:
487                    code = public_id
488                form.save()
489                collection.set_revision(request.user)
490                return HttpResponseRedirect('/archives/collections/'+code)
491        else:
492            form = MediaCollectionForm(instance=collection)
493
494        return render(request, template, {'collection': collection, "form": form,})
495
496    @method_decorator(permission_required('telemeta.add_mediacollection'))
497    def collection_copy(self, request, public_id, template='telemeta/collection_edit.html'):
498        if request.method == 'POST':
499            collection = MediaCollection()
500            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
501            if form.is_valid():
502                code = form.cleaned_data['code']
503                if not code:
504                    code = public_id
505                form.save()
506                collection.set_revision(request.user)
507                return HttpResponseRedirect('/archives/collections/'+code)
508        else:
509            collection = MediaCollection.objects.get(public_id=public_id)
510            form = MediaCollectionForm(instance=collection)
511
512        return render(request, template, {'collection': collection, "form": form,})
513
514    def collection_playlist(self, request, public_id, template, mimetype):
515        try:
516            collection = MediaCollection.objects.get(public_id=public_id)
517        except ObjectDoesNotExist:
518            raise Http404
519
520        template = loader.get_template(template)
521        context = RequestContext(request, {'collection': collection, 'host': request.META['HTTP_HOST']})
522        return HttpResponse(template.render(context), mimetype=mimetype)
523
524    @method_decorator(permission_required('telemeta.delete_mediacollection'))
525    def collection_delete(self, request, public_id):
526        """Delete a given collection"""
527        collection = MediaCollection.objects.get(public_id=public_id)
528        collection.delete()
529        return HttpResponseRedirect('/archives/collections/')
530
531    def related_media_collection_stream(self, request, collection_public_id, media_id):
532        collection = MediaCollection.objects.get(public_id=collection_public_id)
533        media = MediaCollectionRelated.objects.get(collection=collection, id=media_id)
534        response = HttpResponse(stream_from_file(media.file.path), mimetype=media.mime_type)
535#        response['Content-Disposition'] = 'attachment'
536        return response
537
538    @method_decorator(permission_required('telemeta.change_mediacollection'))
539    def related_media_edit(self, request, public_id, template):
540        collection = MediaCollection.objects.get(public_id=public_id)
541        MediaCollectionRelatedFormSet = inlineformset_factory(MediaCollection, MediaCollectionRelated, form=MediaCollectionRelatedForm)
542        if request.method == 'POST':
543            formset = MediaCollectionRelatedFormSet(data=request.POST, files=request.FILES, instance=collection)
544            if formset.is_valid():
545                formset.save()
546                collection.set_revision(request.user)
547                return HttpResponseRedirect('/archives/collections/'+public_id)
548        else:
549            formset = MediaCollectionRelatedFormSet(instance=collection)
550
551        return render(request, template, {'collection': collection, 'formset': formset,})
552
553class ItemView(object):
554    """Provide Collections web UI methods"""
555
556    graphers = timeside.core.processors(timeside.api.IGrapher)
557    decoders = timeside.core.processors(timeside.api.IDecoder)
558    encoders = timeside.core.processors(timeside.api.IEncoder)
559    analyzers = timeside.core.processors(timeside.api.IAnalyzer)
560    cache_data = TelemetaCache(settings.TELEMETA_DATA_CACHE_DIR)
561    cache_export = TelemetaCache(settings.TELEMETA_EXPORT_CACHE_DIR)
562
563    def item_previous_next(self, item):
564        # Get previous and next items
565        pks = []
566        items = MediaItem.objects.filter(collection=item.collection)
567        items = items.order_by('code', 'old_code')
568
569        if len(items) > 1:
570            for it in items:
571                pks.append(it.pk)
572            for pk in pks:
573                if pk == item.pk:
574                    if pk == pks[0]:
575                        previous_pk = pks[-1]
576                        next_pk = pks[1]
577                    elif pk == pks[-1]:
578                        previous_pk = pks[-2]
579                        next_pk = pks[0]
580                    else:
581                        previous_pk = pks[pks.index(pk)-1]
582                        next_pk = pks[pks.index(pk)+1]
583                    for it in items:
584                        if it.pk == previous_pk:
585                            previous = it
586                        if it.pk == next_pk:
587                            next = it
588                    previous = previous.public_id
589                    next = next.public_id
590        else:
591             previous = item.public_id
592             next = item.public_id
593
594        return previous, next
595
596    def item_detail(self, request, public_id=None, marker_id=None, width=None, height=None,
597                        template='telemeta/mediaitem_detail.html'):
598        """Show the details of a given item"""
599
600        if not public_id and marker_id:
601            marker = MediaItemMarker.objects.get(public_id=marker_id)
602            item_id = marker.item_id
603            item = MediaItem.objects.get(id=item_id)
604        else:
605            item = MediaItem.objects.get(public_id=public_id)
606
607        item_public_access = item.public_access != 'none' or item.collection.public_access != 'none'
608        if not item_public_access and not (request.user.is_staff or request.user.is_superuser):
609            mess = ugettext('Access not allowed')
610            title = ugettext('Item') + ' : ' + public_id + ' : ' + mess
611            description = ugettext('Please login or contact the website administator to get a private access.')
612            messages.error(request, title)
613            return render(request, 'telemeta/messages.html', {'description' : description})
614
615        # Get TimeSide processors
616        formats = []
617        for encoder in self.encoders:
618            if settings.TELEMETA_DOWNLOAD_FORMATS:
619                if encoder.file_extension() in settings.TELEMETA_DOWNLOAD_FORMATS:
620                    formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
621            else:
622                formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
623
624        graphers = []
625        for grapher in self.graphers:
626            graphers.append({'name':grapher.name(), 'id': grapher.id()})
627        if request.REQUEST.has_key('grapher_id'):
628            grapher_id = request.REQUEST['grapher_id']
629        else:
630            grapher_id = 'waveform'
631
632        previous, next = self.item_previous_next(item)
633        mime_type = self.item_analyze(item)
634        playlists = get_playlists(request)
635        public_access = get_public_access(item.public_access, str(item.recorded_from_date).split('-')[0],
636                                                str(item.recorded_to_date).split('-')[0])
637
638        related_media = MediaItemRelated.objects.filter(item=item)
639        check_related_media(related_media)
640        revisions = Revision.objects.filter(element_type='item', element_id=item.id).order_by('-time')
641        if revisions:
642            last_revision = revisions[0]
643        else:
644            last_revision = None
645
646        physical_format = Format.objects.filter(item=item)
647        if physical_format:
648            physical_format = physical_format[0]
649
650        return render(request, template,
651                    {'item': item, 'export_formats': formats,
652                    'visualizers': graphers, 'visualizer_id': grapher_id,
653                    'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', True),
654                    'previous' : previous, 'next' : next, 'marker': marker_id, 'playlists' : playlists,
655                    'public_access': public_access, 'width': width, 'height': height,
656                    'related_media': related_media, 'mime_type': mime_type, 'last_revision': last_revision,
657                    'physical_format': physical_format,
658                    })
659
660    @method_decorator(permission_required('telemeta.change_mediaitem'))
661    def item_edit(self, request, public_id, template='telemeta/mediaitem_edit.html'):
662        """Edit a given item"""
663        item = MediaItem.objects.get(public_id=public_id)
664
665        formats = []
666        for encoder in self.encoders:
667            #FIXME: timeside cannot encode to FLAC and OGG now :'(
668            if encoder.file_extension() != 'ogg' and encoder.file_extension() != 'flac':
669                formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
670
671        graphers = []
672        for grapher in self.graphers:
673            graphers.append({'name':grapher.name(), 'id': grapher.id()})
674        if request.REQUEST.has_key('grapher_id'):
675            grapher_id = request.REQUEST['grapher_id']
676        else:
677            grapher_id = 'waveform'
678
679        previous, next = self.item_previous_next(item)
680        mime_type = self.item_analyze(item)
681
682        if request.method == 'POST':
683            form = MediaItemForm(data=request.POST, files=request.FILES, instance=item)
684            if form.is_valid():
685                form.save()
686                code = form.cleaned_data['code']
687                if not code:
688                    code = str(item.id)
689                if form.files:
690                    self.cache_data.delete_item_data(code)
691                    self.cache_export.delete_item_data(code)
692                    flags = MediaItemTranscodingFlag.objects.filter(item=item)
693                    analyses = MediaItemAnalysis.objects.filter(item=item)
694                    for flag in flags:
695                        flag.delete()
696                    for analysis in analyses:
697                        analysis.delete()
698                item.set_revision(request.user)
699                return HttpResponseRedirect('/archives/items/'+code)
700        else:
701            form = MediaItemForm(instance=item)
702
703        return render(request, template,
704                    {'item': item, 'export_formats': formats,
705                    'visualizers': graphers, 'visualizer_id': grapher_id,
706                    'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', True), "form": form,
707                    'previous' : previous, 'next' : next, 'mime_type': mime_type,
708                    })
709
710    def related_media_item_stream(self, request, item_public_id, media_id):
711        item = MediaItem.objects.get(public_id=item_public_id)
712        media = MediaItemRelated.objects.get(item=item, id=media_id)
713        response = HttpResponse(stream_from_file(media.file.path), mimetype=media.mime_type)
714#        response['Content-Disposition'] = 'attachment; '+'filename='+media.title+'.'+ext
715        return response
716
717    @method_decorator(permission_required('telemeta.change_mediaitem'))
718    def related_media_edit(self, request, public_id, template):
719        item = MediaItem.objects.get(public_id=public_id)
720        MediaItemRelatedFormSet = inlineformset_factory(MediaItem, MediaItemRelated, form=MediaItemRelatedForm)
721        if request.method == 'POST':
722            formset = MediaItemRelatedFormSet(data=request.POST, files=request.FILES, instance=item)
723            if formset.is_valid():
724                formset.save()
725                item.set_revision(request.user)
726                return HttpResponseRedirect('/archives/items/'+public_id)
727        else:
728            formset = MediaItemRelatedFormSet(instance=item)
729
730        return render(request, template, {'item': item, 'formset': formset,})
731
732    @method_decorator(permission_required('telemeta.add_mediaitem'))
733    def item_add(self, request, public_id=None, template='telemeta/mediaitem_add.html'):
734        """Add an item"""
735        if public_id:
736            collection = MediaCollection.objects.get(public_id=public_id)
737            items = MediaItem.objects.filter(collection=collection)
738            code = auto_code(items, collection.code)
739            item = MediaItem(collection=collection, code=code)
740        else:
741            item = MediaItem()
742        if request.method == 'POST':
743            form = MediaItemForm(data=request.POST, files=request.FILES, instance=item)
744            if form.is_valid():
745                form.save()
746                item.set_revision(request.user)
747                code = form.cleaned_data['code']
748                if not code:
749                    code = str(item.id)
750                return HttpResponseRedirect('/archives/items/'+code)
751        else:
752            form = MediaItemForm(instance=item)
753
754
755        return render(request, template, {'item': item, 'form': form})
756
757    @method_decorator(permission_required('telemeta.add_mediaitem'))
758    def item_copy(self, request, public_id, template='telemeta/mediaitem_copy.html'):
759        """Copy a given item"""
760        if request.method == 'POST':
761            source_item = MediaItem.objects.get(public_id=public_id)
762            item = MediaItem()
763            form = MediaItemForm(data=request.POST, files=request.FILES, instance=item)
764            if form.is_valid():
765                form.save()
766                code = form.cleaned_data['code']
767                if not code:
768                    code = str(item.id)
769
770                performances = MediaItemPerformance.objects.filter(media_item=source_item)
771                for performance in performances:
772                    performance.pk = None
773                    performance.id = None
774                    performance.media_item = item
775                    performance.save()
776
777                keywords = MediaItemKeyword.objects.filter(item=source_item)
778                for keyword in keywords:
779                    keyword.pk = None
780                    keyword.id = None
781                    keyword.item = item
782                    keyword.save()
783
784                item.set_revision(request.user)
785                return HttpResponseRedirect('/archives/items/'+code)
786        else:
787            item = MediaItem.objects.get(public_id=public_id)
788            items = MediaItem.objects.filter(collection=item.collection)
789            item.code = auto_code(items, item.collection.code)
790            item.approx_duration = ''
791            form = MediaItemForm(instance=item)
792            form.code = item.code
793            form.file = None
794
795        return render(request, template, {'item': item, "form": form})
796
797    @method_decorator(permission_required('telemeta.delete_mediaitem'))
798    def item_delete(self, request, public_id):
799        """Delete a given item"""
800        item = MediaItem.objects.get(public_id=public_id)
801        collection = item.collection
802        item.delete()
803        return HttpResponseRedirect('/archives/collections/'+collection.code)
804
805    def item_analyze(self, item):
806        analyses = MediaItemAnalysis.objects.filter(item=item)
807        mime_type = ''
808
809        if analyses:
810            for analysis in analyses:
811                if not item.approx_duration and analysis.analyzer_id == 'duration':
812                    value = analysis.value
813                    time = value.split(':')
814                    time[2] = time[2].split('.')[0]
815                    time = ':'.join(time)
816                    item.approx_duration = time
817                    item.save()
818                if analysis.analyzer_id == 'mime_type':
819                    mime_type = analysis.value
820        else:
821            analyzers = []
822            analyzers_sub = []
823            if item.file:
824                decoder  = timeside.decoder.FileDecoder(item.file.path)
825                pipe = decoder
826                for analyzer in self.analyzers:
827                    subpipe = analyzer()
828                    analyzers_sub.append(subpipe)
829                    pipe = pipe | subpipe
830                pipe.run()
831
832                mime_type = decoder.format()
833                analysis = MediaItemAnalysis(item=item, name='MIME type',
834                                             analyzer_id='mime_type', unit='', value=mime_type)
835                analysis.save()
836                analysis = MediaItemAnalysis(item=item, name='Channels',
837                                             analyzer_id='channels',
838                                             unit='', value=decoder.channels())
839                analysis.save()
840                analysis = MediaItemAnalysis(item=item, name='Samplerate',
841                                             analyzer_id='samplerate', unit='Hz',
842                                             value=unicode(decoder.audiorate))
843                analysis.save()
844                analysis = MediaItemAnalysis(item=item, name='Resolution',
845                                             analyzer_id='resolution', unit='bits',
846                                             value=unicode(decoder.audiowidth))
847                analysis.save()
848                analysis = MediaItemAnalysis(item=item, name='Duration',
849                                             analyzer_id='duration', unit='s',
850                                             value=unicode(datetime.timedelta(0,decoder.duration)))
851                analysis.save()
852
853                for analyzer in analyzers_sub:
854                    value = analyzer.result()
855                    analysis = MediaItemAnalysis(item=item, name=analyzer.name(),
856                                                 analyzer_id=analyzer.id(),
857                                                 unit=analyzer.unit(), value=str(value))
858                    analysis.save()
859
860#                FIXME: parse tags on first load
861#                tags = decoder.tags
862
863        return mime_type
864
865    def item_analyze_xml(self, request, public_id):
866        item = MediaItem.objects.get(public_id=public_id)
867        analyses = MediaItemAnalysis.objects.filter(item=item)
868        analyzers = []
869        for analysis in analyses:
870            analyzers.append(analysis.to_dict())
871        mime_type = 'text/xml'
872        response = HttpResponse(self.cache_data.get_analyzer_xml(analyzers), mimetype=mime_type)
873        response['Content-Disposition'] = 'attachment; filename='+public_id+'.xml'
874        return response
875
876    def item_visualize(self, request, public_id, visualizer_id, width, height):
877        item = MediaItem.objects.get(public_id=public_id)
878        mime_type = 'image/png'
879        grapher_id = visualizer_id
880
881        for grapher in self.graphers:
882            if grapher.id() == grapher_id:
883                break
884
885        if grapher.id() != grapher_id:
886            raise Http404
887
888        size = width + '_' + height
889        image_file = '.'.join([public_id, grapher_id, size, 'png'])
890
891        if not self.cache_data.exists(image_file):
892            if item.file:
893                path = self.cache_data.dir + os.sep + image_file
894                decoder  = timeside.decoder.FileDecoder(item.file.path)
895                graph = grapher(width = int(width), height = int(height))
896                pipe = decoder | graph
897                pipe.run()
898                graph.watermark('timeside', opacity=.6, margin=(5,5))
899                f = open(path, 'w')
900                graph.render(path)
901                f.close()
902
903        response = HttpResponse(self.cache_data.read_stream_bin(image_file), mimetype=mime_type)
904        return response
905
906    def list_export_extensions(self):
907        "Return the recognized item export file extensions, as a list"
908        list = []
909        for encoder in self.encoders:
910            list.append(encoder.file_extension())
911        return list
912
913    def item_export(self, request, public_id, extension):
914        """Export a given media item in the specified format (OGG, FLAC, ...)"""
915
916        item = MediaItem.objects.get(public_id=public_id)
917        public_access = get_public_access(item.public_access,
918                                          str(item.recorded_from_date).split('-')[0],
919                                          str(item.recorded_to_date).split('-')[0])
920
921        if (not public_access or not extension in settings.TELEMETA_STREAMING_FORMATS) and \
922                    not (request.user.has_perm('telemeta.can_play_all_items') or request.user.is_superuser):
923            mess = ugettext('Access not allowed')
924            title = 'Item file : ' + public_id + '.' + extension + ' : ' + mess
925            description = ugettext('Please login or contact the website administator to get a private access.')
926            messages.error(request, title)
927            return render(request, 'telemeta/messages.html', {'description' : description})
928
929        for encoder in self.encoders:
930            if encoder.file_extension() == extension:
931                break
932
933        if encoder.file_extension() != extension:
934            raise Http404('Unknown export file extension: %s' % extension)
935
936        mime_type = encoder.mime_type()
937        file = public_id + '.' + encoder.file_extension()
938        audio = item.file.path
939
940        flag = MediaItemTranscodingFlag.objects.filter(item=item, mime_type=mime_type)
941        if not flag:
942            flag = MediaItemTranscodingFlag(item=item, mime_type=mime_type)
943            flag.value = False
944            flag.save()
945        else:
946            flag = flag[0]
947
948        format = self.item_analyze(item)
949        dc_metadata = dublincore.express_item(item).to_list()
950        mapping = DublinCoreToFormatMetadata(extension)
951        metadata = mapping.get_metadata(dc_metadata)
952
953        if mime_type in format:
954            # source > stream
955            if not extension in mapping.unavailable_extensions:
956                proc = encoder(audio)
957                proc.set_metadata(metadata)
958                try:
959                    proc.write_metadata()
960                except:
961                    pass
962            response = HttpResponse(stream_from_file(audio), mimetype = mime_type)
963        else:
964            media = self.cache_export.dir + os.sep + file
965            if not self.cache_export.exists(file) or not flag.value:
966                # source > encoder > stream
967                decoder = timeside.decoder.FileDecoder(audio)
968                decoder.setup()
969                proc = encoder(media, streaming=True)
970                proc.setup(channels=decoder.channels(), samplerate=decoder.samplerate())
971                if extension in mapping.unavailable_extensions:
972                    metadata=None
973                response = HttpResponse(stream_from_processor(decoder, proc, flag, metadata=metadata), mimetype = mime_type)
974            else:
975                # cache > stream
976                response = HttpResponse(self.cache_export.read_stream_bin(file), mimetype = mime_type)
977
978        response['Content-Disposition'] = 'attachment'
979        return response
980
981    def item_playlist(self, request, public_id, template, mimetype):
982        try:
983            item = MediaItem.objects.get(public_id=public_id)
984        except ObjectDoesNotExist:
985            raise Http404
986
987        template = loader.get_template(template)
988        context = RequestContext(request, {'item': item, 'host': request.META['HTTP_HOST']})
989        return HttpResponse(template.render(context), mimetype=mimetype)
990
991    @method_decorator(permission_required('telemeta.change_mediaitem'))
992    def item_performances_edit(self, request, public_id, template):
993        item = MediaItem.objects.get(public_id=public_id)
994        PerformanceFormSet = inlineformset_factory(MediaItem, MediaItemPerformance, form=MediaItemPerformanceForm)
995        if request.method == 'POST':
996            formset = PerformanceFormSet(data=request.POST, instance=item)
997            if formset.is_valid():
998                formset.save()
999                return HttpResponseRedirect('/archives/items/'+public_id)
1000        else:
1001            formset = PerformanceFormSet(instance=item)
1002        return render(request, template, {'item': item, 'formset': formset,})
1003
1004    @method_decorator(permission_required('telemeta.change_mediaitem'))
1005    def item_keywords_edit(self, request, public_id, template):
1006        item = MediaItem.objects.get(public_id=public_id)
1007        FormSet = inlineformset_factory(MediaItem, MediaItemKeyword)
1008        if request.method == 'POST':
1009            formset = FormSet(data=request.POST, instance=item)
1010            if formset.is_valid():
1011                formset.save()
1012                return HttpResponseRedirect('/archives/items/'+public_id)
1013        else:
1014            formset = FormSet(instance=item)
1015        return render(request, template, {'item': item, 'formset': formset,})
1016
1017
1018class AdminView(object):
1019    """Provide Admin web UI methods"""
1020
1021    @method_decorator(permission_required('sites.change_site'))
1022    def admin_index(self, request):
1023        return render(request, 'telemeta/admin.html', self.__get_admin_context_vars())
1024
1025    @method_decorator(permission_required('sites.change_site'))
1026    def admin_general(self, request):
1027        return render(request, 'telemeta/admin_general.html', self.__get_admin_context_vars())
1028
1029    @method_decorator(permission_required('sites.change_site'))
1030    def admin_enumerations(self, request):
1031        return render(request, 'telemeta/admin_enumerations.html', self.__get_admin_context_vars())
1032
1033    @method_decorator(permission_required('sites.change_site'))
1034    def admin_users(self, request):
1035        users = User.objects.all()
1036        return render(request, 'telemeta/admin_users.html', {'users': users})
1037
1038    def __get_enumerations_list(self):
1039        from django.db.models import get_models
1040        models = get_models(telemeta.models)
1041
1042        enumerations = []
1043        for model in models:
1044            if issubclass(model, Enumeration):
1045                enumerations.append({"name": model._meta.verbose_name,
1046                    "id": model._meta.module_name})
1047
1048        cmp = lambda obj1, obj2: unaccent_icmp(obj1['name'], obj2['name'])
1049        enumerations.sort(cmp)
1050        return enumerations
1051
1052    def __get_admin_context_vars(self):
1053        return {"enumerations": self.__get_enumerations_list()}
1054
1055    def __get_enumeration(self, id):
1056        from django.db.models import get_models
1057        models = get_models(telemeta.models)
1058        for model in models:
1059            if model._meta.module_name == id:
1060                break
1061
1062        if model._meta.module_name != id:
1063            return None
1064
1065        return model
1066
1067    @method_decorator(permission_required('telemeta.change_keyword'))
1068    def edit_enumeration(self, request, enumeration_id):
1069
1070        enumeration  = self.__get_enumeration(enumeration_id)
1071        if enumeration == None:
1072            raise Http404
1073
1074        vars = self.__get_admin_context_vars()
1075        vars["enumeration_id"] = enumeration._meta.module_name
1076        vars["enumeration_name"] = enumeration._meta.verbose_name
1077        vars["enumeration_values"] = enumeration.objects.all()
1078        return render(request, 'telemeta/enumeration_edit.html', vars)
1079
1080    @method_decorator(permission_required('telemeta.add_keyword'))
1081    def add_to_enumeration(self, request, enumeration_id):
1082
1083        enumeration  = self.__get_enumeration(enumeration_id)
1084        if enumeration == None:
1085            raise Http404
1086
1087        enumeration_value = enumeration(value=request.POST['value'])
1088        enumeration_value.save()
1089
1090        return self.edit_enumeration(request, enumeration_id)
1091
1092    @method_decorator(permission_required('telemeta.change_keyword'))
1093    def update_enumeration(self, request, enumeration_id):
1094
1095        enumeration  = self.__get_enumeration(enumeration_id)
1096        if enumeration == None:
1097            raise Http404
1098
1099        if request.method == 'POST':
1100            enumeration.objects.filter(id__in=request.POST.getlist('sel')).delete()
1101
1102        return self.edit_enumeration(request, enumeration_id)
1103
1104    @method_decorator(permission_required('telemeta.change_keyword'))
1105    def edit_enumeration_value(self, request, enumeration_id, value_id):
1106
1107        enumeration  = self.__get_enumeration(enumeration_id)
1108        if enumeration == None:
1109            raise Http404
1110
1111        vars = self.__get_admin_context_vars()
1112        vars["enumeration_id"] = enumeration._meta.module_name
1113        vars["enumeration_name"] = enumeration._meta.verbose_name
1114        vars["enumeration_record"] = enumeration.objects.get(id__exact=value_id)
1115        return render(request, 'telemeta/enumeration_edit_value.html', vars)
1116
1117    @method_decorator(permission_required('telemeta.change_keyword'))
1118    def update_enumeration_value(self, request, enumeration_id, value_id):
1119
1120        if request.method == 'POST':
1121            enumeration  = self.__get_enumeration(enumeration_id)
1122            if enumeration == None:
1123                raise Http404
1124
1125            record = enumeration.objects.get(id__exact=value_id)
1126            record.value = request.POST["value"]
1127            record.save()
1128
1129        return self.edit_enumeration(request, enumeration_id)
1130
1131
1132class InstrumentView(object):
1133    """Provide Instrument web UI methods"""
1134
1135    @method_decorator(permission_required('telemeta.change_instrument'))
1136    def edit_instrument(self, request):
1137
1138        instruments = Instrument.objects.all().order_by('name')
1139        if instruments == None:
1140            raise Http404
1141        return render(request, 'telemeta/instrument_edit.html', {'instruments': instruments})
1142
1143    @method_decorator(permission_required('telemeta.add_instrument'))
1144    def add_to_instrument(self, request):
1145
1146        if request.method == 'POST':
1147            instrument = Instrument(name=request.POST['value'])
1148            instrument.save()
1149
1150        return self.edit_instrument(request)
1151
1152    @method_decorator(permission_required('telemeta.change_instrument'))
1153    def update_instrument(self, request):
1154
1155        if request.method == 'POST':
1156            Instrument.objects.filter(id__in=request.POST.getlist('sel')).delete()
1157
1158        return self.edit_instrument(request)
1159
1160    @method_decorator(permission_required('telemeta.change_instrument'))
1161    def edit_instrument_value(self, request, value_id):
1162        instrument = Instrument.objects.get(id__exact=value_id)
1163
1164        return render(request, 'telemeta/instrument_edit_value.html', {'instrument': instrument})
1165
1166    @method_decorator(permission_required('telemeta.change_instrument'))
1167    def update_instrument_value(self, request, value_id):
1168
1169        if request.method == 'POST':
1170            instrument = Instrument.objects.get(id__exact=value_id)
1171            instrument.name = request.POST["value"]
1172            instrument.save()
1173
1174        return self.edit_instrument(request)
1175
1176
1177class GeoView(object):
1178    """Provide Geo web UI methods"""
1179
1180    def list_continents(self, request):
1181        continents = MediaItem.objects.all().countries(group_by_continent=True)
1182        return render(request, 'telemeta/geo_continents.html',
1183                    {'continents': continents, 'gmap_key': settings.TELEMETA_GMAP_KEY })
1184
1185    def country_info(self, request, id):
1186        country = Location.objects.get(pk=id)
1187        return render(request, 'telemeta/country_info.html', {
1188            'country': country, 'continent': country.continents()[0]})
1189
1190    def list_countries(self, request, continent):
1191        continent = Location.objects.by_flatname(continent)[0]
1192        countries = MediaItem.objects.by_location(continent).countries()
1193
1194        return render(request, 'telemeta/geo_countries.html', {
1195            'continent': continent,
1196            'countries': countries
1197        })
1198
1199    def list_country_collections(self, request, continent, country):
1200        continent = Location.objects.by_flatname(continent)[0]
1201        country = Location.objects.by_flatname(country)[0]
1202        objects = MediaCollection.objects.enriched().by_location(country)
1203        return list_detail.object_list(request, objects,
1204            template_name='telemeta/geo_country_collections.html', paginate_by=20,
1205            extra_context={'country': country, 'continent': continent})
1206
1207    def list_country_items(self, request, continent, country):
1208        continent = Location.objects.by_flatname(continent)[0]
1209        country = Location.objects.by_flatname(country)[0]
1210        objects = MediaItem.objects.enriched().by_location(country)
1211        return list_detail.object_list(request, objects,
1212            template_name='telemeta/geo_country_items.html', paginate_by=20,
1213            extra_context={'country': country, 'continent': continent})
1214
1215class MarkerView(object):
1216    """Provide Collections web UI methods"""
1217
1218    @jsonrpc_method('telemeta.add_marker')
1219    def add_marker(request, marker):
1220        # marker must be a dict
1221        if isinstance(marker, dict):
1222            item_id = marker['item_id']
1223            item = MediaItem.objects.get(id=item_id)
1224            m = MediaItemMarker(item=item)
1225            m.public_id = marker['public_id']
1226            m.time = float(marker['time'])
1227            m.title = marker['title']
1228            m.description = marker['description']
1229            m.author = User.objects.get(username=marker['author'])
1230            m.save()
1231            m.set_revision(request.user)
1232        else:
1233            raise 'Error : Bad marker dictionnary'
1234
1235    @jsonrpc_method('telemeta.del_marker')
1236    def del_marker(request, public_id):
1237        m = MediaItemMarker.objects.get(public_id=public_id)
1238        m.delete()
1239
1240    @jsonrpc_method('telemeta.get_markers')
1241    def get_markers(request, item_id):
1242        item = MediaItem.objects.get(id=item_id)
1243        markers = MediaItemMarker.objects.filter(item=item)
1244        list = []
1245        for marker in markers:
1246            dict = {}
1247            dict['public_id'] = marker.public_id
1248            dict['time'] = str(marker.time)
1249            dict['title'] = marker.title
1250            dict['description'] = marker.description
1251            dict['author'] = marker.author.username
1252            list.append(dict)
1253        return list
1254
1255    @jsonrpc_method('telemeta.update_marker')
1256    def update_marker(request, marker):
1257        if isinstance(marker, dict):
1258            m = MediaItemMarker.objects.get(public_id=marker['public_id'])
1259            m.time = float(marker['time'])
1260            m.title = marker['title']
1261            m.description = marker['description']
1262            m.save()
1263            m.set_revision(request.user)
1264        else:
1265            raise 'Error : Bad marker dictionnary'
1266
1267    @jsonrpc_method('telemeta.get_marker_id')
1268    def get_marker_id(request, public_id):
1269        marker = MediaItemMarker.objects.get(public_id=public_id)
1270        return marker.id
1271
1272class PlaylistView(object):
1273    """Provide Playlist web UI methods"""
1274
1275    @jsonrpc_method('telemeta.add_playlist')
1276    def add_playlist(request, playlist):
1277        # playlist must be a dict
1278        if isinstance(playlist, dict):
1279            m = Playlist()
1280            m.public_id = playlist['public_id']
1281            m.title = playlist['title']
1282            m.description = playlist['description']
1283            m.author = request.user
1284            m.save()
1285        else:
1286            raise 'Error : Bad playlist dictionnary'
1287
1288    @jsonrpc_method('telemeta.del_playlist')
1289    def del_playlist(request, public_id):
1290        m = Playlist.objects.get(public_id=public_id)
1291        m.delete()
1292
1293    @jsonrpc_method('telemeta.update_playlist')
1294    def update_playlist(request, playlist):
1295        if isinstance(playlist, dict):
1296            m = Playlist.objects.get(public_id=playlist['public_id'])
1297            m.title = playlist['title']
1298            m.description = playlist['description']
1299            m.save()
1300        else:
1301            raise 'Error : Bad playlist dictionnary'
1302
1303    @jsonrpc_method('telemeta.add_playlist_resource')
1304    def add_playlist_resource(request, playlist_id, playlist_resource):
1305        # playlist_resource must be a dict
1306        if isinstance(playlist_resource, dict):
1307            m = PlaylistResource()
1308            m.public_id = playlist_resource['public_id']
1309            m.playlist = Playlist.objects.get(public_id=playlist_id, author=request.user)
1310            m.resource_type = playlist_resource['resource_type']
1311            m.resource_id = playlist_resource['resource_id']
1312            m.save()
1313        else:
1314            raise 'Error : Bad playlist_resource dictionnary'
1315
1316    @jsonrpc_method('telemeta.del_playlist_resource')
1317    def del_playlist_resource(request, public_id):
1318        m = PlaylistResource.objects.get(public_id=public_id)
1319        m.delete()
1320
1321
1322    def playlist_csv_export(self, request, public_id, resource_type):
1323        playlist = Playlist.objects.get(public_id=public_id, author=request.user)
1324        resources = PlaylistResource.objects.filter(playlist=playlist)
1325        response = HttpResponse(mimetype='text/csv')
1326        response['Content-Disposition'] = 'attachment; filename='+playlist.title+'_'+resource_type+'.csv'
1327        writer = UnicodeWriter(response)
1328
1329        elements = []
1330        for resource in resources:
1331            if resource_type == 'items':
1332                if resource.resource_type == 'collection':
1333                    collection = MediaCollection.objects.get(id=resource.resource_id)
1334                    collection_items = MediaItem.objects.filter(collection=collection)
1335                    for item in collection_items:
1336                        elements.append(item)
1337                elif resource.resource_type == 'item':
1338                    item = MediaItem.objects.get(id=resource.resource_id)
1339                    elements.append(item)
1340
1341            elif resource_type == 'collections':
1342                if resource.resource_type == 'collection':
1343                    collection = MediaCollection.objects.get(id=resource.resource_id)
1344                    elements.append(collection)
1345
1346        if elements:
1347            element = elements[0].to_dict()
1348            tags = element.keys()
1349            # code and title on the two first column
1350            tags.remove('code')
1351            tags.remove('title')
1352            tags.sort()
1353            tags.insert(0, 'title')
1354            tags.insert(0, 'code')
1355            writer.writerow(tags)
1356
1357            for element in elements:
1358                data = []
1359                element = element.to_dict()
1360                for tag in tags:
1361                    data.append(element[tag])
1362                writer.writerow(data)
1363        return response
1364
1365
1366class ProfileView(object):
1367    """Provide Collections web UI methods"""
1368
1369    @method_decorator(login_required)
1370    def profile_detail(self, request, username, template='telemeta/profile_detail.html'):
1371        user = User.objects.get(username=username)
1372        try:
1373            profile = user.get_profile()
1374        except:
1375            profile = None
1376        playlists = get_playlists(request, user)
1377        user_revisions = get_revisions(25, user)
1378
1379        return render(request, template, {'profile' : profile, 'usr': user, 'playlists': playlists,
1380                                          'user_revisions': user_revisions})
1381
1382    def profile_edit(self, request, username, template='telemeta/profile_edit.html'):
1383        if request.user.is_superuser:
1384            user_hidden_fields = ['profile-user', 'user-password', 'user-last_login', 'user-date_joined']
1385        else:
1386            user_hidden_fields = ['user-username', 'user-is_staff', 'profile-user', 'user-is_active',
1387                         'user-password', 'user-last_login', 'user-date_joined', 'user-groups',
1388                         'user-user_permissions', 'user-is_superuser', 'profile-expiration_date']
1389
1390        user = User.objects.get(username=username)
1391        if user != request.user and not request.user.is_staff:
1392            mess = ugettext('Access not allowed')
1393            title = ugettext('User profile') + ' : ' + username + ' : ' + mess
1394            description = ugettext('Please login or contact the website administator to get a private access.')
1395            messages.error(request, title)
1396            return render(request, 'telemeta/messages.html', {'description' : description})
1397
1398        try:
1399            profile = user.get_profile()
1400        except:
1401            profile = UserProfile(user=user)
1402
1403        if request.method == 'POST':
1404            user_form = UserChangeForm(request.POST, instance=user, prefix='user')
1405            profile_form = UserProfileForm(request.POST, instance=profile, prefix='profile')
1406            if user_form.is_valid() and profile_form.is_valid():
1407                user_form.save()
1408                profile_form.save()
1409                return HttpResponseRedirect('/users/'+username+'/profile/')
1410        else:
1411            user_form = UserChangeForm(instance=user, prefix='user')
1412            profile_form = UserProfileForm(instance=profile, prefix='profile')
1413            forms = [user_form, profile_form]
1414        return render(request, template, {'forms': forms, 'usr': user,
1415                                'user_hidden_fields': user_hidden_fields})
1416
1417
1418class LastestRevisionsFeed(Feed):
1419    "the RSS feed of the lastest revisions"
1420
1421    organization = settings.TELEMETA_ORGANIZATION
1422    subjects = settings.TELEMETA_SUBJECTS
1423    tags = ['title', 'description', 'comment']
1424    title = organization + ' - Telemeta - ' + ugettext('Last changes')
1425    link = ""
1426    description = ' '.join([subject.decode('utf-8') for subject in subjects])
1427    n_items = 100
1428
1429    def items(self):
1430        return get_revisions(self.n_items)
1431
1432    def item_title(self, r):
1433        element = r['element']
1434        if element.title == '':
1435            title = str(element.public_id)
1436        else:
1437            title = element.title
1438        return element.element_type + ' : ' + title
1439
1440    def item_description(self, r):
1441        revision = r['revision']
1442        element = r['element']
1443        description = '<b>modified by ' + revision.user.username + ' on ' + unicode(revision.time) + '</b><br /><br />'
1444        dict = element.to_dict()
1445        for tag in dict.keys():
1446            try:
1447                value = dict[tag]
1448                if value != '':
1449                    description += tag + ' : ' + value + '<br />'
1450            except:
1451                continue
1452        return description.encode('utf-8')
1453
1454    def item_link(self, r):
1455        revision = r['revision']
1456        element = r['element']
1457        link = '/' + revision.element_type + 's/' + str(element.public_id)
1458        return link
1459
1460
1461class UserRevisionsFeed(LastestRevisionsFeed):
1462
1463    def get_object(self, request, username):
1464        return get_object_or_404(User, username=username)
1465
1466    def items(self, obj):
1467        return get_revisions(self.n_items, obj)
1468
1469
1470class ResourceView(object):
1471    """Provide Resource web UI methods"""
1472
1473    types = {'corpus':
1474                {'model': MediaCorpus,
1475                'form' : MediaCorpusForm,
1476                'related': MediaCorpusRelated,
1477                'related_form': MediaCorpusRelatedForm,
1478                'parent': MediaFonds,
1479                },
1480            'fonds':
1481                {'model': MediaFonds,
1482                'form' : MediaFondsForm,
1483                'related': MediaFondsRelated,
1484                'related_form': MediaFondsRelatedForm,
1485                'parent': None,
1486                }
1487            }
1488
1489    def setup(self, type):
1490        self.model = self.types[type]['model']
1491        self.form = self.types[type]['form']
1492        self.related = self.types[type]['related']
1493        self.related_form = self.types[type]['related_form']
1494        self.parent = self.types[type]['parent']
1495        self.type = type
1496
1497    def detail(self, request, type, public_id, template='telemeta/resource_detail.html'):
1498        self.setup(type)
1499        resource = self.model.objects.get(code=public_id)
1500        children = resource.children.all()
1501        children = children.order_by('code')
1502        related_media = self.related.objects.filter(resource=resource)
1503        check_related_media(related_media)
1504        playlists = get_playlists(request)
1505        revisions = Revision.objects.filter(element_type=type, element_id=resource.id).order_by('-time')
1506        if revisions:
1507            last_revision = revisions[0]
1508        else:
1509            last_revision = None
1510        if self.parent:
1511            parents = self.parent.objects.filter(children=resource)
1512        else:
1513            parents = []
1514
1515        return render(request, template, {'resource': resource, 'type': type, 'children': children,
1516                        'related_media': related_media, 'parents': parents, 'playlists': playlists,
1517                        'last_revision': last_revision })
1518
1519    @jsonrpc_method('telemeta.change_fonds')
1520    @jsonrpc_method('telemeta.change_corpus')
1521    def edit(self, request, type, public_id, template='telemeta/resource_edit.html'):
1522        self.setup(type)
1523        resource = self.model.objects.get(code=public_id)
1524        if request.method == 'POST':
1525            form = self.form(data=request.POST, files=request.FILES, instance=resource)
1526            if form.is_valid():
1527                code = form.cleaned_data['code']
1528                if not code:
1529                    code = public_id
1530                form.save()
1531                resource.set_revision(request.user)
1532                return HttpResponseRedirect('/archives/'+self.type+'/'+code)
1533        else:
1534            form = self.form(instance=resource)
1535        return render(request, template, {'resource': resource, 'type': type, 'form': form,})
1536
1537    @jsonrpc_method('telemeta.add_fonds')
1538    @jsonrpc_method('telemeta.add_corpus')
1539    def add(self, request, type, template='telemeta/resource_add.html'):
1540        self.setup(type)
1541        resource = self.model()
1542        if request.method == 'POST':
1543            form = self.form(data=request.POST, files=request.FILES, instance=resource)
1544            if form.is_valid():
1545                code = form.cleaned_data['code']
1546                if not code:
1547                    code = public_id
1548                form.save()
1549                resource.set_revision(request.user)
1550                return HttpResponseRedirect('/archives/'+self.type +'/'+code)
1551        else:
1552            form = self.form(instance=resource)
1553        return render(request, template, {'resource': resource, 'type': type, 'form': form,})
1554
1555    @jsonrpc_method('telemeta.add_fonds')
1556    @jsonrpc_method('telemeta.add_corpus')
1557    def copy(self, request, type, public_id, template='telemeta/resource_edit.html'):
1558        self.setup(type)
1559        if request.method == 'POST':
1560            resource = self.model()
1561            form = self.form(data=request.POST, files=request.FILES, instance=resource)
1562            if form.is_valid():
1563                code = form.cleaned_data['code']
1564                if not code:
1565                    code = public_id
1566                resource.save()
1567                resource.set_revision(request.user)
1568                return HttpResponseRedirect('/archives/'+self.type +'/'+code)
1569        else:
1570            resource = self.model.objects.get(code=public_id)
1571            form = self.form(instance=resource)
1572        return render(request, template, {'resource': resource, 'type': type, "form": form,})
1573
1574    def playlist(self, request, type, public_id, template, mimetype):
1575        self.setup(type)
1576        try:
1577            resource = self.model.objects.get(code=public_id)
1578        except ObjectDoesNotExist:
1579            raise Http404
1580
1581        template = loader.get_template(template)
1582        context = RequestContext(request, {'resource': resource, 'host': request.META['HTTP_HOST']})
1583        return HttpResponse(template.render(context), mimetype=mimetype)
1584
1585    @jsonrpc_method('telemeta.del_fonds')
1586    @jsonrpc_method('telemeta.del_corpus')
1587    def delete(self, request, type, public_id):
1588        self.setup(type)
1589        resource = self.model.objects.get(code=public_id)
1590        resource.delete()
1591        return HttpResponseRedirect('/archives/'+self.type+'/')
1592
1593    def related_stream(self, request, type, public_id, media_id):
1594        self.setup(type)
1595        resource = self.model.objects.get(code=public_id)
1596        media = self.related.objects.get(resource=resource, id=media_id)
1597        response = HttpResponse(stream_from_file(media.file.path), mimetype=media.mime_type)
1598        return response
1599
1600    @jsonrpc_method('telemeta.add_fonds_related_media')
1601    @jsonrpc_method('telemeta.add_corpus_related_media')
1602    def related_edit(self, request, type, public_id, template):
1603        self.setup(type)
1604        resource = self.model.objects.get(code=public_id)
1605        ResourceRelatedFormSet = inlineformset_factory(self.model, self.related, form=self.related_form)
1606        if request.method == 'POST':
1607            formset = ResourceRelatedFormSet(data=request.POST, files=request.FILES, instance=resource)
1608            if formset.is_valid():
1609                formset.save()
1610                resource.set_revision(request.user)
1611                return HttpResponseRedirect('/archives/'+self.type+'/'+public_id)
1612        else:
1613            formset = ResourceRelatedFormSet(instance=resource)
1614        return render(request, template, {'resource': resource, 'type': type, 'formset': formset,})
1615
1616
Note: See TracBrowser for help on using the repository browser.