meta
This commit is contained in:
parent
edd42dfd76
commit
d385853186
48 changed files with 1344 additions and 488 deletions
16
config.json
16
config.json
|
@ -163,8 +163,12 @@
|
||||||
"id": "mediastate",
|
"id": "mediastate",
|
||||||
"title": "Media State",
|
"title": "Media State",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"find": true,
|
"sort": true,
|
||||||
"sort": true
|
"values": [
|
||||||
|
{"id": "available", "title": "Available"},
|
||||||
|
{"id": "transferring", "title": "Transferring"},
|
||||||
|
{"id": "unavailable", "title": "Unavailable"}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "transferadded",
|
"id": "transferadded",
|
||||||
|
@ -201,6 +205,13 @@
|
||||||
"columnWidth": 96,
|
"columnWidth": 96,
|
||||||
"sort": true
|
"sort": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "asin",
|
||||||
|
"title": "ASIN",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 96,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "lccn",
|
"id": "lccn",
|
||||||
"title": "LCCN",
|
"title": "LCCN",
|
||||||
|
@ -360,6 +371,7 @@
|
||||||
"showSidebar": true,
|
"showSidebar": true,
|
||||||
"sidebarSize": 256,
|
"sidebarSize": 256,
|
||||||
"theme": "oxlight",
|
"theme": "oxlight",
|
||||||
|
"updateAdvancedFindResults": false,
|
||||||
"usersSelection": []
|
"usersSelection": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
ctl
1
ctl
|
@ -44,7 +44,6 @@ if [ "$1" == "debug" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift
|
shift
|
||||||
echo Open browser at http://$HOST
|
|
||||||
python oml server $@
|
python oml server $@
|
||||||
exit $?
|
exit $?
|
||||||
fi
|
fi
|
||||||
|
|
28
migrations/versions/1a7c813a17c2_.py
Normal file
28
migrations/versions/1a7c813a17c2_.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 1a7c813a17c2
|
||||||
|
Revises: 7bb11a24276
|
||||||
|
Create Date: 2014-05-14 01:41:03.495320
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '1a7c813a17c2'
|
||||||
|
down_revision = '7bb11a24276'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('item', sa.Column('sort_asin', sa.String(length=1000), nullable=True))
|
||||||
|
op.create_index(op.f('ix_item_sort_asin'), 'item', ['sort_asin'], unique=False)
|
||||||
|
### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_index(op.f('ix_item_sort_asin'), table_name='item')
|
||||||
|
op.drop_column('item', 'sort_asin')
|
||||||
|
### end Alembic commands ###
|
36
migrations/versions/21589282102d_.py
Normal file
36
migrations/versions/21589282102d_.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 21589282102d
|
||||||
|
Revises: 2350803a5a2d
|
||||||
|
Create Date: 2014-05-13 15:47:29.747858
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '21589282102d'
|
||||||
|
down_revision = '2350803a5a2d'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('filter',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('item_id', sa.String(length=32), nullable=True),
|
||||||
|
sa.Column('key', sa.String(length=200), nullable=True),
|
||||||
|
sa.Column('value', sa.Text(), nullable=True),
|
||||||
|
sa.Column('findvalue', sa.Text(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['item_id'], ['item.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_filter_key'), 'filter', ['key'], unique=False)
|
||||||
|
### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_index(op.f('ix_filter_key'), table_name='filter')
|
||||||
|
op.drop_table('filter')
|
||||||
|
### end Alembic commands ###
|
26
migrations/versions/2350803a5a2d_.py
Normal file
26
migrations/versions/2350803a5a2d_.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 2350803a5a2d
|
||||||
|
Revises: 1ead68a53597
|
||||||
|
Create Date: 2014-05-13 15:43:51.840049
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '2350803a5a2d'
|
||||||
|
down_revision = '1ead68a53597'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
pass
|
||||||
|
### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
pass
|
||||||
|
### end Alembic commands ###
|
36
migrations/versions/7bb11a24276_.py
Normal file
36
migrations/versions/7bb11a24276_.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 7bb11a24276
|
||||||
|
Revises: 21589282102d
|
||||||
|
Create Date: 2014-05-13 18:28:46.214059
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '7bb11a24276'
|
||||||
|
down_revision = '21589282102d'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('filter')
|
||||||
|
op.add_column('find', sa.Column('findvalue', sa.Text(), nullable=True))
|
||||||
|
### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('find', 'findvalue')
|
||||||
|
op.create_table('filter',
|
||||||
|
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||||
|
sa.Column('item_id', sa.VARCHAR(length=32), nullable=True),
|
||||||
|
sa.Column('key', sa.VARCHAR(length=200), nullable=True),
|
||||||
|
sa.Column('value', sa.TEXT(), nullable=True),
|
||||||
|
sa.Column('findvalue', sa.TEXT(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['item_id'], [u'item.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
### end Alembic commands ###
|
|
@ -49,7 +49,7 @@ class Changelog(db.Model):
|
||||||
db.session.add(c)
|
db.session.add(c)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
if state.online:
|
if state.online:
|
||||||
state.nodes.queue('online', 'pushChanges', [c.json()])
|
state.nodes.queue('peered', 'pushChanges', [c.json()])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timestamp(self):
|
def timestamp(self):
|
||||||
|
@ -123,6 +123,7 @@ class Changelog(db.Model):
|
||||||
i = Item.get_or_create(itemid, info)
|
i = Item.get_or_create(itemid, info)
|
||||||
i.users.append(user)
|
i.users.append(user)
|
||||||
i.update()
|
i.update()
|
||||||
|
trigger_event('itemchange', {'fixme': 'new remote changes'})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_edititem(self, user, timestamp, itemid, meta):
|
def action_edititem(self, user, timestamp, itemid, meta):
|
||||||
|
@ -137,6 +138,7 @@ class Changelog(db.Model):
|
||||||
elif meta[key] and (i.meta.get('mainid') != key or meta[key] != i.meta.get(key)):
|
elif meta[key] and (i.meta.get('mainid') != key or meta[key] != i.meta.get(key)):
|
||||||
print 'new mapping', key, meta[key], 'currently', i.meta.get('mainid'), i.meta.get(i.meta.get('mainid'))
|
print 'new mapping', key, meta[key], 'currently', i.meta.get('mainid'), i.meta.get(i.meta.get('mainid'))
|
||||||
i.update_mainid(key, meta[key])
|
i.update_mainid(key, meta[key])
|
||||||
|
trigger_event('itemchange', {'fixme': 'new remote changes'})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_removeitem(self, user, timestamp, itemid):
|
def action_removeitem(self, user, timestamp, itemid):
|
||||||
|
@ -150,6 +152,7 @@ class Changelog(db.Model):
|
||||||
else:
|
else:
|
||||||
db.session.delete(i)
|
db.session.delete(i)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
trigger_event('itemchange', {'fixme': 'new remote changes'})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_addlist(self, user, timestamp, name, query=None):
|
def action_addlist(self, user, timestamp, name, query=None):
|
||||||
|
|
|
@ -25,44 +25,21 @@ def find(request):
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
q = query.parse(data)
|
q = query.parse(data)
|
||||||
if 'group' in q:
|
if 'group' in q:
|
||||||
response['items'] = []
|
names = {}
|
||||||
'''
|
groups = {}
|
||||||
items = 'items'
|
items = [i.id for i in q['qs']]
|
||||||
item_qs = q['qs']
|
qs = models.Find.query.filter_by(key=q['group'])
|
||||||
order_by = query.order_by_group(q)
|
if items:
|
||||||
qs = models.Facet.objects.filter(key=q['group']).filter(item__id__in=item_qs)
|
qs = qs.filter(models.Find.item_id.in_(items))
|
||||||
qs = qs.values('value').annotate(items=Count('id')).order_by(*order_by)
|
for f in qs.values('value', 'findvalue'):
|
||||||
|
value = f[0]
|
||||||
if 'positions' in q:
|
findvalue = f[1]
|
||||||
response['positions'] = {}
|
if findvalue not in groups:
|
||||||
ids = [j['value'] for j in qs]
|
groups[findvalue] = 0
|
||||||
response['positions'] = utils.get_positions(ids, q['positions'])
|
groups[findvalue] += 1
|
||||||
elif 'range' in data:
|
names[findvalue] = value
|
||||||
qs = qs[q['range'][0]:q['range'][1]]
|
g = [{'name': names[k], 'items': groups[k]} for k in groups]
|
||||||
response['items'] = [{'name': i['value'], 'items': i[items]} for i in qs]
|
if 'sort' in q:
|
||||||
else:
|
|
||||||
response['items'] = qs.count()
|
|
||||||
'''
|
|
||||||
_g = {}
|
|
||||||
key = utils.get_by_id(settings.config['itemKeys'], q['group'])
|
|
||||||
for item in q['qs']:
|
|
||||||
i = item.json()
|
|
||||||
if q['group'] in i:
|
|
||||||
values = i[q['group']]
|
|
||||||
if isinstance(values, basestring):
|
|
||||||
values = [values]
|
|
||||||
for value in values:
|
|
||||||
if key.get('filterMap') and value:
|
|
||||||
value = re.compile(key.get('filterMap')).findall(value)
|
|
||||||
if value:
|
|
||||||
value = value[0]
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
if value not in _g:
|
|
||||||
_g[value] = 0
|
|
||||||
_g[value] += 1
|
|
||||||
g = [{'name': k, 'items': _g[k]} for k in _g]
|
|
||||||
if 'sort' in data: # parse adds default sort to q!
|
|
||||||
g.sort(key=lambda k: k[q['sort'][0]['key']])
|
g.sort(key=lambda k: k[q['sort'][0]['key']])
|
||||||
if q['sort'][0]['operator'] == '-':
|
if q['sort'][0]['operator'] == '-':
|
||||||
g.reverse()
|
g.reverse()
|
||||||
|
@ -90,9 +67,11 @@ def find(request):
|
||||||
j = i.json()
|
j = i.json()
|
||||||
response['items'].append({k:j[k] for k in j if not data['keys'] or k in data['keys']})
|
response['items'].append({k:j[k] for k in j if not data['keys'] or k in data['keys']})
|
||||||
else:
|
else:
|
||||||
items = [i.json() for i in q['qs']]
|
response['items'] = q['qs'].count()
|
||||||
response['items'] = len(items)
|
#from sqlalchemy.sql import func
|
||||||
response['size'] = sum([i.get('size',0) for i in items])
|
#models.db.session.query(func.sum(models.Item.sort_size).label("size"))
|
||||||
|
#response['size'] = x.scalar()
|
||||||
|
response['size'] = sum([i.sort_size or 0 for i in q['qs']])
|
||||||
return response
|
return response
|
||||||
actions.register(find)
|
actions.register(find)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ from user.models import User
|
||||||
from person import get_sort_name
|
from person import get_sort_name
|
||||||
|
|
||||||
import media
|
import media
|
||||||
from meta import scraper
|
import meta
|
||||||
|
|
||||||
import state
|
import state
|
||||||
import utils
|
import utils
|
||||||
|
@ -201,6 +201,13 @@ class Item(db.Model):
|
||||||
setattr(self, 'sort_%s' % key['id'], value)
|
setattr(self, 'sort_%s' % key['id'], value)
|
||||||
|
|
||||||
def update_find(self):
|
def update_find(self):
|
||||||
|
|
||||||
|
def add(k, v):
|
||||||
|
f = Find(item_id=self.id, key=k)
|
||||||
|
f.findvalue = v.lower().strip()
|
||||||
|
f.value = v
|
||||||
|
db.session.add(f)
|
||||||
|
|
||||||
for key in config['itemKeys']:
|
for key in config['itemKeys']:
|
||||||
if key.get('find') or key.get('filter'):
|
if key.get('find') or key.get('filter'):
|
||||||
value = self.json().get(key['id'], None)
|
value = self.json().get(key['id'], None)
|
||||||
|
@ -208,16 +215,11 @@ class Item(db.Model):
|
||||||
value = re.compile(key.get('filterMap')).findall(value)
|
value = re.compile(key.get('filterMap')).findall(value)
|
||||||
if value: value = value[0]
|
if value: value = value[0]
|
||||||
if value:
|
if value:
|
||||||
if isinstance(value, list):
|
Find.query.filter_by(item_id=self.id, key=key['id']).delete()
|
||||||
Find.query.filter_by(item_id=self.id, key=key['id']).delete()
|
if not isinstance(value, list):
|
||||||
for v in value:
|
value = [value]
|
||||||
f = Find(item_id=self.id, key=key['id'])
|
for v in value:
|
||||||
f.value = v.lower()
|
add(key['id'], v)
|
||||||
db.session.add(f)
|
|
||||||
else:
|
|
||||||
f = Find.get_or_create(self.id, key['id'])
|
|
||||||
f.value = value.lower()
|
|
||||||
db.session.add(f)
|
|
||||||
else:
|
else:
|
||||||
f = Find.get(self.id, key['id'])
|
f = Find.get(self.id, key['id'])
|
||||||
if f:
|
if f:
|
||||||
|
@ -313,16 +315,9 @@ class Item(db.Model):
|
||||||
def scrape(self):
|
def scrape(self):
|
||||||
mainid = self.meta.get('mainid')
|
mainid = self.meta.get('mainid')
|
||||||
print 'scrape', mainid, self.meta.get(mainid)
|
print 'scrape', mainid, self.meta.get(mainid)
|
||||||
if mainid == 'olid':
|
if mainid:
|
||||||
scraper.update_ol(self)
|
m = meta.lookup(mainid, self.meta[mainid])
|
||||||
scraper.add_lookupbyisbn(self)
|
self.meta.update(m)
|
||||||
elif mainid in ('isbn10', 'isbn13'):
|
|
||||||
scraper.add_lookupbyisbn(self)
|
|
||||||
elif mainid == 'lccn':
|
|
||||||
import meta.lccn
|
|
||||||
info = meta.lccn.info(self.meta[mainid])
|
|
||||||
for key in info:
|
|
||||||
self.meta[key] = info[key]
|
|
||||||
else:
|
else:
|
||||||
print 'FIX UPDATE', mainid
|
print 'FIX UPDATE', mainid
|
||||||
self.update()
|
self.update()
|
||||||
|
@ -380,7 +375,7 @@ for key in config['itemKeys']:
|
||||||
|
|
||||||
Item.id_keys = ['isbn10', 'isbn13', 'lccn', 'olid', 'oclc']
|
Item.id_keys = ['isbn10', 'isbn13', 'lccn', 'olid', 'oclc']
|
||||||
Item.item_keys = config['itemKeys']
|
Item.item_keys = config['itemKeys']
|
||||||
Item.filter_keys = []
|
Item.filter_keys = [k['id'] for k in config['itemKeys'] if k.get('filter')]
|
||||||
|
|
||||||
class Find(db.Model):
|
class Find(db.Model):
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
|
@ -388,9 +383,10 @@ class Find(db.Model):
|
||||||
item = db.relationship('Item', backref=db.backref('find', lazy='dynamic'))
|
item = db.relationship('Item', backref=db.backref('find', lazy='dynamic'))
|
||||||
key = db.Column(db.String(200), index=True)
|
key = db.Column(db.String(200), index=True)
|
||||||
value = db.Column(db.Text())
|
value = db.Column(db.Text())
|
||||||
|
findvalue = db.Column(db.Text())
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return (u'%s=%s' % (self.key, self.value)).encode('utf-8')
|
return (u'%s=%s' % (self.key, self.findvalue)).encode('utf-8')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, item, key):
|
def get(cls, item, key):
|
||||||
|
|
|
@ -11,7 +11,8 @@ from sqlalchemy.sql.expression import nullslast
|
||||||
def parse(data):
|
def parse(data):
|
||||||
query = {}
|
query = {}
|
||||||
query['range'] = [0, 100]
|
query['range'] = [0, 100]
|
||||||
query['sort'] = [{'key':'title', 'operator':'+'}]
|
if not 'group' in data:
|
||||||
|
query['sort'] = [{'key':'title', 'operator':'+'}]
|
||||||
for key in ('keys', 'group', 'list', 'range', 'sort', 'query'):
|
for key in ('keys', 'group', 'list', 'range', 'sort', 'query'):
|
||||||
if key in data:
|
if key in data:
|
||||||
query[key] = data[key]
|
query[key] = data[key]
|
||||||
|
@ -25,9 +26,7 @@ def parse(data):
|
||||||
query['qs'] = models.Item.query.join(
|
query['qs'] = models.Item.query.join(
|
||||||
models.Find, models.Find.item_id==models.Item.id).filter(
|
models.Find, models.Find.item_id==models.Item.id).filter(
|
||||||
models.Find.value.contains(value))
|
models.Find.value.contains(value))
|
||||||
if 'group' in query:
|
if not 'group' in query:
|
||||||
query['qs'] = order_by_group(query['qs'], query['sort'])
|
|
||||||
else:
|
|
||||||
query['qs'] = order(query['qs'], query['sort'])
|
query['qs'] = order(query['qs'], query['sort'])
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
@ -61,23 +60,3 @@ def order(qs, sort, prefix='sort_'):
|
||||||
order_by = _order_by
|
order_by = _order_by
|
||||||
qs = qs.order_by(*order_by)
|
qs = qs.order_by(*order_by)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def order_by_group(qs, sort):
|
|
||||||
return qs
|
|
||||||
if 'sort' in query:
|
|
||||||
if len(query['sort']) == 1 and query['sort'][0]['key'] == 'items':
|
|
||||||
order_by = query['sort'][0]['operator'] == '-' and '-items' or 'items'
|
|
||||||
if query['group'] == "year":
|
|
||||||
secondary = query['sort'][0]['operator'] == '-' and '-sortvalue' or 'sortvalue'
|
|
||||||
order_by = (order_by, secondary)
|
|
||||||
elif query['group'] != "keyword":
|
|
||||||
order_by = (order_by, 'sortvalue')
|
|
||||||
else:
|
|
||||||
order_by = (order_by, 'value')
|
|
||||||
else:
|
|
||||||
order_by = query['sort'][0]['operator'] == '-' and '-sortvalue' or 'sortvalue'
|
|
||||||
order_by = (order_by, 'items')
|
|
||||||
else:
|
|
||||||
order_by = ('-sortvalue', 'items')
|
|
||||||
return order_by
|
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ class LocalNodes(Thread):
|
||||||
'username': preferences.get('username', 'anonymous'),
|
'username': preferences.get('username', 'anonymous'),
|
||||||
'host': self.host,
|
'host': self.host,
|
||||||
'port': server['node_port'],
|
'port': server['node_port'],
|
||||||
|
'cert': server['cert']
|
||||||
})
|
})
|
||||||
sig = sk.sign(message, encoding='base64')
|
sig = sk.sign(message, encoding='base64')
|
||||||
packet = json.dumps([sig, USER_ID, message])
|
packet = json.dumps([sig, USER_ID, message])
|
||||||
|
@ -65,11 +66,12 @@ class LocalNodes(Thread):
|
||||||
data = data[:-1] # Strip trailing \0's
|
data = data[:-1] # Strip trailing \0's
|
||||||
data = self.verify(data)
|
data = self.verify(data)
|
||||||
if data:
|
if data:
|
||||||
if data['id'] not in self._nodes:
|
if data['id'] != USER_ID:
|
||||||
thread.start_new_thread(self.new_node, (data, ))
|
if data['id'] not in self._nodes:
|
||||||
else:
|
thread.start_new_thread(self.new_node, (data, ))
|
||||||
print 'UPDATE NODE', data
|
#else:
|
||||||
self._nodes[data['id']] = data
|
# print 'UPDATE NODE', data
|
||||||
|
self._nodes[data['id']] = data
|
||||||
|
|
||||||
def verify(self, data):
|
def verify(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -81,7 +83,7 @@ class LocalNodes(Thread):
|
||||||
if valid(user_id, data, sig):
|
if valid(user_id, data, sig):
|
||||||
message = json.loads(data)
|
message = json.loads(data)
|
||||||
message['id'] = user_id
|
message['id'] = user_id
|
||||||
for key in ['id', 'username', 'host', 'port']:
|
for key in ['id', 'username', 'host', 'port', 'cert']:
|
||||||
if key not in message:
|
if key not in message:
|
||||||
return None
|
return None
|
||||||
return message
|
return message
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
|
import abebooks
|
||||||
|
import loc
|
||||||
|
import lookupbyisbn
|
||||||
|
import openlibrary
|
||||||
|
import worldcat
|
||||||
|
|
||||||
|
providers = [
|
||||||
|
('openlibrary', 'olid'),
|
||||||
|
('loc', 'lccn'),
|
||||||
|
('worldcat', 'oclc'),
|
||||||
|
('lookupbyisbn', 'asin'),
|
||||||
|
('abebooks', 'isbn10')
|
||||||
|
]
|
||||||
|
|
||||||
|
def find(title, author=None, publisher=None, year=None):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def lookup(key, value):
|
||||||
|
data = {key: value}
|
||||||
|
ids = [(key, value)]
|
||||||
|
provider_data = {}
|
||||||
|
done = False
|
||||||
|
while not done:
|
||||||
|
done = True
|
||||||
|
for provider, id in providers:
|
||||||
|
for key, value in ids:
|
||||||
|
for kv in globals()[provider].get_ids(key, value):
|
||||||
|
if not kv in ids:
|
||||||
|
ids.append(kv)
|
||||||
|
done = False
|
||||||
|
print ids
|
||||||
|
for k, v in ids:
|
||||||
|
for provider, id in providers:
|
||||||
|
if id == k:
|
||||||
|
provider_data[provider] = globals()[provider].lookup(v)
|
||||||
|
for provider in sorted(
|
||||||
|
provider_data.keys(),
|
||||||
|
key=lambda x: -len(provider_data[x])
|
||||||
|
):
|
||||||
|
print provider, len(provider_data[provider])
|
||||||
|
for k_, v_ in provider_data[provider].iteritems():
|
||||||
|
if not k_ in data:
|
||||||
|
data[k_] = v_
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
38
oml/meta/abebooks.py
Normal file
38
oml/meta/abebooks.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
from ox.cache import read_url
|
||||||
|
import re
|
||||||
|
import lxml.html
|
||||||
|
|
||||||
|
def get_ids(key, value):
|
||||||
|
ids = []
|
||||||
|
if key in ('isbn10', 'isbn13'):
|
||||||
|
base = 'http://www.abebooks.com'
|
||||||
|
url = '%s/servlet/SearchResults?isbn=%s&sts=t' % (base, id)
|
||||||
|
data = read_url(url)
|
||||||
|
urls = re.compile('href="(/servlet/BookDetailsPL[^"]+)"').findall(data)
|
||||||
|
if urls:
|
||||||
|
ids.append((key, value))
|
||||||
|
if ids:
|
||||||
|
print 'abebooks.get_ids', key, value
|
||||||
|
print ids
|
||||||
|
return ids
|
||||||
|
|
||||||
|
def lookup(id):
|
||||||
|
print 'abebooks.lookup', id
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_data(id):
|
||||||
|
info = {}
|
||||||
|
base = 'http://www.abebooks.com'
|
||||||
|
url = '%s/servlet/SearchResults?isbn=%s&sts=t' % (base, id)
|
||||||
|
data = read_url(url)
|
||||||
|
urls = re.compile('href="(/servlet/BookDetailsPL[^"]+)"').findall(data)
|
||||||
|
if urls:
|
||||||
|
details = '%s%s' % (base, urls[0])
|
||||||
|
data = read_url(details)
|
||||||
|
doc = lxml.html.document_fromstring(data)
|
||||||
|
for e in doc.xpath("//*[contains(@id, 'biblio')]"):
|
||||||
|
key = e.attrib['id'].replace('biblio-', '')
|
||||||
|
value = e.text_content()
|
||||||
|
if value and key not in ('bookcondition', 'binding'):
|
||||||
|
info[key] = value
|
||||||
|
return info
|
|
@ -4,18 +4,35 @@ from __future__ import division
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from ox.cache import read_url
|
from ox.cache import read_url
|
||||||
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from utils import normalize_isbn
|
from utils import normalize_isbn
|
||||||
from marc_countries import COUNTRIES
|
from marc_countries import COUNTRIES
|
||||||
|
|
||||||
def info(id):
|
def get_ids(key, value):
|
||||||
|
ids = []
|
||||||
|
if key in ['isbn10', 'isbn13']:
|
||||||
|
url = 'http://www.loc.gov/search/?q=%s&all=true' % value
|
||||||
|
html = ox.cache.read_url(url)
|
||||||
|
match = re.search('"http://lccn.loc.gov/(\d+)"', html)
|
||||||
|
if match:
|
||||||
|
ids.append(('lccn', match.group(1)))
|
||||||
|
if ids:
|
||||||
|
print 'loc.get_ids', key, value
|
||||||
|
print ids
|
||||||
|
return ids
|
||||||
|
|
||||||
|
def lookup(id):
|
||||||
|
print 'loc.lookup', id
|
||||||
ns = '{http://www.loc.gov/mods/v3}'
|
ns = '{http://www.loc.gov/mods/v3}'
|
||||||
url = 'http://lccn.loc.gov/%s/mods' % id
|
url = 'http://lccn.loc.gov/%s/mods' % id
|
||||||
data = read_url(url)
|
data = read_url(url)
|
||||||
mods = ET.fromstring(data)
|
mods = ET.fromstring(data)
|
||||||
|
|
||||||
info = {}
|
info = {
|
||||||
|
'lccn': id
|
||||||
|
}
|
||||||
info['title'] = ''.join([e.text for e in mods.findall(ns + 'titleInfo')[0]])
|
info['title'] = ''.join([e.text for e in mods.findall(ns + 'titleInfo')[0]])
|
||||||
origin = mods.findall(ns + 'originInfo')
|
origin = mods.findall(ns + 'originInfo')
|
||||||
if origin:
|
if origin:
|
||||||
|
@ -28,7 +45,9 @@ def info(id):
|
||||||
elif terms and terms[0].attrib['type'] == 'code':
|
elif terms and terms[0].attrib['type'] == 'code':
|
||||||
e = terms[0]
|
e = terms[0]
|
||||||
info['country'] = COUNTRIES.get(e.text, e.text)
|
info['country'] = COUNTRIES.get(e.text, e.text)
|
||||||
info['publisher'] = ''.join([e.text for e in origin[0].findall(ns + 'publisher')])
|
publisher = [e.text for e in origin[0].findall(ns + 'publisher')]
|
||||||
|
if publisher:
|
||||||
|
info['publisher'] = publisher[0]
|
||||||
info['date'] = ''.join([e.text for e in origin[0].findall(ns + 'dateIssued')])
|
info['date'] = ''.join([e.text for e in origin[0].findall(ns + 'dateIssued')])
|
||||||
for i in mods.findall(ns + 'identifier'):
|
for i in mods.findall(ns + 'identifier'):
|
||||||
if i.attrib['type'] == 'oclc':
|
if i.attrib['type'] == 'oclc':
|
||||||
|
@ -43,10 +62,12 @@ def info(id):
|
||||||
info['classification'] = i.text
|
info['classification'] = i.text
|
||||||
info['author'] = []
|
info['author'] = []
|
||||||
for a in mods.findall(ns + 'name'):
|
for a in mods.findall(ns + 'name'):
|
||||||
if a.attrib['usage'] == 'primary':
|
if a.attrib.get('usage') == 'primary':
|
||||||
info['author'].append(''.join([e.text for e in a.findall(ns + 'namePart')]))
|
info['author'].append(' '.join([e.text for e in a.findall(ns + 'namePart') if not e.attrib.get('type') in ('date', )]))
|
||||||
info['author'] = [ox.normalize_name(a[:-1]) for a in info['author']]
|
info['author'] = [ox.normalize_name(a) for a in info['author']]
|
||||||
for key in info.keys():
|
for key in info.keys():
|
||||||
if not info[key]:
|
if not info[key]:
|
||||||
del info[key]
|
del info[key]
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
info = lookup
|
53
oml/meta/lookupbyisbn.py
Normal file
53
oml/meta/lookupbyisbn.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
from ox.cache import read_url
|
||||||
|
from ox import find_re, strip_tags
|
||||||
|
import re
|
||||||
|
|
||||||
|
base = 'http://www.lookupbyisbn.com'
|
||||||
|
|
||||||
|
def get_ids(key, value):
|
||||||
|
ids = []
|
||||||
|
if key in ('isbn10', 'isbn13', 'asin'):
|
||||||
|
url = '%s/Search/Book/%s/1' % (base, value)
|
||||||
|
data = read_url(url).decode('utf-8')
|
||||||
|
m = re.compile('href="(/Lookup/Book/[^"]+?)"').findall(data)
|
||||||
|
if m:
|
||||||
|
asin = m[0].split('/')[-3]
|
||||||
|
ids.append(('asin', asin))
|
||||||
|
if ids:
|
||||||
|
print 'lookupbyisbn.get_ids', key, value
|
||||||
|
print ids
|
||||||
|
return ids
|
||||||
|
|
||||||
|
def lookup(id):
|
||||||
|
print 'lookupbyisbn.lookup', id
|
||||||
|
r = {
|
||||||
|
'asin': id
|
||||||
|
}
|
||||||
|
url = '%s/Lookup/Book/%s/%s/1' % (base, id, id)
|
||||||
|
data = read_url(url).decode('utf-8')
|
||||||
|
r["title"] = find_re(data, "<h2>(.*?)</h2>")
|
||||||
|
keys = {
|
||||||
|
'author': 'Author(s)',
|
||||||
|
'publisher': 'Publisher',
|
||||||
|
'date': 'Publication date',
|
||||||
|
'edition': 'Edition',
|
||||||
|
'binding': 'Binding',
|
||||||
|
'volume': 'Volume(s)',
|
||||||
|
'pages': 'Pages',
|
||||||
|
}
|
||||||
|
for key in keys:
|
||||||
|
r[key] = find_re(data, '<span class="title">%s:</span>(.*?)</li>'% re.escape(keys[key]))
|
||||||
|
if r[key] == '--':
|
||||||
|
r[key] = ''
|
||||||
|
if key == 'pages' and r[key]:
|
||||||
|
r[key] = int(r[key])
|
||||||
|
desc = find_re(data, '<h2>Description:<\/h2>(.*?)<div ')
|
||||||
|
desc = desc.replace('<br /><br />', ' ').replace('<br /> ', ' ').replace('<br />', ' ')
|
||||||
|
r['description'] = strip_tags(desc).strip()
|
||||||
|
if r['description'] == u'Description of this item is not available at this time.':
|
||||||
|
r['description'] = ''
|
||||||
|
r['cover'] = find_re(data, '<img src="(.*?)" alt="Book cover').replace('._SL160_', '')
|
||||||
|
if 'author' in r and isinstance(r['author'], basestring):
|
||||||
|
r['author'] = [r['author']]
|
||||||
|
return r
|
||||||
|
|
|
@ -5,30 +5,39 @@ from __future__ import division
|
||||||
from ox.cache import read_url
|
from ox.cache import read_url
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from utils import normalize_isbn
|
|
||||||
from marc_countries import COUNTRIES
|
from marc_countries import COUNTRIES
|
||||||
|
from utils import normalize_isbn
|
||||||
|
|
||||||
def find(query):
|
def get_ids(key, value):
|
||||||
url = 'https://openlibrary.org/search.json?q=%s' % query
|
ids = []
|
||||||
data = json.loads(read_url(url))
|
if key == 'olid':
|
||||||
return data
|
data = lookup(value, True)
|
||||||
|
for id in ('isbn10', 'isbn13', 'lccn', 'oclc'):
|
||||||
|
if id in data:
|
||||||
|
for v in data[id]:
|
||||||
|
if (id, v) not in ids:
|
||||||
|
ids.append((id, v))
|
||||||
|
elif key in ('isbn10', 'isbn13'):
|
||||||
|
print 'openlibraryid.get_ids', key, value
|
||||||
|
r = find('isbn:%s' % value)
|
||||||
|
for d in sorted(r.get('docs', []), key=lambda d: -d['last_modified_i']):
|
||||||
|
if 'edition_key' in d:
|
||||||
|
v = d['edition_key']
|
||||||
|
if isinstance(v, list):
|
||||||
|
v = v[0]
|
||||||
|
for kv in [('olid', v)] + get_ids('olid', v):
|
||||||
|
if kv not in ids:
|
||||||
|
ids.append(kv)
|
||||||
|
if ids:
|
||||||
|
print 'openlibraryid.get_ids', key, value
|
||||||
|
print ids
|
||||||
|
return ids
|
||||||
|
|
||||||
def authors(authors):
|
def lookup(id, return_all=False):
|
||||||
return resolve_names(authors)
|
#print 'openlibrary.lookup', id
|
||||||
|
data = {
|
||||||
def resolve_names(objects, key='name'):
|
'olid': id
|
||||||
r = []
|
}
|
||||||
for o in objects:
|
|
||||||
url = 'https://openlibrary.org%s.json' % o['key']
|
|
||||||
data = json.loads(read_url(url))
|
|
||||||
r.append(data[key])
|
|
||||||
return r
|
|
||||||
|
|
||||||
def languages(languages):
|
|
||||||
return resolve_names(languages)
|
|
||||||
|
|
||||||
def info(id):
|
|
||||||
data = {}
|
|
||||||
url = 'https://openlibrary.org/books/%s.json' % id
|
url = 'https://openlibrary.org/books/%s.json' % id
|
||||||
info = json.loads(read_url(url))
|
info = json.loads(read_url(url))
|
||||||
keys = {
|
keys = {
|
||||||
|
@ -58,10 +67,34 @@ def info(id):
|
||||||
value = COUNTRIES.get(value, value)
|
value = COUNTRIES.get(value, value)
|
||||||
elif key == 'languages':
|
elif key == 'languages':
|
||||||
value = languages(value)
|
value = languages(value)
|
||||||
elif isinstance(value, list) and key not in ('publish_places'):
|
elif not return_all and isinstance(value, list) and key not in ('publish_places'):
|
||||||
value = value[0]
|
value = value[0]
|
||||||
if key in ('isbn_10', 'isbn_13'):
|
if key in ('isbn_10', 'isbn_13'):
|
||||||
value = normalize_isbn(value)
|
if isinstance(value, list):
|
||||||
|
value = map(normalize_isbn, value)
|
||||||
|
else:
|
||||||
|
value = normalize_isbn(value)
|
||||||
data[keys[key]] = value
|
data[keys[key]] = value
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
info = lookup
|
||||||
|
|
||||||
|
def find(query):
|
||||||
|
url = 'https://openlibrary.org/search.json?q=%s' % query
|
||||||
|
data = json.loads(read_url(url))
|
||||||
|
return data
|
||||||
|
|
||||||
|
def authors(authors):
|
||||||
|
return resolve_names(authors)
|
||||||
|
|
||||||
|
def resolve_names(objects, key='name'):
|
||||||
|
r = []
|
||||||
|
for o in objects:
|
||||||
|
url = 'https://openlibrary.org%s.json' % o['key']
|
||||||
|
data = json.loads(read_url(url))
|
||||||
|
r.append(data[key])
|
||||||
|
return r
|
||||||
|
|
||||||
|
def languages(languages):
|
||||||
|
return resolve_names(languages)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import ox.web.lookupbyisbn
|
||||||
|
|
||||||
from utils import normalize_isbn
|
from utils import normalize_isbn
|
||||||
|
|
||||||
import ol
|
import openlibrary as ol
|
||||||
|
|
||||||
def add_lookupbyisbn(item):
|
def add_lookupbyisbn(item):
|
||||||
isbn = item.meta.get('isbn10', item.meta.get('isbn13'))
|
isbn = item.meta.get('isbn10', item.meta.get('isbn13'))
|
||||||
|
|
5
oml/meta/utils.py
Normal file
5
oml/meta/utils.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_isbn(value):
|
||||||
|
return ''.join([s for s in value if s.isdigit() or s == 'X'])
|
||||||
|
|
69
oml/meta/worldcat.py
Normal file
69
oml/meta/worldcat.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
|
from ox.cache import read_url
|
||||||
|
import lxml.html
|
||||||
|
import re
|
||||||
|
from utils import normalize_isbn
|
||||||
|
import stdnum.isbn
|
||||||
|
|
||||||
|
base_url = 'http://www.worldcat.org'
|
||||||
|
|
||||||
|
def get_ids(key, value):
|
||||||
|
ids = []
|
||||||
|
if key in ['isbn10', 'isbn13']:
|
||||||
|
url = '%s/search?qt=worldcat_org_bks&q=%s' % (base_url, value)
|
||||||
|
html = read_url(url)
|
||||||
|
matches = re.compile('/title.*?oclc/(\d+).*?"').findall(html)
|
||||||
|
if matches:
|
||||||
|
info = lookup(matches[0])
|
||||||
|
ids.append(('oclc', matches[0]))
|
||||||
|
for k in ['isbn10', 'isbn13']:
|
||||||
|
if k in info and k != key:
|
||||||
|
ids.append((k, info[k]))
|
||||||
|
elif key == 'oclc':
|
||||||
|
info = lookup(value)
|
||||||
|
for k in ['isbn10', 'isbn13']:
|
||||||
|
if k in info:
|
||||||
|
ids.append((k, info[k]))
|
||||||
|
if ids:
|
||||||
|
print 'worldcat.get_ids', key, value
|
||||||
|
print ids
|
||||||
|
return ids
|
||||||
|
|
||||||
|
def lookup(id):
|
||||||
|
data = {
|
||||||
|
'oclc': id
|
||||||
|
}
|
||||||
|
url = '%s/oclc/%s' % (base_url, id)
|
||||||
|
html = read_url(url).decode('utf-8')
|
||||||
|
doc = lxml.html.document_fromstring(html)
|
||||||
|
for e in doc.xpath("//*[contains(@id, 'bibtip')]"):
|
||||||
|
key = e.attrib['id'].replace('bibtip_', '')
|
||||||
|
value = e.text_content()
|
||||||
|
data[key] = value
|
||||||
|
info = doc.xpath('//textarea[@id="util-em-note"]')[0].text
|
||||||
|
info = dict([i.split(':', 1) for i in info.split('\n\n')[1].split('\n')])
|
||||||
|
for key in info:
|
||||||
|
k = key.lower()
|
||||||
|
data[k] = info[key].strip()
|
||||||
|
for key in ('id', 'instance', 'mediatype', 'reclist', 'shorttitle'):
|
||||||
|
if key in data:
|
||||||
|
del data[key]
|
||||||
|
if 'isxn' in data:
|
||||||
|
for isbn in data.pop('isxn').split(' '):
|
||||||
|
isbn = normalize_isbn(isbn)
|
||||||
|
if stdnum.isbn.is_valid(isbn):
|
||||||
|
data['isbn%d'%len(isbn)] = isbn
|
||||||
|
if 'author' in data:
|
||||||
|
data['author'] = [data['author']]
|
||||||
|
print 'worldcat.lookup', id
|
||||||
|
print data.keys()
|
||||||
|
return data
|
||||||
|
|
||||||
|
info = lookup
|
||||||
|
|
||||||
|
def find(title, author, year):
|
||||||
|
return []
|
||||||
|
|
42
oml/node/cert.py
Normal file
42
oml/node/cert.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
import OpenSSL
|
||||||
|
import settings
|
||||||
|
|
||||||
|
def get_fingerprint():
|
||||||
|
with open(settings.ssl_cert_path) as fd:
|
||||||
|
data = fd.read()
|
||||||
|
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, data)
|
||||||
|
return hashlib.sha1(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert)).hexdigest()
|
||||||
|
|
||||||
|
def generate_ssl():
|
||||||
|
key = OpenSSL.crypto.PKey()
|
||||||
|
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
|
||||||
|
with open(settings.ssl_key_path, 'wb') as fd:
|
||||||
|
os.chmod(settings.ssl_key_path, 0600)
|
||||||
|
fd.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
|
||||||
|
os.chmod(settings.ssl_key_path, 0400)
|
||||||
|
|
||||||
|
ca = OpenSSL.crypto.X509()
|
||||||
|
ca.set_version(2)
|
||||||
|
ca.set_serial_number(1)
|
||||||
|
ca.get_subject().CN = settings.USER_ID
|
||||||
|
ca.gmtime_adj_notBefore(0)
|
||||||
|
ca.gmtime_adj_notAfter(24 * 60 * 60)
|
||||||
|
ca.set_issuer(ca.get_subject())
|
||||||
|
ca.set_pubkey(key)
|
||||||
|
ca.add_extensions([
|
||||||
|
OpenSSL.crypto.X509Extension("basicConstraints", True, "CA:TRUE, pathlen:0"),
|
||||||
|
OpenSSL.crypto.X509Extension("nsCertType", True, "sslCA"),
|
||||||
|
OpenSSL.crypto.X509Extension("extendedKeyUsage", True,
|
||||||
|
"serverAuth,clientAuth,emailProtection,timeStamping,msCodeInd,msCodeCom,msCTLSign,msSGC,msEFS,nsSGC"),
|
||||||
|
OpenSSL.crypto.X509Extension("keyUsage", False, "keyCertSign, cRLSign"),
|
||||||
|
OpenSSL.crypto.X509Extension("subjectKeyIdentifier", False, "hash", subject=ca),
|
||||||
|
])
|
||||||
|
ca.sign(key, "sha1")
|
||||||
|
with open(settings.ssl_cert_path, 'wb') as fd:
|
||||||
|
fd.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, ca))
|
||||||
|
return get_fingerprint()
|
|
@ -1,25 +0,0 @@
|
||||||
import OpenSSL
|
|
||||||
|
|
||||||
key = OpenSSL.crypto.PKey()
|
|
||||||
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
|
|
||||||
|
|
||||||
ca = OpenSSL.crypto.X509()
|
|
||||||
ca.set_version(2)
|
|
||||||
ca.set_serial_number(1)
|
|
||||||
ca.get_subject().CN = "put_ed25519_key_here"
|
|
||||||
ca.gmtime_adj_notBefore(0)
|
|
||||||
ca.gmtime_adj_notAfter(24 * 60 * 60)
|
|
||||||
ca.set_issuer(ca.get_subject())
|
|
||||||
ca.set_pubkey(key)
|
|
||||||
ca.add_extensions([
|
|
||||||
OpenSSL.crypto.X509Extension("basicConstraints", True,
|
|
||||||
"CA:TRUE, pathlen:0"),
|
|
||||||
OpenSSL.crypto.X509Extension("keyUsage", True,
|
|
||||||
"keyCertSign, cRLSign"),
|
|
||||||
OpenSSL.crypto.X509Extension("subjectKeyIdentifier", False, "hash",
|
|
||||||
subject=ca),
|
|
||||||
OpenSSL.crypto.X509Extension("authorityKeyIdentifier", False, "keyid:always",issuer=ca)
|
|
||||||
])
|
|
||||||
ca.sign(key, "sha1")
|
|
||||||
open("MyCertificate.crt.bin", "wb").write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, ca))
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import user
|
||||||
import json
|
import json
|
||||||
from ed25519_utils import valid
|
from ed25519_utils import valid
|
||||||
import api
|
import api
|
||||||
|
import cert
|
||||||
|
|
||||||
class NodeHandler(tornado.web.RequestHandler):
|
class NodeHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
@ -53,9 +54,10 @@ class NodeHandler(tornado.web.RequestHandler):
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
|
u = user.models.User.get(key)
|
||||||
if action in (
|
if action in (
|
||||||
'requestPeering', 'acceptPeering', 'rejectPeering', 'removePeering'
|
'requestPeering', 'acceptPeering', 'rejectPeering', 'removePeering'
|
||||||
) or user.models.User.get(key):
|
) or (u and u.peered):
|
||||||
content = getattr(api, 'api_' + action)(self.app, key, *args)
|
content = getattr(api, 'api_' + action)(self.app, key, *args)
|
||||||
else:
|
else:
|
||||||
print 'PEER', key, 'IS UNKNOWN SEND 403'
|
print 'PEER', key, 'IS UNKNOWN SEND 403'
|
||||||
|
@ -103,14 +105,22 @@ class ShareHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
|
||||||
def start(app):
|
def start(app):
|
||||||
http_server = tornado.web.Application([
|
application = tornado.web.Application([
|
||||||
(r"/get/(.*)", ShareHandler, dict(app=app)),
|
(r"/get/(.*)", ShareHandler, dict(app=app)),
|
||||||
(r".*", NodeHandler, dict(app=app)),
|
(r".*", NodeHandler, dict(app=app)),
|
||||||
])
|
])
|
||||||
|
if not os.path.exists(settings.ssl_cert_path):
|
||||||
|
settings.server['cert'] = cert.generate_ssl()
|
||||||
|
|
||||||
|
http_server = tornado.httpserver.HTTPServer(application, ssl_options={
|
||||||
|
"certfile": settings.ssl_cert_path,
|
||||||
|
"keyfile": settings.ssl_key_path
|
||||||
|
})
|
||||||
http_server.listen(settings.server['node_port'], settings.server['node_address'])
|
http_server.listen(settings.server['node_port'], settings.server['node_address'])
|
||||||
host = utils.get_public_ipv6()
|
host = utils.get_public_ipv6()
|
||||||
state.online = directory.put(settings.sk, {
|
state.online = directory.put(settings.sk, {
|
||||||
'host': host,
|
'host': host,
|
||||||
'port': settings.server['node_port']
|
'port': settings.server['node_port'],
|
||||||
|
'cert': settings.server['cert']
|
||||||
})
|
})
|
||||||
return http_server
|
return http_server
|
||||||
|
|
83
oml/nodes.py
83
oml/nodes.py
|
@ -11,7 +11,7 @@ import os
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
import ed25519
|
import ed25519
|
||||||
import requests
|
import urllib2
|
||||||
|
|
||||||
import settings
|
import settings
|
||||||
import user.models
|
import user.models
|
||||||
|
@ -20,10 +20,12 @@ from changelog import Changelog
|
||||||
import directory
|
import directory
|
||||||
from websocket import trigger_event
|
from websocket import trigger_event
|
||||||
from localnodes import LocalNodes
|
from localnodes import LocalNodes
|
||||||
|
from ssl_request import get_opener
|
||||||
|
|
||||||
ENCODING='base64'
|
ENCODING='base64'
|
||||||
|
|
||||||
class Node(object):
|
class Node(object):
|
||||||
|
_cert = None
|
||||||
online = False
|
online = False
|
||||||
download_speed = 0
|
download_speed = 0
|
||||||
|
|
||||||
|
@ -39,15 +41,14 @@ class Node(object):
|
||||||
def url(self):
|
def url(self):
|
||||||
local = self.get_local()
|
local = self.get_local()
|
||||||
if local:
|
if local:
|
||||||
url = 'http://[%s]:%s' % (local['host'], local['port'])
|
url = 'https://[%s]:%s' % (local['host'], local['port'])
|
||||||
print 'using local peer discovery to access node', url
|
|
||||||
elif not self.host:
|
elif not self.host:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if ':' in self.host:
|
if ':' in self.host:
|
||||||
url = 'http://[%s]:%s' % (self.host, self.port)
|
url = 'https://[%s]:%s' % (self.host, self.port)
|
||||||
else:
|
else:
|
||||||
url = 'http://%s:%s' % (self.host, self.port)
|
url = 'https://%s:%s' % (self.host, self.port)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def resolve(self):
|
def resolve(self):
|
||||||
|
@ -56,13 +57,20 @@ class Node(object):
|
||||||
self.host = r['host']
|
self.host = r['host']
|
||||||
if 'port' in r:
|
if 'port' in r:
|
||||||
self.port = r['port']
|
self.port = r['port']
|
||||||
|
if r['cert'] != self._cert:
|
||||||
|
self._cert = r['cert']
|
||||||
|
self._opener = get_opener(self._cert)
|
||||||
else:
|
else:
|
||||||
self.host = None
|
self.host = None
|
||||||
self.port = 9851
|
self.port = 9851
|
||||||
|
|
||||||
def get_local(self):
|
def get_local(self):
|
||||||
if self._nodes and self._nodes._local:
|
if self._nodes and self._nodes._local:
|
||||||
return self._nodes._local.get(self.user_id)
|
local = self._nodes._local.get(self.user_id)
|
||||||
|
if local and local['cert'] != self._cert:
|
||||||
|
self._cert = local['cert']
|
||||||
|
self._opener = get_opener(self._cert)
|
||||||
|
return local
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def request(self, action, *args):
|
def request(self, action, *args):
|
||||||
|
@ -84,13 +92,30 @@ class Node(object):
|
||||||
'X-Ed25519-Key': settings.USER_ID,
|
'X-Ed25519-Key': settings.USER_ID,
|
||||||
'X-Ed25519-Signature': sig,
|
'X-Ed25519-Signature': sig,
|
||||||
}
|
}
|
||||||
r = requests.post(url, data=content, headers=headers)
|
self._opener.addheaders = zip(headers.keys(), headers.values())
|
||||||
if r.status_code == 403:
|
try:
|
||||||
print 'REMOTE ENDED PEERING'
|
r = self._opener.open(url, data=content)
|
||||||
if self.user.peered:
|
except urllib2.HTTPError as e:
|
||||||
self.user.update_peering(False)
|
if e.code == 403:
|
||||||
self.online = False
|
print 'REMOTE ENDED PEERING'
|
||||||
data = r.content
|
if self.user.peered:
|
||||||
|
self.user.update_peering(False)
|
||||||
|
self.online = False
|
||||||
|
return
|
||||||
|
print 'urllib2.HTTPError', e, e.code
|
||||||
|
self.online = False
|
||||||
|
return None
|
||||||
|
except urllib2.URLError as e:
|
||||||
|
print 'urllib2.URLError', e
|
||||||
|
self.online = False
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
print 'unknown url error'
|
||||||
|
import traceback
|
||||||
|
print traceback.print_exc()
|
||||||
|
self.online = False
|
||||||
|
return None
|
||||||
|
data = r.read()
|
||||||
sig = r.headers.get('X-Ed25519-Signature')
|
sig = r.headers.get('X-Ed25519-Signature')
|
||||||
if sig and self._valid(data, sig):
|
if sig and self._valid(data, sig):
|
||||||
response = json.loads(data)
|
response = json.loads(data)
|
||||||
|
@ -157,7 +182,7 @@ class Node(object):
|
||||||
'status': 'offline'
|
'status': 'offline'
|
||||||
})
|
})
|
||||||
r = False
|
r = False
|
||||||
print r
|
print 'pushedChanges', r, self.user_id
|
||||||
|
|
||||||
def requestPeering(self, message):
|
def requestPeering(self, message):
|
||||||
p = self.user
|
p = self.user
|
||||||
|
@ -205,31 +230,39 @@ class Node(object):
|
||||||
}
|
}
|
||||||
t1 = datetime.now()
|
t1 = datetime.now()
|
||||||
print 'GET', url
|
print 'GET', url
|
||||||
|
'''
|
||||||
r = requests.get(url, headers=headers)
|
r = requests.get(url, headers=headers)
|
||||||
if r.status_code == 200:
|
if r.status_code == 200:
|
||||||
|
content = r.content
|
||||||
|
'''
|
||||||
|
self._opener.addheaders = zip(headers.keys(), headers.values())
|
||||||
|
r = self._opener.open(url)
|
||||||
|
if r.getcode() == 200:
|
||||||
|
content = r.read()
|
||||||
t2 = datetime.now()
|
t2 = datetime.now()
|
||||||
duration = (t2-t1).total_seconds()
|
duration = (t2-t1).total_seconds()
|
||||||
if duration:
|
if duration:
|
||||||
self.download_speed = len(r.content) / duration
|
self.download_speed = len(content) / duration
|
||||||
print 'SPEED', ox.format_bits(self.download_speed)
|
print 'SPEED', ox.format_bits(self.download_speed)
|
||||||
return item.save_file(r.content)
|
return item.save_file(content)
|
||||||
else:
|
else:
|
||||||
print 'FAILED', url
|
print 'FAILED', url
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def download_upgrade(self):
|
def download_upgrade(self, release):
|
||||||
for module in settings.release['modules']:
|
for module in release['modules']:
|
||||||
path = os.path.join(settings.update_path, settings.release['modules'][module]['name'])
|
path = os.path.join(settings.update_path, release['modules'][module]['name'])
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
url = '%s/oml/%s' % (self.url, settings.release['modules'][module]['name'])
|
url = '%s/oml/%s' % (self.url, release['modules'][module]['name'])
|
||||||
sha1 = settings.release['modules'][module]['sha1']
|
sha1 = release['modules'][module]['sha1']
|
||||||
headers = {
|
headers = {
|
||||||
'User-Agent': settings.USER_AGENT,
|
'User-Agent': settings.USER_AGENT,
|
||||||
}
|
}
|
||||||
r = requests.get(url, headers=headers)
|
self._opener.addheaders = zip(headers.keys(), headers.values())
|
||||||
if r.status_code == 200:
|
r = self._opener.open(url)
|
||||||
|
if r.getcode() == 200:
|
||||||
with open(path, 'w') as fd:
|
with open(path, 'w') as fd:
|
||||||
fd.write(r.content)
|
fd.write(r.read())
|
||||||
if (ox.sha1sum(path) != sha1):
|
if (ox.sha1sum(path) != sha1):
|
||||||
print 'invalid update!'
|
print 'invalid update!'
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
@ -261,6 +294,8 @@ class Nodes(Thread):
|
||||||
def _call(self, target, action, *args):
|
def _call(self, target, action, *args):
|
||||||
if target == 'all':
|
if target == 'all':
|
||||||
nodes = self._nodes.values()
|
nodes = self._nodes.values()
|
||||||
|
elif target == 'peered':
|
||||||
|
nodes = [n for n in self._nodes.values() if n.user.peered]
|
||||||
elif target == 'online':
|
elif target == 'online':
|
||||||
nodes = [n for n in self._nodes.values() if n.online]
|
nodes = [n for n in self._nodes.values() if n.online]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -12,9 +12,13 @@ app = Blueprint('oxflask', __name__)
|
||||||
|
|
||||||
@app.route('/api/', methods=['POST', 'OPTIONS'])
|
@app.route('/api/', methods=['POST', 'OPTIONS'])
|
||||||
def api():
|
def api():
|
||||||
|
if request.host not in request.headers['origin']:
|
||||||
|
print 'reject cross site attempt to access api', request
|
||||||
|
return ''
|
||||||
|
|
||||||
if request.method == "OPTIONS":
|
if request.method == "OPTIONS":
|
||||||
response = render_to_json_response({'status': {'code': 200, 'text': 'use POST'}})
|
response = render_to_json_response({'status': {'code': 200, 'text': 'use POST'}})
|
||||||
response.headers['Access-Control-Allow-Origin'] = '*'
|
#response.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
return response
|
return response
|
||||||
if not 'action' in request.form:
|
if not 'action' in request.form:
|
||||||
methods = actions.keys()
|
methods = actions.keys()
|
||||||
|
@ -30,7 +34,7 @@ def api():
|
||||||
else:
|
else:
|
||||||
response = render_to_json_response(json_response(status=400,
|
response = render_to_json_response(json_response(status=400,
|
||||||
text='Unknown action %s' % action))
|
text='Unknown action %s' % action))
|
||||||
response.headers['Access-Control-Allow-Origin'] = '*'
|
#response.headers['Access-Control-Allow-Origin'] = '*'
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def trim(docstring):
|
def trim(docstring):
|
||||||
|
|
|
@ -37,7 +37,6 @@ def run():
|
||||||
]
|
]
|
||||||
|
|
||||||
http_server = HTTPServer(Application(handlers, **options))
|
http_server = HTTPServer(Application(handlers, **options))
|
||||||
|
|
||||||
http_server.listen(settings.server['port'], settings.server['address'])
|
http_server.listen(settings.server['port'], settings.server['address'])
|
||||||
if PID:
|
if PID:
|
||||||
with open(PID, 'w') as pid:
|
with open(PID, 'w') as pid:
|
||||||
|
@ -56,4 +55,12 @@ def run():
|
||||||
state.nodes.queue('add', p.id)
|
state.nodes.queue('add', p.id)
|
||||||
state.main.add_callback(add_users, app)
|
state.main.add_callback(add_users, app)
|
||||||
state.main.add_callback(start_node)
|
state.main.add_callback(start_node)
|
||||||
|
if ':' in settings.server['address']:
|
||||||
|
host = '[%s]' % settings.server['address']
|
||||||
|
elif not settings.server['address']:
|
||||||
|
host = '[::1]'
|
||||||
|
else:
|
||||||
|
host = settings.server['address']
|
||||||
|
url = 'http://%s:%s/' % (host, settings.server['port'])
|
||||||
|
print 'open browser at %s' % url
|
||||||
state.main.start()
|
state.main.start()
|
||||||
|
|
|
@ -21,6 +21,8 @@ if not os.path.exists(config_dir):
|
||||||
db_path = os.path.join(config_dir, 'openmedialibrary.db')
|
db_path = os.path.join(config_dir, 'openmedialibrary.db')
|
||||||
covers_db_path = os.path.join(config_dir, 'covers.db')
|
covers_db_path = os.path.join(config_dir, 'covers.db')
|
||||||
key_path = os.path.join(config_dir, 'node.key')
|
key_path = os.path.join(config_dir, 'node.key')
|
||||||
|
ssl_cert_path = os.path.join(config_dir, 'node.ssl.crt')
|
||||||
|
ssl_key_path = os.path.join(config_dir, 'node.ssl.key')
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
|
69
oml/ssl_request.py
Normal file
69
oml/ssl_request.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import httplib
|
||||||
|
import socket
|
||||||
|
import urllib2
|
||||||
|
import ssl
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
class InvalidCertificateException(httplib.HTTPException, urllib2.URLError):
|
||||||
|
def __init__(self, fingerprint, cert, reason):
|
||||||
|
httplib.HTTPException.__init__(self)
|
||||||
|
self.fingerprint = fingerprint
|
||||||
|
self.cert_fingerprint = hashlib.sha1(cert).hexdigest()
|
||||||
|
self.reason = reason
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ('%s (local) != %s (remote) (%s)\n' %
|
||||||
|
(self.fingerprint, self.cert_fingerprint, self.reason))
|
||||||
|
|
||||||
|
class CertValidatingHTTPSConnection(httplib.HTTPConnection):
|
||||||
|
default_port = httplib.HTTPS_PORT
|
||||||
|
|
||||||
|
def __init__(self, host, port=None, fingerprint=None, strict=None, **kwargs):
|
||||||
|
httplib.HTTPConnection.__init__(self, host, port, strict, **kwargs)
|
||||||
|
self.fingerprint = fingerprint
|
||||||
|
if self.fingerprint:
|
||||||
|
self.cert_reqs = ssl.CERT_REQUIRED
|
||||||
|
else:
|
||||||
|
self.cert_reqs = ssl.CERT_NONE
|
||||||
|
self.cert_reqs = ssl.CERT_NONE
|
||||||
|
|
||||||
|
def _ValidateCertificateFingerprint(self, cert):
|
||||||
|
fingerprint = hashlib.sha1(cert).hexdigest()
|
||||||
|
return fingerprint == self.fingerprint
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
sock = socket.create_connection((self.host, self.port))
|
||||||
|
self.sock = ssl.wrap_socket(sock, cert_reqs=self.cert_reqs)
|
||||||
|
#if self.cert_reqs & ssl.CERT_REQUIRED:
|
||||||
|
if self.fingerprint:
|
||||||
|
cert = self.sock.getpeercert(binary_form=True)
|
||||||
|
if not self._ValidateCertificateFingerprint(cert):
|
||||||
|
raise InvalidCertificateException(self.fingerprint, cert,
|
||||||
|
'fingerprint mismatch')
|
||||||
|
print 'CIPHER', self.sock.cipher(), 'VERSION', self.sock.ssl_version
|
||||||
|
|
||||||
|
class VerifiedHTTPSHandler(urllib2.HTTPSHandler):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
urllib2.AbstractHTTPHandler.__init__(self)
|
||||||
|
self._connection_args = kwargs
|
||||||
|
|
||||||
|
def https_open(self, req):
|
||||||
|
def http_class_wrapper(host, **kwargs):
|
||||||
|
full_kwargs = dict(self._connection_args)
|
||||||
|
full_kwargs.update(kwargs)
|
||||||
|
return CertValidatingHTTPSConnection(host, **full_kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.do_open(http_class_wrapper, req)
|
||||||
|
except urllib2.URLError, e:
|
||||||
|
if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1:
|
||||||
|
raise InvalidCertificateException(self.fingerprint, '',
|
||||||
|
e.reason.args[1])
|
||||||
|
raise
|
||||||
|
|
||||||
|
https_request = urllib2.HTTPSHandler.do_request_
|
||||||
|
|
||||||
|
def get_opener(fingerprint):
|
||||||
|
handler = VerifiedHTTPSHandler(fingerprint=fingerprint)
|
||||||
|
opener = urllib2.build_opener(handler)
|
||||||
|
return opener
|
|
@ -121,10 +121,8 @@ def editList(request):
|
||||||
name = l.name
|
name = l.name
|
||||||
if 'name' in data:
|
if 'name' in data:
|
||||||
l.name = data['name']
|
l.name = data['name']
|
||||||
l.type = 'static'
|
|
||||||
if 'query' in data:
|
if 'query' in data:
|
||||||
l._query = data['query']
|
l._query = data['query']
|
||||||
l.type = 'smart'
|
|
||||||
if l.type == 'static' and name != l.name:
|
if l.type == 'static' and name != l.name:
|
||||||
Changelog.record(state.user(), 'editlist', name, {'name': l.name})
|
Changelog.record(state.user(), 'editlist', name, {'name': l.name})
|
||||||
l.save()
|
l.save()
|
||||||
|
|
|
@ -8,6 +8,8 @@ import stdnum.isbn
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
from meta.utils import normalize_isbn
|
||||||
|
|
||||||
def valid_olid(id):
|
def valid_olid(id):
|
||||||
return id.startswith('OL') and id.endswith('M')
|
return id.startswith('OL') and id.endswith('M')
|
||||||
|
|
||||||
|
@ -74,9 +76,6 @@ def sort_title(title):
|
||||||
title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title)
|
title = re.sub(u'[\'!¿¡,\.;\-"\:\*\[\]]', '', title)
|
||||||
return title.strip()
|
return title.strip()
|
||||||
|
|
||||||
def normalize_isbn(value):
|
|
||||||
return ''.join([s for s in value if s.isdigit() or s == 'X'])
|
|
||||||
|
|
||||||
def find_isbns(text):
|
def find_isbns(text):
|
||||||
matches = re.compile('\d[\d\-X\ ]+').findall(text)
|
matches = re.compile('\d[\d\-X\ ]+').findall(text)
|
||||||
matches = [normalize_isbn(value) for value in matches]
|
matches = [normalize_isbn(value) for value in matches]
|
||||||
|
|
|
@ -55,9 +55,13 @@ class Background:
|
||||||
|
|
||||||
|
|
||||||
class Handler(WebSocketHandler):
|
class Handler(WebSocketHandler):
|
||||||
|
background = None
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
print "New connection opened."
|
print "New connection opened."
|
||||||
|
if self.request.host not in self.request.headers['origin']:
|
||||||
|
print 'reject cross site attempt to open websocket', self.request
|
||||||
|
self.close()
|
||||||
self.background = Background(self)
|
self.background = Background(self)
|
||||||
state.websockets.append(self.background)
|
state.websockets.append(self.background)
|
||||||
self.t = Thread(target=self.background.worker)
|
self.t = Thread(target=self.background.worker)
|
||||||
|
@ -70,8 +74,9 @@ class Handler(WebSocketHandler):
|
||||||
|
|
||||||
def on_close(self):
|
def on_close(self):
|
||||||
print "Connection closed."
|
print "Connection closed."
|
||||||
state.websockets.remove(self.background)
|
if self.background:
|
||||||
self.background.connected = False
|
state.websockets.remove(self.background)
|
||||||
|
self.background.connected = False
|
||||||
|
|
||||||
def trigger_event(event, data):
|
def trigger_event(event, data):
|
||||||
if len(state.websockets):
|
if len(state.websockets):
|
||||||
|
|
|
@ -9,6 +9,10 @@ oml.UI = (function() {
|
||||||
return value.replace(/\./g, '\\.');
|
return value.replace(/\./g, '\\.');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.getPrevious = function(key) {
|
||||||
|
return !key ? previousUI : previousUI[key];
|
||||||
|
};
|
||||||
|
|
||||||
that.reset = function() {
|
that.reset = function() {
|
||||||
var ui = oml.user.ui;
|
var ui = oml.user.ui;
|
||||||
oml.api.resetUI({}, function() {
|
oml.api.resetUI({}, function() {
|
||||||
|
|
|
@ -4,48 +4,48 @@ oml.ui.appDialog = function() {
|
||||||
|
|
||||||
var ui = oml.user.ui,
|
var ui = oml.user.ui,
|
||||||
|
|
||||||
tabs = Ox.getObjectById(oml.config.pages, 'app').parts.map(function(tab) {
|
tabs = Ox.getObjectById(oml.config.pages, 'app').parts.map(function(tab) {
|
||||||
return {
|
return {
|
||||||
id: tab.id,
|
id: tab.id,
|
||||||
title: tab.title.replace(/ Open Media Library$/, ''),
|
title: tab.title.replace(/ Open Media Library$/, ''),
|
||||||
selected: tab.id == ui.part.app
|
selected: tab.id == ui.part.app
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$panel = Ox.TabPanel({
|
$panel = Ox.TabPanel({
|
||||||
content: function(id) {
|
content: function(id) {
|
||||||
var $logo = Ox.Element(),
|
var $logo = Ox.Element(),
|
||||||
$text = Ox.Element()
|
$text = Ox.Element()
|
||||||
.addClass('OxTextPage'),
|
.addClass('OxTextPage'),
|
||||||
title = Ox.getObjectById(
|
title = Ox.getObjectById(
|
||||||
Ox.getObjectById(oml.config.pages, 'app').parts,
|
Ox.getObjectById(oml.config.pages, 'app').parts,
|
||||||
id
|
id
|
||||||
).title;
|
).title;
|
||||||
$('<img>')
|
$('<img>')
|
||||||
.attr({
|
.attr({
|
||||||
src: '/static/png/oml.png'
|
src: '/static/png/oml.png'
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: '16px',
|
left: '16px',
|
||||||
top: '16px',
|
top: '16px',
|
||||||
width: '192px',
|
width: '192px',
|
||||||
height: '192px'
|
height: '192px'
|
||||||
})
|
})
|
||||||
.appendTo($logo);
|
.appendTo($logo);
|
||||||
$('<div>')
|
$('<div>')
|
||||||
.css({
|
.css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: '16px',
|
left: '16px',
|
||||||
right: '24px',
|
right: '24px',
|
||||||
top: '24px',
|
top: '24px',
|
||||||
overflowY: 'auto'
|
overflowY: 'auto'
|
||||||
})
|
})
|
||||||
.html(
|
.html(
|
||||||
'<h1><b>' + title + '</b></h1>'
|
'<h1><b>' + title + '</b></h1>'
|
||||||
+ '<p>The lazy brown fox jumped over the lazy black fox, but otherwise not really much happened here since you last checked.'
|
+ '<p>The lazy brown fox jumped over the lazy black fox, but otherwise not really much happened here since you last checked.'
|
||||||
)
|
)
|
||||||
.appendTo($text);
|
.appendTo($text);
|
||||||
return Ox.SplitPanel({
|
return Ox.SplitPanel({
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
|
@ -58,14 +58,14 @@ oml.ui.appDialog = function() {
|
||||||
],
|
],
|
||||||
orientation: 'horizontal'
|
orientation: 'horizontal'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
tabs: tabs
|
tabs: tabs
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
oml.UI.set({'part.app': data.selected});
|
oml.UI.set({'part.app': data.selected});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
that = Ox.Dialog({
|
that = Ox.Dialog({
|
||||||
buttons: [
|
buttons: [
|
||||||
|
@ -87,7 +87,7 @@ oml.ui.appDialog = function() {
|
||||||
width: 768
|
width: 768
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
close: function() {
|
close: function() {
|
||||||
if (ui.page == 'app') {
|
if (ui.page == 'app') {
|
||||||
oml.UI.set({page: ''});
|
oml.UI.set({page: ''});
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ oml.ui.appDialog = function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
that.update = function(section) {
|
that.update = function(section) {
|
||||||
$panel.selectTab(section);
|
$panel.selectTab(section);
|
||||||
};
|
};
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
|
@ -19,17 +19,18 @@ oml.ui.deleteListDialog = function() {
|
||||||
oml.api.removeList({
|
oml.api.removeList({
|
||||||
id: ui._list
|
id: ui._list
|
||||||
}, function() {
|
}, function() {
|
||||||
oml.UI.set({
|
oml.updateLists(function() {
|
||||||
find: {
|
oml.UI.set({
|
||||||
conditions: [{
|
find: {
|
||||||
key: 'list',
|
conditions: [{
|
||||||
operator: '==',
|
key: 'list',
|
||||||
value: ':'
|
operator: '==',
|
||||||
}],
|
value: ':'
|
||||||
operator: '&'
|
}],
|
||||||
}
|
operator: '&'
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
oml.updateLists();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -133,12 +133,8 @@ oml.ui.filter = function(id) {
|
||||||
},
|
},
|
||||||
sort: function(data) {
|
sort: function(data) {
|
||||||
var filters = Ox.clone(ui.filters, true);
|
var filters = Ox.clone(ui.filters, true);
|
||||||
filters[filterIndex].sort = [Ox.clone(data)];
|
filters[filterIndex].sort = [{key: data.key, operator: data.operator}];
|
||||||
oml.UI.set({filters: filters});
|
oml.UI.set({filters: filters});
|
||||||
},
|
|
||||||
oml_find: function() {
|
|
||||||
Ox.print('%%%%', 'RELOADING FILTER')
|
|
||||||
that.reloadList(true);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,33 @@ oml.ui.filtersOuterPanel = function() {
|
||||||
});
|
});
|
||||||
oml.updateFilterMenus();
|
oml.updateFilterMenus();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
oml_find: function() {
|
||||||
|
var previousUI = oml.UI.getPrevious();
|
||||||
|
Ox.print('WTF', ui, oml.user.ui, Object.keys(ui), Object.keys(previousUI));
|
||||||
|
ui._filterState.forEach(function(data, index) {
|
||||||
|
if (!Ox.isEqual(data.selected, previousUI._filterState[index].selected)) {
|
||||||
|
oml.$ui.filters[index].options(
|
||||||
|
ui.showFilters ? {
|
||||||
|
selected: data.selected
|
||||||
|
} : {
|
||||||
|
_selected: data.selected,
|
||||||
|
selected: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!Ox.isEqual(data.find, previousUI._filterState[index].find)) {
|
||||||
|
Ox.print('::::', index, 'UNEQUAL', data.find, previousUI._filterState[index].find)
|
||||||
|
if (!ui.showFilters) {
|
||||||
|
oml.$ui.filters[index].options({
|
||||||
|
_selected: data.selected
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// we can call reloadList here, since the items function
|
||||||
|
// handles the hidden filters case without making requests
|
||||||
|
oml.$ui.filters[index].reloadList();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
56
static/js/findDialog.js
Normal file
56
static/js/findDialog.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
oml.ui.findDialog = function() {
|
||||||
|
|
||||||
|
var ui = oml.user.ui,
|
||||||
|
|
||||||
|
that = Ox.Dialog({
|
||||||
|
buttons: [
|
||||||
|
Ox.Button({
|
||||||
|
id: 'done',
|
||||||
|
title: Ox._('Done')
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
var list = oml.$ui.findForm.getList();
|
||||||
|
if (list.save) {
|
||||||
|
oml.addList({
|
||||||
|
name: list.name,
|
||||||
|
query: list.query
|
||||||
|
});
|
||||||
|
}
|
||||||
|
that.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
closeButton: true,
|
||||||
|
content: oml.$ui.findForm = oml.ui.findForm()
|
||||||
|
.css({margin: '16px'}),
|
||||||
|
fixedSize: true,
|
||||||
|
height: 264,
|
||||||
|
removeOnClose: true,
|
||||||
|
title: Ox._('Advanced Find'),
|
||||||
|
width: 648 + Ox.UI.SCROLLBAR_SIZE
|
||||||
|
}),
|
||||||
|
|
||||||
|
$updateCheckbox = Ox.Checkbox({
|
||||||
|
title: Ox._('Update Results in the Background'),
|
||||||
|
value: oml.user.ui.updateAdvancedFindResults
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'left',
|
||||||
|
margin: '4px'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
oml.UI.set({updateAdvancedFindResults: data.value});
|
||||||
|
//data.value && oml.$ui.findForm.updateResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$($updateCheckbox.find('.OxButton')[0]).css({margin: 0});
|
||||||
|
$(that.find('.OxBar')[1]).append($updateCheckbox);
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
|
@ -36,12 +36,17 @@ oml.ui.findElement = function() {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
|
var menu = 'findMenu_finditems_' + data.value,
|
||||||
|
previousMenu = 'findMenu_finditems_' + previousFindKey;
|
||||||
|
oml.$ui.mainMenu.checkItem(menu);
|
||||||
|
oml.$ui.mainMenu.setItemKeyboard(previousMenu, '');
|
||||||
|
oml.$ui.mainMenu.setItemKeyboard(menu, 'control f');
|
||||||
if (data.value == 'advanced') {
|
if (data.value == 'advanced') {
|
||||||
oml.$ui.mainMenu.checkItem('findMenu_find_' + previousFindKey);
|
// FIXME: control f when advanced
|
||||||
that.updateElement();
|
that.updateElement();
|
||||||
oml.$ui.filterDialog = oml.ui.filterDialog().open();
|
oml.$ui.findDialog = oml.ui.findDialog().open();
|
||||||
} else {
|
} else {
|
||||||
oml.$ui.mainMenu.checkItem('findMenu_find_' + data.value);
|
|
||||||
oml.$ui.findInput.options({
|
oml.$ui.findInput.options({
|
||||||
autocomplete: getAutocomplete(),
|
autocomplete: getAutocomplete(),
|
||||||
placeholder: ''
|
placeholder: ''
|
||||||
|
@ -77,7 +82,7 @@ oml.ui.findElement = function() {
|
||||||
hasPressedClear = false;
|
hasPressedClear = false;
|
||||||
}
|
}
|
||||||
oml.$ui.findInput.blurInput();
|
oml.$ui.findInput.blurInput();
|
||||||
oml.$ui.filterDialog = oml.ui.filterDialog().open();
|
oml.$ui.findDialog = oml.ui.findDialog().open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
submit: function(data) {
|
submit: function(data) {
|
||||||
|
|
67
static/js/findForm.js
Normal file
67
static/js/findForm.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
oml.ui.findForm = function(list) {
|
||||||
|
|
||||||
|
//Ox.print('FIND FORM LIST QUERY', list.query);
|
||||||
|
|
||||||
|
var ui = oml.user.ui,
|
||||||
|
|
||||||
|
that = Ox.Element(),
|
||||||
|
|
||||||
|
$filter = Ox.Filter({
|
||||||
|
findKeys: oml.config.itemKeys.map(function(key) {
|
||||||
|
return Ox.extend({}, key, {
|
||||||
|
title: Ox._(key.title),
|
||||||
|
type: key.id == 'mediastate' ? 'item' : key.type
|
||||||
|
});
|
||||||
|
}).concat([{
|
||||||
|
id: 'list',
|
||||||
|
title: Ox._('List'),
|
||||||
|
type: 'item',
|
||||||
|
values: ui._lists.filter(function(list) {
|
||||||
|
return list.type != 'smart'
|
||||||
|
}).map(function(list) {
|
||||||
|
return {id: list.id, title: list.title};
|
||||||
|
})
|
||||||
|
}]),
|
||||||
|
list: list ? null : {
|
||||||
|
sort: ui.listSort,
|
||||||
|
view: ui.listView
|
||||||
|
},
|
||||||
|
sortKeys: oml.config.sortKeys,
|
||||||
|
value: Ox.clone(list ? list.query : ui.find, true),
|
||||||
|
viewKeys: oml.config.listViews
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
(list ? oml.api.editList : Ox.noop)(list ? {
|
||||||
|
id: list.id,
|
||||||
|
query: data.value
|
||||||
|
} : {}, function(result) {
|
||||||
|
if (ui.updateAdvancedFindResults) {
|
||||||
|
updateResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo(that);
|
||||||
|
|
||||||
|
function updateResults() {
|
||||||
|
if (list) {
|
||||||
|
Ox.Request.clearCache(list.id);
|
||||||
|
oml.$ui.list.reloadList();
|
||||||
|
oml.$ui.filters.forEach(function($filter) {
|
||||||
|
$filter.reloadList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
oml.UI.set({find: Ox.clone($filter.options('value'), true)});
|
||||||
|
oml.$ui.findElement.updateElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.getList = $filter.getList;
|
||||||
|
that.value = $filter.value;
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
|
@ -33,9 +33,6 @@ oml.ui.folders = function() {
|
||||||
oml.api.find({query: getFind()}, function(result) {
|
oml.api.find({query: getFind()}, function(result) {
|
||||||
oml.$ui.librariesList.value('', 'items', result.data.items);
|
oml.$ui.librariesList.value('', 'items', result.data.items);
|
||||||
});
|
});
|
||||||
},
|
|
||||||
open: function() {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
select: function() {
|
select: function() {
|
||||||
oml.UI.set({find: getFind('')});
|
oml.UI.set({find: getFind('')});
|
||||||
|
@ -148,9 +145,6 @@ oml.ui.folders = function() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
open: function() {
|
|
||||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
|
||||||
},
|
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
oml.UI.set({find: getFind(data.ids[0])});
|
oml.UI.set({find: getFind(data.ids[0])});
|
||||||
},
|
},
|
||||||
|
@ -206,7 +200,7 @@ oml.ui.folders = function() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
open: function() {
|
open: function() {
|
||||||
oml.ui.listDialog().open();
|
!index && oml.ui.listDialog().open();
|
||||||
},
|
},
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
oml.UI.set({find: getFind(data.ids[0])});
|
oml.UI.set({find: getFind(data.ids[0])});
|
||||||
|
|
191
static/js/identifyDialog.js
Normal file
191
static/js/identifyDialog.js
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
oml.ui.identifyDialog = function(data) {
|
||||||
|
|
||||||
|
var ui = oml.user.ui,
|
||||||
|
|
||||||
|
ids = [
|
||||||
|
'isbn10', 'isbn13', 'asin', 'lccn', 'oclc', 'olid'
|
||||||
|
].map(function(id) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
title: Ox._(Ox.getObjectById(oml.config.itemKeys, id).title)
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
keys = [
|
||||||
|
'title', 'author', 'publisher', 'date'
|
||||||
|
].map(function(id) {
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
title: Ox._(Ox.getObjectById(oml.config.itemKeys, id).title)
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
$input = Ox.FormElementGroup({
|
||||||
|
elements: [
|
||||||
|
Ox.Select({
|
||||||
|
items: ids,
|
||||||
|
overlap: 'right',
|
||||||
|
value: 'isbn10',
|
||||||
|
width: 128
|
||||||
|
}),
|
||||||
|
Ox.Input({
|
||||||
|
value: data['isbn10'] || '',
|
||||||
|
width: 610
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.css({margin: '16px'}),
|
||||||
|
|
||||||
|
$preview = Ox.Element(),
|
||||||
|
|
||||||
|
$idPanel = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{element: Ox.Element().append($input), size: 48},
|
||||||
|
{element: $preview}
|
||||||
|
],
|
||||||
|
orientation: 'vertical'
|
||||||
|
}),
|
||||||
|
|
||||||
|
$form = Ox.Form({
|
||||||
|
items: keys.map(function(key) {
|
||||||
|
return Ox.Input({
|
||||||
|
id: key.id,
|
||||||
|
labelWidth: 128,
|
||||||
|
label: key.title,
|
||||||
|
value: key == 'author'
|
||||||
|
? (data[key.id] || []).join(', ')
|
||||||
|
: data[key.id],
|
||||||
|
width: 736
|
||||||
|
});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.css({padding: '16px'})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
Ox.print('FORM CHANGE', data);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
$list = Ox.TableList({
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
id: 'index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'title',
|
||||||
|
visible: true,
|
||||||
|
width: 288,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'author',
|
||||||
|
visible: true,
|
||||||
|
width: 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'publisher',
|
||||||
|
visible: true,
|
||||||
|
width: 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'date',
|
||||||
|
visible: true,
|
||||||
|
width: 96
|
||||||
|
}
|
||||||
|
],
|
||||||
|
items: [],
|
||||||
|
max: 1,
|
||||||
|
sort: [{key: 'index', operator: '+'}],
|
||||||
|
unique: 'index'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
select: function(data) {
|
||||||
|
$that.options('buttons')[1].options({
|
||||||
|
disabled: data.ids.length == 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
$titlePanel = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{element: Ox.Element().append($form), size: 120},
|
||||||
|
{element: $list}
|
||||||
|
],
|
||||||
|
orientation: 'vertical'
|
||||||
|
}),
|
||||||
|
|
||||||
|
$bar = Ox.Bar({size: 24}),
|
||||||
|
|
||||||
|
$buttons = Ox.ButtonGroup({
|
||||||
|
buttons: [
|
||||||
|
{id: 'id', title: Ox._('Look Up by ID')},
|
||||||
|
{id: 'title', title: Ox._('Find by Title')}
|
||||||
|
],
|
||||||
|
selectable: true,
|
||||||
|
selected: 'id'
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
width: '768px',
|
||||||
|
padding: '4px 0',
|
||||||
|
textAlign: 'center'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
$innerPanel.options({selected: data.value});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($bar),
|
||||||
|
|
||||||
|
$innerPanel = Ox.SlidePanel({
|
||||||
|
elements: [
|
||||||
|
{id: 'id', element: $idPanel},
|
||||||
|
{id: 'title', element: $titlePanel}
|
||||||
|
],
|
||||||
|
orientation: 'horizontal',
|
||||||
|
selected: 'id',
|
||||||
|
size: 768
|
||||||
|
}),
|
||||||
|
|
||||||
|
$outerPanel = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{element: $bar, size: 24},
|
||||||
|
{element: $innerPanel}
|
||||||
|
],
|
||||||
|
orientation: 'vertical'
|
||||||
|
}),
|
||||||
|
|
||||||
|
that = Ox.Dialog({
|
||||||
|
buttons: [
|
||||||
|
Ox.Button({
|
||||||
|
id: 'dontupdate',
|
||||||
|
title: Ox._('No, Don\'t Update')
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
that.close();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Ox.Button({
|
||||||
|
disabled: true,
|
||||||
|
id: 'update',
|
||||||
|
title: Ox._('Yes, Update')
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
Ox.print('NOT IMPLEMENTED');
|
||||||
|
that.close();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
closeButton: true,
|
||||||
|
content: $outerPanel,
|
||||||
|
fixedSize: true,
|
||||||
|
height: 384,
|
||||||
|
title: Ox._('Identify Book'),
|
||||||
|
width: 768
|
||||||
|
});
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
|
@ -20,45 +20,6 @@ oml.ui.infoView = function() {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$identifyPanel = Ox.SplitPanel({
|
|
||||||
elements: [
|
|
||||||
{element: Ox.Element(), size: 120},
|
|
||||||
{element: Ox.Element()}
|
|
||||||
],
|
|
||||||
orientation: 'vertical'
|
|
||||||
}),
|
|
||||||
|
|
||||||
$identifyDialog = Ox.Dialog({
|
|
||||||
buttons: [
|
|
||||||
Ox.Button({
|
|
||||||
id: 'dontupdate',
|
|
||||||
title: Ox._('No, Don\'t Update')
|
|
||||||
})
|
|
||||||
.bindEvent({
|
|
||||||
click: function() {
|
|
||||||
$identifyDialog.close();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Ox.Button({
|
|
||||||
disabled: true,
|
|
||||||
id: 'update',
|
|
||||||
title: Ox._('Yes, Update')
|
|
||||||
})
|
|
||||||
.bindEvent({
|
|
||||||
click: function() {
|
|
||||||
$identifyDialog.close();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
|
||||||
closeButton: true,
|
|
||||||
content: $identifyPanel,
|
|
||||||
fixedSize: true,
|
|
||||||
height: 384,
|
|
||||||
//removeOnClose: true,
|
|
||||||
title: Ox._('Identify Book'),
|
|
||||||
width: 768
|
|
||||||
}),
|
|
||||||
|
|
||||||
$cover = Ox.Element()
|
$cover = Ox.Element()
|
||||||
.css({
|
.css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -88,71 +49,27 @@ oml.ui.infoView = function() {
|
||||||
})
|
})
|
||||||
.appendTo(that);
|
.appendTo(that);
|
||||||
|
|
||||||
|
function formatLight(str) {
|
||||||
|
return '<span class="OxLight">' + str + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValue(value, key) {
|
||||||
|
return (Ox.isArray(value) ? value : [value]).map(function(value) {
|
||||||
|
return key ?
|
||||||
|
'<a href="/' + key + '==' + value + '">' + value + '</a>'
|
||||||
|
: value;
|
||||||
|
}).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
function identify(data) {
|
function identify(data) {
|
||||||
var $form = Ox.Form({
|
oml.ui.identifyDialog(data).open();
|
||||||
items: [
|
return;
|
||||||
'title', 'author', 'publisher', 'date'
|
$identifyPanel.select('id');
|
||||||
].map(function(key) {
|
|
||||||
return Ox.Input({
|
|
||||||
id: key,
|
|
||||||
labelWidth: 128,
|
|
||||||
label: Ox.getObjectById(oml.config.itemKeys, key).title,
|
|
||||||
value: key == 'author' ? (data[key] || []).join(', ') : data[key],
|
|
||||||
width: 736
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.css({padding: '16px'})
|
|
||||||
.bindEvent({
|
|
||||||
change: function(data) {
|
|
||||||
Ox.print('FORM CHANGE', data);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
$list = Ox.TableList({
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
id: 'index'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'title',
|
|
||||||
visible: true,
|
|
||||||
width: 288,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'author',
|
|
||||||
visible: true,
|
|
||||||
width: 224
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'publisher',
|
|
||||||
visible: true,
|
|
||||||
width: 160
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'date',
|
|
||||||
visible: true,
|
|
||||||
width: 96
|
|
||||||
}
|
|
||||||
],
|
|
||||||
items: [],
|
|
||||||
max: 1,
|
|
||||||
sort: [{key: 'index', operator: '+'}],
|
|
||||||
unique: 'index'
|
|
||||||
})
|
|
||||||
.bindEvent({
|
|
||||||
select: function(data) {
|
|
||||||
$identifyDialog.options('buttons')[1].options({
|
|
||||||
disabled: data.ids.length == 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$identifyPanel.replaceElement(0, $form);
|
|
||||||
$identifyPanel.replaceElement(1, $list);
|
|
||||||
$identifyDialog.open();
|
$identifyDialog.open();
|
||||||
identify(data);
|
identify(data);
|
||||||
function identify(data) {
|
function identify(data) {
|
||||||
oml.api.identify(data, function(result) {
|
oml.api.identify(data, function(result) {
|
||||||
$list.options({
|
$identifyList.options({
|
||||||
items: result.data.items.map(function(item, index) {
|
items: result.data.items.map(function(item, index) {
|
||||||
return Ox.extend(item, {index: index});
|
return Ox.extend(item, {index: index});
|
||||||
})
|
})
|
||||||
|
@ -162,7 +79,38 @@ oml.ui.infoView = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMediaButton(data) {
|
function renderMediaButton(data) {
|
||||||
return data.mediastate == 'unavailable'
|
function getListItems() {
|
||||||
|
var items = [];
|
||||||
|
if (ui._lists) {
|
||||||
|
items = ui._lists.filter(function(list) {
|
||||||
|
return list.user == oml.user.preferences.username
|
||||||
|
&& list.type != 'smart';
|
||||||
|
}).map(function(list) {
|
||||||
|
return {
|
||||||
|
id: list.id,
|
||||||
|
title: Ox._('Download to {0}', [list.name])
|
||||||
|
};
|
||||||
|
});
|
||||||
|
items.splice(1, 0, [{}]);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
function setListItems() {
|
||||||
|
if ($element && ui._lists) {
|
||||||
|
$element.options({
|
||||||
|
disabled: false
|
||||||
|
}).options('elements')[1].options({
|
||||||
|
items: getListItems()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeout(setListItems, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.mediastate == 'unavailable' && !ui._lists) {
|
||||||
|
setListItems();
|
||||||
|
}
|
||||||
|
var $element = data.mediastate == 'unavailable'
|
||||||
? Ox.FormElementGroup({
|
? Ox.FormElementGroup({
|
||||||
elements: [
|
elements: [
|
||||||
Ox.Button({
|
Ox.Button({
|
||||||
|
@ -179,17 +127,24 @@ oml.ui.infoView = function() {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
Ox.MenuButton({
|
Ox.MenuButton({
|
||||||
items: [
|
disabled: !ui._lists,
|
||||||
{id: '', title: Ox._('Library')}
|
items: getListItems(),
|
||||||
],
|
|
||||||
overlap: 'left',
|
overlap: 'left',
|
||||||
title: 'list',
|
title: 'list',
|
||||||
tooltip: Ox._('Download Book to a List'),
|
tooltip: Ox._('Download Book to a List'),
|
||||||
type: 'image'
|
type: 'image'
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function(data) {
|
||||||
// ...
|
data.mediastate = 'transferring';
|
||||||
|
that.update(data, $data);
|
||||||
|
oml.api.download(Ox.extend({
|
||||||
|
id: ui.item,
|
||||||
|
}, data.id == ':' ? {} : {
|
||||||
|
list: data.id.slice(1)
|
||||||
|
}), function(result) {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
@ -234,12 +189,13 @@ oml.ui.infoView = function() {
|
||||||
oml.UI.set({itemView: 'book'});
|
oml.UI.set({itemView: 'book'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return $element;
|
||||||
}
|
}
|
||||||
|
|
||||||
that.update = function(idOrData, $elements) {
|
that.update = function(idOrData, $elements) {
|
||||||
|
|
||||||
var data = Ox.isObject(idOrData) ? idOrData : null,
|
var data = Ox.isObject(idOrData) ? idOrData : null,
|
||||||
id = data ? null : idOrData;
|
id = data ? null : idOrData,
|
||||||
|
|
||||||
$elements = $elements
|
$elements = $elements
|
||||||
? Ox.makeArray($elements)
|
? Ox.makeArray($elements)
|
||||||
|
@ -256,6 +212,7 @@ oml.ui.infoView = function() {
|
||||||
Ox.print('BOOK DATA', data)
|
Ox.print('BOOK DATA', data)
|
||||||
|
|
||||||
var $reflection, $mediaButton,
|
var $reflection, $mediaButton,
|
||||||
|
isEditable = !data.mainid && data.mediastate == 'available',
|
||||||
ratio = data.coverRatio || 0.75,
|
ratio = data.coverRatio || 0.75,
|
||||||
size = 256,
|
size = 256,
|
||||||
width = Math.round(ratio >= 1 ? size : size * ratio),
|
width = Math.round(ratio >= 1 ? size : size * ratio),
|
||||||
|
@ -312,22 +269,47 @@ oml.ui.infoView = function() {
|
||||||
} else if ($element == $info) {
|
} else if ($element == $info) {
|
||||||
|
|
||||||
$('<div>')
|
$('<div>')
|
||||||
.css({
|
.css({marginTop: '-2px'})
|
||||||
marginTop: '-4px',
|
.append(
|
||||||
fontSize: '13px',
|
Ox.EditableContent({
|
||||||
fontWeight: 'bold'
|
clickLink: oml.clickLink,
|
||||||
})
|
editable: isEditable,
|
||||||
.text(data.title || '')
|
tooltip: isEditable ? oml.getEditTooltip() : '',
|
||||||
|
value: data.title
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
})
|
||||||
|
)
|
||||||
.appendTo($info);
|
.appendTo($info);
|
||||||
|
|
||||||
$('<div>')
|
if (data.author || isEditable) {
|
||||||
.css({
|
$('<div>')
|
||||||
marginTop: '4px',
|
.css({
|
||||||
fontSize: '13px',
|
marginTop: '4px',
|
||||||
fontWeight: 'bold'
|
fontSize: '13px',
|
||||||
})
|
fontWeight: 'bold'
|
||||||
.text((data.author || []).join(', '))
|
})
|
||||||
.appendTo($info);
|
.append(
|
||||||
|
Ox.EditableContent({
|
||||||
|
clickLink: oml.clickLink,
|
||||||
|
editable: isEditable,
|
||||||
|
format: function(value) {
|
||||||
|
return formatValue(value.split(', '), 'author');
|
||||||
|
},
|
||||||
|
placeholder: formatLight(Ox._('Unknown Author')),
|
||||||
|
tooltip: isEditable ? oml.getEditTooltip() : '',
|
||||||
|
value: data.author ? data.author.join(', ') : ''
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 'bold'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.appendTo($info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$('<div>')
|
$('<div>')
|
||||||
.css({
|
.css({
|
||||||
|
@ -342,17 +324,16 @@ oml.ui.infoView = function() {
|
||||||
)
|
)
|
||||||
.appendTo($info);
|
.appendTo($info);
|
||||||
|
|
||||||
$('<div>')
|
if (data.description) {
|
||||||
.css({
|
$('<div>')
|
||||||
marginTop: '8px',
|
.css({
|
||||||
textAlign: 'justify'
|
marginTop: '8px',
|
||||||
})
|
textAlign: 'justify'
|
||||||
.html(
|
})
|
||||||
data.description
|
.html(Ox.encodeHTMLEntities(data.description))
|
||||||
? Ox.encodeHTMLEntities(data.description)
|
.appendTo($info);
|
||||||
: '<span class="OxLight">No description</span>'
|
}
|
||||||
)
|
|
||||||
.appendTo($info);
|
|
||||||
|
|
||||||
} else if ($element == $data) {
|
} else if ($element == $data) {
|
||||||
|
|
||||||
|
@ -362,7 +343,7 @@ oml.ui.infoView = function() {
|
||||||
$('<div>')
|
$('<div>')
|
||||||
.addClass('OxSelectable')
|
.addClass('OxSelectable')
|
||||||
.css({
|
.css({
|
||||||
marginTop: '8px',
|
marginTop: '10px',
|
||||||
})
|
})
|
||||||
.text(
|
.text(
|
||||||
[
|
[
|
||||||
|
@ -383,39 +364,31 @@ oml.ui.infoView = function() {
|
||||||
})
|
})
|
||||||
.text(title)
|
.text(title)
|
||||||
.appendTo($data);
|
.appendTo($data);
|
||||||
Ox.EditableContent({
|
$('<div>')
|
||||||
editable: false,
|
.text(Ox.formatDate(data[id], '%b %e, %Y'))
|
||||||
format: function(value) {
|
|
||||||
return value ? Ox.formatDate(value, '%b %e, %Y') : '';
|
|
||||||
},
|
|
||||||
placeholder: Ox._('unknown'),
|
|
||||||
value: data[id] || ''
|
|
||||||
})
|
|
||||||
.appendTo($data);
|
.appendTo($data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data.mediastate == 'available') {
|
Ox.Button({
|
||||||
|
disabled: data.mediastate != 'available',
|
||||||
Ox.Button({
|
title: Ox._('Identify Book...'),
|
||||||
title: Ox._('Identify Book...'),
|
width: 128
|
||||||
width: 128
|
})
|
||||||
})
|
.css({marginTop: '16px'})
|
||||||
.css({marginTop: '16px'})
|
.bindEvent({
|
||||||
.bindEvent({
|
click: function() {
|
||||||
click: function() {
|
identify(data);
|
||||||
identify(data);
|
}
|
||||||
}
|
})
|
||||||
})
|
.appendTo($data);
|
||||||
.appendTo($data);
|
|
||||||
|
|
||||||
[
|
|
||||||
'isbn10', 'isbn13', 'lccn', 'olid', 'oclc', 'mainid'
|
|
||||||
].forEach(function(id, index) {
|
|
||||||
|
|
||||||
var title = Ox.getObjectById(oml.config.itemKeys, id).title,
|
|
||||||
placeholder = id == 'mainid' ? 'none' : 'unknown';
|
|
||||||
|
|
||||||
|
[
|
||||||
|
'isbn10', 'isbn13', 'asin', 'lccn', 'oclc', 'olid'
|
||||||
|
].forEach(function(id, index) {
|
||||||
|
var title;
|
||||||
|
if (data[id]) {
|
||||||
|
title = Ox.getObjectById(oml.config.itemKeys, id).title;
|
||||||
$('<div>')
|
$('<div>')
|
||||||
.css({
|
.css({
|
||||||
marginTop: (index == 0 ? 10 : 6) + 'px',
|
marginTop: (index == 0 ? 10 : 6) + 'px',
|
||||||
|
@ -423,28 +396,13 @@ oml.ui.infoView = function() {
|
||||||
})
|
})
|
||||||
.text(title)
|
.text(title)
|
||||||
.appendTo($data);
|
.appendTo($data);
|
||||||
|
|
||||||
Ox.EditableContent({
|
Ox.EditableContent({
|
||||||
editable: true,
|
editable: false,
|
||||||
format: function(value) {
|
value: data[id]
|
||||||
return id == 'mainid'
|
|
||||||
? Ox.getObjectById(oml.config.itemKeys, value).title
|
|
||||||
: value;
|
|
||||||
},
|
|
||||||
placeholder: placeholder,
|
|
||||||
tooltip: Ox._('Doubleclick to edit'),
|
|
||||||
value: data[id] || ''
|
|
||||||
})
|
|
||||||
.bindEvent({
|
|
||||||
submit: function(data) {
|
|
||||||
editMetadata(id, data.value);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.appendTo($data);
|
.appendTo($data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$('<div>').css({height: '16px'}).appendTo($data);
|
$('<div>').css({height: '16px'}).appendTo($data);
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ oml.ui.listDialog = function() {
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
that.close();
|
that.close();
|
||||||
oml.deleteList();
|
oml.ui.deleteListDialog().open();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
] : []).concat([
|
] : []).concat([
|
||||||
|
@ -42,7 +42,7 @@ oml.ui.listDialog = function() {
|
||||||
]),
|
]),
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
content: Ox.LoadingScreen().start(),
|
content: Ox.LoadingScreen().start(),
|
||||||
height: 256,
|
height: 264,
|
||||||
title: Ox._('List – {0}', [
|
title: Ox._('List – {0}', [
|
||||||
ui._list == '' ? Ox._('All Libraries')
|
ui._list == '' ? Ox._('All Libraries')
|
||||||
: ui._list
|
: ui._list
|
||||||
|
@ -91,8 +91,15 @@ oml.ui.listDialog = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.appendTo($content),
|
||||||
|
$findForm;
|
||||||
|
Ox.print('DEBUG:', list, listData)
|
||||||
|
if (listData.type == 'smart') {
|
||||||
|
$findForm = oml.ui.findForm(listData)
|
||||||
|
.css({marginTop: '8px'})
|
||||||
.appendTo($content);
|
.appendTo($content);
|
||||||
that.options({content: $content})
|
}
|
||||||
|
that.options({content: $content});
|
||||||
});
|
});
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
|
@ -228,7 +228,7 @@ oml.ui.mainMenu = function() {
|
||||||
title: Ox._('Find'),
|
title: Ox._('Find'),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 'find',
|
id: 'finditems',
|
||||||
title: Ox._('Find'),
|
title: Ox._('Find'),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
@ -413,6 +413,9 @@ oml.ui.mainMenu = function() {
|
||||||
Ox.print('MAIN MENU DOES NOT YET HANDLE', id);
|
Ox.print('MAIN MENU DOES NOT YET HANDLE', id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
key_backtick: function() {
|
||||||
|
changeFocus(1);
|
||||||
|
},
|
||||||
key_control_comma: function() {
|
key_control_comma: function() {
|
||||||
if (!oml.hasDialogOrScreen()) {
|
if (!oml.hasDialogOrScreen()) {
|
||||||
oml.UI.set({page: 'preferences'});
|
oml.UI.set({page: 'preferences'});
|
||||||
|
@ -429,6 +432,35 @@ oml.ui.mainMenu = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
key_control_m: function() {
|
||||||
|
if (!oml.hasDialogOrScreen() && !that.isSelected()) {
|
||||||
|
that.options('menus')[0].element.trigger('click');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key_control_shift_w: function() {
|
||||||
|
if (!oml.hasDialogOrScreen()) {
|
||||||
|
oml.UI.set({
|
||||||
|
find: oml.config.user.ui.find,
|
||||||
|
item: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key_control_shift_z: function() {
|
||||||
|
oml.redoHistory();
|
||||||
|
},
|
||||||
|
key_control_slash: function() {
|
||||||
|
if (!oml.hasDialogOrScreen()) {
|
||||||
|
oml.UI.set({page: 'help'});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key_control_w: function() {
|
||||||
|
if (!oml.hasDialogOrScreen()) {
|
||||||
|
oml.UI.set({item: ''});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key_control_z: function() {
|
||||||
|
oml.undoHistory();
|
||||||
|
},
|
||||||
key_shift_b: function() {
|
key_shift_b: function() {
|
||||||
ui.item && oml.UI.set({showBrowser: !ui.showBrowser});
|
ui.item && oml.UI.set({showBrowser: !ui.showBrowser});
|
||||||
},
|
},
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
? Ox['format' + Ox.toTitleCase(key.format.type)].apply(
|
? Ox['format' + Ox.toTitleCase(key.format.type)].apply(
|
||||||
this, [value].concat(key.format.args || [])
|
this, [value].concat(key.format.args || [])
|
||||||
)
|
)
|
||||||
|
: Ox.isArray(key.type) ? value.join(', ')
|
||||||
: value;
|
: value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -107,7 +107,7 @@ oml.ui.preferencesDialog = function() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'resetUI',
|
id: 'resetUI',
|
||||||
title: 'Reset UI Settings',
|
title: 'Reset UI Settings...',
|
||||||
click: function() {
|
click: function() {
|
||||||
oml.$ui.resetUIDialog = oml.ui.resetUIDialog().open();
|
oml.$ui.resetUIDialog = oml.ui.resetUIDialog().open();
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,32 +2,21 @@
|
||||||
|
|
||||||
oml.ui.resetUIDialog = function(data) {
|
oml.ui.resetUIDialog = function(data) {
|
||||||
|
|
||||||
var that = oml.ui.iconDialog({
|
var that = oml.ui.confirmDialog({
|
||||||
buttons: [
|
buttons: [
|
||||||
Ox.Button({
|
Ox.Button({
|
||||||
id: 'cancel',
|
title: Ox._('No, Don\'t Reset')
|
||||||
title: Ox._('Don\'t Reset')
|
}),
|
||||||
})
|
|
||||||
.bindEvent({
|
|
||||||
click: function() {
|
|
||||||
that.close();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Ox.Button({
|
Ox.Button({
|
||||||
id: 'reset',
|
title: Ox._('Yes, Reset')
|
||||||
title: Ox._('Reset')
|
})
|
||||||
}).bindEvent({
|
|
||||||
click: function() {
|
|
||||||
that.close();
|
|
||||||
oml.$ui.preferencesDialog.close();
|
|
||||||
oml.UI.set({page: ''});
|
|
||||||
oml.UI.reset();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
content: Ox._('Are you sure you want to reset all UI settings?'),
|
content: Ox._('Are you sure you want to reset all UI settings?'),
|
||||||
keys: {enter: 'reset', escape: 'cancel'},
|
|
||||||
title: Ox._('Reset UI Settings')
|
title: Ox._('Reset UI Settings')
|
||||||
|
}, function() {
|
||||||
|
oml.$ui.preferencesDialog.close();
|
||||||
|
oml.UI.set({page: ''});
|
||||||
|
oml.UI.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
|
@ -19,9 +19,13 @@ oml.addList = function() {
|
||||||
name: oml.validateName(Ox._('Untitled'), listNames),
|
name: oml.validateName(Ox._('Untitled'), listNames),
|
||||||
type: !isSmart ? 'static' : 'smart'
|
type: !isSmart ? 'static' : 'smart'
|
||||||
};
|
};
|
||||||
if (isFrom) {
|
if (!isSmart) {
|
||||||
if (!isSmart) {
|
if (isFrom) {
|
||||||
data.items = ui.listSelection;
|
data.items = ui.listSelection;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isFrom) {
|
||||||
|
data.query = oml.config.user.ui.find;
|
||||||
} else {
|
} else {
|
||||||
data.query = ui.find;
|
data.query = ui.find;
|
||||||
}
|
}
|
||||||
|
@ -532,6 +536,16 @@ oml.enableDragAndDrop = function($list, canMove) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oml.getEditTooltip = function(title) {
|
||||||
|
return function(e) {
|
||||||
|
var $target = $(e.target);
|
||||||
|
return (
|
||||||
|
$target.is('a') || $target.parents('a').length
|
||||||
|
? Ox._('Shift+doubleclick to edit') : Ox._('Doubleclick to edit')
|
||||||
|
) + (title ? ' ' + Ox._(title) : '');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
// Note: getFindState has to run after getListState and getFilterState
|
// Note: getFindState has to run after getListState and getFilterState
|
||||||
|
@ -705,14 +719,9 @@ oml.getInfoHeight = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
oml.getListData = function(list) {
|
oml.getListData = function(list) {
|
||||||
var data = {}, ui = oml.user.ui;
|
var ui = oml.user.ui;
|
||||||
if (Ox.isUndefined(list)) {
|
list = Ox.isUndefined(list) ? ui._list : list;
|
||||||
list = ui._list;
|
return ui._lists ? Ox.getObjectById(ui._lists, list) : {};
|
||||||
}
|
|
||||||
if (ui._lists) {
|
|
||||||
data = ui._lists[list];
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
oml.getListFoldersHeight = function() {
|
oml.getListFoldersHeight = function() {
|
||||||
|
@ -769,25 +778,30 @@ oml.getUsersAndLists = function(callback) {
|
||||||
return user.peered;
|
return user.peered;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
users.forEach(function(user) {
|
|
||||||
lists.push({
|
|
||||||
id: (user.nickname == username ? '' : user.nickname) + ':',
|
|
||||||
name: Ox._('Library'),
|
|
||||||
type: 'library',
|
|
||||||
user: user.nickname
|
|
||||||
});
|
|
||||||
});
|
|
||||||
oml.api.getLists(function(result) {
|
oml.api.getLists(function(result) {
|
||||||
lists = lists.concat(result.data.lists);
|
users.forEach(function(user) {
|
||||||
|
lists = lists.concat([{
|
||||||
|
id: (user.nickname == username ? '' : user.nickname) + ':',
|
||||||
|
name: Ox._('Library'),
|
||||||
|
type: 'library',
|
||||||
|
user: user.nickname
|
||||||
|
}].concat(
|
||||||
|
result.data.lists.filter(function(list) {
|
||||||
|
return list.user == user.nickname;
|
||||||
|
})
|
||||||
|
));
|
||||||
|
});
|
||||||
|
lists = lists.map(function(list) {
|
||||||
|
return Ox.extend(list, {
|
||||||
|
editable: list.user == username && list.type == 'static',
|
||||||
|
title: (list.user ? list.user + ': ' : '') + list.name
|
||||||
|
});
|
||||||
|
})
|
||||||
if (!ui.lists) {
|
if (!ui.lists) {
|
||||||
oml.$ui.mainMenu.update();
|
oml.$ui.mainMenu.update();
|
||||||
}
|
}
|
||||||
ui._lists = {};
|
ui._lists = lists;
|
||||||
Ox.forEach(lists, function(list) {
|
Ox.print('UI._LISTS', JSON.stringify(ui._lists));
|
||||||
ui._lists[list.id] = Ox.extend(list, {
|
|
||||||
editable: list.user == username && list.type == 'static',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
callback(users, lists);
|
callback(users, lists);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -857,8 +871,8 @@ oml.updateFilterMenus = function() {
|
||||||
oml.updateLists = function(callback) {
|
oml.updateLists = function(callback) {
|
||||||
// FIXME: can this go somewhere else?
|
// FIXME: can this go somewhere else?
|
||||||
Ox.Request.clearCache('getLists');
|
Ox.Request.clearCache('getLists');
|
||||||
oml.api.getLists(function(result) {
|
oml.getUsersAndLists(function(users, lists) {
|
||||||
var items = result.data.lists.filter(function(list) {
|
var items = lists.filter(function(list) {
|
||||||
return list.user == oml.user.preferences.username;
|
return list.user == oml.user.preferences.username;
|
||||||
});
|
});
|
||||||
oml.$ui.folderList[0].options({
|
oml.$ui.folderList[0].options({
|
||||||
|
|
|
@ -15,13 +15,16 @@
|
||||||
"filter.js",
|
"filter.js",
|
||||||
"filtersInnerPanel.js",
|
"filtersInnerPanel.js",
|
||||||
"filtersOuterPanel.js",
|
"filtersOuterPanel.js",
|
||||||
|
"findDialog.js",
|
||||||
"findElement.js",
|
"findElement.js",
|
||||||
|
"findForm.js",
|
||||||
"folderList.js",
|
"folderList.js",
|
||||||
"folderPlaceholder.js",
|
"folderPlaceholder.js",
|
||||||
"folders.js",
|
"folders.js",
|
||||||
"fullscreenButton.js",
|
"fullscreenButton.js",
|
||||||
"gridView.js",
|
"gridView.js",
|
||||||
"iconDialog.js",
|
"iconDialog.js",
|
||||||
|
"identifyDialog.js",
|
||||||
"info.js",
|
"info.js",
|
||||||
"infoView.js",
|
"infoView.js",
|
||||||
"itemInnerPanel.js",
|
"itemInnerPanel.js",
|
||||||
|
|
Loading…
Reference in a new issue