Merge with e107b22256805bd992817fc0161f0611235eff51
authorgrandjoncl
Mon, 12 Nov 2012 16:17:22 +0100
changeset 931 cc5442349bdd
parent 925 fd2d4a7a5de6 (diff)
parent 930 e107b2225680 (current diff)
child 932 170da3907040
Merge with e107b22256805bd992817fc0161f0611235eff51
src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/embed_iframe.html
src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/embed_popup.html
src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/partial/embed_player.html
src/ldt/ldt/ldt_utils/views/workspace.py
src/ldt/ldt/locale/fr/LC_MESSAGES/django.mo
src/ldt/ldt/locale/fr/LC_MESSAGES/django.po
src/ldt/ldt/static/ldt/js/embed_popup.js
src/ldt/ldt/static/ldt/js/projectscontents.js
--- a/.hgignore	Mon Nov 12 15:03:36 2012 +0100
+++ b/.hgignore	Mon Nov 12 16:17:22 2012 +0100
@@ -33,3 +33,6 @@
 syntax: regexp
 ^virtualenv/sync/project-boot\.py$
 relre:^.metadata
+
+syntax: regexp
+^web/\.htusers$
\ No newline at end of file
--- a/.hgtags	Mon Nov 12 15:03:36 2012 +0100
+++ b/.hgtags	Mon Nov 12 16:17:22 2012 +0100
@@ -112,3 +112,13 @@
 36d8d68fda811a9020dfcb4794ad42403ab7806b V01.26
 3eae57bb42b3d5c8daf3e94331f9ef30cf444693 V01.26
 2151b47c58c3291f82a2e190c474e7f616266742 V01.27
+ba611074ecb2c86907386b1c76c87f38e35aa6f0 V01.28
+ba611074ecb2c86907386b1c76c87f38e35aa6f0 V01.28
+be9868b4b13dbbf18bb82c5cad5008e5a540f43b V01.28
+be9868b4b13dbbf18bb82c5cad5008e5a540f43b V01.28
+ba43c842648c3d997157fae50693af90680e9875 V01.28
+ba43c842648c3d997157fae50693af90680e9875 V01.28
+7be50692abb4ad436eb668f20732dd79449c199f V01.28
+7be50692abb4ad436eb668f20732dd79449c199f V01.28
+e20fa653364745615a79f887eb6b77dfd58b171d V01.28
+d42fbe878062cf9765b8f968c5aad2161c479cd5 V01.29
--- a/src/ldt/README	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/README	Mon Nov 12 16:17:22 2012 +0100
@@ -5,7 +5,6 @@
 The ldt platform, is a django module allowing the annotation, indexation, consultation of temporal content (audio, video)
 
 
-
 trick:
 compile messages for js
 python ../../../web/ldtplatform/manage.py makemessages -a -d djangojs
@@ -62,3 +61,16 @@
 swfobject.js
 ZeroClipboard
 tiny_mce
+
+
+==============
+Unit Test
+==============
+
+In a terminal :
+	python manage.py test %test_api%
+Will launch all the tests of the api
+
+For example :
+	python manage.py test ldt_utils
+Will launch all the test defined in /ldt_utils/tests	
\ No newline at end of file
--- a/src/ldt/ldt/__init__.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/__init__.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,4 +1,4 @@
-VERSION = (1, 27, 0, "final", 0)
+VERSION = (1, 29, 0, "final", 0)
 
 
 def get_version():
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/api/ldt/authentication.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,58 @@
+from django.conf import settings
+from django.middleware.csrf import _sanitize_token, constant_time_compare
+from django.utils.http import same_origin
+from tastypie.authentication import Authentication
+
+# imported from tastypie's next version 0.9.12
+class SessionAuthentication(Authentication):
+    """
+    An authentication mechanism that piggy-backs on Django sessions.
+    
+    This is useful when the API is talking to Javascript on the same site.
+    Relies on the user being logged in through the standard Django login
+    setup.
+    
+    Requires a valid CSRF token.
+    """
+    def is_authenticated(self, request, **kwargs):
+        """
+        Checks to make sure the user is logged in & has a Django session.
+        """
+        # Cargo-culted from Django 1.3/1.4's ``django/middleware/csrf.py``.
+        # We can't just use what's there, since the return values will be
+        # wrong.
+        # We also can't risk accessing ``request.POST``, which will break with
+        # the serialized bodies.
+        if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
+            return request.user.is_authenticated()
+
+        if getattr(request, '_dont_enforce_csrf_checks', False):
+            return request.user.is_authenticated()
+
+        csrf_token = _sanitize_token(request.COOKIES.get(settings.CSRF_COOKIE_NAME, ''))
+
+        if request.is_secure():
+            referer = request.META.get('HTTP_REFERER')
+
+            if referer is None:
+                return False
+
+            good_referer = 'https://%s/' % request.get_host()
+
+            if not same_origin(referer, good_referer):
+                return False
+
+        request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
+
+        if not constant_time_compare(request_csrf_token, csrf_token):
+            return False
+
+        return request.user.is_authenticated()
+
+    def get_identifier(self, request):
+        """
+        Provides a unique string identifier for the requestor.
+        
+        This implementation returns the user's username.
+        """
+        return request.user.username
\ No newline at end of file
--- a/src/ldt/ldt/api/ldt/resources/__init__.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/api/ldt/resources/__init__.py	Mon Nov 12 16:17:22 2012 +0100
@@ -2,5 +2,6 @@
 from content import ContentResource
 from project import ProjectResource
 from segment import SegmentResource
+from user import UserResource
 
-__all__ = ["AnnotationResource", "ContentResource", "ProjectResource", "SegmentResource"]
+__all__ = ["AnnotationResource", "ContentResource", "ProjectResource", "SegmentResource", "UserResource"]
--- a/src/ldt/ldt/api/ldt/resources/annotation.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/api/ldt/resources/annotation.py	Mon Nov 12 16:17:22 2012 +0100
@@ -10,17 +10,17 @@
 
 
 class AnnotationObject(object):
-    # For the moment, these attributes are useless. We just need to define AnnotationObject
-    id = ""
-    project = ""
-    type = ""
-    type_title = ""
-    media = ""
-    begin = ""
-    end = "" 
-    content = {"title":"", "description":""}
-    tags = []
-    meta = {"creator":"","created":""}
+    def __init__(self, id="", project = "", type = "", type_title = "", media = "", begin = 0, end = 0, content = {"title":"", "description":""}, tags = [], meta = {"creator":"","created":""}):
+        self.id = id
+        self.project = project
+        self.type = type
+        self.type_title = type_title
+        self.media = media
+        self.begin = begin
+        self.end = end
+        self.content = content
+        self.tags = tags
+        self.meta = meta
     
 class AnnotationResource(Resource):
     # For the moment, these attributes are useless. We just prepare relations to AnnotationObject
@@ -36,23 +36,24 @@
     meta = fields.DictField(attribute = 'meta')
     
     class Meta:
-        allowed_methods = ['put']
+        allowed_methods = ['post']
         resource_name = 'annotations'
         object_class = AnnotationObject
         authorization = Authorization()
         # always_return_data = True because we want the api returns the data with the updated ids
         always_return_data = True
+        include_resource_uri = False
     
     def obj_delete_list(self, request=None, **kwargs):
         return True
     
     def obj_create(self, bundle, request=None, **kwargs):
-        """
-        """
-        logging.debug("ICI 0-1 bundle.data = " + repr(bundle.data))
+        #logging.debug("ICI 0-1 bundle.data = " + repr(bundle.data))
+        # Here the a has the datas for only one annotation. Tastypie's post allows only one resource addition
+        a = bundle.data
         project_id = ""
-        if bundle.data.has_key('project') :
-            project_id = bundle.data["project"]
+        if a.has_key('project') :
+            project_id = a["project"]
         if project_id and project_id != "" :
             try:
                 project = Project.objects.get(ldt_id=project_id)
@@ -60,18 +61,17 @@
                 raise NotFound("Project not found. project_id = " + project_id)
         else :
             # If the project's is not defined, we get or create the content's front project.
-            iri_id = bundle.data["media"]
+            iri_id = a["media"]
             try:
                 content = Content.objects.get(iri_id=iri_id)
             except Content.DoesNotExist:
                 raise NotFound("Content not found. iri_id = " + iri_id)
             project = content.get_or_create_front_project()
-            bundle.data["project"] = project.ldt_id
+            a[u"project"] = project.ldt_id
         
         adder = LdtAnnotation(project)
         unprotect_models() # Allows anonymous user to modify models in this request only
-        # Here the bundle.data has the datas for only one annotation. The others api's functions parse the "objects" from the request's json.
-        a = bundle.data
+        
         dur = str(a['end'] - a['begin'])
         begin = str(a['begin'])
         # We test if the annotation has audio node
@@ -95,16 +95,19 @@
         add_annotation_to_stat(content, a['begin'], a['end'])
         
         # We update the ids
-        bundle.data['type'] = type_id
-        bundle.data['id'] = new_id
-        if not bundle.data['content'].has_key('audio') :
-            bundle.data['content']['audio'] = {'src':audio_src, 'href':audio_href}
-        # We reinject the datas in the bundle's response
-        bundle = self.full_hydrate(bundle)
+        a['type'] = type_id
+        a['id'] = new_id
+        if not a['content'].has_key('audio') :
+            a['content']['audio'] = {'src':audio_src, 'href':audio_href}
             
         # We save the added annotation and reprotect the contents and projects
         adder.save()
         protect_models()
+        # We update the AnnotationObject for the returned datas to be correct.
+        bundle.obj = AnnotationObject(id = a["id"], project = a["project"], type = a["type"], type_title = a["type_title"], media = a["media"], begin = a["begin"], end = a["end"], content = a['content'], tags = a['tags'], meta = a['meta'])
         return bundle
+    
+    def get_resource_uri(self, bundle_or_obj):
+        return ''
 
     
\ No newline at end of file
--- a/src/ldt/ldt/api/ldt/resources/content.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/api/ldt/resources/content.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,18 +1,56 @@
 from django.conf.urls.defaults import url
-from ldt.ldt_utils.models import Content
-from tastypie.resources import ModelResource
+from ldt.ldt_utils.models import Content, Segment
+from tastypie.resources import Bundle, ModelResource
+from ldt.indexation import get_results_list
+from itertools import groupby
 
         
 class ContentResource(ModelResource):
     class Meta:
         allowed_methods = ['get']
         resource_name = 'contents'
-        queryset = Content.objects.all()
+        queryset = Content.safe_objects.all()
         excludes = ['media_obj']
 
     def override_urls(self):
         # WARNING : in tastypie <= 1.0, override_urls is used instead of prepend_urls. From 1.0.0, prepend_urls will be prefered and override_urls deprecated 
         return [
-            url(r"^(?P<resource_name>%s)/(?P<iri_id>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
+            url(r"^(?P<resource_name>%s)/recommended/$" % self._meta.resource_name, self.wrap_view('get_recommended'), name="api_contents_recommended"),
+            url(r"^(?P<resource_name>%s)/(?P<iri_id>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_contents_dispatch_detail"),            
         ]
+    def get_resource_uri(self, bundle_or_obj):
+        kwargs = {
+            'resource_name': self._meta.resource_name,
+            'api_name': self._meta.api_name
+        }
+        if isinstance(bundle_or_obj, Bundle):
+            kwargs['iri_id'] = bundle_or_obj.obj.iri_id
+        else:
+            kwargs['iri_id'] = bundle_or_obj.iri_id
+        return self._build_reverse_url("api_dispatch_detail", kwargs=kwargs)
+    
+    def get_recommended(self, request, **kwargs):
+        self.method_check(request, allowed=['get'])
+        
+        keywords = request.GET.get('keywords','')
+        keywords_search = " OR ".join(keywords.split(','))
+        field = request.GET.get('field','all')
+        
+        result_list = get_results_list(field, keywords_search)
+        score_dict = dict([(k,sum([e.score for e in i])) for k,i in groupby(result_list, lambda e: e.iri_id)])
+        
+        res = [self.full_dehydrate(self.build_bundle(obj=c, request=request)) for c in Content.safe_objects.filter(iri_id__in = score_dict.keys())]
+
+        def add_score(b,s):
+            b.data['score'] = s
+            return b
+
+        object_list = {
+            'objects': sorted([add_score(b, score_dict.get(b.data['iri_id'],0)) for b in res], key=lambda b: b.data['score'], reverse=True),
+        }
+
+        self.log_throttled_access(request)
+        
+        return  self.create_response(request, object_list)
+        
             
\ No newline at end of file
--- a/src/ldt/ldt/api/ldt/resources/project.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/api/ldt/resources/project.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,15 +1,26 @@
 from django.conf.urls.defaults import url
 from ldt.ldt_utils.models import Project
+from ldt.api.ldt.authentication import SessionAuthentication
+from ldt.api.ldt.serializers.cinelabserializer import CinelabSerializer
+from ldt.api.ldt.resources import ContentResource
+from ldt.api.ldt.resources.user import UserResource
 from tastypie.authorization import Authorization
-from tastypie.resources import ModelResource
-
+from tastypie.resources import Bundle, ModelResource
+from tastypie import fields
+from ldt.security import protect_models, unprotect_models
 
 class ProjectResource(ModelResource):
+    contents = fields.ManyToManyField(ContentResource, 'contents')
+    owner = fields.ForeignKey(UserResource, 'owner')
     class Meta:
-        allowed_methods = ['get', 'put']
-        authorization= Authorization() # BE CAREFUL WITH THAT, it's unsecure
+        allowed_methods = ['get', 'post']
+        authorization = Authorization() # BE CAREFUL WITH THAT, it's unsecure
+        authentication = SessionAuthentication()
         resource_name = 'projects'
-        queryset = Project.objects.all()
+        queryset = Project.safe_objects.all()
+        serializer = CinelabSerializer()
+        # In the future version :
+        # detail_uri_name = 'ldt_id'
     
 #    # WARNING : this project API will only accepts and returns json format, no matter format get parameter.
 #    def determine_format(self, request):
@@ -20,4 +31,21 @@
         return [
             url(r"^(?P<resource_name>%s)/(?P<ldt_id>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
         ]
+    
+    def get_resource_uri(self, bundle_or_obj):
+        kwargs = {
+            'resource_name': self._meta.resource_name,
+            'api_name': self._meta.api_name
+        }
+        if isinstance(bundle_or_obj, Bundle):
+            kwargs['ldt_id'] = bundle_or_obj.obj.ldt_id
+        else:
+            kwargs['ldt_id'] = bundle_or_obj.ldt_id
+        return self._build_reverse_url("api_dispatch_detail", kwargs=kwargs)
+    
+    # TEMPORARY (used before authentication/authorization, because saving a project modifies a Content (via ContentStat))
+    def save_m2m(self, bundle):
+        unprotect_models()
+        super(ProjectResource, self).save_m2m(bundle)
+        protect_models()
     
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/api/ldt/resources/user.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,26 @@
+from django.contrib.auth.models import User
+from django.conf.urls.defaults import url
+from tastypie.resources import Bundle, ModelResource
+
+class UserResource(ModelResource):
+    class Meta:
+        allowed_methods = ['get']
+        queryset =  User.objects.all()
+        resource_name = 'users'
+    
+    def override_urls(self):
+        # WARNING : in tastypie <= 1.0, override_urls is used instead of prepend_urls. From 1.0.0, prepend_urls will be prefered and override_urls deprecated 
+        return [
+            url(r"^(?P<resource_name>%s)/(?P<username>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
+        ]
+    
+    def get_resource_uri(self, bundle_or_obj):
+        kwargs = {
+            'resource_name': self._meta.resource_name,
+            'api_name': self._meta.api_name
+        }
+        if isinstance(bundle_or_obj, Bundle):
+            kwargs['username'] = bundle_or_obj.obj.username
+        else:
+            kwargs['username'] = bundle_or_obj.username
+        return self._build_reverse_url("api_dispatch_detail", kwargs=kwargs)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/api/ldt/serializers/__init__.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,1 @@
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/api/ldt/serializers/cinelabserializer.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,292 @@
+from django.conf import settings
+from django.core.serializers import json
+from django.core.urlresolvers import reverse
+from django.utils import simplejson
+from ldt.ldt_utils.models import Content, Project
+from ldt.ldt_utils.projectserializer import ProjectJsonSerializer
+from ldt.ldt_utils.utils import generate_uuid
+from tastypie.serializers import Serializer
+from tastypie.exceptions import NotFound
+import math
+import lxml.etree
+import logging
+
+
+class CinelabSerializer(Serializer):
+    # Thanks to the this serializer, the api will be able to serialize and deserialize a project in cinelab json format
+    # See http://liris.cnrs.fr/advene/cinelab/ for more information
+    formats = ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist', 'cinelab']
+    content_types = {
+        'cinelab': 'application/cinelab',
+        'json': 'application/json',
+        'jsonp': 'text/javascript',
+        'xml': 'application/xml',
+        'yaml': 'text/yaml',
+        'html': 'text/html',
+        'plist': 'application/x-plist',
+    }
+    json_indent = 2
+     
+    def to_cinelab(self, data, options=None):
+        """
+        Given some Python data, produces Cinelab/JSON output.
+        N.B. : for the moment, this serializer works with api_dispatch_detail only. 
+               It does not work with api_dispatch_list. Only from_cinelab deals with api_dispatch_list's multiple objects like {"objects":[...]}
+        """
+        options = options or {}
+        if hasattr(data, 'obj') :
+            if isinstance(data.obj, Project) :
+                ps = ProjectJsonSerializer(data.obj)
+                data = ps.serialize_to_cinelab()
+                return simplejson.dumps(data, cls=json.DjangoJSONEncoder, sort_keys=True, indent=self.json_indent)
+        raise NotFound("Project not found")
+    
+
+    def from_cinelab(self, content):
+        """
+        This function takes cinelab/json and transform it into a regular REST API json project data.
+        All fields are (* required at rebuilt json) :
+        "changed_by": "admin",
+        "created_by": "admin",
+        "creation_date": "2012-02-11T01:18:48.028534",
+        "description": "",
+        "image": "/pf/static/media/thumbnails/contents/content_default_icon.png",
+        "ldt*": "<iri><project>...<medias>...<annotations>...<displays>...<edits>...</iri>",
+        "ldt_id*": "594dc612-544e-11e1-96e5-00145ea49a02",
+        "modification_date": "2012-02-11T01:19:40.203322",
+        "state": 2,
+        "title*": "front project : test DailyMotion"
+        "contents*": ["IRI_ID_1","IRI_ID_2"]
+        "owner*": "user_id"
+        """
+        logging.debug("FROM cinelab content = " + content)
+        
+        cinelab = simplejson.loads(content)
+        meta = cinelab["meta"]
+        ldt_id = generate_uuid()
+        # default state = (1, 'edition') OR (2, 'published')
+        state = 2
+        contents = [reverse("api_dispatch_detail", kwargs={"api_name":"1.0", "resource_name":"contents", "iri_id":c["id"]}) for c in cinelab["medias"]]
+        owner_uri = reverse("api_dispatch_detail", kwargs={"api_name":"1.0", "resource_name":"users", "username":meta["dc:creator"]})
+        
+        ldt = lxml.etree.tostring(self.cinelab_to_ldt(cinelab), pretty_print=True)
+        
+        s = {"description": meta["dc:description"], "ldt_id": ldt_id, "title": meta["dc:title"], 
+             "created_by": meta["dc:creator"], "changed_by": meta["dc:contributor"], "created_by": meta["dc:creator"], "changed_by": meta["dc:contributor"],
+             "contents": contents, "owner": owner_uri, "state":state, "ldt":ldt}
+        
+        #s = '{"description": "", "ldt": "<iri><project/><medias/><annotations/><displays/><edits/></iri>", "000ldt_id": "gen_by_tc","title": "aaa GEN BY TC"}'
+        #s = '{"description": "", "ldt": "<iri><project/><medias/><annotations/><displays/><edits/></iri>", "title": "aaaGEN BY TC"}'
+        #return simplejson.loads(simplejson.dumps(s))
+        return s
+    
+    
+    def cinelab_to_ldt(self, cinelab, ldt_id=None):
+        # Start xml
+        meta = cinelab["meta"]
+        annotation_types = cinelab["annotation-types"]
+        annotations = cinelab["annotations"]
+        # If the ldt_id is not set, we get in the cinelab meta
+        if not ldt_id:
+            ldt_id = meta["id"]
+        
+        # Before creating the xml, we build a dict for tag {"id":"label"}
+        # Careful : works with python >= 2.7
+        tag_dict = {t["id"]:t["meta"]["dc:title"] for t in cinelab["tags"]}
+        # We'll also build a annotation-type to media/ensemble dict to simplify the views node building
+        at_media_dict = {}
+        # We'll also build a dict from annotation id to media/ensemble/decoupage dict to simplify the edits/mashup node building
+        annot_ids_dict = {}
+        # We'll also build a dict from media id to media url dict to simplify the edits/mashup node building
+        media_url_dict = {}
+        
+        # create a dom
+        iri = lxml.etree.Element('iri')
+        
+        # Node project
+        projectNode = lxml.etree.SubElement(iri, 'project')
+        projectNode.set('abstract', meta["dc:description"])
+        projectNode.set('title', meta["dc:title"])
+        projectNode.set('user', meta["dc:creator"])
+        projectNode.set('id', ldt_id)
+    
+        # Node medias and node annotations
+        mediasNode = lxml.etree.SubElement(iri, 'medias')
+        annotationsNode = lxml.etree.SubElement(iri, 'annotations')
+        for c in cinelab["medias"]:
+            # We add the content to the medias node
+            content = Content.objects.get(iri_id=c["id"])
+            iri_id = content.iri_id
+            mediaNode = lxml.etree.SubElement(mediasNode, 'media')
+            mediaNode.set('id', iri_id)
+            mediaNode.set('src', content.iri_url())
+            if content.videopath != None :
+                mediaNode.set('video', content.videopath)
+                media_url_dict[iri_id] = {'url':content.videopath + content.src, 'pos':None}
+            else:
+                mediaNode.set('video', settings.STREAM_URL)
+                media_url_dict[iri_id] = {'url':settings.STREAM_URL + content.src, 'pos':None}
+            mediaNode.set('pict', "")
+            mediaNode.set('extra', "")
+            # We add the annotations
+            contentNode = lxml.etree.SubElement(annotationsNode, 'content')
+            contentNode.set('id', iri_id)
+            # we search the cinelab lists if they represent a content's list of annotation-type
+            for l in cinelab["lists"]:
+                if l["meta"].has_key("id-ref"):
+                    if l["meta"]["id-ref"]==iri_id:
+                        # We build the ensemble node
+                        ensembleNode = lxml.etree.SubElement(contentNode, 'ensemble')
+                        ensembleNode.set('id', l["id"])
+                        ensembleNode.set('idProject', ldt_id)
+                        ensembleNode.set('title', l["meta"]["dc:title"])
+                        ensembleNode.set('author', l["meta"]["dc:creator"])
+                        ensembleNode.set('abstract', l["meta"]["dc:description"])
+                        # We build the decoupage node, equivalent to an annotation-type
+                        for at_ref in l["items"]:
+                            at_id = at_ref["id-ref"]
+                            # We get the annotation-type datas
+                            for at in annotation_types:
+                                if at["id"]==at_id:
+                                    at_media_dict[at_id] = (iri_id, l["id"])
+                                    decoupageNode = lxml.etree.SubElement(ensembleNode, 'decoupage')
+                                    decoupageNode.set('id', at_id)
+                                    decoupageNode.set('author', at["dc:creator"])
+                                    titleDec = lxml.etree.SubElement(decoupageNode, 'title')
+                                    titleDec.text = at["dc:title"]
+                                    abstractDec = lxml.etree.SubElement(decoupageNode, 'abstract')
+                                    abstractDec.text = at["dc:description"]
+                                    elementsNode = lxml.etree.SubElement(decoupageNode, 'elements')
+                                    # We get all the annotations for this media and this annotation-type
+                                    for a in annotations:
+                                        if a["media"]==iri_id and a["meta"]["id-ref"]==at_id:
+                                            annot_ids_dict[a["id"]] = (iri_id, l["id"], at_id, a["begin"], a["end"], a["color"])
+                                            elementNode = lxml.etree.SubElement(elementsNode, 'element')
+                                            elementNode.set('id', a["id"])
+                                            elementNode.set('begin', str(a["begin"]))
+                                            elementNode.set('dur', str(a["end"]-a["begin"]))
+                                            elementNode.set('author', a["meta"]["dc:creator"])
+                                            elementNode.set('date', a["meta"]["dc:modified"])
+                                            elementNode.set('color', a["color"])
+                                            elementNode.set('src', "")
+                                            titleElm = lxml.etree.SubElement(elementNode, 'title')
+                                            titleElm.text = a["content"]["title"]
+                                            abstractElm = lxml.etree.SubElement(elementNode, 'abstract')
+                                            abstractElm.text = a["content"]["description"]
+                                            # Audio node, if the dict has the audio key
+                                            audioElm = lxml.etree.SubElement(elementNode, 'audio')
+                                            audioElm.set('source', "")
+                                            if a["content"].has_key("audio"):
+                                                audioElm.set('source', a["content"]["audio"]["src"])
+                                                audioElm.text = a["content"]["audio"]["href"]
+                                            # The tags dict has been set before, we just get the labels
+                                            tagsNode = lxml.etree.SubElement(elementNode, 'tags')
+                                            if a["tags"]:
+                                                for t in a["tags"]:
+                                                    if tag_dict.has_key(t["id-ref"]):
+                                                        tagNode = lxml.etree.SubElement(tagsNode, 'tag')
+                                                        tagNode.text = tag_dict[t["id-ref"]]
+                                            # Last element's node
+                                            lxml.etree.SubElement(elementNode, 'meta')
+        
+        # Now all medias and annotation-types and annotations are the xml
+        # We can set the views/displays node
+        displaysNode = lxml.etree.SubElement(iri, 'displays')
+        id_sel = None
+        i = 1
+        for v in cinelab["views"]:
+            if "stat" not in v["id"] and v.has_key("annotation_types"):
+                displayNode = lxml.etree.SubElement(displaysNode, 'display')
+                displayNode.set('id', v["id"])
+                displayNode.set('title', "View " + str(i))
+                i += 1
+                displayNode.set('tc', "0")
+                displayNode.set('zoom', "0")
+                displayNode.set('scroll', "0")
+                audioDis = lxml.etree.SubElement(displayNode, 'audio')
+                audioDis.set('source', "")
+                last_iri_id = ""
+                last_content_node = None
+                for at_id in v["annotation_types"]:
+                    iri_id, ens_id = at_media_dict[at_id]
+                    if iri_id != last_iri_id:
+                        last_iri_id = iri_id
+                        last_content_node = lxml.etree.SubElement(displayNode, 'content')
+                        last_content_node.set('id', iri_id)
+                    if last_content_node is not None:
+                        decoupageNode = lxml.etree.SubElement(last_content_node, 'decoupage')
+                        decoupageNode.set('idens', ens_id)
+                        decoupageNode.set('id', at_id)
+                        decoupageNode.set('tagsSelect', "")
+                    if not id_sel:
+                        id_sel = iri_id
+                if not id_sel:
+                    id_sel = ""
+                displayNode.set('idsel', id_sel)
+        
+        # Now we build the edit node
+        editsNode = lxml.etree.SubElement(iri, 'edits')
+        i = 0
+        for l in cinelab["lists"]:
+            if l["meta"].has_key("listtype"):
+                if l["meta"]["listtype"]=="mashup":
+                    editingNode = lxml.etree.SubElement(editsNode, 'editing')
+                    editingNode.set('id', str(i))
+                    i += 1
+                    editingNode.set('tags', "")
+                    titleEd = lxml.etree.SubElement(editingNode, 'title')
+                    titleEd.text = l["meta"]["dc:title"]
+                    abstractEd = lxml.etree.SubElement(editingNode, 'abstract')
+                    abstractEd.text = l["meta"]["dc:description"]
+                    editNode = lxml.etree.SubElement(editingNode, 'edit')
+                    editNode.set('id', "edit1")
+                    editNode.set('tags', "")
+                    # We build the 4 nodes (2 are used in reality : edit list and media list
+                    eListNode = lxml.etree.SubElement(editNode, 'eList')
+                    lxml.etree.SubElement(editNode, 'caption')
+                    lxml.etree.SubElement(editNode, 'audio')
+                    mListNode = lxml.etree.SubElement(editNode, 'mList')
+                    media_pos = 0
+                    edit_fulltime = 0
+                    for a_id in l["items"]:
+                        # the key is the annotation's id
+                        iri_id, ens_id, at_id, begin, end, color = annot_ids_dict[a_id]
+                        # We check the media's position in media list. If it was not set, we create the mList node
+                        if media_url_dict[iri_id]["pos"] is None:
+                            media_url_dict[iri_id]["pos"] = media_pos
+                            mNode = lxml.etree.SubElement(mListNode, 'm')
+                            mNode.set('ref', iri_id)
+                            mNode.set('id', str(media_pos))
+                            media_pos += 1
+                            mNode.set('t', "v")
+                            mNode.set('c', color)
+                            contentEd = lxml.etree.SubElement(mNode, 'content')
+                            contentEd.text = media_url_dict[iri_id]["url"]
+                        # We add the annotation/instruction to the eList
+                        instNode = lxml.etree.SubElement(eListNode, 'inst')
+                        instNode.set('ref', iri_id + "|;|" + ens_id + "|;|" + at_id + "|;||;||;|" + a_id)
+                        b = int(math.floor(begin/1000))
+                        instNode.set('begin', str(b))
+                        e = int(math.floor(end/1000))
+                        instNode.set('end', str(e))
+                        instNode.set('m', str(media_url_dict[iri_id]["pos"]))
+                        instNode.set('v', "100")
+                        instNode.set('eBegin', str(edit_fulltime))
+                        edit_fulltime = edit_fulltime + e - b
+                        instNode.set('eEnd', str(edit_fulltime))
+                        instNode.set('trId', "0")
+                        instNode.set('trIc', "0")
+                        instNode.set('trOd', "0")
+                        instNode.set('trOc', "0")
+                    # The second empty edit
+                    edit2Node = lxml.etree.SubElement(editingNode, 'edit')
+                    edit2Node.set('id', "edit2")
+                    edit2Node.set('tags', "")
+                    lxml.etree.SubElement(edit2Node, 'eList')
+                    lxml.etree.SubElement(edit2Node, 'caption')
+                    lxml.etree.SubElement(edit2Node, 'audio')
+                    lxml.etree.SubElement(edit2Node, 'mList')
+        
+        # This is the end
+        return iri
+        
\ No newline at end of file
--- a/src/ldt/ldt/api/ldt/urls.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/api/ldt/urls.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,5 +1,5 @@
 from django.conf.urls.defaults import patterns, include
-from ldt.api.ldt.resources import ProjectResource, ContentResource, SegmentResource, AnnotationResource
+from ldt.api.ldt.resources import ProjectResource, ContentResource, SegmentResource, AnnotationResource, UserResource
 from tastypie.api import Api
 
 v1_api = Api(api_name='1.0')
@@ -7,6 +7,7 @@
 v1_api.register(ContentResource())
 v1_api.register(SegmentResource())
 v1_api.register(AnnotationResource())
+v1_api.register(UserResource())
 
 urlpatterns = patterns('',
     (r'', include(v1_api.urls)),
--- a/src/ldt/ldt/ldt_utils/contentindexer.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/contentindexer.py	Mon Nov 12 16:17:22 2012 +0100
@@ -8,7 +8,8 @@
 import logging
 import lxml.etree #@UnresolvedImport
 import tagging.utils
-import urllib #@UnresolvedImport
+from ldt.utils.url import request_with_auth
+from StringIO import StringIO
 
 logger = logging.getLogger(__name__)
 
@@ -129,8 +130,8 @@
             
     def index_content(self, content):        
         url = content.iri_url()
-        filepath = urllib.urlopen(url)
-        doc = lxml.etree.parse(filepath) #@UndefinedVariable
+        _, file_content = request_with_auth(url)
+        doc = lxml.etree.parse(StringIO(file_content)) #@UndefinedVariable
        
         Segment.objects.filter(iri_id=content.iri_id).delete() #@UndefinedVariable
         
--- a/src/ldt/ldt/ldt_utils/fileimport.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/fileimport.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,11 +1,13 @@
+from StringIO import StringIO
 from copy import deepcopy #@UnresolvedImport
 from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import transaction
 from ldt.utils import zipfileext
+from ldt.utils.url import request_with_auth
 from models import Content, Media
 import fnmatch
-import lxml.etree
+import lxml.etree #@UnresolvedImport
 import mimetypes #@UnresolvedImport
 import os.path
 import shutil #@UnresolvedImport
@@ -25,8 +27,8 @@
 class IriInfo(object):
 
     
-    def __init__(self, id, order, titledesc, basepath="", videopath=settings.STREAM_URL, decoupage_blacklist=settings.DECOUPAGE_BLACKLIST, flatten=True):
-        self.id = id
+    def __init__(self, iri_id, order, titledesc, basepath="", videopath=settings.STREAM_URL, decoupage_blacklist=settings.DECOUPAGE_BLACKLIST, flatten=True):
+        self.id = iri_id
         self.basepath = basepath
         self.order = order
         self.src = ""
@@ -48,14 +50,15 @@
     def process_iri(self):
         # for just import a file ldt and get the title for every media
         if 'http' in self.src:
-            #url = urllib.urlopen(self.src)
             path = self.src
-            #doc = xml.dom.minidom.parse(url)
         #for import a zip, get title and copy file .iri in the media directory
         else:
             path = os.path.join(self.basepath, self.src)
-            #doc = xml.dom.minidom.parse(path)
-            
+        
+        if 'http' in path:
+            _,content = request_with_auth(path)
+            path = StringIO(content)
+        
         doc = lxml.etree.parse(path) #@UndefinedVariable
         
         
@@ -78,9 +81,9 @@
             #if node.nodeType == xml.dom.Node.ELEMENT_NODE and node.tagName == "ensemble":
             if node.tag == "ensemble":
                 #id = node.getAttributeNS(None,u"id")
-                id = node.attrib["id"]
-                if id not in ensembleids:
-                    ensembleids.append(id)
+                ens_id = node.attrib["id"]
+                if ens_id not in ensembleids:
+                    ensembleids.append(ens_id)
 
         if self.annotations is not None:
             newEnsemble = None
@@ -304,43 +307,41 @@
         
         for i, medianode in  enumerate(result):
         # get iri file's id from file ldt
-            #id = medianode.attributes['id'].value
-            id = medianode.attrib['id']
+            iri_id = medianode.attrib['id']
             if self.check_existing_media:
                 try:
-                    Content.objects.get(iri_id=id)
+                    Content.objects.get(iri_id=iri_id)
                     do_pass = True
                 except ObjectDoesNotExist: #Content.DoesNotExist
                     do_pass = False
             else:
                     do_pass = False
             if not do_pass:
-                if not (contents.has_key(id)):
+                if not (contents.has_key(iri_id)):
                     # Create instance iriInfo(id, order, titledesc, basepath="", videopath=settings.STREAM_URL)
                     if ldtpath:
-                        contents[id] = IriInfo(id, i, "", os.path.dirname(ldtpath), flatten=self.flatten)
+                        contents[iri_id] = IriInfo(iri_id, i, "", os.path.dirname(ldtpath), flatten=self.flatten)
                     else: 
-                        contents[id] = IriInfo(id, i, "", flatten=self.flatten)
+                        contents[iri_id] = IriInfo(iri_id, i, "", flatten=self.flatten)
                     # Get iri file's url from ldt. This url can be relative path or absolute path.
-                #contents[id].src = medianode.attributes['src'].value
-                contents[id].src = medianode.attrib['src']
+                contents[iri_id].src = medianode.attrib['src']
                 if medianode.attrib['video'] != "":
-                    contents[id].videopath = medianode.attrib['video']
+                    contents[iri_id].videopath = medianode.attrib['video']
                 elif self.videopath != "" or self.videopath:
-                    contents[id].videopath = self.videopath
+                    contents[iri_id].videopath = self.videopath
                 else:
-                    contents[id].videopath = settings.STREAM_URL
+                    contents[iri_id].videopath = settings.STREAM_URL
                     
                 
         #get annotation of file ldt
         result = doc.xpath("/iri/annotations/content")
         
         for contentnode in result:
-            id = contentnode.attrib['id']
-            if contents.has_key(id):
+            content_id = contentnode.attrib['id']
+            if contents.has_key(content_id):
                 if self.author:
                     contentnode.set("author", unicode(self.author))
-                contents[id].annotations = contentnode
+                contents[content_id].annotations = contentnode
         
         #go throught values
         for iriinfo in contents.values():
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/fixtures/base_data.json	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,36 @@
+[
+  {
+    "pk": 6, 
+    "model": "text.annotation", 
+    "fields": {
+      "update_date": "2010-11-16 17:30:50", 
+      "description": "texte de description", 
+      "title": "titre de l'annotation", 
+      "color": "#DDDDDD", 
+      "text": "texte selectionne lors de la creation de l'annotation", 
+      "creator": "wakimd", 
+      "uri": "", 
+      "creation_date": "2010-11-16 17:01:41", 
+      "contributor": "oaubert", 
+      "tags_field": "tag3,tag1,", 
+      "external_id": "z2c1d1fa-629d-4520-a3d2-955b4f2582c0"
+    }
+  },
+  {
+    "pk": 7, 
+    "model": "text.annotation", 
+    "fields": {
+      "update_date": "2010-11-16 17:30:50", 
+      "description": "texte de description", 
+      "title": "titre de l'annotation", 
+      "color": "#DDDDDD", 
+      "text": "texte selectionne lors de la creation de l'annotation", 
+      "creator": "wakimd", 
+      "uri": "http://www.leezam.com/pub/epub/123456!/OPS/chapter2.xhtml#pos=56,168", 
+      "creation_date": "2010-11-16 17:01:41", 
+      "contributor": "oaubert", 
+      "tags_field": "tag3,tag1,", 
+      "external_id": "mypersonnalid2"
+    }
+  }
+]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/fixtures/user_data.json	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,67 @@
+[
+  {
+    "pk": 2, 
+    "model": "auth.user", 
+    "fields": {
+      "username": "admin", 
+      "first_name": "", 
+      "last_name": "", 
+      "is_active": true, 
+      "is_superuser": true, 
+      "is_staff": false, 
+      "last_login": "2010-12-12 00:04:07", 
+      "groups": [], "user_permissions": [], 
+      "password": "", 
+      "email": "admin@example.com", 
+      "date_joined": "2010-12-12 00:04:07"
+    }
+  },
+  {
+    "pk": 1, 
+    "model": "oauth_provider.resource", 
+    "fields": {
+      "url": "/api/1.0/text/delete/", 
+      "name": "delete", 
+      "is_readonly": true
+    }
+  },
+  {
+    "pk": 2, 
+    "model": "oauth_provider.resource", 
+    "fields": {
+      "url": "/api/1.0/text/create/", 
+      "name": "create", 
+      "is_readonly": true
+    }
+  },
+  {
+    "pk": 3, 
+    "model": "oauth_provider.resource", 
+    "fields": {
+      "url": "/api/1.0/text/update/", 
+      "name": "update", 
+      "is_readonly": true
+    }
+  },
+  {
+    "pk": 4, 
+    "model": "oauth_provider.resource", 
+    "fields": {
+      "url": "", 
+      "name": "all", 
+      "is_readonly": true
+    }
+  },
+  {
+    "pk": 1, 
+    "model": "oauth_provider.consumer", 
+    "fields": {
+      "status": 1, 
+      "name": "example.com", 
+      "secret": "kd94hf93k423kf44", 
+      "user": 2, 
+      "key": "dpf43f3p2l4k3l03", 
+      "description": ""
+    }
+  }
+]
\ No newline at end of file
--- a/src/ldt/ldt/ldt_utils/models.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/models.py	Mon Nov 12 16:17:22 2012 +0100
@@ -8,13 +8,14 @@
     get_current_user)
 from ldt.security.manager import SafeManager
 from ldt.security.models import SafeModel
+from ldt.utils import url as url_utils
 from sorl.thumbnail import ImageField
 from tagging.models import Tag
 from utils import (create_ldt, copy_ldt, create_empty_iri, update_iri, 
     generate_uuid)
 from ldt.utils import generate_hash 
 import datetime
-import lxml.etree
+import lxml.etree #@UnresolvedImport
 import mimetypes
 import os.path
 import re
@@ -54,7 +55,6 @@
     description = models.TextField(null=True, blank=True, verbose_name=_('description'))
     title = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('title'))
     src = models.CharField(max_length=1024, verbose_name=_('media.src'))
-    #TODO: use a fixed length char field
     src_hash = models.CharField(max_length=128, unique=True, verbose_name=_('media.src_hash'), blank=True)
     mimetype_field = models.CharField(max_length=512, null=True, blank=True, verbose_name=_('media.mimetype'))
     
@@ -178,10 +178,10 @@
         iri_file_path = self.iri_file_path()
         thumbnail = os.path.join(settings.MEDIA_ROOT, unicode(self.image))
         if os.path.exists(iri_file_path):
-            dir = os.path.dirname(iri_file_path)
-            temp = os.path.join(os.path.join(os.path.dirname(dir), "temp"), self.iri_id)             
+            iri_dir = os.path.dirname(iri_file_path)
+            temp = os.path.join(os.path.join(os.path.dirname(iri_dir), "temp"), self.iri_id)             
             try:
-                move(dir, temp)
+                move(iri_dir, temp)
             except Exception, e:
                 raise e
         if os.path.exists(thumbnail):
@@ -197,8 +197,8 @@
     #del .iri, and .png from temp directory
     def commit(self):
         iri_file_path=self.iri_file_path()
-        dir = os.path.dirname(iri_file_path)
-        temp = os.path.join(os.path.join(os.path.dirname(dir), "temp"), self.iri_id)
+        iri_dir = os.path.dirname(iri_file_path)
+        temp = os.path.join(os.path.join(os.path.dirname(iri_dir), "temp"), self.iri_id)
         thumbnail = os.path.join(settings.MEDIA_ROOT, unicode(self.image))
         temp_thumbnail = os.path.join(os.path.dirname(thumbnail), "temp")
         if os.path.exists(temp):
@@ -211,12 +211,12 @@
     #move .iri, and .png to there original directory
     def rollback(self):
         iri_file_path=self.iri_file_path()
-        dir = os.path.dirname(iri_file_path)
-        temp = os.path.join(os.path.join(os.path.dirname(dir), "temp"), self.iri_id)
+        iri_dir = os.path.dirname(iri_file_path)
+        temp = os.path.join(os.path.join(os.path.dirname(iri_dir), "temp"), self.iri_id)
         thumbnail = os.path.join(settings.MEDIA_ROOT, unicode(self.image))
         temp_thumbnail = os.path.join(os.path.dirname(thumbnail), "temp")
         if os.path.exists(temp):
-            move(temp, dir)
+            move(temp, iri_dir)
             os.rmdir(os.path.dirname(temp))
         if os.path.exists(temp_thumbnail):
             move(os.path.join(temp_thumbnail, os.path.basename(thumbnail)), os.path.dirname(thumbnail))
@@ -260,12 +260,12 @@
         try:
             iri_file_path = self.iri_file_path()
             if not os.path.exists(iri_file_path):
-                dir = os.path.dirname(iri_file_path)
-                if not os.path.exists(dir):
-                    os.makedirs(dir)
+                iri_dir = os.path.dirname(iri_file_path)
+                if not os.path.exists(iri_dir):
+                    os.makedirs(iri_dir)
                 created = True
-                file = open(iri_file_path, "w")
-                create_empty_iri(file, self, "IRI")
+                iri_file = open(iri_file_path, "w")
+                create_empty_iri(iri_file, self, "IRI")
             else:
                 created = False
                 update_iri(iri_file_path, self, "IRI")
@@ -301,10 +301,13 @@
         return str(self.id) + ": " + self.iri_id
         
     def iri_url(self, web_url=settings.WEB_URL):
-        if 'http' in self.iriurl or 'https' in self.iriurl:
+        if url_utils.is_absolute(self.iriurl):
             return self.iriurl
         else:
-            return unicode(web_url) + unicode(settings.MEDIA_URL) + u"ldt/" + unicode(self.iriurl)
+            res_url = unicode(settings.MEDIA_URL) + u"ldt/" + unicode(self.iriurl)
+            if not url_utils.is_absolute(res_url):
+                res_url = unicode(web_url) + res_url
+            return res_url 
     
     def iri_file_path(self):
         return os.path.join(os.path.join(os.path.join(settings.MEDIA_ROOT, "ldt"), self.iri_id), os.path.basename(self.iriurl))
--- a/src/ldt/ldt/ldt_utils/projectserializer.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/projectserializer.py	Mon Nov 12 16:17:22 2012 +0100
@@ -16,7 +16,7 @@
 """
 Serialize a project object to a cinelab compatible array
 """
-class ProjectSerializer:
+class ProjectJsonSerializer:
     
     def __init__(self, project, from_contents=True, from_display=True, first_cutting=None, only_one_cutting=False):
         self.project = project
--- a/src/ldt/ldt/ldt_utils/searchutils.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/searchutils.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,4 +1,3 @@
-from django.conf import settings
 from ldt.indexation import SimpleSearch
 from ldt.ldt_utils.models import Content, Project
 from ldt.ldt_utils.utils import LdtUtils
--- a/src/ldt/ldt/ldt_utils/templates/front/front_base.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/front/front_base.html	Mon Nov 12 16:17:22 2012 +0100
@@ -78,7 +78,7 @@
 <!-- FOOTER COMMUN -->
     <ul id="footer">
         <li>{% blocktrans %}{{WEB_VERSION}} | {{ VERSION }}{% endblocktrans %}</li>
-        <li>©2011 IRI</li>
+        <li>©2012 IRI</li>
         <li>
             <a target="_blank" href="http://www.iri.centrepompidou.fr" title="{% trans 'link IRI'%}">{% trans "about" %}</a>
         </li>
--- a/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/create_group.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/create_group.html	Mon Nov 12 16:17:22 2012 +0100
@@ -1,12 +1,11 @@
-{% extends "ldt/ldt_raw_base.html" %}
+{% extends "ldt/ldt_utils/workspace_base.html" %}
+{% load i18n %}
 
-{% load i18n %}
 {% load adminmedia %}
 {% load thumbnail %}
 
 {% block js_import %}
 	{{ block.super }}
-    <script type="text/javascript" src="{{LDT_MEDIA_PREFIX}}js/jquery.nyroModal.min.js"></script> 
     <script type="text/javascript" src="{% url ldt.jsi18n packages='ldt' %}" ></script>
 	<script type="text/javascript" src="{{LDT_MEDIA_PREFIX}}js/projectscontents.js" ></script> 
 	<script type="text/javascript" src="{{LDT_MEDIA_PREFIX}}js/tiny_mce/tiny_mce.js"></script>
@@ -25,28 +24,8 @@
 {% block js_declaration %}
 	{{ block.super }}
 	<script type="text/javascript">
-	
-    tinyMCE.init({
-        plugins : '-example', 
-        mode : "textareas",
-        theme : "advanced",
-        language : "{{ LANGUAGE_CODE }}",
-        theme_advanced_buttons1 : "bold,italic,underline,separator,justifyleft,justifycenter,justifyfull,undo,redo,link,unlink",
-        theme_advanced_buttons2 : "",
-        theme_advanced_buttons3 : "",
-        theme_advanced_toolbar_location : "top",
-        theme_advanced_toolbar_align : "left",
-        width: "250",
-        height: "150",
-        entity_encoding : "raw"
-	});
 		
 	$(document).ready(function() {	
-		$("#close_button").click(function (e) {
-			e.preventDefault();
-			parent.$.nmTop().close();
-		});
-		
         $("#ldt_submit_delete").click(function(e){
             $(this).addClass("e-clicked");
         });
@@ -63,16 +42,13 @@
            $( this ).find("button[type=submit]").removeClass("e-clicked");
         });
 
-		
 		var search_url = "{% url ldt.ldt_utils.views.workspace.share_filter filter='__FILTER__' use_groups='false' %}";
-		resize_modal_window($("#add_group"));
-		
 	});
 	</script>
 
 {% endblock %}
 
-{% block body %}
+{% block content %}
 				 
 	<div id="add_group" class="span-18 last">
 	<div class="projectscontentstitle span-18 last">{% if group_id %}{% trans "Update a group" %}{% else %}{% trans "Create a group" %}{% endif %}</div>
@@ -96,9 +72,9 @@
 
 	{% include "ldt/ldt_utils/partial/permissions.html" %}		
 	{% include "ldt/ldt_utils/partial/picture.html"%}
-	
+
 	<div id="submitcontent-buttons" class="span-12 last">
-		<button type="button" id="close_button"  value="close">{% trans "close_cancel" %}</button>
+		<button class="button" id="close_button" type="submit" value="close" name="submit_button">{% trans "close_cancel" %}</button>
 		{% if group_id %}
           <button class="button" style="float:right; margin-left:4px" id="ldt_submit_update" type="submit" value="update" name="submit_button">{% trans "update_group" %}</button>
           {% if is_owner_group %}
--- a/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/embed_iframe.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/embed_iframe.html	Mon Nov 12 16:17:22 2012 +0100
@@ -54,22 +54,19 @@
 			    },
 			    widgets: [
 			        {
-			    {% if external_url %}
-			            type: "AutoPlayer",
-			            video: "{{ external_url }}",
-			    {% else %}
-			            type: "JwpPlayer",
-			            streamer: function(_url) {
-			                var _matches = _url.match(/^[^\/]+\/\/[^\/]+\/[^\/]+\//);
-			                if (_matches) {
-			                    return _matches[0];
-			                } else {
-			                    return _url;
-			                }
-			            },
-			            live: true,
-			            provider: "rtmp",
-			    {% endif %}
+			        	type: "AutoPlayer",
+			            {% if external_url %}
+			                    video: "{{ external_url }}",
+			            {% else %}
+			                    streamer: function(_url) {
+			                        var _matches = _url.match(/^[^\/]+\/\/[^\/]+\/[^\/]+\//);
+			                        if (_matches) {
+			                            return _matches[0];
+			                        } else {
+			                            return _url;
+			                        }
+			                    },
+			            {% endif %}
 			            height: '{{ player_height }}',
 			            autostart: true
 			        }  {% if polemic == 'all' or polemic == 'tweet' %},
--- a/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/embed_popup.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/embed_popup.html	Mon Nov 12 16:17:22 2012 +0100
@@ -90,7 +90,7 @@
 							<div id="iframe_div_premode" class="embedPersonnalisation">
 								<input type="radio" id="mode_1" name="embed_mode" value="mode1" checked onClick="mode1()" > {% trans 'Basic configuration' %} </input><br>
 								<input type="radio" id="mode_2" name="embed_mode" value="mode2" onClick="mode2()">  {% trans 'Polemic configuration' %} </input><br>
-								<input type="radio" id="mode_3" name="embed_mode" value="mode3" onClick="mode3()">  {% trans 'Polemic configuration with sparkline and tagcloud' %} </input><br>
+								<input type="radio" id="mode_3" name="embed_mode" value="mode3" onClick="mode3()">  {% trans 'Polemic configuration with tagcloud, annotationsList and Social widgets' %} </input><br>
 								<br>
 							</div>
 	
--- a/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/groups.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/groups.html	Mon Nov 12 16:17:22 2012 +0100
@@ -19,7 +19,6 @@
 
 function init_events(base_node) {
 	init_events_projects(base_node, "{% url ldt.ldt_utils.views.workspace.popup_embed %}", project_filter_url, publish_project_url, unpublish_project_url)
-	init_events_groups(base_node, "{% url ldt.ldt_utils.views.workspace.popup_embed %}", groups_filter_url);
 }
 	
 var global_csrf_token = "{{ csrf_token }}";
--- a/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/partial/embed_player.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/partial/embed_player.html	Mon Nov 12 16:17:22 2012 +0100
@@ -34,11 +34,10 @@
     },
     widgets: [
         {
+            type: "AutoPlayer",
     {% if external_url %}
-            type: "AutoPlayer",
             video: "{{ external_url }}",
     {% else %}
-            type: "JwpPlayer",
             streamer: function(_url) {
                 var _matches = _url.match(/^[^\/]+\/\/[^\/]+\/[^\/]+\//);
                 if (_matches) {
@@ -47,8 +46,6 @@
                     return _url;
                 }
             },
-            live: true,
-            provider: "rtmp",
     {% endif %}
             height: 300,
             autostart: true
@@ -90,7 +87,6 @@
         },{
             type: "CreateAnnotation",
             api_endpoint_template: "{{WEB_URL}}{% url api_dispatch_list resource_name='annotations' api_name='1.0' %}",
-            api_method: "PUT",
             after_send_timeout: 8000,
             show_mic_record: {% if show_mic_record %}true{% else %}false{% endif %},
             close_after_send: false,
--- a/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/partial/permissions.html	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/templates/ldt/ldt_utils/partial/permissions.html	Mon Nov 12 16:17:22 2012 +0100
@@ -121,8 +121,10 @@
 
 	<div id="permissioncontainer" class="append-bottom clear" {% if media_form and media_form.media_public.value %}style="display: none;"{% endif %}>
 		<div id="elemslistcontainer" clas="span-9">
+			{% if media_form %}
 			<div id="content_public">{{ content_form.is_public }}<label>{% trans  "publish the title on the front" %}</label></div>
 			<br />
+			{% endif %}
 			<div id="elemslistheader">
 				{% trans "User and group list" %}<br />
 				<div class="searchfield span-4">
@@ -147,7 +149,9 @@
 			<a href="#" id="selectelems"><img src="{{LDT_MEDIA_PREFIX}}img/to-right.gif" title="{% trans "select users" %}"></a><br />
 			<a href="#" id="removeelems"><img src="{{LDT_MEDIA_PREFIX}}img/to-left.gif" title="{% trans "remove users" %}"></a>
 		</div>
+		{% if media_form %}
 		<br />	<br />
+		{% endif %}
 		<div id="selectedlistcontainer" class="span-9 last">
 			<div id="selectedlistheader" >
 				{% trans "Members list" %}
--- a/src/ldt/ldt/ldt_utils/tests.py	Mon Nov 12 15:03:36 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +0,0 @@
-"""
-This file demonstrates two different styles of tests (one doctest and one
-unittest). These will both pass when you run "manage.py test".
-
-Replace these with more appropriate tests for your application.
-"""
-
-from django.conf import settings
-from django.test import TestCase
-from ldt.ldt_utils.models import User
-from models import Project, Content
-from utils import LdtUtils, LdtAnnotation, create_ldt, create_empty_iri, copy_ldt
-import lxml.etree
-import tempfile
-import unittest
-import uuid
-
-class SimpleTest(TestCase):
-    def test_basic_addition(self):
-        """
-        Tests that 1 + 1 always equals 2.
-        """
-        self.failUnlessEqual(1 + 1, 2)
-
-__test__ = {"doctest": """
-Another way to test that 1 + 1 is equal to 2.
-
->>> 1 + 1 == 2
-True
-"""}
-
-class UtilsTest(unittest.TestCase):
-    def setUp(self):
-        self.user = User()
-        self.user.username = "toto"
-        self.LU = LdtUtils()
-    
-        self.project = Project(title="titleproj1", owner=self.user)
-        self.project.ldt = '<iri ldtversion="1.0.3" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/"><project id="af3b99e4-b695-11df-bfde-00145ea4a2be" user="admin" title="CA:reponse a TC" abstract=""/> <medias> <media extra="" id="laurentcantet_entrelesmurs" pict="" src="http://amateur.iri.centrepompidou.fr//atelier/static/media/ldt/laurentcantet_entrelesmurs/laurentcantet_entrelesmurs.iri" video="rtmp://media.iri.centrepompidou.fr/ddc_player/video/regardssignes/"/> </medias> <annotations> <content id="laurentcantet_entrelesmurs"> <ensemble id="ens_perso" idProject="fe0d5d4c-2201-11df-8a24-00145ea4a2be" title="Decoupages personnels" author="perso" abstract=""> <decoupage id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" author="perso"> <title>CA: prof et admin</title> <abstract/> <elements> <element id="s_442AAB3A-42DA-F9BF-75E7-D2A0663FD5FF" begin="985690" dur="373222" author="" date="2010/09/02" color="16711680" src=""> <title/> <abstract/> <audio source=""/> <tags/> </element> <element id="s_0050F043-3AD2-0A7C-6699-D2A03A1EBA02" begin="5052858" dur="124407" author="" date="2010/09/02" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> <decoupage id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" author="perso"> <title>TC: prof et admin</title> <abstract/> <elements> <element id="s_880D9D4B-8BC0-BA43-5ECA-04EBA9FC9E59" begin="2426894" dur="141478" author="" date="2010/02/25" color="10053375" src=""> <title>Conseil de classe</title> <abstract/> <audio source=""/> <tags> <tag>Argumentation</tag> </tags> </element> <element id="s_D568A57C-7110-DED2-3165-04EC54387060" begin="5052858" dur="124407" author="" date="2010/02/25" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> </ensemble> </content> </annotations> <displays> <display id="0" title="Init view" idsel="laurentcantet_entrelesmurs" tc="2426424" zoom="0" scroll="0" infoBAB=""> <audio source=""/> <content id="laurentcantet_entrelesmurs"> <decoupage idens="en_2" id="de_PPP" tagsSelect=""/> <decoupage idens="laurentcantet_entrelesmurs" id="c_14A2E638-1936-97DC-E303-2DBA6A82A8B3" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" tagsSelect=""/> </content> </display> </displays> <edits> <editing id="0" tags=""> <title>Bout a bout 1</title> <abstract/> <edit id="edit1" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> <edit id="edit2" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> </editing> </edits> </iri>'
-        self.project.id = "11"
-        self.project.ldt_id = str(uuid.uuid1())
-        self.project.description = "proj1description"
-        self.project.save()
-        
-        self.projectcopy = Project(title="the2ndproject")
-        self.projectcopy.id = "22"
-
-    def tearDown(self):
-        self.project.delete()
-        self.projectcopy.delete()
-        #self.cont1.delete()
-        #self.cont2.delete()
-
-    def test_generate_ldt(self):
-        self.cont1 = Content(iriurl="id1/iriurl1")
-        self.cont1.iri_id = "id1"
-        self.cont1.save()
-        
-        self.cont2 = Content(iriurl="id2/iriurl2")
-        self.cont2.iri_id = "id2"
-        self.cont2.save()
-        
-        self.project.contents.add(self.cont1, self.cont2)
-    
-        f = tempfile.TemporaryFile(mode='r+')
-        doc = self.LU.generate_ldt(Content.objects.all())
-        doc.write(f, pretty_print=True)
-        f.seek(0)
-        ldoc = lxml.etree.parse(f)
-        self.assertEqual(ldoc.xpath("/iri/displays/display/content")[10].get("id"), self.cont2.iri_id)
-        self.assertEqual(ldoc.xpath("/iri/medias/media")[9].get("id"), self.cont1.iri_id)
-        f.close()
-
-    def test_generate_init(self):
-        self.cont3 = Content(iriurl="id3/iriurl1")
-        self.cont3.iri_id = "id3"
-        self.cont3.save()
-        
-        self.cont4 = Content(iriurl="id4/iriurl2")
-        self.cont4.iri_id = "id4"
-        self.cont4.save()
-        
-        self.project.contents.add(self.cont3, self.cont4)
-        ldoc = self.LU.generate_init(['all', 'foo'], 'ldt.ldt_utils.views.search_ldt')
-        self.assertEqual(ldoc.xpath("/iri/files/init")[0].tag, "init")
-        self.assertEqual(ldoc.xpath("/iri/files/library")[0].tag, "library")
-
-    def test_create_ldt(self):
-        self.cont5 = Content(iriurl="id5/iriurl1")
-        self.cont5.iri_id = "id5"
-        self.cont5.save()
-        
-        self.cont6 = Content(iriurl="id6/iriurl2")
-        self.cont6.iri_id = "id6"
-        self.cont6.save()
-        
-        self.project.contents.add(self.cont5, self.cont6)
-        self.project.ldt = ""
-        create_ldt(self.project, self.user)
-        ldt = lxml.etree.fromstring(self.project.ldt_encoded)
-        self.assertEqual(ldt.xpath("/iri")[0].tag, "iri")
-        self.assertEqual(ldt.xpath("/iri/project")[0].get("title"), self.project.title)
-        self.assertEqual(ldt.xpath("/iri/medias/media")[0].get("src"), self.cont5.iri_url())
-        self.assertEqual(ldt.xpath("/iri/medias/media")[1].get("id"), self.cont6.iri_id)
-
-    def test_copy_ldt(self):
-        self.cont7 = Content(iriurl="id7/iriurl1")
-        self.cont7.iri_id = "id7"
-        self.cont7.save()
-        
-        self.cont8 = Content(iriurl="id8/iriurl2")
-        self.cont8.iri_id = "id8"
-        self.cont8.save()
-        
-        self.project.contents.add(self.cont7, self.cont8)
-        copy_ldt(self.project, self.projectcopy, self.user)
-        ldt1 = lxml.etree.fromstring(self.project.ldt_encoded)
-        ldt2 = lxml.etree.fromstring(self.projectcopy.ldt_encoded)
-        self.assertTrue(ldt1.xpath("/iri/project")[0].get("id") != ldt2.xpath("/iri/project")[0].get("id"))
-        self.assertEqual(ldt1.xpath("/iri/medias/media")[0].get("id"), ldt2.xpath("/iri/medias/media")[0].get("id"))
-        self.assertEqual(ldt1.xpath("/iri/annotations/content/ensemble")[0].get("title"), ldt2.xpath("/iri/annotations/content/ensemble")[0].get("title"))
-        self.assertEqual(ldt1.xpath("/iri/annotations/content/ensemble/decoupage")[0].get("id"), ldt2.xpath("/iri/annotations/content/ensemble/decoupage")[0].get("id"))
-        self.assertEqual(ldt1.xpath("/iri/annotations/content/ensemble/decoupage/title")[1].text, ldt2.xpath("/iri/annotations/content/ensemble/decoupage/title")[1].text.strip("\n\t"))
-
-    def test_create_empty_iri(self):
-        self.cont9 = Content(iriurl="id9/iriurl1")
-        self.cont9.iri_id = "id9"
-        self.cont9.save()
-        
-        self.cont10 = Content(iriurl="id10/iriurl2")
-        self.cont10.iri_id = "id10"
-        self.cont10.save()
-        
-        self.project.contents.add(self.cont9, self.cont10)
-        tmp = tempfile.TemporaryFile(mode='r+')
-        create_empty_iri(tmp, self.cont9, "admin")
-        tmp.seek(0)
-        ldoc = lxml.etree.parse(tmp)
-        self.assertEqual(ldoc.xpath("/iri/head/meta")[0].get("content"), self.cont9.iri_id)
-        self.assertEqual(ldoc.xpath("/iri/body/medias/media/video")[0].get("id"), self.cont9.iri_id)
-        tmp.close()
-        
-    def test_add_annotation(self):
-        
-        self.cont11 = Content(iriurl="id11/iriurl1")
-        self.cont11.iri_id = "id11"
-        self.cont11.save()
-        
-        self.project.contents.add(self.cont11)
-        self.project.ldt = ""
-        create_ldt(self.project, self.user)
-                
-        self.LA = LdtAnnotation(self.project)
-        
-        self.LA.add("id11", "cutting", "title", "text", ["tag1", "tag2"], "800",
-                    "10000", "jdoe", "2011-09-10T09:12:58")
-        self.LA.save()
-        ldt = lxml.etree.fromstring(self.project.ldt)
-        ann = ldt.xpath('/iri/annotations/content[@id="id11"]/ensemble/decoupage/elements/element')[0]
-        title = ann.xpath('title')[0].text
-        abstract = ann.xpath('abstract')[0].text
-
-        self.assertEqual(ann.get("author"), "jdoe")
-        self.assertEqual(title, "title")
-        self.assertEqual(abstract, "text")
-
-
-class ViewsTest(unittest.TestCase):
-    def setUp(self):
-        self.project = Project()
-        self.project.id = "121"
-        self.project.save()
-        self.project.ldt = '<iri ldtversion="1.0.3" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/"><project id="af3b99e4-b695-11df-bfde-00145ea4a2be" user="admin" title="CA:reponse a TC" abstract=""/> <medias> <media extra="" id="laurentcantet_entrelesmurs" pict="" src="http://amateur.iri.centrepompidou.fr//atelier/static/media/ldt/laurentcantet_entrelesmurs/laurentcantet_entrelesmurs.iri" video="rtmp://media.iri.centrepompidou.fr/ddc_player/video/regardssignes/"/> </medias> <annotations> <content id="laurentcantet_entrelesmurs"> <ensemble id="ens_perso" idProject="fe0d5d4c-2201-11df-8a24-00145ea4a2be" title="Decoupages personnels" author="perso" abstract=""> <decoupage id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" author="perso"> <title>CA: prof et admin</title> <abstract/> <elements> <element id="s_442AAB3A-42DA-F9BF-75E7-D2A0663FD5FF" begin="985690" dur="373222" author="" date="2010/09/02" color="16711680" src=""> <title/> <abstract/> <audio source=""/> <tags/> </element> <element id="s_0050F043-3AD2-0A7C-6699-D2A03A1EBA02" begin="5052858" dur="124407" author="" date="2010/09/02" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> <decoupage id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" author="perso"> <title>TC: prof et admin</title> <abstract/> <elements> <element id="s_880D9D4B-8BC0-BA43-5ECA-04EBA9FC9E59" begin="2426894" dur="141478" author="" date="2010/02/25" color="10053375" src=""> <title>Conseil de classe</title> <abstract/> <audio source=""/> <tags> <tag>Argumentation</tag> </tags> </element> <element id="s_D568A57C-7110-DED2-3165-04EC54387060" begin="5052858" dur="124407" author="" date="2010/02/25" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> </ensemble> </content> </annotations> <displays> <display id="0" title="Init view" idsel="laurentcantet_entrelesmurs" tc="2426424" zoom="0" scroll="0" infoBAB=""> <audio source=""/> <content id="laurentcantet_entrelesmurs"> <decoupage idens="en_2" id="de_PPP" tagsSelect=""/> <decoupage idens="laurentcantet_entrelesmurs" id="c_14A2E638-1936-97DC-E303-2DBA6A82A8B3" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" tagsSelect=""/> </content> </display> </displays> <edits> <editing id="0" tags=""> <title>Bout a bout 1</title> <abstract/> <edit id="edit1" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> <edit id="edit2" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> </editing> </edits> </iri>' 
-    
-        self.cont1 = Content(iriurl="/laurentcantet_entrelesmurs/iriurl1")
-        self.cont1.iri_id = 'laurentcantet_entrelesmurs'
-        self.cont1.save()
-    
-        self.cont2 = Content(iriurl="/content_notinldt/iriurl2")
-        self.cont2.iri_id = 'content_notinldt'
-        self.cont2.save()
-        
-        self.project.contents.add(self.cont1, self.cont2)
-
-    def tearDown(self):
-        self.project.delete()
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/tests/__init__.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,4 @@
+from content_tests import *
+from ldt_tests import *
+from media_tests import *
+from project_tests import *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/tests/content_tests.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,75 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from ldt.test.testcases import OAuthWebTestCase, TestCase
+from django.conf import settings
+from django.test import TestCase
+from ldt.ldt_utils.models import User, Project, Content, Media
+from ldt.ldt_utils.utils import LdtUtils, LdtAnnotation, create_ldt, create_empty_iri, copy_ldt
+from ldt.test.client import Client
+import lxml.etree
+import tempfile
+import unittest
+import uuid
+import logging
+
+class ContentTest(TestCase):
+    
+    fixtures = ['base_data.json', 'user_data.json']
+    
+    def setUp(self):
+        self.client = Client()
+        User.objects.create_superuser('blop', 'blop@blop.com', 'blop')
+        
+        client = self.client.login(username='blop', password='blop')
+        
+        self.user = User()
+        self.user.username = 'blop'
+
+    #test the creation of a content without media
+    def test_create_content_v1(self):
+        self.cont12 = Content(iriurl="id12/iriurl12", duration = 100)
+        self.cont12.iri_id = "id112"
+        self.cont12.save()
+        
+        self.assertEqual(Content.objects.get(iri_id=self.cont12.iri_id), self.cont12)
+        
+    #test the creation of a content with a media
+    def test_create_content_v2(self):
+        self.media13 = Media()
+        self.cont13 = Content(iriurl="id13/iriurl13", duration = 100, media_obj = self.media13)
+        self.cont13 = Content(iriurl="id13/iriurl13", duration = 100)
+        self.cont13.iri_id = "id113"
+        self.cont13.save()
+        
+        self.assertEqual(Content.objects.get(iri_id=self.cont13.iri_id), self.cont13) 
+    
+    #test the deletion of a content without media
+    def test_del_content_v1(self):
+        self.cont14 = Content(iriurl="id14/iriurl14", duration = 100)
+        self.cont14.iri_id = "id114"
+        self.cont14.save()
+
+        self.cont14.delete()
+        self.cont14.commit()
+        
+        with self.assertRaises(Content.DoesNotExist):
+            Content.objects.get(iri_id=self.cont14.iri_id)
+        
+    #test the deletion of a content with media
+    def test_del_content_v2(self):
+        self.media15 = Media()
+        self.cont15 = Content(iriurl="id15/iriurl15", duration = 100, media_obj = self.media15)
+        self.cont15.iri_id = "id115"
+        self.cont15.save()
+        
+        self.cont15.delete()
+        self.cont15.commit()
+    
+        with self.assertRaises(Content.DoesNotExist):
+            Content.objects.get(iri_id=self.cont15.iri_id)
+        
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/tests/ldt_tests.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,165 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from ldt.test.testcases import OAuthWebTestCase, TestCase
+from django.conf import settings
+from django.test import TestCase
+from ldt.ldt_utils.models import User, Project, Content, Media
+from ldt.ldt_utils.utils import LdtUtils, LdtAnnotation, create_ldt, create_empty_iri, copy_ldt
+from ldt.test.client import Client
+import lxml.etree
+import tempfile
+import unittest
+import uuid
+import logging
+
+class UtilsTest(TestCase):
+    
+    fixtures = ['base_data.json', 'user_data.json']
+    
+    def setUp(self):
+        self.client = Client()
+        User.objects.create_superuser('blop', 'blop@blop.com', 'blop')
+        
+        client = self.client.login(username='blop', password='blop')
+        
+        self.user = User()
+        self.user.username = 'blop'
+        
+        self.LU = LdtUtils()
+        self.project = Project(title="titleproj1", owner=self.user)
+        self.project.ldt = '<iri ldtversion="1.0.3" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/"><project id="af3b99e4-b695-11df-bfde-00145ea4a2be" user="admin" title="CA:reponse a TC" abstract=""/> <medias> <media extra="" id="laurentcantet_entrelesmurs" pict="" src="http://amateur.iri.centrepompidou.fr//atelier/static/media/ldt/laurentcantet_entrelesmurs/laurentcantet_entrelesmurs.iri" video="rtmp://media.iri.centrepompidou.fr/ddc_player/video/regardssignes/"/> </medias> <annotations> <content id="laurentcantet_entrelesmurs"> <ensemble id="ens_perso" idProject="fe0d5d4c-2201-11df-8a24-00145ea4a2be" title="Decoupages personnels" author="perso" abstract=""> <decoupage id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" author="perso"> <title>CA: prof et admin</title> <abstract/> <elements> <element id="s_442AAB3A-42DA-F9BF-75E7-D2A0663FD5FF" begin="985690" dur="373222" author="" date="2010/09/02" color="16711680" src=""> <title/> <abstract/> <audio source=""/> <tags/> </element> <element id="s_0050F043-3AD2-0A7C-6699-D2A03A1EBA02" begin="5052858" dur="124407" author="" date="2010/09/02" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> <decoupage id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" author="perso"> <title>TC: prof et admin</title> <abstract/> <elements> <element id="s_880D9D4B-8BC0-BA43-5ECA-04EBA9FC9E59" begin="2426894" dur="141478" author="" date="2010/02/25" color="10053375" src=""> <title>Conseil de classe</title> <abstract/> <audio source=""/> <tags> <tag>Argumentation</tag> </tags> </element> <element id="s_D568A57C-7110-DED2-3165-04EC54387060" begin="5052858" dur="124407" author="" date="2010/02/25" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> </ensemble> </content> </annotations> <displays> <display id="0" title="Init view" idsel="laurentcantet_entrelesmurs" tc="2426424" zoom="0" scroll="0" infoBAB=""> <audio source=""/> <content id="laurentcantet_entrelesmurs"> <decoupage idens="en_2" id="de_PPP" tagsSelect=""/> <decoupage idens="laurentcantet_entrelesmurs" id="c_14A2E638-1936-97DC-E303-2DBA6A82A8B3" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" tagsSelect=""/> </content> </display> </displays> <edits> <editing id="0" tags=""> <title>Bout a bout 1</title> <abstract/> <edit id="edit1" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> <edit id="edit2" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> </editing> </edits> </iri>'
+        self.project.id = "11321"
+        self.project.ldt_id = str(uuid.uuid1())
+        self.project.description = "proj1description"
+        self.project.save()
+        
+        self.projectcopy = Project(title="the2ndproject")
+        self.projectcopy.id = "21"
+    
+    def tearDown(self):
+        self.project.delete()
+        self.projectcopy.delete()
+  
+    def test_generate_ldt(self):
+        self.cont1 = Content(iriurl="cont1_id/iriurl1", duration=123)
+        self.cont1.iri_id = "cont1_id"
+        self.cont1.save()
+        
+        self.cont2 = Content(iriurl="cont2_id/iriurl2", duration=100)
+        self.cont2.iri_id = "cont2_id"
+        self.cont2.save()
+        
+        self.project.contents.add(self.cont1, self.cont2)
+    
+        f = tempfile.TemporaryFile(mode='r+')
+        doc = self.LU.generate_ldt(Content.objects.all())
+        doc.write(f, pretty_print=True)
+        f.seek(0)
+        ldoc = lxml.etree.parse(f)
+        self.assertEqual(ldoc.xpath("/iri/displays/display/content")[0].get("id"), self.cont1.iri_id)
+        self.assertEqual(ldoc.xpath("/iri/medias/media")[0].get("id"), self.cont1.iri_id)
+        
+        self.assertEqual(ldoc.xpath("/iri/displays/display/content")[1].get("id"), self.cont2.iri_id)
+        self.assertEqual(ldoc.xpath("/iri/medias/media")[1].get("id"), self.cont2.iri_id)
+        f.close()
+        
+    """
+    def test_generate_init(self):
+        self.cont3 = Content(iriurl="id3/iriurl3", duration=111)
+        self.cont3.iri_id = "id3"
+        self.cont3.save()
+        
+        self.cont4 = Content(iriurl="id4/iriurl4", duration=111)
+        self.cont4.iri_id = "id4"
+        self.cont4.save()
+        
+        self.project.contents.add(self.cont3, self.cont4)
+        ldoc = self.LU.generate_init([], 'ldt.ldt_utils.views.search_ldt')
+        self.assertEqual(ldoc.xpath("/iri/files/init")[0].tag, "init")
+        self.assertEqual(ldoc.xpath("/iri/files/library")[0].tag, "library")
+    """
+    
+    def test_create_ldt(self):
+        self.cont5 = Content(iriurl="id5/iriurl5", duration=111)
+        self.cont5.iri_id = "id5"
+        self.cont5.save()
+        
+        self.cont6 = Content(iriurl="id6/iriurl6", duration=111)
+        self.cont6.iri_id = "id6"
+        self.cont6.save()
+        
+        self.project.contents.add(self.cont5, self.cont6)
+        self.project.ldt = ""
+        create_ldt(self.project, self.user)
+        ldt = lxml.etree.fromstring(self.project.ldt_encoded)
+        self.assertEqual(ldt.xpath("/iri")[0].tag, "iri")
+        self.assertEqual(ldt.xpath("/iri/project")[0].get("title"), self.project.title)
+        self.assertEqual(ldt.xpath("/iri/medias/media")[0].get("src"), self.cont5.iri_url())
+        self.assertEqual(ldt.xpath("/iri/medias/media")[1].get("id"), self.cont6.iri_id)
+
+    def test_copy_ldt(self):
+        self.cont7 = Content(iriurl="id7/iriurl7", duration=111)
+        self.cont7.iri_id = "id7"
+        self.cont7.save()
+        
+        self.cont8 = Content(iriurl="id8/iriurl8", duration=111)
+        self.cont8.iri_id = "id8"
+        self.cont8.save()
+        
+        self.project.contents.add(self.cont7, self.cont8)
+        copy_ldt(self.project, self.projectcopy, self.user)
+        ldt1 = lxml.etree.fromstring(self.project.ldt_encoded)
+        ldt2 = lxml.etree.fromstring(self.projectcopy.ldt_encoded)
+        self.assertTrue(ldt1.xpath("/iri/project")[0].get("id") != ldt2.xpath("/iri/project")[0].get("id"))
+        self.assertEqual(ldt1.xpath("/iri/medias/media")[0].get("id"), ldt2.xpath("/iri/medias/media")[0].get("id"))
+        self.assertEqual(ldt1.xpath("/iri/annotations/content/ensemble")[0].get("title"), ldt2.xpath("/iri/annotations/content/ensemble")[0].get("title"))
+        self.assertEqual(ldt1.xpath("/iri/annotations/content/ensemble/decoupage")[0].get("id"), ldt2.xpath("/iri/annotations/content/ensemble/decoupage")[0].get("id"))
+        self.assertEqual(ldt1.xpath("/iri/annotations/content/ensemble/decoupage/title")[1].text, ldt2.xpath("/iri/annotations/content/ensemble/decoupage/title")[1].text.strip("\n\t"))
+
+    def test_create_empty_iri(self):
+        self.cont9 = Content(iriurl="id9/iriurl9", duration=111)
+        self.cont9.iri_id = "id9"
+        self.cont9.save()
+        
+        self.cont10 = Content(iriurl="id10/iriurl10", duration=111)
+        self.cont10.iri_id = "id10"
+        self.cont10.save()
+        
+        self.project.contents.add(self.cont9, self.cont10)
+        tmp = tempfile.TemporaryFile(mode='r+')
+        create_empty_iri(tmp, self.cont9, "admin")
+        tmp.seek(0)
+        ldoc = lxml.etree.parse(tmp)
+        self.assertEqual(ldoc.xpath("/iri/head/meta")[0].get("content"), self.cont9.iri_id)
+        self.assertEqual(ldoc.xpath("/iri/body/medias/media/video")[0].get("id"), self.cont9.iri_id)
+        tmp.close()
+   
+    def test_add_annotation(self):
+        
+        self.cont11 = Content(iriurl="id11/iriurl11", duration=111)
+        self.cont11.iri_id = "id11"
+        self.cont11.save()
+        
+        self.project.contents.add(self.cont11)
+        self.project.ldt = ""
+        create_ldt(self.project, self.user)
+        
+        self.LA = LdtAnnotation(self.project)
+
+        self.LA.add("id11", "cutting_id", "cutting_title", "title", "text", ["tag1", "tag2"], "800",
+                    "10000", "jdoe", "2011-09-10T09:12:58")
+        self.LA.save()
+        ldt = lxml.etree.fromstring(self.project.ldt)
+        ann = ldt.xpath('/iri/annotations/content[@id="id11"]/ensemble/decoupage/elements/element')[0]
+        title = ann.xpath('title')[0].text
+        abstract = ann.xpath('abstract')[0].text
+
+        self.assertEqual(ann.get("author"), "jdoe")
+        self.assertEqual(title, "title")
+        self.assertEqual(abstract, "text")
+        
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/tests/media_tests.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,50 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from ldt.test.testcases import OAuthWebTestCase, TestCase
+from django.conf import settings
+from django.test import TestCase
+from ldt.ldt_utils.models import User, Project, Content, Media
+from ldt.ldt_utils.utils import LdtUtils, LdtAnnotation, create_ldt, create_empty_iri, copy_ldt
+from ldt.test.client import Client
+from django.core.exceptions import ObjectDoesNotExist
+import lxml.etree
+import tempfile
+import unittest
+import uuid
+import logging
+
+class MediaTest(TestCase):
+    
+    fixtures = ['base_data.json', 'user_data.json']
+    
+    def setUp(self):
+        self.client = Client()
+        User.objects.create_superuser('blop', 'blop@blop.com', 'blop')
+        
+        client = self.client.login(username='blop', password='blop')
+        
+        self.user = User()
+        self.user.username = 'blop'
+    
+    def test_create_media(self):
+        self.media1, created = Media.objects.get_or_create(src = "http://www.youtube.com/watch?v=O2G-PEtyKSY")
+        self.media1.id = 1
+        self.media1.save()        
+        
+        self.assertEqual(Media.objects.get(id=self.media1.id), self.media1)
+        
+    def test_del_media(self):
+        self.media2 = Media()
+        self.media2.id = 2
+        self.media2.save()
+        
+        self.media2.delete()
+        
+        with self.assertRaises(Media.DoesNotExist):
+            Media.objects.get(id=self.media2.id)
+            
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/tests/project_tests.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,55 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from ldt.test.testcases import OAuthWebTestCase, TestCase
+from django.conf import settings
+from django.test import TestCase
+from ldt.ldt_utils.models import User, Project, Content, Media
+from ldt.ldt_utils.utils import LdtUtils, LdtAnnotation, create_ldt, create_empty_iri, copy_ldt
+from ldt.test.client import Client
+import lxml.etree
+import tempfile
+import unittest
+import uuid
+import logging
+
+class ProjectTest(TestCase):
+    
+    fixtures = ['base_data.json', 'user_data.json']
+    
+    def setUp(self):
+        self.client = Client()
+        User.objects.create_superuser('blop', 'blop@blop.com', 'blop')
+        
+        client = self.client.login(username='blop', password='blop')
+        
+        self.user = User()
+        self.user.username = 'blop'
+        
+    def test_create_project(self):       
+        self.project2 = Project(title="titleproj2", owner=self.user)
+        self.project2.ldt = '<iri ldtversion="1.0.3" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/"><project id="af3b99e4-b695-11df-bfde-00145ea4a212" user="admin" title="CA:reponse a TC" abstract=""/> <medias> <media extra="" id="laurentcantet_entrelesmurs" pict="" src="http://amateur.iri.centrepompidou.fr//atelier/static/media/ldt/laurentcantet_entrelesmurs/laurentcantet_entrelesmurs.iri" video="rtmp://media.iri.centrepompidou.fr/ddc_player/video/regardssignes/"/> </medias> <annotations> <content id="laurentcantet_entrelesmurs"> <ensemble id="ens_perso" idProject="fe0d5d4c-2201-11df-8a24-00145ea4a2be" title="Decoupages personnels" author="perso" abstract=""> <decoupage id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" author="perso"> <title>CA: prof et admin</title> <abstract/> <elements> <element id="s_442AAB3A-42DA-F9BF-75E7-D2A0663FD5FF" begin="985690" dur="373222" author="" date="2010/09/02" color="16711680" src=""> <title/> <abstract/> <audio source=""/> <tags/> </element> <element id="s_0050F043-3AD2-0A7C-6699-D2A03A1EBA02" begin="5052858" dur="124407" author="" date="2010/09/02" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> <decoupage id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" author="perso"> <title>TC: prof et admin</title> <abstract/> <elements> <element id="s_880D9D4B-8BC0-BA43-5ECA-04EBA9FC9E59" begin="2426894" dur="141478" author="" date="2010/02/25" color="10053375" src=""> <title>Conseil de classe</title> <abstract/> <audio source=""/> <tags> <tag>Argumentation</tag> </tags> </element> <element id="s_D568A57C-7110-DED2-3165-04EC54387060" begin="5052858" dur="124407" author="" date="2010/02/25" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> </ensemble> </content> </annotations> <displays> <display id="0" title="Init view" idsel="laurentcantet_entrelesmurs" tc="2426424" zoom="0" scroll="0" infoBAB=""> <audio source=""/> <content id="laurentcantet_entrelesmurs"> <decoupage idens="en_2" id="de_PPP" tagsSelect=""/> <decoupage idens="laurentcantet_entrelesmurs" id="c_14A2E638-1936-97DC-E303-2DBA6A82A8B3" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" tagsSelect=""/> </content> </display> </displays> <edits> <editing id="0" tags=""> <title>Bout a bout 1</title> <abstract/> <edit id="edit1" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> <edit id="edit2" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> </editing> </edits> </iri>'
+        self.project2.ldt_id = str(uuid.uuid1())
+        self.project2.description = "proj2description"
+        self.project2.save()
+        
+        self.assertEqual(Project.objects.get(ldt_id=self.project2.ldt_id), self.project2)
+        
+    #test deletion of project without annotation
+    def test_del_project_v1(self):
+        self.project3 = Project(title="titleproj3", owner=self.user)
+        self.project3.ldt = '<iri ldtversion="1.0.3" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/"><project id="af3b99e4-b695-11df-bfde-00145ea4a333" user="admin" title="CA:reponse a TC" abstract=""/> <medias> <media extra="" id="laurentcantet_entrelesmurs" pict="" src="http://amateur.iri.centrepompidou.fr//atelier/static/media/ldt/laurentcantet_entrelesmurs/laurentcantet_entrelesmurs.iri" video="rtmp://media.iri.centrepompidou.fr/ddc_player/video/regardssignes/"/> </medias> <annotations> <content id="laurentcantet_entrelesmurs"> <ensemble id="ens_perso" idProject="fe0d5d4c-2201-11df-8a24-00145ea4a2be" title="Decoupages personnels" author="perso" abstract=""> <decoupage id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" author="perso"> <title>CA: prof et admin</title> <abstract/> <elements> <element id="s_442AAB3A-42DA-F9BF-75E7-D2A0663FD5FF" begin="985690" dur="373222" author="" date="2010/09/02" color="16711680" src=""> <title/> <abstract/> <audio source=""/> <tags/> </element> <element id="s_0050F043-3AD2-0A7C-6699-D2A03A1EBA02" begin="5052858" dur="124407" author="" date="2010/09/02" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> <decoupage id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" author="perso"> <title>TC: prof et admin</title> <abstract/> <elements> <element id="s_880D9D4B-8BC0-BA43-5ECA-04EBA9FC9E59" begin="2426894" dur="141478" author="" date="2010/02/25" color="10053375" src=""> <title>Conseil de classe</title> <abstract/> <audio source=""/> <tags> <tag>Argumentation</tag> </tags> </element> <element id="s_D568A57C-7110-DED2-3165-04EC54387060" begin="5052858" dur="124407" author="" date="2010/02/25" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> </ensemble> </content> </annotations> <displays> <display id="0" title="Init view" idsel="laurentcantet_entrelesmurs" tc="2426424" zoom="0" scroll="0" infoBAB=""> <audio source=""/> <content id="laurentcantet_entrelesmurs"> <decoupage idens="en_2" id="de_PPP" tagsSelect=""/> <decoupage idens="laurentcantet_entrelesmurs" id="c_14A2E638-1936-97DC-E303-2DBA6A82A8B3" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" tagsSelect=""/> </content> </display> </displays> <edits> <editing id="0" tags=""> <title>Bout a bout 1</title> <abstract/> <edit id="edit1" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> <edit id="edit2" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> </editing> </edits> </iri>'
+        self.project3.id = "333"
+        self.project3.ldt_id = str(uuid.uuid1())
+        self.project3.description = "proj3description"
+        self.project3.save()
+        
+        self.project3.delete()
+
+        with self.assertRaises(Project.DoesNotExist):
+            Project.objects.get(ldt_id=self.project3.ldt_id)
+        
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/ldt_utils/tests/view_tests.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,38 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from ldt.test.testcases import OAuthWebTestCase, TestCase
+from django.conf import settings
+from django.test import TestCase
+from ldt.ldt_utils.models import User, Project, Content, Media
+from ldt.ldt_utils.utils import LdtUtils, LdtAnnotation, create_ldt, create_empty_iri, copy_ldt
+from ldt.test.client import Client
+import lxml.etree
+import tempfile
+import unittest
+import uuid
+import logging
+
+class ViewsTest(unittest.TestCase):
+    def setUp(self):
+        self.project = Project()
+        self.project.id = "121"
+        self.project.save()
+        self.project.ldt = '<iri ldtversion="1.0.3" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/"><project id="af3b99e4-b695-11df-bfde-00145ea4a2be" user="admin" title="CA:reponse a TC" abstract=""/> <medias> <media extra="" id="laurentcantet_entrelesmurs" pict="" src="http://amateur.iri.centrepompidou.fr//atelier/static/media/ldt/laurentcantet_entrelesmurs/laurentcantet_entrelesmurs.iri" video="rtmp://media.iri.centrepompidou.fr/ddc_player/video/regardssignes/"/> </medias> <annotations> <content id="laurentcantet_entrelesmurs"> <ensemble id="ens_perso" idProject="fe0d5d4c-2201-11df-8a24-00145ea4a2be" title="Decoupages personnels" author="perso" abstract=""> <decoupage id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" author="perso"> <title>CA: prof et admin</title> <abstract/> <elements> <element id="s_442AAB3A-42DA-F9BF-75E7-D2A0663FD5FF" begin="985690" dur="373222" author="" date="2010/09/02" color="16711680" src=""> <title/> <abstract/> <audio source=""/> <tags/> </element> <element id="s_0050F043-3AD2-0A7C-6699-D2A03A1EBA02" begin="5052858" dur="124407" author="" date="2010/09/02" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> <decoupage id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" author="perso"> <title>TC: prof et admin</title> <abstract/> <elements> <element id="s_880D9D4B-8BC0-BA43-5ECA-04EBA9FC9E59" begin="2426894" dur="141478" author="" date="2010/02/25" color="10053375" src=""> <title>Conseil de classe</title> <abstract/> <audio source=""/> <tags> <tag>Argumentation</tag> </tags> </element> <element id="s_D568A57C-7110-DED2-3165-04EC54387060" begin="5052858" dur="124407" author="" date="2010/02/25" color="10053375" src=""> <title>conseil de classe</title> <abstract>Reprise de la figure precedente</abstract> <audio source="undefined"/> <tags/> </element> </elements> </decoupage> </ensemble> </content> </annotations> <displays> <display id="0" title="Init view" idsel="laurentcantet_entrelesmurs" tc="2426424" zoom="0" scroll="0" infoBAB=""> <audio source=""/> <content id="laurentcantet_entrelesmurs"> <decoupage idens="en_2" id="de_PPP" tagsSelect=""/> <decoupage idens="laurentcantet_entrelesmurs" id="c_14A2E638-1936-97DC-E303-2DBA6A82A8B3" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EEEF5C29-86E1-4AAE-E068-04EB5B00E492" tagsSelect=""/> <decoupage idens="ens_perso" id="c_EFC3FFE7-0204-A086-EBEC-D2A03A0E56CB" tagsSelect=""/> </content> </display> </displays> <edits> <editing id="0" tags=""> <title>Bout a bout 1</title> <abstract/> <edit id="edit1" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> <edit id="edit2" tags=""> <eList/> <caption/> <audio/> <mList/> </edit> </editing> </edits> </iri>' 
+    
+        self.cont1 = Content(iriurl="/laurentcantet_entrelesmurs/iriurl1")
+        self.cont1.iri_id = 'laurentcantet_entrelesmurs'
+        self.cont1.save()
+    
+        self.cont2 = Content(iriurl="/content_notinldt/iriurl2")
+        self.cont2.iri_id = 'content_notinldt'
+        self.cont2.save()
+        
+        self.project.contents.add(self.cont1, self.cont2)
+
+    def tearDown(self):
+        self.project.delete()
--- a/src/ldt/ldt/ldt_utils/urls.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/urls.py	Mon Nov 12 16:17:22 2012 +0100
@@ -9,7 +9,7 @@
     url(r'^searchInit/(?P<field>.*)/(?P<query>.*)$', 'views.lignesdetemps.search_init'),
     url(r'^searchForm/$', 'views.workspace.search_form'),
     url(r'^published/$', 'views.workspace.published_project'),
-    url(r'^groups/$', 'views.workspace.groups'),
+    url(r'^groups/$', 'views.workspace.groups', name="groups-view"),
     url(r'^get_group_projects/$', 'views.group.get_group_projects'),
     url(r'^search/$', 'views.workspace.search_index'),
     url(r'^search/(?P<field>.*)/(?P<query>.*)$', 'views.lignesdetemps.search_index_get'),
--- a/src/ldt/ldt/ldt_utils/utils.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/utils.py	Mon Nov 12 16:17:22 2012 +0100
@@ -4,9 +4,9 @@
 from StringIO import StringIO
 import datetime
 import django.core.urlresolvers
-import lxml.etree
-import urllib
+import lxml.etree #@UnresolvedImport
 import uuid
+from ldt.utils.url import request_with_auth
 
 __BOOLEAN_DICT = {
     'false':False,
@@ -25,12 +25,12 @@
         node_list = [element_node.text]
     return reduce(lambda t, s: t + s, node_list , "")
 
-def boolean_convert(bool):
-    if bool is None:
+def boolean_convert(bool_val):
+    if bool_val is None:
         return False
-    if bool is True or bool is False:
-        return bool
-    key = str(bool).lower()
+    if bool_val is True or bool_val is False:
+        return bool_val
+    key = str(bool_val).lower()
     return __BOOLEAN_DICT.get(key, False)
 
 def generate_uuid():
@@ -102,7 +102,8 @@
             for content in contentList:
                 contentd = lxml.etree.SubElement(display, "content")
                 contentd.set(u"id", content.iri_id)
-                filepath = urllib.urlopen(content.iri_url())
+                __, content = request_with_auth(content.iri_url())
+                filepath = StringIO(content)
             
                 udoc = lxml.etree.parse(filepath)
                 res = udoc.xpath("/iri/body/ensembles/ensemble/decoupage")
@@ -357,8 +358,8 @@
             url = content.iri_url()
         else:
             url = content.iriurl
-        file = urllib.urlopen(url)
-        doc = lxml.etree.parse(file)
+        __, content = request_with_auth(url)
+        doc = lxml.etree.parse(StringIO(content))
         res = doc.xpath("/iri/body/ensembles/ensemble/decoupage")        
 
         #node decoupage
@@ -441,7 +442,7 @@
     new_project.save()
     return new_project
 
-def create_empty_iri(file, content, username):
+def create_empty_iri(iri_file, content, username):
     
     iri = lxml.etree.Element('iri')
     doc = lxml.etree.ElementTree(iri)
@@ -492,7 +493,7 @@
 
     lxml.etree.SubElement(body, 'display')
 
-    doc.write(file, pretty_print=True)
+    doc.write(iri_file, pretty_print=True)
 
 
 def update_iri(filepath, content, username):
--- a/src/ldt/ldt/ldt_utils/views/content.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/views/content.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,37 +1,158 @@
 from django.conf import settings
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.models import Group
+from django.core.files import File
 from django.core.urlresolvers import reverse
+from django.db import transaction
 from django.forms.models import model_to_dict
-from django.core.files import File
-from django.db import transaction
 from django.forms.util import ErrorList
 from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import render_to_response, redirect
 from django.template import RequestContext
 from django.utils.translation import ugettext as _, ungettext
+from guardian.shortcuts import remove_perm
 from ldt.ldt_utils.forms import ContentForm, MediaForm
-from guardian.shortcuts import remove_perm
 from ldt.ldt_utils.models import Content, Media, Project
-from ldt.security.utils import assign_perm_to_obj, add_change_attr, get_userlist, get_userlist_model
 from ldt.security.cache import cached_assign
+from ldt.security.utils import (assign_perm_to_obj, add_change_attr, get_userlist, 
+    get_userlist_model)
 from ldt.user.forms import PictureForm
 from tagging.models import Tag, TaggedItem
+import datetime
 import ldt.utils.path as ldt_utils_path
 import logging
+import math
 import mimetypes
 import os
-import urllib2
-import subprocess
 import re
-import datetime
-import math
 import requests
-import urlparse  
+import subprocess
+import sys
 import tempfile
+import traceback
+import urllib2
+import urlparse
+#from django.core.files.temp import NamedTemporaryFile
 
+def media_management(request, media_input_type, cleaned_data, content_form, media_form, form_status):
+    if media_input_type == "none":
+                    media = None
+    elif media_input_type == "link":
+        media = content_form.cleaned_data["media_obj"]
+        created = False
+    elif media_input_type == "create":
+        del cleaned_data["media_file"]
+        if not cleaned_data['videopath']:
+            cleaned_data['videopath'] = settings.STREAM_URL
+        # if the source is already http:// or rtmp:// we don't have to add STREAM_URL
+        if cleaned_data['src'].startswith("rtmp://") or cleaned_data['src'].startswith("http://"):
+            cleaned_data['videopath'] = ''
+            
+            media, created = Media.objects.get_or_create(src=cleaned_data['src'], defaults=cleaned_data) #@UndefinedVariable
+    
+    elif media_input_type == "url" or media_input_type == "upload" :
+        # copy file
+        #complet src
+        destination_file = None
+        source_file = None
+        try:
+            if media_input_type == "url":
+                url = cleaned_data["external_src_url"]
+                source_file = urllib2.urlopen(url)
+                source_filename = source_file.info().get('Content-Disposition', None)
+                if not source_filename:
+                    source_filename = urlparse.urlparse(url).path.rstrip("/").split('/')[-1]
+            elif media_input_type == "upload":
+                #source_file = request.FILES['media-media_file']
+                # At this point the file has already be uploaded thanks to the upload view, and original file name is sent through a post var
+                source_filename = request.POST["media-local_file_name"]
+            
+            source_filename = ldt_utils_path.sanitize_filename(source_filename)
+            destination_filepath = os.path.join(settings.STREAM_PATH, source_filename)
+            base_source_filename = source_filename
+            extension = base_source_filename.split(".")[-1]
+            if extension == base_source_filename:
+                extension = ""
+                base_basename_filename = base_source_filename
+            else:
+                base_basename_filename = base_source_filename[:-1 * (len(extension) + 1)]
+            i = 0
+            
+            while os.path.exists(destination_filepath):
+                base_source_filename = "%s.%d.%s" % (base_basename_filename, i, extension)
+                destination_filepath = os.path.join(settings.STREAM_PATH, base_source_filename)
+                i += 1
+            
+            if media_input_type == "url":
+                # we upload the file if we are in url case
+                destination_file = open(destination_filepath, "wb")
+                chunck = source_file.read(2048)
+                while chunck:
+                    destination_file.write(chunck)
+                    chunck = source_file.read(2048)
+                
+            elif media_input_type == "upload":
+                # The media file has been uploaded in the session temp folder 
+                # so we just have to move to the regular folder and rename it.
+                if os.path.exists(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename)):
+                    os.rename(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename), os.path.join(settings.STREAM_PATH, base_source_filename))
+            
+            
+            src_prefix = settings.STREAM_SRC_PREFIX.rstrip("/")
+            if len(src_prefix) > 0:
+                cleaned_data["src"] = src_prefix + "/" + base_source_filename
+            else:
+                cleaned_data["src"] = base_source_filename
+            
+            
+        except Exception as inst:
+            logging.debug("write_content_base : POST error when processing file:" + str(inst)) #@UndefinedVariable
+            form_status = "error"
+            #set error for form
+            if media_input_type == "url":
+                errors = media_form._errors.setdefault("external_src_url", ErrorList())
+                errors.append(_("Problem when downloading file from url : ") + url)
+            elif media_input_type == "upload":
+                errors = media_form._errors.setdefault("media_file", ErrorList())
+                errors.append(_("Problem when uploading file : ") + str(inst))
+        finally:
+            if media_input_type == "url":
+                if destination_file:
+                    destination_file.close()
+                if source_file:
+                    source_file.close()
+        
+        
+        if form_status != "error":
+            del cleaned_data["media_file"]
+            if not cleaned_data['videopath']:
+                cleaned_data['videopath'] = settings.STREAM_URL
+            mimetype = cleaned_data.get('mimetype_field', None)
+            if not mimetype:
+                mimetype = mimetypes.guess_type(cleaned_data['src'])
+            cleaned_data['mimetype_field'] = mimetype
+            media, created = Media.safe_objects.get_or_create(src=cleaned_data['src'], defaults=cleaned_data) #@UndefinedVariable
+            cached_assign('view_media', request.user, media)
+        else:
+            media = None
+            
+
+    if media and not created:
+        for attribute in ('external_id', 'external_permalink', 'external_publication_url', 'external_src_url', 'media_creation_date', 'videopath', 'duration', 'description', 'title', 'front_project'):
+            setattr(media, attribute, cleaned_data.get(attribute))
+        mimetype = cleaned_data.get('mimetype_field', None)
+        if not mimetype:
+            mimetype = mimetypes.guess_type(media.src)
+        media.mimetype_field = mimetype
+        cached_assign('view_media', request.user, media)
+        cached_assign('change_media', request.user, media)
+        media.save()
+        
+    return media, form_status
+    
+    
 @transaction.commit_manually
-def write_content_base(request, iri_id=None): 
+def write_content_base(request, iri_id=None):
     if iri_id:
         instance_content = Content.safe_objects.get(iri_id=iri_id) #@UndefinedVariable
         instance_media = instance_content.media_obj
@@ -46,10 +167,8 @@
     form_status = 'none'
     errors_transaction = []
     
-    # catch error from creating content, project, media
-    try:
-        if request.method == "POST":
-            
+    if request.method == "POST":
+        try:
             if instance_content is not None:
                 content_instance_val = model_to_dict(instance_content, exclude=ContentForm.Meta.exclude)
             else:
@@ -82,11 +201,11 @@
        
             if request.user.is_staff:
                 content_form.fields['front_project'].queryset = Project.objects.filter(contents__in=[instance_content])
-    
             
             media_valid = media_form.is_valid()
             content_valid = content_form.is_valid()
             picture_valid = picture_form.is_valid()
+            
             if 'image' in request.POST.keys():
                 image_link = request.POST.get('url_image')
                 if picture_valid and image_link!='' :
@@ -98,8 +217,8 @@
                             img_temp.flush()
                             picture_form.cleaned_data["image"]=File(img_temp) 
                     except Exception as inst:
-                        logging.debug("couldn't download video thumbnail from image_link : "+str(image_link))
-            logging.debug("write_content_base : valid form: for instance : " + repr(instance_media) + " -> media " + str(media_valid) + " content : for instance : " + repr(instance_content) + " : " + str(content_valid) + "picture : valid :" +str(picture_valid)+" loulou") #@UndefinedVariable
+                        logging.debug("couldn't download video thumbnail from image_link : " + str(image_link))
+            logging.debug("write_content_base : valid form: for instance : " + repr(instance_media) + " -> media " + str(media_valid) + " content : for instance : " + repr(instance_content) + " : " + str(content_valid) + "picture : valid :" +str(picture_valid)) #@UndefinedVariable
             
             if media_valid and content_valid and picture_valid:
     
@@ -110,117 +229,13 @@
                 
                 media_input_type = content_form.cleaned_data["media_input_type"]
                 
-                if media_input_type == "none":
-                    media = None
-                elif media_input_type == "link":
-                    media = content_form.cleaned_data["media_obj"]
-                    created = False
-                elif media_input_type == "create":
-                    del cleaned_data["media_file"]
-                    if not cleaned_data['videopath']:
-                        cleaned_data['videopath'] = settings.STREAM_URL
-                    # if the source is already http:// or rtmp:// we don't have to add STREAM_URL
-                    if cleaned_data['src'].startswith("rtmp://") or cleaned_data['src'].startswith("http://"):
-                        cleaned_data['videopath'] = ''
-                    media, created = Media.objects.get_or_create(src=cleaned_data['src'], defaults=cleaned_data) #@UndefinedVariable
-                elif media_input_type == "url" or media_input_type == "upload" :
-                    # copy file
-                    #complet src
-                    destination_file = None
-                    source_file = None
-                    try:
-                        if media_input_type == "url":
-                            url = cleaned_data["external_src_url"]
-                            source_file = urllib2.urlopen(url)
-                            source_filename = source_file.info().get('Content-Disposition', None)
-                            if not source_filename:
-                                source_filename = urlparse.urlparse(url).path.rstrip("/").split('/')[-1]
-                        elif media_input_type == "upload":
-                            #source_file = request.FILES['media-media_file']
-                            # At this point the file has already be uploaded thanks to the upload view, and original file name is sent through a post var
-                            source_filename = request.POST["media-local_file_name"]
-                        
-                        source_filename = ldt_utils_path.sanitize_filename(source_filename)
-                        destination_filepath = os.path.join(settings.STREAM_PATH, source_filename)
-                        base_source_filename = source_filename
-                        extension = base_source_filename.split(".")[-1]
-                        if extension == base_source_filename:
-                            extension = ""
-                            base_basename_filename = base_source_filename
-                        else:
-                            base_basename_filename = base_source_filename[:-1 * (len(extension) + 1)]
-                        i = 0
-                        
-                        while os.path.exists(destination_filepath):
-                            base_source_filename = "%s.%d.%s" % (base_basename_filename, i, extension)
-                            destination_filepath = os.path.join(settings.STREAM_PATH, base_source_filename)
-                            i += 1
-                        
-                        if media_input_type == "url":
-                            # we upload the file if we are in url case
-                            destination_file = open(destination_filepath, "wb")
-                            chunck = source_file.read(2048)
-                            while chunck:
-                                destination_file.write(chunck)
-                                chunck = source_file.read(2048)
-                            
-                        elif media_input_type == "upload":
-                            # The media file has been uploaded in the session temp folder 
-                            # so we just have to move to the regular folder and rename it.
-                            if os.path.exists(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename)):
-                                os.rename(os.path.join(settings.STREAM_PATH, "tmp/" + request.COOKIES[settings.SESSION_COOKIE_NAME] + "/", source_filename), os.path.join(settings.STREAM_PATH, base_source_filename))
-                        
-                        
-                        src_prefix = settings.STREAM_SRC_PREFIX.rstrip("/")
-                        if len(src_prefix) > 0:
-                            cleaned_data["src"] = src_prefix + "/" + base_source_filename
-                        else:
-                            cleaned_data["src"] = base_source_filename
-                        
-                        
-                    except Exception as inst:
-                        logging.debug("write_content_base : POST error when processing file:" + str(inst)) #@UndefinedVariable
-                        form_status = "error"
-                        #set error for form
-                        if media_input_type == "url":
-                            errors = media_form._errors.setdefault("external_src_url", ErrorList())
-                            errors.append(_("Problem when downloading file from url : ") + url)
-                        elif media_input_type == "upload":
-                            errors = media_form._errors.setdefault("media_file", ErrorList())
-                            errors.append(_("Problem when uploading file : ") + str(inst))
-                    finally:
-                        if media_input_type == "url":
-                            if destination_file:
-                                destination_file.close()
-                            if source_file:
-                                source_file.close()
-                    
-                    
-                    if form_status != "error":
-                        #try:
-                        del cleaned_data["media_file"]
-                        if not cleaned_data['videopath']:
-                            cleaned_data['videopath'] = settings.STREAM_URL
-                        mimetype = cleaned_data.get('mimetype_field', None)
-                        if not mimetype:
-                            mimetype = mimetypes.guess_type(cleaned_data['src'])
-                        cleaned_data['mimetype_field'] = mimetype
-                        media, created = Media.safe_objects.get_or_create(src=cleaned_data['src'], defaults=cleaned_data) #@UndefinedVariable
-                        cached_assign('view_media', request.user, media)
-                    else:
-                        media = None
-                        
-    
-                if media and not created:
-                    for attribute in ('external_id', 'external_permalink', 'external_publication_url', 'external_src_url', 'media_creation_date', 'videopath', 'duration', 'description', 'title', 'front_project'):
-                        setattr(media, attribute, cleaned_data.get(attribute))
-                    mimetype = cleaned_data.get('mimetype_field', None)
-                    if not mimetype:
-                        mimetype = mimetypes.guess_type(media.src)
-                    media.mimetype_field = mimetype
-                    cached_assign('view_media', request.user, media)
-                    cached_assign('change_media', request.user, media)
-                    media.save()
+                
+                
+                
+                media, form_status = media_management(request, media_input_type, cleaned_data, content_form, media_form, form_status)
+                
+                
+                
                 
                 if form_status != "error": 
                     content_defaults = {}
@@ -229,9 +244,9 @@
                                    
                     for key in ["media_input_type", "groups", "is_public", "read_list", "write_list", "share" ]:
                         del content_defaults[key]
-                         
+                        
                     content, created = Content.safe_objects.get_or_create(iri_id=content_form.cleaned_data['iri_id'], defaults=content_defaults) #@UndefinedVariable
-                    
+                                      
                     if not created and not request.user.has_perm('ldt_utils.change_content', content):
                         raise AttributeError("%s is not allowed to change content %s" % (request.user, content))
                     
@@ -245,9 +260,9 @@
                             cached_assign('view_media', everyone, media)
                     else:
                         remove_perm('ldt_utils.view_media', everyone, media)
+                        assign_perm_to_obj(content, content_form.cleaned_data['read_list'], content_form.cleaned_data['write_list'], request.user)
                         if media:
                             assign_perm_to_obj(media, content_form.cleaned_data['read_list'], content_form.cleaned_data['write_list'], request.user)
-                        assign_perm_to_obj(content, content_form.cleaned_data['read_list'], content_form.cleaned_data['write_list'], request.user)
                         if content_form.cleaned_data['is_public']:
                             cached_assign('view_content', everyone, content)
                         else:
@@ -259,7 +274,7 @@
                             
                         if request.user.is_staff and content_defaults.has_key('front_project'):
                             content.front_project = content_defaults['front_project']
-                        
+                    
                     content.save()
                     picture_form.model = content
                     picture_form.save()  
@@ -269,40 +284,42 @@
                     picture_form = PictureForm()
             else:
                 form_status = 'error'
+        except Exception, e:
+            transaction.rollback()
+            __, value, traceback = sys.exc_info()
+            return False, False, False, False, False, errors_transaction, e, traceback
+            
+    else:
+        form_status = 'empty'
+        initial_c = { 'media_input_type':"link"}
+        initial_m = {}
+        if instance_media:
+            initial_m['media_public'] = instance_media.is_public
         else:
-            form_status = 'empty'
-            initial_c = { 'media_input_type':"link"}
-            initial_m = {}
-            if instance_media:
-                initial_m['media_public'] = instance_media.is_public
-            else:
-                initial_m['media_public'] = True
-            if instance_content:
-                initial_c['is_public'] = instance_content.is_public
-            else:
-                initial_c['is_public'] = True
-            content_form = ContentForm(prefix="content", instance=instance_content, initial=initial_c)
-            media_form = MediaForm(prefix="media", instance=instance_media, initial=initial_m) 
-            picture_form = PictureForm() 
-                    
-            if instance_content is not None:
-                content_form.media_input_type = "link"
-                       
-                if request.user.is_staff:
-                    content_form.fields['front_project'].queryset = Project.objects.filter(contents__in=[instance_content])
-    except:
+            initial_m['media_public'] = True
+        if instance_content:
+            initial_c['is_public'] = instance_content.is_public
+        else:
+            initial_c['is_public'] = True
+        content_form = ContentForm(prefix="content", instance=instance_content, initial=initial_c)
+        media_form = MediaForm(prefix="media", instance=instance_media, initial=initial_m) 
+        picture_form = PictureForm() 
+                
+        if instance_content is not None:
+            content_form.media_input_type = "link"
+                   
+            if request.user.is_staff:
+                content_form.fields['front_project'].queryset = Project.objects.filter(contents__in=[instance_content])
+    
+    try:
+        transaction.commit()
+    except Exception, e:
         transaction.rollback()
-        errors_transaction.append(_("Content creation failure"))
-        return False, False, False, False, False, errors_transaction
-    else:
-        #Try to make sure the commit succeeded or not.
-        try:
-            transaction.commit()
-        except:
-            transaction.rollback()
-            errors_transaction.append(_("Commit of the content creation failed"))
-            return False, False, False, False, False, errors_transaction
-    return content_form, media_form, picture_form, form_status, current_front_project, errors_transaction
+        errors_transaction.append(e)
+        type, value, traceback = sys.exc_info()
+        return False, False, False, False, False, errors_transaction, e, traceback
+        
+    return content_form, media_form, picture_form, form_status, current_front_project, errors_transaction, "", ""
 
 @login_required
 def write_content(request, iri_id=None):  
@@ -353,7 +370,7 @@
     elif submit_action=="close":
         return redirect("root-view")
     else:
-        content_form, media_form, picture_form, form_status, current_front_project, errors_transaction = write_content_base(request, iri_id)        
+        content_form, media_form, picture_form, form_status, current_front_project, errors_transaction, e, traceback = write_content_base(request, iri_id)        
         if iri_id:
             content_temp = Content.objects.get(iri_id=iri_id)
             media_temp = content_temp.media_obj
@@ -366,7 +383,8 @@
     if (deleted == False)  or (content_form == False and media_form == False and picture_form == False and form_status == False and current_front_project == False):
         message=_("An error occurred - Please try again or contact webmaster")
         title = _("Error")
-        return render_to_response('ldt/ldt_utils/error_confirm.html', {'errors':errors_transaction, 'message':message, 'title': title}, context_instance=RequestContext(request))
+        logging.error(e)
+        raise e, None, traceback
     
     if iri_id:
         create_content_action = reverse('ldt.ldt_utils.views.content.write_content', kwargs={'iri_id':iri_id})
@@ -396,7 +414,6 @@
 def prepare_delete_content(request, iri_id=None): 
     errors = []
     titles = []
-    delete = False
     message={}
     if not iri_id:
         iri_id = request.REQUEST.get("iri_id", None)
@@ -434,10 +451,11 @@
             content.delete()
             if project is not None:
                 Project.safe_objects.get(ldt_id= project.ldt_id).delete()
-        except:
+        except Exception, e:
             content.rollback()
             transaction.rollback()
             errors_transaction.append(_("Content deletion failure"))
+            errors_transaction.append(e)
             return False, errors_transaction
         else:
             try:
@@ -445,9 +463,10 @@
                 content.commit()
                 return True, errors_transaction
             except:
+                content.rollback()
                 transaction.rollback()
-                content.rollback()
                 errors_transaction.append(_("Commit of the content deletion failed"))
+                errors_transaction.append(e)
                 return False, errors_transaction
 
 def upload(request):
--- a/src/ldt/ldt/ldt_utils/views/group.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/views/group.py	Mon Nov 12 16:17:22 2012 +0100
@@ -2,13 +2,14 @@
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.models import Group
 from django.http import HttpResponseServerError
-from django.shortcuts import render_to_response, get_object_or_404
+from django.shortcuts import render_to_response, get_object_or_404, redirect
 from django.template import RequestContext
 from ldt.ldt_utils.forms import GroupAddForm
 from guardian.shortcuts import remove_perm, get_objects_for_group, get_objects_for_user
 from ldt.security.utils import add_change_attr, get_userlist, get_userlist_group
 from ldt.security.cache import get_cached_checker, cached_assign
 from ldt.user.forms import PictureForm
+import logging
 
 
 @login_required
@@ -41,37 +42,43 @@
     if request.method == 'POST':
         form = GroupAddForm(request.POST)
         picture_form = PictureForm(None, request.POST, request.FILES)
-
-        if form.is_valid():        
-            name = form.cleaned_data['name']
-            members_list = form.cleaned_data['read_list']
-            admin_list = form.cleaned_data['write_list']          
-            
-            group = Group.objects.create(name=name)
-            group.save()
-            group.profile.description = form.cleaned_data['description']
-            group.profile.save()
-            
-            picture_form.model = group
-            if picture_form.is_valid():
-                picture_form.save()
-           
-                cached_assign('is_owner_group', request.user, group)
-                cached_assign('change_group', request.user, group)
-                request.user.groups.add(group)             
-    
-                for elem in members_list:
-                    if hasattr(elem, 'username'):
-                        elem.groups.add(group)
-                        if elem in admin_list:
-                            cached_assign('change_group', elem, group)
-                form_status = 'saved'
-            else:
-                group.delete()
-            
+        submit_action = request.REQUEST.get("submit_button", False)
+        
+        if submit_action == 'close':
+            return redirect("groups-view")
+        else:    
+            if form.is_valid():        
+                name = form.cleaned_data['name']
+                members_list = form.cleaned_data['read_list']
+                admin_list = form.cleaned_data['write_list']          
+                
+                group = Group.objects.create(name=name)
+                group.save()
+                group.profile.description = form.cleaned_data['description']
+                group.profile.save()
+                
+                picture_form.model = group
+                if picture_form.is_valid():
+                    picture_form.save()
+               
+                    cached_assign('is_owner_group', request.user, group)
+                    cached_assign('change_group', request.user, group)
+                    request.user.groups.add(group)             
+        
+                    for elem in members_list:
+                        if hasattr(elem, 'username'):
+                            elem.groups.add(group)
+                            if elem in admin_list:
+                                cached_assign('change_group', elem, group)
+                    form_status = 'saved'
+                else:
+                    group.delete()
     else:
         form = GroupAddForm()
         picture_form = PictureForm()
+    
+    if form_status == 'saved':
+        return redirect("groups-view")
         
     return render_to_response("ldt/ldt_utils/create_group.html", {'group_edition': True, 'form' : form, 'form_status' : form_status, 
                                                                   'elem_list' : get_userlist(request.user), 'profile_picture_form': picture_form}, context_instance=RequestContext(request))
@@ -97,6 +104,8 @@
             if is_owner_group:
                 group.delete()
                 form_status = 'deleted'
+        elif submit_action == 'close':
+            return redirect("groups-view")
         else:   
             if form.is_valid() and picture_form.is_valid():
                 name = form.cleaned_data['name']
@@ -130,12 +139,11 @@
         form = GroupAddForm(initial={'name':unicode(group.name)}) 
         picture_form = PictureForm()   
 
-    if form_status != 'deleted':
+    if form_status == 'deleted' or form_status == 'saved':
+        return redirect("groups-view")
+    else:
         member_list, admin_list = get_userlist_group(group, request.user)
         profile_image = group.profile
-    else:    
-        member_list = admin_list = []
-        profile_image = ''
         
     return render_to_response("ldt/ldt_utils/create_group.html", {'group_id' : group_id, 'form' : form, 'form_status' : form_status, 'group_edition': True,
                                                                   'elem_list' : get_userlist(request.user), 'member_list': member_list, 'admin_list': admin_list,
--- a/src/ldt/ldt/ldt_utils/views/json.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/views/json.py	Mon Nov 12 16:17:22 2012 +0100
@@ -5,9 +5,8 @@
 from django.utils import simplejson
 from django.utils.html import escape
 from django.utils.translation import ugettext as _
-from ldt.indexation import get_results_with_context
 from ldt.ldt_utils.models import Project
-from ldt.ldt_utils.projectserializer import ProjectSerializer
+from ldt.ldt_utils.projectserializer import ProjectJsonSerializer
 from ldt.ldt_utils.searchutils import search_generate_ldt
 from datetime import datetime
 import ldt.auth as ldt_auth
@@ -63,7 +62,7 @@
         escape_bool = {'true': True, 'false': False, "0": False, "1": True}.get(escape_str.lower())
         
         
-    ps = ProjectSerializer(project, serialize_contents, first_cutting=first_cutting)
+    ps = ProjectJsonSerializer(project, serialize_contents, first_cutting=first_cutting)
     project_dict = ps.serialize_to_cinelab()
     
     json_str = simplejson.dumps(project_dict, ensure_ascii=False, indent=indent)
@@ -104,7 +103,7 @@
         now = datetime.now()
         project.modification_date = project.creation_date = now
         #return HttpResponse(lxml.etree.tostring(project_xml, pretty_print=True), mimetype="text/xml;charset=utf-8")
-        ps = ProjectSerializer(project, from_contents=False)
+        ps = ProjectJsonSerializer(project, from_contents=False)
         mashup_dict = ps.serialize_to_cinelab()
         # Now we build the mashup with the good segments (the ones between in and out)
         if results:
@@ -133,8 +132,8 @@
                 cur_in = float(res["start_ts"])
                 cur_out = cur_in + float(res["duration"])
                 if tc_in<=cur_in and cur_out<=tc_out:
-                     #filtered_results.append(res)
-                     mashup_list["items"].append(res["element_id"])
+                    #filtered_results.append(res)
+                    mashup_list["items"].append(res["element_id"])
             mashup_dict["lists"].append(mashup_list)
     
     #mashup_dict["escape_bool"] = escape_bool
--- a/src/ldt/ldt/ldt_utils/views/rdf.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/views/rdf.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,7 +1,7 @@
 from django.http import HttpResponse, HttpResponseForbidden
 from django.utils.translation import ugettext as _
 from ldt.ldt_utils.models import Project
-from ldt.ldt_utils.projectserializer import ProjectSerializer
+from ldt.ldt_utils.projectserializer import ProjectJsonSerializer
 import ldt.auth as ldt_auth
 import logging
 import lxml.etree
@@ -25,7 +25,7 @@
     resp['Cache-Control'] = 'no-cache, must-revalidate'
     resp['Pragma'] = 'no-cache'
 
-    ps = ProjectSerializer(project, from_contents=False, from_display=True) 
+    ps = ProjectJsonSerializer(project, from_contents=False, from_display=True) 
     annotations = ps.get_annotations(first_cutting=True)
     
     rdf_ns = u"http://www.w3.org/1999/02/22-rdf-syntax-ns#"
--- a/src/ldt/ldt/ldt_utils/views/workspace.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/ldt_utils/views/workspace.py	Mon Nov 12 16:17:22 2012 +0100
@@ -13,7 +13,7 @@
 from ldt.ldt_utils.forms import SearchForm
 from ldt.ldt_utils.models import Content, Project, Segment
 from ldt.ldt_utils.utils import boolean_convert
-from ldt.ldt_utils.projectserializer import ProjectSerializer
+from ldt.ldt_utils.projectserializer import ProjectJsonSerializer
 from ldt.ldt_utils.views.content import get_contents_page, get_content_tags
 from ldt.ldt_utils.views.project import get_projects_page, get_published_projects_page
 from ldt.security.utils import add_change_attr, get_userlist
@@ -23,7 +23,6 @@
 import django.core.urlresolvers
 import ldt.auth as ldt_auth
 from django.utils.safestring import mark_safe
-import logging 
 
 
 
@@ -40,13 +39,6 @@
 
     is_gecko = ((request.META['HTTP_USER_AGENT'].lower().find("firefox")) > -1);
     
-    # Resolve a URL
-    #project_api_view, project_api_args, project_api_kwargs = resolve('/api/ldt/projects/c8448f21-272d-11e1-876b-c8bcc896c290.json')
-    #match = resolve('/api/ldt/projects/c8448f21-272d-11e1-876b-c8bcc896c290.json')
-    
-    # Print the URL pattern that matches the URL
-    #print match.url_name
-    
     # render list
     return render_to_response("ldt/ldt_utils/workspace.html",
                               {'contents': content_list, 'nb_ct_pages': nb_ct_pages, 'content_nb': content_nb, 'current_content_page':float(num_page),
@@ -107,6 +99,7 @@
     embed_rendered = dict((typestr,
                            (lambda s:escape(render_to_string("ldt/ldt_utils/partial/embed_%s.html" % (s), rend_dict, context_instance=RequestContext(request))))(typestr))
                            for typestr in ('seo_body', 'seo_meta', 'links'))
+
     rend_dict['embed_rendered'] = embed_rendered
 
     return render_to_response("ldt/ldt_utils/embed_popup.html", rend_dict, context_instance=RequestContext(request))
@@ -206,7 +199,7 @@
     WEB_URL=settings.WEB_URL
     LDT_MEDIA_PREFIX=settings.LDT_MEDIA_PREFIX
     LANGUAGE_CODE=settings.LANGUAGE_CODE
-    ps = ProjectSerializer(project, from_contents=True, from_display=True)
+    ps = ProjectJsonSerializer(project, from_contents=True, from_display=True)
     annotations = ps.get_annotations(first_cutting=True)
     rend_dict = {'json_url':json_url, 'player_id':player_id, 'annotations':annotations, 'ldt_id': ldt_id, 'stream_mode': stream_mode, 
                 'external_url': external_url,
@@ -296,19 +289,21 @@
                 
         valid_segments = []
         for s in all_related_segments:
-            segment = [seg for seg in all_segments if seg.element_id == s['element_id'] and seg.project_id == s['project_id'] and seg.iri_id == s['iri_id'] and seg.cutting_id == s['cutting_id'] and seg.ensemble_id == s['ensemble_id'] ][0]
-                
-            segment.score = s['score']
-            segment.indexation_id = s['indexation_id']
-            segment.context = s['context']
-            segment.context_tags = s['tags']
-            segment.highlighted = s['highlighted']
-                                    
-            if not s['project_id']:
-                segment.project_id = '_'
-                valid_segments.append(segment)
-            elif s['project_id'] in viewable_projects_id:
-                valid_segments.append(segment)
+            array_of_segs = [seg for seg in all_segments if seg.element_id == s['element_id'] and seg.project_id == s['project_id'] and seg.iri_id == s['iri_id'] and seg.cutting_id == s['cutting_id'] and seg.ensemble_id == s['ensemble_id'] ]
+            if len(array_of_segs)>0:
+                segment = array_of_segs[0]
+                    
+                segment.score = s['score']
+                segment.indexation_id = s['indexation_id']
+                segment.context = s['context']
+                segment.context_tags = s['tags']
+                segment.highlighted = s['highlighted']
+                                        
+                if not s['project_id']:
+                    segment.project_id = '_'
+                    valid_segments.append(segment)
+                elif s['project_id'] in viewable_projects_id:
+                    valid_segments.append(segment)
             
         # If all segments found belong to unpublished projects or projects
         # the current user is not allowed to see
Binary file src/ldt/ldt/locale/fr/LC_MESSAGES/django.mo has changed
--- a/src/ldt/ldt/locale/fr/LC_MESSAGES/django.po	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/locale/fr/LC_MESSAGES/django.po	Mon Nov 12 16:17:22 2012 +0100
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-11-12 14:54+0100\n"
+"POT-Creation-Date: 2012-11-12 16:10+0100\n"
 "PO-Revision-Date: 2010-03-09 15:52+0100\n"
 "Last-Translator: Yves-Marie Haussonne <ymh.work@gmail.com>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -37,7 +37,7 @@
 msgid "all"
 msgstr "tous"
 
-#: .\ldt_utils\forms.py:33 .\ldt_utils\models.py:55
+#: .\ldt_utils\forms.py:33 .\ldt_utils\models.py:56
 #: .\ldt_utils\templates\ldt\ldt_utils\content_list.html.py:69
 msgid "title"
 msgstr "titre"
@@ -98,51 +98,51 @@
 msgid "content.duration"
 msgstr "Durée"
 
-#: .\ldt_utils\models.py:44
+#: .\ldt_utils\models.py:45
 msgid "media.external_id"
 msgstr "id externe"
 
-#: .\ldt_utils\models.py:45
+#: .\ldt_utils\models.py:46
 msgid "media.external_permalink"
 msgstr "permalien externe"
 
-#: .\ldt_utils\models.py:46
+#: .\ldt_utils\models.py:47
 msgid "media.external_publication_url"
 msgstr "url de publication externe"
 
-#: .\ldt_utils\models.py:47
+#: .\ldt_utils\models.py:48
 msgid "media.external_src_url"
 msgstr "url source"
 
-#: .\ldt_utils\models.py:48
+#: .\ldt_utils\models.py:49
 msgid "media.creation_date"
 msgstr "Date de création"
 
-#: .\ldt_utils\models.py:49
+#: .\ldt_utils\models.py:50
 msgid "media.media_creation_date"
 msgstr "Date de création du média"
 
-#: .\ldt_utils\models.py:50
+#: .\ldt_utils\models.py:51
 msgid "media.update_date"
 msgstr "Date de maj"
 
-#: .\ldt_utils\models.py:51
+#: .\ldt_utils\models.py:52
 msgid "media.videopath"
 msgstr "videopath"
 
-#: .\ldt_utils\models.py:52
+#: .\ldt_utils\models.py:53
 msgid "media.duration"
 msgstr "Durée du contenu (ms)"
 
-#: .\ldt_utils\models.py:53
+#: .\ldt_utils\models.py:54
 msgid "media.creator"
 msgstr "Créateur"
 
-#: .\ldt_utils\models.py:54
+#: .\ldt_utils\models.py:55
 msgid "description"
 msgstr "description"
 
-#: .\ldt_utils\models.py:56
+#: .\ldt_utils\models.py:57
 msgid "media.src"
 msgstr "Sources"
 
@@ -182,35 +182,35 @@
 msgid "content.authors"
 msgstr "Auteurs"
 
-#: .\ldt_utils\models.py:523
+#: .\ldt_utils\models.py:526
 msgid "content_stat.content"
 msgstr "statistiques d'annotation"
 
-#: .\ldt_utils\models.py:524
+#: .\ldt_utils\models.py:527
 msgid "content_stat.annotations_volume"
 msgstr "Volume d'annotations"
 
-#: .\ldt_utils\models.py:525
+#: .\ldt_utils\models.py:528
 msgid "content_stat.polemics_volume"
 msgstr "Volume d'annotations"
 
-#: .\ldt_utils\models.py:526
+#: .\ldt_utils\models.py:529
 msgid "content.nb_annotation"
 msgstr "nombre d'annotations"
 
-#: .\ldt_utils\models.py:527
+#: .\ldt_utils\models.py:530
 msgid "content.last_annotated"
 msgstr "annoté pour la dernière foiss"
 
-#: .\ldt_utils\models.py:582
+#: .\ldt_utils\models.py:585
 msgid "created by"
 msgstr "créé par"
 
-#: .\ldt_utils\models.py:583
+#: .\ldt_utils\models.py:586
 msgid "changed by"
 msgstr "modifié par"
 
-#: .\ldt_utils\utils.py:203 .\ldt_utils\utils.py:386
+#: .\ldt_utils\utils.py:204 .\ldt_utils\utils.py:387
 msgid "Personal cutting"
 msgstr "Découpages personnels"
 
@@ -408,13 +408,13 @@
 msgstr "Toutes les annotations sur le média"
 
 #: .\ldt_utils\templates\front\front_search_results.html.py:6
-#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:101
-#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:113
+#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:100
+#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:112
 #: .\ldt_utils\templates\ldt\ldt_utils\ldt_list.html.py:80
 #: .\ldt_utils\templates\ldt\ldt_utils\published_projects.html.py:70
 #: .\ldt_utils\templates\ldt\ldt_utils\workspace_base.html.py:56
 #: .\ldt_utils\templates\ldt\ldt_utils\workspace_base.html.py:71
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:129
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:131
 #: .\templates\ldt\ldt_base.html.py:127
 msgid "search"
 msgstr "Recherche"
@@ -557,7 +557,7 @@
 msgstr "Le fichier média est en cours de traitement. Veuillez patienter."
 
 #: .\ldt_utils\templates\ldt\ldt_utils\create_content.html.py:145
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:101
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:77
 #: .\ldt_utils\templates\ldt\ldt_utils\create_ldt.html.py:145
 #: .\ldt_utils\templates\ldt\ldt_utils\error_confirm.html.py:37
 #: .\ldt_utils\templates\ldt\ldt_utils\error_confirm_popup.html.py:53
@@ -574,36 +574,36 @@
 msgid "write"
 msgstr "Enregistrer"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:57
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:36
 msgid "Do you really want to delete this group ?"
 msgstr "Voulez-vous quitter ce groupe ?"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:78
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:54
 msgid "Update a group"
 msgstr "Mettre à jour votre groupe"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:78
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:54
 msgid "Create a group"
 msgstr "Créer un groupe"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:85
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:61
 #: .\user\templates\ldt\user\change_profile.html.py:62
 msgid "Name"
 msgstr "Nom"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:91
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:67
 msgid "Description"
 msgstr "Description"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:103
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:79
 msgid "update_group"
 msgstr "Mettre à jour le groupe"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:105
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:81
 msgid "delete_group"
 msgstr "Effacer le groupe"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:108
+#: .\ldt_utils\templates\ldt\ldt_utils\create_group.html.py:84
 msgid "create_group"
 msgstr "Créer un nouveau groupe"
 
@@ -716,8 +716,8 @@
 msgstr "Configuration avec widget polemic"
 
 #: .\ldt_utils\templates\ldt\ldt_utils\embed_popup.html.py:93
-msgid "Polemic configuration with sparkline and tagcloud"
-msgstr "Configuration avec widget polemic, sparkline et tagcloud"
+msgid "Polemic configuration with tagcloud, annotationsList and Social widgets"
+msgstr "Configuration avec widget polemic, annotationsList, social et tagcloud"
 
 #: .\ldt_utils\templates\ldt\ldt_utils\embed_popup.html.py:100
 msgid "Create your own configuration"
@@ -820,7 +820,7 @@
 msgstr "Partager par email"
 
 #: .\ldt_utils\templates\ldt\ldt_utils\embed_popup.html.py:195
-msgid " Displays information relative to a single segment/annotation while it is being played"
+msgid "Displays information relative to a single segment/annotation while it is being played"
 msgstr "Affiche les informations relatives à une annotation au moment où celle-ci est jouée"
 
 #: .\ldt_utils\templates\ldt\ldt_utils\embed_popup.html.py:198
@@ -854,20 +854,20 @@
 msgid "close_error"
 msgstr "Fermer"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:68
+#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:67
 msgid "Do you want to leave this group ?"
 msgstr "Voulez-vous quitter ce groupe ?"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:93
+#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:92
 #: .\templates\ldt\ldt_base.html.py:115
 msgid "My groups"
 msgstr "Groupes"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:95
+#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:94
 msgid "Create group"
 msgstr "Créer un nouveau groupe"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:111
+#: .\ldt_utils\templates\ldt\ldt_utils\groups.html.py:110
 msgid "The group's projects"
 msgstr "projets du groupe"
 
@@ -962,12 +962,12 @@
 
 #: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:52
 #: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:79
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:174
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:178
 msgid "share.eye"
 msgstr "Cet utilisateur ou ce groupe a le droit de voir cet élement"
 
 #: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:60
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:172
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:176
 msgid "share.pencil"
 msgstr "cet utilisateur ou ce groupe a le droit de modifier cet élement"
 
@@ -979,41 +979,41 @@
 msgid "publish for everyone"
 msgstr "publier pour tout le monde"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:124
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:125
 msgid "publish the title on the front"
 msgstr "publier le titre sur le front"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:127
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:129
 msgid "User and group list"
 msgstr "Liste des groupes et des utilisateurs"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:142
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:144
 msgid "select all displayed elements"
 msgstr "ajouter tous les élements affichés"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:147
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:149
 msgid "select users"
 msgstr "choisir des utilisateurs"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:148
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:150
 msgid "remove users"
 msgstr "enlever des utilisateurs"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:153
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:157
 msgid "Members list"
 msgstr "liste des membres"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:163
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:167
 #: .\ldt_utils\templates\ldt\ldt_utils\partial\sharewith.html.py:7
 msgid "user"
 msgstr "utilisateur"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:165
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:169
 #: .\ldt_utils\templates\ldt\ldt_utils\partial\sharewith.html.py:9
 msgid "group"
 msgstr "groupe"
 
-#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:185
+#: .\ldt_utils\templates\ldt\ldt_utils\partial\permissions.html.py:189
 msgid "remove all"
 msgstr "tout enlever"
 
@@ -1063,71 +1063,63 @@
 msgid "Project published"
 msgstr "Projet publié"
 
-#: .\ldt_utils\views\content.py:187
+#: .\ldt_utils\views\content.py:114
 msgid "Problem when downloading file from url : "
 msgstr "Problème lors du téléchargement du fichier : "
 
-#: .\ldt_utils\views\content.py:190
+#: .\ldt_utils\views\content.py:117
 msgid "Problem when uploading file : "
 msgstr "Problème lors de l'upload du fichier : "
 
-#: .\ldt_utils\views\content.py:295
-msgid "Content creation failure"
-msgstr "Echec lors de la creation du contenu"
-
-#: .\ldt_utils\views\content.py:303
-msgid "Commit of the content creation failed"
-msgstr "Echec lors du commit de la creation du contenu"
-
-#: .\ldt_utils\views\content.py:317
+#: .\ldt_utils\views\content.py:334
 #, python-format
 msgid "There is %(count)d error when deleting content"
 msgid_plural "There are %(count)d errors when deleting content"
 msgstr[0] "Il y a %(count)d erreur lors de l'effacement du contenu"
 msgstr[1] "Il y a %(count)d erreurs lors de l'effacement du contenu"
 
-#: .\ldt_utils\views\content.py:318
+#: .\ldt_utils\views\content.py:335
 msgid "title error deleting content"
 msgstr "Erreur lors de l'effacement du contenu"
 
-#: .\ldt_utils\views\content.py:323
+#: .\ldt_utils\views\content.py:340
 #, python-format
 msgid "Confirm delete content %(titles)s"
 msgstr "Veuillez confirmer l'effacement du contenu %(titles)s"
 
-#: .\ldt_utils\views\content.py:324
+#: .\ldt_utils\views\content.py:341
 msgid "confirm delete content"
 msgstr "Confirmation effacement contenu"
 
-#: .\ldt_utils\views\content.py:335
+#: .\ldt_utils\views\content.py:352
 #, python-format
 msgid "Please unpublish the front project %(title)s"
 msgstr "Veuillez dépublier le projet : %(title)s"
 
-#: .\ldt_utils\views\content.py:336
+#: .\ldt_utils\views\content.py:353
 msgid "The front project is published"
 msgstr "Projet publié"
 
-#: .\ldt_utils\views\content.py:337 .\ldt_utils\views\content.py:340
+#: .\ldt_utils\views\content.py:354 .\ldt_utils\views\content.py:357
 #: .\ldt_utils\views\project.py:137
 msgid "confirm reset"
 msgstr "Confirmer la réinitialisation"
 
-#: .\ldt_utils\views\content.py:339 .\ldt_utils\views\project.py:136
+#: .\ldt_utils\views\content.py:356 .\ldt_utils\views\project.py:136
 #, python-format
 msgid "please confirm reseting project %(title)s"
 msgstr "Veuillez confirmer la réinitialisation du projet %(title)s"
 
-#: .\ldt_utils\views\content.py:367
+#: .\ldt_utils\views\content.py:384
 msgid "An error occurred - Please try again or contact webmaster"
 msgstr ""
 "Une erreur est apparue - Merci de réessayer ou de contacter le webmaster"
 
-#: .\ldt_utils\views\content.py:368
+#: .\ldt_utils\views\content.py:385
 msgid "Error"
 msgstr "Erreur"
 
-#: .\ldt_utils\views\content.py:411
+#: .\ldt_utils\views\content.py:428
 #, python-format
 msgid ""
 "Content '%(title)s' is referenced by this project : %(project_titles)s. "
@@ -1142,7 +1134,7 @@
 "Le contenu '%(title)s' est référencé par les projets suivants : '%"
 "(project_titles)s'.Veuillez les effacer préalablement."
 
-#: .\ldt_utils\views\content.py:414
+#: .\ldt_utils\views\content.py:431
 #, python-format
 msgid ""
 "The project '%(project_title)s' pointing on the content '%(title)s' has "
@@ -1152,16 +1144,16 @@
 "Le projet '%(project_title)s' référençant le contenu '%(title)s' comporte "
 "plusieurs annotations. Voulez vous quand même supprimer le contenu ?"
 
-#: .\ldt_utils\views\content.py:440
+#: .\ldt_utils\views\content.py:457
 msgid "Content deletion failure"
 msgstr "Echec lors de la suppression du contenu"
 
-#: .\ldt_utils\views\content.py:450
+#: .\ldt_utils\views\content.py:468
 msgid "Commit of the content deletion failed"
 msgstr "Echec lors du commit de la suppression du contenu"
 
-#: .\ldt_utils\views\json.py:40 .\ldt_utils\views\rdf.py:15
-#: .\ldt_utils\views\workspace.py:205
+#: .\ldt_utils\views\json.py:39 .\ldt_utils\views\rdf.py:15
+#: .\ldt_utils\views\workspace.py:198
 msgid "You can not access this project"
 msgstr "vous n'avez pas l'autorisation d'accéder à ce projet"
 
@@ -1197,12 +1189,12 @@
 msgid "confirm deletion"
 msgstr "Confirmation d'effacement"
 
-#: .\ldt_utils\views\workspace.py:123
+#: .\ldt_utils\views\workspace.py:116
 msgid ""
 "The content does not exists or you are not allowed to access this content"
 msgstr "Ce contenu n'existe pas, ou vous n'êtes pas autorisé a y acceder"
 
-#: .\ldt_utils\views\workspace.py:127
+#: .\ldt_utils\views\workspace.py:120
 msgid "Parameters project_id or content_id must be given in the url"
 msgstr "Les paramètres project_id et content_id doivent être passés dans l'URL"
 
@@ -1854,6 +1846,12 @@
 "Nous vous avons envoyé par courriel les instructions pour activer le compte "
 "à l'adresse que vous avez indiquée. Vous devriez le recevoir rapidement."
 
+#~ msgid "Content creation failure"
+#~ msgstr "Echec lors de la creation du contenu"
+
+#~ msgid "Commit of the content creation failed"
+#~ msgstr "Echec lors du commit de la creation du contenu"
+
 #~ msgid "Error 500"
 #~ msgstr "Erreur 500"
 
--- a/src/ldt/ldt/settings.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/settings.py	Mon Nov 12 16:17:22 2012 +0100
@@ -54,6 +54,7 @@
 
 
 WEB_URL = getattr(settings, 'WEB_URL', '')
+WEB_AUTH = getattr(settings, 'WEB_AUTH', [])
 BASE_URL = getattr(settings, 'BASE_URL', '')
 STATIC_URL = getattr(settings, 'STATIC_URL', '')
 MEDIA_URL = getattr(settings, 'MEDIA_URL', '')
@@ -71,6 +72,8 @@
 LOG_LEVEL = getattr(settings, 'LOG_LEVEL', logging.INFO)
 EMPTY_MEDIA_EXTERNALID = getattr(settings, 'EMPTY_MEDIA_EXTERNALID', None)
 
+ADMIN_MEDIA_PREFIX = getattr(settings, 'ADMIN_MEDIA_PREFIX', None)
+
 TEST_WEBSERVER_ADDRPORT = getattr(settings, 'TEST_WEBSERVER_ADDRPORT', '127.0.0.1:8000')
 
 ACCOUNT_ACTIVATION_DAYS = getattr(settings, 'ACCOUNT_ACTIVATION_DAYS', 7)
@@ -106,3 +109,4 @@
         'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
     },
 }
+
--- a/src/ldt/ldt/static/ldt/js/embed_popup.js	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/static/ldt/js/embed_popup.js	Mon Nov 12 16:17:22 2012 +0100
@@ -210,15 +210,15 @@
 	var player_height_val=$j('#player_height').val();
 	widget_code='\
 	widgets: [\n\
-		{\n';
+		{\n\
+			type: "AutoPlayer",\n';
+	
 	if (external_url!='None'){
 		widget_code+='\
-			type: "AutoPlayer",\n\
 			video: "'+external_url+'",\n';
 	}
 	else{	
 		widget_code+='\
-			type: "JwpPlayer",\n\
 	        streamer: function(_url) {\n\
 	        var _matches = _url.match(/^[^\\/]+\\/\\/[^\\/]+\\/[^\\/]+\\//);\n\
 	        if (_matches) {\n\
@@ -226,9 +226,7 @@
 	        } else {\n\
 	                return _url;\n\
 	        }\n\
-	        },\n\
-	        live: true,\n\
-	        provider: "rtmp",\n';
+	        },\n';
 	}
 	
 	widget_code+='\
--- a/src/ldt/ldt/static/ldt/js/projectscontents.js	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/static/ldt/js/projectscontents.js	Mon Nov 12 16:17:22 2012 +0100
@@ -332,35 +332,6 @@
     $('.create_group',base_node).each(function(i){
         $(this).attr("target","_iri");
     });
-
-    $('.create_group',base_node).nyroModal({
-        filters: ['iriIframe'],
-        sizes: {
-            minW: '750',
-            minH: '750'
-        },
-        showCloseButton: true,
-        closeOnEscape:false,
-        closeOnClick:false,
-        callbacks: {
-            afterShowCont: function(nm) {
-                var iframe = nm.store.iframe;
-                iframe.load(function(){
-                    var form_status = $(this).contents().find("#project_form_status").val(); 
-                    if(form_status === 'saved' || form_status === 'deleted' ) {
-                        $.nmTop().close();
-                    }
-                        
-                });  
-                nm.store.iframe.width(740);
-                nm.store.iframe.height(740);
-            },
-            afterClose: function(nm) {
-            	searchCallback($("#searchprojectsinput"), $("#groupslistcontainer"), groupfilterurl, 0);
-            }
-        }
-    });
-    
     
     $('.create_group',base_node).each(function(i, e) {
         nm = $(e).data('nmObj');
--- a/src/ldt/ldt/static/ldt/metadataplayer/AutoPlayer.js	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/static/ldt/metadataplayer/AutoPlayer.js	Mon Nov 12 16:17:22 2012 +0100
@@ -18,6 +18,14 @@
         _opts = {},
         _types = [
             {
+                regexp: /^rtmp:\/\//,
+                type: "JwpPlayer"
+            },
+            {
+                regexp: /\.(mp4|m4v)$/,
+                type: "AdaptivePlayer"
+            },
+            {
                 regexp: /\.(ogg|ogv|webm)$/,
                 type: "PopcornPlayer"
             },
@@ -33,11 +41,13 @@
                 regexp: /^(https?:\/\/)?(www\.)?dailymotion\.com/,
                 type: "DailymotionPlayer"
             }
-        ];
+        ],
+        _rtmprgx = /^rtmp:\/\//;
     
     for (var i = 0; i < _types.length; i++) {
         if (_types[i].regexp.test(this.video)) {
-            _opts.type =  _types[i].type
+            _opts.type =  _types[i].type;
+            break;
         }
     }
     
@@ -45,12 +55,21 @@
         _opts.type = this.default_type
     }
     
+    if (_opts.type === "AdaptivePlayer") {
+        var _canPlayType = document.createElement('video').canPlayType("video/mp4");
+        _opts.type = (_canPlayType == "maybe" || _canPlayType == "probably") ? "PopcornPlayer" : "JwpPlayer";
+    }
+    
+    if (_rtmprgx.test(this.video)) {
+        _opts.provider = "rtmp";
+        _opts.live = true;
+    }
+    
     for (var i = 0; i < _props.length; i++) {
         if (typeof this[_props[i]] !== "undefined") {
             _opts[_props[i]] = this[_props[i]];
         }
     }
-    
 
     this.insertSubwidget(this.$, _opts);
     
--- a/src/ldt/ldt/static/ldt/metadataplayer/CreateAnnotation.js	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/static/ldt/metadataplayer/CreateAnnotation.js	Mon Nov 12 16:17:22 2012 +0100
@@ -43,7 +43,7 @@
     annotation_type: "Contributions",
     api_serializer: "ldt_annotate",
     api_endpoint_template: "",
-    api_method: "PUT",
+    api_method: "POST",
     after_send_timeout: 0,
     close_after_send: false,
 }
--- a/src/ldt/ldt/static/ldt/metadataplayer/LdtPlayer-core.js	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/static/ldt/metadataplayer/LdtPlayer-core.js	Mon Nov 12 16:17:22 2012 +0100
@@ -235,33 +235,30 @@
 /* TODO: Separate Project-specific data from Source */
 
 /* model.js is where data is stored in a standard form, whatever the serializer */
+
 IriSP.Model = (function (ns) {
+    
+    function pad(n, x, b) {
+        b = b || 10;
+        var s = (x).toString(b);
+        while (s.length < n) {
+            s = "0" + s;
+        }
+        return s;
+    }
+    
+    function rand16(n) {
+        return pad(n, Math.floor(Math.random()*Math.pow(16,n)), 16);
+    }
+    
+    var uidbase = rand16(8) + "-" + rand16(4) + "-", uidincrement = Math.floor(Math.random()*0x10000);
 
 var Model = {
     _SOURCE_STATUS_EMPTY : 0,
     _SOURCE_STATUS_WAITING : 1,
     _SOURCE_STATUS_READY : 2,
-    _ID_AUTO_INCREMENT : 0,
-    _ID_BASE : (function(_d) {
-        function pad(n){return n<10 ? '0'+n : n}
-        function fillrand(n) {
-            var _res = ''
-            for (var i=0; i<n; i++) {
-                _res += Math.floor(16*Math.random()).toString(16);
-            }
-            return _res;
-        }
-        return _d.getUTCFullYear() + '-'  
-            + pad(_d.getUTCMonth()+1) + '-'  
-            + pad(_d.getUTCDate()) + '-'
-            + fillrand(16);
-    })(new Date()),
     getUID : function() {
-        var _n = (++this._ID_AUTO_INCREMENT).toString();
-        while (_n.length < 4) {
-            _n = '0' + _n
-        }
-        return "autoid-" + this._ID_BASE + '-' + _n;
+        return uidbase + pad(4, (++uidincrement % 0x10000), 16) + "-" + rand16(4) + "-" + rand16(6) + rand16(6);
     },
     regexpFromTextOrArray : function(_textOrArray, _testOnly, _iexact) {
         var _testOnly = _testOnly || false,
@@ -309,13 +306,12 @@
         return _res;
     },
     dateToIso : function(d) {
-        function pad(n){return n<10 ? '0'+n : n}  
         return d.getUTCFullYear()+'-'  
-            + pad(d.getUTCMonth()+1)+'-'  
-            + pad(d.getUTCDate())+'T'  
-            + pad(d.getUTCHours())+':'  
-            + pad(d.getUTCMinutes())+':'  
-            + pad(d.getUTCSeconds())+'Z'  
+            + pad(2, d.getUTCMonth()+1)+'-'  
+            + pad(2, d.getUTCDate())+'T'  
+            + pad(2, d.getUTCHours())+':'  
+            + pad(2, d.getUTCMinutes())+':'  
+            + pad(2, d.getUTCSeconds())+'Z'  
     }
 }
 
@@ -580,19 +576,12 @@
 }
 
 Model.Time.prototype.toString = function() {
-    function pad(_n) {
-        var _res = _n.toString();
-        while (_res.length < 2) {
-            _res = '0' + _res;
-        }
-        return _res;
-    }
     var _hms = this.getHMS(),
         _res = '';
     if (_hms.hours) {
-        _res += pad(_hms.hours) + ':'
+        _res += _hms.hours + ':'
     }
-    _res += pad(_hms.minutes) + ':' + pad(_hms.seconds);
+    _res += pad(2, _hms.minutes) + ':' + pad(2, _hms.seconds);
     return _res;
 }
 
@@ -853,11 +842,17 @@
 Model.Annotation.prototype = new Model.Element();
 
 Model.Annotation.prototype.setBegin = function(_beginMs) {
-    this.begin.setMilliseconds(_beginMs);
+    this.begin.setMilliseconds(Math.max(0,_beginMs));
+    this.trigger("change-begin");
 }
 
-Model.Annotation.prototype.setEnd = function(_beginMs) {
-    this.end.setMilliseconds(_beginMs);
+Model.Annotation.prototype.setEnd = function(_endMs) {
+    this.end.setMilliseconds(Math.min(_endMs));
+    this.trigger("change-end");
+}
+
+Model.Annotation.prototype.setDuration = function(_durMs) {
+    this.setEnd(_durMs + this.begin.milliseconds);
 }
 
 Model.Annotation.prototype.setMedia = function(_idRef) {
@@ -1284,6 +1279,9 @@
         noCss: true,
         requires: [ "swfObject" ]
     },
+    AdaptivePlayer: {
+        noCss: true
+    },
     AutoPlayer: {
         noCss: true
     },
@@ -1728,14 +1726,42 @@
             }
         }
     },
+    deserializeAnnotation : function(_anndata, _source) {
+        var _ann = new IriSP.Model.Annotation(_anndata.id, _source);
+        _ann.description = _anndata.content.description || "";
+        _ann.title = _anndata.content.title || "";
+        _ann.creator = _anndata.meta.creator || "";
+        _ann.created = new Date(_anndata.meta.created);
+        _ann.setMedia(_anndata.media, _source);
+        var _anntype = _source.getElement(_anndata.type);
+        if (!_anntype) {
+            _anntype = new IriSP.Model.AnnotationType(_anndata.type, _source);
+            _anntype.title = _anndata.type_title;
+            _source.getAnnotationTypes().push(_anntype);
+        }
+        _ann.setAnnotationType(_anntype.id);
+        var _tagIds = IriSP._(_anndata.tags).map(function(_title) {
+            var _tags = _source.getTags(true).searchByTitle(_title, true);
+            if (_tags.length) {
+                var _tag = _tags[0];
+            }
+            else {
+                _tag = new IriSP.Model.Tag(_title.replace(/\W/g,'_'),_source);
+                _tag.title = _title;
+                _source.getTags().push(_tag);
+            }
+            return _tag.id;
+        });
+        _ann.setTags(_tagIds);
+        _ann.setBegin(_anndata.begin);
+        _ann.setEnd(_anndata.end);
+        if (typeof _anndata.content.audio !== "undefined" && _anndata.content.audio.href) {
+            _ann.audio = _anndata.content.audio;
+        }
+        _source.getAnnotations().push(_ann);
+    },
     serialize : function(_source) {
-        var _this = this
-            _res = {
-                "objects": _source.getAnnotations().map(function(_annotation) {
-                    return _this.serializeAnnotation(_annotation, _source);
-                })
-            };
-        return JSON.stringify(_res);
+        return JSON.stringify(this.serializeAnnotation(_source.getAnnotations()[0], _source));
     },
     deSerialize : function(_data, _source) {
         if (typeof _data == "string") {
@@ -1745,39 +1771,6 @@
         _source.addList('tag', new IriSP.Model.List(_source.directory));
         _source.addList('annotationType', new IriSP.Model.List(_source.directory));
         _source.addList('annotation', new IriSP.Model.List(_source.directory));
-        IriSP._(_data.objects).each(function(_anndata) {
-            var _ann = new IriSP.Model.Annotation(_anndata.id, _source);
-            _ann.description = _anndata.content.description || "";
-            _ann.title = _anndata.content.title || "";
-            _ann.creator = _anndata.meta.creator || "";
-            _ann.created = new Date(_anndata.meta.created);
-            _ann.setMedia(_anndata.media, _source);
-            var _anntype = _source.getElement(_anndata.type);
-            if (!_anntype) {
-                _anntype = new IriSP.Model.AnnotationType(_anndata.type, _source);
-                _anntype.title = _anndata.type_title;
-                _source.getAnnotationTypes().push(_anntype);
-            }
-            _ann.setAnnotationType(_anntype.id);
-            var _tagIds = IriSP._(_anndata.tags).map(function(_title) {
-                var _tags = _source.getTags(true).searchByTitle(_title, true);
-                if (_tags.length) {
-                    var _tag = _tags[0];
-                }
-                else {
-                    _tag = new IriSP.Model.Tag(_title.replace(/\W/g,'_'),_source);
-                    _tag.title = _title;
-                    _source.getTags().push(_tag);
-                }
-                return _tag.id;
-            });
-            _ann.setTags(_tagIds);
-            _ann.setBegin(_anndata.begin);
-            _ann.setEnd(_anndata.end);
-            if (typeof _anndata.content.audio !== "undefined" && _anndata.content.audio.href) {
-                _ann.audio = _anndata.content.audio;
-            }
-            _source.getAnnotations().push(_ann);
-        });
+        this.deserializeAnnotation(_data, _source);
     }
 }
\ No newline at end of file
--- a/src/ldt/ldt/static/ldt/metadataplayer/Sparkline.js	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/static/ldt/metadataplayer/Sparkline.js	Mon Nov 12 16:17:22 2012 +0100
@@ -18,15 +18,22 @@
         _duration = this.source.getDuration(),
         _max = 0,
         _list = this.getWidgetAnnotations();
-    
     for (var _i = 0; _i < this.slice_count; _i++) {
-        var _begin = new IriSP.Model.Time(_i*_duration/this.slice_count),
-            _end = new IriSP.Model.Time((_i+1)*_duration/this.slice_count),
-            _annotations = _list.filter(function(_annotation) {
-                return _annotation.begin >= _begin && _annotation.end < _end;
-            }).length;
-        _max = Math.max(_max, _annotations);
-        _slices.push(_annotations);
+        var _begin = (_i*_duration/this.slice_count),
+            _end = ((_i+1)*_duration/this.slice_count),
+            _volume = 0;
+            _list.forEach(function(_annotation) {
+                if (_annotation.begin < _end && _annotation.end >= _begin) {
+                    var _d = _annotation.getDuration().milliseconds;
+                    if (!_d) {
+                        _volume += 1;
+                    } else {
+                        _volume += (Math.min(_annotation.end, _end) - Math.max(_annotation.begin, _begin)) / _d;
+                    }
+                }
+            });
+            _max = Math.max(_max, _volume);
+        _slices.push(_volume);
     }
     if (!_max) {
         return;
--- a/src/ldt/ldt/utils/context_processors.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/src/ldt/ldt/utils/context_processors.py	Mon Nov 12 16:17:22 2012 +0100
@@ -1,4 +1,4 @@
-from django.conf import settings
+from ldt import settings
 import ldt
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldt/ldt/utils/url.py	Mon Nov 12 16:17:22 2012 +0100
@@ -0,0 +1,21 @@
+from ldt import settings
+import httplib2
+import re
+import urlparse
+
+def request_with_auth(url, method='GET'):
+    h = httplib2.Http()
+    web_auth = settings.WEB_AUTH if settings.WEB_AUTH else []
+    for entry in web_auth:
+        if not isinstance(entry, dict):
+            continue
+        regex = entry.get('REGEX', None)
+        if regex and re.search(regex, url, re.IGNORECASE):
+            h.add_credentials(entry.get('NAME', ''), entry.get('PASSWORD', ''), entry.get('DOMAIN', ''))
+            break
+            
+    return h.request(url, method)
+
+
+def is_absolute(url):
+    return bool(urlparse.urlparse(url).scheme)
\ No newline at end of file
--- a/virtualenv/res/lib/lib_create_env.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/virtualenv/res/lib/lib_create_env.py	Mon Nov 12 16:17:22 2012 +0100
@@ -44,10 +44,11 @@
     'WSGIREF' : {'setup':'wsgiref', 'url':'http://pypi.python.org/packages/source/w/wsgiref/wsgiref-0.1.2.zip', 'local': 'wsgiref-0.1.2.zip', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     # dependencies for Tastypie : mimeparse>=0.1.3, python-dateutil>=2.1, lxml, PyYAML (not necessary but we never know), python-digest
     'MIMEPARSE' : {'setup':'mimeparse', 'url':'http://pypi.python.org/packages/source/m/mimeparse/mimeparse-0.1.3.tar.gz', 'local': 'mimeparse-0.1.3.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+    'SIX' : {'setup':'six', 'url':'http://pypi.python.org/packages/source/s/six/six-1.2.0.tar.gz', 'local': 'six-1.2.0.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'PYTHON-DATEUTIL' : {'setup':'python-dateutil', 'url':'http://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-2.1.tar.gz', 'local': 'python-dateutil-2.1.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'PYYAML' : {'setup':'pyyaml', 'url':'http://pypi.python.org/packages/source/P/PyYAML/PyYAML-3.10.tar.gz', 'local': 'PyYAML-3.10.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'PYTHON-DIGEST' : {'setup':'python-digest', 'url':'http://pypi.python.org/packages/source/p/python-digest/python-digest-1.7.tar.gz', 'local': 'python-digest-1.7.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
-    'DJANGO-TASTYPIE' : {'setup':'django-tastypie', 'url':'http://pypi.python.org/packages/source/d/django-tastypie/django-tastypie-0.9.11.tar.gz', 'local': 'django-tastypie-0.9.11.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
+    'DJANGO-TASTYPIE' : {'setup':'django-tastypie', 'url':'django-tastypie-0.9.11-modified.tar.gz', 'local': 'django-tastypie-0.9.11-modified.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
 }
 
 if system_str == 'Windows':
Binary file virtualenv/res/src/django-tastypie-0.9.11-modified.tar.gz has changed
Binary file virtualenv/res/src/django-tastypie-0.9.11.tar.gz has changed
Binary file virtualenv/res/src/six-1.2.0.tar.gz has changed
--- a/virtualenv/web/res/res_create_env.py	Mon Nov 12 15:03:36 2012 +0100
+++ b/virtualenv/web/res/res_create_env.py	Mon Nov 12 16:17:22 2012 +0100
@@ -30,6 +30,7 @@
     'PYELASTICSEARCH',
     'WHOOSH',
     'MIMEPARSE',
+    'SIX',
     'PYTHON-DATEUTIL',
     'PYYAML',
     'PYTHON-DIGEST',
--- a/web/ldtplatform/config.py.tmpl	Mon Nov 12 15:03:36 2012 +0100
+++ b/web/ldtplatform/config.py.tmpl	Mon Nov 12 16:17:22 2012 +0100
@@ -7,6 +7,7 @@
 BASE_DIR = '%(base_dir)s'
 BASE_URL = '%(base_url)s'
 WEB_URL = '%(web_url)s'
+WEB_AUTH = [] # example [{'REGEX': 'localhost/~ymh/platform', 'NAME': 'ymh', 'PASSWORD': 'ymh'}]
 STATIC_URL = BASE_URL + 'static/site/'