minimal edits with a sortable list interface and 'add selected item/in/out support'

This commit is contained in:
j 2013-05-27 20:06:56 +00:00
parent 27877df0cd
commit cfdaaa4464
17 changed files with 1363 additions and 120 deletions

View file

@ -791,6 +791,11 @@
"showMapControls": false,
"showMapLabels": false,
"showFolder": {
"edits": {
"personal": true,
"favorite": true,
"featured": true
}
"items": {
"personal": true,
"favorite": true,

View file

@ -829,6 +829,11 @@
"showMapControls": false,
"showMapLabels": false,
"showFolder": {
"edits": {
"personal": true,
"favorite": true,
"featured": true
},
"items": {
"personal": true,
"favorite": true,

View file

@ -713,6 +713,12 @@
"showMapControls": false,
"showMapLabels": false,
"showFolder": {
"edits": {
"personal": true,
"favorite": true,
"featured": true,
"volumes": true
},
"items": {
"personal": true,
"favorite": true,

View file

@ -632,6 +632,12 @@
"showMapControls": false,
"showMapLabels": false,
"showFolder": {
"edits": {
"personal": true,
"favorite": true,
"featured": true,
"volumes": true
},
"items": {
"personal": true,
"favorite": true,

133
pandora/edit/managers.py Normal file
View file

@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from django.db.models import Q, Manager
def parseCondition(condition, user):
'''
'''
k = condition.get('key', 'name')
k = {
'user': 'user__username',
'position': 'position__position',
'posterFrames': 'poster_frames',
}.get(k, k)
if not k:
k = 'name'
v = condition['value']
op = condition.get('operator')
if not op:
op = '='
if op.startswith('!'):
op = op[1:]
exclude = True
else:
exclude = False
if k == 'id':
v = v.split(":")
if len(v) >= 2:
v = (v[0], ":".join(v[1:]))
q = Q(user__username=v[0], name=v[1])
else:
q = Q(id__in=[])
return q
if k == 'subscribed':
key = 'subscribed_users__username'
v = user.username
elif isinstance(v, bool): #featured and public flag
key = k
else:
key = "%s%s" % (k, {
'==': '__iexact',
'^': '__istartswith',
'$': '__iendswith',
}.get(op, '__icontains'))
key = str(key)
if exclude:
q = ~Q(**{key: v})
else:
q = Q(**{key: v})
return q
def parseConditions(conditions, operator, user):
'''
conditions: [
{
value: "war"
}
{
key: "year",
value: "1970-1980,
operator: "!="
},
{
key: "country",
value: "f",
operator: "^"
}
],
operator: "&"
'''
conn = []
for condition in conditions:
if 'conditions' in condition:
q = parseConditions(condition['conditions'],
condition.get('operator', '&'), user)
if q:
conn.append(q)
pass
else:
conn.append(parseCondition(condition, user))
if conn:
q = conn[0]
for c in conn[1:]:
if operator == '|':
q = q | c
else:
q = q & c
return q
return None
class EditManager(Manager):
def get_query_set(self):
return super(EditManager, self).get_query_set()
def find(self, data, user):
'''
query: {
conditions: [
{
value: "war"
}
{
key: "year",
value: "1970-1980,
operator: "!="
},
{
key: "country",
value: "f",
operator: "^"
}
],
operator: "&"
}
'''
#join query with operator
qs = self.get_query_set()
conditions = parseConditions(data['query'].get('conditions', []),
data['query'].get('operator', '&'),
user)
if conditions:
qs = qs.filter(conditions)
if user.is_anonymous():
qs = qs.filter(Q(status='public') | Q(status='featured'))
else:
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
return qs

View file

@ -0,0 +1,321 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Position'
db.create_table('edit_position', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('edit', self.gf('django.db.models.fields.related.ForeignKey')(related_name='position', to=orm['edit.Edit'])),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='edit_position', to=orm['auth.User'])),
('section', self.gf('django.db.models.fields.CharField')(max_length='255')),
('position', self.gf('django.db.models.fields.IntegerField')(default=0)),
))
db.send_create_signal('edit', ['Position'])
# Adding unique constraint on 'Position', fields ['user', 'edit', 'section']
db.create_unique('edit_position', ['user_id', 'edit_id', 'section'])
# Deleting field 'Clip.position'
db.delete_column('edit_clip', 'position')
# Deleting field 'Clip.edit_position'
db.delete_column('edit_clip', 'edit_position')
# Adding field 'Clip.index'
db.add_column('edit_clip', 'index',
self.gf('django.db.models.fields.IntegerField')(default=0),
keep_default=False)
# Adding field 'Clip.annotation'
db.add_column('edit_clip', 'annotation',
self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='editclip', null=True, to=orm['annotation.Annotation']),
keep_default=False)
# Changing field 'Clip.item'
db.alter_column('edit_clip', 'item_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['item.Item']))
# Deleting field 'Edit.public'
db.delete_column('edit_edit', 'public')
# Deleting field 'Edit.duration'
db.delete_column('edit_edit', 'duration')
# Adding field 'Edit.status'
db.add_column('edit_edit', 'status',
self.gf('django.db.models.fields.CharField')(default='private', max_length=20),
keep_default=False)
# Adding field 'Edit.description'
db.add_column('edit_edit', 'description',
self.gf('django.db.models.fields.TextField')(default=''),
keep_default=False)
# Adding field 'Edit.rightslevel'
db.add_column('edit_edit', 'rightslevel',
self.gf('django.db.models.fields.IntegerField')(default=0, db_index=True),
keep_default=False)
# Adding field 'Edit.icon'
db.add_column('edit_edit', 'icon',
self.gf('django.db.models.fields.files.ImageField')(default=None, max_length=100, null=True, blank=True),
keep_default=False)
# Adding field 'Edit.poster_frames'
db.add_column('edit_edit', 'poster_frames',
self.gf('ox.django.fields.TupleField')(default=[]),
keep_default=False)
# Adding M2M table for field subscribed_users on 'Edit'
db.create_table('edit_edit_subscribed_users', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('edit', models.ForeignKey(orm['edit.edit'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('edit_edit_subscribed_users', ['edit_id', 'user_id'])
def backwards(self, orm):
# Removing unique constraint on 'Position', fields ['user', 'edit', 'section']
db.delete_unique('edit_position', ['user_id', 'edit_id', 'section'])
# Deleting model 'Position'
db.delete_table('edit_position')
# Adding field 'Clip.position'
db.add_column('edit_clip', 'position',
self.gf('django.db.models.fields.IntegerField')(default=0),
keep_default=False)
# Adding field 'Clip.edit_position'
db.add_column('edit_clip', 'edit_position',
self.gf('django.db.models.fields.FloatField')(default=0),
keep_default=False)
# Deleting field 'Clip.index'
db.delete_column('edit_clip', 'index')
# Deleting field 'Clip.annotation'
db.delete_column('edit_clip', 'annotation_id')
# Changing field 'Clip.item'
db.alter_column('edit_clip', 'item_id', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['item.Item']))
# Adding field 'Edit.public'
db.add_column('edit_edit', 'public',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
# Adding field 'Edit.duration'
db.add_column('edit_edit', 'duration',
self.gf('django.db.models.fields.FloatField')(default=0),
keep_default=False)
# Deleting field 'Edit.status'
db.delete_column('edit_edit', 'status')
# Deleting field 'Edit.description'
db.delete_column('edit_edit', 'description')
# Deleting field 'Edit.rightslevel'
db.delete_column('edit_edit', 'rightslevel')
# Deleting field 'Edit.icon'
db.delete_column('edit_edit', 'icon')
# Deleting field 'Edit.poster_frames'
db.delete_column('edit_edit', 'poster_frames')
# Removing M2M table for field subscribed_users on 'Edit'
db.delete_table('edit_edit_subscribed_users')
models = {
'annotation.annotation': {
'Meta': {'object_name': 'Annotation'},
'clip': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotations'", 'null': 'True', 'to': "orm['clip.Clip']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'end': ('django.db.models.fields.FloatField', [], {'default': '-1', 'db_index': 'True'}),
'findvalue': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'item': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotations'", 'to': "orm['item.Item']"}),
'layer': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'public_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'unique': 'True', 'null': 'True'}),
'sortvalue': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '1000', 'null': 'True', 'blank': 'True'}),
'start': ('django.db.models.fields.FloatField', [], {'default': '-1', 'db_index': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'value': ('django.db.models.fields.TextField', [], {})
},
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '255', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
},
'clip.clip': {
'Meta': {'unique_together': "(('item', 'start', 'end'),)", 'object_name': 'Clip'},
'aspect_ratio': ('django.db.models.fields.FloatField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'duration': ('django.db.models.fields.FloatField', [], {'default': '0', 'db_index': 'True'}),
'end': ('django.db.models.fields.FloatField', [], {'default': '-1'}),
'findvalue': ('django.db.models.fields.TextField', [], {'null': 'True', 'db_index': 'True'}),
'hue': ('django.db.models.fields.FloatField', [], {'default': '0', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'item': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clips'", 'to': "orm['item.Item']"}),
'keywords': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'lightness': ('django.db.models.fields.FloatField', [], {'default': '0', 'db_index': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'notes': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'saturation': ('django.db.models.fields.FloatField', [], {'default': '0', 'db_index': 'True'}),
'sort': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'matching_clips'", 'to': "orm['item.ItemSort']"}),
'sortvalue': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'start': ('django.db.models.fields.FloatField', [], {'default': '-1', 'db_index': 'True'}),
'subtitles': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'user': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_index': 'True'}),
'volume': ('django.db.models.fields.FloatField', [], {'default': '0', 'null': 'True', 'db_index': 'True'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'edit.clip': {
'Meta': {'object_name': 'Clip'},
'annotation': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'editclip'", 'null': 'True', 'to': "orm['annotation.Annotation']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'edit': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clips'", 'to': "orm['edit.Edit']"}),
'end': ('django.db.models.fields.FloatField', [], {'default': '0'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'index': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'item': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'editclip'", 'null': 'True', 'to': "orm['item.Item']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'start': ('django.db.models.fields.FloatField', [], {'default': '0'})
},
'edit.edit': {
'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Edit'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''"}),
'icon': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'poster_frames': ('ox.django.fields.TupleField', [], {'default': '[]'}),
'rightslevel': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}),
'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_edits'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'edit.position': {
'Meta': {'unique_together': "(('user', 'edit', 'section'),)", 'object_name': 'Position'},
'edit': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['edit.Edit']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'section': ('django.db.models.fields.CharField', [], {'max_length': "'255'"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_position'", 'to': "orm['auth.User']"})
},
'item.item': {
'Meta': {'object_name': 'Item'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'data': ('ox.django.fields.DictField', [], {'default': '{}'}),
'external_data': ('ox.django.fields.DictField', [], {'default': '{}'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'items'", 'blank': 'True', 'to': "orm['auth.Group']"}),
'icon': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'itemId': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'blank': 'True'}),
'json': ('ox.django.fields.DictField', [], {'default': '{}'}),
'level': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'oxdbId': ('django.db.models.fields.CharField', [], {'max_length': '42', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'poster': ('django.db.models.fields.files.ImageField', [], {'default': 'None', 'max_length': '100', 'blank': 'True'}),
'poster_frame': ('django.db.models.fields.FloatField', [], {'default': '-1'}),
'poster_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'poster_source': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'poster_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'rendered': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'stream_aspect': ('django.db.models.fields.FloatField', [], {'default': '1.3333333333333333'}),
'stream_info': ('ox.django.fields.DictField', [], {'default': '{}'}),
'torrent': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '1000', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'items'", 'null': 'True', 'to': "orm['auth.User']"})
},
'item.itemsort': {
'Meta': {'object_name': 'ItemSort'},
'accessed': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'aspectratio': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'bitrate': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'cinematographer': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'codirector': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'color': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'composer': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'country': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'cutsperminute': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'director': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'duration': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'editor': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'genre': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'height': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'hue': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'imdbId': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'item': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'sort'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['item.Item']"}),
'itemId': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'lightness': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'lyricist': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'numberofactors': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'numberofcuts': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'numberoffiles': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'parts': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'pixels': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'producer': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'productionCompany': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'random': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'resolution': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'rightslevel': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'runtime': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'saturation': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'size': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'sound': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'timesaccessed': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'volume': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'width': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'words': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'wordsperminute': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'writer': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True', 'db_index': 'True'}),
'year': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'db_index': 'True'})
}
}
complete_apps = ['edit']

View file

@ -2,51 +2,318 @@
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
from django.db import models
from django.contrib.auth.models import User
import re
import os
import shutil
import ox
from django.conf import settings
from django.db import models
from django.db.models import Max
from django.contrib.auth.models import User
from ox.django.fields import TupleField
from annotation.models import Annotation
from item.models import Item
from archive import extract
import managers
class Edit(models.Model):
class Meta:
unique_together = ("user", "name")
objects = managers.EditManager()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User)
name = models.CharField(max_length=255)
public = models.BooleanField(default=False)
duration = models.FloatField(default=0)
#FIXME: how to deal with width/height?
status = models.CharField(max_length=20, default='private')
_status = ['private', 'public', 'featured']
description = models.TextField(default='')
rightslevel = models.IntegerField(db_index=True, default=0)
icon = models.ImageField(default=None, blank=True, null=True,
upload_to=lambda i, x: i.path("icon.jpg"))
poster_frames = TupleField(default=[], editable=False)
subscribed_users = models.ManyToManyField(User, related_name='subscribed_edits')
def __unicode__(self):
return u'%s (%s)' % (self.title, self.user)
def get_id(self):
return u'%s:%s' % (self.user.username, self.name)
def get_absolute_url(self):
return ('/edits/%s' % quote(self.get_id())).replace('%3A', ':')
def add_clip(self, data):
clip = Clip(edit=self)
if 'annotation' in data:
clip.annotation = Annotation.objects.get(public_id=data['annotation'])
else:
clip.item = Item.objects.get(itemId=data['item'])
clip.start = data['in']
clip.end = data['out']
clip.index = Clip.objects.filter(edit=self).aggregate(Max('index'))['index__max']
if clip.index == None:
clip.index = 0
else:
clip.index +=1
clip.save()
return clip
def accessible(self, user):
return self.user == user or self.status in ('public', 'featured')
def editable(self, user):
#FIXME: make permissions work
if self.user == user or user.is_staff:
if not user or user.is_anonymous():
return False
if self.user == user or \
user.is_staff or \
user.get_profile().capability('canEditFeaturedEdits') == True:
return True
return False
'''
#creating a new file from clips seams to work not to bad, needs testing for frame accuracy
ffmpeg -i 96p.webm -ss 123.33 -t 3 -vcodec copy -acodec copy 1.webm
ffmpeg -i 96p.webm -ss 323.33 -t 4 -vcodec copy -acodec copy 2.webm
ffmpeg -i 96p.webm -ss 423.33 -t 1 -vcodec copy -acodec copy 3.webm
mkvmerge 1.webm +2.webm +3.webm -o cutup.webm
'''
def edit(self, data, user):
for key in data:
if key == 'status':
value = data[key]
if value not in self._status:
value = self._status[0]
if value == 'private':
for user in self.subscribed_users.all():
self.subscribed_users.remove(user)
qs = Position.objects.filter(user=user,
section='section', edit=self)
if qs.count() > 1:
pos = qs[0]
pos.section = 'personal'
pos.save()
elif value == 'featured':
if user.get_profile().capability('canEditFeaturedEdits'):
pos, created = Position.objects.get_or_create(edit=self, user=user,
section='featured')
if created:
qs = Position.objects.filter(user=user, section='featured')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
Position.objects.filter(edit=self).exclude(id=pos.id).delete()
else:
value = self.status
elif self.status == 'featured' and value == 'public':
Position.objects.filter(edit=self).delete()
pos, created = Position.objects.get_or_create(edit=self,
user=self.user,section='personal')
qs = Position.objects.filter(user=self.user,
section='personal')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
for u in self.subscribed_users.all():
pos, created = Position.objects.get_or_create(edit=self, user=u,
section='public')
qs = Position.objects.filter(user=u, section='public')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
self.status = value
elif key == 'name':
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
if not data['name']:
data['name'] = "Untitled"
name = data['name']
num = 1
while Edit.objects.filter(name=name, user=self.user).exclude(id=self.id).count()>0:
num += 1
name = data['name'] + ' [%d]' % num
self.name = name
elif key == 'description':
self.description = ox.sanitize_html(data['description'])
elif key == 'rightslevel':
self.rightslevel = int(data['rightslevel'])
if 'position' in data:
pos, created = Position.objects.get_or_create(edit=self, user=user)
pos.position = data['position']
pos.section = 'featured'
if self.status == 'private':
pos.section = 'personal'
pos.save()
if 'type' in data:
self.type = data['type'] == 'pdf' and 'pdf' or 'html'
if 'posterFrames' in data:
self.poster_frames = tuple(data['posterFrames'])
self.save()
if 'posterFrames' in data:
self.update_icon()
def path(self, name=''):
h = "%07d" % self.id
return os.path.join('edits', h[:2], h[2:4], h[4:6], h[6:], name)
def get_items(self, user=None):
return Item.objects.filter(editclips__id__in=self.clips.all()).distinct()
def update_icon(self):
frames = []
if not self.poster_frames:
items = self.get_items(self.user).filter(rendered=True)
if items.count():
poster_frames = []
for i in range(0, items.count(), max(1, int(items.count()/4))):
poster_frames.append({
'item': items[int(i)].itemId,
'position': items[int(i)].poster_frame
})
self.poster_frames = tuple(poster_frames)
self.save()
for i in self.poster_frames:
s = Item.objects.filter(itemId=i['item'])
if qs.count() > 0:
frame = qs[0].frame(i['position'])
if frame:
frames.append(frame)
self.icon.name = self.path('icon.jpg')
icon = self.icon.path
if frames:
while len(frames) < 4:
frames += frames
folder = os.path.dirname(icon)
ox.makedirs(folder)
for f in glob("%s/icon*.jpg" % folder):
os.unlink(f)
cmd = [
settings.LIST_ICON,
'-f', ','.join(frames),
'-o', icon
]
p = subprocess.Popen(cmd)
p.wait()
self.save()
def get_icon(self, size=16):
path = self.path('icon%d.jpg' % size)
path = os.path.join(settings.MEDIA_ROOT, path)
if not os.path.exists(path):
folder = os.path.dirname(path)
ox.makedirs(folder)
if self.icon and os.path.exists(self.icon.path):
source = self.icon.path
max_size = min(self.icon.width, self.icon.height)
else:
source = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
max_size = 256
if size < max_size:
extract.resize_image(source, path, size=size)
else:
path = source
return path
def json(self, keys=None, user=None):
if not keys:
keys=[
'description',
'editable',
'rightslevel',
'id',
'clips',
'name',
'posterFrames',
'status',
'subscribed',
'user'
]
response = {
'type': 'static'
}
_map = {
'posterFrames': 'poster_frames'
}
for key in keys:
if key == 'id':
response[key] = self.get_id()
elif key == 'clips':
response[key] = [c.json(user) for c in self.clips.all().order_by('index')]
elif key == 'editable':
response[key] = self.editable(user)
elif key == 'user':
response[key] = self.user.username
elif key == 'subscribers':
response[key] = self.subscribed_users.all().count()
elif key == 'subscribed':
if user and not user.is_anonymous():
response[key] = self.subscribed_users.filter(id=user.id).exists()
elif hasattr(self, _map.get(key, key)):
response[key] = getattr(self, _map.get(key,key))
return response
def render(self):
#creating a new file from clips
tmp = tempfile.mkdtemp()
clips = []
for clip in self.clips.all().order_by('index'):
data = clip.json()
clips.append(os.path.join(tmp, '%06d.webm' % data['index']))
cmd = ['avconv', '-i', path,
'-ss', data['in'], '-t', data['out'],
'-vcodec', 'copy', '-acodec', 'copy',
clips[-1]]
#p = subprocess.Popen(cmd)
#p.wait()
cmd = ['mkvmerge', clips[0]] \
+ ['+'+c for c in clips[1:]] \
+ [os.path.join(tmp, 'render.webm')]
#p = subprocess.Popen(cmd)
#p.wait()
shutil.rmtree(tmp)
class Clip(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
edit = models.ForeignKey(Edit)
position = models.IntegerField(default=0) #clip position
edit_position = models.FloatField(default=0) #Position in seconds on edit
item = models.ForeignKey("item.Item")
edit = models.ForeignKey(Edit, related_name='clips')
index = models.IntegerField(default=0)
item = models.ForeignKey(Item, null=True, default=None, related_name='editclip')
annotation = models.ForeignKey(Annotation, null=True, default=None, related_name='editclip')
start = models.FloatField(default=0)
end = models.FloatField(default=0)
def __unicode__(self):
if self.annotation:
return u'%s' % self.annotation.public_id
return u'%s/%0.3f-%0.3f' % (self.item.itemId, self.start, self.end)
def json(self, user=None):
data = {
'id': ox.toAZ(self.id),
'index': self.index
}
if self.annotation:
data['annotation'] = self.annotation.public_id
data['in'] = self.annotation.start
data['out'] = self.annotation.end
else:
data['item'] = self.item.itemId
data['in'] = self.start
data['out'] = self.end
data['duration'] = data['out'] - data['in']
return data
class Position(models.Model):
class Meta:
unique_together = ("user", "edit", "section")
edit = models.ForeignKey(Edit, related_name='position')
user = models.ForeignKey(User, related_name='edit_position')
section = models.CharField(max_length='255')
position = models.IntegerField(default=0)
def __unicode__(self):
return u'%s/%s/%s' % (self.section, self.position, self.edit)

View file

@ -1,41 +1,49 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
import os
import re
import ox
from ox.utils import json
from ox.django.decorators import login_required_json
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from django.db.models import Max
from ox.django.http import HttpFileResponse
from ox.django.api import actions
from item import utils
import models
def get_edit_or_404_json(id):
id = id.split(':')
username = id[0]
name = ":".join(id[1:])
return get_object_or_404_json(models.Edit, user__username=username, name=name)
@login_required_json
def addClip(request):
'''
takes {
item: string,
edit: string,
start: float,
end: float,
item: string,
in: float,
out: float,
annotation: string
}
add clip with item/in/out or annotation to edit with id
returns {
}
'''
response = json_response()
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if 'item' in data:
item = get_object_or_404_json(models.Item, pk=data['item'])
if list.editable(request.user):
list.add(item)
response = json_response(status=200, text='item added')
else:
response = json_response(status=403, text='not allowed')
elif 'query' in data:
response = json_response(status=501, text='not implemented')
edit = get_edit_or_404_json(data['edit'])
if edit.editable(request.user):
clip = edit.add_clip(data)
response['data'] = clip.json(request.user)
else:
response = json_response(status=501, text='not implemented')
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(addClip, cache=False)
@ -44,112 +52,172 @@ actions.register(addClip, cache=False)
def removeClip(request):
'''
takes {
item: string
edit: string
ids: [string]
}
returns {
}
'''
response = json_response()
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if 'item' in data:
item = get_object_or_404_json(models.Item, pk=data['item'])
if list.editable(request.user):
list.remove(item)
response = json_response(status=200, text='item removed')
else:
response = json_response(status=403, text='not allowed')
elif 'query' in data:
response = json_response(status=501, text='not implemented')
edit = get_edit_or_404_json(data['edit'])
if 'id' in data:
ids = [data['id']]
else:
response = json_response(status=501, text='not implemented')
ids = data['ids']
ids = map(ox.fromAZ, ids)
if edit.editable(request.user):
for clip in edit.clips.filter(id__in=ids):
clip.delete()
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(removeClip, cache=False)
def getTimeline(request):
'''
takes {
name: string,
user: string
}
returns {
...
}
could be
timeline: {
0: {
itemId:, start, end
},
123: {
itemId:, start, end
}
}
or implicit timeline position
timeline: [
{
itemId:, start, end
},
{
itemId:, start, end
}
]
'''
response = json_response(status=501, text='not implemented')
return render_to_json_response(response)
actions.register(getTimeline)
@login_required_json
def addTimeline(request):
def editClip(request):
'''
takes {
...
takes {
id: string,
in: float
}
returns {
...
}
'''
response = json_response()
data = json.loads(request.POST['data'])
if models.Timeline.filter(name=data['name'], user=request.user).count() == 0:
list = models.Timeline(name=data['name'], user=request.user)
list.save()
response = json_response(status=200, text='created')
clip = get_object_or_404_json(models.Clip, pk=ox.fromAZ(data['id']))
if clip.edit.editable(request.user):
for key in ('in', 'out'):
if key in data:
if clip.annotation:
clip.start = clip.annotation.start
clip.end = clip.annotation.end
clip.annotation = None
setattr(clip, {'in': 'start', 'out': 'end'}.get(key), float(data[key]))
clip.save()
response['data'] = clip.json(user=request.user)
else:
response = json_response(status=200, text='list already exists')
response['data']['errors'] = {
'name': 'List already exists'
}
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(addTimeline, cache=False)
actions.register(editClip, cache=False)
@login_required_json
def editTimeline(request):
def sortClips(request):
'''
takes {
edit: string
ids: [string]
}
returns {
}
'''
data = json.loads(request.POST['data'])
edit = get_edit_or_404_json(data['edit'])
response = json_response()
ids = map(ox.fromAZ, data['ids'])
if edit.editable(request.user):
index = 0
for i in ids:
models.Clip.objects.filter(edit=edit, id=i).update(index=index)
index += 1
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(sortClips, cache=False)
def getEdit(request):
'''
takes {
...
id:
}
returns {
id:
clips:
}
'''
data = json.loads(request.POST['data'])
if 'id' in data:
edit = get_edit_or_404_json(data['id'])
response = json_response()
if edit.accessible(request.user):
response['data'] = edit.json(user=request.user)
else:
response = json_response(status=403, text='not allowed')
else:
response = json_response(status=404, text='not found')
return render_to_json_response(response)
actions.register(getEdit)
@login_required_json
def addEdit(request):
'''
takes {
name
}
returns {
...
}
'''
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if list.editable(request.user):
for key in data:
if key in ('name', 'public'):
setattr(list, key, data['key'])
data['name'] = re.sub(' \[\d+\]$', '', data.get('name', 'Untitled')).strip()
name = data['name']
if not name:
name = "Untitled"
num = 1
created = False
while not created:
edit, created = models.Edit.objects.get_or_create(name=name, user=request.user)
num += 1
name = data['name'] + ' [%d]' % num
del data['name']
if data:
edit.edit(data, request.user)
else:
edit.save()
if edit.status == 'featured':
pos, created = models.Position.objects.get_or_create(edit=edit,
user=request.user, section='featured')
qs = models.Position.objects.filter(section='featured')
else:
pos, created = models.Position.objects.get_or_create(edit=edit,
user=request.user, section='personal')
qs = models.Position.objects.filter(user=request.user, section='personal')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
response = json_response(status=200, text='created')
response['data'] = edit.json(user=request.user)
return render_to_json_response(response)
actions.register(addEdit, cache=False)
@login_required_json
def editEdit(request):
'''
takes {
id
}
returns {
...
}
'''
data = json.loads(request.POST['data'])
edit = get_edit_or_404_json(data['id'])
response = json_response()
if edit.editable(request.user):
edit.edit(data, request.user)
response['data'] = edit.json(keys=[
'description', 'editable', 'rightslevel',
'id', 'name', 'posterFrames', 'status',
'subscribed', 'user'
], user=request.user)
else:
response = json_response(status=403, text='not allowed')
return render_to_json_response(response)
actions.register(editTimeline, cache=False)
actions.register(editEdit, cache=False)
@login_required_json
def removeTimeline(request):
def removeEdit(request):
'''
takes {
...
@ -159,10 +227,211 @@ def removeTimeline(request):
}
'''
data = json.loads(request.POST['data'])
list = get_object_or_404_json(models.Timeline, pk=data['list'])
if list.editable(request.user):
list.delete()
edit = get_edit_or_404_json(data['id'])
response = json_response()
if edit.editable(request.user):
edit.delete()
else:
response = json_response(status=403, text='not allowed')
return render_to_json_response(response)
actions.register(removeTimeline, cache=False)
actions.register(removeEdit, cache=False)
def _order_query(qs, sort):
order_by = []
for e in sort:
operator = e['operator']
if operator != '-':
operator = ''
key = {
'subscribed': 'subscribed_users',
'items': 'numberofitems'
}.get(e['key'], e['key'])
order = '%s%s' % (operator, key)
order_by.append(order)
if key == 'subscribers':
qs = qs.annotate(subscribers=Sum('subscribed_users'))
if order_by:
qs = qs.order_by(*order_by)
qs = qs.distinct()
return qs
def parse_query(data, user):
query = {}
query['range'] = [0, 100]
query['sort'] = [{'key':'user', 'operator':'+'}, {'key':'name', 'operator':'+'}]
for key in ('keys', 'group', 'edit', 'range', 'position', 'positions', 'sort'):
if key in data:
query[key] = data[key]
query['qs'] = models.Edit.objects.find(data, user).exclude(name='')
return query
def findEdits(request):
'''
takes {
query: {
conditions: [
{
key: 'user',
value: 'something',
operator: '='
}
]
operator: ","
},
sort: [{key: 'name', operator: '+'}],
range: [0, 100]
keys: []
}
possible query keys:
name, user, featured, subscribed
possible keys:
name, user, featured, subscribed, query
}
returns {
items: [object]
}
'''
data = json.loads(request.POST['data'])
query = parse_query(data, request.user)
#order
is_section_request = query['sort'] == [{u'operator': u'+', u'key': u'position'}]
def is_featured_condition(x):
return x['key'] == 'status' and \
x['value'] == 'featured' and \
x['operator'] in ('=', '==')
is_featured = len(filter(is_featured_condition, data['query'].get('conditions', []))) > 0
if is_section_request:
qs = query['qs']
if not is_featured and not request.user.is_anonymous():
qs = qs.filter(position__in=models.Position.objects.filter(user=request.user))
qs = qs.order_by('position__position')
else:
qs = _order_query(query['qs'], query['sort'])
response = json_response()
if 'keys' in data:
qs = qs[query['range'][0]:query['range'][1]]
response['data']['items'] = [l.json(data['keys'], request.user) for l in qs]
elif 'position' in data:
#FIXME: actually implement position requests
response['data']['position'] = 0
elif 'positions' in data:
ids = [i.get_id() for i in qs]
response['data']['positions'] = utils.get_positions(ids, query['positions'])
else:
response['data']['items'] = qs.count()
return render_to_json_response(response)
actions.register(findEdits)
@login_required_json
def subscribeToEdit(request):
'''
takes {
id: string,
}
returns {}
'''
data = json.loads(request.POST['data'])
edit = get_edit_or_404_json(data['id'])
user = request.user
if edit.status == 'public' and \
edit.subscribed_users.filter(username=user.username).count() == 0:
edit.subscribed_users.add(user)
pos, created = models.Position.objects.get_or_create(edit=edit, user=user, section='public')
if created:
qs = models.Position.objects.filter(user=user, section='public')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
response = json_response()
return render_to_json_response(response)
actions.register(subscribeToEdit, cache=False)
@login_required_json
def unsubscribeFromEdit(request):
'''
takes {
id: string,
user: username(only admins)
}
returns {}
'''
data = json.loads(request.POST['data'])
edit = get_edit_or_404_json(data['id'])
user = request.user
edit.subscribed_users.remove(user)
models.Position.objects.filter(edit=edit, user=user, section='public').delete()
response = json_response()
return render_to_json_response(response)
actions.register(unsubscribeFromEdit, cache=False)
@login_required_json
def sortEdits(request):
'''
takes {
section: 'personal',
ids: [1,2,4,3]
}
known sections: 'personal', 'public', 'featured'
featured can only be edited by admins
returns {}
'''
data = json.loads(request.POST['data'])
position = 0
section = data['section']
#ids = list(set(data['ids']))
ids = data['ids']
if section == 'featured' and not request.user.get_profile().capability('canEditFeaturedEdits'):
response = json_response(status=403, text='not allowed')
else:
user = request.user
if section == 'featured':
for i in ids:
l = get_edit_or_404_json(i)
qs = models.Position.objects.filter(section=section, edit=l)
if qs.count() > 0:
pos = qs[0]
else:
pos = models.Position(edit=l, user=user, section=section)
if pos.position != position:
pos.position = position
pos.save()
position += 1
models.Position.objects.filter(section=section, edit=l).exclude(id=pos.id).delete()
else:
for i in ids:
l = get_edit_or_404_json(i)
pos, created = models.Position.objects.get_or_create(edit=l,
user=request.user, section=section)
if pos.position != position:
pos.position = position
pos.save()
position += 1
response = json_response()
return render_to_json_response(response)
actions.register(sortEdits, cache=False)
def icon(request, id, size=16):
if not size:
size = 16
id = id.split(':')
username = id[0]
name = ":".join(id[1:])
qs = models.Edit.objects.filter(user__username=username, name=name)
if qs.count() == 1 and qs[0].accessible(request.user):
edit = qs[0]
icon = edit.get_icon(int(size))
else:
icon = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
return HttpFileResponse(icon, content_type='image/jpeg')

View file

@ -21,8 +21,8 @@ import models
def get_text_or_404_json(id):
id = id.split(':')
username = id[0]
textname = ":".join(id[1:])
return get_object_or_404_json(models.Text, user__username=username, name=textname)
name = ":".join(id[1:])
return get_object_or_404_json(models.Text, user__username=username, name=name)
@login_required_json
def addText(request):

View file

@ -32,6 +32,7 @@ urlpatterns = patterns('',
(r'^resetUI$', 'user.views.reset_ui'),
(r'^documents/(?P<id>.*?.pdf).jpg$', 'document.views.thumbnail'),
(r'^documents/(?P<id>.*?.)$', 'document.views.file'),
(r'^edit/(?P<id>.*?)/icon(?P<size>\d*).jpg$', 'edit.views.icon'),
(r'^list/(?P<id>.*?)/icon(?P<size>\d*).jpg$', 'itemlist.views.icon'),
(r'^text/(?P<id>.*?)/icon(?P<size>\d*).jpg$', 'text.views.icon'),
(r'^texts/(?P<id>.*?)/text.pdf$', 'text.views.pdf'),

View file

@ -317,6 +317,20 @@ pandora.URL = (function() {
item: {}
};
// Edits
views['edits'] = {
list: [],
item: ['edit']
};
spanType['edits'] = {
list: [],
item: {edit: 'number'}
};
sortKeys['edits'] = {
list: {},
item: {}
};
findKeys = [{id: 'list', type: 'string'}].concat(pandora.site.itemKeys);
self.URL = Ox.URL({

View file

@ -0,0 +1,200 @@
'use strict';
pandora.ui.editPanel = function() {
var that = Ox.SplitPanel({
elements: [
{element: Ox.Element(), size: 24},
{element: Ox.Element()},
{element: Ox.Element(), size: 16}
],
orientation: 'vertical'
});
pandora.user.ui.edit && render();
function render() {
pandora.api.getEdit({id: pandora.user.ui.edit}, function(result) {
var edit = result.data;
var $toolbar = Ox.Bar({size: 24}),
$editMenu,
$statusbar = Ox.Bar({size: 16}),
$panel = Ox.SplitPanel({
elements: [
{
element: pandora.$ui.edit = pandora.ui.editList(edit)
},
{
element: Ox.Element(),
size: 0,
resizable: false
}
],
orientation: 'horizontal'
});
that.replaceElement(0, $toolbar);
that.replaceElement(1, $panel);
that.replaceElement(2, $statusbar);
});
}
that.reload = function() {
render();
}
return that;
};
pandora.ui.editList = function(edit) {
var height = getHeight(),
width = getWidth(),
that = Ox.Element()
.css({
'overflow-y': 'auto'
});
self.$list = Ox.TableList({
columns: [
{
align: 'left',
id: 'index',
operator: '+',
title: Ox._('Index'),
visible: false,
width: 60
},
{
align: 'left',
id: 'id',
operator: '+',
title: Ox._('ID'),
visible: false,
width: 60
},
{
align: 'left',
id: 'item',
operator: '+',
title: Ox._(pandora.site.itemName.singular),
visible: true,
width: 360
},
{
editable: true,
id: 'in',
operator: '+',
title: Ox._('In'),
visible: true,
width: 60
},
{
editable: true,
id: 'out',
operator: '+',
title: Ox._('Out'),
visible: true,
width: 60
},
{
id: 'duration',
operator: '+',
title: Ox._('Duration'),
visible: true,
width: 60
}
],
columnsMovable: true,
columnsRemovable: true,
columnsResizable: true,
columnsVisible: true,
items: edit.clips,
scrollbarVisible: true,
sort: [{key: 'index', operator: '+'}],
sortable: true,
unique: 'id'
})
.appendTo(that)
.bindEvent({
add: function(data) {
if(pandora.user.ui.item) {
pandora.api.addClip({
edit: pandora.user.ui.edit,
item: pandora.user.ui.item,
'in': pandora.user.ui.videoPoints[pandora.user.ui.item]['in'],
out: pandora.user.ui.videoPoints[pandora.user.ui.item].out,
}, function(result) {
Ox.Request.clearCache();
pandora.$ui.rightPanel.reload()
});
}
},
'delete': function(data) {
if (data.ids.length > 0 && edit.editable) {
pandora.api.removeClip({
ids: data.ids,
edit: pandora.user.ui.edit
}, function(result) {
Ox.Request.clearCache();
pandora.$ui.rightPanel.reload()
});
}
},
move: function(data) {
Ox.Request.clearCache();
pandora.api.sortClips({
edit: pandora.user.ui.edit,
ids: data.ids
})
},
select: function(data) {
},
submit: function(data) {
var value = self.$list.value(data.id, data.key);
if (data.value != value && !(data.value === '' && value === null)) {
self.$list.value(data.id, data.key, data.value || null);
var edit = {
id: data.id,
};
edit[data.key] = parseFloat(data.value);
pandora.api.editClip(edit, function(result) {
self.$list.value(data.id, data.key, result.data[data.key]);
self.$list.value(data.id, 'duration', result.data.duration);
});
}
}
});
function getHeight() {
// 24 menu + 24 toolbar + 16 statusbar + 32 title + 32 margins
// + 1px to ge trid of scrollbar
return window.innerHeight - 128 -1;
}
function getWidth() {
return window.innerWidth
- pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize - 1
- pandora.user.ui.embedSize - 1
- 32;
}
that.update = function() {
$text.options({
maxHeight: getHeight(),
width: getWidth()
});
return that;
};
return that;
};

View file

@ -20,6 +20,9 @@ pandora.ui.mainPanel = function() {
orientation: 'horizontal'
})
.bindEvent({
pandora_edit: function(data) {
that.replaceElement(1, pandora.$ui.rightPanel = pandora.ui.rightPanel());
},
pandora_find: function() {
var previousUI = pandora.UI.getPrevious();
Ox.Log('FIND', 'handled in mainPanel', previousUI.item, previousUI._list)

View file

@ -59,6 +59,8 @@ pandora.ui.rightPanel = function() {
});
} else if (pandora.user.ui.section == 'texts') {
that = pandora.$ui.textPanel = pandora.ui.textPanel();
} else if (pandora.user.ui.section == 'edits') {
that = pandora.$ui.editPanel = pandora.ui.editPanel();
}
return that;
};

View file

@ -3,8 +3,8 @@
pandora.ui.sectionButtons = function() {
var that = Ox.ButtonGroup({
buttons: [
{id: 'items', title: pandora.site.itemName.plural},
{id: 'edits', title: Ox._('Edits'), disabled: true},
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
{id: 'edits', title: Ox._('Edits'), disabled: pandora.user.level != 'admin'},
{id: 'texts', title: Ox._('Texts'), disabled: pandora.user.level != 'admin'}
],
id: 'sectionButtons',

View file

@ -5,9 +5,9 @@ pandora.ui.sectionSelect = function() {
var that = Ox.Select({
id: 'sectionSelect',
items: [
{id: 'items', title: pandora.site.itemName.plural},
{id: 'edits', title: Ox._('Edits'), disabled: true},
{id: 'texts', title: Ox._('Texts'), disabled: true}
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
{id: 'edits', title: Ox._('Edits'), disabled: pandora.user.level != 'admin'}
{id: 'texts', title: Ox._('Texts'), disabled: pandora.user.level != 'admin'}
],
value: pandora.user.ui.section
}).css({

View file

@ -785,6 +785,16 @@ pandora.getItem = function(state, str, callback) {
callback();
}
});
} else if (state.type == 'edits') {
pandora.api.getEdit({id: str}, function(result) {
if (result.status.code == 200) {
state.item = result.data.id;
callback();
} else {
state.item = '';
callback();
}
});
} else {
callback();
}
@ -1444,9 +1454,10 @@ pandora.selectList = function() {
});
}
} else {
var id = pandora.user.ui[pandora.user.ui.section.slice(0,-1)];
var id = pandora.user.ui[pandora.user.ui.section.slice(0,-1)],
section = Ox.toTitleCase(pandora.user.ui.section.slice(0, -1));
if (id) {
pandora.api.getText({id: id}, function(result) {
pandora.api['edit' + section]({id: id}, function(result) {
var folder;
if (result.data.id) {
folder = result.data.status == 'featured' ? 'featured' : (