source: telemeta/web/base.py @ 40aa64b

cremcrem2devdev2diademsdj1.6feature/breadcrumbsfeature/ts-0.5feature/ts-0.5.4feature/writecacheformagenericinstru_searchlamlam2mapsv3mergenlivemultiproductionrelease/1.4.4sabiodsecurityserversocialstoragetelecastertestvideo
Last change on this file since 40aa64b was 40aa64b, checked in by yomguy <yomguy@…>, 3 years ago

just try metadata writing, fix cache dir creation, update example

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