import/lists/autocompleteFolder
This commit is contained in:
parent
94443ee667
commit
d6f350e5a1
42 changed files with 955 additions and 436 deletions
|
@ -10,7 +10,7 @@ Networking
|
||||||
----------
|
----------
|
||||||
|
|
||||||
At this time you need a working IPv6 connection to use Open Media Libary.
|
At this time you need a working IPv6 connection to use Open Media Libary.
|
||||||
If you dont have native IPv6 you can use Teredo/Miredo (apt-get install miredo)
|
If you dont have native IPv6 you can use Teredo/Miredo (`apt-get install miredo`)
|
||||||
or get a tunnel Hurricane Electric (https://tunnelbroker.net/)
|
or get a tunnel Hurricane Electric (https://tunnelbroker.net/)
|
||||||
or SixSS (https://sixxs.net).
|
or SixSS (https://sixxs.net).
|
||||||
|
|
||||||
|
|
75
oml/api.py
75
oml/api.py
|
@ -2,5 +2,80 @@
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import ox
|
||||||
|
from oxflask.api import actions
|
||||||
|
from oxflask.shortcuts import returns_json
|
||||||
|
|
||||||
import item.api
|
import item.api
|
||||||
import user.api
|
import user.api
|
||||||
|
|
||||||
|
@returns_json
|
||||||
|
def selectFolder(request):
|
||||||
|
'''
|
||||||
|
returns {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
cmd = ['./ctl', 'ui', 'folder']
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
path = stdout.decode('utf-8').strip()
|
||||||
|
return {
|
||||||
|
'path': path
|
||||||
|
}
|
||||||
|
actions.register(selectFolder, cache=False)
|
||||||
|
|
||||||
|
@returns_json
|
||||||
|
def selectFile(request):
|
||||||
|
'''
|
||||||
|
returns {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
cmd = ['./ctl', 'ui', 'file']
|
||||||
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
|
stdout, stderr = p.communicate()
|
||||||
|
path = stdout.decode('utf-8').strip()
|
||||||
|
return {
|
||||||
|
'path': path
|
||||||
|
}
|
||||||
|
actions.register(selectFile, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@returns_json
|
||||||
|
def autocompleteFolder(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
items
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
path = data['path']
|
||||||
|
path = os.path.expanduser(path)
|
||||||
|
if os.path.isdir(path):
|
||||||
|
if path.endswith('/') and path != '/':
|
||||||
|
path = path[:-1]
|
||||||
|
folder = path
|
||||||
|
name = ''
|
||||||
|
else:
|
||||||
|
folder, name = os.path.split(path)
|
||||||
|
if os.path.exists(folder):
|
||||||
|
prefix, folders, files = os.walk(folder).next()
|
||||||
|
folders = [os.path.join(prefix, f) for f in folders if not name or f.startswith(name)]
|
||||||
|
if prefix == path:
|
||||||
|
folders = [path] + folders
|
||||||
|
else:
|
||||||
|
folders = []
|
||||||
|
return {
|
||||||
|
'items': ox.sorted_strings(folders)
|
||||||
|
}
|
||||||
|
actions.register(autocompleteFolder, cache=False)
|
||||||
|
|
|
@ -18,8 +18,7 @@ import item.models
|
||||||
import user.models
|
import user.models
|
||||||
import item.person
|
import item.person
|
||||||
|
|
||||||
import item.api
|
import api
|
||||||
import user.api
|
|
||||||
|
|
||||||
import item.views
|
import item.views
|
||||||
import commands
|
import commands
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Downloads(Thread):
|
||||||
import item.models
|
import item.models
|
||||||
for i in item.models.Item.query.filter(
|
for i in item.models.Item.query.filter(
|
||||||
item.models.Item.transferadded!=None).filter(
|
item.models.Item.transferadded!=None).filter(
|
||||||
item.models.Item.transferprogress<1):
|
item.models.Item.transferprogress<1).order_by(item.models.Item.transferadded):
|
||||||
logger.debug('DOWNLOAD %s %s', i, i.users)
|
logger.debug('DOWNLOAD %s %s', i, i.users)
|
||||||
for p in i.users:
|
for p in i.users:
|
||||||
if state.nodes.check_online(p.id):
|
if state.nodes.check_online(p.id):
|
||||||
|
|
101
oml/item/api.py
101
oml/item/api.py
|
@ -2,11 +2,12 @@
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
import logging
|
import os
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from oxflask.api import actions
|
from oxflask.api import actions
|
||||||
from oxflask.shortcuts import returns_json
|
from oxflask.shortcuts import returns_json
|
||||||
|
from sqlalchemy.orm import load_only
|
||||||
|
|
||||||
import query
|
import query
|
||||||
|
|
||||||
|
@ -18,12 +19,22 @@ import meta
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
import logging
|
||||||
logger = logging.getLogger('oml.item.api')
|
logger = logging.getLogger('oml.item.api')
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def find(request):
|
def find(request):
|
||||||
'''
|
'''
|
||||||
find items
|
takes {
|
||||||
|
query {
|
||||||
|
conditions [{}]
|
||||||
|
operator string
|
||||||
|
}
|
||||||
|
group string
|
||||||
|
keys [string]
|
||||||
|
sort [{}]
|
||||||
|
range [int, int]
|
||||||
|
}
|
||||||
'''
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
@ -31,7 +42,7 @@ def find(request):
|
||||||
if 'group' in q:
|
if 'group' in q:
|
||||||
names = {}
|
names = {}
|
||||||
groups = {}
|
groups = {}
|
||||||
items = [i.id for i in q['qs']]
|
items = [i.id for i in q['qs'].options(load_only('id'))]
|
||||||
qs = models.Find.query.filter_by(key=q['group'])
|
qs = models.Find.query.filter_by(key=q['group'])
|
||||||
if items:
|
if items:
|
||||||
qs = qs.filter(models.Find.item_id.in_(items))
|
qs = qs.filter(models.Find.item_id.in_(items))
|
||||||
|
@ -58,16 +69,12 @@ def find(request):
|
||||||
else:
|
else:
|
||||||
response['items'] = len(g)
|
response['items'] = len(g)
|
||||||
elif 'position' in data:
|
elif 'position' in data:
|
||||||
ids = [i.id for i in q['qs']]
|
ids = [i.id for i in q['qs'].options(load_only('id'))]
|
||||||
response['position'] = utils.get_positions(ids, [data['qs'][0].id])[0]
|
response['position'] = utils.get_positions(ids, [data['qs'][0].id])[0]
|
||||||
elif 'positions' in data:
|
elif 'positions' in data:
|
||||||
ids = [i.id for i in q['qs']]
|
ids = [i.id for i in q['qs'].options(load_only('id'))]
|
||||||
response['positions'] = utils.get_positions(ids, data['positions'])
|
response['positions'] = utils.get_positions(ids, data['positions'])
|
||||||
elif 'keys' in data:
|
elif 'keys' in data:
|
||||||
'''
|
|
||||||
qs = qs[q['range'][0]:q['range'][1]]
|
|
||||||
response['items'] = [p.json(data['keys']) for p in qs]
|
|
||||||
'''
|
|
||||||
response['items'] = []
|
response['items'] = []
|
||||||
for i in q['qs'][q['range'][0]:q['range'][1]]:
|
for i in q['qs'][q['range'][0]:q['range'][1]]:
|
||||||
j = i.json()
|
j = i.json()
|
||||||
|
@ -77,12 +84,18 @@ def find(request):
|
||||||
#from sqlalchemy.sql import func
|
#from sqlalchemy.sql import func
|
||||||
#models.db.session.query(func.sum(models.Item.sort_size).label("size"))
|
#models.db.session.query(func.sum(models.Item.sort_size).label("size"))
|
||||||
#response['size'] = x.scalar()
|
#response['size'] = x.scalar()
|
||||||
response['size'] = sum([i.sort_size or 0 for i in q['qs']])
|
response['size'] = sum([i.sort_size or 0 for i in q['qs'].options(load_only('id', 'sort_size'))])
|
||||||
return response
|
return response
|
||||||
actions.register(find)
|
actions.register(find)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def get(request):
|
def get(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
keys
|
||||||
|
}
|
||||||
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
item = models.Item.get(data['id'])
|
item = models.Item.get(data['id'])
|
||||||
|
@ -93,29 +106,48 @@ actions.register(get)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def edit(request):
|
def edit(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
...
|
||||||
|
}
|
||||||
|
setting identifier or base metadata is possible not both at the same time
|
||||||
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
logger.debug('edit', data)
|
logger.debug('edit %s', data)
|
||||||
item = models.Item.get(data['id'])
|
item = models.Item.get(data['id'])
|
||||||
keys = filter(lambda k: k in models.Item.id_keys, data.keys())
|
keys = filter(lambda k: k in models.Item.id_keys, data.keys())
|
||||||
logger.debug(item, keys)
|
logger.debug('edit of %s id keys: %s', item, keys)
|
||||||
if item and keys and item.json()['mediastate'] == 'available':
|
if item and item.json()['mediastate'] == 'available':
|
||||||
|
if keys:
|
||||||
key = keys[0]
|
key = keys[0]
|
||||||
logger.debug('update mainid %s %s', key, data[key])
|
logger.debug('update mainid %s %s', key, data[key])
|
||||||
if key in ('isbn10', 'isbn13'):
|
if key in ('isbn10', 'isbn13'):
|
||||||
data[key] = utils.normalize_isbn(data[key])
|
data[key] = utils.normalize_isbn(data[key])
|
||||||
item.update_mainid(key, data[key])
|
item.update_mainid(key, data[key])
|
||||||
response = item.json()
|
response = item.json()
|
||||||
|
elif not item.meta.get('mainid'):
|
||||||
|
logger.debug('chustom data %s', data)
|
||||||
|
for key in ('title', 'author', 'date', 'publisher', 'edition'):
|
||||||
|
if key in data:
|
||||||
|
item.meta[key] = data[key]
|
||||||
|
item.update()
|
||||||
|
logger.debug('FIXME: custom metadata not published to changelog!!!')
|
||||||
else:
|
else:
|
||||||
logger.info('can only edit available items')
|
logger.info('can only edit available items')
|
||||||
response = item.json()
|
|
||||||
return response
|
return response
|
||||||
actions.register(edit, cache=False)
|
actions.register(edit, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def remove(request):
|
def remove(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
logger.debug('remove files', data)
|
logger.debug('remove files %s', data)
|
||||||
if 'ids' in data and data['ids']:
|
if 'ids' in data and data['ids']:
|
||||||
for i in models.Item.query.filter(models.Item.id.in_(data['ids'])):
|
for i in models.Item.query.filter(models.Item.id.in_(data['ids'])):
|
||||||
i.remove_file()
|
i.remove_file()
|
||||||
|
@ -132,10 +164,11 @@ def findMetadata(request):
|
||||||
date: string
|
date: string
|
||||||
}
|
}
|
||||||
returns {
|
returns {
|
||||||
title: string,
|
items: [{
|
||||||
autor: [string],
|
key: value
|
||||||
date: string,
|
}]
|
||||||
}
|
}
|
||||||
|
key is one of the supported identifiers: isbn10, isbn13...
|
||||||
'''
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
@ -146,18 +179,30 @@ actions.register(findMetadata)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def getMetadata(request):
|
def getMetadata(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
key: value
|
||||||
|
}
|
||||||
|
key can be one of the supported identifiers: isbn10, isbn13, oclc, olid,...
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
logger.debug('getMetadata %s', data)
|
logger.debug('getMetadata %s', data)
|
||||||
key, value = data.iteritems().next()
|
key, value = data.iteritems().next()
|
||||||
if key in ('isbn10', 'isbn13'):
|
if key in ('isbn10', 'isbn13'):
|
||||||
value = utils.normalize_isbn(value)
|
value = utils.normalize_isbn(value)
|
||||||
response = meta.lookup(key, value)
|
response = meta.lookup(key, value)
|
||||||
|
if response:
|
||||||
response['mainid'] = key
|
response['mainid'] = key
|
||||||
return response
|
return response
|
||||||
actions.register(getMetadata)
|
actions.register(getMetadata)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def download(request):
|
def download(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
item = models.Item.get(data['id'])
|
item = models.Item.get(data['id'])
|
||||||
|
@ -170,6 +215,11 @@ actions.register(download, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def cancelDownloads(request):
|
def cancelDownloads(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
ids = data['ids']
|
ids = data['ids']
|
||||||
|
@ -195,8 +245,21 @@ actions.register(scan, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def _import(request):
|
def _import(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
path absolute path to import
|
||||||
|
list listename (add new items to this list)
|
||||||
|
mode copy|move
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
logger.debug('api.import %s', data)
|
logger.debug('api.import %s', data)
|
||||||
state.main.add_callback(state.websockets[0].put, json.dumps(['import', data]))
|
state.main.add_callback(state.websockets[0].put, json.dumps(['import', data]))
|
||||||
return {}
|
return {}
|
||||||
actions.register(_import, 'import', cache=False)
|
actions.register(_import, 'import', cache=False)
|
||||||
|
|
||||||
|
@returns_json
|
||||||
|
def cancelImport(request):
|
||||||
|
state.activity['cancel'] = True
|
||||||
|
return {}
|
||||||
|
actions.register(cancelImport, cache=False)
|
||||||
|
|
|
@ -33,6 +33,7 @@ from oxflask.db import MutableDict
|
||||||
from covers import covers
|
from covers import covers
|
||||||
from changelog import Changelog
|
from changelog import Changelog
|
||||||
from websocket import trigger_event
|
from websocket import trigger_event
|
||||||
|
from utils import remove_empty_folders
|
||||||
|
|
||||||
logger = logging.getLogger('oml.item.model')
|
logger = logging.getLogger('oml.item.model')
|
||||||
|
|
||||||
|
@ -296,8 +297,8 @@ class Item(db.Model):
|
||||||
|
|
||||||
def extract_cover(self):
|
def extract_cover(self):
|
||||||
path = self.get_path()
|
path = self.get_path()
|
||||||
if not path:
|
if path:
|
||||||
return getattr(media, self.meta['extensions']).cover(path)
|
return getattr(media, self.info['extension']).cover(path)
|
||||||
|
|
||||||
def update_cover(self):
|
def update_cover(self):
|
||||||
cover = None
|
cover = None
|
||||||
|
@ -327,8 +328,6 @@ class Item(db.Model):
|
||||||
if mainid:
|
if mainid:
|
||||||
m = meta.lookup(mainid, self.meta[mainid])
|
m = meta.lookup(mainid, self.meta[mainid])
|
||||||
self.meta.update(m)
|
self.meta.update(m)
|
||||||
else:
|
|
||||||
logger.debug('FIX UPDATE %s', mainid)
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def queue_download(self):
|
def queue_download(self):
|
||||||
|
@ -360,6 +359,7 @@ class Item(db.Model):
|
||||||
Changelog.record(u, 'additem', self.id, self.info)
|
Changelog.record(u, 'additem', self.id, self.info)
|
||||||
self.update()
|
self.update()
|
||||||
f.move()
|
f.move()
|
||||||
|
self.update_cover()
|
||||||
trigger_event('transfer', {
|
trigger_event('transfer', {
|
||||||
'id': self.id, 'progress': 1
|
'id': self.id, 'progress': 1
|
||||||
})
|
})
|
||||||
|
@ -376,6 +376,7 @@ class Item(db.Model):
|
||||||
logger.debug('remove file %s', path)
|
logger.debug('remove file %s', path)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
remove_empty_folders(os.path.dirname(path))
|
||||||
db.session.delete(f)
|
db.session.delete(f)
|
||||||
user = state.user()
|
user = state.user()
|
||||||
self.users.remove(user)
|
self.users.remove(user)
|
||||||
|
@ -399,7 +400,7 @@ for key in config['itemKeys']:
|
||||||
col = db.Column(db.String(1000), index=True)
|
col = db.Column(db.String(1000), index=True)
|
||||||
setattr(Item, 'sort_%s' % key['id'], col)
|
setattr(Item, 'sort_%s' % key['id'], col)
|
||||||
|
|
||||||
Item.id_keys = ['isbn10', 'isbn13', 'lccn', 'olid', 'oclc']
|
Item.id_keys = ['isbn10', 'isbn13', 'lccn', 'olid', 'oclc', 'asin']
|
||||||
Item.item_keys = config['itemKeys']
|
Item.item_keys = config['itemKeys']
|
||||||
Item.filter_keys = [k['id'] for k in config['itemKeys'] if k.get('filter')]
|
Item.filter_keys = [k['id'] for k in config['itemKeys'] if k.get('filter')]
|
||||||
|
|
||||||
|
|
124
oml/item/scan.py
124
oml/item/scan.py
|
@ -19,6 +19,10 @@ from changelog import Changelog
|
||||||
import media
|
import media
|
||||||
from websocket import trigger_event
|
from websocket import trigger_event
|
||||||
import state
|
import state
|
||||||
|
from utils import remove_empty_folders
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('oml.item.scan')
|
||||||
|
|
||||||
extensions = ['epub', 'pdf', 'txt']
|
extensions = ['epub', 'pdf', 'txt']
|
||||||
|
|
||||||
|
@ -35,38 +39,10 @@ def remove_missing():
|
||||||
if dirty:
|
if dirty:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
def run_scan():
|
def add_file(id, f, prefix):
|
||||||
remove_missing()
|
user = state.user()
|
||||||
with app.app_context():
|
|
||||||
prefs = settings.preferences
|
|
||||||
prefix = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/')
|
|
||||||
if not prefix[-1] == '/':
|
|
||||||
prefix += '/'
|
|
||||||
user = User.get_or_create(settings.USER_ID)
|
|
||||||
assert isinstance(prefix, unicode)
|
|
||||||
books = []
|
|
||||||
for root, folders, files in os.walk(prefix):
|
|
||||||
for f in files:
|
|
||||||
#if f.startswith('._') or f == '.DS_Store':
|
|
||||||
if f.startswith('.'):
|
|
||||||
continue
|
|
||||||
f = os.path.join(root, f)
|
|
||||||
ext = f.split('.')[-1]
|
|
||||||
if ext in extensions:
|
|
||||||
books.append(f)
|
|
||||||
|
|
||||||
position = 0
|
|
||||||
added = 0
|
|
||||||
for f in ox.sorted_strings(books):
|
|
||||||
position += 1
|
|
||||||
id = media.get_id(f)
|
|
||||||
file = File.get(id)
|
|
||||||
path = f[len(prefix):]
|
path = f[len(prefix):]
|
||||||
if not file:
|
|
||||||
data = media.metadata(f)
|
data = media.metadata(f)
|
||||||
ext = f.split('.')[-1]
|
|
||||||
data['extension'] = ext
|
|
||||||
data['size'] = os.stat(f).st_size
|
|
||||||
file = File.get_or_create(id, data, path)
|
file = File.get_or_create(id, data, path)
|
||||||
item = file.item
|
item = file.item
|
||||||
if 'mainid' in file.info:
|
if 'mainid' in file.info:
|
||||||
|
@ -84,6 +60,35 @@ def run_scan():
|
||||||
})
|
})
|
||||||
item.added = datetime.now()
|
item.added = datetime.now()
|
||||||
item.scrape()
|
item.scrape()
|
||||||
|
return file
|
||||||
|
|
||||||
|
def run_scan():
|
||||||
|
remove_missing()
|
||||||
|
with app.app_context():
|
||||||
|
prefs = settings.preferences
|
||||||
|
prefix = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/')
|
||||||
|
if not prefix[-1] == '/':
|
||||||
|
prefix += '/'
|
||||||
|
assert isinstance(prefix, unicode)
|
||||||
|
books = []
|
||||||
|
for root, folders, files in os.walk(prefix):
|
||||||
|
for f in files:
|
||||||
|
#if f.startswith('._') or f == '.DS_Store':
|
||||||
|
if f.startswith('.'):
|
||||||
|
continue
|
||||||
|
f = os.path.join(root, f)
|
||||||
|
ext = f.split('.')[-1]
|
||||||
|
if ext in extensions:
|
||||||
|
books.append(f)
|
||||||
|
|
||||||
|
position = 0
|
||||||
|
added = 0
|
||||||
|
for f in ox.sorted_strings(books):
|
||||||
|
position += 1
|
||||||
|
id = media.get_id(f)
|
||||||
|
file = File.get(id)
|
||||||
|
if not file:
|
||||||
|
file = add_file(id, f, prefix)
|
||||||
added += 1
|
added += 1
|
||||||
trigger_event('change', {})
|
trigger_event('change', {})
|
||||||
|
|
||||||
|
@ -93,18 +98,28 @@ def run_import(options=None):
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
prefs = settings.preferences
|
prefs = settings.preferences
|
||||||
prefix = os.path.expanduser(options.get('path', prefs['importPath']))
|
prefix = os.path.expanduser(options.get('path', prefs['importPath']))
|
||||||
|
if os.path.islink(prefix):
|
||||||
|
prefix = os.path.realpath(prefix)
|
||||||
if not prefix[-1] == '/':
|
if not prefix[-1] == '/':
|
||||||
prefix += '/'
|
prefix += '/'
|
||||||
prefix_books = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/')
|
prefix_books = os.path.join(os.path.expanduser(prefs['libraryPath']), 'Books/')
|
||||||
prefix_imported = os.path.join(prefix_books, 'Imported/')
|
prefix_imported = os.path.join(prefix_books, 'Imported/')
|
||||||
if not os.path.exists(prefix):
|
if prefix_books.startswith(prefix) or prefix.startswith(prefix_books):
|
||||||
|
error = 'invalid path'
|
||||||
|
elif not os.path.exists(prefix):
|
||||||
|
error = 'path not found'
|
||||||
|
elif not os.path.isdir(prefix):
|
||||||
|
error = 'path must be a folder'
|
||||||
|
else:
|
||||||
|
error = None
|
||||||
|
if error:
|
||||||
trigger_event('activity', {
|
trigger_event('activity', {
|
||||||
'activity': 'import',
|
'activity': 'import',
|
||||||
'progress': [0, 0],
|
'progress': [0, 0],
|
||||||
'status': {'code': 404, 'text': 'path not found'}
|
'status': {'code': 404, 'text': error}
|
||||||
})
|
})
|
||||||
state.activity = {}
|
state.activity = {}
|
||||||
user = User.get_or_create(settings.USER_ID)
|
return
|
||||||
listname = options.get('list')
|
listname = options.get('list')
|
||||||
if listname:
|
if listname:
|
||||||
listitems = []
|
listitems = []
|
||||||
|
@ -122,6 +137,7 @@ def run_import(options=None):
|
||||||
|
|
||||||
state.activity = {
|
state.activity = {
|
||||||
'activity': 'import',
|
'activity': 'import',
|
||||||
|
'path': prefix,
|
||||||
'progress': [0, len(books)],
|
'progress': [0, len(books)],
|
||||||
}
|
}
|
||||||
trigger_event('activity', state.activity)
|
trigger_event('activity', state.activity)
|
||||||
|
@ -133,7 +149,6 @@ def run_import(options=None):
|
||||||
continue
|
continue
|
||||||
id = media.get_id(f)
|
id = media.get_id(f)
|
||||||
file = File.get(id)
|
file = File.get(id)
|
||||||
path = f[len(prefix):]
|
|
||||||
if not file:
|
if not file:
|
||||||
f_import = f
|
f_import = f
|
||||||
f = f.replace(prefix, prefix_imported)
|
f = f.replace(prefix, prefix_imported)
|
||||||
|
@ -142,45 +157,38 @@ def run_import(options=None):
|
||||||
shutil.move(f_import, f)
|
shutil.move(f_import, f)
|
||||||
else:
|
else:
|
||||||
shutil.copy(f_import, f)
|
shutil.copy(f_import, f)
|
||||||
path = f[len(prefix_books):]
|
file = add_file(id, f, prefix_books)
|
||||||
data = media.metadata(f)
|
|
||||||
ext = f.split('.')[-1]
|
|
||||||
data['extension'] = ext
|
|
||||||
data['size'] = os.stat(f).st_size
|
|
||||||
file = File.get_or_create(id, data, path)
|
|
||||||
item = file.item
|
|
||||||
if 'mainid' in file.info:
|
|
||||||
del file.info['mainid']
|
|
||||||
db.session.add(file)
|
|
||||||
if 'mainid' in item.info:
|
|
||||||
item.meta['mainid'] = item.info.pop('mainid')
|
|
||||||
item.meta[item.meta['mainid']] = item.info[item.meta['mainid']]
|
|
||||||
db.session.add(item)
|
|
||||||
item.users.append(user)
|
|
||||||
Changelog.record(user, 'additem', item.id, item.info)
|
|
||||||
if item.meta.get('mainid'):
|
|
||||||
Changelog.record(user, 'edititem', item.id, {
|
|
||||||
item.meta['mainid']: item.meta[item.meta['mainid']]
|
|
||||||
})
|
|
||||||
item.scrape()
|
|
||||||
file.move()
|
file.move()
|
||||||
|
item = file.item
|
||||||
if listname:
|
if listname:
|
||||||
listitems.append(item.id)
|
listitems.append(item.id)
|
||||||
added += 1
|
added += 1
|
||||||
|
if state.activity.get('cancel'):
|
||||||
|
state.activity = {}
|
||||||
|
trigger_event('activity', {
|
||||||
|
'activity': 'import',
|
||||||
|
'status': {'code': 200, 'text': 'canceled'}
|
||||||
|
})
|
||||||
|
return
|
||||||
state.activity = {
|
state.activity = {
|
||||||
'activity': 'import',
|
'activity': 'import',
|
||||||
'progress': [position, len(books)],
|
'progress': [position, len(books)],
|
||||||
'path': path,
|
'path': prefix,
|
||||||
'added': added,
|
'added': added,
|
||||||
}
|
}
|
||||||
trigger_event('activity', state.activity)
|
trigger_event('activity', state.activity)
|
||||||
if listname:
|
if listname and listitems:
|
||||||
l = List.get_or_create(settings.USER_ID, listname)
|
l = List.get(settings.USER_ID, listname)
|
||||||
|
if l:
|
||||||
l.add_items(listitems)
|
l.add_items(listitems)
|
||||||
trigger_event('activity', {
|
trigger_event('activity', {
|
||||||
'activity': 'import',
|
'activity': 'import',
|
||||||
'progress': [position, len(books)],
|
'progress': [position, len(books)],
|
||||||
|
'path': prefix,
|
||||||
'status': {'code': 200, 'text': ''},
|
'status': {'code': 200, 'text': ''},
|
||||||
'added': added,
|
'added': added,
|
||||||
})
|
})
|
||||||
state.activity = {}
|
state.activity = {}
|
||||||
|
remove_empty_folders(prefix_books)
|
||||||
|
if options.get('mode') == 'move':
|
||||||
|
remove_empty_folders(prefix)
|
||||||
|
|
|
@ -22,6 +22,8 @@ def get_id(f=None, data=None):
|
||||||
def metadata(f):
|
def metadata(f):
|
||||||
ext = f.split('.')[-1]
|
ext = f.split('.')[-1]
|
||||||
data = {}
|
data = {}
|
||||||
|
data['extension'] = ext
|
||||||
|
data['size'] = os.stat(f).st_size
|
||||||
if ext == 'pdf':
|
if ext == 'pdf':
|
||||||
info = pdf.info(f)
|
info = pdf.info(f)
|
||||||
elif ext == 'epub':
|
elif ext == 'epub':
|
||||||
|
|
|
@ -71,9 +71,11 @@ def info(pdf):
|
||||||
with open(pdf, 'rb') as fd:
|
with open(pdf, 'rb') as fd:
|
||||||
try:
|
try:
|
||||||
pdfreader = PdfFileReader(fd)
|
pdfreader = PdfFileReader(fd)
|
||||||
|
data['pages'] = pdfreader.numPages
|
||||||
info = pdfreader.getDocumentInfo()
|
info = pdfreader.getDocumentInfo()
|
||||||
if info:
|
if info:
|
||||||
for key in info:
|
for key in info:
|
||||||
|
print key, info
|
||||||
if info[key]:
|
if info[key]:
|
||||||
data[key[1:].lower()] = info[key]
|
data[key[1:].lower()] = info[key]
|
||||||
xmp =pdfreader.getXmpMetadata()
|
xmp =pdfreader.getXmpMetadata()
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
import logging
|
import stdnum.isbn
|
||||||
logger = logging.getLogger('meta')
|
|
||||||
|
|
||||||
import abebooks
|
import abebooks
|
||||||
import loc
|
import loc
|
||||||
|
@ -13,6 +12,10 @@ import worldcat
|
||||||
import google
|
import google
|
||||||
import duckduckgo
|
import duckduckgo
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('meta')
|
||||||
|
|
||||||
|
|
||||||
providers = [
|
providers = [
|
||||||
('openlibrary', 'olid'),
|
('openlibrary', 'olid'),
|
||||||
('loc', 'lccn'),
|
('loc', 'lccn'),
|
||||||
|
@ -32,6 +35,8 @@ def find(title, author=None, publisher=None, date=None):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def lookup(key, value):
|
def lookup(key, value):
|
||||||
|
if not isvalid_id(key, value):
|
||||||
|
return {}
|
||||||
data = {key: value}
|
data = {key: value}
|
||||||
ids = [(key, value)]
|
ids = [(key, value)]
|
||||||
provider_data = {}
|
provider_data = {}
|
||||||
|
@ -59,4 +64,13 @@ def lookup(key, value):
|
||||||
data[k_] = v_
|
data[k_] = v_
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def isvalid_id(key, value):
|
||||||
|
if key in ('isbn10', 'isbn13'):
|
||||||
|
if 'isbn%d'%len(value) != key or not stdnum.isbn.is_valid(value):
|
||||||
|
return False
|
||||||
|
if key == 'asin' and len(value) != 10:
|
||||||
|
return False
|
||||||
|
if key == 'olid' and not (value.startswith('OL') and value.endswith('M')):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,11 @@ import lxml.html
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('meta.abebooks')
|
logger = logging.getLogger('meta.abebooks')
|
||||||
|
|
||||||
|
base = 'http://www.abebooks.com'
|
||||||
|
|
||||||
def get_ids(key, value):
|
def get_ids(key, value):
|
||||||
ids = []
|
ids = []
|
||||||
if key in ('isbn10', 'isbn13'):
|
if key in ('isbn10', 'isbn13'):
|
||||||
base = 'http://www.abebooks.com'
|
|
||||||
url = '%s/servlet/SearchResults?isbn=%s&sts=t' % (base, id)
|
url = '%s/servlet/SearchResults?isbn=%s&sts=t' % (base, id)
|
||||||
data = read_url(url)
|
data = read_url(url)
|
||||||
urls = re.compile('href="(/servlet/BookDetailsPL[^"]+)"').findall(data)
|
urls = re.compile('href="(/servlet/BookDetailsPL[^"]+)"').findall(data)
|
||||||
|
@ -24,21 +25,20 @@ def get_ids(key, value):
|
||||||
|
|
||||||
def lookup(id):
|
def lookup(id):
|
||||||
logger.debug('lookup %s', id)
|
logger.debug('lookup %s', id)
|
||||||
return {}
|
data = {}
|
||||||
|
|
||||||
def get_data(id):
|
|
||||||
info = {}
|
|
||||||
base = 'http://www.abebooks.com'
|
|
||||||
url = '%s/servlet/SearchResults?isbn=%s&sts=t' % (base, id)
|
url = '%s/servlet/SearchResults?isbn=%s&sts=t' % (base, id)
|
||||||
data = read_url(url)
|
html = read_url(url)
|
||||||
urls = re.compile('href="(/servlet/BookDetailsPL[^"]+)"').findall(data)
|
urls = re.compile('href="(/servlet/BookDetailsPL[^"]+)"').findall(html)
|
||||||
|
keys = {
|
||||||
|
'pubdate': 'date'
|
||||||
|
}
|
||||||
if urls:
|
if urls:
|
||||||
details = '%s%s' % (base, urls[0])
|
details = '%s%s' % (base, urls[0])
|
||||||
data = read_url(details)
|
html = read_url(details)
|
||||||
doc = lxml.html.document_fromstring(data)
|
doc = lxml.html.document_fromstring(html)
|
||||||
for e in doc.xpath("//*[contains(@id, 'biblio')]"):
|
for e in doc.xpath("//*[contains(@id, 'biblio')]"):
|
||||||
key = e.attrib['id'].replace('biblio-', '')
|
key = e.attrib['id'].replace('biblio-', '')
|
||||||
value = e.text_content()
|
value = e.text_content()
|
||||||
if value and key not in ('bookcondition', 'binding'):
|
if value and key not in ('bookcondition', 'binding', 'edition-amz'):
|
||||||
info[key] = value
|
data[keys.get(key, key)] = value
|
||||||
return info
|
return data
|
||||||
|
|
|
@ -37,6 +37,6 @@ def find(title, author=None, publisher=None, date=None):
|
||||||
done.add(isbn)
|
done.add(isbn)
|
||||||
if len(isbn) == 10:
|
if len(isbn) == 10:
|
||||||
done.add(stdnum.isbn.to_isbn13(isbn))
|
done.add(stdnum.isbn.to_isbn13(isbn))
|
||||||
if len(isbn) == 13:
|
if len(isbn) == 13 and isbn.startswith('978'):
|
||||||
done.add(stdnum.isbn.to_isbn10(isbn))
|
done.add(stdnum.isbn.to_isbn10(isbn))
|
||||||
return results
|
return results
|
||||||
|
|
|
@ -45,9 +45,9 @@ def lookup(id):
|
||||||
}
|
}
|
||||||
for key in keys:
|
for key in keys:
|
||||||
r[key] = find_re(data, '<span class="title">%s:</span>(.*?)</li>'% re.escape(keys[key]))
|
r[key] = find_re(data, '<span class="title">%s:</span>(.*?)</li>'% re.escape(keys[key]))
|
||||||
if r[key] == '--':
|
if r[key] == '--' or not r[key]:
|
||||||
r[key] = ''
|
del r[key]
|
||||||
if key == 'pages' and r[key]:
|
if key == 'pages' and key in r:
|
||||||
r[key] = int(r[key])
|
r[key] = int(r[key])
|
||||||
desc = find_re(data, '<h2>Description:<\/h2>(.*?)<div ')
|
desc = find_re(data, '<h2>Description:<\/h2>(.*?)<div ')
|
||||||
desc = desc.replace('<br /><br />', ' ').replace('<br /> ', ' ').replace('<br />', ' ')
|
desc = desc.replace('<br /><br />', ' ').replace('<br /> ', ' ').replace('<br />', ' ')
|
||||||
|
|
|
@ -62,6 +62,8 @@ def api_requestPeering(app, user_id, username, message):
|
||||||
def api_acceptPeering(app, user_id, username, message):
|
def api_acceptPeering(app, user_id, username, message):
|
||||||
user = User.get(user_id)
|
user = User.get(user_id)
|
||||||
logger.debug('incoming acceptPeering event: pending: %s', user.pending)
|
logger.debug('incoming acceptPeering event: pending: %s', user.pending)
|
||||||
|
if user and user.peered:
|
||||||
|
return True
|
||||||
if user and user.pending == 'sent':
|
if user and user.pending == 'sent':
|
||||||
if not user.info:
|
if not user.info:
|
||||||
user.info = {}
|
user.info = {}
|
||||||
|
|
|
@ -120,7 +120,7 @@ def publish_node(app):
|
||||||
for u in user.models.User.query.filter_by(queued=True):
|
for u in user.models.User.query.filter_by(queued=True):
|
||||||
logger.debug('adding queued node... %s', u.id)
|
logger.debug('adding queued node... %s', u.id)
|
||||||
state.nodes.queue('add', u.id)
|
state.nodes.queue('add', u.id)
|
||||||
state.check_nodes = PeriodicCallback(lambda: check_nodes(app), 60000)
|
state.check_nodes = PeriodicCallback(lambda: check_nodes(app), 120000)
|
||||||
state.check_nodes.start()
|
state.check_nodes.start()
|
||||||
|
|
||||||
def check_nodes(app):
|
def check_nodes(app):
|
||||||
|
@ -135,7 +135,7 @@ def start(app):
|
||||||
application = Application([
|
application = Application([
|
||||||
(r"/get/(.*)", ShareHandler, dict(app=app)),
|
(r"/get/(.*)", ShareHandler, dict(app=app)),
|
||||||
(r".*", NodeHandler, dict(app=app)),
|
(r".*", NodeHandler, dict(app=app)),
|
||||||
])
|
], gzip=True)
|
||||||
if not os.path.exists(settings.ssl_cert_path):
|
if not os.path.exists(settings.ssl_cert_path):
|
||||||
settings.server['cert'] = cert.generate_ssl()
|
settings.server['cert'] = cert.generate_ssl()
|
||||||
|
|
||||||
|
|
38
oml/nodes.py
38
oml/nodes.py
|
@ -6,13 +6,15 @@ from Queue import Queue
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import json
|
import json
|
||||||
import socket
|
import socket
|
||||||
|
from StringIO import StringIO
|
||||||
|
import gzip
|
||||||
|
import urllib2
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
import ed25519
|
import ed25519
|
||||||
import urllib2
|
from tornado.ioloop import PeriodicCallback
|
||||||
|
|
||||||
import settings
|
import settings
|
||||||
import user.models
|
import user.models
|
||||||
|
@ -42,6 +44,8 @@ class Node(object):
|
||||||
self.vk = ed25519.VerifyingKey(key, encoding=ENCODING)
|
self.vk = ed25519.VerifyingKey(key, encoding=ENCODING)
|
||||||
self.go_online()
|
self.go_online()
|
||||||
logger.debug('new Node %s online=%s', self.user_id, self.online)
|
logger.debug('new Node %s online=%s', self.user_id, self.online)
|
||||||
|
self._ping = PeriodicCallback(self.ping, 120000)
|
||||||
|
self._ping.start()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
@ -120,6 +124,8 @@ class Node(object):
|
||||||
self.online = False
|
self.online = False
|
||||||
return None
|
return None
|
||||||
data = r.read()
|
data = r.read()
|
||||||
|
if r.headers.get('content-encoding', None) == 'gzip':
|
||||||
|
data = gzip.GzipFile(fileobj=StringIO(data)).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)
|
||||||
|
@ -151,6 +157,13 @@ class Node(object):
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
with self._app.app_context():
|
||||||
|
if self.online:
|
||||||
|
self.online = self.can_connect()
|
||||||
|
else:
|
||||||
|
self.go_online()
|
||||||
|
|
||||||
def go_online(self):
|
def go_online(self):
|
||||||
self.resolve()
|
self.resolve()
|
||||||
u = self.user
|
u = self.user
|
||||||
|
@ -179,7 +192,7 @@ class Node(object):
|
||||||
self.online = False
|
self.online = False
|
||||||
trigger_event('status', {
|
trigger_event('status', {
|
||||||
'id': self.user_id,
|
'id': self.user_id,
|
||||||
'status': 'online' if self.online else 'offline'
|
'online': self.online
|
||||||
})
|
})
|
||||||
|
|
||||||
def pullChanges(self):
|
def pullChanges(self):
|
||||||
|
@ -199,7 +212,7 @@ class Node(object):
|
||||||
self.online = False
|
self.online = False
|
||||||
trigger_event('status', {
|
trigger_event('status', {
|
||||||
'id': self.user_id,
|
'id': self.user_id,
|
||||||
'status': 'offline'
|
'online': self.online
|
||||||
})
|
})
|
||||||
r = False
|
r = False
|
||||||
logger.debug('pushedChanges %s %s', r, self.user_id)
|
logger.debug('pushedChanges %s %s', r, self.user_id)
|
||||||
|
@ -210,7 +223,7 @@ class Node(object):
|
||||||
r = self.request(action, settings.preferences['username'], u.info.get('message'))
|
r = self.request(action, settings.preferences['username'], u.info.get('message'))
|
||||||
else:
|
else:
|
||||||
r = self.request(action, u.info.get('message'))
|
r = self.request(action, u.info.get('message'))
|
||||||
if r:
|
if r != None:
|
||||||
u.queued = False
|
u.queued = False
|
||||||
if 'message' in u.info:
|
if 'message' in u.info:
|
||||||
del u.info['message']
|
del u.info['message']
|
||||||
|
@ -237,7 +250,21 @@ class Node(object):
|
||||||
self._opener.addheaders = zip(headers.keys(), headers.values())
|
self._opener.addheaders = zip(headers.keys(), headers.values())
|
||||||
r = self._opener.open(url, timeout=self.TIMEOUT)
|
r = self._opener.open(url, timeout=self.TIMEOUT)
|
||||||
if r.getcode() == 200:
|
if r.getcode() == 200:
|
||||||
|
if r.headers.get('content-encoding', None) == 'gzip':
|
||||||
|
content = gzip.GzipFile(fileobj=r).read()
|
||||||
|
else:
|
||||||
|
'''
|
||||||
|
content = ''
|
||||||
|
for chunk in iter(lambda: r.read(1024*1024), ''):
|
||||||
|
content += chunk
|
||||||
|
item.transferprogress = len(content) / item.info['size']
|
||||||
|
item.save()
|
||||||
|
trigger_event('transfer', {
|
||||||
|
'id': item.id, 'progress': item.transferprogress
|
||||||
|
})
|
||||||
|
'''
|
||||||
content = r.read()
|
content = r.read()
|
||||||
|
|
||||||
t2 = datetime.now()
|
t2 = datetime.now()
|
||||||
duration = (t2-t1).total_seconds()
|
duration = (t2-t1).total_seconds()
|
||||||
if duration:
|
if duration:
|
||||||
|
@ -308,7 +335,6 @@ class Nodes(Thread):
|
||||||
from user.models import User
|
from user.models import User
|
||||||
self._nodes[user_id] = Node(self, User.get_or_create(user_id))
|
self._nodes[user_id] = Node(self, User.get_or_create(user_id))
|
||||||
else:
|
else:
|
||||||
logger.debug('bring existing node online %s', user_id)
|
|
||||||
if not self._nodes[user_id].online:
|
if not self._nodes[user_id].online:
|
||||||
self._nodes[user_id].go_online()
|
self._nodes[user_id].go_online()
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,14 @@ from sqlalchemy.sql.expression import and_, not_, or_, ClauseElement
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from sqlalchemy.sql import operators, extract
|
from sqlalchemy.sql import operators, extract
|
||||||
|
from sqlalchemy.orm import load_only
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
import settings
|
import settings
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('oxflask.query')
|
||||||
|
|
||||||
def get_operator(op, type='str'):
|
def get_operator(op, type='str'):
|
||||||
return {
|
return {
|
||||||
'str': {
|
'str': {
|
||||||
|
@ -56,6 +60,9 @@ class Parser(object):
|
||||||
}
|
}
|
||||||
...
|
...
|
||||||
'''
|
'''
|
||||||
|
logger.debug('parse_condition %s', condition)
|
||||||
|
if not 'value' in condition:
|
||||||
|
return None
|
||||||
k = condition.get('key', '*')
|
k = condition.get('key', '*')
|
||||||
if not k:
|
if not k:
|
||||||
k = '*'
|
k = '*'
|
||||||
|
@ -105,34 +112,7 @@ class Parser(object):
|
||||||
q = ~q
|
q = ~q
|
||||||
return q
|
return q
|
||||||
elif k == 'list':
|
elif k == 'list':
|
||||||
'''
|
nickname, name = v.split(':', 1)
|
||||||
q = Q(id=0)
|
|
||||||
l = v.split(":")
|
|
||||||
if len(l) == 1:
|
|
||||||
vqs = Volume.objects.filter(name=v, user=user)
|
|
||||||
if vqs.count() == 1:
|
|
||||||
v = vqs[0]
|
|
||||||
q = Q(files__instances__volume__id=v.id)
|
|
||||||
elif len(l) >= 2:
|
|
||||||
l = (l[0], ":".join(l[1:]))
|
|
||||||
lqs = list(List.objects.filter(name=l[1], user__username=l[0]))
|
|
||||||
if len(lqs) == 1 and lqs[0].accessible(user):
|
|
||||||
l = lqs[0]
|
|
||||||
if l.query.get('static', False) == False:
|
|
||||||
data = l.query
|
|
||||||
q = self.parse_conditions(data.get('conditions', []),
|
|
||||||
data.get('operator', '&'),
|
|
||||||
user, l.user)
|
|
||||||
else:
|
|
||||||
q = Q(id__in=l.items.all())
|
|
||||||
if exclude:
|
|
||||||
q = ~q
|
|
||||||
else:
|
|
||||||
q = Q(id=0)
|
|
||||||
'''
|
|
||||||
l = v.split(":")
|
|
||||||
nickname = l[0]
|
|
||||||
name = ':'.join(l[1:])
|
|
||||||
if nickname:
|
if nickname:
|
||||||
p = self._user.query.filter_by(nickname=nickname).first()
|
p = self._user.query.filter_by(nickname=nickname).first()
|
||||||
v = '%s:%s' % (p.id, name)
|
v = '%s:%s' % (p.id, name)
|
||||||
|
@ -150,6 +130,16 @@ class Parser(object):
|
||||||
data = l._query
|
data = l._query
|
||||||
q = self.parse_conditions(data.get('conditions', []),
|
q = self.parse_conditions(data.get('conditions', []),
|
||||||
data.get('operator', '&'))
|
data.get('operator', '&'))
|
||||||
|
else:
|
||||||
|
if exclude:
|
||||||
|
q = (self._find.key == 'list') & (self._find.value == v)
|
||||||
|
ids = [i.id
|
||||||
|
for i in self._model.query.join(self._find).filter(q).group_by(self._model.id).options(load_only('id'))]
|
||||||
|
if ids:
|
||||||
|
q = ~self._model.id.in_(ids)
|
||||||
|
else:
|
||||||
|
q = (self._model.id != 0)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
q = (self._find.key == 'list') & (self._find.value == v)
|
q = (self._find.key == 'list') & (self._find.value == v)
|
||||||
return q
|
return q
|
||||||
|
|
195
oml/user/api.py
195
oml/user/api.py
|
@ -4,7 +4,6 @@ from __future__ import division
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import subprocess
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from oxflask.api import actions
|
from oxflask.api import actions
|
||||||
|
@ -12,7 +11,7 @@ from oxflask.shortcuts import returns_json
|
||||||
|
|
||||||
import models
|
import models
|
||||||
|
|
||||||
from utils import get_position_by_id
|
from utils import update_dict
|
||||||
|
|
||||||
import settings
|
import settings
|
||||||
import state
|
import state
|
||||||
|
@ -24,7 +23,14 @@ logger = logging.getLogger('oml.user.api')
|
||||||
@returns_json
|
@returns_json
|
||||||
def init(request):
|
def init(request):
|
||||||
'''
|
'''
|
||||||
this is an init request to test stuff
|
takes {
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
config
|
||||||
|
user
|
||||||
|
preferences
|
||||||
|
ui
|
||||||
|
}
|
||||||
'''
|
'''
|
||||||
response = {}
|
response = {}
|
||||||
if os.path.exists(settings.oml_config_path):
|
if os.path.exists(settings.oml_config_path):
|
||||||
|
@ -43,26 +49,14 @@ def init(request):
|
||||||
return response
|
return response
|
||||||
actions.register(init)
|
actions.register(init)
|
||||||
|
|
||||||
def update_dict(root, data):
|
|
||||||
for key in data:
|
|
||||||
keys = map(lambda p: p.replace('\0', '\\.'), key.replace('\\.', '\0').split('.'))
|
|
||||||
value = data[key]
|
|
||||||
p = root
|
|
||||||
while len(keys)>1:
|
|
||||||
key = keys.pop(0)
|
|
||||||
if isinstance(p, list):
|
|
||||||
p = p[get_position_by_id(p, key)]
|
|
||||||
else:
|
|
||||||
if key not in p:
|
|
||||||
p[key] = {}
|
|
||||||
p = p[key]
|
|
||||||
if value == None and keys[0] in p:
|
|
||||||
del p[keys[0]]
|
|
||||||
else:
|
|
||||||
p[keys[0]] = value
|
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def setPreferences(request):
|
def setPreferences(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
key: value,
|
||||||
|
'sub.key': value
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
update_dict(settings.preferences, data)
|
update_dict(settings.preferences, data)
|
||||||
return settings.preferences
|
return settings.preferences
|
||||||
|
@ -70,6 +64,12 @@ actions.register(setPreferences)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def setUI(request):
|
def setUI(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
key: value,
|
||||||
|
'sub.key': value
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
update_dict(settings.ui, data)
|
update_dict(settings.ui, data)
|
||||||
return settings.ui
|
return settings.ui
|
||||||
|
@ -77,6 +77,11 @@ actions.register(setUI)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def getUsers(request):
|
def getUsers(request):
|
||||||
|
'''
|
||||||
|
returns {
|
||||||
|
users: []
|
||||||
|
}
|
||||||
|
'''
|
||||||
users = []
|
users = []
|
||||||
for u in models.User.query.filter(models.User.id!=settings.USER_ID).all():
|
for u in models.User.query.filter(models.User.id!=settings.USER_ID).all():
|
||||||
users.append(u.json())
|
users.append(u.json())
|
||||||
|
@ -87,7 +92,20 @@ actions.register(getUsers)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def getLists(request):
|
def getLists(request):
|
||||||
|
'''
|
||||||
|
returns {
|
||||||
|
lists: []
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
from item.models import Item
|
||||||
lists = []
|
lists = []
|
||||||
|
lists.append({
|
||||||
|
'id': '',
|
||||||
|
'items': Item.query.count(),
|
||||||
|
'name': 'Libraries',
|
||||||
|
'type': 'libraries',
|
||||||
|
'user': '',
|
||||||
|
})
|
||||||
for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)):
|
for u in models.User.query.filter((models.User.peered==True)|(models.User.id==settings.USER_ID)):
|
||||||
lists += u.lists_json()
|
lists += u.lists_json()
|
||||||
return {
|
return {
|
||||||
|
@ -95,30 +113,47 @@ def getLists(request):
|
||||||
}
|
}
|
||||||
actions.register(getLists)
|
actions.register(getLists)
|
||||||
|
|
||||||
|
def validate_query(query):
|
||||||
|
for condition in query['conditions']:
|
||||||
|
if not list(sorted(condition.keys())) in (
|
||||||
|
['conditions', 'operator'],
|
||||||
|
['key', 'operator', 'value']
|
||||||
|
):
|
||||||
|
raise Exception('invalid query condition', condition)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def addList(request):
|
def addList(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
name
|
||||||
|
items
|
||||||
|
query
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
logger.debug('addList %s', data)
|
||||||
user_id = settings.USER_ID
|
user_id = settings.USER_ID
|
||||||
l = models.List.get(user_id, data['name'])
|
if 'query' in data:
|
||||||
if not l:
|
validate_query(data['query'])
|
||||||
|
if data['name']:
|
||||||
l = models.List.create(user_id, data['name'], data.get('query'))
|
l = models.List.create(user_id, data['name'], data.get('query'))
|
||||||
if 'items' in data:
|
if 'items' in data:
|
||||||
l.add_items(data['items'])
|
l.add_items(data['items'])
|
||||||
return l.json()
|
return l.json()
|
||||||
|
else:
|
||||||
|
raise Exception('name not set')
|
||||||
return {}
|
return {}
|
||||||
actions.register(addList, cache=False)
|
actions.register(addList, cache=False)
|
||||||
|
|
||||||
@returns_json
|
|
||||||
def removeList(request):
|
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
|
||||||
l = models.List.get(data['id'])
|
|
||||||
if l:
|
|
||||||
l.remove()
|
|
||||||
return {}
|
|
||||||
actions.register(removeList, cache=False)
|
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def editList(request):
|
def editList(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
query
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
logger.debug('editList %s', data)
|
logger.debug('editList %s', data)
|
||||||
l = models.List.get_or_create(data['id'])
|
l = models.List.get_or_create(data['id'])
|
||||||
|
@ -126,6 +161,7 @@ def editList(request):
|
||||||
if 'name' in data:
|
if 'name' in data:
|
||||||
l.name = data['name']
|
l.name = data['name']
|
||||||
if 'query' in data:
|
if 'query' in data:
|
||||||
|
validate_query(data['query'])
|
||||||
l._query = data['query']
|
l._query = data['query']
|
||||||
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})
|
||||||
|
@ -133,8 +169,29 @@ def editList(request):
|
||||||
return l.json()
|
return l.json()
|
||||||
actions.register(editList, cache=False)
|
actions.register(editList, cache=False)
|
||||||
|
|
||||||
|
@returns_json
|
||||||
|
def removeList(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
|
l = models.List.get(data['id'])
|
||||||
|
if l:
|
||||||
|
l.remove()
|
||||||
|
return {}
|
||||||
|
actions.register(removeList, cache=False)
|
||||||
|
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def addListItems(request):
|
def addListItems(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
list
|
||||||
|
items
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if data['list'] == ':':
|
if data['list'] == ':':
|
||||||
from item.models import Item
|
from item.models import Item
|
||||||
|
@ -153,6 +210,12 @@ actions.register(addListItems, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def removeListItems(request):
|
def removeListItems(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
list
|
||||||
|
items
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
l = models.List.get(data['list'])
|
l = models.List.get(data['list'])
|
||||||
if l:
|
if l:
|
||||||
|
@ -163,6 +226,11 @@ actions.register(removeListItems, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def sortLists(request):
|
def sortLists(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
ids
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
n = 0
|
n = 0
|
||||||
logger.debug('sortLists %s', data)
|
logger.debug('sortLists %s', data)
|
||||||
|
@ -177,6 +245,12 @@ actions.register(sortLists, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def editUser(request):
|
def editUser(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
nickname
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if 'nickname' in data:
|
if 'nickname' in data:
|
||||||
p = models.User.get_or_create(data['id'])
|
p = models.User.get_or_create(data['id'])
|
||||||
|
@ -187,6 +261,12 @@ actions.register(editUser, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def requestPeering(request):
|
def requestPeering(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if len(data.get('id', '')) != 43:
|
if len(data.get('id', '')) != 43:
|
||||||
logger.debug('invalid user id')
|
logger.debug('invalid user id')
|
||||||
|
@ -203,6 +283,12 @@ actions.register(requestPeering, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def acceptPeering(request):
|
def acceptPeering(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if len(data.get('id', '')) != 43:
|
if len(data.get('id', '')) != 43:
|
||||||
logger.debug('invalid user id')
|
logger.debug('invalid user id')
|
||||||
|
@ -218,6 +304,12 @@ actions.register(acceptPeering, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def rejectPeering(request):
|
def rejectPeering(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if len(data.get('id', '')) != 43:
|
if len(data.get('id', '')) != 43:
|
||||||
logger.debug('invalid user id')
|
logger.debug('invalid user id')
|
||||||
|
@ -232,6 +324,12 @@ actions.register(rejectPeering, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def removePeering(request):
|
def removePeering(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
id
|
||||||
|
message
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if len(data.get('id', '')) != 43:
|
if len(data.get('id', '')) != 43:
|
||||||
logger.debug('invalid user id')
|
logger.debug('invalid user id')
|
||||||
|
@ -246,6 +344,10 @@ actions.register(removePeering, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def cancelPeering(request):
|
def cancelPeering(request):
|
||||||
|
'''
|
||||||
|
takes {
|
||||||
|
}
|
||||||
|
'''
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
||||||
if len(data.get('id', '')) != 43:
|
if len(data.get('id', '')) != 43:
|
||||||
logger.debug('invalid user id')
|
logger.debug('invalid user id')
|
||||||
|
@ -260,29 +362,12 @@ actions.register(cancelPeering, cache=False)
|
||||||
|
|
||||||
@returns_json
|
@returns_json
|
||||||
def getActivity(request):
|
def getActivity(request):
|
||||||
|
'''
|
||||||
|
return {
|
||||||
|
activity
|
||||||
|
progress
|
||||||
|
}
|
||||||
|
'''
|
||||||
return state.activity
|
return state.activity
|
||||||
actions.register(getActivity, cache=False)
|
actions.register(getActivity, cache=False)
|
||||||
|
|
||||||
@returns_json
|
|
||||||
def selectFolder(request):
|
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
|
||||||
cmd = ['./ctl', 'ui', 'folder']
|
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
|
||||||
stdout, stderr = p.communicate()
|
|
||||||
path = stdout.decode('utf-8').strip()
|
|
||||||
return {
|
|
||||||
'path': path
|
|
||||||
}
|
|
||||||
actions.register(selectFolder, cache=False)
|
|
||||||
|
|
||||||
@returns_json
|
|
||||||
def selectFile(request):
|
|
||||||
data = json.loads(request.form['data']) if 'data' in request.form else {}
|
|
||||||
cmd = ['./ctl', 'ui', 'file']
|
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
|
||||||
stdout, stderr = p.communicate()
|
|
||||||
path = stdout.decode('utf-8').strip()
|
|
||||||
return {
|
|
||||||
'path': path
|
|
||||||
}
|
|
||||||
actions.register(selectFile, cache=False)
|
|
||||||
|
|
|
@ -66,7 +66,13 @@ class User(db.Model):
|
||||||
return state.nodes and state.nodes.check_online(self.id)
|
return state.nodes and state.nodes.check_online(self.id)
|
||||||
|
|
||||||
def lists_json(self):
|
def lists_json(self):
|
||||||
return [l.json() for l in self.lists.order_by('position')]
|
return [{
|
||||||
|
'id': '%s:' % ('' if self.id == settings.USER_ID else self.nickname),
|
||||||
|
'name': 'Library',
|
||||||
|
'type': 'library',
|
||||||
|
'items': self.items.count(),
|
||||||
|
'user': self.nickname if self.id != settings.USER_ID else settings.preferences['username'],
|
||||||
|
}] + [l.json() for l in self.lists.order_by('position')]
|
||||||
|
|
||||||
def update_peering(self, peered, username=None):
|
def update_peering(self, peered, username=None):
|
||||||
was_peering = self.peered
|
was_peering = self.peered
|
||||||
|
@ -132,15 +138,13 @@ class List(db.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, user_id, name=None):
|
def get(cls, user_id, name=None):
|
||||||
if not name:
|
if name is None:
|
||||||
user_id, name = cls.get_user_name(user_id)
|
user_id, name = cls.get_user_name(user_id)
|
||||||
return cls.query.filter_by(user_id=user_id, name=name).first()
|
return cls.query.filter_by(user_id=user_id, name=name).first()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_user_name(cls, user_id):
|
def get_user_name(cls, user_id):
|
||||||
l = user_id.split(':')
|
nickname, name = user_id.split(':', 1)
|
||||||
nickname = l[0]
|
|
||||||
name = ':'.join(l[1:])
|
|
||||||
if nickname:
|
if nickname:
|
||||||
user = User.query.filter_by(nickname=nickname).first()
|
user = User.query.filter_by(nickname=nickname).first()
|
||||||
user_id = user.id
|
user_id = user.id
|
||||||
|
@ -149,19 +153,22 @@ class List(db.Model):
|
||||||
return user_id, name
|
return user_id, name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_or_create(cls, user_id, name=None):
|
def get_or_create(cls, user_id, name=None, query=None):
|
||||||
if not name:
|
if name is None:
|
||||||
user_id, name = cls.get_user_name(user_id)
|
user_id, name = cls.get_user_name(user_id)
|
||||||
l = cls.get(user_id, name)
|
l = cls.get(user_id, name)
|
||||||
if not l:
|
if not l:
|
||||||
l = cls(name=name, user_id=user_id)
|
l = cls.create(user_id, name, query)
|
||||||
db.session.add(l)
|
|
||||||
db.session.commit()
|
|
||||||
return l
|
return l
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, user_id, name, query=None):
|
def create(cls, user_id, name, query=None):
|
||||||
l = cls(name=name, user_id=user_id)
|
prefix = name
|
||||||
|
n = 2
|
||||||
|
while cls.get(user_id, name):
|
||||||
|
name = '%s [%s]' % (prefix, n)
|
||||||
|
n += 1
|
||||||
|
l = cls(user_id=user_id, name=name)
|
||||||
l._query = query
|
l._query = query
|
||||||
l.type = 'smart' if l._query else 'static'
|
l.type = 'smart' if l._query else 'static'
|
||||||
l.position = cls.query.filter_by(user_id=user_id).count()
|
l.position = cls.query.filter_by(user_id=user_id).count()
|
||||||
|
|
40
oml/utils.py
40
oml/utils.py
|
@ -2,17 +2,22 @@
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
|
||||||
|
import os
|
||||||
import Image
|
import Image
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
import re
|
import re
|
||||||
import stdnum.isbn
|
import stdnum.isbn
|
||||||
import socket
|
import socket
|
||||||
|
import cStringIO
|
||||||
|
import gzip
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
import ed25519
|
import ed25519
|
||||||
|
|
||||||
from meta.utils import normalize_isbn, find_isbns
|
from meta.utils import normalize_isbn, find_isbns
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('oml.utils')
|
||||||
|
|
||||||
ENCODING='base64'
|
ENCODING='base64'
|
||||||
|
|
||||||
|
@ -108,3 +113,38 @@ def get_public_ipv6():
|
||||||
s.close()
|
s.close()
|
||||||
return ip
|
return ip
|
||||||
|
|
||||||
|
def update_dict(root, data):
|
||||||
|
for key in data:
|
||||||
|
keys = map(lambda part: part.replace('\0', '\\.'), key.replace('\\.', '\0').split('.'))
|
||||||
|
value = data[key]
|
||||||
|
p = root
|
||||||
|
while len(keys)>1:
|
||||||
|
key = keys.pop(0)
|
||||||
|
if isinstance(p, list):
|
||||||
|
p = p[get_position_by_id(p, key)]
|
||||||
|
else:
|
||||||
|
if key not in p:
|
||||||
|
p[key] = {}
|
||||||
|
p = p[key]
|
||||||
|
if value == None and keys[0] in p:
|
||||||
|
del p[keys[0]]
|
||||||
|
else:
|
||||||
|
p[keys[0]] = value
|
||||||
|
|
||||||
|
def remove_empty_folders(prefix):
|
||||||
|
empty = []
|
||||||
|
for root, folders, files in os.walk(prefix):
|
||||||
|
if not folders and not files:
|
||||||
|
empty.append(root)
|
||||||
|
for folder in empty:
|
||||||
|
remove_empty_tree(folder)
|
||||||
|
|
||||||
|
def remove_empty_tree(leaf):
|
||||||
|
while leaf:
|
||||||
|
if not os.path.exists(leaf):
|
||||||
|
leaf = os.path.dirname(leaf)
|
||||||
|
elif os.path.isdir(leaf) and not os.listdir(leaf):
|
||||||
|
logger.debug('rmdir %s', leaf)
|
||||||
|
os.rmdir(leaf)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
|
@ -45,7 +45,6 @@ oml.ui.appPanel = function() {
|
||||||
oml.$ui.importExportDialog
|
oml.$ui.importExportDialog
|
||||||
&& oml.$ui.importExportDialog.is(':visible')
|
&& oml.$ui.importExportDialog.is(':visible')
|
||||||
) {
|
) {
|
||||||
Ox.print('AAAAAAAA')
|
|
||||||
oml.$ui.importExportDialog.select(page);
|
oml.$ui.importExportDialog.select(page);
|
||||||
} else {
|
} else {
|
||||||
oml.$ui.importExportDialog = oml.ui.importExportDialog(page).open();
|
oml.$ui.importExportDialog = oml.ui.importExportDialog(page).open();
|
||||||
|
@ -56,6 +55,7 @@ oml.ui.appPanel = function() {
|
||||||
that.reload = function() {
|
that.reload = function() {
|
||||||
Ox.Request.cancel();
|
Ox.Request.cancel();
|
||||||
Ox.Request.clearCache();
|
Ox.Request.clearCache();
|
||||||
|
oml.unbindEvent();
|
||||||
oml.$ui.appPanel.remove();
|
oml.$ui.appPanel.remove();
|
||||||
oml.$ui.appPanel = oml.ui.appPanel().appendTo(Ox.$body);
|
oml.$ui.appPanel = oml.ui.appPanel().appendTo(Ox.$body);
|
||||||
return that;
|
return that;
|
||||||
|
|
|
@ -16,7 +16,10 @@ oml.ui.backButton = function() {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
oml.UI.set({item: ''});
|
oml.UI.set({
|
||||||
|
item: '',
|
||||||
|
itemView: 'info'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,10 @@ oml.ui.browser = function() {
|
||||||
unique: 'id'
|
unique: 'id'
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
open: function() {
|
open: function(data) {
|
||||||
|
if (that.value(data.ids[0], 'mediastate') == 'available') {
|
||||||
oml.UI.set({itemView: 'book'});
|
oml.UI.set({itemView: 'book'});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
oml.UI.set({
|
oml.UI.set({
|
||||||
|
|
|
@ -27,7 +27,6 @@ oml.ui.deleteItemsDialog = function() {
|
||||||
}, function() {
|
}, function() {
|
||||||
oml.UI.set({listSelection: []});
|
oml.UI.set({listSelection: []});
|
||||||
Ox.Request.clearCache('find');
|
Ox.Request.clearCache('find');
|
||||||
oml.$ui.folders.updateElement();
|
|
||||||
oml.$ui.list.updateElement();
|
oml.$ui.list.updateElement();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,7 +62,6 @@ oml.ui.filter = function(id) {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
Ox.print('UI FILTER STATE', ui._filterState)
|
|
||||||
// fixme: cant index be an empty array, instead of -1?
|
// fixme: cant index be an empty array, instead of -1?
|
||||||
// FIXME: this is still incorrect when deselecting a filter item
|
// FIXME: this is still incorrect when deselecting a filter item
|
||||||
// makes a selected item in another filter disappear
|
// makes a selected item in another filter disappear
|
||||||
|
|
|
@ -59,7 +59,6 @@ oml.ui.filtersOuterPanel = function() {
|
||||||
},
|
},
|
||||||
oml_find: function() {
|
oml_find: function() {
|
||||||
var previousUI = oml.UI.getPrevious();
|
var previousUI = oml.UI.getPrevious();
|
||||||
Ox.print('WTF', ui, oml.user.ui, Object.keys(ui), Object.keys(previousUI));
|
|
||||||
ui._filterState.forEach(function(data, index) {
|
ui._filterState.forEach(function(data, index) {
|
||||||
if (!Ox.isEqual(data.selected, previousUI._filterState[index].selected)) {
|
if (!Ox.isEqual(data.selected, previousUI._filterState[index].selected)) {
|
||||||
oml.$ui.filters[index].options(
|
oml.$ui.filters[index].options(
|
||||||
|
@ -72,7 +71,6 @@ oml.ui.filtersOuterPanel = function() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!Ox.isEqual(data.find, previousUI._filterState[index].find)) {
|
if (!Ox.isEqual(data.find, previousUI._filterState[index].find)) {
|
||||||
Ox.print('::::', index, 'UNEQUAL', data.find, previousUI._filterState[index].find)
|
|
||||||
if (!ui.showFilters) {
|
if (!ui.showFilters) {
|
||||||
oml.$ui.filters[index].options({
|
oml.$ui.filters[index].options({
|
||||||
_selected: data.selected
|
_selected: data.selected
|
||||||
|
|
|
@ -188,7 +188,6 @@ oml.ui.findElement = function() {
|
||||||
$select.superValue = $select.value;
|
$select.superValue = $select.value;
|
||||||
$select.value = function(value) {
|
$select.value = function(value) {
|
||||||
if (arguments.length == 1) {
|
if (arguments.length == 1) {
|
||||||
Ox.print('I AM HERE')
|
|
||||||
$select.options({title: value == 'all' ? 'data' : value});
|
$select.options({title: value == 'all' ? 'data' : value});
|
||||||
}
|
}
|
||||||
$select.superValue.apply($select, arguments);
|
$select.superValue.apply($select, arguments);
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
oml.ui.findForm = function(list) {
|
oml.ui.findForm = function(list) {
|
||||||
|
|
||||||
//Ox.print('FIND FORM LIST QUERY', list.query);
|
|
||||||
|
|
||||||
var ui = oml.user.ui,
|
var ui = oml.user.ui,
|
||||||
|
|
||||||
that = Ox.Element(),
|
that = Ox.Element(),
|
||||||
|
@ -19,7 +17,7 @@ oml.ui.findForm = function(list) {
|
||||||
title: Ox._('List'),
|
title: Ox._('List'),
|
||||||
type: 'item',
|
type: 'item',
|
||||||
values: ui._lists.filter(function(list) {
|
values: ui._lists.filter(function(list) {
|
||||||
return list.type != 'smart'
|
return Ox.contains(['library', 'static'], list.type);
|
||||||
}).map(function(list) {
|
}).map(function(list) {
|
||||||
return {id: list.id, title: list.title};
|
return {id: list.id, title: list.title};
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
oml.ui.folders = function() {
|
oml.ui.folders = function() {
|
||||||
|
|
||||||
var ui = oml.user.ui,
|
var ui = oml.user.ui,
|
||||||
|
username = oml.user.preferences.username,
|
||||||
|
|
||||||
userIndex,
|
userIndex,
|
||||||
|
users,
|
||||||
|
|
||||||
$lists,
|
$lists,
|
||||||
|
|
||||||
|
@ -28,6 +30,24 @@ oml.ui.folders = function() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFolderList(list) {
|
||||||
|
var index = users.map(function(user) {
|
||||||
|
return user.nickname;
|
||||||
|
}).indexOf(list.user);
|
||||||
|
return list.id == '' ? oml.$ui.librariesList
|
||||||
|
: Ox.endsWith(list.id, ':') ? oml.$ui.libraryList[index]
|
||||||
|
: oml.$ui.folderList[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsersAndLists(callback) {
|
||||||
|
oml.getUsers(function() {
|
||||||
|
users = arguments[0];
|
||||||
|
oml.getLists(function(lists) {
|
||||||
|
callback(users, lists);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function selectList() {
|
function selectList() {
|
||||||
var split = ui._list.split(':'),
|
var split = ui._list.split(':'),
|
||||||
index = userIndex[split[0] || oml.user.preferences.username],
|
index = userIndex[split[0] || oml.user.preferences.username],
|
||||||
|
@ -50,6 +70,15 @@ oml.ui.folders = function() {
|
||||||
|
|
||||||
$lists = [];
|
$lists = [];
|
||||||
|
|
||||||
|
oml.$ui.folder = [];
|
||||||
|
oml.$ui.libraryList = [];
|
||||||
|
oml.$ui.folderList = [];
|
||||||
|
|
||||||
|
getUsersAndLists(function(users, lists) {
|
||||||
|
|
||||||
|
Ox.print('GOT USERS AND LISTS', users, lists);
|
||||||
|
userIndex = {};
|
||||||
|
|
||||||
$lists.push(
|
$lists.push(
|
||||||
oml.$ui.librariesList = oml.ui.folderList({
|
oml.$ui.librariesList = oml.ui.folderList({
|
||||||
items: [
|
items: [
|
||||||
|
@ -57,16 +86,11 @@ oml.ui.folders = function() {
|
||||||
id: '',
|
id: '',
|
||||||
name: Ox._('All Libraries'),
|
name: Ox._('All Libraries'),
|
||||||
type: 'libraries',
|
type: 'libraries',
|
||||||
items: -1
|
items: Ox.getObjectById(lists, '').items
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
load: function() {
|
|
||||||
oml.api.find({query: getFind('')}, function(result) {
|
|
||||||
oml.$ui.librariesList.value('', 'items', result.data.items);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
select: function() {
|
select: function() {
|
||||||
oml.UI.set({find: getFind('')});
|
oml.UI.set({find: getFind('')});
|
||||||
oml.$ui.librariesList.options({selected: ['']});
|
oml.$ui.librariesList.options({selected: ['']});
|
||||||
|
@ -74,7 +98,7 @@ oml.ui.folders = function() {
|
||||||
selectnext: function() {
|
selectnext: function() {
|
||||||
oml.UI.set(Ox.extend(
|
oml.UI.set(Ox.extend(
|
||||||
{find: getFind(':')},
|
{find: getFind(':')},
|
||||||
'showFolder.' + oml.user.preferences.username,
|
'showFolder.' + username,
|
||||||
true
|
true
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
@ -84,15 +108,6 @@ oml.ui.folders = function() {
|
||||||
);
|
);
|
||||||
oml.$ui.librariesList.$body.css({height: '16px'}); // FIXME!
|
oml.$ui.librariesList.$body.css({height: '16px'}); // FIXME!
|
||||||
|
|
||||||
oml.$ui.folder = [];
|
|
||||||
oml.$ui.libraryList = [];
|
|
||||||
oml.$ui.folderList = [];
|
|
||||||
|
|
||||||
oml.getUsersAndLists(function(users, lists) {
|
|
||||||
|
|
||||||
Ox.print('GOT USERS AND LISTS', users, lists);
|
|
||||||
userIndex = {};
|
|
||||||
|
|
||||||
users.forEach(function(user, index) {
|
users.forEach(function(user, index) {
|
||||||
|
|
||||||
var $content,
|
var $content,
|
||||||
|
@ -107,11 +122,7 @@ oml.ui.folders = function() {
|
||||||
oml.$ui.folder[index] = Ox.CollapsePanel({
|
oml.$ui.folder[index] = Ox.CollapsePanel({
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
extras: [
|
extras: [
|
||||||
oml.ui.statusIcon(
|
oml.ui.statusIcon(user, index),
|
||||||
!oml.user.online && index ? 'unknown'
|
|
||||||
: user.online ? 'connected'
|
|
||||||
: 'disconnected'
|
|
||||||
),
|
|
||||||
{},
|
{},
|
||||||
Ox.Button({
|
Ox.Button({
|
||||||
style: 'symbol',
|
style: 'symbol',
|
||||||
|
@ -162,7 +173,7 @@ oml.ui.folders = function() {
|
||||||
id: libraryId,
|
id: libraryId,
|
||||||
name: Ox._('Library'),
|
name: Ox._('Library'),
|
||||||
type: 'library',
|
type: 'library',
|
||||||
items: -1
|
items: Ox.getObjectById(lists, libraryId).items
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -170,15 +181,6 @@ oml.ui.folders = function() {
|
||||||
add: function() {
|
add: function() {
|
||||||
!index && oml.addList();
|
!index && oml.addList();
|
||||||
},
|
},
|
||||||
load: function() {
|
|
||||||
oml.api.find({
|
|
||||||
query: getFind(libraryId)
|
|
||||||
}, function(result) {
|
|
||||||
oml.$ui.libraryList[index].value(
|
|
||||||
libraryId, 'items', result.data.items
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
oml.UI.set({find: getFind(data.ids[0])});
|
oml.UI.set({find: getFind(data.ids[0])});
|
||||||
},
|
},
|
||||||
|
@ -186,16 +188,20 @@ oml.ui.folders = function() {
|
||||||
oml.UI.set({find: getFind(items[0].id)});
|
oml.UI.set({find: getFind(items[0].id)});
|
||||||
},
|
},
|
||||||
selectprevious: function() {
|
selectprevious: function() {
|
||||||
var userId = !index ? null : users[index - 1].id,
|
// FIXME: ugly
|
||||||
set = {
|
var set, user, userLists;
|
||||||
find: getFind(
|
if (!index) {
|
||||||
!index
|
set = {find: getFind('')};
|
||||||
? ''
|
} else {
|
||||||
: Ox.last(lists[userId]).id
|
user = users[index - 1].nickname;
|
||||||
)
|
userLists = lists.filter(function(list) {
|
||||||
};
|
return list.user == user;
|
||||||
if (userId) {
|
});
|
||||||
Ox.extend(set, 'showFolder.' + userId, true);
|
set = {find: getFind(
|
||||||
|
!userLists.length ? (user == oml.user.preferences.username ? '' : user) + ':'
|
||||||
|
: Ox.last(userLists).id
|
||||||
|
)};
|
||||||
|
Ox.extend(set, 'showFolder.' + user, true);
|
||||||
}
|
}
|
||||||
oml.UI.set(set);
|
oml.UI.set(set);
|
||||||
}
|
}
|
||||||
|
@ -219,14 +225,6 @@ oml.ui.folders = function() {
|
||||||
key_control_d: function() {
|
key_control_d: function() {
|
||||||
oml.addList(ui._list);
|
oml.addList(ui._list);
|
||||||
},
|
},
|
||||||
load: function() {
|
|
||||||
// FIXME: too much
|
|
||||||
items.forEach(function(item) {
|
|
||||||
oml.api.find({query: getFind(item.id)}, function(result) {
|
|
||||||
oml.$ui.folderList[index].value(item.id, 'items', result.data.items);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
move: function(data) {
|
move: function(data) {
|
||||||
lists[user.id] = data.ids.map(function(listId) {
|
lists[user.id] = data.ids.map(function(listId) {
|
||||||
return Ox.getObjectById(items, listId);
|
return Ox.getObjectById(items, listId);
|
||||||
|
@ -273,32 +271,53 @@ oml.ui.folders = function() {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.updateItems = function() {
|
that.updateItems = function(items) {
|
||||||
oml.getUsersAndLists(function(users, lists) {
|
Ox.print('UPDATE ITEMS', items);
|
||||||
Ox.Request.clearCache('find');
|
var $list;
|
||||||
$lists.forEach(function($list) {
|
if (arguments.length == 0) {
|
||||||
$list.reloadList();
|
oml.getLists(function(lists) {
|
||||||
|
lists.forEach(function(list) {
|
||||||
|
$list = getFolderList(list);
|
||||||
|
if ($list) {
|
||||||
|
$list.value(list.id, 'items', list.items);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
$list = $lists.filter(function($list) {
|
||||||
|
return $list.options('selected').length;
|
||||||
|
})[0];
|
||||||
|
if ($list && !Ox.isEmpty($list.value(ui._list))) {
|
||||||
|
$list.value(ui._list, 'items', items);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
that.updateOwnLists = function(callback) {
|
that.updateOwnLists = function(callback) {
|
||||||
oml.getUsersAndLists(function(users, lists) {
|
oml.getLists(function(lists) {
|
||||||
var items = lists.filter(function(list) {
|
var items = lists.filter(function(list) {
|
||||||
return list.user == oml.user.preferences.username
|
return list.user == oml.user.preferences.username
|
||||||
&& list.type != 'library';
|
&& list.type != 'library';
|
||||||
});
|
});
|
||||||
|
oml.$ui.folder[0].$content
|
||||||
|
.css({height: 16 + items.length * 16 + 'px'});
|
||||||
oml.$ui.folderList[0].options({
|
oml.$ui.folderList[0].options({
|
||||||
items: items
|
items: items
|
||||||
})
|
})
|
||||||
.css({height: items.length * 16 + 'px'})
|
.css({height: items.length * 16 + 'px'})
|
||||||
.size();
|
.size();
|
||||||
oml.$ui.folder[0].$content
|
|
||||||
.css({height: 16 + items.length * 16 + 'px'});
|
|
||||||
callback && callback();
|
callback && callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oml.bindEvent({
|
||||||
|
activity: function(data) {
|
||||||
|
if (data.activity == 'import') {
|
||||||
|
that.updateItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return that.updateElement();
|
return that.updateElement();
|
||||||
|
|
||||||
};
|
};
|
|
@ -34,6 +34,8 @@ oml.ui.identifyDialog = function(data) {
|
||||||
|
|
||||||
originalData = Ox.clone(data, true),
|
originalData = Ox.clone(data, true),
|
||||||
|
|
||||||
|
selected = data.mainid ? 'id' : 'title',
|
||||||
|
|
||||||
idValue, titleValue,
|
idValue, titleValue,
|
||||||
|
|
||||||
$idInputs, $idButtons = {},
|
$idInputs, $idButtons = {},
|
||||||
|
@ -59,7 +61,7 @@ oml.ui.identifyDialog = function(data) {
|
||||||
$titlePanel = Ox.SplitPanel({
|
$titlePanel = Ox.SplitPanel({
|
||||||
elements: [
|
elements: [
|
||||||
{element: $titleForm, size: 96},
|
{element: $titleForm, size: 96},
|
||||||
{element: renderResults([])}
|
{element: Ox.Element()}
|
||||||
],
|
],
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
}),
|
}),
|
||||||
|
@ -72,7 +74,7 @@ oml.ui.identifyDialog = function(data) {
|
||||||
{id: 'title', title: Ox._('Find by Title')}
|
{id: 'title', title: Ox._('Find by Title')}
|
||||||
],
|
],
|
||||||
selectable: true,
|
selectable: true,
|
||||||
selected: 'id'
|
value: selected
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
width: '768px',
|
width: '768px',
|
||||||
|
@ -81,7 +83,8 @@ oml.ui.identifyDialog = function(data) {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
$innerPanel.options({selected: data.value});
|
selected = data.value;
|
||||||
|
$innerPanel.options({selected: selected});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.appendTo($bar),
|
.appendTo($bar),
|
||||||
|
@ -92,7 +95,7 @@ oml.ui.identifyDialog = function(data) {
|
||||||
{id: 'title', element: $titlePanel}
|
{id: 'title', element: $titlePanel}
|
||||||
],
|
],
|
||||||
orientation: 'horizontal',
|
orientation: 'horizontal',
|
||||||
selected: 'id',
|
selected: selected,
|
||||||
size: 768
|
size: 768
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -122,16 +125,16 @@ oml.ui.identifyDialog = function(data) {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
|
Ox.print('$$$', idValue);
|
||||||
var edit = Ox.extend(
|
var edit = Ox.extend(
|
||||||
{id: data.id},
|
{id: data.id},
|
||||||
$innerPanel.options('selected') == 'id'
|
$innerPanel.options('selected') == 'id'
|
||||||
? idValue
|
? idValue || {mainid: ''}
|
||||||
: titleValue
|
: titleValue
|
||||||
);
|
);
|
||||||
that.options({content: Ox.LoadingScreen().start()});
|
that.options({content: Ox.LoadingScreen().start()});
|
||||||
that.disableButtons();
|
that.disableButtons();
|
||||||
oml.api.edit(edit, function(result) {
|
oml.api.edit(edit, function(result) {
|
||||||
Ox.print('EDITED', result.data);
|
|
||||||
that.close();
|
that.close();
|
||||||
Ox.Request.clearCache('find');
|
Ox.Request.clearCache('find');
|
||||||
oml.$ui.browser.reloadList(true);
|
oml.$ui.browser.reloadList(true);
|
||||||
|
@ -145,28 +148,36 @@ oml.ui.identifyDialog = function(data) {
|
||||||
content: $outerPanel,
|
content: $outerPanel,
|
||||||
fixedSize: true,
|
fixedSize: true,
|
||||||
height: 384,
|
height: 384,
|
||||||
|
removeOnClose: true,
|
||||||
title: Ox._('Identify Book'),
|
title: Ox._('Identify Book'),
|
||||||
width: 768
|
width: 768
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function disableButtons() {
|
||||||
|
Ox.forEach(selected == 'id' ? $idButtons : $titleButtons, function($button) {
|
||||||
|
$button.options({disabled: true});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function findMetadata(data) {
|
function findMetadata(data) {
|
||||||
|
disableButtons();
|
||||||
$titlePanel.replaceElement(1, Ox.LoadingScreen().start());
|
$titlePanel.replaceElement(1, Ox.LoadingScreen().start());
|
||||||
oml.api.findMetadata(data, function(result) {
|
oml.api.findMetadata(data, function(result) {
|
||||||
Ox.print('GOT RESULTS', result.data);
|
|
||||||
// FIXME: CONCAT HERE
|
// FIXME: CONCAT HERE
|
||||||
var items = result.data.items.map(function(item, index) {
|
var items = result.data.items.map(function(item, index) {
|
||||||
return Ox.extend({index: (index + 1).toString()}, item);
|
return Ox.extend({index: (index + 1).toString()}, item);
|
||||||
});
|
});
|
||||||
|
updateTitleButtons();
|
||||||
$titlePanel.replaceElement(1, renderResults(items));
|
$titlePanel.replaceElement(1, renderResults(items));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMetadata(key, value) {
|
function getMetadata(key, value) {
|
||||||
|
disableButtons();
|
||||||
$idPanel.replaceElement(1, Ox.LoadingScreen().start());
|
$idPanel.replaceElement(1, Ox.LoadingScreen().start());
|
||||||
oml.api.getMetadata(Ox.extend({}, key, value), function(result) {
|
oml.api.getMetadata(Ox.extend({}, key, value), function(result) {
|
||||||
Ox.print('GOT RESULT', result.data);
|
|
||||||
$idForm = renderIdForm(result.data);
|
$idForm = renderIdForm(result.data);
|
||||||
$idPreview = oml.ui.infoView(result.data);
|
$idPreview = Ox.isEmpty(data) ? Ox.Element() : oml.ui.infoView(result.data);
|
||||||
$idPanel
|
$idPanel
|
||||||
.replaceElement(0, $idForm)
|
.replaceElement(0, $idForm)
|
||||||
.replaceElement(1, $idPreview);
|
.replaceElement(1, $idPreview);
|
||||||
|
@ -174,7 +185,6 @@ oml.ui.identifyDialog = function(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function idInputValues(key, values) {
|
function idInputValues(key, values) {
|
||||||
Ox.print('WTF,', $idInputs);
|
|
||||||
var $input = $idInputs[ids.map(function(id) {
|
var $input = $idInputs[ids.map(function(id) {
|
||||||
return id.id;
|
return id.id;
|
||||||
}).indexOf(key)];
|
}).indexOf(key)];
|
||||||
|
@ -190,30 +200,13 @@ oml.ui.identifyDialog = function(data) {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
function titleInputValue(key, value) {
|
|
||||||
var $input = $titleInputs[keys.map(function(key) {
|
|
||||||
return key.id;
|
|
||||||
}).indexOf(key)];
|
|
||||||
if (Ox.isUndefined(value)) {
|
|
||||||
value = $input.value();
|
|
||||||
if (key == 'author') {
|
|
||||||
value = value ? value.split(', ') : [];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$input.value(
|
|
||||||
key == 'author' ? (value || []).join(', ') : value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmpty(data) {
|
function isEmpty(data) {
|
||||||
return Ox.every(data, Ox.isEmpty);
|
return Ox.every(data, Ox.isEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOriginal(data) {
|
function isOriginal(data) {
|
||||||
return Ox.every(data, function(value, key) {
|
return Ox.every(Object.keys(data), function(key) {
|
||||||
return value == originalData[key];
|
return data[key] == originalData[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +261,7 @@ oml.ui.identifyDialog = function(data) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
getMetadata(id.id, data.value, function() {
|
getMetadata(id.id, data.value, function() {
|
||||||
// ...
|
Ox.print('GOT METADATA');
|
||||||
updateIdButtons();
|
updateIdButtons();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -335,15 +328,27 @@ oml.ui.identifyDialog = function(data) {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
|
var key, value;
|
||||||
|
Ox.forEach(ids, function(id) {
|
||||||
|
var values = idInputValues(id.id);
|
||||||
|
if (values[0]) {
|
||||||
|
key = id.id;
|
||||||
|
value = values[1];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getMetadata(key, value, function() {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
Ox.print('NOT IMPLEMENTED')
|
Ox.print('NOT IMPLEMENTED')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.appendTo($element);
|
.appendTo($element);
|
||||||
|
updateIdButtons();
|
||||||
return $element;
|
return $element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderResults(items) {
|
function renderResults(items) {
|
||||||
Ox.print('LIST ITEMS::::', items);
|
|
||||||
var $list = Ox.TableList({
|
var $list = Ox.TableList({
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
|
@ -375,7 +380,6 @@ oml.ui.identifyDialog = function(data) {
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
var index = data.ids[0], mainid;
|
var index = data.ids[0], mainid;
|
||||||
mainid = $list.value(index, 'mainid');
|
mainid = $list.value(index, 'mainid');
|
||||||
Ox.print('MAINID', mainid)
|
|
||||||
titleValue = Ox.extend({}, mainid, $list.value(index, mainid));
|
titleValue = Ox.extend({}, mainid, $list.value(index, mainid));
|
||||||
$results.replaceElement(1, Ox.LoadingScreen().start());
|
$results.replaceElement(1, Ox.LoadingScreen().start());
|
||||||
oml.api.getMetadata(titleValue, function(result) {
|
oml.api.getMetadata(titleValue, function(result) {
|
||||||
|
@ -476,6 +480,23 @@ oml.ui.identifyDialog = function(data) {
|
||||||
return $element;
|
return $element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function titleInputValue(key, value) {
|
||||||
|
var $input = $titleInputs[keys.map(function(key) {
|
||||||
|
return key.id;
|
||||||
|
}).indexOf(key)];
|
||||||
|
if (Ox.isUndefined(value)) {
|
||||||
|
value = $input.value();
|
||||||
|
if (key == 'author') {
|
||||||
|
value = value ? value.split(', ') : [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$input.value(
|
||||||
|
key == 'author' ? (value || []).join(', ') : value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
function updateIdButtons() {
|
function updateIdButtons() {
|
||||||
var data = {}, empty, original;
|
var data = {}, empty, original;
|
||||||
ids.forEach(function(id) {
|
ids.forEach(function(id) {
|
||||||
|
@ -486,7 +507,7 @@ oml.ui.identifyDialog = function(data) {
|
||||||
$idButtons.clear.options({disabled: empty});
|
$idButtons.clear.options({disabled: empty});
|
||||||
$idButtons.reset.options({disabled: original});
|
$idButtons.reset.options({disabled: original});
|
||||||
$idButtons.find.options({disabled: empty});
|
$idButtons.find.options({disabled: empty});
|
||||||
that[original ? 'disableButton' : 'enableButton']('update');
|
that && that[original ? 'disableButton' : 'enableButton']('update');
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTitleButtons() {
|
function updateTitleButtons() {
|
||||||
|
|
|
@ -50,8 +50,8 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
that = Ox.Dialog({
|
that = Ox.Dialog({
|
||||||
buttons: [
|
buttons: [
|
||||||
Ox.Button({
|
Ox.Button({
|
||||||
id: 'close',
|
id: 'hide',
|
||||||
title: Ox._('Close')
|
title: Ox._('Hide')
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
|
@ -87,7 +87,7 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
$status = {},
|
$status = {},
|
||||||
$progressButton = {};
|
$progressButton = {};
|
||||||
|
|
||||||
oml.getUsersAndLists(function() {
|
oml.getLists(function() {
|
||||||
oml.api.getActivity(function(result) {
|
oml.api.getActivity(function(result) {
|
||||||
var isActive = !Ox.isEmpty(result.data),
|
var isActive = !Ox.isEmpty(result.data),
|
||||||
activity = result.data.activity;
|
activity = result.data.activity;
|
||||||
|
@ -98,7 +98,6 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
progress: [0,42]
|
progress: [0,42]
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
Ox.print(result.data, '!!!!!!!!')
|
|
||||||
$innerPanel
|
$innerPanel
|
||||||
.replaceElement(0,
|
.replaceElement(0,
|
||||||
activity == 'import' ? renderActivity(result.data)
|
activity == 'import' ? renderActivity(result.data)
|
||||||
|
@ -208,6 +207,12 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
$form = Ox.Form({
|
$form = Ox.Form({
|
||||||
items: selected == 'import' ? [
|
items: selected == 'import' ? [
|
||||||
Ox.Input({
|
Ox.Input({
|
||||||
|
autocomplete: function(value, callback) {
|
||||||
|
oml.api.autocompleteFolder({path: value}, function(result) {
|
||||||
|
callback(result.data.items);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocompleteSelect: true,
|
||||||
changeOnKeypress: true,
|
changeOnKeypress: true,
|
||||||
id: 'path',
|
id: 'path',
|
||||||
label: 'Source Path',
|
label: 'Source Path',
|
||||||
|
@ -216,7 +221,7 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
}),
|
}),
|
||||||
Ox.SelectInput({
|
Ox.SelectInput({
|
||||||
id: 'list',
|
id: 'list',
|
||||||
inputValue: oml.validateName(Ox._('Untitled'), getListNames()),
|
inputValue: oml.getValidName(Ox._('Untitled'), getListNames()),
|
||||||
inputWidth: 224,
|
inputWidth: 224,
|
||||||
items: getListItems('import'),
|
items: getListItems('import'),
|
||||||
label: 'Destination',
|
label: 'Destination',
|
||||||
|
@ -229,8 +234,8 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
Ox.Select({
|
Ox.Select({
|
||||||
id: 'mode',
|
id: 'mode',
|
||||||
items: [
|
items: [
|
||||||
{id: 'copy', title: Ox._('Copy files')},
|
{id: 'copy', title: Ox._('Copy (keep files in source path)')},
|
||||||
{id: 'move', title: Ox._('Move files')}
|
{id: 'move', title: Ox._('Move (delete files from source path)')}
|
||||||
],
|
],
|
||||||
label: Ox._('Import Mode'),
|
label: Ox._('Import Mode'),
|
||||||
labelWidth: 128,
|
labelWidth: 128,
|
||||||
|
@ -255,10 +260,10 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
Ox.Select({
|
Ox.Select({
|
||||||
id: 'mode',
|
id: 'mode',
|
||||||
items: [
|
items: [
|
||||||
{id: 'keep', title: Ox._('Keep existing files')},
|
{id: 'add', title: Ox._('Add (keep files in destination path)')},
|
||||||
{id: 'remove', title: Ox._('Remove existing files')}
|
{id: 'replace', title: Ox._('Replace (delete files from destination path)')}
|
||||||
],
|
],
|
||||||
label: Ox._('Import Mode'),
|
label: Ox._('Export Mode'),
|
||||||
labelWidth: 128,
|
labelWidth: 128,
|
||||||
width: 480
|
width: 480
|
||||||
})
|
})
|
||||||
|
@ -268,15 +273,8 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
var values = $form.values();
|
var values = $form.values();
|
||||||
Ox.print('FORM CHANGE', data);
|
|
||||||
if (data.id == 'list') {
|
|
||||||
// FIXME: WRONG
|
|
||||||
if (data.data.value[0] != '') {
|
|
||||||
$form.values('list', oml.validateName(data.data.value, getListNames()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$activityButton[selected].options({
|
$activityButton[selected].options({
|
||||||
disabled: !values.path //|| !values.list
|
disabled: !values.path
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -310,7 +308,11 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
var data = $form.values();
|
var data = $form.values(),
|
||||||
|
addList = data.list && !Ox.contains(
|
||||||
|
oml.getOwnListNames(),
|
||||||
|
data.list
|
||||||
|
);
|
||||||
$innerPanel.replaceElement(0,
|
$innerPanel.replaceElement(0,
|
||||||
renderActivity({
|
renderActivity({
|
||||||
activity: 'import',
|
activity: 'import',
|
||||||
|
@ -319,13 +321,15 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
$label['export'].show();
|
$label['export'].show();
|
||||||
|
(addList ? oml.addList : Ox.noop)(false, false, data.list, function() {
|
||||||
oml.api.import({
|
oml.api.import({
|
||||||
list: data.list, // FIXME: WRONG for Library
|
list: data.list,
|
||||||
mode: data.mode,
|
mode: data.mode,
|
||||||
path: data.path,
|
path: data.path,
|
||||||
}, function() {
|
}, function() {
|
||||||
// ...
|
// ...
|
||||||
})
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.appendTo($element);
|
.appendTo($element);
|
||||||
|
@ -341,7 +345,6 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setProgress(data) {
|
function setProgress(data) {
|
||||||
Ox.print('SET PROGRESS', data, $progress)
|
|
||||||
var progress = data.status ? 1
|
var progress = data.status ? 1
|
||||||
: !data.progress[0] || !data.progress[1] ? -1
|
: !data.progress[0] || !data.progress[1] ? -1
|
||||||
: data.progress[0] / data.progress[1];
|
: data.progress[0] / data.progress[1];
|
||||||
|
@ -379,7 +382,6 @@ oml.ui.importExportDialog = function(selected) {
|
||||||
|
|
||||||
oml.bindEvent({
|
oml.bindEvent({
|
||||||
activity: function(data) {
|
activity: function(data) {
|
||||||
Ox.print('activity', arguments);
|
|
||||||
setProgress(data);
|
setProgress(data);
|
||||||
setStatus(data);
|
setStatus(data);
|
||||||
setButton(data);
|
setButton(data);
|
||||||
|
|
|
@ -321,6 +321,11 @@ oml.ui.infoView = function(identifyData) {
|
||||||
tooltip: isEditable ? oml.getEditTooltip() : '',
|
tooltip: isEditable ? oml.getEditTooltip() : '',
|
||||||
value: data.title || ''
|
value: data.title || ''
|
||||||
})
|
})
|
||||||
|
.bindEvent({
|
||||||
|
submit: function(data) {
|
||||||
|
editMetadata('title', data.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
.css({
|
.css({
|
||||||
fontSize: '13px',
|
fontSize: '13px',
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
|
@ -353,7 +358,9 @@ oml.ui.infoView = function(identifyData) {
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
// ...
|
submit: function(data) {
|
||||||
|
editMetadata('author', value.split(', '));
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.appendTo($info);
|
.appendTo($info);
|
||||||
|
@ -502,12 +509,13 @@ oml.ui.infoView = function(identifyData) {
|
||||||
// FIXME: identify dialog should call this too
|
// FIXME: identify dialog should call this too
|
||||||
function editMetadata(key, value) {
|
function editMetadata(key, value) {
|
||||||
var edit;
|
var edit;
|
||||||
|
Ox.print('EM', key, value, data[key])
|
||||||
if (value != data[key]) {
|
if (value != data[key]) {
|
||||||
edit = Ox.extend({id: ui.item}, key, value);
|
edit = Ox.extend({id: ui.item}, key, value);
|
||||||
oml.api.edit(edit, function(result) {
|
oml.api.edit(edit, function(result) {
|
||||||
Ox.Request.clearCache('find');
|
Ox.Request.clearCache('find');
|
||||||
oml.$ui.browser.reloadList();
|
oml.$ui.browser.reloadList();
|
||||||
that.updateElement(result.data, $data);
|
//that.updateElement(result.data, $info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,14 @@ oml.ui.list = function() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
init: function(data) {
|
init: function(data) {
|
||||||
|
Ox.print('MAIN LIST INIT', data);
|
||||||
|
if (ui.find.conditions.length == 0 || (
|
||||||
|
ui.find.conditions.length == 1
|
||||||
|
&& ui.find.conditions[0].key == 'list'
|
||||||
|
&& ui.find.conditions[0].operator == '=='
|
||||||
|
)) {
|
||||||
|
oml.$ui.folders.updateItems(data.items);
|
||||||
|
}
|
||||||
oml.$ui.statusbar.set('total', data);
|
oml.$ui.statusbar.set('total', data);
|
||||||
},
|
},
|
||||||
key_control_delete: function() {
|
key_control_delete: function() {
|
||||||
|
@ -58,6 +66,15 @@ oml.ui.list = function() {
|
||||||
oml.ui.deleteItemsDialog().open();
|
oml.ui.deleteItemsDialog().open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
key_shift_enter: function() {
|
||||||
|
var selected = that.options('selected');
|
||||||
|
if (selected.length) {
|
||||||
|
oml.UI.set({
|
||||||
|
item: selected[0],
|
||||||
|
itemView: 'book'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
open: function(data) {
|
open: function(data) {
|
||||||
oml.UI.set({
|
oml.UI.set({
|
||||||
item: data.ids[0],
|
item: data.ids[0],
|
||||||
|
|
|
@ -65,14 +65,18 @@ oml.ui.listDialog = function() {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
var value = oml.validateName(data.value, listNames);
|
var value = oml.getValidName(
|
||||||
|
data.value || Ox._('Untitled'),
|
||||||
|
listNames.filter(function(listName) {
|
||||||
|
return listName != listData.name;
|
||||||
|
})
|
||||||
|
);
|
||||||
that.options({title: getTitle(':' + value)})
|
that.options({title: getTitle(':' + value)})
|
||||||
$nameInput.value(value);
|
$nameInput.value(value);
|
||||||
// FIXME: UGLY
|
// FIXME: UGLY
|
||||||
listNames[listNames.indexOf(listData.name)] = value;
|
listNames[listNames.indexOf(listData.name)] = value;
|
||||||
listData.name = value;
|
listData.name = value;
|
||||||
//
|
//
|
||||||
Ox.print(listData.name, 'LIST NAMES ???', listNames)
|
|
||||||
oml.api.editList({
|
oml.api.editList({
|
||||||
id: ui._list,
|
id: ui._list,
|
||||||
name: value
|
name: value
|
||||||
|
@ -94,13 +98,13 @@ oml.ui.listDialog = function() {
|
||||||
})
|
})
|
||||||
.appendTo($content),
|
.appendTo($content),
|
||||||
$findForm;
|
$findForm;
|
||||||
Ox.print('DEBUG:', list, listData)
|
|
||||||
if (listData.type == 'smart') {
|
if (listData.type == 'smart') {
|
||||||
$findForm = oml.ui.findForm(listData)
|
$findForm = oml.ui.findForm(listData)
|
||||||
.css({marginTop: '8px'})
|
.css({marginTop: '8px'})
|
||||||
.appendTo($content);
|
.appendTo($content);
|
||||||
}
|
}
|
||||||
that.options({content: $content});
|
that.options({content: $content});
|
||||||
|
$nameInput.focusInput(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getTitle(list) {
|
function getTitle(list) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ oml.ui.openButton = function() {
|
||||||
oml.UI.set({item: ui.listSelection[0]});
|
oml.UI.set({item: ui.listSelection[0]});
|
||||||
},
|
},
|
||||||
oml_listselection: function() {
|
oml_listselection: function() {
|
||||||
that.update();
|
that.updateElement();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ oml.ui.preferencesDialog = function() {
|
||||||
title: 'Username',
|
title: 'Username',
|
||||||
value: preferences.username,
|
value: preferences.username,
|
||||||
placeholder: 'anonymous',
|
placeholder: 'anonymous',
|
||||||
help: 'Your username doesn\'t have to be your real name, and you can change it at any time. You can also leave it blank, in which case you will appear as "anonymous". Any characters other than leading, trailing or consecutive spaces are okay.'
|
help: 'Your username doesn\'t have to be your real name, and you can change it at any time. You can also leave it blank, in which case you will appear as "anonymous". Any characters other than colons and leading, trailing or consecutive spaces are okay.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'contact',
|
id: 'contact',
|
||||||
|
@ -25,12 +25,24 @@ oml.ui.preferencesDialog = function() {
|
||||||
{
|
{
|
||||||
id: 'libraryPath',
|
id: 'libraryPath',
|
||||||
title: 'Library Path',
|
title: 'Library Path',
|
||||||
|
autocomplete: function(value, callback) {
|
||||||
|
oml.api.autocompleteFolder({path: value}, function(result) {
|
||||||
|
callback(result.data.items);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocompleteSelect: true,
|
||||||
value: preferences.libraryPath,
|
value: preferences.libraryPath,
|
||||||
help: 'The directory in which your "Books" folder is located. This is where your media files are stored. It can be on your local machine, but just as well on an external drive or networked volume.'
|
help: 'The directory in which your "Books" folder is located. This is where your media files are stored. It can be on your local machine, but just as well on an external drive or networked volume.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'importPath',
|
id: 'importPath',
|
||||||
title: 'Import Path',
|
title: 'Import Path',
|
||||||
|
autocomplete: function(value, callback) {
|
||||||
|
oml.api.autocompleteFolder({path: value}, function(result) {
|
||||||
|
callback(result.data.items);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocompleteSelect: true,
|
||||||
value: preferences.importPath,
|
value: preferences.importPath,
|
||||||
help: 'Any media files that you put in this folder will be added to your library. Once added, they will be removed from this folder.'
|
help: 'Any media files that you put in this folder will be added to your library. Once added, they will be removed from this folder.'
|
||||||
}
|
}
|
||||||
|
@ -376,6 +388,8 @@ oml.ui.preferencesDialog = function() {
|
||||||
width: 384
|
width: 384
|
||||||
})
|
})
|
||||||
: Ox.Input({
|
: Ox.Input({
|
||||||
|
autocomplete: item.autocomplete || null,
|
||||||
|
autocompleteSelect: item.autocompleteSelect || false,
|
||||||
label: Ox._(item.title),
|
label: Ox._(item.title),
|
||||||
labelWidth: 128,
|
labelWidth: 128,
|
||||||
placeholder: item.placeholder || '',
|
placeholder: item.placeholder || '',
|
||||||
|
@ -415,6 +429,9 @@ oml.ui.preferencesDialog = function() {
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
var key = data.id,
|
var key = data.id,
|
||||||
value = data.data.value[0];
|
value = data.data.value[0];
|
||||||
|
if (key == 'username') {
|
||||||
|
value = getValidName(value, [], ':');
|
||||||
|
}
|
||||||
if (key in oml.config.user.preferences) {
|
if (key in oml.config.user.preferences) {
|
||||||
oml.Preferences.set(key, value);
|
oml.Preferences.set(key, value);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,6 +26,7 @@ oml.ui.rightPanel = function() {
|
||||||
oml.$ui.itemViewPanel.options({size: data.size});
|
oml.$ui.itemViewPanel.options({size: data.size});
|
||||||
},
|
},
|
||||||
oml_item: function(data) {
|
oml_item: function(data) {
|
||||||
|
Ox.print('rightPanel, oml_item', data);
|
||||||
if (!!data.value != !!data.previousValue) {
|
if (!!data.value != !!data.previousValue) {
|
||||||
that.options({selected: !ui.item ? 'list' : 'item'});
|
that.options({selected: !ui.item ? 'list' : 'item'});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
oml.ui.statusIcon = function(status) {
|
oml.ui.statusIcon = function(user, index) {
|
||||||
|
|
||||||
// FIXME: not only '-webkit'
|
// FIXME: not only '-webkit'
|
||||||
|
|
||||||
var color = {
|
var status = getStatus(user),
|
||||||
connected: [[64, 255, 64], [0, 192, 0]],
|
|
||||||
disconnected: [[255, 64, 64], [192, 0, 0]],
|
|
||||||
transferring: [[64, 255, 255], [0, 192, 192]],
|
|
||||||
unknown: [[255, 255, 64], [192, 192, 0]]
|
|
||||||
}[status].map(function(rgb) {
|
|
||||||
return 'rgb(' + rgb.join(', ') + ')';
|
|
||||||
}).join(', '),
|
|
||||||
|
|
||||||
that = Ox.Element({
|
that = Ox.Element({
|
||||||
tooltip: Ox._({
|
tooltip: Ox._({
|
||||||
connected: 'Connected',
|
connected: 'Connected',
|
||||||
|
@ -24,7 +16,6 @@ oml.ui.statusIcon = function(status) {
|
||||||
width: '10px',
|
width: '10px',
|
||||||
height: '10px',
|
height: '10px',
|
||||||
margin: '3px',
|
margin: '3px',
|
||||||
background: '-webkit-linear-gradient(bottom, ' + color + ')',
|
|
||||||
borderRadius: '5px'
|
borderRadius: '5px'
|
||||||
})
|
})
|
||||||
.append(
|
.append(
|
||||||
|
@ -33,11 +24,65 @@ oml.ui.statusIcon = function(status) {
|
||||||
width: '8px',
|
width: '8px',
|
||||||
height: '8px',
|
height: '8px',
|
||||||
margin: '1px',
|
margin: '1px',
|
||||||
background: '-webkit-linear-gradient(top, ' + color + ')',
|
|
||||||
borderRadius: '4px'
|
borderRadius: '4px'
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return that;
|
render();
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
var superRemove = that.remove;
|
||||||
|
that.remove = function() {
|
||||||
|
oml.unbindEvent({
|
||||||
|
status: update
|
||||||
|
})
|
||||||
|
superRemove();
|
||||||
|
};
|
||||||
|
oml.bindEvent({
|
||||||
|
status: update
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus(data) {
|
||||||
|
return !oml.user.online && index ? 'unknown'
|
||||||
|
: data.online ? 'connected'
|
||||||
|
: 'disconnected';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
var color = {
|
||||||
|
connected: [[64, 255, 64], [0, 192, 0]],
|
||||||
|
disconnected: [[255, 64, 64], [192, 0, 0]],
|
||||||
|
transferring: [[64, 255, 255], [0, 192, 192]],
|
||||||
|
unknown: [[255, 255, 64], [192, 192, 0]]
|
||||||
|
}[status].map(function(rgb) {
|
||||||
|
return 'rgb(' + rgb.join(', ') + ')';
|
||||||
|
}).join(', ');
|
||||||
|
|
||||||
|
that.options({
|
||||||
|
tooltip: Ox._({
|
||||||
|
connected: 'Connected',
|
||||||
|
disconnected: 'Disconnected',
|
||||||
|
transferring: 'Transferring'
|
||||||
|
}[status])
|
||||||
|
}).css({
|
||||||
|
background: '-webkit-linear-gradient(bottom, ' + color + ')',
|
||||||
|
});
|
||||||
|
|
||||||
|
that.find('div').css({
|
||||||
|
background: '-webkit-linear-gradient(top, ' + color + ')',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(data) {
|
||||||
|
if (data.id == user.id) {
|
||||||
|
var newStatus = getStatus(data);
|
||||||
|
if (status != newStatus) {
|
||||||
|
status = newStatus;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return that;
|
||||||
};
|
};
|
|
@ -35,7 +35,9 @@ oml.ui.statusbar = function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function getText(data) {
|
function getText(data) {
|
||||||
return Ox.toTitleCase(Ox.formatCount(data.items, 'book'));
|
return Ox.toTitleCase(Ox.formatCount(data.items, 'book')) + (
|
||||||
|
data.items ? ', ' + Ox.formatValue(data.size, 'B') : ''
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
that.set = function(key, data) {
|
that.set = function(key, data) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ oml.ui.transfersDialog = function() {
|
||||||
visible: true,
|
visible: true,
|
||||||
width: id == 'title' ? 240
|
width: id == 'title' ? 240
|
||||||
: id == 'transferadded' ? 144
|
: id == 'transferadded' ? 144
|
||||||
: id == 'transferprogress' ? 80
|
: id == 'transferprogress' ? 80 - Ox.UI.SCROLLBAR_SIZE
|
||||||
: key.columnWidth
|
: key.columnWidth
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
@ -40,6 +40,7 @@ oml.ui.transfersDialog = function() {
|
||||||
}), callback);
|
}), callback);
|
||||||
},
|
},
|
||||||
keys: ['author'],
|
keys: ['author'],
|
||||||
|
scrollbarVisible: true,
|
||||||
sort: [{key: 'transferprogress', operator: '-'}],
|
sort: [{key: 'transferprogress', operator: '-'}],
|
||||||
unique: 'id'
|
unique: 'id'
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -38,7 +38,15 @@ oml.ui.usersDialog = function() {
|
||||||
width: 768
|
width: 768
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
|
open: function() {
|
||||||
|
oml.bindEvent({
|
||||||
|
peering: peering,
|
||||||
|
});
|
||||||
|
},
|
||||||
close: function() {
|
close: function() {
|
||||||
|
oml.unbindEvent({
|
||||||
|
peering: peering,
|
||||||
|
});
|
||||||
if (ui.page == 'users') {
|
if (ui.page == 'users') {
|
||||||
oml.UI.set({page: ''});
|
oml.UI.set({page: ''});
|
||||||
}
|
}
|
||||||
|
@ -112,6 +120,10 @@ oml.ui.usersDialog = function() {
|
||||||
.appendTo($users);
|
.appendTo($users);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var peering = Ox.throttle(function() {
|
||||||
|
updateUsers();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
function renderSectionList(folder) {
|
function renderSectionList(folder) {
|
||||||
|
|
||||||
var $list = Ox.TableList({
|
var $list = Ox.TableList({
|
||||||
|
@ -254,11 +266,15 @@ oml.ui.usersDialog = function() {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
var value = oml.validateName(
|
var value = oml.getValidName(
|
||||||
data.value,
|
data.value || 'anonymous',
|
||||||
users.map(function(user) {
|
// FIXME: WRONG
|
||||||
return user.nickname;
|
users.filter(function(user_) {
|
||||||
})
|
return user_.nickname != user.nickname;
|
||||||
|
}).map(function(u) {
|
||||||
|
return user_.nickname;
|
||||||
|
}),
|
||||||
|
':'
|
||||||
);
|
);
|
||||||
this.value(value);
|
this.value(value);
|
||||||
oml.api.editUser({
|
oml.api.editUser({
|
||||||
|
@ -447,9 +463,7 @@ oml.ui.usersDialog = function() {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
format: function(value, data) {
|
format: function(value, data) {
|
||||||
return oml.ui.statusIcon(
|
return oml.ui.statusIcon(data)
|
||||||
value ? 'connected' : 'disconnected'
|
|
||||||
)
|
|
||||||
.css({
|
.css({
|
||||||
margin: '2px 3px 3px 0'
|
margin: '2px 3px 3px 0'
|
||||||
});
|
});
|
||||||
|
@ -581,7 +595,6 @@ oml.ui.usersDialog = function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
that.updateElement = function() {
|
that.updateElement = function() {
|
||||||
|
|
||||||
that.options({
|
that.options({
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
oml.addList = function() {
|
oml.addList = function() {
|
||||||
// addList(isSmart, isFrom) or addList(list) [=dupicate]
|
// addList(isSmart, isFrom[, name[, callback]])
|
||||||
|
// or addList(list) [=duplicate]
|
||||||
var args = arguments,
|
var args = arguments,
|
||||||
isDuplicate = args.length == 1,
|
isDuplicate = args.length == 1,
|
||||||
isSmart, isFrom, list, listData, data,
|
isSmart, isFrom, name, callback,
|
||||||
|
list, listData, data,
|
||||||
username = oml.user.preferences.username;
|
username = oml.user.preferences.username;
|
||||||
Ox.Request.clearCache('getLists');
|
Ox.Request.clearCache('getLists');
|
||||||
oml.api.getLists(function(result) {
|
oml.api.getLists(function(result) {
|
||||||
|
@ -16,8 +18,10 @@ oml.addList = function() {
|
||||||
if (!isDuplicate) {
|
if (!isDuplicate) {
|
||||||
isSmart = args[0];
|
isSmart = args[0];
|
||||||
isFrom = args[1];
|
isFrom = args[1];
|
||||||
|
name = args[2] || Ox._('Untitled');
|
||||||
|
callback = args[3];
|
||||||
data = {
|
data = {
|
||||||
name: oml.validateName(Ox._('Untitled'), listNames),
|
name: oml.getValidName(name, listNames),
|
||||||
type: !isSmart ? 'static' : 'smart'
|
type: !isSmart ? 'static' : 'smart'
|
||||||
};
|
};
|
||||||
if (!isSmart) {
|
if (!isSmart) {
|
||||||
|
@ -36,7 +40,7 @@ oml.addList = function() {
|
||||||
list = args[0];
|
list = args[0];
|
||||||
listData = Ox.getObjectById(Ox.flatten(Ox.values(lists)), list);
|
listData = Ox.getObjectById(Ox.flatten(Ox.values(lists)), list);
|
||||||
data = Ox.extend({
|
data = Ox.extend({
|
||||||
name: oml.validateName(listData.name, listNames),
|
name: oml.getValidName(listData.name, listNames),
|
||||||
type: listData.type
|
type: listData.type
|
||||||
}, listData.query ? {
|
}, listData.query ? {
|
||||||
query: listData.query
|
query: listData.query
|
||||||
|
@ -74,6 +78,10 @@ oml.addList = function() {
|
||||||
$folderList = oml.$ui.folderList[0];
|
$folderList = oml.$ui.folderList[0];
|
||||||
oml.$ui.folder[0].options({collapsed: false}); // FIXME: SET UI!
|
oml.$ui.folder[0].options({collapsed: false}); // FIXME: SET UI!
|
||||||
// FIXME: DOESN'T WORK
|
// FIXME: DOESN'T WORK
|
||||||
|
if (
|
||||||
|
!oml.$ui.importExportDialog
|
||||||
|
|| !oml.$ui.importExportDialog.is(':visible')
|
||||||
|
) {
|
||||||
$folderList
|
$folderList
|
||||||
.bindEventOnce({
|
.bindEventOnce({
|
||||||
load: function() {
|
load: function() {
|
||||||
|
@ -93,7 +101,9 @@ oml.addList = function() {
|
||||||
oml.$ui.listDialog = oml.ui.listDialog().open();
|
oml.$ui.listDialog = oml.ui.listDialog().open();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
oml.$ui.folders.updateOwnLists();
|
oml.$ui.folders.updateOwnLists();
|
||||||
|
callback && callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -267,7 +277,6 @@ oml.enableDragAndDrop = function($list, canMove) {
|
||||||
|
|
||||||
$list.bindEvent({
|
$list.bindEvent({
|
||||||
draganddropstart: function(data) {
|
draganddropstart: function(data) {
|
||||||
Ox.print('DND START', data);
|
|
||||||
var $lists = oml.$ui.libraryList.concat(oml.$ui.folderList);
|
var $lists = oml.$ui.libraryList.concat(oml.$ui.folderList);
|
||||||
drag.action = 'copy';
|
drag.action = 'copy';
|
||||||
drag.ids = $list.options('selected');
|
drag.ids = $list.options('selected');
|
||||||
|
@ -732,7 +741,6 @@ oml.getListData = function(list) {
|
||||||
oml.getListFoldersHeight = function() {
|
oml.getListFoldersHeight = function() {
|
||||||
var ui = oml.user.ui;
|
var ui = oml.user.ui;
|
||||||
return Object.keys(ui.showFolder).reduce(function(value, id, index) {
|
return Object.keys(ui.showFolder).reduce(function(value, id, index) {
|
||||||
Ox.print('WTF WTF', index)
|
|
||||||
var items = oml.$ui.folderList[index].options('items').length;
|
var items = oml.$ui.folderList[index].options('items').length;
|
||||||
return value + 16 + ui.showFolder[id] * (1 + items) * 16;
|
return value + 16 + ui.showFolder[id] * (1 + items) * 16;
|
||||||
}, 16);
|
}, 16);
|
||||||
|
@ -748,6 +756,35 @@ oml.getListFoldersWidth = function() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oml.getLists = function(callback) {
|
||||||
|
var ui = oml.user.ui;
|
||||||
|
Ox.Request.clearCache('getLists');
|
||||||
|
oml.api.getLists(function(result) {
|
||||||
|
var username = oml.user.preferences.username;
|
||||||
|
ui._lists = result.data.lists.map(function(list) {
|
||||||
|
// FIXME: 'editable' is notoriously vague
|
||||||
|
list.name = list.type == 'libraries' ? Ox._('Libraries')
|
||||||
|
: list.type == 'library' ? Ox._('Library') : list.name;
|
||||||
|
return Ox.extend(list, {
|
||||||
|
editable: list.user == username && list.type == 'static',
|
||||||
|
own: list.user == username,
|
||||||
|
title: (list.user ? list.user + ': ' : '') + list.name
|
||||||
|
});
|
||||||
|
});
|
||||||
|
callback(ui._lists);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
oml.getOwnListNames = function() {
|
||||||
|
var ui = oml.user.ui,
|
||||||
|
username = oml.user.preferences.username;
|
||||||
|
return ui._lists.filter(function(list) {
|
||||||
|
return list.user == username;
|
||||||
|
}).filter(function(list) {
|
||||||
|
return list.name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
oml.getPageTitle = function(stateOrURL) {
|
oml.getPageTitle = function(stateOrURL) {
|
||||||
var page = Ox.getObjectById(
|
var page = Ox.getObjectById(
|
||||||
oml.config.pages,
|
oml.config.pages,
|
||||||
|
@ -765,6 +802,22 @@ oml.getSortOperator = function(key) {
|
||||||
) ? '+' : '-';
|
) ? '+' : '-';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oml.getUsers = function(callback) {
|
||||||
|
var users = [{
|
||||||
|
id: oml.user.id,
|
||||||
|
nickname: oml.user.preferences.username,
|
||||||
|
online: oml.user.online
|
||||||
|
}];
|
||||||
|
oml.api.getUsers(function(result) {
|
||||||
|
users = users.concat(
|
||||||
|
result.data.users.filter(function(user) {
|
||||||
|
return user.peered;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
callback(users);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
oml.getUsersAndLists = function(callback) {
|
oml.getUsersAndLists = function(callback) {
|
||||||
var lists = [{
|
var lists = [{
|
||||||
id: '',
|
id: '',
|
||||||
|
@ -811,12 +864,30 @@ oml.getUsersAndLists = function(callback) {
|
||||||
oml.$ui.mainMenu.updateElement();
|
oml.$ui.mainMenu.updateElement();
|
||||||
}
|
}
|
||||||
ui._lists = lists;
|
ui._lists = lists;
|
||||||
Ox.print('UI._LISTS', JSON.stringify(ui._lists));
|
Ox.print('GOT LISTS ::::', lists);
|
||||||
callback(users, lists);
|
callback(users, lists);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oml.getValidName = function(value, names, chars) {
|
||||||
|
var index = 1, length = 256, suffix;
|
||||||
|
if (chars) {
|
||||||
|
value = value.replace(
|
||||||
|
new RegExp('[' + Ox.escapeRegExp(chars) + ']', 'g'),
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value = Ox.clean(Ox.clean(value).slice(0, length));
|
||||||
|
names = names || [];
|
||||||
|
while (Ox.contains(names, value)) {
|
||||||
|
suffix = ' [' + (++index) + ']';
|
||||||
|
value = value.replace(/ \[\d+\]$/, '')
|
||||||
|
.slice(0, length - suffix.length) + suffix;
|
||||||
|
};
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
oml.hasDialogOrScreen = function() {
|
oml.hasDialogOrScreen = function() {
|
||||||
return !!$('.OxDialog:visible').length
|
return !!$('.OxDialog:visible').length
|
||||||
|| !!$('.OxFullscreen').length
|
|| !!$('.OxFullscreen').length
|
||||||
|
@ -834,7 +905,6 @@ oml.resizeFilters = function() {
|
||||||
|
|
||||||
oml.resizeListFolders = function() {
|
oml.resizeListFolders = function() {
|
||||||
// FIXME: does this have to be here?
|
// FIXME: does this have to be here?
|
||||||
Ox.print('RESIZING LIST FOLDERS', 'WIDTH', oml.getListFoldersWidth(), 'HEIGHT', oml.getListFoldersHeight())
|
|
||||||
var width = oml.getListFoldersWidth(),
|
var width = oml.getListFoldersWidth(),
|
||||||
columnWidth = width - 58;
|
columnWidth = width - 58;
|
||||||
oml.$ui.librariesList
|
oml.$ui.librariesList
|
||||||
|
@ -842,7 +912,6 @@ oml.resizeListFolders = function() {
|
||||||
.resizeColumn('name', columnWidth);
|
.resizeColumn('name', columnWidth);
|
||||||
Ox.forEach(oml.$ui.folder, function($folder, index) {
|
Ox.forEach(oml.$ui.folder, function($folder, index) {
|
||||||
$folder.css({width: width + 'px'});
|
$folder.css({width: width + 'px'});
|
||||||
Ox.print('SHOULD BE:', width);
|
|
||||||
oml.$ui.libraryList[index]
|
oml.$ui.libraryList[index]
|
||||||
.css({width: width + 'px'})
|
.css({width: width + 'px'})
|
||||||
.resizeColumn('name', columnWidth);
|
.resizeColumn('name', columnWidth);
|
||||||
|
@ -881,18 +950,6 @@ oml.updateFilterMenus = function() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
oml.validateName = function(value, names) {
|
|
||||||
var index = 1, length = 256, suffix;
|
|
||||||
value = Ox.clean(Ox.clean(value).slice(0, length));
|
|
||||||
names = names || [];
|
|
||||||
while (Ox.contains(names, value)) {
|
|
||||||
suffix = ' [' + (++index) + ']';
|
|
||||||
value = value.replace(/ \[\d+\]$/, '')
|
|
||||||
.slice(0, length - suffix.length) + suffix;
|
|
||||||
};
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
oml.validatePublicKey = function(value) {
|
oml.validatePublicKey = function(value) {
|
||||||
return /^[A-Za-z0-9+\/]{43}$/.test(value);
|
return /^[A-Za-z0-9+\/]{43}$/.test(value);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue