From 84cdd2ebf5e9f2bf84b0d3054626f0156ed1f7c2 Mon Sep 17 00:00:00 2001 From: rolux Date: Sun, 15 Jan 2012 20:35:37 +0530 Subject: [PATCH] update video editor --- pandora/annotation/models.py | 2 +- pandora/annotation/views.py | 2 + pandora/archive/views.py | 11 +++++ pandora/item/models.py | 30 ++++++++++++ pandora/item/views.py | 4 ++ pandora/padma.jsonc | 7 ++- static/js/pandora/clipList.js | 4 +- static/js/pandora/clipsView.js | 4 +- static/js/pandora/filesView.js | 14 ++++++ static/js/pandora/infoView.padma.js | 71 ++++++++++++++++++----------- static/js/pandora/item.js | 13 ++++-- static/js/pandora/placesDialog.js | 19 +++++--- static/js/pandora/utils.js | 18 ++++++++ 13 files changed, 155 insertions(+), 44 deletions(-) diff --git a/pandora/annotation/models.py b/pandora/annotation/models.py index b8b5293c6..df22e4526 100644 --- a/pandora/annotation/models.py +++ b/pandora/annotation/models.py @@ -118,7 +118,6 @@ class Annotation(models.Model): } for key in ('id', 'in', 'out', 'value', 'created', 'modified'): j[key] = getattr(self, { - 'duration': 'clip__duration', 'hue': 'clip__hue', 'id': 'public_id', 'in': 'start', @@ -127,6 +126,7 @@ class Annotation(models.Model): 'saturation': 'clip__saturation', 'volume': 'clip__volume', }.get(key, key)) + j['duration'] = j['out'] - j['in'] l = self.get_layer() if l['type'] == 'place': diff --git a/pandora/annotation/views.py b/pandora/annotation/views.py index dc8aa542a..c75240811 100644 --- a/pandora/annotation/views.py +++ b/pandora/annotation/views.py @@ -142,6 +142,7 @@ def addAnnotation(request): value=data['value']) annotation.save() response = json_response(annotation.json()) + response['data']['editable'] = True else: response = json_response(status=403, text='permission denied') return render_to_json_response(response) @@ -198,6 +199,7 @@ def editAnnotation(request): }.get(key,key), data[key]) a.save() response['data'] = a.json() + response['data']['editable'] = True else: response = json_response(status=403, text='permission denied') return render_to_json_response(response) diff --git a/pandora/archive/views.py b/pandora/archive/views.py index d1b34bad2..931314389 100644 --- a/pandora/archive/views.py +++ b/pandora/archive/views.py @@ -364,6 +364,17 @@ def editFile(request): return render_to_json_response(response) actions.register(editFile, cache=False) +@login_required_json +def removeFiles(request): + data = json.loads(request.POST['data']) + response = json_response() + if request.user.get_profile().get_level() == 'admin': + models.File.objects.filter(oshash__in=data['ids'], instances__id=None).delete() + else: + response = json_response(status=403, text='permissino denied') + return render_to_json_response(response) +actions.register(removeFiles, cache=False) + def lookup_file(request, oshash): f = get_object_or_404(models.File, oshash=oshash) diff --git a/pandora/item/models.py b/pandora/item/models.py index d33ec2047..2ad5eda60 100644 --- a/pandora/item/models.py +++ b/pandora/item/models.py @@ -216,6 +216,15 @@ class Item(models.Model): for g in filter(lambda g: g not in current_groups, groups): group, created = Group.objects.get_or_create(name=g) self.groups.add(group) + keys = [k['id'] for k in + filter(lambda i: i.get('description'), settings.CONFIG['itemKeys'])] + for k in keys: + key = '%sdescription' % k + if key in data: + value = data.get(k, self.get(k, '')) + d, created = Description.objects.get_or_create(key=k, value=value) + d.description = data.pop(key) + d.save() for key in data: self.data[key] = data[key] return self.save() @@ -496,6 +505,17 @@ class Item(models.Model): elif self.poster_frame != -1.0: i['posterFrame'] = self.poster_frame + dkeys = [k['id'] for k in + filter(lambda i: i.get('description'), settings.CONFIG['itemKeys'])] + if keys: + dkeys = filter(lambda k: k in keys, dkeys) + for key in dkeys: + qs = Description.objects.filter(key=key, value=self.get(key, '')) + if qs.count() == 0: + i['%sdescription'%key] = '' + else: + i['%sdescription'%key] = qs[0].description + if keys: info = {} for key in keys: @@ -1299,3 +1319,13 @@ class Facet(models.Model): self.sortvalue = utils.sort_string(self.value) super(Facet, self).save(*args, **kwargs) +class Description(models.Model): + ''' + shared itemkey descriptions + ''' + class Meta: + unique_together = ("key", "value") + key = models.CharField(max_length=200, db_index=True) + value = models.CharField(max_length=1000, db_index=True) + description = models.TextField() + diff --git a/pandora/item/views.py b/pandora/item/views.py index 363e0174a..d394fd37b 100644 --- a/pandora/item/views.py +++ b/pandora/item/views.py @@ -404,7 +404,11 @@ def get(request): if not data['keys'] or 'notes' in data['keys'] \ and request.user.get_profile().capability('canEditMetadata'): info['notes'] = item.notes + if not data['keys'] or 'groups' in data['keys'] \ + and request.user.get_profile().capability('canEditMetadata'): info['groups'] = [g.name for g in item.groups.all()] + + info['editable'] = item.editable(request.user) response['data'] = info else: response = json_response(status=403, text='permission denied') diff --git a/pandora/padma.jsonc b/pandora/padma.jsonc index 7f2c23596..80b827a1d 100644 --- a/pandora/padma.jsonc +++ b/pandora/padma.jsonc @@ -56,7 +56,8 @@ {"id": "keywords", "title": "Keywords", "type": "string"}, {"id": "language", "title": "Languages", "type": "string"}, {"id": "places", "title": "Places", "type": "string"}, - {"id": "year", "title": "Years", "type": "integer"}, + //{"id": "year", "title": "Years", "type": "integer"}, + {"id": "features", "title": "Features", "type": "string"}, {"id": "director", "title": "Directors", "type": "string"}, {"id": "cinematographer", "title": "Cinematographers", "type": "string"} ], @@ -104,6 +105,7 @@ "title": "Source", "type": "string", "autocomplete": true, + "description": true, "columnWidth": 180, "filter": true, "find": true @@ -113,6 +115,7 @@ "title": "Project", "type": "string", "autocomplete": true, + "description": true, "columnWidth": 120, "filter": true, "find": true @@ -562,7 +565,7 @@ "showBrowser": true, "showCalendarControls": true, // fixme: should be false "showFilters": true, - "showFlags": true, + "showFlags": false, "showHome": true, "showIconBrowser": false, "showInfo": true, diff --git a/static/js/pandora/clipList.js b/static/js/pandora/clipList.js index 10a9b21a1..5fb2dc3ec 100644 --- a/static/js/pandora/clipList.js +++ b/static/js/pandora/clipList.js @@ -7,9 +7,7 @@ pandora.ui.clipList = function(videoRatio) { var ui = pandora.user.ui, fixedRatio = !ui.item ? 16/9 : videoRatio, isClipView = !ui.item ? ui.listView == 'clip' : ui.itemView == 'clips', - textKey = Ox.getObjectById(pandora.site.layers, 'subtitles') - ? 'subtitles' - : 'annotations', + textKey = pandora.getClipTextKey(), that = Ox.IconList({ fixedRatio: fixedRatio, item: function(data, sort, size) { diff --git a/static/js/pandora/clipsView.js b/static/js/pandora/clipsView.js index 36f268409..7cf69258b 100644 --- a/static/js/pandora/clipsView.js +++ b/static/js/pandora/clipsView.js @@ -4,9 +4,7 @@ pandora.ui.clipsView = function(videoRatio) { - var textKey = Ox.getObjectById(pandora.site.layers, 'subtitles') - ? 'subtitles' - : 'annotations', + var textKey = pandora.getClipTextKey(), that = Ox.SplitPanel({ elements: [ { diff --git a/static/js/pandora/filesView.js b/static/js/pandora/filesView.js index 16406c206..62f93b152 100644 --- a/static/js/pandora/filesView.js +++ b/static/js/pandora/filesView.js @@ -203,6 +203,20 @@ pandora.ui.filesView = function(options, self) { }); } }, + 'delete': function(data) { + var ids = data.ids.filter(function(id) { + return self.$filesList.value(id, 'instances').length == 0; + }); + if(ids.length>0 && pandora.user.level == 'admin') { + Ox.print('delete', ids); + pandora.api.removeFiles({ + ids: ids + }, function(result) { + Ox.Request.clearCache(); + self.$filesList.reloadList(); + }); + } + }, init: function(data) { self.numberOfItems = data.items; }, diff --git a/static/js/pandora/infoView.padma.js b/static/js/pandora/infoView.padma.js index c0bc418dc..031580ad8 100644 --- a/static/js/pandora/infoView.padma.js +++ b/static/js/pandora/infoView.padma.js @@ -6,7 +6,8 @@ pandora.ui.infoView = function(data) { // when collapsing the movies browser, the info view should become a split panel var ui = pandora.user.ui, - canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level], + descriptions = [], + canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level] || data.editable, css = { marginTop: '4px', textAlign: 'justify', @@ -108,7 +109,6 @@ pandora.ui.infoView = function(data) { .appendTo($reflection), $text = Ox.Element({ - tooltip: canEdit && !isEditable ? 'Doubleclick to reload metadata' : '' }) .css({ position: 'absolute', @@ -117,9 +117,6 @@ pandora.ui.infoView = function(data) { right: margin + statisticsWidth + margin + 'px', //background: 'green' }) - .bindEvent(canEdit && !isEditable ? { - doubleclick: reloadMetadata - } : {}) .appendTo($data.$element), $statistics = $('
') @@ -252,7 +249,8 @@ pandora.ui.infoView = function(data) { Ox.Editable({ clickLink: pandora.clickLink, placeholder: formatLight('unknown'), - tooltip: 'Doubleclick to edit', + editable: isEditable, + tooltip: isEditable ? 'Doubleclick to edit' : '', type: 'textarea', value: data.description || '' }) @@ -267,14 +265,14 @@ pandora.ui.infoView = function(data) { var list_keys = ['language', 'topic', 'director', 'cinematographer', 'features', 'groups']; $('
').html('
').appendTo($text); [ + 'source', + 'project', 'date', 'location', 'director', 'cinematographer', 'features', 'language', - 'source', - 'project', 'topic', 'user', ].forEach(function(key) { @@ -291,11 +289,16 @@ pandora.ui.infoView = function(data) { clickLink: pandora.clickLink, format: function(value) { return list_keys.indexOf(key) >= 0 - ? formatValue(value.split(', '), key) + ? formatValue(value.split(', '), { + 'director': 'name', + 'cinematographer': 'name', + 'features': 'name', + }[key] || key) : value; }, placeholder: formatLight('unknown'), - tooltip: 'Doubleclick to edit', + editable: isEditable, + tooltip: isEditable ? 'Doubleclick to edit' : '', value: list_keys.indexOf(key) >= 0 ? (data[key] || []).join(', ') : data[key] || '' @@ -306,6 +309,31 @@ pandora.ui.infoView = function(data) { } }) .appendTo($div); + if(pandora.site.itemKeys.filter(function(item) { + if (item.id == key) + return item.description + }).length > 0) { + $('
') + .append( + descriptions[key] = Ox.Editable({ + clickLink: pandora.clickLink, + placeholder: formatLight(Ox.toTitleCase(key) + ' Description'), + editable: isEditable, + tooltip: isEditable ? 'Doubleclick to edit' : '', + type: 'textarea', + value: data[key + 'description']|| '' + }) + .bindEvent({ + submit: function(event) { + editMetadata(key + 'description', event.value); + } + }) + ).css({ + 'padding-top': '4px', + 'padding-bottom': '4px' + }) + .appendTo($div); + } }); $('
').css({height: '16px'}).appendTo($text); @@ -348,9 +376,10 @@ pandora.ui.infoView = function(data) { .appendTo($statistics); renderRightsLevel(); - // Notes ------------------------------------------------------------------- + // Groups, Notes --------------------------------------------------------- if (canEdit) { + $('
') .css({marginBottom: '4px'}) .append(formatKey('Groups', true)) @@ -403,6 +432,11 @@ pandora.ui.infoView = function(data) { edit[key] = value; } pandora.api.edit(edit, function(result) { + data[key] = value; + descriptions[key] && descriptions[key].options({ + value: result.data[key+'description'] + }); + Ox.Request.clearCache(); // fixme: too much? can change filter/list etc if (result.data.id != data.id) { pandora.UI.set({item: result.data.id}); @@ -445,21 +479,6 @@ pandora.ui.infoView = function(data) { ); } - function reloadMetadata() { - var item = ui.item; - // fixme: maybe there's a better method name for this? - pandora.api.updateExternalData({ - id: ui.item - }, function(result) { - Ox.Request.clearCache(item); - if (ui.item == item && ui.itemView == 'info') { - pandora.$ui.contentPanel.replaceElement( - 1, pandora.$ui.item = pandora.ui.item() - ); - } - }); - } - function renderCapabilities(rightsLevel) { var capabilities = Ox.merge( canEdit ? [{name: 'canSeeItem', symbol: 'Find'}] : [], diff --git a/static/js/pandora/item.js b/static/js/pandora/item.js index 29e46de5d..2690b4d17 100644 --- a/static/js/pandora/item.js +++ b/static/js/pandora/item.js @@ -278,8 +278,9 @@ pandora.ui.item = function() { out: data.out, value: data.value, }, function(result) { - result.data.editable = true; + // fixme: both should come from backend result.data.duration = result.data.out - result.data['in']; + result.data.editable = true; pandora.$ui.editor.addAnnotation(data.layer, result.data); }); }, @@ -295,6 +296,10 @@ pandora.ui.item = function() { annotationssort: function(data) { pandora.UI.set({annotationsSort: data.sort}); }, + define: function(data) { + pandora.$ui.placesDialog && pandora.$ui.placesDialog.remove(); + pandora.$ui.placesDialog = pandora.ui.placesDialog(data).open(); + }, editannotation: function(data) { Ox.Log('', 'editAnnotation', data); //fixme: check that edit was successfull @@ -304,9 +309,11 @@ pandora.ui.item = function() { out: data.out, value: data.value, }, function(result) { - Ox.Log('', 'editAnnotation result', result); + Ox.print('', 'editAnnotation result', result); + // fixme: both should come from backend result.data.duration = result.data.out - result.data['in']; - pandora.$ui.editor.updateAnnotation(data.layer, result.data); + result.data.editable = true; + pandora.$ui.editor.updateAnnotation(result.data); }); }, find: function(data) { diff --git a/static/js/pandora/placesDialog.js b/static/js/pandora/placesDialog.js index 05f87d982..1d7186025 100644 --- a/static/js/pandora/placesDialog.js +++ b/static/js/pandora/placesDialog.js @@ -2,16 +2,12 @@ 'use strict'; -pandora.ui.placesDialog = function() { +pandora.ui.placesDialog = function(options) { + // options can be {id: '...'} or {name: '...'} var height = Math.round((window.innerHeight - 48) * 0.9), width = Math.round(window.innerWidth * 0.9), $content = Ox.ListMap({ height: height - 48, - places: function(data, callback) { - return pandora.api.findPlaces(Ox.extend({ - query: {conditions: [], operator: ''} - }, data), callback); - }, addPlace: function(place, callback) { pandora.api.addPlace(place, function(result) { Ox.Request.clearCache(); // fixme: remove @@ -45,12 +41,23 @@ pandora.ui.placesDialog = function() { callback(result.data.items); }); }, + names: pandora.hasPlacesLayer ? function(callback) { + pandora.api.getPlaceNames(function(result) { + callback(result.data.items); + }); + } : null, + places: function(data, callback) { + return pandora.api.findPlaces(Ox.extend({ + query: {conditions: [], operator: ''} + }, data), callback); + }, removePlace: function(place, callback) { pandora.api.removePlace(place, function(result) { Ox.Request.clearCache(); // fixme: remove callback(result); }); }, + selected: options ? options.id : void 0, showControls: pandora.user.ui.showMapControls, showLabels: pandora.user.ui.showMapLabels, showTypes: true, diff --git a/static/js/pandora/utils.js b/static/js/pandora/utils.js index 2881f786e..a0935fa5a 100644 --- a/static/js/pandora/utils.js +++ b/static/js/pandora/utils.js @@ -495,6 +495,12 @@ pandora.getClipsQuery = function() { return clipsQuery; }; +pandora.getClipTextKey = function() { + return Ox.getObjectById(pandora.site.layers, 'subtitles') + ? 'subtitles' + : 'annotations'; +}; + (function() { var itemTitles = {}; pandora.getDocumentTitle = function(itemTitle) { @@ -820,11 +826,23 @@ pandora.hasDialogOrScreen = function() { || $('.OxScreen').length; }; +pandora.hasEventsLayer = function() { + return pandora.site.layers.some(function(layer) { + return layer.type == 'event'; + }); +}; + pandora.hasFocusedInput = function() { var focused = Ox.Focus.focused(); return focused && Ox.UI.elements[focused].is('.OxInput'); }; +pandora.hasPlacesLayer = function() { + return pandora.site.layers.some(function(layer) { + return layer.type == 'place'; + }); +}; + pandora.isClipView = function(view, item) { if (arguments.length == 0) { item = pandora.user.ui.item;