Merge
authorAnthony Ly <anthonyly.com@gmail.com>
Wed, 22 May 2013 17:10:38 +0200
changeset 29 ec6a5592c617
parent 28 a0a048ff33a1 (current diff)
parent 27 badc36cf05d2 (diff)
child 31 11c8b3318486
child 34 e3a17ec94cd8
Merge
--- a/src/metadatacomposer/static/metadatacomposer/css/style.css	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/static/metadatacomposer/css/style.css	Wed May 22 17:10:38 2013 +0200
@@ -36,7 +36,7 @@
 .project-title-editor{line-height: 28px; }
 .project-title-editor-form{margin: 0;}
 .project-action a:last-child{margin-right: 20px;}
-.chapter-widget-info{background-color:#e74c3c; padding: 10px 0; margin-bottom: 20px;}
+.chapter-widget-info{}
 .chapter-widget form{ margin: 0;}
 .chapter-widget input, .form-info-general-annotation input, .form-info-general-annotation textarea{width: 196px; max-width: 196px;  }
 .chapter-widget textarea{width: 426px; max-width: 426px; min-width: 426px; height: 20px;
@@ -44,16 +44,16 @@
 -moz-transition: height .3s ease;
 transition: height .3s ease;
 }
+.form-chapter-edit{padding-top: 10px;}
 .chapter-widget-info table{margin: 0; padding-top: 40px; }
 .chapter-widget table td{}
 .chapter-widget textarea:focus{height: 100px;}
-.chapitre-cut-wrap{position: relative; height: 60px;}
-.btn-cut-chapter{position: absolute;bottom: 0;left: 11px;}
-.indicateur-chapter{z-index:100; left:10px; width: 1px; height: 100%;background-color: #e74c3c; position: absolute;}
-.chapter-segments {width: 100%; height: 20px; background-color: #856; margin: 0;}
-.chapter-segments li{ width:50%; background-color: #f1c40f;float: left; list-style: none; line-height: 20px; height: 20px; text-align: center;}
-.chapter-segments li:last-child{background-color: #27ae60;}
-.list-chapter-wrap{background-color: #3498db;}
+.chapitre-cut-wrap{position: relative; height: 50px; margin-bottom: 10px;}
+.btn-cut-chapter{position: absolute;top: 20px;left: 11px;}
+.indicateur-chapter{z-index:100; left:10px; width: 1px; height: 100%;background-color: #000; position: absolute;}
+.chapter-segments {width: 100%; height: 20px; margin: 0;}
+.chapter-segments li{cursor:pointer; float: left; list-style: none; line-height: 20px; height: 20px; text-align: center; overflow: hidden;}
+.list-chapter-wrap{}
 .list-chapter-wrap h4{padding: 10px; border-bottom: 1px solid; margin-bottom: 10px;}
 .add-annotation-wrap, form.form-info-general-annotation{padding: 10px 0; background-color: #1abc9c;}
 .add-annotation-wrap strong{padding-left: 10px;}
@@ -81,6 +81,9 @@
 .input-image-url{display: none;}
 .timeline-annotation-widget{width: 460px;height: 40px;background-color: #ecf0f1; position: relative;}
 .timeline-annotation-widget .annotation{width: 10px; height: 10px; background-color: #c0392b; position: absolute; top:15px;}
+ul.tagit{margin-left: 10px;-webkit-border-radius: 0px;
+-moz-border-radius: 0px;
+border-radius: 0px;}
 .loader {
     background: url(../img/loader.gif) center no-repeat;
     background-size: 25px 25px;
--- a/src/metadatacomposer/static/metadatacomposer/js/common.js	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/static/metadatacomposer/js/common.js	Wed May 22 17:10:38 2013 +0200
@@ -44,11 +44,15 @@
         var titleMedia = $(this).attr('data-title'),
             textModal = $('<p>Êtes-vous sûr de vouloir supprimer <strong>'+titleMedia+'</strong> ?</p>'),
             eltDelete = $(this).attr('href');
-        $("#modal-confirm #btn-delete-modal").attr('data-id-elt-delete', eltDelete);
+        $("#modal-confirm #btn-delete-modal").attr('data-id-elt-delete', eltDelete).focus();
         $("#modal-confirm .modal-body").empty().append(textModal); 
         $("#modal-confirm").modal('show');
     });
 
+    $("#modal-confirm").on('shown', function() {
+        $("#modal-confirm #btn-delete-modal").focus()
+    });
+    
     $("#modal-confirm").on('click', '#btn-delete-modal', function(e){
         e.preventDefault();
         var idEltDelete = $(this).attr('data-id-elt-delete');
--- a/src/metadatacomposer/static/metadatacomposer/js/edition.js	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/static/metadatacomposer/js/edition.js	Wed May 22 17:10:38 2013 +0200
@@ -2,9 +2,14 @@
 
 var global = {
     diaporama : null,
-    idAnnotation : null
+    idAnnotation : null,
+    colors : ['#f39c12', '#2ecc71', '#3498db', '#9b59b6',  
+    '#f1c40f', '#e67e22', '#e74c3c', '#ecf0f1', 
+    '#16a085', '#27ae60', '#2980b9', '#8e44ad', '#2c3e50',
+    '#f39c12', '#d35400', '#c0392b', '#bdc3c7', '#7f8c8d']
 },
-chapitres = [],
+currentChapter = null,
+chapters = [],
 annotations = [];
 
 myProject.onLoad(function() {
@@ -13,6 +18,8 @@
     
     myMedia = myProject.getCurrentMedia();
     
+    loadChapters();
+
     IriSP.htmlPlayer(
         myMedia,
         $(".main-video"),
@@ -27,8 +34,8 @@
     myMedia.on("timeupdate", function(t) {
 
         //curseur chapitre
-        var pos = $(".chapitre-cut-wrap").width() * t / myMedia.duration,
-            wContainer = $('.chapitre-cut-wrap').width(),
+        var wContainer = $('.chapitre-cut-wrap').width() - 1,
+            pos = wContainer * t / myMedia.duration,
             btnCutChapter = $('.btn-cut-chapter'),
             wBtnCutChapter = btnCutChapter.outerWidth();
             
@@ -38,7 +45,27 @@
         }else{
             btnCutChapter.css("left",pos);
         }
-            
+
+        /*
+        //chapitre edit
+        var formChapter = $('.form-chapter-edit'),
+            inputBeginChapter = formChapter.find('input[name=begin]'),
+            inputDurationChapter = formChapter.find('input[name=duration]'),
+            inputEndChapter = formChapter.find('input[name=end]'),
+            viewBeginChapter = formChapter.find('.begin'),
+            viewDurationChapter = formChapter.find('.duration'),
+            viewEndChapter = formChapter.find('.end'),
+            timeBegin = 0,
+            timeEnd = t.milliseconds,
+            timeDuration = timeEnd - timeBegin;
+
+        inputBeginChapter.val(timeBegin);
+        inputEndChapter.val(timeEnd);
+        inputDurationChapter.val(timeDuration);
+        viewBeginChapter.html(millisecondsToString(timeBegin));
+        viewDurationChapter.html(millisecondsToString(timeDuration));
+        viewEndChapter.html(millisecondsToString(timeEnd));
+        */
     });//timeupdate
     
 });//myProject.onLoad
@@ -55,13 +82,15 @@
         if(idAnnotation !== undefined){
             global.idAnnotation = idAnnotation;
         }
-    })
+    });
+
 //edition image
     $('.popup').on('change', '#media-type-select', function(e){
         var typeImage = $(this).val();
         $('.input-image-url, .input-image-upload').hide();
         $('.input-image-'+typeImage).show();
     });
+
 //bibliotheque
     //video
     $('.popup').on('click', '.bibliotheque-video a', function(e){
@@ -82,16 +111,16 @@
         $('.popup').modal('hide');
     });
 
-    $(document).on('click','.btn-delete', function(e){
+    $(document).on('click','.btn-delete-annotation', function(e){
         e.preventDefault();
         //si c'est une annotation et que la tab est ouverte, on la ferme
         var type = $(this).attr('data-type');
-        if(type == 'annotation'){
-            var idAnnotation = $(this).attr('data-id');
-            $('a[href=#annotation-'+idAnnotation+']').closest('li').remove();
-            $('.tab-content #annotation'+idAnnotation).remove();
-            $('#tab-list-annotation').tab('show');
-        }
+     
+        var idAnnotation = $(this).attr('data-id');
+        $('a[href=#annotation-'+idAnnotation+']').closest('li').remove();
+        $('.tab-content #annotation'+idAnnotation).remove();
+        $('#tab-list-annotation').tab('show');
+        
     });
 
     //confirmation suppression
@@ -131,39 +160,200 @@
     });
 
 //--chapter
-    $('.list-chapter-wrap').on('click', '.edit-chapter', function(e){
+
+//edit
+    $('.list-chapter-wrap').on('click', '.btn-edit-chapter', function(e){
         e.preventDefault();
-        var viewChapter = {
-            titre : 'titre du chapitre',
-            tags : 'tag 1, tag 2, tag 3',
-            description : 'lorem ipsum'
+        var idChapter = $(this).attr('data-chapter-id');
+        loadFormChapter(idChapter);
+        currentChapter = _.find(chapters, function(c){ return c.id == idChapter; });
+    });
+
+    $('.chapter-segments').on('click', 'li', function(){
+        var idChapter = $(this).attr('id');
+        loadFormChapter(idChapter);
+    });
+
+    $('.chapter-widget-info').on('keyup', 'input[name=title], textarea', function(e){
+        var name = $(this).attr('name'),
+            value = $(this).val();
+        currentChapter[name] = value;
+        if(name == 'title'){
+            var idChapter = $(this).parents('form').attr('data-chapter-id');
+            $('.chapter-segments').find('#'+idChapter).text(value);
+            $('#row-list-chapter-'+idChapter).find('td:first').text(value);
         }
-        
+    });
+
+    function onTagItChange(e, ui) {
+        var idChapter = $(this).parents('form').attr('data-chapter-id'),
+            value = $('input[name=tags]').val();
+        currentChapter.tags = value;
+        $('#row-list-chapter-'+idChapter).find('.list-chapter-tags').text(value);
+    }
+
+    var tagitParam = {
+        afterTagRemoved : onTagItChange,
+        afterTagAdded : onTagItChange
+    }
+
+    function loadFormChapter(idChapter){
+        var chapterData = _.find(chapters, function(c){ return c.id == idChapter; }),
+            chapterWrap = $('.chapter-widget-info');
+
+        currentChapter = chapterData;
+
         $.get('template.html', function(templates){
             var tpl = $(templates).filter('#tpl-chapter-edit').html();
-            var tpl = Mustache.render(tpl, viewChapter);
-            $('.form-chapter-edit').empty().append(tpl);
+            tpl = Mustache.render(tpl, chapterData);
+            chapterWrap.empty().append(tpl);
+            chapterWrap.find('.tag-it').tagit(tagitParam);
         });
-    });
-    //nouveau chapitre
+    }
+
+
+//supprimer
+$('.list-chapter-wrap').on('click', '.btn-delete-chapter', function(e){
+    e.preventDefault();
+
+    if(chapters.length == 1){alert('Le projet doit contenir au moins un chapitre.'); return;}
+    var idChapter = $(this).attr('data-chapter-id'),
+        chapter = _.find(chapters, function(c){ return c.id == idChapter; }),
+        indexChapter = _.indexOf(chapters, chapter),
+        chapterModify;
+    if(indexChapter == 0){
+        chapterModify = chapters[1];
+        chapterModify.begin = 0;
+        chapterModify.duration = chapterModify.end;
+    }else{
+        chapterModify = chapters[indexChapter-1];
+        chapterModify.end = chapter.end;
+        chapterModify.duration = chapterModify.end - chapterModify.begin;
+    }
+    chapters = _(chapters).reject(function(c) { return c.id == idChapter; });
+    renderChapter();
+    //si le formulaire est visible
+    if($('#form-chapter-edit-'+idChapter).length){
+        $('#form-chapter-edit-'+idChapter).remove();
+    }
+});
+
+//nouveau chapitre
     $('.chapter-widget').on('click', '.btn-cut-chapter', function(e){
         e.preventDefault();
 
         var uniqId = 'id' + (new Date()).getTime();
-        $.get('template.html', function(templates){
-            var viewChapterRow = {
-                    id : uniqId
-                };
-            var tpl = $(templates).filter('#tpl-chapter-row').html();
-            var tpl = Mustache.render(tpl, viewChapterRow);
-            $('.list-chapter-rows-wrap').append(tpl);
+
+        var title = 'New',
+            tags = 'tag',
+            begin = myMedia.currentTime.milliseconds,
+            end = organizeNewChapter(begin),
+            duration = end - begin,
+            description = '',
+            id = uniqId;
+
+        var dataChapter = {
+            title : title,
+            tags : tags,
+            begin : begin,
+            duration : duration,
+            end : end,
+            description : description,
+            color : global.colors[chapters.length],
+            id : uniqId
+        };
+      
+        chapters.push(dataChapter);
+        renderChapter();
+        loadFormChapter(id);
+    });
+    
+    function organizeNewChapter(beginNew){
+
+        var returnEnd;
+        $.each(chapters, function(k, v){
+            var begin = v.begin,
+                end = v.end;
+            if(beginNew>=begin && beginNew<=end){
+                v.end = beginNew;
+                v.duration = v.end - v.begin;
+                returnEnd = end;
+            }
         });
-    });
-
-    function addChapter(data){
-
+        return returnEnd;
     }
     
+    function renderChapter(){
+        var chapterSegmentWrap = $('.chapter-segments'),
+            wChapterSegmentWrap = chapterSegmentWrap.width(),
+            chapterList = $('.list-chapter-rows-wrap');
+
+        chapters = _.sortBy(chapters, function(c){
+            return c.begin;
+        });
+
+        chapterSegmentWrap.empty();
+        chapterList.empty();
+
+        $.each(chapters, function(k, v){
+
+            //segments
+                color = global.colors[k],
+                width = Math.floor(v.duration * wChapterSegmentWrap / myMedia.duration.milliseconds),
+                segment = $('<li>'+v.title+'</li>').css({
+                    width : width,
+                    backgroundColor : v.color
+                }).attr('id', v.id);
+    
+            chapterSegmentWrap.append(segment);
+ 
+            //liste
+            $.get('template.html', function(templates){
+
+                v.beginString = millisecondsToString(v.begin);
+                v.durationString = millisecondsToString(v.duration);
+                v.endString = millisecondsToString(v.end);
+             
+                var tplChapterRow = $(templates).filter('#tpl-chapter-row').html();
+                tplChapterRow = Mustache.render(tplChapterRow, v);
+                chapterList.append(tplChapterRow);
+
+            });
+        });
+    }
+
+//init
+    function loadChapters(){//nouveau projet, 1 chapitre
+
+        var uniqId = 'id' + (new Date()).getTime();
+
+        var title = 'chapitre 1',
+            tags = 'tag1,tag2',
+            begin = 0,
+            duration = myMedia.duration.milliseconds,
+            end = myMedia.duration.milliseconds,
+            description = 'description du chapitre 1',
+            id = uniqId;
+
+        var dataChapter = {
+            title : title,
+            tags : tags,
+            begin : begin,
+            duration : duration,
+            end : end,
+            description : description,
+            color : global.colors[chapters.length],
+            id : id
+        };
+
+        chapters.push(dataChapter);
+        renderChapter();
+    }
+
+
+
+
+
     //edit annotation
     $('#list-annotations').on('click', 'a.btn-edit-annotation', function(e){
         e.preventDefault();
@@ -410,4 +600,6 @@
 
 $('#annotation-tab a:last-child').tab('show');
 
+$(".tag-it").tagit();
+
 });//ready
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metadatacomposer/static/metadatacomposer/js/tangle.js	Wed May 22 17:10:38 2013 +0200
@@ -0,0 +1,62 @@
+/* Tangles */
+    var tangleMsPerPixel = 100,
+        activeTangle,
+        tangleStartX,
+        tangleStartVal,
+        tangleHasMoved;
+    
+    $('.chapter-widget-info').on('mousedown', '.time-tangle', function(evt){
+   // $(".time-tangle").mousedown(function(evt) {
+
+        activeTangle = $(this);
+        activeTangle.addClass("active");
+        tangleStartVal = +activeTangle.attr("data-milliseconds");
+        tangleStartX = evt.pageX;
+        tangleHasMoved = false;
+        $(this).siblings(".time-tangle").addClass("deactivate");
+        return false;
+    });
+    $(document)
+        .mousemove(function(evt) {
+            if (activeTangle) {
+                tangleHasMoved = true;
+                var newval = new IriSP.Model.Time(tangleMsPerPixel * (evt.pageX - tangleStartX) + tangleStartVal);
+                activeTangle.trigger("valuechange", newval);
+                return false;
+            }
+        })
+        .mouseup(function() {
+            if (activeTangle) {
+                activeTangle.text(activeTangle.text().replace(/\.\d+$/,''));
+                $(".time-tangle").removeClass("active deactivate");
+                activeTangle = undefined;
+            }
+        });
+        
+    $(".tangle-start")
+        .mouseup(function(evt) {
+            if (!tangleHasMoved && currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.begin);
+            }
+        })
+        .on("valuechange", function(evt, val) {
+            if (currentMedia && currentSegment) {
+                currentSegment.setBegin(val);
+            }
+        });
+    $(".tangle-end")
+        .mouseup(function(evt) {
+            if (!tangleHasMoved && currentMedia && currentSegment) {
+                currentMedia.setCurrentTime(currentSegment.end);
+            }
+        })
+        .on("valuechange", function(evt, val) {
+            if (currentMedia && currentSegment) {
+                currentSegment.setEnd(val);
+            }
+        });
+    $(".tangle-duration").on("valuechange", function(evt, val) {
+        if (currentMedia && currentSegment) {
+            currentSegment.setDuration(val);
+        }
+    });
\ No newline at end of file
--- a/src/metadatacomposer/templates/metadatacomposer_home.html	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/templates/metadatacomposer_home.html	Wed May 22 17:10:38 2013 +0200
@@ -61,7 +61,11 @@
             <article class="container">
                 <div class="row article-title">
                     <h3 class="span8"><i class="icon-picture"></i> Dernières images</h3>
-                    <div class="span4 wrap-btn-add"><a class="btn btn-success" href="#">Ajouter une image <i class="icon-plus-sign"></i></a></div>
+                    <div class="span4 wrap-btn-add">
+                        <a data-type-media="image" data-title="Ajouter une image" class="btn btn-success open-modal" 
+                         href="{% url composer_modal_image branding=branding %}" data-hide-bibliotheque>
+                         Ajouter une image <i class="icon-plus-sign"></i></a>
+                    </div>
                 </div>
                 <div class="row">
                   {% for i in images %}
@@ -75,7 +79,11 @@
             <article class="container">
                 <div class="row article-title">
                     <h3 class="span8"><i class="icon-film"></i> Dernières vidéos</h3>
-                    <div class="span4 wrap-btn-add"><a class="btn btn-success" href="#">Ajouter une vidéo <i class="icon-plus-sign"></i></a></div>
+                    <div class="span4 wrap-btn-add">
+                        <a data-type-media="video" data-title="Ajouter une vidéo" class="btn btn-success open-modal" 
+                         href="{% url composer_modal_video branding=branding %}" data-hide-bibliotheque>
+                         Ajouter une vidéo <i class="icon-plus-sign"></i></a>
+                    </div>
                 </div>
                 <div class="row">
                   {% for c in contents %}
@@ -91,6 +99,13 @@
         </section>
         <footer></footer>
     </div><!-- wrap -->
+    <div id="modal-template" class="modal hide fade popup" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+        <div class="modal-header">
+            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
+            <h3></h3>
+        </div>
+        <div class="modal-body"></div>
+    </div>
 {% endblock %}
     {% block modal_divs %}
     {% endblock %}
--- a/src/metadatacomposer/templates/metadatacomposer_modal_image.html	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/templates/metadatacomposer_modal_image.html	Wed May 22 17:10:38 2013 +0200
@@ -1,4 +1,5 @@
-{% load static %} 
+{% load static %}
+{% load thumbnail %}
 <div class="row">
     <div class="span3">
         <ul class="modal-menu">
@@ -9,19 +10,20 @@
     <div class="span8 offset1 popup-content">
     <h4>Informations et source de l'image</h4>
     <form class="form-horizontal" action="{% url composer_modal_image branding=branding %}" enctype="multipart/form-data" method="post">{% csrf_token %}
-	    {% for error in form.errors.name %}
+        {% if image_pk %}<input type="hidden" value="{{ image_pk }}" name="image_pk">{% endif %}
+	    {% for error in form.errors %}
 	    <span class="error">{{ error }}</span>
 	    {% endfor %}
         <div class="control-group">
             <label class="control-label" for="media-title">Titre :</label>
             <div class="controls">
-                <input type="text" name="title" id="id_title" placeholder="Titre de l'image.." maxlength="1024"/>
+                <input type="text" name="title" id="id_title" placeholder="Titre de l'image..." maxlength="1024" value="{{ form.title.value|default_if_none:'' }}"/>
             </div>
         </div>
         <div class="control-group">
             <label class="control-label" for="media-description">Description :</label>
             <div class="controls">
-                <textarea name="description" id="id_description"></textarea>
+                <textarea name="description" id="id_description">{{ form.description.value|default_if_none:"" }}</textarea>
             </div>
         </div>
         <!--div class="control-group">
@@ -41,10 +43,16 @@
             </div>
         </div-->
         <div class="control-group input-image-upload">
-            <label class="control-label" for="media-url-link">Fichier de l'image :</label>
+            <label class="control-label" for="media-url-link">Fichier de l'image :<br/>(jpg, png)</label>
             <div class="controls">
-                <input type="file" name="image_file" id="id_image_file"/>
+                <input type="file" name="image_file" id="id_image_file" accept="image/jpeg, image/png"/>
                 <a href="#" class="btn btn-previsualisation" class="btn"><i class="icon-eye-open"></i> Prévisualiser</a>
+                {% if form.image_file.value %}
+                <p>Image actuelle :</p>
+                {% thumbnail form.image_file.value "100" crop="center" format="PNG" as im %}<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />{% empty %}<img src="{% static 'metadatacomposer/img/60x60.gif' %}" width="60px" height="60px" />{% endthumbnail %}
+                {% endif %}
+                <br/>
+                
             </div>
         </div>
         <div class="control-group">
--- a/src/metadatacomposer/templates/metadatacomposer_modal_video.html	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/templates/metadatacomposer_modal_video.html	Wed May 22 17:10:38 2013 +0200
@@ -1,4 +1,6 @@
 {% load static %}
+{% load thumbnail %}
+{% load composer_tags %}
 <div class="row">
     <div class="span3">
         <ul class="modal-menu">
@@ -8,32 +10,40 @@
     </div>
     <div class="span8 offset1 popup-content">
 
-    <form class="row" action="#" method="post">
+    <form class="row" action="{% if content %}{% url composer_modal_video branding=branding iri_id=content.iri_id %}{% else %}{% url composer_modal_video branding=branding %}{% endif %}" enctype="multipart/form-data" method="post">
         <h4 class="span8">Informations sur la vidéo</h4>
         <div class="span4">
-            
-            <label for="title-media-video">Titre :</label>
-            <input type="text" id="title-media-video" name="">
-            <label for="description-title-media">Description :</label>
-            <textarea name="" id="description-title-media"></textarea>
+            {% csrf_token %}
+            <label for="id_content-title">Titre :</label>
+            <input type="text" name="content-title" id="id_content-title" {% if content %}value="{{ content.title }}"{% endif %}/>
+            <label for="id_content-description">Description :</label>
+            <textarea name="content-description" id="id_content-description">{% if content %}{{ content.description }}{% endif %}</textarea>
+            <input type="hidden" name="content-duration" id="id_content-duration" {% if content %}value="{{ content.duration }}"{% endif %}/>
+            <input type="hidden" name="content-media_input_type" id="id_content-media_input_type" value="create" />
+            <input type="hidden" name="content-iri_id" id="id_content-iri_id" value="{{ iri_id }}"/>
+            <input type="checkbox" name="media-media_public" id="id_media-media_public" checked="checked" style="display:none" />
         </div>
         <div class="span4">
             <label for="">Durée de la vidéo :</label>
             <div class="select-duration">
-                <input pattern="[0-9]*" name="" type="text" id="video-hour" class="number-spin span1" value="1">
+                <input pattern="[0-9]*" name="" type="text" id="video-hour" class="number-spin span1" value="{% if content %}{{ content.duration|to_hours }}{% else %}0{% endif %}">
                 <label for="video-hour">H</label>
-                <input pattern="[0-9]*" name="" type="text" id="video-minute" class="number-spin span1" value="1">
+                <input pattern="[0-9]*" name="" type="text" id="video-minute" class="number-spin span1" value="{% if content %}{{ content.duration|to_minutes }}{% else %}10{% endif %}">
                 <label for="video-minute">M</label>
-                <input pattern="[0-9]*" name="" type="text" id="video-seconde" class="number-spin span1" value="1">
+                <input pattern="[0-9]*" name="" type="text" id="video-seconde" class="number-spin span1" value="{% if content %}{{ content.duration|to_seconds }}{% else %}0{% endif %}">
                 <label for="video-seconde">S</label>
             </div>
-            <label for="">Vignette de la vidéo :</label>
-            <input type="file">
+            <label for="">Vignette de la vidéo (jpg, png, 1Mo max) :</label>
+            <input type="file" name="image" id="id_image" accept="image/jpeg, image/png" />
+            {% if content %}
+            <p>Image actuelle :</p>
+            {% thumbnail content.image "x60" crop="center" format="PNG" as im %}<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />{% empty %}<img src="{% static 'metadatacomposer/img/60x60.gif' %}" width="60px" height="60px" />{% endthumbnail %}
+            {% endif %}
         </div>
         <div class="span8">
             <h4>Source de la vidéo</h4>
             <label for="">URL de la vidéo :</label>
-            <input type="text" class="input-xxlarge" placeholder="http://youtu.be">
+            <input type="text" class="input-xxlarge" placeholder="http://youtu.be" name="media-src" id="id_media-src" {% if content %}value="{{ content.media_obj.src }}"{% endif %}/>
             <div class="control-group">
                 <div class="controls">
                     <a href="#" class="btn-cancel btn btn-inverse"><i class="icon-reply" data-dismiss="modal" aria-hidden="true"></i> Annuler</a>
@@ -50,4 +60,9 @@
     max:60,
     min:0
 });
+// Update time input in milliseconds
+$('.select-duration .number-spin').change(function(){
+    $("#id_content-duration").val( (parseInt($("#video-hour").val()) * 3600000) + (parseInt($("#video-minute").val()) * 60000) + (parseInt($("#video-seconde").val()) * 1000) );
+    console.log($("#id_content-duration").val());
+});
 </script>
\ No newline at end of file
--- a/src/metadatacomposer/templates/metadatacomposer_resource_list.html	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/templates/metadatacomposer_resource_list.html	Wed May 22 17:10:38 2013 +0200
@@ -24,66 +24,8 @@
                                 </a>
                             </div>
                         </div>
-
-                        <table class="table table-striped">
-                            <thead>
-                                <tr>
-                                    <th>Aperçu</th>
-                                    <th>Titre</th>
-                                    <th>Projets associés</th>
-                                    <th>Date de modification</th>
-                                    <th>Actions</th>
-                                </tr>
-                            </thead>
-                            <tbody class="liste">
-                                <tr id="row-list-ressources-image-id1">
-                                    <td><img src="{% static 'metadatacomposer/img/60x60.gif' %}" alt=""></td>
-                                    <td>Titre de l'image qwerty</td>
-                                    <td>
-                                        <ul>
-                                            <li><a href="">Titre du projet azerty</a></li>
-                                            <li><a href="">Titre du projet azerty</a></li>
-                                            <li><a href="">Titre du projet azerty</a></li>
-                                        </ul>
-                                    </td>
-                                    <td>14/03/2014</td>
-                                    <td>
-                                        <a data-type-media="image" data-title="Modifier une image" class="btn open-modal" href="{% url composer_modal_image branding=branding %}"><i class="icon-pencil"></i></a>
-                                        <a class="btn btn-delete" data-title="Titre du média" href="#row-list-ressources-image-id1">
-                                            <i class="icon-remove"></i>
-                                        </a>
-                                    </td>
-                                </tr>
-                                <tr id="row-list-ressources-image-id2">
-                                    <td><img src="{% static 'metadatacomposer/img/60x60.gif' %}" alt=""></td>
-                                    <td>Titre de l'image azerty</td>
-                                    <td>
-                                        <ul>
-                                            <li><a href="">Titre du projet azerty</a></li>
-                                            <li><a href="">Titre du projet azerty</a></li>
-                                            <li><a href="">Titre du projet azerty</a></li>
-                                        </ul>
-                                    </td>
-                                    <td>14/03/2014</td>
-                                    <td>
-                                        <a data-type-media="image" data-title="Modifier une image" class="btn open-modal" href="{% url composer_modal_image branding=branding %}"><i class="icon-pencil"></i></a>
-                                        <a class="btn btn-delete" data-title="Titre du média" href="#row-list-ressources-image-id2">
-                                            <i class="icon-remove"></i>
-                                        </a>
-                                    </td>
-                                </tr>
-                            </tbody>
-                        </table>
-                        <div class="pagination">
-                            <ul>
-                                <li class="disabled"><a href="#">Prev</a></li>
-                                <li class="active"><a href="#">1</a></li>
-                                <li><a href="#">2</a></li>
-                                <li><a href="#">3</a></li>
-                                <li><a href="#">4</a></li>
-                                <li><a href="#">5</a></li>
-                                <li><a href="#">Next</a></li>
-                            </ul>
+                        <div id="image_list_container">
+                        {% include 'partial/resource_image_list.html' %}
                         </div>
                     </article>
 
@@ -148,5 +90,22 @@
             }
         });
     });
+    $(document).on('click', 'a.image_pagination', function(e){
+        e.preventDefault();e.stopPropagation();
+        var url = $(this).attr('href');
+        $(this).addClass("loader");
+        $.ajax({
+            url: url,
+            cache: false,
+            type: 'GET',
+            success: function(data, status, request) {
+                $("#image_list_container").html(data);
+            },
+            error: function(jqXHR, textStatus, errorThrown) {
+                resp = $.parseJSON(jqXHR.responseText);
+                alert(resp.message);
+            }
+        });
+    });
     </script>
 {% endblock %}
--- a/src/metadatacomposer/templates/partial/resource_content_list.html	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/templates/partial/resource_content_list.html	Wed May 22 17:10:38 2013 +0200
@@ -26,7 +26,7 @@
                                         </td>
                                         <td>{{ res.content.update_date|date:"Y/m/d" }}</td>
                                         <td>
-                                            <a data-type-media="video" data-title="Modifier une vidéo" class="btn open-modal" href="{% url composer_modal_video branding=branding %}"><i class="icon-pencil"></i></a>
+                                            <a data-type-media="video" data-title="Modifier une vidéo" class="btn open-modal" href="{% url composer_modal_video branding=branding iri_id=res.content.iri_id %}"><i class="icon-pencil"></i></a>
                                             <a class="btn btn-delete" data-title="Titre du média" href="#row-list-ressources-video-id1">
                                             <i class="icon-remove"></i>
                                         </a>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metadatacomposer/templates/partial/resource_image_list.html	Wed May 22 17:10:38 2013 +0200
@@ -0,0 +1,61 @@
+{% load static %}
+{% load i18n %}
+{% load thumbnail %}
+{% load front_tags %}
+                        <table class="table table-striped">
+                            <thead>
+                                <tr>
+                                    <th>Aperçu</th>
+                                    <th>Titre</th>
+                                    <th>Projets associés</th>
+                                    <th>Date de modification</th>
+                                    <th>Actions</th>
+                                </tr>
+                            </thead>
+                            <tbody class="liste">
+                              {% for res in image_results %}
+                                <tr id="row-list-ressources-image-id1">
+                                    <td>{% thumbnail res.image_file "60x60" crop="center" format="PNG" as im %}<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />{% empty %}<img src="{% static 'metadatacomposer/img/60x60.gif' %}" width="60px" height="60px" />{% endthumbnail %}</td>
+                                    <td>{{ res.title }}</td>
+                                    <td>
+                                        <!--ul>
+                                            <li><a href="">Titre du projet azerty</a></li>
+                                        </ul-->
+                                    </td>
+                                    <td>{{ res.modification_date|date:"Y/m/d" }}</td>
+                                    <td>
+                                        <a data-type-media="image" data-title="Modifier une image" class="btn open-modal" href="{% url composer_modal_image branding=branding %}?image={{ res.pk }}"><i class="icon-pencil"></i></a>
+                                        <a class="btn btn-delete" data-title="Titre du média" href="#row-list-ressources-image-id1">
+                                            <i class="icon-remove"></i>
+                                        </a>
+                                    </td>
+                                </tr>
+                              {% endfor %}
+                            </tbody>
+                        </table>
+                            <div class="pagination">
+                                <ul>
+			                    {% if image_results.has_previous %}
+			                        <li><a href="{% url composer_image_page branding=branding %}?page={{ image_results.previous_page_number }}" class="image_pagination">{% trans "Previous" %}</a></li>
+			                    {% else %}
+			                       <li class="disabled"><a>{% trans "Previous" %}</a></li>
+			                    {% endif %}
+			                    {% if image_results.paginator.num_pages > 1 %}
+			                     {% for i in image_results.paginator.num_pages|get_range %}
+			                      {% if i|add:'1' == image_results.number %}
+			                        <li class="active"><a href="#">{{i|add:'1'}}</a></li>
+			                      {% else %}
+			                        <li><a href="{% url composer_image_page branding=branding %}?page={{i|add:'1'}}" class="image_pagination">{{i|add:'1'}}</a></li>
+			                      {% endif %}
+			                     {% endfor %}
+			                    {% endif %}
+			                    {% if image_results.has_next %}
+			                        <li><a href="{% url composer_image_page branding=branding %}?page={{ image_results.next_page_number }}" class="image_pagination">{% trans "Next" %}</a></li>
+			                    {% else %}
+			                       <li class="disabled"><a>{% trans "Next" %}</a></li>
+			                    {% endif %}
+			                    {% if image_results.paginator.num_pages > 1 %}
+			                        <li><a href="{% url composer_image_page branding=branding %}?page=x" class="image_pagination">({% trans "All" %})</a></li>
+			                    {% endif %}
+                                </ul>
+                            </div>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metadatacomposer/templatetags/__init__.py	Wed May 22 17:10:38 2013 +0200
@@ -0,0 +1,1 @@
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/metadatacomposer/templatetags/composer_tags.py	Wed May 22 17:10:38 2013 +0200
@@ -0,0 +1,68 @@
+from django.template import Library, TemplateSyntaxError
+
+register = Library()
+
+@register.filter
+def to_hours(value, arg=None):
+    """Takes an integer value of milliseconds and write the hour duration"""
+    # Error management
+    if value is None :
+        return ""
+    if not isinstance(value, (int,long,float,str,unicode)) :
+        raise TemplateSyntaxError('str_duration value error : value must be integer or long or float or string. type = ' + str(type(value)))
+    if isinstance(value, (str,unicode)) :
+        try:
+            value = int(value)
+        except :
+            raise TemplateSyntaxError('str_duration value error : can not convert value "' + value + '" into integer')
+    # We take off the milliseconds
+    ms = abs(value)
+    sec = ms//1000
+    return (sec//3600)
+to_hours.is_safe = True
+
+
+@register.filter
+def to_minutes(value, arg=None):
+    """Takes an integer value of milliseconds and write the hour duration"""
+    # Error management
+    if value is None :
+        return ""
+    if not isinstance(value, (int,long,float,str,unicode)) :
+        raise TemplateSyntaxError('str_duration value error : value must be integer or long or float or string. type = ' + str(type(value)))
+    if isinstance(value, (str,unicode)) :
+        try:
+            value = int(value)
+        except :
+            raise TemplateSyntaxError('str_duration value error : can not convert value "' + value + '" into integer')
+    # We take off the milliseconds
+    ms = abs(value)
+    sec = ms//1000
+    hours = sec//3600
+    min = (sec - (hours * 3600))//60
+    return min
+to_minutes.is_safe = True
+
+
+@register.filter
+def to_seconds(value, arg=None):
+    """Takes an integer value of milliseconds and write the hour duration"""
+    # Error management
+    if value is None :
+        return ""
+    if not isinstance(value, (int,long,float,str,unicode)) :
+        raise TemplateSyntaxError('str_duration value error : value must be integer or long or float or string. type = ' + str(type(value)))
+    if isinstance(value, (str,unicode)) :
+        try:
+            value = int(value)
+        except :
+            raise TemplateSyntaxError('str_duration value error : can not convert value "' + value + '" into integer')
+    # We take off the milliseconds
+    ms = abs(value)
+    sec = ms//1000
+    hours = sec//3600
+    min = (sec - (hours * 3600))//60
+    sec = (sec - (hours * 3600) - (min*60))
+    return sec
+to_seconds.is_safe = True
+
--- a/src/metadatacomposer/urls.py	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/urls.py	Wed May 22 17:10:38 2013 +0200
@@ -1,13 +1,16 @@
 from django.conf.urls.defaults import patterns, url
 from metadatacomposer.views import MetadataComposerHome, MetadataComposerProjectList,\
     MetadataComposerResourceList, MetadataComposerContentPagination,\
-    MetadataComposerModalVideo, MetadataComposerModalImage
+    MetadataComposerModalVideo, MetadataComposerModalImage,\
+    MetadataComposerImagePagination
 
 urlpatterns = patterns('',
     url(r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog', name='jsi18n'),
     url(r'^(?P<branding>.*)/projectlist/$', MetadataComposerProjectList.as_view(), name="composer_project_list"),
     url(r'^(?P<branding>.*)/resourcelist/$', MetadataComposerResourceList.as_view(), name="composer_resource_list"),
+    url(r'^(?P<branding>.*)/imagepage/$', MetadataComposerImagePagination.as_view(), name="composer_image_page"),
     url(r'^(?P<branding>.*)/contentpage/$', MetadataComposerContentPagination.as_view(), name="composer_content_page"),
+    url(r'^(?P<branding>.*)/modalvideo/(?P<iri_id>[\w-]+)$', MetadataComposerModalVideo.as_view(), name="composer_modal_video"),
     url(r'^(?P<branding>.*)/modalvideo/$', MetadataComposerModalVideo.as_view(), name="composer_modal_video"),
     url(r'^(?P<branding>.*)/modalimage/$', MetadataComposerModalImage.as_view(), name="composer_modal_image"),
     url(r'^(?P<branding>.*)/$', MetadataComposerHome.as_view(), name="composer_home"),
--- a/src/metadatacomposer/views.py	Wed May 22 17:09:59 2013 +0200
+++ b/src/metadatacomposer/views.py	Wed May 22 17:10:38 2013 +0200
@@ -3,9 +3,11 @@
 from django.core.paginator import Paginator, InvalidPage, EmptyPage
 from django.shortcuts import redirect
 from django.utils.decorators import method_decorator
+from django.views.decorators.cache import never_cache
 from django.views.generic.base import View, TemplateResponseMixin
-from django.views.generic.edit import FormView
 from ldt.ldt_utils.models import Project, Content
+from ldt.ldt_utils.views.content import write_content_base
+from ldt.ldt_utils.utils import generate_uuid
 from metadatacomposer.forms import ImageUploadModelForm
 from metadatacomposer.models import Image
 
@@ -32,6 +34,7 @@
         return "metadatacomposer_home.html"
     
     @method_decorator(login_required)
+    @method_decorator(never_cache)
     def dispatch(self, *args, **kwargs):
         return super(MetadataComposerHome, self).dispatch(*args, **kwargs)
     
@@ -40,7 +43,6 @@
         
         projects = Project.safe_objects.filter(owner=request.user).order_by('-modification_date')[:6]
         images = Image.objects.order_by('-modification_date')[:6]
-        logger.debug(images)
         contents = Content.safe_objects.order_by('-update_date')[:6]
         
         context = self.get_context_dict(request)
@@ -55,6 +57,7 @@
         return "metadatacomposer_project_list.html"
     
     @method_decorator(login_required)
+    @method_decorator(never_cache)
     def dispatch(self, *args, **kwargs):
         return super(MetadataComposerProjectList, self).dispatch(*args, **kwargs)
     
@@ -85,18 +88,42 @@
         return "metadatacomposer_resource_list.html"
     
     @method_decorator(login_required)
+    @method_decorator(never_cache)
     def dispatch(self, *args, **kwargs):
         return super(MetadataComposerResourceList, self).dispatch(*args, **kwargs)
     
     def get(self, request, branding="iri", **kwargs):
         self.branding = branding
         
+        # We get the first page of images
+        image_results = get_images(1)
         # We get the first contents page and theirs projects
         content_results = get_contents_and_projects(1, request.user)
-        # We get the first page of images
         
         context = self.get_context_dict(request)
-        context.update({"content_results":content_results})
+        context.update({"image_results":image_results, "content_results":content_results})
+        return self.render_to_response(context)
+
+
+
+class MetadataComposerImagePagination(TemplateResponseMixin, MetadataComposerContextView):
+    
+    def get_template_names(self):
+        return "partial/resource_image_list.html"
+    
+    @method_decorator(login_required)
+    @method_decorator(never_cache)
+    def dispatch(self, *args, **kwargs):
+        return super(MetadataComposerImagePagination, self).dispatch(*args, **kwargs)
+    
+    def get(self, request, branding="iri", **kwargs):
+        self.branding = branding
+        page = request.GET.get("page") or 1
+        # Get current contents page and theirs projects
+        image_results = get_images(page)
+        
+        context = self.get_context_dict(request)
+        context.update({"image_results":image_results})
         return self.render_to_response(context)
 
 
@@ -107,6 +134,7 @@
         return "partial/resource_content_list.html"
     
     @method_decorator(login_required)
+    @method_decorator(never_cache)
     def dispatch(self, *args, **kwargs):
         return super(MetadataComposerContentPagination, self).dispatch(*args, **kwargs)
     
@@ -122,6 +150,24 @@
 
 
 
+def get_images(page):
+    
+    # We get the current's page images
+    images = Image.objects.order_by('-modification_date')
+    #nb = getattr(settings, 'METADATACOMPOSER_ELEMENTS_PER_PAGE', 9)
+    nb = 2
+    if page=="x":
+        nb = images.count()
+    paginator = Paginator(images, nb)
+    try:
+        results = paginator.page(page)
+    except (EmptyPage, InvalidPage):
+        results = paginator.page(paginator.num_pages)
+    
+    return results
+
+
+
 def get_contents_and_projects(page, user):
     
     # We get the current's page contents
@@ -154,13 +200,35 @@
         return "metadatacomposer_modal_video.html"
     
     @method_decorator(login_required)
+    @method_decorator(never_cache)
     def dispatch(self, *args, **kwargs):
         return super(MetadataComposerModalVideo, self).dispatch(*args, **kwargs)
     
-    def get(self, request, branding="iri", **kwargs):
+    def get(self, request, branding="iri", iri_id=None, **kwargs):
         self.branding = branding
+        
+        # Generate fake id to validate ContentForm
+        if not iri_id:
+            iri_id = generate_uuid()
+            content = None
+        else:
+            content = Content.safe_objects.select_related("media_obj").get(iri_id=iri_id)
+        
         context = self.get_context_dict(request)
+        context.update({"iri_id":iri_id, "content":content})
         return self.render_to_response(context)
+    
+    def post(self, request, branding="iri", iri_id=None, **kwargs):
+        self.branding = branding
+        
+        # We create the media
+        content_form, media_form, picture_form, form_status, _, current_front_project, e, traceback = write_content_base(request, iri_id)
+        # And test creation
+        if (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")
+            raise e, None, traceback
+        return redirect(request.META['HTTP_REFERER'])
 
 
 
@@ -170,38 +238,40 @@
         return "metadatacomposer_modal_image.html"
     
     @method_decorator(login_required)
+    @method_decorator(never_cache)
     def dispatch(self, *args, **kwargs):
         return super(MetadataComposerModalImage, self).dispatch(*args, **kwargs)
     
     def get(self, request, branding="iri", **kwargs):
         self.branding = branding
+        image_pk = request.GET.get("image") or None
         
         # Add form
-        form = ImageUploadModelForm()
+        if image_pk:
+            form = ImageUploadModelForm(instance=Image.objects.get(pk=image_pk))
+        else:
+            form = ImageUploadModelForm()
         
         context = self.get_context_dict(request)
-        context.update({"form":form})
+        context.update({"form":form, "image_pk":image_pk})
         return self.render_to_response(context)
     
     def post(self, request, branding="iri", **kwargs):
         self.branding = branding
         
         # Check form
-        form = ImageUploadModelForm(request.POST, request.FILES)
-        logger.debug("COUCOU 1")
-        logger.debug(form)
-        logger.debug(form.is_valid())
+        if 'image_pk' in request.POST:
+            form = ImageUploadModelForm(request.POST, request.FILES, instance=Image.objects.get(pk=request.POST['image_pk']))
+        else:
+            form = ImageUploadModelForm(request.POST, request.FILES)
         if form.is_valid():
-            logger.debug("COUCOU 2")
             # If an image id was in the form, we update the existing image
-            if 'image_pk' in request.POST:
-                form = ImageUploadModelForm(request.POST, request.FILES, instance=Image.objects.get(pk=request.POST['image_pk']))
             form.save()
+            return redirect(request.META['HTTP_REFERER'])
         else:
-            logger.debug("COUCOU 3")
             context = self.get_context_dict(request)
             context.update({"form":form})
-        return redirect(request.META['HTTP_REFERER'])
+            return redirect("composer_modal_image", branding=branding)