initial round of text section

This commit is contained in:
j 2013-02-16 01:20:40 +00:00
parent f8ededaf55
commit c8af45e7cf
25 changed files with 2243 additions and 984 deletions

View file

@ -31,6 +31,8 @@
"canEditAnnotations": {"staff": true, "admin": true},
"canEditEvents": {"staff": true, "admin": true},
"canEditFeaturedLists": {"staff": true, "admin": true},
"canEditFeaturedTexts": {"staff": true, "admin": true},
"canEditFeaturedEdits": {"staff": true, "admin": true},
"canEditMetadata": {"staff": true, "admin": true},
"canEditPlaces": {"staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true},
@ -712,6 +714,8 @@
"icons": "posters",
"infoIconSize": 256,
"item": "",
"text": "",
"edit": "",
"itemFind": "",
"itemSort": [{"key": "position", "operator": "+"}],
"itemView": "info",
@ -749,6 +753,11 @@
"featured": true,
"volumes": true
}
"texts": {
"personal": true,
"favorite": true,
"featured": true
}
},
"showSidebar": true,
"showSitePosters": false,

View file

@ -29,6 +29,8 @@
"canEditAnnotations": {"staff": true, "admin": true},
"canEditEvents": {"staff": true, "admin": true},
"canEditFeaturedLists": {"staff": true, "admin": true},
"canEditFeaturedTexts": {"staff": true, "admin": true},
"canEditFeaturedEdits": {"staff": true, "admin": true},
"canEditMetadata": {"staff": true, "admin": true},
"canEditPlaces": {"staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true},
@ -630,6 +632,8 @@
"icons": "frames",
"infoIconSize": 256,
"item": "",
"text": "",
"edit": "",
"itemFind": "",
"itemSort": [{"key": "position", "operator": "+"}],
"itemView": "info",
@ -670,6 +674,11 @@
"favorite": true,
"featured": true,
"volumes": true
},
"texts": {
"personal": true,
"favorite": true,
"featured": true
}
},
"showSidebar": true,

View file

@ -29,6 +29,8 @@
"canEditAnnotations": {"staff": true, "admin": true},
"canEditEvents": {"staff": true, "admin": true},
"canEditFeaturedLists": {"staff": true, "admin": true},
"canEditFeaturedTexts": {"staff": true, "admin": true},
"canEditFeaturedEdits": {"staff": true, "admin": true},
"canEditMetadata": {"staff": true, "admin": true},
"canEditPlaces": {"staff": true, "admin": true},
"canEditSitePages": {"staff": true, "admin": true},
@ -549,6 +551,8 @@
"icons": "posters",
"infoIconSize": 256,
"item": "",
"text": "",
"edit": "",
"itemFind": "",
"itemSort": [{"key": "position", "operator": "+"}],
"itemView": "info",
@ -589,6 +593,11 @@
"featured": true,
"volumes": true
}
"texts": {
"personal": true,
"favorite": true,
"featured": true
}
},
"showSidebar": true,
"showSitePosters": false,

View file

@ -207,7 +207,9 @@ def addList(request):
return {
status: {'code': int, 'text': string},
data: {
list:
id:
name:
...
}
}
'''

136
pandora/text/managers.py Normal file
View file

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from datetime import datetime
from django.db.models import Q, Manager
import models
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 TextManager(Manager):
def get_query_set(self):
return super(TextManager, 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,205 @@
# -*- 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):
# Deleting model 'Image'
db.delete_table('text_image')
# Deleting model 'Attachment'
db.delete_table('text_attachment')
# Adding model 'Position'
db.create_table('text_position', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('text', self.gf('django.db.models.fields.related.ForeignKey')(related_name='position', to=orm['text.Text'])),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='text_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('text', ['Position'])
# Adding unique constraint on 'Position', fields ['user', 'text', 'section']
db.create_unique('text_position', ['user_id', 'text_id', 'section'])
# Deleting field 'Text.public'
db.delete_column('text_text', 'public')
# Deleting field 'Text.slug'
db.delete_column('text_text', 'slug')
# Deleting field 'Text.title'
db.delete_column('text_text', 'title')
# Deleting field 'Text.published'
db.delete_column('text_text', 'published')
# Adding field 'Text.name'
db.add_column('text_text', 'name',
self.gf('django.db.models.fields.CharField')(default=datetime.datetime(2013, 2, 15, 0, 0), max_length=255),
keep_default=False)
# Adding field 'Text.status'
db.add_column('text_text', 'status',
self.gf('django.db.models.fields.CharField')(default='private', max_length=20),
keep_default=False)
# Adding field 'Text.description'
db.add_column('text_text', 'description',
self.gf('django.db.models.fields.TextField')(default=''),
keep_default=False)
# Adding field 'Text.icon'
db.add_column('text_text', 'icon',
self.gf('django.db.models.fields.files.ImageField')(default='', max_length=100, blank=True),
keep_default=False)
# Adding field 'Text.poster_frames'
db.add_column('text_text', 'poster_frames',
self.gf('ox.django.fields.TupleField')(default=[]),
keep_default=False)
# Adding M2M table for field subscribed_users on 'Text'
db.create_table('text_text_subscribed_users', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('text', models.ForeignKey(orm['text.text'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('text_text_subscribed_users', ['text_id', 'user_id'])
# Adding unique constraint on 'Text', fields ['user', 'name']
db.create_unique('text_text', ['user_id', 'name'])
def backwards(self, orm):
# Removing unique constraint on 'Text', fields ['user', 'name']
db.delete_unique('text_text', ['user_id', 'name'])
# Removing unique constraint on 'Position', fields ['user', 'text', 'section']
db.delete_unique('text_position', ['user_id', 'text_id', 'section'])
# Adding model 'Image'
db.create_table('text_image', (
('caption', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal('text', ['Image'])
# Adding model 'Attachment'
db.create_table('text_attachment', (
('caption', self.gf('django.db.models.fields.CharField')(default='', max_length=255)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
))
db.send_create_signal('text', ['Attachment'])
# Deleting model 'Position'
db.delete_table('text_position')
# Adding field 'Text.public'
db.add_column('text_text', 'public',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
# Adding field 'Text.slug'
db.add_column('text_text', 'slug',
self.gf('django.db.models.fields.SlugField')(default='', max_length=50),
keep_default=False)
# Adding field 'Text.title'
db.add_column('text_text', 'title',
self.gf('django.db.models.fields.CharField')(max_length=1000, null=True),
keep_default=False)
# Adding field 'Text.published'
db.add_column('text_text', 'published',
self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now),
keep_default=False)
# Deleting field 'Text.name'
db.delete_column('text_text', 'name')
# Deleting field 'Text.status'
db.delete_column('text_text', 'status')
# Deleting field 'Text.description'
db.delete_column('text_text', 'description')
# Deleting field 'Text.icon'
db.delete_column('text_text', 'icon')
# Deleting field 'Text.poster_frames'
db.delete_column('text_text', 'poster_frames')
# Removing M2M table for field subscribed_users on 'Text'
db.delete_table('text_text_subscribed_users')
models = {
'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'})
},
'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'})
},
'text.position': {
'Meta': {'unique_together': "(('user', 'text', 'section'),)", 'object_name': 'Position'},
'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'"}),
'text': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['text.Text']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'text_position'", 'to': "orm['auth.User']"})
},
'text.text': {
'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Text'},
'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', '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': '[]'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}),
'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_texts'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'text': ('django.db.models.fields.TextField', [], {'default': "''"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'texts'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['text']

View file

@ -0,0 +1,93 @@
# -*- 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 field 'Text.type'
db.add_column('text_text', 'type',
self.gf('django.db.models.fields.CharField')(default='html', max_length=255),
keep_default=False)
# Adding field 'Text.links'
db.add_column('text_text', 'links',
self.gf('ox.django.fields.DictField')(default={}),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Text.type'
db.delete_column('text_text', 'type')
# Deleting field 'Text.links'
db.delete_column('text_text', 'links')
models = {
'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'})
},
'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'})
},
'text.position': {
'Meta': {'unique_together': "(('user', 'text', 'section'),)", 'object_name': 'Position'},
'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'"}),
'text': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'position'", 'to': "orm['text.Text']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'text_position'", 'to': "orm['auth.User']"})
},
'text.text': {
'Meta': {'unique_together': "(('user', 'name'),)", 'object_name': 'Text'},
'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', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'links': ('ox.django.fields.DictField', [], {'default': '{}'}),
'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': '[]'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'private'", 'max_length': '20'}),
'subscribed_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subscribed_texts'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'text': ('django.db.models.fields.TextField', [], {'default': "''"}),
'type': ('django.db.models.fields.CharField', [], {'default': "'html'", 'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'texts'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['text']

View file

@ -2,40 +2,158 @@
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
from datetime import datetime
import os
import subprocess
from glob import glob
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
import ox
from ox.django.fields import DictField, TupleField
from archive import extract
import managers
class Text(models.Model):
class Meta:
unique_together = ("user", "name")
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
published = models.DateTimeField(default=datetime.now, editable=False)
public = models.BooleanField(default=False)
user = models.ForeignKey(User, related_name='texts')
name = models.CharField(max_length=255)
status = models.CharField(max_length=20, default='private')
_status = ['private', 'public', 'featured']
type = models.CharField(max_length=255, default='html')
description = models.TextField(default='')
user = models.ForeignKey(User)
slug = models.SlugField()
title = models.CharField(null=True, max_length=1000)
text = models.TextField(default='')
icon = models.ImageField(default=None, blank=True,
upload_to=lambda i, x: i.path("icon.jpg"))
text = models.TextField(default="")
links = DictField(default={}, editable=True)
poster_frames = TupleField(default=[], editable=False)
subscribed_users = models.ManyToManyField(User, related_name='subscribed_texts')
objects = managers.TextManager()
def save(self, *args, **kwargs):
super(Text, self).save(*args, **kwargs)
def __unicode__(self):
return u"%s <%s>" % (self.title, self.slug)
return self.get_id()
def get_absolute_url(self):
return '/text/%s' % self.slug
def get_id(self):
return u'%s:%s' % (self.user.username, self.name)
def accessible(self, user):
return self.user == user or self.status in ('public', 'featured')
class Image(models.Model):
image = models.ImageField(upload_to='text/image')
caption = models.CharField(max_length=255, default="")
def editable(self, user):
if user.is_anonymous():
return False
if self.user == user or \
user.is_staff or \
user.get_profile().capability('canEditFeaturedTexts') == True:
return True
return False
def get_absolute_url(self):
return self.image.url
def json(self, keys=None, user=None):
if not keys:
keys=['id', 'name', 'user', 'status', 'subscribed', 'posterFrames', 'description', 'text', 'type', 'links']
response = {}
_map = {
'posterFrames': 'poster_frames'
}
for key in keys:
if key == 'id':
response[key] = self.get_id()
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 path(self, name=''):
h = "%07d" % self.id
return os.path.join('texts', h[:2], h[2:4], h[4:6], h[6:], name)
class Attachment(models.Model):
file = models.FileField(upload_to='text/attachment')
caption = models.CharField(max_length=255, default="")
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:
from item.models import Item
qs = 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
class Position(models.Model):
class Meta:
unique_together = ("user", "text", "section")
text = models.ForeignKey(Text, related_name='position')
user = models.ForeignKey(User, related_name='text_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.text)
def get_absolute_url(self):
return self.file.url

View file

@ -1,27 +1,96 @@
# -*- 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
import models
from ox.django.api import actions
from ox.django.decorators import login_required_json
from ox.django.http import HttpFileResponse
from ox.django.shortcuts import render_to_json_response, get_object_or_404_json, json_response
from django.db.models import Count, Q, Sum, Max
from item import utils
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)
@login_required_json
def addText(request):
'''
param data {
name: value,
}
return {
status: {'code': int, 'text': string},
data: {
id:
name:
...
}
}
'''
data = json.loads(request.POST['data'])
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
name = data['name']
if not name:
name = "Untitled"
num = 1
created = False
while not created:
text, created = models.Text.objects.get_or_create(name=name, user=request.user)
num += 1
name = data['name'] + ' [%d]' % num
text.save()
if text.status == 'featured':
pos, created = models.Position.objects.get_or_create(text=text,
user=request.user, section='featured')
qs = models.Position.objects.filter(section='featured')
else:
pos, created = models.Position.objects.get_or_create(text=text,
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'] = text.json()
return render_to_json_response(response)
actions.register(addText, cache=False)
def getText(request):
'''
param data
string id
return page
param data {
id: textid
}
return {
id:
text:
...
}
'''
response = json_response({})
itemId = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Text, pk=itemId)
response['data']['page'] = item.html()
response = json_response()
data = json.loads(request.POST['data'])
public_id = data['id']
if public_id == '':
qs = models.Text.objects.filter(name='')
if qs.count() == 0:
text = models.Text()
text.name = ''
text.text = 'Please put something here'
text.user = request.user
text.save()
else:
text = qs[0]
else:
text = get_text_or_404_json(data['id'])
response['data'] = text.json()
return render_to_json_response(response)
actions.register(getText)
@ -29,22 +98,355 @@ actions.register(getText)
@login_required_json
def editText(request):
'''
param data
string id
return page
param data {
id:
text:
public: boolean
}
return {
id:
text:
...
}
'''
response = json_response({})
itemId = json.loads(request.POST['data'])
item = get_object_or_404_json(models.Text, pk=itemId)
response['data']['page'] = item.html()
response = json_response()
data = json.loads(request.POST['data'])
if data['id']:
public_id = data['id'].split(':')
username = public_id[0]
name = ":".join(public_id[1:])
text, created = models.Text.objects.get_or_create(name=name, user=models.User.objects.get(username=username))
if created:
text.user = request.user
else:
qs = models.Text.objects.filter(name='')
if qs.count() == 0:
if request.user.get_profile().capability('canEditFeaturedTexts'):
text = models.Text(name='', user=request.user)
text.save()
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
else:
text = qs[0]
if text.editable(request.user):
for key in data:
if key == 'status':
value = data[key]
if value not in text._status:
value = text._status[0]
if value == 'private':
for user in text.subscribed_users.all():
text.subscribed_users.remove(user)
qs = models.Position.objects.filter(user=request.user,
section='section', text=text)
if qs.count() > 1:
pos = qs[0]
pos.section = 'personal'
pos.save()
elif value == 'featured':
if request.user.get_profile().capability('canEditFeaturedTexts'):
pos, created = models.Position.objects.get_or_create(text=text, user=request.user,
section='featured')
if created:
qs = models.Position.objects.filter(user=request.user, section='featured')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
models.Position.objects.filter(text=text).exclude(id=pos.id).delete()
else:
value = text.status
elif text.status == 'featured' and value == 'public':
models.Position.objects.filter(text=text).delete()
pos, created = models.Position.objects.get_or_create(text=text,
user=text.user,section='personal')
qs = models.Position.objects.filter(user=text.user,
section='personal')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
for u in text.subscribed_users.all():
pos, created = models.Position.objects.get_or_create(text=text, user=u,
section='public')
qs = models.Position.objects.filter(user=u, section='public')
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
text.status = value
elif key == 'name':
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
name = data['name']
if not name:
name = "Untitled"
num = 1
while models.Text.objects.filter(name=name, user=text.user).exclude(id=text.id).count()>0:
num += 1
name = data['name'] + ' [%d]' % num
text.name = name
elif key == 'description':
text.description = ox.sanitize_html(data['description'])
elif key == 'text':
text.text = ox.sanitize_html(data['text'])
if 'position' in data:
pos, created = models.Position.objects.get_or_create(text=text, user=request.user)
pos.position = data['position']
pos.section = 'featured'
if text.status == 'private':
pos.section = 'personal'
pos.save()
if 'type' in data:
if data['type'] == 'pdf':
text.type = 'pdf'
else:
text.type = 'html'
if 'posterFrames' in data:
text.poster_frames = tuple(data['posterFrames'])
text.update_icon()
text.save()
response['data'] = text.json(user=request.user)
else:
response = json_response(status=403, text='permission denied')
return render_to_json_response(response)
actions.register(editText, cache=False)
def findText(request):
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', 'text', 'range', 'position', 'positions', 'sort'):
if key in data:
query[key] = data[key]
print data
query['qs'] = models.Text.objects.find(data, user).exclude(name='')
print query
return query
def findTexts(request):
'''
param data {
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
}
return {status: {code: int, text: string},
data: {
items: [
{name:, user:, featured:, public...}
]
}
}
'''
response = json_response({})
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(findText)
actions.register(findTexts)
@login_required_json
def removeText(request):
'''
param data {
id: testId,
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
text = get_text_or_404_json(data['id'])
response = json_response()
if text.editable(request.user):
text.delete()
else:
response = json_response(status=403, text='not allowed')
return render_to_json_response(response)
actions.register(removeText, cache=False)
@login_required_json
def subscribeToText(request):
'''
param data {
id: testId,
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
text = get_text_or_404_json(data['id'])
user = request.user
if text.status == 'public' and \
text.subscribed_users.filter(username=user.username).count() == 0:
text.subscribed_users.add(user)
pos, created = models.Position.objects.get_or_create(text=text, 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(subscribeToText, cache=False)
@login_required_json
def unsubscribeFromText(request):
'''
param data {
id: testId,
user: username(only admins)
}
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
data = json.loads(request.POST['data'])
text = get_text_or_404_json(data['id'])
user = request.user
text.subscribed_users.remove(user)
models.Position.objects.filter(text=text, user=user, section='public').delete()
response = json_response()
return render_to_json_response(response)
actions.register(unsubscribeFromText, cache=False)
@login_required_json
def sortTexts(request):
'''
param data {
section: 'personal',
ids: [1,2,4,3]
}
known sections: 'personal', 'public', 'featured'
featured can only be edited by admins
return {
status: {'code': int, 'text': string},
data: {
}
}
'''
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('canEditFeaturedTexts'):
response = json_response(status=403, text='not allowed')
else:
user = request.user
if section == 'featured':
for i in ids:
l = get_text_or_404_json(i)
qs = models.Position.objects.filter(section=section, text=l)
if qs.count() > 0:
pos = qs[0]
else:
pos = models.Position(text=l, user=user, section=section)
if pos.position != position:
pos.position = position
pos.save()
position += 1
models.Position.objects.filter(section=section, text=l).exclude(id=pos.id).delete()
else:
for i in ids:
l = get_text_or_404_json(i)
pos, created = models.Position.objects.get_or_create(text=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(sortTexts, cache=False)
def icon(request, id, size=16):
if not size:
size = 16
id = id.split(':')
username = id[0]
textname = ":".join(id[1:])
qs = models.Text.objects.filter(user__username=username, name=textname)
if qs.count() == 1 and qs[0].accessible(request.user):
text = qs[0]
icon = text.get_icon(int(size))
else:
icon = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
return HttpFileResponse(icon, content_type='image/jpeg')

View file

@ -28,6 +28,7 @@ urlpatterns = patterns('',
(r'^api/?$', include(ox.django.api.urls)),
(r'^resetUI$', 'user.views.reset_ui'),
(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'^robots.txt$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'robots.txt'), 'content_type': 'text/plain'}),
(r'^favicon.ico$', serve_static_file, {'location': os.path.join(settings.STATIC_ROOT, 'png/icon.16.png'), 'content_type': 'image/x-icon'}),
(r'^opensearch.xml$', 'app.views.opensearch_xml'),

View file

@ -14,6 +14,7 @@ from ox.django.fields import DictField
from ox.utils import json
from itemlist.models import List, Position
import text
import managers
import tasks
@ -262,6 +263,29 @@ def get_ui(user_ui, user=None):
ids.append(id)
return ids
def add_texts(texts, section):
P = text.models.Position
ids = []
for t in texts:
qs = P.objects.filter(section=section)
if section == 'featured':
try:
pos = P.objects.get(text=t, section=section)
created = False
except P.DoesNotExist:
pos = P(text=t, section=section, user=l.user)
pos.save()
created = True
else:
pos, created = P.objects.get_or_create(text=t, user=user, section=section)
qs = qs.filter(user=user)
if created:
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
pos.save()
ids.append(t.get_id())
return ids
ids = ['']
if user:
ids += add(user.lists.exclude(status="featured"), 'personal')
@ -270,6 +294,11 @@ def get_ui(user_ui, user=None):
for i in ui['lists'].keys():
if i not in ids:
del ui['lists'][i]
tids = ['']
if user:
tids += add_texts(user.texts.exclude(status="featured"), 'personal')
tids += add_texts(user.subscribed_texts.filter(status='public'), 'public')
tids += add_texts(text.models.Text.objects.filter(status='featured'), 'featured')
return ui
def init_user(user, request=None):

View file

@ -54,7 +54,10 @@ pandora.UI = (function() {
self.previousUI = Ox.clone(pandora.user.ui, true);
self.previousUI._list = pandora.getListState(self.previousUI.find);
if (args.section == 'texts') {
trigger['section'] = args['section'];
trigger['text'] = args['text'];
} else {
if ('find' in args) {
// the challenge here is that find may change list,
// and list may then change listSort and listView,
@ -152,6 +155,7 @@ pandora.UI = (function() {
add['videoPoints.' + item] = {annotation: '', 'in': 0, out: 0, position: 0};
}
}
}
// items in args trigger events, items in add do not
[args, add].forEach(function(obj, isAdd) {

View file

@ -16,10 +16,10 @@ pandora.URL = (function() {
var state = {};
state.type = pandora.site.itemsSection;
state.item = pandora.user.ui.item;
state.type = pandora.user.ui.section == 'items' ? pandora.site.itemsSection : pandora.user.ui.section;
state.item = pandora.user.ui[pandora.user.ui.section.slice(0, -1)];
if(pandora.user.ui.section == 'items') {
if (!pandora.user.ui.item) {
state.view = pandora.user.ui.listView;
state.sort = pandora.user.ui.listSort;
@ -28,6 +28,7 @@ pandora.URL = (function() {
state.view = pandora.user.ui.itemView;
state.sort = pandora.user.ui.itemSort;
}
}
if (state.view == 'map') {
state.span = pandora.user.ui.mapFind
@ -79,10 +80,11 @@ pandora.URL = (function() {
var set = {
section: state.type == pandora.site.itemsSection ? 'items' : state.type,
item: state.item,
page: ''
};
set[set.section.slice(0, -1)] = state.item;
if (set.section == 'items') {
if (state.view) {
set[!state.item ? 'listView' : 'itemView'] = state.view;
}
@ -131,6 +133,7 @@ pandora.URL = (function() {
set.find = pandora.site.user.ui.find;
}
}
}
if (state.hash && state.hash.query) {
state.hash.query.forEach(function(kv) {
@ -246,6 +249,19 @@ pandora.URL = (function() {
calendar: 'date'
}
};
//Text
views['texts'] = {
list: [],
item: ['text']
}
spanType['texts'] = {
list: [],
item: {}
}
sortKeys['texts'] = {
list: {},
item: {}
}
findKeys = [{id: 'list', type: 'string'}].concat(pandora.site.itemKeys);

View file

@ -12,7 +12,11 @@ pandora.ui.allItems = function() {
.on({
click: function() {
that.gainFocus();
if (pandora.user.ui.section == 'items') {
pandora.user.ui._list && pandora.UI.set('find', {conditions: [], operator: '&'});
} else {
pandora.UI.set(pandora.user.ui.section.slice(0, -1), '');
}
}
})
.bindEvent({
@ -33,9 +37,12 @@ pandora.ui.allItems = function() {
overflow: 'hidden',
whiteSpace: 'nowrap'
})
.html('All ' + pandora.site.itemName.plural)
.appendTo(that),
$items = $('<div>')
.html(pandora.user.ui.section == 'items' ? 'All ' + pandora.site.itemName.plural
: pandora.site.site.name + ' ' + Ox.toTitleCase(pandora.user.ui.section))
.appendTo(that);
if (pandora.user.ui.section == 'items') {
var $items = $('<div>')
.css({
float: 'left',
width: '42px',
@ -56,12 +63,12 @@ pandora.ui.allItems = function() {
type: 'image'
})
.appendTo(that);
pandora.api.find({
query: {conditions: [], operator: '&'}
}, function(result) {
that.update(result.data.items);
});
}
that.update = function(items) {
$items.html(Ox.formatNumber(items));

View file

@ -3,6 +3,9 @@
'use strict';
pandora.ui.deleteListDialog = function(list) {
var ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1);
var listData = pandora.getListData(list),
$folderList = pandora.$ui.folderList[listData.folder],
@ -10,7 +13,7 @@ pandora.ui.deleteListDialog = function(list) {
buttons: [
Ox.Button({
id: 'keep',
title: 'Keep List'
title: 'Keep ' + folderItem
}).bindEvent({
click: function() {
that.close();
@ -18,23 +21,27 @@ pandora.ui.deleteListDialog = function(list) {
}),
Ox.Button({
id: 'delete',
title: 'Delete List'
title: 'Delete ' + folderItem
}).bindEvent({
click: function() {
that.close();
pandora.api.removeList({
pandora.api['remove' + folderItem]({
id: listData.id
}, function(result) {
Ox.Request.clearCache('findLists');
Ox.Request.clearCache('find' + folderItems);
Ox.Request.clearCache(listData.id);
$folderList
.options({selected: []})
.bindEventOnce({
load: function() {
if (ui.section == 'items') {
pandora.UI.set('lists.' + listData.id, null);
pandora.UI.set({
find: pandora.site.user.ui.find
});
} else {
pandora.UI.set(folderItem.toLowerCase(), '');
}
}
})
.reloadList();
@ -51,7 +58,7 @@ pandora.ui.deleteListDialog = function(list) {
.append(
$('<div>')
.css({position: 'absolute', left: '96px', top: '16px', width: '192px'})
.html('Are you sure you want to delete the list "' + listData.name + '"?')
.html('Are you sure you want to delete the ' + folderItem.toLowerCase() + ' "' + listData.name + '"?')
),
height: 128,
keys: {enter: 'delete', escape: 'keep'},

View file

@ -1,7 +1,10 @@
// vim: et:ts=4:sw=4:sts=4:ft=javascript
'use strict';
pandora.ui.folderBrowserBar = function(id) {
var that = Ox.Bar({
var ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1),
that = Ox.Bar({
size: 24
});
pandora.$ui.findListElement[id] = Ox.FormElementGroup({
@ -9,7 +12,7 @@ pandora.ui.folderBrowserBar = function(id) {
pandora.$ui.findListSelect[id] = Ox.Select({
items: [
{id: 'user', title: 'Find: User'},
{id: 'name', title: 'Find: List'}
{id: 'name', title: 'Find: ' + folderItem}
],
overlap: 'right',
type: 'image'
@ -56,7 +59,7 @@ pandora.ui.folderBrowserBar = function(id) {
{key: 'status', value: 'private', operator: '!='},
{key: key, value: value, operator: '='}
], operator: '&'};
return pandora.api.findLists(Ox.extend(data, {
return pandora.api['find' + folderItems](Ox.extend(data, {
query: query
}), callback);
}

View file

@ -3,8 +3,11 @@
pandora.ui.folderBrowserList = function(id) {
// fixme: user and name are set to the same width here,
// but resizeFolders will set them to different widths
var columnWidth = (pandora.user.ui.sidebarSize - Ox.UI.SCROLLBAR_SIZE - 96) / 2,
i = Ox.getIndexById(pandora.site.sectionFolders[pandora.user.ui.section], id),
var ui = pandora.user.ui,
columnWidth = (ui.sidebarSize - Ox.UI.SCROLLBAR_SIZE - (ui.section == 'items' ? 96 : 32)) / 2,
i = Ox.getIndexById(pandora.site.sectionFolders[ui.section], id),
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1),
that = Ox.TableList({
columns: [
{
@ -52,7 +55,7 @@ pandora.ui.folderBrowserList = function(id) {
},
id: 'name',
operator: '+',
title: 'List',
title: folderItem,
visible: true,
width: Math.ceil(columnWidth)
},
@ -62,7 +65,7 @@ pandora.ui.folderBrowserList = function(id) {
format: {type: 'number'},
operator: '-',
title: 'Items',
visible: true,
visible: ui.section == 'items',
width: 48
},
{
@ -72,7 +75,7 @@ pandora.ui.folderBrowserList = function(id) {
format: function(value, data) {
return $('<img>')
.attr({
src: Ox.UI.getImageURL(value == 'static' ? 'symbolClick' : 'symbolFind')
src: Ox.UI.getImageURL(value == 'smart' ? 'symbolFind' : value == 'pdf' ? 'symbolFiles' : value == 'html' ? 'symbolFile' : 'symbolClick')
})
.css({
width: '10px',
@ -89,7 +92,7 @@ pandora.ui.folderBrowserList = function(id) {
? (data.user == pandora.user.username ? 'Edit Query' : 'Show Query')
: (data.user == pandora.user.username ? 'Edit Default View' : 'Default View: ...');
},
visible: true,
visible: ui.section == 'items',
width: 16
},
{
@ -116,7 +119,7 @@ pandora.ui.folderBrowserList = function(id) {
tooltip: function(data) {
var checked = id == 'favorite' ? data.subscribed : data.status == 'featured';
return (checked ? 'Remove from' : 'Add to')
+ ' ' + Ox.toTitleCase(id) + ' Lists';
+ ' ' + Ox.toTitleCase(id) + ' ' + folderItems;
},
visible: true,
width: 16
@ -130,7 +133,7 @@ pandora.ui.folderBrowserList = function(id) {
], operator: '&'} : {conditions: [
{key: 'status', value: 'private', operator: '!='}
], operator: ''};
return pandora.api.findLists(Ox.extend(data, {
return pandora.api['find' + folderItems](Ox.extend(data, {
query: query
}), callback);
},
@ -139,7 +142,7 @@ pandora.ui.folderBrowserList = function(id) {
// not-featured list may be in the user's favorites folder
keys: id == 'featured' ? ['subscribed'] : [],
pageLength: 1000,
selected: pandora.getListData().folder == id ? [pandora.user.ui._list] : [],
selected: pandora.getListData().folder == id ? [ui.section == 'items' ? ui._list : ui[ui.section.slice(0, -1)]] : [],
sort: [{key: 'name', operator: '+'}],
unique: 'id'
})
@ -153,19 +156,19 @@ pandora.ui.folderBrowserList = function(id) {
*/
} else if (data.key == 'subscribed') {
var subscribed = that.value(data.id, 'subscribed');
pandora.api[subscribed ? 'unsubscribeFromList' : 'subscribeToList']({
pandora.api[subscribed ? 'unsubscribeFrom' + folderItem : 'subscribeTo' + folderItem]({
id: data.id
}, function(result) {
that.value(data.id, 'subscribed', !subscribed);
});
} else if (data.key == 'status') {
pandora.api.editList({
pandora.api['edit' + folderItem]({
id: data.id,
status: that.value(data.id, 'status') == 'featured' ? 'public' : 'featured'
}, function(result) {
Ox.Log('', 'result', result)
if (result.data.user == pandora.user.username || result.data.subscribed) {
Ox.Request.clearCache(); // fixme: remove
Ox.Request.clearCache(); // fixme: removen
pandora.$ui.folderList[
result.data.user == pandora.user.username ? 'personal' : 'favorite'
].reloadList();
@ -175,7 +178,7 @@ pandora.ui.folderBrowserList = function(id) {
}
},
init: function(data) {
pandora.site.sectionFolders[pandora.user.ui.section][i].items = data.items;
pandora.site.sectionFolders[ui.section][i].items = data.items;
pandora.$ui.folder[i].$content.css({
height: 40 + data.items * 16 + 'px'
});
@ -185,7 +188,9 @@ pandora.ui.folderBrowserList = function(id) {
pandora.resizeFolders();
},
paste: function(data) {
if (ui.section == 'items') {
pandora.$ui.list.triggerEvent('paste', data);
}
},
select: function(data) {
// fixme: duplicated
@ -195,6 +200,7 @@ pandora.ui.folderBrowserList = function(id) {
id != id_ && $list.options('selected', []);
});
}
if (ui.section == 'items') {
pandora.UI.set({
find: {
conditions: list ? [
@ -203,6 +209,9 @@ pandora.ui.folderBrowserList = function(id) {
operator: '&'
}
});
} else {
pandora.UI.set(ui.section.slice(0, -1), list);
}
}
});
return that;

View file

@ -1,20 +1,23 @@
// vim: et:ts=4:sw=4:sts=4:ft=javascript
'use strict';
pandora.ui.folderList = function(id) {
var i = Ox.getIndexById(pandora.site.sectionFolders[pandora.user.ui.section], id),
canEditFeaturedLists = pandora.site.capabilities.canEditFeaturedLists[pandora.user.level],
var ui = pandora.user.ui,
i = Ox.getIndexById(pandora.site.sectionFolders[ui.section], id),
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1),
canEditFeatured = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
that;
if (pandora.user.ui.section == 'items') {
var columns, items;
if (id != 'volumes') {
columns = [
{
clickable: function(data) {
return data.user == pandora.user.username || (id == 'featured' && canEditFeaturedLists);
return data.user == pandora.user.username || (id == 'featured' && canEditFeatured);
},
format: function(value, data) {
return $('<img>').attr({
src: '/list/' + data.id + '/icon.jpg'
src: '/' + folderItem.toLowerCase() + '/' + data.id + '/icon.jpg'
}).css({
width: '14px',
height: '14px',
@ -26,7 +29,7 @@ pandora.ui.folderList = function(id) {
operator: '+',
tooltip: function(data) {
return data.user == pandora.user.username
|| (id == 'featured' && canEditFeaturedLists)
|| (id == 'featured' && canEditFeatured)
? 'Edit Icon'
: '';
},
@ -42,7 +45,7 @@ pandora.ui.folderList = function(id) {
visible: id == 'favorite',
// fixme: user and name are set to the same width here,
// but resizeFolders will set them to different widths
width: pandora.user.ui.sidebarWidth - 96
width: ui.sidebarWidth - 96
},
{
editable: function(data) {
@ -61,7 +64,7 @@ pandora.ui.folderList = function(id) {
return Ox.decodeHTMLEntities(value);
},
visible: id != 'favorite',
width: pandora.user.ui.sidebarWidth - 96
width: ui.sidebarWidth - 96
},
{
align: 'right',
@ -78,7 +81,7 @@ pandora.ui.folderList = function(id) {
format: function(value, data) {
return $('<img>')
.attr({
src: Ox.UI.getImageURL(value == 'static' ? 'symbolClick' : 'symbolFind')
src: Ox.UI.getImageURL(value == 'smart' ? 'symbolFind' : value == 'pdf' ? 'symbolFiles' : value == 'html' ? 'symbolFile' : 'symbolClick')
})
.css({
width: '10px',
@ -140,7 +143,7 @@ pandora.ui.folderList = function(id) {
{key: 'status', value: 'featured', operator: '='} // fixme: '==' performs better
], operator: '&'};
}
return pandora.api.findLists(Ox.extend(data, {
return pandora.api['find' + folderItems](Ox.extend(data, {
query: query
}), callback);
};
@ -167,7 +170,7 @@ pandora.ui.folderList = function(id) {
operator: '+',
tooltip: 'Edit Title',
visible: true,
width: pandora.user.ui.sidebarWidth - 96
width: ui.sidebarWidth - 96
},
{
align: 'right',
@ -241,19 +244,19 @@ pandora.ui.folderList = function(id) {
that = Ox.TableList({
columns: columns,
items: items,
keys: ['query'],
keys: ui.section == 'items' ? ['query'] : [],
max: 1,
min: 0,
pageLength: 1000,
//selected: pandora.getListData().folder == id ? [pandora.user.ui._list] : [],
//selected: pandora.getListData().folder == id ? [ui._list] : [],
sort: [{key: 'position', operator: '+'}],
sortable: id != 'featured' || canEditFeaturedLists,
sortable: id != 'featured' || canEditFeatured,
unique: id != 'volumes' ? 'id' : 'name'
})
.css({
left: 0,
top: 0,
width: pandora.user.ui.sidebarWidth + 'px'
width: ui.sidebarWidth + 'px'
})
.bindEvent({
add: function(event) {
@ -284,7 +287,7 @@ pandora.ui.folderList = function(id) {
});
});
}
} else if (id == 'favorite' || (id == 'featured' && canEditFeaturedLists)) {
} else if (id == 'favorite' || (id == 'featured' && canEditFeatured)) {
// this makes the button trigger a change event,
// which is already being handled in folders.js
pandora.$ui.manageListsButton[id].options({value: true});
@ -309,7 +312,7 @@ pandora.ui.folderList = function(id) {
}
} else if (data.key == 'status') {
var status = that.value(data.id, data.key) == 'private' ? 'public' : 'private';
pandora.changeListStatus(data.id, status, function(result) {
pandora.changeFolderItemStatus(data.id, status, function(result) {
that.value(result.data.id, 'status', result.data.status);
});
} else if (data.key == 'path') {
@ -323,15 +326,15 @@ pandora.ui.folderList = function(id) {
pandora.ui.deleteListDialog(data.ids[0]).open();
} else if (id == 'favorite') {
that.options({selected: []});
pandora.api.unsubscribeFromList({
pandora.api['unsubscribeFrom' + folderItem]({
id: data.ids[0]
}, function(result) {
Ox.Request.clearCache(); // fixme: remove
that.reloadList();
});
} else if (id == 'featured' && canEditFeaturedLists) {
} else if (id == 'featured' && canEditFeatured) {
that.options({selected: []});
pandora.api.editList({
pandora.api['edit' + folderItem]({
id: data.ids[0],
status: 'public'
}, function(result) {
@ -352,7 +355,7 @@ pandora.ui.folderList = function(id) {
},
*/
init: function(data) {
pandora.site.sectionFolders[pandora.user.ui.section][i].items = data.items;
pandora.site.sectionFolders[ui.section][i].items = data.items;
pandora.$ui.folder[i].$content.css({
height: data.items * 16 + 'px'
});
@ -362,7 +365,7 @@ pandora.ui.folderList = function(id) {
pandora.resizeFolders();
},
move: function(data) {
pandora.api.sortLists({
pandora.api['sort' + foldeItem]({
section: id,
ids: data.ids
});
@ -377,6 +380,7 @@ pandora.ui.folderList = function(id) {
id != id_ && $list.options('selected', []);
});
}
if (ui.section == 'items') {
pandora.UI.set({
find: {
conditions: list ? [
@ -385,11 +389,14 @@ pandora.ui.folderList = function(id) {
operator: '&'
}
});
} else {
pandora.UI.set(ui.section.slice(0, -1), list);
}
},
submit: function(data) {
var data_ = {id: data.id};
data_[data.key] = data.value;
pandora.api.editList(data_, function(result) {
pandora.api['edit' + folderItem](data_, function(result) {
if (result.data.id != data.id) {
pandora.renameList(data.id, result.data.id, result.data.name, id);
pandora.$ui.info.updateListInfo();
@ -397,6 +404,5 @@ pandora.ui.folderList = function(id) {
});
}
});
}
return that;
};

View file

@ -7,7 +7,9 @@ pandora.ui.folders = function() {
.css({overflowX: 'hidden', overflowY: 'auto'})
.bindEvent({
resize: pandora.resizeFolders
});
}),
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1);
//var $sections = [];
pandora.$ui.allItems = pandora.ui.allItems().appendTo(that);
@ -18,15 +20,17 @@ pandora.ui.folders = function() {
pandora.$ui.findListSelect = {};
pandora.$ui.findListInput = {};
pandora.$ui.manageListsButton = {};
if (ui.section == 'items') {
pandora.site.sectionFolders.items.forEach(function(folder, i) {
pandora.site.sectionFolders[ui.section].forEach(function(folder, i) {
var extras, $select;
if (folder.id == 'personal') {
if (pandora.user.level == 'guest') {
extras = [
infoButton('Personal Lists', 'To create and share your own lists of movies, please sign up or sign in.')
infoButton('Personal ' + folderItems, 'To create and share your own ' + (ui.section == 'items'
? 'lists of ' + pandora.site.itemName.plural.toLowerCase()
: ui.section) + ', please sign up or sign in.')
];
} else {
if (ui.section == 'items') {
extras = [
pandora.$ui.personalListsMenu = Ox.MenuButton({
items: [
@ -78,24 +82,55 @@ pandora.ui.folders = function() {
}
})
];
} else {
extras = [
pandora.$ui.personalListsMenu = Ox.MenuButton({
items: [
{ id: 'new' + folderItem.toLowerCase(), title: 'New ' + folderItem },
{},
{ id: 'delete' + folderItem.toLowerCase(), title: 'Delete Selected ' + folderItem + '...', disabled: !ui[ui.section.slice(0,-1)] }
],
title: 'edit',
tooltip: 'Manage Personal ' + folderItems,
type: 'image'
})
.bindEvent({
click: function(data) {
var $list = pandora.$ui.folderList[folder.id];
// fixme: duplicated
if (data.id == 'new' + folderItem.toLowerCase()) {
pandora['add' + folderItem]();
} else if (data.id == 'delete' + folderItem.toLowerCase()) {
pandora.ui.deleteListDialog().open();
}
}
})
.bindEvent('pandora_' + ui.section.slice(0,-1), function(data) {
pandora.$ui.personalListsMenu[
data.value && data.value.length ? 'enableItem' : 'disableItem'
]('delete' + folderItem.toLowerCase());
})
];
}
}
} else if (folder.id == 'favorite') {
if (pandora.user.level == 'guest') {
extras = [infoButton('Favorite Lists', 'To browse and subscribe to shared lists from other users, please sign up or sign in.')];
extras = [infoButton('Favorite ' + folderItems,
'To browse and subscribe to shared ' + folderItems.toLowerCase() + ' from other users, please sign up or sign in.')];
} else {
extras = [pandora.$ui.manageListsButton['favorite'] = Ox.Button({
selectable: true,
style: 'symbol',
title: 'Edit',
tooltip: 'Manage Favorite Lists',
tooltip: 'Manage Favorite ' + folderItems,
type: 'image'
})
.bindEvent({
change: function(data) {
var listData;
Ox.Request.clearCache(); // fixme: remove
pandora.site.sectionFolders.items[i].showBrowser = !pandora.site.sectionFolders.items[i].showBrowser;
if (pandora.site.sectionFolders.items[i].showBrowser) {
pandora.site.sectionFolders[ui.section][i].showBrowser = !pandora.site.sectionFolders[ui.section][i].showBrowser;
if (pandora.site.sectionFolders[ui.section][i].showBrowser) {
pandora.$ui.folderList.favorite.replaceWith(
pandora.$ui.folderBrowser.favorite = pandora.ui.folderBrowser('favorite')
);
@ -107,7 +142,7 @@ pandora.ui.folders = function() {
) {
// the selected list in the favorites browser is not in the favorites folder
pandora.$ui.folderList.favorite.options({selected: []});
if (Ox.getObjectById(pandora.site.sectionFolders.items, 'featured').showBrowser) {
if (Ox.getObjectById(pandora.site.sectionFolders[ui.section], 'featured').showBrowser) {
// but in the featured browser
pandora.$ui.folderList.featured.options({selected: [listData.id]});
} else {
@ -127,21 +162,21 @@ pandora.ui.folders = function() {
}
} else if (folder.id == 'featured') {
if (pandora.user.level != 'admin') {
extras = [infoButton('Featured Lists', 'Featured lists are selected public lists, picked by the ' + pandora.site.site.name + ' staff.')];
extras = [infoButton('Featured ' + folderItems, 'Featured ' + folderItems.toLowerCase() + ' are selected public ' + folderItems.toLowerCase() + ', picked by the ' + pandora.site.site.name + ' staff.')];
} else {
extras = [pandora.$ui.manageListsButton['featured'] = Ox.Button({
selectable: true,
style: 'symbol',
title: 'Edit',
tooltip: 'Manage Featured Lists',
tooltip: 'Manage Featured ' + folderItems,
type: 'image'
})
.bindEvent({
change: function(data) {
var listData;
Ox.Request.clearCache(); // fixme: remove
pandora.site.sectionFolders.items[i].showBrowser = !pandora.site.sectionFolders.items[i].showBrowser;
if (pandora.site.sectionFolders.items[i].showBrowser) {
pandora.site.sectionFolders[ui.section][i].showBrowser = !pandora.site.sectionFolders[ui.section][i].showBrowser;
if (pandora.site.sectionFolders[ui.section][i].showBrowser) {
pandora.$ui.folderList.featured.replaceWith(
pandora.$ui.folderBrowser.featured = pandora.ui.folderBrowser('featured')
);
@ -159,7 +194,7 @@ pandora.ui.folders = function() {
pandora.$ui.folderList.personal.options({selected: [listData.id]});
} else if (
listData.subscribed
|| Ox.getObjectById(pandora.site.sectionFolders.items, 'favorite').showBrowser
|| Ox.getObjectById(pandora.site.sectionFolders[ui.section], 'favorite').showBrowser
) {
// but in the favorites folder or browser
pandora.$ui.folderList.favorite.options({selected: [listData.id]});
@ -255,7 +290,7 @@ pandora.ui.folders = function() {
})
.bindEventOnce({
init: function(data) {
if (++counter == 4) {
if (++counter == pandora.site.sectionFolders[ui.section].length) {
pandora.$ui.folder.forEach(function($folder) {
that.append($folder);
});
@ -266,7 +301,6 @@ pandora.ui.folders = function() {
})
.appendTo(pandora.$ui.folder[i].$content);
});
}
function infoButton(title, text) {
return Ox.Button({
style: 'symbol',
@ -340,6 +374,13 @@ pandora.ui.folders = function() {
});
}
*/
},
pandora_text: function() {
if (!pandora.user.ui.text) {
Ox.forEach(pandora.$ui.folderList, function($list, id) {
$list.options('selected', []);
});
}
}
})
return that;

View file

@ -13,7 +13,10 @@ pandora.ui.listDialog = function(section) {
], listData.type == 'smart'
? [{id: 'query', title: 'Query'}]
: []
);
),
ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1);
Ox.getObjectById(tabs, section).selected = true;
pandora.$ui.listDialogTabPanel = Ox.TabPanel({
@ -103,7 +106,7 @@ pandora.ui.listDialog = function(section) {
height: 312,
// keys: {enter: 'save', escape: 'cancel'},
removeOnClose: true,
title: 'List &mdash; ' + Ox.encodeHTMLEntities(listData.name),
title: folderItem + ' &mdash; ' + Ox.encodeHTMLEntities(listData.name),
width: width
});
@ -117,8 +120,11 @@ pandora.ui.listDialog = function(section) {
};
pandora.ui.listGeneralPanel = function(listData) {
var that = Ox.Element();
pandora.api.findLists({
var that = Ox.Element(),
ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1);
pandora.api['find' + folderItems]({
query: {conditions: [{key: 'id', value: listData.id, operator: '=='}]},
keys: ['description', 'subscribers']
}, function(result) {
@ -129,7 +135,7 @@ pandora.ui.listGeneralPanel = function(listData) {
tooltip: 'Doubleclick to edit icon'
})
.attr({
src: '/list/' + listData.id + '/icon256.jpg?' + Ox.uid()
src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon256.jpg?' + Ox.uid()
})
.css({
position: 'absolute',
@ -157,7 +163,8 @@ pandora.ui.listGeneralPanel = function(listData) {
submit: editName
})
.appendTo(that),
$itemsInput = Ox.Input({
$itemsInput = ui.section == 'items'
? Ox.Input({
disabled: true,
label: 'Items',
labelWidth: 80,
@ -165,6 +172,21 @@ pandora.ui.listGeneralPanel = function(listData) {
width: 320
})
.css({position: 'absolute', left: '160px', top: '40px'})
.appendTo(that)
: Ox.Select({
items: [
{id: 'html', title: 'HMTL'},
{id: 'pdf', title: 'PDF'}
],
label: 'Type',
labelWidth: 80,
value: listData.type,
width: 320
})
.css({position: 'absolute', left: '160px', top: '40px'})
.bindEvent({
change: editType
})
.appendTo(that),
$statusSelect = listData.status == 'featured'
? Ox.Input({
@ -216,19 +238,19 @@ pandora.ui.listGeneralPanel = function(listData) {
.appendTo(that);
function editDescription(data) {
if (data.value != description) {
pandora.api.editList({
pandora.api['edit' + folderItem]({
id: listData.id,
description: data.value
}, function(result) {
description = result.data.description;
Ox.Request.clearCache('findLists');
Ox.Request.clearCache('find' + folderItems);
pandora.$ui.info.updateListInfo();
});
}
}
function editName(data) {
if (data.value != listData.name) {
pandora.api.editList({
pandora.api['edit' + folderItem]({
id: listData.id,
name: data.value
}, function(result) {
@ -236,10 +258,10 @@ pandora.ui.listGeneralPanel = function(listData) {
pandora.renameList(listData.id, result.data.id, result.data.name);
listData.id = result.data.id;
listData.name = result.data.name;
Ox.Request.clearCache('findLists');
Ox.Request.clearCache('find' + folderItems);
pandora.$ui.info.updateListInfo();
pandora.$ui.listDialog.options({
title: 'List &mdash; ' + Ox.encodeHTMLEntities(listData.name)
title: folderItme + ' &mdash; ' + Ox.encodeHTMLEntities(listData.name)
});
}
});
@ -248,7 +270,7 @@ pandora.ui.listGeneralPanel = function(listData) {
function editStatus(data) {
var status = data.value;
$statusSelect.value(status == 'private' ? 'public' : 'private');
pandora.changeListStatus(listData.id, status, function(result) {
pandora.changeFolderItemStatus(listData.id, status, function(result) {
listData.status = result.data.status;
if (result.data.status == 'private') {
subscribers = 0;
@ -264,6 +286,18 @@ pandora.ui.listGeneralPanel = function(listData) {
);
});
}
function editType(data) {
var type = data.value;
$itemsInput.value(type == 'html' ? 'html' : 'pdf');
pandora.api.editText({
id: listData.id,
type: type
}, function(result) {
Ox.Request.clearCache('getText');
//fixme: reload text and folder list
$itemsInput.value(result.data.type);
});
}
function getDescriptionHeight() {
return listData.status == 'private' ? 184 : 160;
}
@ -282,10 +316,14 @@ pandora.ui.listIconPanel = function(listData) {
var quarter = 0,
quarters = ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1),
$iconPanel = Ox.Element(),
$icon = $('<img>')
.attr({src: '/list/' + listData.id + '/icon256.jpg?' + Ox.uid()})
.attr({src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon256.jpg?' + Ox.uid()})
.css({position: 'absolute', borderRadius: '64px', margin: '16px'})
.appendTo($iconPanel),
@ -295,6 +333,10 @@ pandora.ui.listIconPanel = function(listData) {
$list = Ox.Element(),
ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1),
that = Ox.SplitPanel({
elements: [
{
@ -312,7 +354,7 @@ pandora.ui.listIconPanel = function(listData) {
orientation: 'horizontal'
});
pandora.api.findLists({
pandora.api['find' + folderItems]({
query: {
conditions: [{key: 'id', value: listData.id, operator: '=='}],
operator: '&'
@ -364,7 +406,7 @@ pandora.ui.listIconPanel = function(listData) {
items: function(data, callback) {
pandora.api.find(Ox.extend(data, {
query: {
conditions: [{key: 'list', value: listData.id, operator: '=='}],
conditions: ui.section == 'items' ? [{key: 'list', value: listData.id, operator: '=='}] : [],
operator: '&'
}
}), callback);
@ -375,7 +417,7 @@ pandora.ui.listIconPanel = function(listData) {
//orientation: 'vertical',
selected: posterFrame ? [posterFrame.item] : [],
size: 128,
sort: pandora.user.ui.listSort,
sort: ui.section == 'items' ? pandora.user.ui.listSort : pandora.site.user.ui.listSort,
unique: 'id'
})
//.css({width: '144px'})
@ -461,17 +503,17 @@ pandora.ui.listIconPanel = function(listData) {
} else {
posterFrames = Ox.repeat([posterFrame], 4);
}
pandora.api.editList({
pandora.api['edit' + folderItem]({
id: listData.id,
posterFrames: posterFrames
}, function() {
$icon.attr({
src: '/list/' + listData.id + '/icon256.jpg?' + Ox.uid()
src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon256.jpg?' + Ox.uid()
});
pandora.$ui.folderList[listData.folder].$element
.find('img[src*="/' + listData.id + '/"]')
.attr({
src: '/list/' + listData.id + '/icon.jpg?' + Ox.uid()
src: '/' + folderItem.toLowerCase() + '/' + listData.id + '/icon.jpg?' + Ox.uid()
});
pandora.$ui.info.updateListInfo();
pandora.clearListIconCache(listData.id);

View file

@ -65,6 +65,12 @@ pandora.ui.mainPanel = function() {
}
}
},
pandora_section: function(data) {
if (data.value != data.previousValue) {
that.replaceElement(0, pandora.$ui.leftPanel = pandora.ui.leftPanel());
that.replaceElement(1, pandora.$ui.rightPanel = pandora.ui.rightPanel());
}
},
pandora_item: function(data) {
if (!data.value || !data.previousValue) {
that.replaceElement(1, pandora.$ui.rightPanel = pandora.ui.rightPanel());

View file

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

View file

@ -5,7 +5,7 @@ pandora.ui.sectionButtons = function() {
buttons: [
{id: 'items', title: pandora.site.itemName.plural},
{id: 'edits', title: 'Edits', disabled: true},
{id: 'texts', title: 'Texts', disabled: true}
{id: 'texts', title: 'Texts', disabled: pandora.user.level != 'admin'}
],
id: 'sectionButtons',
selectable: true,
@ -17,13 +17,7 @@ pandora.ui.sectionButtons = function() {
.bindEvent({
change: function(data) {
var section = data.value;
if (section == 'items') {
pandora.URL.set(pandora.Query.toString());
} else if (section == 'clips') {
pandora.URL.set('clips');
} else if (section == 'texts') {
pandora.URL.set('texts');
}
pandora.UI.set({section: section});
}
});
return that;

58
static/js/pandora/text.js Normal file
View file

@ -0,0 +1,58 @@
'use strict';
pandora.ui.text = function() {
var ui = pandora.user.ui,
canEdit = pandora.site.capabilities.canEditSitePages[pandora.user.level],
that,
text,
$text;
getText(ui.text);
that = Ox.Element()
.bindEvent({
pandora_text: function(data) {
if (ui.text != text) {
text = ui.text;
getText(ui.text);
}
}
});
function getText(id) {
pandora.api.getText({id: id}, function(result) {
if (result.data.type == 'pdf') {
$text && $text.remove();
$text = Ox.Editable({
clickLink: pandora.clickLink,
editable: false,
type: 'textarea',
value: 'REPLACE ME WITH PDF VIEWER'
})
.appendTo(that);
} else {
var text = result.data ? result.data.text : '';
$text && $text.remove();
$text = Ox.Editable({
clickLink: pandora.clickLink,
editable: canEdit,
tooltip: canEdit ? 'Doubleclick to edit' : '',
type: 'textarea',
placeholder: 'No text',
value: text
})
.bindEvent({
submit: function(data) {
Ox.Request.clearCache('getText');
pandora.api.editText({
id: ui.text,
text: data.value
});
}
})
.appendTo(that);
}
});
}
return that;
}

View file

@ -147,10 +147,31 @@ pandora.addList = function() {
}).reloadList();
}
};
pandora.addText = function() {
var $folderList = pandora.$ui.folderList.personal;
pandora.api.addText({name: 'Untitled'}, function(result) {
reloadFolder(result.data.id);
});
function reloadFolder(newId) {
pandora.$ui.folder[0].options({collapsed: false});
Ox.Request.clearCache('findTexts');
$folderList.bindEventOnce({
load: function(data) {
$folderList.gainFocus()
.options({selected: [newId]})
.editCell(newId, 'name', true);
pandora.UI.set(pandora.user.ui.section.slice(0, -1), newId);
}
}).reloadList();
}
}
pandora.changeListStatus = function(id, status, callback) {
pandora.changeFolderItemStatus = function(id, status, callback) {
var ui = pandora.user.ui,
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
folderItem = folderItems.slice(0, -1);
if (status == 'private') {
pandora.api.findLists({
pandora.api['find' + folderItems]({
query: {conditions: [{key: 'id', value: id, operator: '=='}]},
keys: ['name', 'subscribers']
}, function(result) {
@ -168,14 +189,14 @@ pandora.changeListStatus = function(id, status, callback) {
}
}).open();
} else {
changeListStatus();
changeFolderItemStatus();
}
});
} else {
changeListStatus();
changeFolderItemStatus();
}
function changeListStatus() {
pandora.api.editList({
function changeFolderItemStatus() {
pandora.api['edit' + folderItem]({
id: id,
status: status
}, callback);
@ -606,7 +627,8 @@ pandora.getInfoHeight = function(includeHidden) {
return height;
}
pandora.getItemByIdOrTitle = function(str, callback) {
pandora.getItemByIdOrTitle = function(type, str, callback) {
if (type == pandora.site.itemName.plural.toLowerCase()) {
var sortKey = Ox.getObjectById(pandora.site.itemKeys, 'votes')
? 'votes'
: 'timesaccessed';
@ -642,6 +664,9 @@ pandora.getItemByIdOrTitle = function(str, callback) {
});
}
});
} else {
callback(str);
}
}
pandora.getItemFind = function(find) {
@ -706,7 +731,11 @@ pandora.getItemIdAndPosition = function() {
pandora.getListData = function(list) {
var data = {}, folder;
if (pandora.user.ui.section == 'items') {
list = Ox.isUndefined(list) ? pandora.user.ui._list : list;
} else {
list = Ox.isUndefined(list) ? pandora.user.ui[pandora.user.ui.section.slice(0, -1)] : list;
}
if (list) {
Ox.forEach(pandora.$ui.folderList, function($list, id) {
var ret = true;
@ -726,8 +755,12 @@ pandora.getListData = function(list) {
// FIXME: Is there a `return ret` statement missing here?
});
if (folder) {
data = pandora.$ui.folderList[folder].value(pandora.user.ui._list);
data = pandora.$ui.folderList[folder].value(list);
if (pandora.user.ui.section == 'item') {
data.editable = data.user == pandora.user.username && data.type == 'static';
} else {
data.editable = data.user == pandora.user.username;
}
data.folder = folder;
}
}
@ -1076,6 +1109,7 @@ pandora.renameList = function(oldId, newId, newName, folder) {
folder = folder || pandora.getListData(oldId).folder;
pandora.$ui.folderList[folder].value(oldId, 'name', newName);
pandora.$ui.folderList[folder].value(oldId, 'id', newId);
if (pandora.user.ui.section == 'items') {
pandora.$ui.toolbar.updateListName(newId);
pandora.UI.set({
find: {
@ -1083,17 +1117,20 @@ pandora.renameList = function(oldId, newId, newName, folder) {
operator: '&'
}
}, false);
} else {
pandora.UI.set(pandora.user.ui.section.slice(0, -1), newId);
}
};
pandora.resizeFilters = function(width) {
pandora.user.ui.filterSizes = pandora.getFilterSizes();
pandora.$ui.browser
pandora.$ui.browser && pandora.$ui.browser
.size(0, pandora.user.ui.filterSizes[0])
.size(2, pandora.user.ui.filterSizes[4]);
pandora.$ui.filtersInnerPanel
pandora.$ui.filtersInnerPanel && pandora.$ui.filtersInnerPanel
.size(0, pandora.user.ui.filterSizes[1])
.size(2, pandora.user.ui.filterSizes[3]);
pandora.$ui.filters.forEach(function($list, i) {
pandora.$ui.filters && pandora.$ui.filters.forEach(function($list, i) {
$list.resizeColumn('name', pandora.user.ui.filterSizes[i] - 44 - Ox.UI.SCROLLBAR_SIZE);
if (pandora.user.ui.showFlags) {
$list.find('.flagname').css({width: pandora.user.ui.filterSizes[i] - 68 - Ox.UI.SCROLLBAR_SIZE})
@ -1103,18 +1140,16 @@ pandora.resizeFilters = function(width) {
pandora.resizeFolders = function() {
var width = pandora.getFoldersWidth(),
columnWidth = {};
if (pandora.user.ui.section == 'items') {
columnWidth = {user: parseInt((width - 96) * 0.4)};
columnWidth.name = (width - 96) - columnWidth.user;
}
columnWidth = {},
sectionWidth = pandora.user.ui.section == 'items'? 96 : 32;
columnWidth = {user: parseInt((width - (sectionWidth)) * 0.4)};
columnWidth.name = (width - sectionWidth) - columnWidth.user;
Ox.Log('', 'RESIZE FOLDERS', width);
pandora.$ui.allItems.resizeElement(width - 104);
Ox.forEach(pandora.$ui.folderList, function($list, id) {
var pos = Ox.getIndexById(pandora.site.sectionFolders[pandora.user.ui.section], id);
pandora.$ui.folder[pos].css({width: width + 'px'});
$list.css({width: width + 'px'});
if (pandora.user.ui.section == 'items') {
if (pandora.site.sectionFolders[pandora.user.ui.section][pos].showBrowser) {
pandora.$ui.findListInput[id].options({
width: width - 24
@ -1124,7 +1159,6 @@ pandora.resizeFolders = function() {
} else {
$list.resizeColumn(id == 'favorite' ? 'id' : 'name', width - 96);
}
}
if (!pandora.user.ui.showFolder[pandora.user.ui.section][id]) {
pandora.$ui.folder[pos].updatePanel();
}
@ -1135,6 +1169,7 @@ pandora.resizeWindow = function() {
// FIXME: a lot of this throws errors on load
pandora.$ui.leftPanel && pandora.$ui.leftPanel.size(2, pandora.getInfoHeight(true));
pandora.resizeFolders();
if (pandora.user.ui.section == 'item') {
if (!pandora.user.ui.item) {
pandora.resizeFilters(pandora.$ui.rightPanel.width());
if (pandora.user.ui.listView == 'clips') {
@ -1192,9 +1227,11 @@ pandora.resizeWindow = function() {
pandora.$ui.calendar.resizeCalendar();
}
}
}
};
pandora.selectList = function() {
if (pandora.user.ui.section == 'items') {
if (pandora.user.ui._list) {
pandora.api.findLists({
keys: ['status', 'user'],
@ -1218,6 +1255,20 @@ pandora.selectList = function() {
}
});
}
} else {
var id = pandora.user.ui[pandora.user.ui.section.slice(0,-1)];
if (id) {
pandora.api.getText({id: id}, function(result) {
var folder;
if (result.data.id) {
folder = result.data.status == 'featured' ? 'featured' : (
result.data.user == pandora.user.username ? 'personal' : 'favorite'
);
pandora.$ui.folderList[folder].options({selected: [id]});
}
});
}
}
};
pandora.beforeunloadWindow = function() {