forked from 0x2620/pandora
new documents section
This commit is contained in:
parent
3fcbd59525
commit
e1f35b1ec8
74 changed files with 6737 additions and 631 deletions
|
@ -94,10 +94,20 @@ def load_config(init=False):
|
||||||
for key in config['itemKeys']:
|
for key in config['itemKeys']:
|
||||||
config['keys'][key['id']] = key
|
config['keys'][key['id']] = key
|
||||||
|
|
||||||
|
# add entities if needed
|
||||||
|
if len(config.get('entities', [])) and not [k for k in config['documentKeys'] if k['id'] == 'entites']:
|
||||||
|
config['documentKeys'].append({
|
||||||
|
'id': 'entity',
|
||||||
|
'title': 'Entity',
|
||||||
|
'type': 'string',
|
||||||
|
'find': True
|
||||||
|
})
|
||||||
|
|
||||||
# add missing defaults
|
# add missing defaults
|
||||||
for section in sorted((
|
for section in sorted((
|
||||||
'capabilities', 'cantPlay', 'entities', 'itemName', 'itemTitleKeys', 'media', 'posters',
|
'capabilities', 'cantPlay',
|
||||||
|
'documentKeys',
|
||||||
|
'entities', 'itemName', 'itemTitleKeys', 'itemKeys', 'media', 'posters',
|
||||||
'site', 'tv', 'user.ui', 'user.ui.part', 'user.ui.showFolder',
|
'site', 'tv', 'user.ui', 'user.ui.part', 'user.ui.showFolder',
|
||||||
'menuExtras', 'languages'
|
'menuExtras', 'languages'
|
||||||
)):
|
)):
|
||||||
|
|
|
@ -35,11 +35,13 @@
|
||||||
*/
|
*/
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"canAddItems": {"staff": true, "admin": true},
|
"canAddItems": {"staff": true, "admin": true},
|
||||||
|
"canAddDocuments": {"staff": true, "admin": true},
|
||||||
"canDownloadVideo": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1},
|
"canDownloadVideo": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1},
|
||||||
"canEditAnnotations": {"staff": true, "admin": true},
|
"canEditAnnotations": {"staff": true, "admin": true},
|
||||||
"canEditEntities": {"staff": true, "admin": true},
|
"canEditEntities": {"staff": true, "admin": true},
|
||||||
"canEditDocuments": {"staff": true, "admin": true},
|
"canEditDocuments": {"staff": true, "admin": true},
|
||||||
"canEditEvents": {"staff": true, "admin": true},
|
"canEditEvents": {"staff": true, "admin": true},
|
||||||
|
"canEditFeaturedCollections": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedLists": {"staff": true, "admin": true},
|
"canEditFeaturedLists": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
||||||
|
@ -61,11 +63,13 @@
|
||||||
"canPlayVideo": {"guest": 1, "member": 1, "friend": 4, "staff": 4, "admin": 4},
|
"canPlayVideo": {"guest": 1, "member": 1, "friend": 4, "staff": 4, "admin": 4},
|
||||||
"canReadText": {"guest": 0, "member": 0, "friend": 1, "staff": 1, "admin": 1},
|
"canReadText": {"guest": 0, "member": 0, "friend": 1, "staff": 1, "admin": 1},
|
||||||
"canRemoveItems": {"admin": true},
|
"canRemoveItems": {"admin": true},
|
||||||
|
"canRemoveDocuments": {"staff": true, "admin": true},
|
||||||
"canSeeAccessed": {"staff": true, "admin": true},
|
"canSeeAccessed": {"staff": true, "admin": true},
|
||||||
"canSeeAllTasks": {"staff": true, "admin": true},
|
"canSeeAllTasks": {"staff": true, "admin": true},
|
||||||
"canSeeDebugMenu": {"staff": true, "admin": true},
|
"canSeeDebugMenu": {"staff": true, "admin": true},
|
||||||
"canSeeExtraItemViews": {"staff": true, "admin": true},
|
"canSeeExtraItemViews": {"staff": true, "admin": true},
|
||||||
"canSeeMedia": {"staff": true, "admin": true},
|
"canSeeMedia": {"staff": true, "admin": true},
|
||||||
|
"canSeeDocument": {"guest": 1, "member": 1, "firend": 4, "staff": 4, "admin": 4},
|
||||||
"canSeeItem": {"guest": 3, "member": 3, "friend": 4, "staff": 4, "admin": 4},
|
"canSeeItem": {"guest": 3, "member": 3, "friend": 4, "staff": 4, "admin": 4},
|
||||||
"canSeeSize": {"friend": true, "staff": true, "admin": true},
|
"canSeeSize": {"friend": true, "staff": true, "admin": true},
|
||||||
"canSeeSoftwareVersion": {"staff": true, "admin": true},
|
"canSeeSoftwareVersion": {"staff": true, "admin": true},
|
||||||
|
@ -91,6 +95,212 @@
|
||||||
list means it will not be included in find annotations.
|
list means it will not be included in find annotations.
|
||||||
*/
|
*/
|
||||||
"clipLayers": ["subtitles"],
|
"clipLayers": ["subtitles"],
|
||||||
|
"documentKeys": [
|
||||||
|
{
|
||||||
|
"id": "*",
|
||||||
|
"title": "All",
|
||||||
|
"type": "text",
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Title",
|
||||||
|
"type": "string",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "title",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "type",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Type",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "author",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Author",
|
||||||
|
"type": ["string"],
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "person",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "publisher",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Publisher",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "place",
|
||||||
|
"title": "Place",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "date",
|
||||||
|
"title": "Date",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 120,
|
||||||
|
//"format": {"type": "date", "args": ["%a, %b %e, %Y"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "series",
|
||||||
|
"title": "Series",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edition",
|
||||||
|
"title": "Edition",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language",
|
||||||
|
"title": "Language",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "id",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "ID",
|
||||||
|
"type": "string",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "extension",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Extension",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dimensions",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Dimensions",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "size",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Size",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"format": {"type": "value", "args": ["B"]},
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "description",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Description",
|
||||||
|
"type": "text",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "matches",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Matches",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "user",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "User",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "created",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Created",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "modified",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Modified",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "accessed",
|
||||||
|
"title": "Last Accessed",
|
||||||
|
"type": "date",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 150,
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timesaccessed",
|
||||||
|
"title": "Times Accessed",
|
||||||
|
"type": "integer",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 60,
|
||||||
|
"format": {"type": "number", "args": []},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rightslevel",
|
||||||
|
"title": "Rights Level",
|
||||||
|
"type": "enum",
|
||||||
|
"columnWidth": 90,
|
||||||
|
"format": {"type": "ColorLevel", "args": [
|
||||||
|
["Public", "Out of Copyright", "Under Copyright", "Private"]
|
||||||
|
]},
|
||||||
|
"sort": true,
|
||||||
|
"sortOperator": "+",
|
||||||
|
"values": ["Public", "Out of Copyright", "Under Copyright", "Private", "Unknown"]
|
||||||
|
}
|
||||||
|
],
|
||||||
/*
|
/*
|
||||||
"entities" can be used to store arbitrary data. They can be referenced in
|
"entities" can be used to store arbitrary data. They can be referenced in
|
||||||
annotations, info view, or elsewhere. Each entry defines a specific class
|
annotations, info view, or elsewhere. Each entry defines a specific class
|
||||||
|
@ -976,13 +1186,24 @@
|
||||||
"calendarFind": "",
|
"calendarFind": "",
|
||||||
"calendarSelection": "",
|
"calendarSelection": "",
|
||||||
"clipColumns": 2,
|
"clipColumns": 2,
|
||||||
|
"collectionColumns": ["title", "id", "extension", "dimensions", "size", "description", "matches", "user", "created", "modified"],
|
||||||
|
"collectionColumnWidth": {},
|
||||||
|
"collectionSelection": [],
|
||||||
|
"collectionSort": [
|
||||||
|
{"key": "title", "operator": "+"},
|
||||||
|
{"key": "extension", "operator": "+"}
|
||||||
|
],
|
||||||
|
"collectionView": "grid",
|
||||||
|
"collections": {},
|
||||||
"columns": {
|
"columns": {
|
||||||
"Colors": {
|
"Colors": {
|
||||||
"columns": ["title", "director", "country", "year", "hue", "saturation", "brightness"],
|
"columns": ["title", "director", "country", "year", "hue", "saturation", "brightness"],
|
||||||
"columnWidth": {}
|
"columnWidth": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"document": "",
|
||||||
"documents": {},
|
"documents": {},
|
||||||
|
"documentView": "view",
|
||||||
"documentSize": 256,
|
"documentSize": 256,
|
||||||
"documentsSelection": {},
|
"documentsSelection": {},
|
||||||
"documentsSort": [{"key": "name", "operator": "+"}],
|
"documentsSort": [{"key": "name", "operator": "+"}],
|
||||||
|
@ -1011,6 +1232,7 @@
|
||||||
],
|
],
|
||||||
"filtersSize": 176,
|
"filtersSize": 176,
|
||||||
"find": {"conditions": [], "operator": "&"},
|
"find": {"conditions": [], "operator": "&"},
|
||||||
|
"findDocuments": {"conditions": [], "operator": "&"},
|
||||||
"followPlayer": true,
|
"followPlayer": true,
|
||||||
"help": "",
|
"help": "",
|
||||||
"icons": "posters",
|
"icons": "posters",
|
||||||
|
@ -1076,7 +1298,7 @@
|
||||||
"featured": true,
|
"featured": true,
|
||||||
"volumes": true
|
"volumes": true
|
||||||
},
|
},
|
||||||
"texts": {
|
"documents": {
|
||||||
"personal": true,
|
"personal": true,
|
||||||
"favorite": true,
|
"favorite": true,
|
||||||
"featured": true
|
"featured": true
|
||||||
|
|
|
@ -36,11 +36,13 @@
|
||||||
*/
|
*/
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"canAddItems": {"researcher": true, "staff": true, "admin": true},
|
"canAddItems": {"researcher": true, "staff": true, "admin": true},
|
||||||
|
"canAddDocuments": {"researcher": true, "staff": true, "admin": true},
|
||||||
"canDownloadVideo": {"guest": -1, "member": -1, "researcher": 3, "staff": 3, "admin": 3},
|
"canDownloadVideo": {"guest": -1, "member": -1, "researcher": 3, "staff": 3, "admin": 3},
|
||||||
"canEditAnnotations": {"staff": true, "admin": true},
|
"canEditAnnotations": {"staff": true, "admin": true},
|
||||||
"canEditEntities": {"staff": true, "admin": true},
|
|
||||||
"canEditDocuments": {"researcher": true, "staff": true, "admin": true},
|
"canEditDocuments": {"researcher": true, "staff": true, "admin": true},
|
||||||
|
"canEditEntities": {"staff": true, "admin": true},
|
||||||
"canEditEvents": {"researcher": true, "staff": true, "admin": true},
|
"canEditEvents": {"researcher": true, "staff": true, "admin": true},
|
||||||
|
"canEditFeaturedCollections": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedLists": {"staff": true, "admin": true},
|
"canEditFeaturedLists": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
||||||
|
@ -63,11 +65,13 @@
|
||||||
"canPlayVideo": {"guest": 1, "member": 1, "researcher": 3, "staff": 3, "admin": 3},
|
"canPlayVideo": {"guest": 1, "member": 1, "researcher": 3, "staff": 3, "admin": 3},
|
||||||
"canReadText": {"guest": 0, "member": 0, "researcher": 1, "staff": 1, "admin": 1},
|
"canReadText": {"guest": 0, "member": 0, "researcher": 1, "staff": 1, "admin": 1},
|
||||||
"canRemoveItems": {"staff": true, "admin": true},
|
"canRemoveItems": {"staff": true, "admin": true},
|
||||||
|
"canRemoveDocuments": {"staff": true, "admin": true},
|
||||||
"canSeeAccessed": {"researcher": true, "staff": true, "admin": true},
|
"canSeeAccessed": {"researcher": true, "staff": true, "admin": true},
|
||||||
"canSeeAllTasks": {"staff": true, "admin": true},
|
"canSeeAllTasks": {"staff": true, "admin": true},
|
||||||
"canSeeDebugMenu": {"researcher": true, "staff": true, "admin": true},
|
"canSeeDebugMenu": {"researcher": true, "staff": true, "admin": true},
|
||||||
"canSeeExtraItemViews": {"researcher": true, "staff": true, "admin": true},
|
"canSeeExtraItemViews": {"researcher": true, "staff": true, "admin": true},
|
||||||
"canSeeMedia": {"researcher": true, "staff": true, "admin": true},
|
"canSeeMedia": {"researcher": true, "staff": true, "admin": true},
|
||||||
|
"canSeeDocument": {"guest": 3, "member": 3, "researcher": 3, "staff": 3, "admin": 3},
|
||||||
"canSeeItem": {"guest": 3, "member": 3, "researcher": 3, "staff": 3, "admin": 3},
|
"canSeeItem": {"guest": 3, "member": 3, "researcher": 3, "staff": 3, "admin": 3},
|
||||||
"canSeeSize": {"researcher": true, "staff": true, "admin": true},
|
"canSeeSize": {"researcher": true, "staff": true, "admin": true},
|
||||||
"canSeeSoftwareVersion": {"researcher": true, "staff": true, "admin": true},
|
"canSeeSoftwareVersion": {"researcher": true, "staff": true, "admin": true},
|
||||||
|
@ -94,6 +98,257 @@
|
||||||
*/
|
*/
|
||||||
"clipLayers": ["subtitles", "keywords", "notes"],
|
"clipLayers": ["subtitles", "keywords", "notes"],
|
||||||
/*
|
/*
|
||||||
|
"documentKeys" defines the metadata associated with each document. Required keys
|
||||||
|
are "*", "id" and "title".
|
||||||
|
A documentKey must have the following properties:
|
||||||
|
"id": The unique id of the key (as used by the server)
|
||||||
|
"title": The title of the key (as displayed by the client)
|
||||||
|
"type": Can be "boolean", "date", "enum", "float", "hue", "integer",
|
||||||
|
"layer", "string", "text", "time" or ["..."] (list of values of
|
||||||
|
this type). If type is "layer", this is a reference to the
|
||||||
|
annotations layer with the same id.
|
||||||
|
and can have any of the following properties:
|
||||||
|
"additionalSort": Ordered list of {key, operator} objects, where key is
|
||||||
|
another itemKey and operator is "+" or "-". This can be used to
|
||||||
|
override user.ui.listSort when results are sorted by this key.
|
||||||
|
"autocomplete": If true, the find element will provide autocomplete
|
||||||
|
"autocompleteSort": Sort order of autocomplete suggestions
|
||||||
|
"capability": A capability required to see data for this key
|
||||||
|
"columnRequired": If true, the column can't be removed from list view
|
||||||
|
"columnWidth": Default column width in px. If absent, no column for
|
||||||
|
this key can be added in list view.
|
||||||
|
"filter": If true, one can filter results by this key
|
||||||
|
"find": If true, this key will appear as an option in the find element
|
||||||
|
"flag": Can be "country" or "language". If set (and filter is true), a
|
||||||
|
flag icon corresponding to the field's value will be displayed.
|
||||||
|
"format": {type: string, args: [value, value, ...]}, used for special
|
||||||
|
formatting. This will invoke Ox.formatType(args). For details, see
|
||||||
|
https://oxjs.org/#doc/Ox.formatArea etc.
|
||||||
|
"secondaryId": If true, loading the URL "/value" will redirect to the
|
||||||
|
corresponding item, in case there is an exact match for this key
|
||||||
|
"sort": If true, one can sort results by this key
|
||||||
|
"sortOperator": Sort order ("+" or "-"), in case it differs from the
|
||||||
|
default for the key's type ("+" for strings, "-" for numbers)
|
||||||
|
"sortType": Special sort type ("person" or "title") which can be
|
||||||
|
further configured in "Manage Names" or "Manage Titles"
|
||||||
|
"value": {key: string, type: string} or {layer: string, type: string},
|
||||||
|
for keys whose value is derived from other keys or layers (like
|
||||||
|
"number of actors" or "words per minute"). Possible values for type
|
||||||
|
are "length", "lengthperminute", "words", and "wordsperminute".
|
||||||
|
Alternatively, "value" can be set to the string "capability", which
|
||||||
|
results in an itemKey whose boolean value indicates the presence or
|
||||||
|
absence of a userLevel-dependent capability. This can be used to
|
||||||
|
create queries and lists like "all items this user can play" etc.
|
||||||
|
"values": [value, value, ...] Ordered list of values, in case "type" is
|
||||||
|
"enum"
|
||||||
|
*/
|
||||||
|
"documentKeys": [
|
||||||
|
{
|
||||||
|
"id": "*",
|
||||||
|
"title": "All",
|
||||||
|
"type": "text",
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Title",
|
||||||
|
"type": "string",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "title",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "type",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Type",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "author",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Author",
|
||||||
|
"type": ["string"],
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "person",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "publisher",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Publisher",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "place",
|
||||||
|
"title": "Place",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "date",
|
||||||
|
"title": "Date",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 120,
|
||||||
|
//"format": {"type": "date", "args": ["%a, %b %e, %Y"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "series",
|
||||||
|
"title": "Series",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edition",
|
||||||
|
"title": "Edition",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language",
|
||||||
|
"title": "Language",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "id",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "ID",
|
||||||
|
"type": "string",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "extension",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Extension",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dimensions",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Dimensions",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "size",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Size",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"format": {"type": "value", "args": ["B"]},
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "description",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Description",
|
||||||
|
"type": "text",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "matches",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Matches",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "user",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "User",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "created",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Created",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "modified",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Modified",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "accessed",
|
||||||
|
"title": "Last Accessed",
|
||||||
|
"type": "date",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 150,
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timesaccessed",
|
||||||
|
"title": "Times Accessed",
|
||||||
|
"type": "integer",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 60,
|
||||||
|
"format": {"type": "number", "args": []},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rightslevel",
|
||||||
|
"title": "Rights Level",
|
||||||
|
"type": "enum",
|
||||||
|
"columnWidth": 90,
|
||||||
|
"format": {"type": "ColorLevel", "args": [
|
||||||
|
["Public", "Out of Copyright", "Under Copyright", "Private"]
|
||||||
|
]},
|
||||||
|
"sort": true,
|
||||||
|
"sortOperator": "+",
|
||||||
|
"values": ["Public", "Out of Copyright", "Under Copyright", "Private", "Unknown"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
/*
|
||||||
"entities" can be used to store arbitrary data. They can be referenced in
|
"entities" can be used to store arbitrary data. They can be referenced in
|
||||||
annotations, info view, or elsewhere. Each entry defines a specific class
|
annotations, info view, or elsewhere. Each entry defines a specific class
|
||||||
of entity object, its properties and their types (for example an "actor"
|
of entity object, its properties and their types (for example an "actor"
|
||||||
|
@ -1004,16 +1259,26 @@
|
||||||
"calendarFind": "",
|
"calendarFind": "",
|
||||||
"calendarSelection": "",
|
"calendarSelection": "",
|
||||||
"clipColumns": 2,
|
"clipColumns": 2,
|
||||||
|
"collectionColumns": ["title", "id", "extension", "dimensions", "size", "description", "matches", "user", "created", "modified"],
|
||||||
|
"collectionColumnWidth": {},
|
||||||
|
"collectionSelection": [],
|
||||||
|
"collectionSort": [
|
||||||
|
{"key": "title", "operator": "+"},
|
||||||
|
{"key": "extension", "operator": "+"}
|
||||||
|
],
|
||||||
|
"collectionView": "grid",
|
||||||
|
"collections": {},
|
||||||
"columns": {
|
"columns": {
|
||||||
"Colors": {
|
"Colors": {
|
||||||
"columns": ["title", "director", "country", "year", "hue", "saturation", "brightness"],
|
"columns": ["title", "director", "country", "year", "hue", "saturation", "brightness"],
|
||||||
"columnWidth": {}
|
"columnWidth": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"documentView": "view",
|
||||||
"documents": {},
|
"documents": {},
|
||||||
"documentSize": 256,
|
"documentSize": 256,
|
||||||
"documentsSelection": {},
|
"documentsSelection": {},
|
||||||
"documentsSort": [{"key": "name", "operator": "+"}],
|
"documentsSort": [{"key": "title", "operator": "+"}],
|
||||||
"documentsView": "grid",
|
"documentsView": "grid",
|
||||||
"edit": "",
|
"edit": "",
|
||||||
"edits": {},
|
"edits": {},
|
||||||
|
@ -1039,6 +1304,7 @@
|
||||||
],
|
],
|
||||||
"filtersSize": 176,
|
"filtersSize": 176,
|
||||||
"find": {"conditions": [], "operator": "&"},
|
"find": {"conditions": [], "operator": "&"},
|
||||||
|
"findDocuments": {"conditions": [], "operator": "&"},
|
||||||
"followPlayer": true,
|
"followPlayer": true,
|
||||||
"help": "",
|
"help": "",
|
||||||
"icons": "posters",
|
"icons": "posters",
|
||||||
|
@ -1063,7 +1329,6 @@
|
||||||
"page": "",
|
"page": "",
|
||||||
"part": {
|
"part": {
|
||||||
"api": "",
|
"api": "",
|
||||||
"documents": "",
|
|
||||||
"entities": "",
|
"entities": "",
|
||||||
"faq": "",
|
"faq": "",
|
||||||
"help": "",
|
"help": "",
|
||||||
|
@ -1104,6 +1369,11 @@
|
||||||
"featured": true,
|
"featured": true,
|
||||||
"volumes": true
|
"volumes": true
|
||||||
},
|
},
|
||||||
|
"documents": {
|
||||||
|
"personal": true,
|
||||||
|
"favorite": true,
|
||||||
|
"featured": true
|
||||||
|
},
|
||||||
"texts": {
|
"texts": {
|
||||||
"personal": true,
|
"personal": true,
|
||||||
"favorite": true,
|
"favorite": true,
|
||||||
|
@ -1117,6 +1387,8 @@
|
||||||
"sidebarSize": 256,
|
"sidebarSize": 256,
|
||||||
"text": "",
|
"text": "",
|
||||||
"texts": {},
|
"texts": {},
|
||||||
|
"documents": {},
|
||||||
|
"document": "",
|
||||||
"theme": "oxmedium",
|
"theme": "oxmedium",
|
||||||
"updateAdvancedFindResults": false,
|
"updateAdvancedFindResults": false,
|
||||||
"videoLoop": false,
|
"videoLoop": false,
|
||||||
|
|
|
@ -35,11 +35,13 @@
|
||||||
*/
|
*/
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"canAddItems": {"member": true, "staff": true, "admin": true},
|
"canAddItems": {"member": true, "staff": true, "admin": true},
|
||||||
|
"canAddDocuments": {"member": true, "staff": true, "admin": true},
|
||||||
"canDownloadVideo": {"guest": 0, "member": 0, "staff": 4, "admin": 4},
|
"canDownloadVideo": {"guest": 0, "member": 0, "staff": 4, "admin": 4},
|
||||||
"canEditAnnotations": {"staff": true, "admin": true},
|
"canEditAnnotations": {"staff": true, "admin": true},
|
||||||
"canEditEntities": {"staff": true, "admin": true},
|
"canEditEntities": {"staff": true, "admin": true},
|
||||||
"canEditDocuments": {"staff": true, "admin": true},
|
"canEditDocuments": {"staff": true, "admin": true},
|
||||||
"canEditEvents": {"staff": true, "admin": true},
|
"canEditEvents": {"staff": true, "admin": true},
|
||||||
|
"canEditFeaturedCollections": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedLists": {"staff": true, "admin": true},
|
"canEditFeaturedLists": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
||||||
|
@ -61,11 +63,13 @@
|
||||||
"canPlayVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
"canPlayVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canReadText": {"guest": 0, "member": 0, "staff": 1, "admin": 1},
|
"canReadText": {"guest": 0, "member": 0, "staff": 1, "admin": 1},
|
||||||
"canRemoveItems": {"admin": true},
|
"canRemoveItems": {"admin": true},
|
||||||
|
"canRemoveDocuments": {"staff": true, "admin": true},
|
||||||
"canSeeAccessed": {"staff": true, "admin": true},
|
"canSeeAccessed": {"staff": true, "admin": true},
|
||||||
"canSeeAllTasks": {"staff": true, "admin": true},
|
"canSeeAllTasks": {"staff": true, "admin": true},
|
||||||
"canSeeDebugMenu": {"staff": true, "admin": true},
|
"canSeeDebugMenu": {"staff": true, "admin": true},
|
||||||
"canSeeExtraItemViews": {"staff": true, "admin": true},
|
"canSeeExtraItemViews": {"staff": true, "admin": true},
|
||||||
"canSeeMedia": {"staff": true, "admin": true},
|
"canSeeMedia": {"staff": true, "admin": true},
|
||||||
|
"canSeeDocument": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canSeeItem": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
"canSeeItem": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canSeeSize": {"staff": true, "admin": true},
|
"canSeeSize": {"staff": true, "admin": true},
|
||||||
"canSeeSoftwareVersion": {"staff": true, "admin": true},
|
"canSeeSoftwareVersion": {"staff": true, "admin": true},
|
||||||
|
@ -92,6 +96,257 @@
|
||||||
*/
|
*/
|
||||||
"clipLayers": ["transcripts", "keywords", "places", "events", "descriptions"],
|
"clipLayers": ["transcripts", "keywords", "places", "events", "descriptions"],
|
||||||
/*
|
/*
|
||||||
|
"documentKeys" defines the metadata associated with each document. Required keys
|
||||||
|
are "*", "id" and "title".
|
||||||
|
A documentKey must have the following properties:
|
||||||
|
"id": The unique id of the key (as used by the server)
|
||||||
|
"title": The title of the key (as displayed by the client)
|
||||||
|
"type": Can be "boolean", "date", "enum", "float", "hue", "integer",
|
||||||
|
"layer", "string", "text", "time" or ["..."] (list of values of
|
||||||
|
this type). If type is "layer", this is a reference to the
|
||||||
|
annotations layer with the same id.
|
||||||
|
and can have any of the following properties:
|
||||||
|
"additionalSort": Ordered list of {key, operator} objects, where key is
|
||||||
|
another itemKey and operator is "+" or "-". This can be used to
|
||||||
|
override user.ui.listSort when results are sorted by this key.
|
||||||
|
"autocomplete": If true, the find element will provide autocomplete
|
||||||
|
"autocompleteSort": Sort order of autocomplete suggestions
|
||||||
|
"capability": A capability required to see data for this key
|
||||||
|
"columnRequired": If true, the column can't be removed from list view
|
||||||
|
"columnWidth": Default column width in px. If absent, no column for
|
||||||
|
this key can be added in list view.
|
||||||
|
"filter": If true, one can filter results by this key
|
||||||
|
"find": If true, this key will appear as an option in the find element
|
||||||
|
"flag": Can be "country" or "language". If set (and filter is true), a
|
||||||
|
flag icon corresponding to the field's value will be displayed.
|
||||||
|
"format": {type: string, args: [value, value, ...]}, used for special
|
||||||
|
formatting. This will invoke Ox.formatType(args). For details, see
|
||||||
|
https://oxjs.org/#doc/Ox.formatArea etc.
|
||||||
|
"secondaryId": If true, loading the URL "/value" will redirect to the
|
||||||
|
corresponding item, in case there is an exact match for this key
|
||||||
|
"sort": If true, one can sort results by this key
|
||||||
|
"sortOperator": Sort order ("+" or "-"), in case it differs from the
|
||||||
|
default for the key's type ("+" for strings, "-" for numbers)
|
||||||
|
"sortType": Special sort type ("person" or "title") which can be
|
||||||
|
further configured in "Manage Names" or "Manage Titles"
|
||||||
|
"value": {key: string, type: string} or {layer: string, type: string},
|
||||||
|
for keys whose value is derived from other keys or layers (like
|
||||||
|
"number of actors" or "words per minute"). Possible values for type
|
||||||
|
are "length", "lengthperminute", "words", and "wordsperminute".
|
||||||
|
Alternatively, "value" can be set to the string "capability", which
|
||||||
|
results in an itemKey whose boolean value indicates the presence or
|
||||||
|
absence of a userLevel-dependent capability. This can be used to
|
||||||
|
create queries and lists like "all items this user can play" etc.
|
||||||
|
"values": [value, value, ...] Ordered list of values, in case "type" is
|
||||||
|
"enum"
|
||||||
|
*/
|
||||||
|
"documentKeys": [
|
||||||
|
{
|
||||||
|
"id": "*",
|
||||||
|
"title": "All",
|
||||||
|
"type": "text",
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Title",
|
||||||
|
"type": "string",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "title",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "type",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Type",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "author",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Author",
|
||||||
|
"type": ["string"],
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "person",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "publisher",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Publisher",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "place",
|
||||||
|
"title": "Place",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "date",
|
||||||
|
"title": "Date",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 120,
|
||||||
|
//"format": {"type": "date", "args": ["%a, %b %e, %Y"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "series",
|
||||||
|
"title": "Series",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edition",
|
||||||
|
"title": "Edition",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language",
|
||||||
|
"title": "Language",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "id",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "ID",
|
||||||
|
"type": "string",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "extension",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Extension",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dimensions",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Dimensions",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "size",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Size",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"format": {"type": "value", "args": ["B"]},
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "description",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Description",
|
||||||
|
"type": "text",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "matches",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Matches",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "user",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "User",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "created",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Created",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "modified",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Modified",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "accessed",
|
||||||
|
"title": "Last Accessed",
|
||||||
|
"type": "date",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 150,
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timesaccessed",
|
||||||
|
"title": "Times Accessed",
|
||||||
|
"type": "integer",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 60,
|
||||||
|
"format": {"type": "number", "args": []},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rightslevel",
|
||||||
|
"title": "Rights Level",
|
||||||
|
"type": "enum",
|
||||||
|
"columnWidth": 90,
|
||||||
|
"format": {"type": "ColorLevel", "args": [
|
||||||
|
["Public", "Out of Copyright", "Under Copyright", "Private"]
|
||||||
|
]},
|
||||||
|
"sort": true,
|
||||||
|
"sortOperator": "+",
|
||||||
|
"values": ["Public", "Out of Copyright", "Under Copyright", "Private", "Unknown"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
/*
|
||||||
"entities" can be used to store arbitrary data. They can be referenced in
|
"entities" can be used to store arbitrary data. They can be referenced in
|
||||||
annotations, info view, or elsewhere. Each entry defines a specific class
|
annotations, info view, or elsewhere. Each entry defines a specific class
|
||||||
of entity object, its properties and their types (for example an "actor"
|
of entity object, its properties and their types (for example an "actor"
|
||||||
|
@ -882,16 +1137,27 @@
|
||||||
"calendarFind": "",
|
"calendarFind": "",
|
||||||
"calendarSelection": "",
|
"calendarSelection": "",
|
||||||
"clipColumns": 2,
|
"clipColumns": 2,
|
||||||
|
"collectionColumns": ["title", "id", "extension", "dimensions", "size", "description", "matches", "user", "created", "modified"],
|
||||||
|
"collectionColumnWidth": {},
|
||||||
|
"collectionSelection": [],
|
||||||
|
"collectionSort": [
|
||||||
|
{"key": "title", "operator": "+"},
|
||||||
|
{"key": "extension", "operator": "+"}
|
||||||
|
],
|
||||||
|
"collectionView": "grid",
|
||||||
|
"collections": {},
|
||||||
"columns": {
|
"columns": {
|
||||||
"Colors": {
|
"Colors": {
|
||||||
"columns": ["title", "source", "project", "language", "hue", "saturation", "brightness"],
|
"columns": ["title", "director", "language", "hue", "saturation", "brightness"],
|
||||||
"columnWidth": {}
|
"columnWidth": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"document": "",
|
||||||
"documents": {},
|
"documents": {},
|
||||||
"documentSize": 256,
|
"documentSize": 256,
|
||||||
|
"documentView": "view",
|
||||||
"documentsSelection": {},
|
"documentsSelection": {},
|
||||||
"documentsSort": [{"key": "name", "operator": "+"}],
|
"documentsSort": [{"key": "title", "operator": "+"}],
|
||||||
"documentsView": "grid",
|
"documentsView": "grid",
|
||||||
"edit": "",
|
"edit": "",
|
||||||
"edits": {},
|
"edits": {},
|
||||||
|
@ -917,6 +1183,7 @@
|
||||||
],
|
],
|
||||||
"filtersSize": 176,
|
"filtersSize": 176,
|
||||||
"find": {"conditions": [], "operator": "&"},
|
"find": {"conditions": [], "operator": "&"},
|
||||||
|
"findDocuments": {"conditions": [], "operator": "&"},
|
||||||
"followPlayer": true,
|
"followPlayer": true,
|
||||||
"help": "",
|
"help": "",
|
||||||
"icons": "frames",
|
"icons": "frames",
|
||||||
|
@ -937,7 +1204,6 @@
|
||||||
"page": "",
|
"page": "",
|
||||||
"part": {
|
"part": {
|
||||||
"api": "",
|
"api": "",
|
||||||
"documents": "",
|
|
||||||
"entities": "",
|
"entities": "",
|
||||||
"faq": "",
|
"faq": "",
|
||||||
"help": "",
|
"help": "",
|
||||||
|
@ -981,7 +1247,7 @@
|
||||||
"featured": true,
|
"featured": true,
|
||||||
"volumes": true
|
"volumes": true
|
||||||
},
|
},
|
||||||
"texts": {
|
"documents": {
|
||||||
"personal": true,
|
"personal": true,
|
||||||
"favorite": true,
|
"favorite": true,
|
||||||
"featured": true
|
"featured": true
|
||||||
|
|
|
@ -39,11 +39,13 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
*/
|
*/
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"canAddItems": {"member": true, "staff": true, "admin": true},
|
"canAddItems": {"member": true, "staff": true, "admin": true},
|
||||||
|
"canAddDocuments": {"member": true, "staff": true, "admin": true},
|
||||||
"canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
"canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canEditAnnotations": {"staff": true, "admin": true},
|
"canEditAnnotations": {"staff": true, "admin": true},
|
||||||
"canEditDocuments": {"staff": true, "admin": true},
|
"canEditDocuments": {"staff": true, "admin": true},
|
||||||
"canEditEntities": {"staff": true, "admin": true},
|
"canEditEntities": {"staff": true, "admin": true},
|
||||||
"canEditEvents": {"staff": true, "admin": true},
|
"canEditEvents": {"staff": true, "admin": true},
|
||||||
|
"canEditFeaturedCollections": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
"canEditFeaturedEdits": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedLists": {"staff": true, "admin": true},
|
"canEditFeaturedLists": {"staff": true, "admin": true},
|
||||||
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
"canEditFeaturedTexts": {"staff": true, "admin": true},
|
||||||
|
@ -64,12 +66,14 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
"canPlayClips": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
"canPlayClips": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canPlayVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
"canPlayVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canReadText": {"guest": 0, "member": 0, "staff": 1, "admin": 1},
|
"canReadText": {"guest": 0, "member": 0, "staff": 1, "admin": 1},
|
||||||
"canRemoveItems": {"admin": true},
|
"canRemoveItems": {"staff": true, "admin": true},
|
||||||
|
"canRemoveDocuments": {"staff": true, "admin": true},
|
||||||
"canSeeAccessed": {"staff": true, "admin": true},
|
"canSeeAccessed": {"staff": true, "admin": true},
|
||||||
"canSeeAllTasks": {"staff": true, "admin": true},
|
"canSeeAllTasks": {"staff": true, "admin": true},
|
||||||
"canSeeDebugMenu": {"staff": true, "admin": true},
|
"canSeeDebugMenu": {"staff": true, "admin": true},
|
||||||
"canSeeExtraItemViews": {"staff": true, "admin": true},
|
"canSeeExtraItemViews": {"staff": true, "admin": true},
|
||||||
"canSeeMedia": {"staff": true, "admin": true},
|
"canSeeMedia": {"staff": true, "admin": true},
|
||||||
|
"canSeeDocument": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canSeeItem": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
"canSeeItem": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
|
||||||
"canSeeSize": {"staff": true, "admin": true},
|
"canSeeSize": {"staff": true, "admin": true},
|
||||||
"canSeeSoftwareVersion": {"staff": true, "admin": true},
|
"canSeeSoftwareVersion": {"staff": true, "admin": true},
|
||||||
|
@ -96,6 +100,257 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
*/
|
*/
|
||||||
"clipLayers": ["publicnotes", "keywords", "subtitles"],
|
"clipLayers": ["publicnotes", "keywords", "subtitles"],
|
||||||
/*
|
/*
|
||||||
|
"documentKeys" defines the metadata associated with each document. Required keys
|
||||||
|
are "*", "id" and "title".
|
||||||
|
A documentKey must have the following properties:
|
||||||
|
"id": The unique id of the key (as used by the server)
|
||||||
|
"title": The title of the key (as displayed by the client)
|
||||||
|
"type": Can be "boolean", "date", "enum", "float", "hue", "integer",
|
||||||
|
"layer", "string", "text", "time" or ["..."] (list of values of
|
||||||
|
this type). If type is "layer", this is a reference to the
|
||||||
|
annotations layer with the same id.
|
||||||
|
and can have any of the following properties:
|
||||||
|
"additionalSort": Ordered list of {key, operator} objects, where key is
|
||||||
|
another itemKey and operator is "+" or "-". This can be used to
|
||||||
|
override user.ui.listSort when results are sorted by this key.
|
||||||
|
"autocomplete": If true, the find element will provide autocomplete
|
||||||
|
"autocompleteSort": Sort order of autocomplete suggestions
|
||||||
|
"capability": A capability required to see data for this key
|
||||||
|
"columnRequired": If true, the column can't be removed from list view
|
||||||
|
"columnWidth": Default column width in px. If absent, no column for
|
||||||
|
this key can be added in list view.
|
||||||
|
"filter": If true, one can filter results by this key
|
||||||
|
"find": If true, this key will appear as an option in the find element
|
||||||
|
"flag": Can be "country" or "language". If set (and filter is true), a
|
||||||
|
flag icon corresponding to the field's value will be displayed.
|
||||||
|
"format": {type: string, args: [value, value, ...]}, used for special
|
||||||
|
formatting. This will invoke Ox.formatType(args). For details, see
|
||||||
|
https://oxjs.org/#doc/Ox.formatArea etc.
|
||||||
|
"secondaryId": If true, loading the URL "/value" will redirect to the
|
||||||
|
corresponding item, in case there is an exact match for this key
|
||||||
|
"sort": If true, one can sort results by this key
|
||||||
|
"sortOperator": Sort order ("+" or "-"), in case it differs from the
|
||||||
|
default for the key's type ("+" for strings, "-" for numbers)
|
||||||
|
"sortType": Special sort type ("person" or "title") which can be
|
||||||
|
further configured in "Manage Names" or "Manage Titles"
|
||||||
|
"value": {key: string, type: string} or {layer: string, type: string},
|
||||||
|
for keys whose value is derived from other keys or layers (like
|
||||||
|
"number of actors" or "words per minute"). Possible values for type
|
||||||
|
are "length", "lengthperminute", "words", and "wordsperminute".
|
||||||
|
Alternatively, "value" can be set to the string "capability", which
|
||||||
|
results in an itemKey whose boolean value indicates the presence or
|
||||||
|
absence of a userLevel-dependent capability. This can be used to
|
||||||
|
create queries and lists like "all items this user can play" etc.
|
||||||
|
"values": [value, value, ...] Ordered list of values, in case "type" is
|
||||||
|
"enum"
|
||||||
|
*/
|
||||||
|
"documentKeys": [
|
||||||
|
{
|
||||||
|
"id": "*",
|
||||||
|
"title": "All",
|
||||||
|
"type": "text",
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "title",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Title",
|
||||||
|
"type": "string",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "title",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "type",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Type",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "author",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Author",
|
||||||
|
"type": ["string"],
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"sortType": "person",
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "publisher",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Publisher",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "place",
|
||||||
|
"title": "Place",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "date",
|
||||||
|
"title": "Date",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 120,
|
||||||
|
//"format": {"type": "date", "args": ["%a, %b %e, %Y"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "series",
|
||||||
|
"title": "Series",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edition",
|
||||||
|
"title": "Edition",
|
||||||
|
"type": "string",
|
||||||
|
"columnWidth": 128,
|
||||||
|
"find": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "language",
|
||||||
|
"title": "Language",
|
||||||
|
"type": ["string"],
|
||||||
|
"columnWidth": 128,
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "id",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "ID",
|
||||||
|
"type": "string",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "extension",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Extension",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dimensions",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Dimensions",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "size",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Size",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"format": {"type": "value", "args": ["B"]},
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "description",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "Description",
|
||||||
|
"type": "text",
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "matches",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Matches",
|
||||||
|
"type": "integer",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "user",
|
||||||
|
"operator": "+",
|
||||||
|
"title": "User",
|
||||||
|
"type": "string",
|
||||||
|
"filter": true,
|
||||||
|
"find": true,
|
||||||
|
"sort": true,
|
||||||
|
"autocomplete": true,
|
||||||
|
"columnWidth": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "created",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Created",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "modified",
|
||||||
|
"operator": "-",
|
||||||
|
"title": "Modified",
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"type": "date",
|
||||||
|
"sort": true,
|
||||||
|
"columnWidth": 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "accessed",
|
||||||
|
"title": "Last Accessed",
|
||||||
|
"type": "date",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 150,
|
||||||
|
"format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timesaccessed",
|
||||||
|
"title": "Times Accessed",
|
||||||
|
"type": "integer",
|
||||||
|
"capability": "canSeeAccessed",
|
||||||
|
"columnWidth": 60,
|
||||||
|
"format": {"type": "number", "args": []},
|
||||||
|
"sort": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rightslevel",
|
||||||
|
"title": "Rights Level",
|
||||||
|
"type": "enum",
|
||||||
|
"columnWidth": 90,
|
||||||
|
"format": {"type": "ColorLevel", "args": [
|
||||||
|
["Public", "Out of Copyright", "Under Copyright", "Private"]
|
||||||
|
]},
|
||||||
|
"sort": true,
|
||||||
|
"sortOperator": "+",
|
||||||
|
"values": ["Public", "Out of Copyright", "Under Copyright", "Private", "Unknown"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
/*
|
||||||
"entities" can be used to store arbitrary data. They can be referenced in
|
"entities" can be used to store arbitrary data. They can be referenced in
|
||||||
annotations, info view, or elsewhere. Each entry defines a specific class
|
annotations, info view, or elsewhere. Each entry defines a specific class
|
||||||
of entity object, its properties and their types (for example an "actor"
|
of entity object, its properties and their types (for example an "actor"
|
||||||
|
@ -822,16 +1077,27 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
"calendarFind": "",
|
"calendarFind": "",
|
||||||
"calendarSelection": "",
|
"calendarSelection": "",
|
||||||
"clipColumns": 2,
|
"clipColumns": 2,
|
||||||
|
"collectionColumns": ["title", "id", "extension", "dimensions", "size", "description", "matches", "user", "created", "modified"],
|
||||||
|
"collectionColumnWidth": {},
|
||||||
|
"collectionSelection": [],
|
||||||
|
"collectionSort": [
|
||||||
|
{"key": "title", "operator": "+"},
|
||||||
|
{"key": "extension", "operator": "+"}
|
||||||
|
],
|
||||||
|
"collectionView": "grid",
|
||||||
|
"collections": {},
|
||||||
"columns": {
|
"columns": {
|
||||||
"Colors": {
|
"Colors": {
|
||||||
"columns": ["title", "director", "language", "hue", "saturation", "brightness"],
|
"columns": ["title", "director", "language", "hue", "saturation", "brightness"],
|
||||||
"columnWidth": {}
|
"columnWidth": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"document": "",
|
||||||
"documents": {},
|
"documents": {},
|
||||||
"documentSize": 256,
|
"documentSize": 256,
|
||||||
|
"documentView": "view",
|
||||||
"documentsSelection": {},
|
"documentsSelection": {},
|
||||||
"documentsSort": [{"key": "name", "operator": "+"}],
|
"documentsSort": [{"key": "title", "operator": "+"}],
|
||||||
"documentsView": "grid",
|
"documentsView": "grid",
|
||||||
"edit": "",
|
"edit": "",
|
||||||
"edits": {},
|
"edits": {},
|
||||||
|
@ -857,6 +1123,7 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
],
|
],
|
||||||
"filtersSize": 176,
|
"filtersSize": 176,
|
||||||
"find": {"conditions": [], "operator": "&"},
|
"find": {"conditions": [], "operator": "&"},
|
||||||
|
"findDocuments": {"conditions": [], "operator": "&"},
|
||||||
"followPlayer": true,
|
"followPlayer": true,
|
||||||
"help": "",
|
"help": "",
|
||||||
"icons": "posters",
|
"icons": "posters",
|
||||||
|
@ -877,7 +1144,6 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
"page": "",
|
"page": "",
|
||||||
"part": {
|
"part": {
|
||||||
"api": "",
|
"api": "",
|
||||||
"documents": "",
|
|
||||||
"entities": "",
|
"entities": "",
|
||||||
"faq": "",
|
"faq": "",
|
||||||
"help": "",
|
"help": "",
|
||||||
|
@ -920,7 +1186,7 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
||||||
"featured": true,
|
"featured": true,
|
||||||
"volumes": true
|
"volumes": true
|
||||||
},
|
},
|
||||||
"texts": {
|
"documents": {
|
||||||
"personal": true,
|
"personal": true,
|
||||||
"favorite": true,
|
"favorite": true,
|
||||||
"featured": true
|
"featured": true
|
||||||
|
|
0
pandora/document/management/__init__.py
Normal file
0
pandora/document/management/__init__.py
Normal file
0
pandora/document/management/commands/__init__.py
Normal file
0
pandora/document/management/commands/__init__.py
Normal file
29
pandora/document/management/commands/rebuild_documentfind.py
Normal file
29
pandora/document/management/commands/rebuild_documentfind.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import connection, transaction
|
||||||
|
from django.db.models import fields
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
settings.RELOAD_CONFIG = False
|
||||||
|
import app.monkey_patch
|
||||||
|
from ... import models
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'update document find and sort values'
|
||||||
|
args = ''
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
ids = [i['id'] for i in models.Document.objects.all().values('id')]
|
||||||
|
for id in ids:
|
||||||
|
try:
|
||||||
|
i = models.Document.objects.get(id=id)
|
||||||
|
if i.file:
|
||||||
|
i.get_info()
|
||||||
|
i.get_ratio()
|
||||||
|
#print(i, i.ratio)
|
||||||
|
i.save()
|
||||||
|
except:
|
||||||
|
pass
|
|
@ -1,6 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
|
from six import string_types
|
||||||
from django.db.models import Q, Manager
|
from django.db.models import Q, Manager
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from oxdjango.query import QuerySet
|
from oxdjango.query import QuerySet
|
||||||
|
@ -8,14 +12,31 @@ from oxdjango.query import QuerySet
|
||||||
import entity.managers
|
import entity.managers
|
||||||
from oxdjango.managers import get_operator
|
from oxdjango.managers import get_operator
|
||||||
|
|
||||||
|
from documentcollection.models import Collection
|
||||||
|
from item import utils
|
||||||
|
|
||||||
|
|
||||||
keymap = {
|
keymap = {
|
||||||
'user': 'user__username',
|
|
||||||
'item': 'items__public_id',
|
'item': 'items__public_id',
|
||||||
}
|
}
|
||||||
default_key = 'name'
|
default_key = 'title'
|
||||||
|
|
||||||
def parseCondition(condition, user, item=None):
|
def get_key_type(k):
|
||||||
|
key_type = (utils.get_by_id(settings.CONFIG['documentKeys'], k) or {'type': 'string'}).get('type')
|
||||||
|
if isinstance(key_type, list):
|
||||||
|
key_type = key_type[0]
|
||||||
|
key_type = {
|
||||||
|
'title': 'string',
|
||||||
|
'person': 'string',
|
||||||
|
'text': 'string',
|
||||||
|
'year': 'string',
|
||||||
|
'length': 'string',
|
||||||
|
'layer': 'string',
|
||||||
|
'list': 'list',
|
||||||
|
}.get(key_type, key_type)
|
||||||
|
return key_type
|
||||||
|
|
||||||
|
def parseCondition(condition, user, item=None, owner=None):
|
||||||
'''
|
'''
|
||||||
'''
|
'''
|
||||||
k = condition.get('key', default_key)
|
k = condition.get('key', default_key)
|
||||||
|
@ -33,17 +54,47 @@ def parseCondition(condition, user, item=None):
|
||||||
op = '='
|
op = '='
|
||||||
|
|
||||||
if op.startswith('!'):
|
if op.startswith('!'):
|
||||||
return ~buildCondition(k, op[1:], v)
|
return buildCondition(k, op[1:], v, user, True, owner=owner)
|
||||||
else:
|
else:
|
||||||
return buildCondition(k, op, v)
|
return buildCondition(k, op, v, user, owner=owner)
|
||||||
|
|
||||||
|
def buildCondition(k, op, v, user, exclude=False, owner=None):
|
||||||
def buildCondition(k, op, v):
|
|
||||||
import entity.models
|
import entity.models
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
# fixme: frontend should never call with list
|
||||||
|
if k == 'list':
|
||||||
|
print('fixme: frontend should never call with list', k, op, v)
|
||||||
|
k = 'collection'
|
||||||
|
|
||||||
|
key_type = get_key_type(k)
|
||||||
|
facet_keys = models.Document.facet_keys
|
||||||
if k == 'id':
|
if k == 'id':
|
||||||
v = ox.fromAZ(v)
|
v = ox.fromAZ(v)
|
||||||
return Q(**{k: v})
|
q = Q(**{k: v})
|
||||||
if isinstance(v, bool):
|
if exclude:
|
||||||
|
q = ~Q(id__in=models.Document.objects.filter(q))
|
||||||
|
return q
|
||||||
|
elif k == 'groups':
|
||||||
|
if op == '==' and v == '$my':
|
||||||
|
if not owner:
|
||||||
|
owner = user
|
||||||
|
groups = owner.groups.all()
|
||||||
|
else:
|
||||||
|
key = 'name' + get_operator(op)
|
||||||
|
groups = Group.objects.filter(**{key: v})
|
||||||
|
if not groups.count():
|
||||||
|
return Q(id=0)
|
||||||
|
q = Q(groups__in=groups)
|
||||||
|
if exclude:
|
||||||
|
q = ~q
|
||||||
|
return q
|
||||||
|
elif k in ('oshash', 'items__public_id'):
|
||||||
|
q = Q(**{k: v})
|
||||||
|
if exclude:
|
||||||
|
q = ~Q(id__in=models.Document.objects.filter(q))
|
||||||
|
return q
|
||||||
|
elif isinstance(v, bool):
|
||||||
key = k
|
key = k
|
||||||
elif k == 'entity':
|
elif k == 'entity':
|
||||||
entity_key, entity_v = entity.managers.namePredicate(op, v)
|
entity_key, entity_v = entity.managers.namePredicate(op, v)
|
||||||
|
@ -51,13 +102,87 @@ def buildCondition(k, op, v):
|
||||||
v = entity.models.DocumentProperties.objects.filter(**{
|
v = entity.models.DocumentProperties.objects.filter(**{
|
||||||
'entity__' + entity_key: entity_v
|
'entity__' + entity_key: entity_v
|
||||||
}).values_list('document_id', flat=True)
|
}).values_list('document_id', flat=True)
|
||||||
else:
|
elif k == 'collection':
|
||||||
key = k + get_operator(op, 'istr')
|
q = Q(id=0)
|
||||||
|
l = v.split(":", 1)
|
||||||
|
if len(l) >= 2:
|
||||||
|
lqs = list(Collection.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) is False:
|
||||||
|
data = l.query
|
||||||
|
q = parseConditions(data.get('conditions', []),
|
||||||
|
data.get('operator', '&'),
|
||||||
|
user, owner=l.user)
|
||||||
|
else:
|
||||||
|
q = Q(id__in=l.documents.all())
|
||||||
|
else:
|
||||||
|
q = Q(id=0)
|
||||||
|
return q
|
||||||
|
elif key_type == 'boolean':
|
||||||
|
q = Q(**{'find__key': k, 'find__value': v})
|
||||||
|
if exclude:
|
||||||
|
q = ~Q(id__in=models.Document.objects.filter(q))
|
||||||
|
return q
|
||||||
|
elif key_type == "string":
|
||||||
|
in_find = True
|
||||||
|
if in_find:
|
||||||
|
value_key = 'find__value'
|
||||||
|
else:
|
||||||
|
value_key = k
|
||||||
|
if isinstance(v, string_types):
|
||||||
|
v = unicodedata.normalize('NFKD', v).lower()
|
||||||
|
if k in facet_keys:
|
||||||
|
in_find = False
|
||||||
|
facet_value = 'facets__value' + get_operator(op, 'istr')
|
||||||
|
v = models.Document.objects.filter(**{'facets__key': k, facet_value: v})
|
||||||
|
value_key = 'id__in'
|
||||||
|
else:
|
||||||
|
value_key = value_key + get_operator(op)
|
||||||
|
k = str(k)
|
||||||
|
value_key = str(value_key)
|
||||||
|
if k == '*':
|
||||||
|
q = Q(**{value_key: v})
|
||||||
|
elif in_find:
|
||||||
|
q = Q(**{'find__key': k, value_key: v})
|
||||||
|
else:
|
||||||
|
q = Q(**{value_key: v})
|
||||||
|
if exclude:
|
||||||
|
q = ~Q(id__in=models.Document.objects.filter(q))
|
||||||
|
return q
|
||||||
|
elif key_type == 'date':
|
||||||
|
def parse_date(d):
|
||||||
|
while len(d) < 3:
|
||||||
|
d.append(1)
|
||||||
|
return datetime(*[int(i) for i in d])
|
||||||
|
|
||||||
|
#using sort here since find only contains strings
|
||||||
|
v = parse_date(v.split('-'))
|
||||||
|
vk = 'sort__%s%s' % (k, get_operator(op, 'int'))
|
||||||
|
vk = str(vk)
|
||||||
|
q = Q(**{vk: v})
|
||||||
|
if exclude:
|
||||||
|
q = ~q
|
||||||
|
return q
|
||||||
|
else: # integer, float, list, time
|
||||||
|
#use sort table here
|
||||||
|
if key_type == 'time':
|
||||||
|
v = int(utils.parse_time(v))
|
||||||
|
|
||||||
|
vk = 'sort__%s%s' % (k, get_operator(op, 'int'))
|
||||||
|
vk = str(vk)
|
||||||
|
q = Q(**{vk: v})
|
||||||
|
if exclude:
|
||||||
|
q = ~q
|
||||||
|
return q
|
||||||
key = str(key)
|
key = str(key)
|
||||||
return Q(**{key: v})
|
q = Q(**{key: v})
|
||||||
|
if exclude:
|
||||||
|
q = ~q
|
||||||
|
return q
|
||||||
|
|
||||||
|
|
||||||
def parseConditions(conditions, operator, user, item=None):
|
def parseConditions(conditions, operator, user, item=None, owner=None):
|
||||||
'''
|
'''
|
||||||
conditions: [
|
conditions: [
|
||||||
{
|
{
|
||||||
|
@ -80,12 +205,12 @@ def parseConditions(conditions, operator, user, item=None):
|
||||||
for condition in conditions:
|
for condition in conditions:
|
||||||
if 'conditions' in condition:
|
if 'conditions' in condition:
|
||||||
q = parseConditions(condition['conditions'],
|
q = parseConditions(condition['conditions'],
|
||||||
condition.get('operator', '&'), user, item)
|
condition.get('operator', '&'), user, item, owner=owner)
|
||||||
if q:
|
if q:
|
||||||
conn.append(q)
|
conn.append(q)
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
conn.append(parseCondition(condition, user, item))
|
conn.append(parseCondition(condition, user, item, owner=owner))
|
||||||
if conn:
|
if conn:
|
||||||
q = conn[0]
|
q = conn[0]
|
||||||
for c in conn[1:]:
|
for c in conn[1:]:
|
||||||
|
@ -133,4 +258,21 @@ class DocumentManager(Manager):
|
||||||
if conditions:
|
if conditions:
|
||||||
qs = qs.filter(conditions)
|
qs = qs.filter(conditions)
|
||||||
|
|
||||||
|
#anonymous can only see public items
|
||||||
|
if not user or user.is_anonymous():
|
||||||
|
level = 'guest'
|
||||||
|
allowed_level = settings.CONFIG['capabilities']['canSeeDocument'][level]
|
||||||
|
qs = qs.filter(rightslevel__lte=allowed_level)
|
||||||
|
rendered_q = Q(rendered=True)
|
||||||
|
#users can see public items, there own items and items of there groups
|
||||||
|
else:
|
||||||
|
level = user.profile.get_level()
|
||||||
|
allowed_level = settings.CONFIG['capabilities']['canSeeDocument'][level]
|
||||||
|
q = Q(rightslevel__lte=allowed_level) | Q(user=user)
|
||||||
|
rendered_q = Q(rendered=True) | Q(user=user)
|
||||||
|
if user.groups.count():
|
||||||
|
q |= Q(groups__in=user.groups.all())
|
||||||
|
rendered_q |= Q(groups__in=user.groups.all())
|
||||||
|
qs = qs.filter(q)
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
26
pandora/document/migrations/0003_new_fields.py
Normal file
26
pandora/document/migrations/0003_new_fields.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-04 16:31
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import oxdjango.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('document', '0002_auto_20160219_1537'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='document',
|
||||||
|
name='data',
|
||||||
|
field=oxdjango.fields.DictField(default={}),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='document',
|
||||||
|
name='rightslevel',
|
||||||
|
field=models.IntegerField(db_index=True, default=0),
|
||||||
|
),
|
||||||
|
]
|
109
pandora/document/migrations/0004_migrate_text.py
Normal file
109
pandora/document/migrations/0004_migrate_text.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
import ox
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db.models import Max
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
import oxdjango.fields
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_texts(apps, schema_editor):
|
||||||
|
import os
|
||||||
|
import ox
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import document.models
|
||||||
|
from document import utils
|
||||||
|
|
||||||
|
Text = apps.get_model("text", "Text")
|
||||||
|
Document = apps.get_model("document", "Document")
|
||||||
|
Collection = apps.get_model("documentcollection", "Collection")
|
||||||
|
CollectionDocument = apps.get_model("documentcollection", "CollectionDocument")
|
||||||
|
User = apps.get_model("auth", "User")
|
||||||
|
|
||||||
|
def add(self, document):
|
||||||
|
q = self.documents.filter(id=document.id)
|
||||||
|
if q.count() == 0:
|
||||||
|
l = CollectionDocument()
|
||||||
|
l.collection = self
|
||||||
|
l.document = document
|
||||||
|
l.index = CollectionDocument.objects.filter(collection=self).aggregate(Max('index'))['index__max']
|
||||||
|
if l.index is None:
|
||||||
|
l.index = 0
|
||||||
|
else:
|
||||||
|
l.index += 1
|
||||||
|
l.save()
|
||||||
|
|
||||||
|
def path(self, name=''):
|
||||||
|
h = ox.toAZ(self.id)
|
||||||
|
h = (7-len(h))*'0' + h
|
||||||
|
return os.path.join('documents', h[:2], h[2:4], h[4:6], h[6:], name)
|
||||||
|
|
||||||
|
def update_info(self):
|
||||||
|
pdf = self.file.path
|
||||||
|
page = 1
|
||||||
|
image = os.path.join(os.path.dirname(pdf), '1024p%d.jpg' % page)
|
||||||
|
utils.extract_pdfpage(pdf, image, page)
|
||||||
|
self.pages = utils.pdfpages(self.file.path)
|
||||||
|
if os.path.exists(image):
|
||||||
|
size = Image.open(image).size
|
||||||
|
self.ratio = size[0] / size[1]
|
||||||
|
|
||||||
|
if Text.objects.filter(status='featured').count():
|
||||||
|
first_user = User.objects.all()[0]
|
||||||
|
featured, created = Collection.objects.get_or_create(user=first_user, name='Featured Texts')
|
||||||
|
if created:
|
||||||
|
featured.status = 'featured'
|
||||||
|
featured.save()
|
||||||
|
|
||||||
|
for t in Text.objects.all():
|
||||||
|
d = Document()
|
||||||
|
d.extension = t.type
|
||||||
|
if t.name == '':
|
||||||
|
d.name = 'Index'
|
||||||
|
else:
|
||||||
|
d.name = t.name
|
||||||
|
d.user = t.user
|
||||||
|
d.description = t.description
|
||||||
|
d.data['text'] = t.text
|
||||||
|
d.data['embeds'] = t.embeds
|
||||||
|
d.save()
|
||||||
|
if t.type == 'pdf':
|
||||||
|
d.file.name = path(d, 'data.pdf')
|
||||||
|
os.makedirs(os.path.dirname(d.file.path))
|
||||||
|
shutil.copy2(t.file.path, d.file.path)
|
||||||
|
d.oshash = ox.oshash(d.file.path)
|
||||||
|
update_info(d)
|
||||||
|
d.save()
|
||||||
|
Document.objects.filter(id=d.id).update(created=t.created, modified=t.modified)
|
||||||
|
c, created = Collection.objects.get_or_create(user=t.user, name='Texts')
|
||||||
|
add(c, d)
|
||||||
|
if t.status == 'featured':
|
||||||
|
add(featured, d)
|
||||||
|
for user in t.subscribed_users.all():
|
||||||
|
favorite, created = Collection.objects.get_or_create(user=user, name='Favorite Texts')
|
||||||
|
add(favorite, d)
|
||||||
|
|
||||||
|
'''
|
||||||
|
for d in document.models.Document.objects.filter(id__in=fix_info):
|
||||||
|
d.get_info()
|
||||||
|
d.get_ratio()
|
||||||
|
d.save()
|
||||||
|
'''
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '__first__'),
|
||||||
|
('text', '__first__'),
|
||||||
|
('document', '0003_new_fields'),
|
||||||
|
('documentcollection', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrate_texts),
|
||||||
|
]
|
32
pandora/document/migrations/0005_auto_20161008_1232.py
Normal file
32
pandora/document/migrations/0005_auto_20161008_1232.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-08 12:32
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('document', '0004_migrate_text'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Access',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('access', models.DateTimeField(auto_now=True)),
|
||||||
|
('accessed', models.IntegerField(default=0)),
|
||||||
|
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accessed', to='document.Document')),
|
||||||
|
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accessed_documents', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='access',
|
||||||
|
unique_together=set([('document', 'user')]),
|
||||||
|
),
|
||||||
|
]
|
52
pandora/document/migrations/0006_auto_20161026_1259.py
Normal file
52
pandora/document/migrations/0006_auto_20161026_1259.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-26 12:59
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('document', '0005_auto_20161008_1232'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Find',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('key', models.CharField(db_index=True, max_length=200)),
|
||||||
|
('value', models.TextField(blank=True, db_index=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Sort',
|
||||||
|
fields=[
|
||||||
|
('document', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='sort', serialize=False, to='document.Document')),
|
||||||
|
('created', models.DateTimeField(blank=True, db_index=True, null=True)),
|
||||||
|
('name', models.CharField(db_index=True, max_length=1000, null=True)),
|
||||||
|
('id', models.CharField(db_index=True, max_length=1000, null=True)),
|
||||||
|
('extension', models.CharField(db_index=True, max_length=1000, null=True)),
|
||||||
|
('dimensions', models.BigIntegerField(blank=True, db_index=True, null=True)),
|
||||||
|
('size', models.BigIntegerField(blank=True, db_index=True, null=True)),
|
||||||
|
('description', models.CharField(db_index=True, max_length=1000, null=True)),
|
||||||
|
('matches', models.BigIntegerField(blank=True, db_index=True, null=True)),
|
||||||
|
('user', models.CharField(db_index=True, max_length=1000, null=True)),
|
||||||
|
('modified', models.DateTimeField(blank=True, db_index=True, null=True)),
|
||||||
|
('accessed', models.DateTimeField(blank=True, db_index=True, null=True)),
|
||||||
|
('timesaccessed', models.BigIntegerField(blank=True, db_index=True, null=True)),
|
||||||
|
('rightslevel', models.BigIntegerField(blank=True, db_index=True, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='find',
|
||||||
|
name='document',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='find', to='document.Document'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='find',
|
||||||
|
unique_together=set([('document', 'key')]),
|
||||||
|
),
|
||||||
|
]
|
55
pandora/document/migrations/0007_auto_20161026_1559.py
Normal file
55
pandora/document/migrations/0007_auto_20161026_1559.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-26 15:59
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
def migrate_data(apps, schema_editor):
|
||||||
|
Document = apps.get_model('document', 'Document')
|
||||||
|
for d in Document.objects.all():
|
||||||
|
if 'title' not in d.data:
|
||||||
|
d.data['title'] = d.name
|
||||||
|
if 'description' not in d.data:
|
||||||
|
d.data['description'] = d.description
|
||||||
|
d.save()
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('document', '0006_auto_20161026_1259'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrate_data),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='document',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='document',
|
||||||
|
unique_together=set([]),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='document',
|
||||||
|
name='description_sort',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='document',
|
||||||
|
name='dimensions_sort',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='document',
|
||||||
|
name='name_sort',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='document',
|
||||||
|
name='name',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='document',
|
||||||
|
name='description',
|
||||||
|
),
|
||||||
|
]
|
30
pandora/document/migrations/0008_auto_20161026_1625.py
Normal file
30
pandora/document/migrations/0008_auto_20161026_1625.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-26 16:25
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('document', '0007_auto_20161026_1559'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Facet',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('key', models.CharField(db_index=True, max_length=200)),
|
||||||
|
('value', models.CharField(db_index=True, max_length=1000)),
|
||||||
|
('sortvalue', models.CharField(db_index=True, max_length=1000)),
|
||||||
|
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='facets', to='document.Document')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='facet',
|
||||||
|
unique_together=set([('document', 'key', 'value')]),
|
||||||
|
),
|
||||||
|
]
|
21
pandora/document/migrations/0009_add_group.py
Normal file
21
pandora/document/migrations/0009_add_group.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-27 12:27
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '__first__'),
|
||||||
|
('document', '0008_auto_20161026_1625'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='document',
|
||||||
|
name='groups',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='documents', to='auth.Group'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -5,17 +5,23 @@ from __future__ import division, print_function, absolute_import
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from six.moves.urllib.parse import quote, unquote
|
from six.moves.urllib.parse import quote, unquote
|
||||||
from django.db import models
|
from django.db import models, transaction
|
||||||
from django.db.models import Max
|
from django.db.models import Q, Sum, Max
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User, Group
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import ox
|
import ox
|
||||||
|
|
||||||
|
|
||||||
|
from oxdjango import fields
|
||||||
|
from oxdjango.sortmodel import get_sort_field
|
||||||
|
from person.models import get_name_sort
|
||||||
from item.models import Item
|
from item.models import Item
|
||||||
from archive.extract import resize_image
|
from archive.extract import resize_image
|
||||||
from archive.chunk import save_chunk
|
from archive.chunk import save_chunk
|
||||||
|
@ -23,57 +29,249 @@ from archive.chunk import save_chunk
|
||||||
from . import managers
|
from . import managers
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
def get_path(f, x): return f.path(x)
|
|
||||||
|
def get_path(f, x):
|
||||||
|
return f.path(x)
|
||||||
|
|
||||||
class Document(models.Model):
|
class Document(models.Model):
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ("user", "name", "extension")
|
|
||||||
|
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
user = models.ForeignKey(User, related_name='files')
|
user = models.ForeignKey(User, related_name='documents')
|
||||||
name = models.CharField(max_length=255)
|
groups = models.ManyToManyField(Group, blank=True, related_name='documents')
|
||||||
|
|
||||||
extension = models.CharField(max_length=255)
|
extension = models.CharField(max_length=255)
|
||||||
size = models.IntegerField(default=0)
|
size = models.IntegerField(default=0)
|
||||||
matches = models.IntegerField(default=0)
|
matches = models.IntegerField(default=0)
|
||||||
ratio = models.FloatField(default=1)
|
ratio = models.FloatField(default=640/1024)
|
||||||
pages = models.IntegerField(default=-1)
|
pages = models.IntegerField(default=-1)
|
||||||
width = models.IntegerField(default=-1)
|
width = models.IntegerField(default=-1)
|
||||||
height = models.IntegerField(default=-1)
|
height = models.IntegerField(default=-1)
|
||||||
description = models.TextField(default="")
|
|
||||||
oshash = models.CharField(max_length=16, unique=True, null=True)
|
oshash = models.CharField(max_length=16, unique=True, null=True)
|
||||||
|
|
||||||
file = models.FileField(default=None, blank=True,null=True, upload_to=get_path)
|
file = models.FileField(default=None, blank=True, null=True, upload_to=get_path)
|
||||||
|
|
||||||
objects = managers.DocumentManager()
|
objects = managers.DocumentManager()
|
||||||
uploading = models.BooleanField(default = False)
|
uploading = models.BooleanField(default=False)
|
||||||
|
|
||||||
name_sort = models.CharField(max_length=255, null=True)
|
|
||||||
description_sort = models.CharField(max_length=512, null=True)
|
|
||||||
dimensions_sort = models.CharField(max_length=512)
|
|
||||||
|
|
||||||
items = models.ManyToManyField(Item, through='ItemProperties', related_name='documents')
|
items = models.ManyToManyField(Item, through='ItemProperties', related_name='documents')
|
||||||
|
|
||||||
|
rightslevel = models.IntegerField(db_index=True, default=0)
|
||||||
|
data = fields.DictField(default={})
|
||||||
|
|
||||||
|
def update_access(self, user):
|
||||||
|
if not user.is_authenticated():
|
||||||
|
user = None
|
||||||
|
access, created = Access.objects.get_or_create(document=self, user=user)
|
||||||
|
if not created:
|
||||||
|
access.save()
|
||||||
|
|
||||||
|
def update_facet(self, key):
|
||||||
|
current_values = self.get_value(key, [])
|
||||||
|
if key == 'name':
|
||||||
|
current_values = []
|
||||||
|
for k in settings.CONFIG['documentKeys']:
|
||||||
|
if k.get('sortType') == 'person':
|
||||||
|
current_values += self.get(k['id'], [])
|
||||||
|
if not isinstance(current_values, list):
|
||||||
|
if not current_values:
|
||||||
|
current_values = []
|
||||||
|
else:
|
||||||
|
current_values = [unicode(current_values)]
|
||||||
|
|
||||||
|
filter_map = utils.get_by_id(settings.CONFIG['documentKeys'], key).get('filterMap')
|
||||||
|
if filter_map:
|
||||||
|
filter_map = re.compile(filter_map)
|
||||||
|
_current_values = []
|
||||||
|
for value in current_values:
|
||||||
|
value = filter_map.findall(value)
|
||||||
|
if value:
|
||||||
|
_current_values.append(value[0])
|
||||||
|
current_values = _current_values
|
||||||
|
|
||||||
|
current_values = list(set(current_values))
|
||||||
|
current_values = [ox.decode_html(ox.strip_tags(v)) for v in current_values]
|
||||||
|
current_values = [unicodedata.normalize('NFKD', v) for v in current_values]
|
||||||
|
self.update_facet_values(key, current_values)
|
||||||
|
|
||||||
|
def update_facet_values(self, key, current_values):
|
||||||
|
current_sortvalues = set([value.lower() for value in current_values])
|
||||||
|
saved_values = [i.value.lower() for i in Facet.objects.filter(document=self, key=key)]
|
||||||
|
removed_values = filter(lambda i: i not in current_sortvalues, saved_values)
|
||||||
|
|
||||||
|
if removed_values:
|
||||||
|
q = Q()
|
||||||
|
for v in removed_values:
|
||||||
|
q |= Q(value__iexact=v)
|
||||||
|
Facet.objects.filter(document=self, key=key).filter(q).delete()
|
||||||
|
|
||||||
|
for value in current_values:
|
||||||
|
if value.lower() not in saved_values:
|
||||||
|
sortvalue = value
|
||||||
|
if key in self.person_keys + ['name']:
|
||||||
|
sortvalue = get_name_sort(value)
|
||||||
|
sortvalue = utils.sort_string(sortvalue).lower()[:900]
|
||||||
|
f, created = Facet.objects.get_or_create(document=self, key=key, value=value, sortvalue=sortvalue)
|
||||||
|
if created:
|
||||||
|
Facet.objects.filter(document=self, key=key, value__iexact=value).exclude(value=value).delete()
|
||||||
|
Facet.objects.filter(key=key, value__iexact=value).exclude(value=value).update(value=value)
|
||||||
|
saved_values.append(value.lower())
|
||||||
|
|
||||||
|
def update_facets(self):
|
||||||
|
for key in set(self.facet_keys + ['title']):
|
||||||
|
self.update_facet(key)
|
||||||
|
|
||||||
|
def update_find(self):
|
||||||
|
|
||||||
|
def save(key, value):
|
||||||
|
if value not in ('', None):
|
||||||
|
f, created = Find.objects.get_or_create(document=self, key=key)
|
||||||
|
if isinstance(value, bool):
|
||||||
|
value = value and 'true' or 'false'
|
||||||
|
if isinstance(value, string_types):
|
||||||
|
value = ox.decode_html(ox.strip_tags(value.strip()))
|
||||||
|
value = unicodedata.normalize('NFKD', value).lower()
|
||||||
|
f.value = value
|
||||||
|
f.save()
|
||||||
|
else:
|
||||||
|
Find.objects.filter(document=self, key=key).delete()
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
data = self.json()
|
||||||
|
for key in settings.CONFIG['documentKeys']:
|
||||||
|
i = key['id']
|
||||||
|
if i == 'rightslevel':
|
||||||
|
save(i, self.rightslevel)
|
||||||
|
elif i not in ('*', 'dimensions') and i not in self.facet_keys:
|
||||||
|
value = data.get(i)
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = u'\n'.join(value)
|
||||||
|
save(i, value)
|
||||||
|
|
||||||
|
base_keys = ('id', 'size', 'dimensions', 'extension', 'matches')
|
||||||
|
|
||||||
|
def update_sort(self):
|
||||||
|
try:
|
||||||
|
s = self.sort
|
||||||
|
except Sort.DoesNotExist:
|
||||||
|
s = Sort(document=self)
|
||||||
|
|
||||||
|
s.id = self.id
|
||||||
|
s.extension = self.extension
|
||||||
|
s.size = self.size
|
||||||
|
s.matches = self.matches
|
||||||
|
if self.extension == 'pdf':
|
||||||
|
s.dimensions = ox.sort_string('2') + ox.sort_string('%d' % self.pages)
|
||||||
|
else:
|
||||||
|
if self.extension == 'html':
|
||||||
|
resolution_sort = self.dimensions
|
||||||
|
s.dimensions = ox.sort_string('1') + ox.sort_string('%d' % resolution_sort)
|
||||||
|
else:
|
||||||
|
resolution_sort = self.width * self.height
|
||||||
|
s.dimensions = ox.sort_string('0') + ox.sort_string('%d' % resolution_sort)
|
||||||
|
|
||||||
|
def sortNames(values):
|
||||||
|
sort_value = u''
|
||||||
|
if values:
|
||||||
|
sort_value = u'; '.join([get_name_sort(name) for name in values])
|
||||||
|
if not sort_value:
|
||||||
|
sort_value = u''
|
||||||
|
return sort_value
|
||||||
|
|
||||||
|
def set_value(s, name, value):
|
||||||
|
if isinstance(value, string_types):
|
||||||
|
value = ox.decode_html(value.lower())
|
||||||
|
if not value:
|
||||||
|
value = None
|
||||||
|
setattr(s, name, value)
|
||||||
|
|
||||||
|
def get_value(source, key):
|
||||||
|
if 'value' in key and 'layer' in key['value']:
|
||||||
|
value = [a.value for a in self.annotations.filter(layer=key['value']['layer']).exclude(value='')]
|
||||||
|
else:
|
||||||
|
value = self.get_value(source)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def get_words(source, key):
|
||||||
|
value = get_value(source, key)
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = '\n'.join(value)
|
||||||
|
value = len(value.split(' ')) if value else 0
|
||||||
|
return value
|
||||||
|
|
||||||
|
for key in filter(lambda k: k.get('sort', False), settings.CONFIG['documentKeys']):
|
||||||
|
name = key['id']
|
||||||
|
if name not in self.base_keys:
|
||||||
|
source = name
|
||||||
|
sort_type = key.get('sortType', key['type'])
|
||||||
|
if 'value' in key:
|
||||||
|
if 'key' in key['value']:
|
||||||
|
source = key['value']['key']
|
||||||
|
sort_type = key['value'].get('type', sort_type)
|
||||||
|
if isinstance(sort_type, list):
|
||||||
|
sort_type = sort_type[0]
|
||||||
|
if sort_type == 'title':
|
||||||
|
value = self.get_value(source, u'Untitled')
|
||||||
|
value = utils.sort_title(value)[:955]
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type == 'person':
|
||||||
|
value = sortNames(self.get_value(source, []))
|
||||||
|
value = utils.sort_string(value)[:955]
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type == 'string':
|
||||||
|
value = self.get_value(source, u'')
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = u','.join(value)
|
||||||
|
value = utils.sort_string(value)[:955]
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type == 'words':
|
||||||
|
value = get_words(source, key) if s.duration else None
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type == 'wordsperminute':
|
||||||
|
value = get_words(source, key)
|
||||||
|
value = value / (s.duration / 60) if value and s.duration else None
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type in ('length', 'integer', 'time', 'float'):
|
||||||
|
# can be length of strings or length of arrays, i.e. keywords
|
||||||
|
if 'layer' in key.get('value', []):
|
||||||
|
value = self.annotations.filter(layer=key['value']['layer']).count()
|
||||||
|
else:
|
||||||
|
value = self.get_value(source)
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = len(value)
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type == 'year':
|
||||||
|
value = self.get_value(source)
|
||||||
|
set_value(s, name, value)
|
||||||
|
elif sort_type == 'date':
|
||||||
|
value = self.get_value(source)
|
||||||
|
if isinstance(value, string_types):
|
||||||
|
value = datetime_safe.datetime.strptime(value, '%Y-%m-%d')
|
||||||
|
set_value(s, name, value)
|
||||||
|
s.save()
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.uploading:
|
if not self.uploading:
|
||||||
if self.file:
|
if self.file:
|
||||||
self.size = self.file.size
|
self.size = self.file.size
|
||||||
self.get_info()
|
self.get_info()
|
||||||
|
if self.extension == 'html':
|
||||||
self.name_sort = ox.sort_string(self.name or u'')[:255].lower()
|
self.size = len(self.data.get('text', ''))
|
||||||
if self.description:
|
|
||||||
self.description_sort = ox.sort_string(self.description)[:512].lower()
|
if self.id:
|
||||||
|
self.update_sort()
|
||||||
|
self.update_find()
|
||||||
|
self.update_facets()
|
||||||
|
new = False
|
||||||
else:
|
else:
|
||||||
self.description_sort = None
|
new = True
|
||||||
if self.extension == 'pdf':
|
|
||||||
self.dimensions_sort = ox.sort_string('1') + ox.sort_string('%d' % self.pages)
|
|
||||||
else:
|
|
||||||
resolution_sort = self.width * self.height
|
|
||||||
self.dimensions_sort = ox.sort_string('0') + ox.sort_string('%d' % resolution_sort)
|
|
||||||
|
|
||||||
super(Document, self).save(*args, **kwargs)
|
super(Document, self).save(*args, **kwargs)
|
||||||
|
if new:
|
||||||
|
self.update_sort()
|
||||||
|
self.update_find()
|
||||||
|
self.update_facets()
|
||||||
self.update_matches()
|
self.update_matches()
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
@ -100,40 +298,61 @@ class Document(models.Model):
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
return ox.toAZ(self.id)
|
return ox.toAZ(self.id)
|
||||||
|
|
||||||
|
def accessible(self, user):
|
||||||
|
return self.user == user or self.status in ('public', 'featured')
|
||||||
|
|
||||||
def editable(self, user, item=None):
|
def editable(self, user, item=None):
|
||||||
if not user or user.is_anonymous():
|
if not user or user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
if self.user == user or \
|
if self.user == user or \
|
||||||
user.is_staff or \
|
user.is_staff or \
|
||||||
user.profile.capability('canEditDocuments') == True or \
|
user.profile.capability('canEditDocuments') is True or \
|
||||||
(item and item.editable(user)):
|
(item and item.editable(user)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def edit(self, data, user, item=None):
|
def edit(self, data, user, item=None):
|
||||||
for key in data:
|
|
||||||
if key == 'name':
|
|
||||||
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
|
|
||||||
if not data['name']:
|
|
||||||
data['name'] = "Untitled"
|
|
||||||
name = data['name']
|
|
||||||
num = 1
|
|
||||||
while Document.objects.filter(name=name, user=self.user, extension=self.extension).exclude(id=self.id).count()>0:
|
|
||||||
num += 1
|
|
||||||
name = data['name'] + ' [%d]' % num
|
|
||||||
self.name = name
|
|
||||||
elif key == 'description' and not item:
|
|
||||||
self.description = ox.sanitize_html(data['description'])
|
|
||||||
if item:
|
if item:
|
||||||
p, created = ItemProperties.objects.get_or_create(item=item, document=self)
|
p, created = ItemProperties.objects.get_or_create(item=item, document=self)
|
||||||
if 'description' in data:
|
if 'description' in data:
|
||||||
p.description = ox.sanitize_html(data['description'])
|
p.description = ox.sanitize_html(data['description'])
|
||||||
p.save()
|
p.save()
|
||||||
|
else:
|
||||||
|
for key in data:
|
||||||
|
k = list(filter(lambda i: i['id'] == key, settings.CONFIG['documentKeys']))
|
||||||
|
ktype = k and k[0].get('type') or ''
|
||||||
|
if key == 'text' and self.extension == 'html':
|
||||||
|
self.data['text'] = ox.sanitize_html(data['text'], global_attributes=[
|
||||||
|
'data-name',
|
||||||
|
'data-type',
|
||||||
|
'data-value',
|
||||||
|
'lang'
|
||||||
|
])
|
||||||
|
elif ktype == 'text':
|
||||||
|
self.data[key] = ox.sanitize_html(data[key])
|
||||||
|
elif ktype == '[text]':
|
||||||
|
self.data[key] = [ox.sanitize_html(t) for t in data[key]]
|
||||||
|
elif ktype == '[string]':
|
||||||
|
self.data[key] = [ox.escape_html(t) for t in data[key]]
|
||||||
|
elif isinstance(data[key], string_types):
|
||||||
|
self.data[key] = ox.escape_html(data[key])
|
||||||
|
elif isinstance(data[key], list):
|
||||||
|
def cleanup(i):
|
||||||
|
if isinstance(i, string_types):
|
||||||
|
i = ox.escape_html(i)
|
||||||
|
return i
|
||||||
|
self.data[key] = [cleanup(i) for i in data[key]]
|
||||||
|
elif isinstance(data[key], int) or isinstance(data[key], float):
|
||||||
|
self.data[key] = data[key]
|
||||||
|
else:
|
||||||
|
self.data[key] = ox.escape_html(data[key])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dimensions(self):
|
def dimensions(self):
|
||||||
if self.extension == 'pdf':
|
if self.extension == 'pdf':
|
||||||
return self.pages
|
return self.pages
|
||||||
|
elif self.extension == 'html':
|
||||||
|
return len(self.data.get('text', '').split(' '))
|
||||||
else:
|
else:
|
||||||
return self.resolution
|
return self.resolution
|
||||||
|
|
||||||
|
@ -141,21 +360,43 @@ class Document(models.Model):
|
||||||
def resolution(self):
|
def resolution(self):
|
||||||
return [self.width, self.height]
|
return [self.width, self.height]
|
||||||
|
|
||||||
|
def get_value(self, key, default=None):
|
||||||
|
if key in (
|
||||||
|
'extension',
|
||||||
|
'id',
|
||||||
|
'matches',
|
||||||
|
'ratio',
|
||||||
|
'size',
|
||||||
|
):
|
||||||
|
return getattr(self, key)
|
||||||
|
elif key == 'user':
|
||||||
|
return self.user.username
|
||||||
|
else:
|
||||||
|
return self.data.get(key, default)
|
||||||
|
|
||||||
def json(self, keys=None, user=None, item=None):
|
def json(self, keys=None, user=None, item=None):
|
||||||
if not keys:
|
if not keys:
|
||||||
keys=[
|
keys = [
|
||||||
'description',
|
'description',
|
||||||
'dimensions',
|
'dimensions',
|
||||||
'editable',
|
'editable',
|
||||||
'entities',
|
'entities',
|
||||||
'extension',
|
'extension',
|
||||||
'id',
|
'id',
|
||||||
'name',
|
|
||||||
'oshash',
|
'oshash',
|
||||||
|
'title',
|
||||||
'ratio',
|
'ratio',
|
||||||
|
'matches',
|
||||||
'size',
|
'size',
|
||||||
'user',
|
'user',
|
||||||
]
|
]
|
||||||
|
if self.extension in ('html', 'txt'):
|
||||||
|
keys.append('text')
|
||||||
|
for key in settings.CONFIG['documentKeys']:
|
||||||
|
if key['id'] in ('*', ):
|
||||||
|
continue
|
||||||
|
if key['id'] not in keys:
|
||||||
|
keys.append(key['id'])
|
||||||
response = {}
|
response = {}
|
||||||
_map = {
|
_map = {
|
||||||
}
|
}
|
||||||
|
@ -166,6 +407,10 @@ class Document(models.Model):
|
||||||
response[key] = self.editable(user)
|
response[key] = self.editable(user)
|
||||||
elif key == 'user':
|
elif key == 'user':
|
||||||
response[key] = self.user.username
|
response[key] = self.user.username
|
||||||
|
elif key == 'accessed':
|
||||||
|
response[key] = self.accessed.aggregate(Max('access'))['access__max']
|
||||||
|
elif key == 'timesaccessed':
|
||||||
|
response[key] = self.accessed.aggregate(Sum('accessed'))['accessed__sum']
|
||||||
elif key == 'entities':
|
elif key == 'entities':
|
||||||
dps = self.documentproperties.select_related('entity').order_by('index')
|
dps = self.documentproperties.select_related('entity').order_by('index')
|
||||||
response[key] = entity_jsons = []
|
response[key] = entity_jsons = []
|
||||||
|
@ -175,8 +420,12 @@ class Document(models.Model):
|
||||||
entity_jsons.append(entity_json)
|
entity_jsons.append(entity_json)
|
||||||
elif key == 'items':
|
elif key == 'items':
|
||||||
response[key] = [i['public_id'] for i in self.items.all().values('public_id')]
|
response[key] = [i['public_id'] for i in self.items.all().values('public_id')]
|
||||||
|
elif key in self.data:
|
||||||
|
response[key] = self.data[key]
|
||||||
elif hasattr(self, _map.get(key, key)):
|
elif hasattr(self, _map.get(key, key)):
|
||||||
response[key] = getattr(self, _map.get(key,key)) or ''
|
response[key] = getattr(self, _map.get(key, key)) or ''
|
||||||
|
if self.extension == 'html':
|
||||||
|
response['text'] = self.data.get('text', '')
|
||||||
if item:
|
if item:
|
||||||
if isinstance(item, string_types):
|
if isinstance(item, string_types):
|
||||||
item = Item.objects.get(public_id=item)
|
item = Item.objects.get(public_id=item)
|
||||||
|
@ -185,6 +434,10 @@ class Document(models.Model):
|
||||||
if 'description' in keys and d[0].description:
|
if 'description' in keys and d[0].description:
|
||||||
response['description'] = d[0].description
|
response['description'] = d[0].description
|
||||||
response['index'] = d[0].index
|
response['index'] = d[0].index
|
||||||
|
if keys:
|
||||||
|
for key in list(response):
|
||||||
|
if key not in keys:
|
||||||
|
del response[key]
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def path(self, name=''):
|
def path(self, name=''):
|
||||||
|
@ -211,6 +464,9 @@ class Document(models.Model):
|
||||||
return False, 0
|
return False, 0
|
||||||
|
|
||||||
def thumbnail(self, size=None, page=None):
|
def thumbnail(self, size=None, page=None):
|
||||||
|
if not self.file:
|
||||||
|
return os.path.join(settings.STATIC_ROOT, 'png/cover.png')
|
||||||
|
return os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
|
||||||
src = self.file.path
|
src = self.file.path
|
||||||
folder = os.path.dirname(src)
|
folder = os.path.dirname(src)
|
||||||
if size:
|
if size:
|
||||||
|
@ -278,12 +534,12 @@ class Document(models.Model):
|
||||||
try:
|
try:
|
||||||
size = Image.open(image).size
|
size = Image.open(image).size
|
||||||
except:
|
except:
|
||||||
size = [1,1]
|
size = [1, 1]
|
||||||
else:
|
else:
|
||||||
if self.width > 0:
|
if self.width > 0:
|
||||||
size = self.resolution
|
size = self.resolution
|
||||||
else:
|
else:
|
||||||
size = [1,1]
|
size = [640, 1024]
|
||||||
self.ratio = size[0] / size[1]
|
self.ratio = size[0] / size[1]
|
||||||
return self.ratio
|
return self.ratio
|
||||||
|
|
||||||
|
@ -337,6 +593,97 @@ class ItemProperties(models.Model):
|
||||||
if self.description:
|
if self.description:
|
||||||
self.description_sort = ox.sort_string(self.description)[:512].lower()
|
self.description_sort = ox.sort_string(self.description)[:512].lower()
|
||||||
else:
|
else:
|
||||||
self.description_sort = self.document.description_sort
|
self.description_sort = self.document.sort.description
|
||||||
|
|
||||||
super(ItemProperties, self).save(*args, **kwargs)
|
super(ItemProperties, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Access(models.Model):
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("document", "user")
|
||||||
|
|
||||||
|
access = models.DateTimeField(auto_now=True)
|
||||||
|
document = models.ForeignKey(Document, related_name='accessed')
|
||||||
|
user = models.ForeignKey(User, null=True, related_name='accessed_documents')
|
||||||
|
accessed = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.accessed:
|
||||||
|
self.accessed = 0
|
||||||
|
self.accessed += 1
|
||||||
|
super(Access, self).save(*args, **kwargs)
|
||||||
|
timesaccessed = Access.objects.filter(document=self.document).aggregate(Sum('accessed'))['accessed__sum']
|
||||||
|
Sort.objects.filter(document=self.document).update(timesaccessed=timesaccessed, accessed=self.access)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
if self.user:
|
||||||
|
return u"%s/%s/%s" % (self.user, self.document, self.access)
|
||||||
|
return u"%s/%s" % (self.item, self.access)
|
||||||
|
|
||||||
|
class Facet(models.Model):
|
||||||
|
'''
|
||||||
|
used for keys that can have multiple values like people, languages etc.
|
||||||
|
does not perform to well if total number of items goes above 10k
|
||||||
|
this happens for keywords in 0xdb right now
|
||||||
|
'''
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("document", "key", "value")
|
||||||
|
|
||||||
|
document = models.ForeignKey('Document', related_name='facets')
|
||||||
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
|
value = models.CharField(max_length=1000, db_index=True)
|
||||||
|
sortvalue = models.CharField(max_length=1000, db_index=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s=%s" % (self.key, self.value)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.sortvalue:
|
||||||
|
self.sortvalue = utils.sort_string(self.value).lower()[:900]
|
||||||
|
self.sotvalue = self.sortvalue.lower()
|
||||||
|
super(Facet, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
Document.facet_keys = []
|
||||||
|
for key in settings.CONFIG['documentKeys']:
|
||||||
|
if 'autocomplete' in key and 'autocompleteSortKey' not in key or \
|
||||||
|
key.get('filter'):
|
||||||
|
Document.facet_keys.append(key['id'])
|
||||||
|
|
||||||
|
Document.person_keys = []
|
||||||
|
for key in settings.CONFIG['itemKeys']:
|
||||||
|
if key.get('sortType') == 'person':
|
||||||
|
Document.person_keys.append(key['id'])
|
||||||
|
|
||||||
|
class Find(models.Model):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ('document', 'key')
|
||||||
|
|
||||||
|
document = models.ForeignKey('Document', related_name='find', db_index=True)
|
||||||
|
key = models.CharField(max_length=200, db_index=True)
|
||||||
|
value = models.TextField(blank=True, db_index=settings.DB_GIN_TRGM)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u'%s=%s' % (self.key, self.value)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Sort
|
||||||
|
table constructed based on info in settings.CONFIG['documentKeys']
|
||||||
|
'''
|
||||||
|
attrs = {
|
||||||
|
'__module__': 'document.models',
|
||||||
|
'document': models.OneToOneField('Document', related_name='sort', primary_key=True),
|
||||||
|
'created': models.DateTimeField(null=True, blank=True, db_index=True),
|
||||||
|
}
|
||||||
|
for key in filter(lambda k: k.get('sort', False) or k['type'] in ('integer', 'time', 'float', 'date', 'enum'), settings.CONFIG['documentKeys']):
|
||||||
|
name = key['id']
|
||||||
|
sort_type = key.get('sortType', key['type'])
|
||||||
|
if isinstance(sort_type, list):
|
||||||
|
sort_type = sort_type[0]
|
||||||
|
field = get_sort_field(sort_type)
|
||||||
|
if name not in attrs:
|
||||||
|
attrs[name] = field[0](**field[1])
|
||||||
|
|
||||||
|
Sort = type('Sort', (models.Model,), attrs)
|
||||||
|
Sort.fields = [f.name for f in Sort._meta.fields]
|
||||||
|
|
72
pandora/document/sync_sort.py
Normal file
72
pandora/document/sync_sort.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import connection, transaction
|
||||||
|
from django.db.models import fields
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
def update_tables(debug=False):
|
||||||
|
table_name = models.Sort._meta.db_table
|
||||||
|
cursor = connection.cursor()
|
||||||
|
db_rows = connection.introspection.get_table_description(cursor, table_name)
|
||||||
|
db_fields = dict([(row[0], row) for row in db_rows])
|
||||||
|
db_types = dict([(row[0],
|
||||||
|
connection.introspection.data_types_reverse[row[1]]) for row in db_rows])
|
||||||
|
|
||||||
|
model_fields = ['document_id'] + [f.name for f in models.Sort._meta.fields]
|
||||||
|
rebuild = False
|
||||||
|
|
||||||
|
changes = []
|
||||||
|
for name in db_types:
|
||||||
|
if name not in model_fields:
|
||||||
|
sql = 'ALTER TABLE "%s" DROP COLUMN "%s"' % (table_name, name)
|
||||||
|
changes.append(sql)
|
||||||
|
|
||||||
|
for f in models.Sort._meta.fields:
|
||||||
|
if not f.primary_key:
|
||||||
|
name = f.name
|
||||||
|
col_type = f.db_type(connection)
|
||||||
|
if name not in db_fields:
|
||||||
|
sql = 'ALTER TABLE "%s" ADD COLUMN "%s" %s' % (table_name, name, col_type)
|
||||||
|
changes.append(sql)
|
||||||
|
sql = 'CREATE INDEX "%s_%s_idx" ON "%s" ("%s")' % (table_name, name,
|
||||||
|
table_name, name)
|
||||||
|
changes.append(sql)
|
||||||
|
rebuild = True
|
||||||
|
elif f.__class__.__name__ != db_types[name]:
|
||||||
|
sql = 'ALTER TABLE "%s" DROP COLUMN "%s"' % (table_name, name)
|
||||||
|
changes.append(sql)
|
||||||
|
sql = 'ALTER TABLE "%s" ADD COLUMN "%s" %s' % (table_name, name, col_type)
|
||||||
|
changes.append(sql)
|
||||||
|
sql = 'CREATE INDEX "%s_%s_idx" ON "%s" ("%s")' % (table_name, name,
|
||||||
|
table_name, name)
|
||||||
|
changes.append(sql)
|
||||||
|
rebuild = True
|
||||||
|
elif db_types[name] == 'CharField' and db_fields[name][3] != f.max_length:
|
||||||
|
sql = 'ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s' % (table_name, name,
|
||||||
|
col_type)
|
||||||
|
changes.append(sql)
|
||||||
|
sql = 'ALTER TABLE "%s" ALTER COLUMN "%s" %s NOT NULL' % (table_name, name,
|
||||||
|
f.null and "DROP" or "SET")
|
||||||
|
changes.append(sql)
|
||||||
|
rebuild = True
|
||||||
|
|
||||||
|
if changes:
|
||||||
|
print("Updating document sort schema...")
|
||||||
|
for sql in changes:
|
||||||
|
if debug:
|
||||||
|
print(sql)
|
||||||
|
cursor.execute(sql)
|
||||||
|
transaction.commit()
|
||||||
|
if rebuild:
|
||||||
|
print("Updating document sort values...")
|
||||||
|
ids = [i['id'] for i in models.Document.objects.all().values('id')]
|
||||||
|
for id in ids:
|
||||||
|
d = models.Document.objects.get(pk=id)
|
||||||
|
if debug:
|
||||||
|
print(d)
|
||||||
|
d.update_sort()
|
|
@ -1,8 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
from item.utils import sort_title, sort_string, get_by_id
|
||||||
|
|
||||||
def pdfpages(pdf):
|
def pdfpages(pdf):
|
||||||
return int(pdfinfo(pdf).get('pages', '0'))
|
return int(pdfinfo(pdf).get('pages', '0'))
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
from __future__ import division, print_function, absolute_import
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
import ox
|
import ox
|
||||||
|
@ -13,7 +15,8 @@ from oxdjango.decorators import login_required_json
|
||||||
from oxdjango.http import HttpFileResponse
|
from oxdjango.http import HttpFileResponse
|
||||||
from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response, HttpErrorJson
|
from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response, HttpErrorJson
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db.models import Sum
|
from django.db.models import Count, Sum
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from item import utils
|
from item import utils
|
||||||
from item.models import Item
|
from item.models import Item
|
||||||
|
@ -35,6 +38,13 @@ def get_document_or_404_json(id):
|
||||||
@login_required_json
|
@login_required_json
|
||||||
def addDocument(request, data):
|
def addDocument(request, data):
|
||||||
'''
|
'''
|
||||||
|
Create new html document
|
||||||
|
takes {
|
||||||
|
title: string
|
||||||
|
}
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
Adds one or more documents to one or more items
|
Adds one or more documents to one or more items
|
||||||
takes {
|
takes {
|
||||||
item: string or [string], // one or more item ids (optional)
|
item: string or [string], // one or more item ids (optional)
|
||||||
|
@ -46,46 +56,54 @@ def addDocument(request, data):
|
||||||
see: editDocument, findDocuments, getDocument, removeDocument, sortDocuments
|
see: editDocument, findDocuments, getDocument, removeDocument, sortDocuments
|
||||||
'''
|
'''
|
||||||
response = json_response()
|
response = json_response()
|
||||||
if 'ids' in data:
|
if 'title' in data:
|
||||||
ids = data['ids']
|
doc = models.Document(user=request.user, extension='html')
|
||||||
|
doc.data['title'] = data['title']
|
||||||
|
doc.save()
|
||||||
|
response = json_response(status=200, text='created')
|
||||||
|
response['data'] = doc.json(user=request.user)
|
||||||
|
add_changelog(request, data, doc.get_id())
|
||||||
else:
|
else:
|
||||||
ids = [data['id']]
|
if 'ids' in data:
|
||||||
if 'item' in data:
|
ids = data['ids']
|
||||||
if isinstance(data['item'], string_types):
|
|
||||||
item = Item.objects.get(public_id=data['item'])
|
|
||||||
if item.editable(request.user):
|
|
||||||
for id in ids:
|
|
||||||
document = models.Document.get(id)
|
|
||||||
document.add(item)
|
|
||||||
add_changelog(request, data, item.public_id)
|
|
||||||
else:
|
|
||||||
response = json_response(status=403, text='permission denied')
|
|
||||||
else:
|
else:
|
||||||
for item in Item.objects.filter(public_id__in=data['item']):
|
ids = [data['id']]
|
||||||
|
if 'item' in data:
|
||||||
|
if isinstance(data['item'], string_types):
|
||||||
|
item = Item.objects.get(public_id=data['item'])
|
||||||
if item.editable(request.user):
|
if item.editable(request.user):
|
||||||
for id in ids:
|
for id in ids:
|
||||||
document = models.Document.get(id)
|
document = models.Document.get(id)
|
||||||
document.add(item)
|
document.add(item)
|
||||||
add_changelog(request, data, data['item'])
|
add_changelog(request, data, item.public_id)
|
||||||
elif 'entity' in data:
|
else:
|
||||||
if isinstance(data['entity'], string_types):
|
response = json_response(status=403, text='permission denied')
|
||||||
entity = Entity.get(data['entity'])
|
|
||||||
if entity.editable(request.user):
|
|
||||||
for id in ids:
|
|
||||||
document = models.Document.get(id)
|
|
||||||
entity.add(document)
|
|
||||||
add_changelog(request, data, entity.get_id())
|
|
||||||
else:
|
else:
|
||||||
response = json_response(status=403, text='permission denied')
|
for item in Item.objects.filter(public_id__in=data['item']):
|
||||||
else:
|
if item.editable(request.user):
|
||||||
for entity in Entity.objects.filter(id__in=map(ox.fromAZ, data['entity'])):
|
for id in ids:
|
||||||
|
document = models.Document.get(id)
|
||||||
|
document.add(item)
|
||||||
|
add_changelog(request, data, data['item'])
|
||||||
|
elif 'entity' in data:
|
||||||
|
if isinstance(data['entity'], string_types):
|
||||||
|
entity = Entity.get(data['entity'])
|
||||||
if entity.editable(request.user):
|
if entity.editable(request.user):
|
||||||
for id in ids:
|
for id in ids:
|
||||||
document = models.Document.get(id)
|
document = models.Document.get(id)
|
||||||
entity.add(document)
|
entity.add(document)
|
||||||
add_changelog(request, data, data['entity'])
|
add_changelog(request, data, entity.get_id())
|
||||||
else:
|
else:
|
||||||
response = json_response(status=500, text='invalid request')
|
response = json_response(status=403, text='permission denied')
|
||||||
|
else:
|
||||||
|
for entity in Entity.objects.filter(id__in=map(ox.fromAZ, data['entity'])):
|
||||||
|
if entity.editable(request.user):
|
||||||
|
for id in ids:
|
||||||
|
document = models.Document.get(id)
|
||||||
|
entity.add(document)
|
||||||
|
add_changelog(request, data, data['entity'])
|
||||||
|
else:
|
||||||
|
response = json_response(status=500, text='invalid request')
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
actions.register(addDocument, cache=False)
|
actions.register(addDocument, cache=False)
|
||||||
|
|
||||||
|
@ -95,7 +113,8 @@ def editDocument(request, data):
|
||||||
Edits data for a document
|
Edits data for a document
|
||||||
takes {
|
takes {
|
||||||
id: string, // document id
|
id: string, // document id
|
||||||
name: string, // new document name
|
|
||||||
|
key: value, // set new data
|
||||||
description: string // new document description
|
description: string // new document description
|
||||||
item: string // item id (optional)
|
item: string // item id (optional)
|
||||||
}
|
}
|
||||||
|
@ -126,22 +145,26 @@ actions.register(editDocument, cache=False)
|
||||||
|
|
||||||
|
|
||||||
def _order_query(qs, sort, item=None):
|
def _order_query(qs, sort, item=None):
|
||||||
|
prefix = 'sort__'
|
||||||
order_by = []
|
order_by = []
|
||||||
for e in sort:
|
for e in sort:
|
||||||
operator = e['operator']
|
operator = e['operator']
|
||||||
if operator != '-':
|
if operator != '-':
|
||||||
operator = ''
|
operator = ''
|
||||||
key = {
|
key = {
|
||||||
'name': 'name_sort',
|
|
||||||
'description': 'descriptions__description_sort'
|
'description': 'descriptions__description_sort'
|
||||||
if item else 'description_sort',
|
if item else 'description',
|
||||||
'dimensions': 'dimensions_sort',
|
|
||||||
'index': 'items__itemproperties__index',
|
'index': 'items__itemproperties__index',
|
||||||
|
#fixme:
|
||||||
|
'position': 'id',
|
||||||
|
'name': 'title',
|
||||||
}.get(e['key'], e['key'])
|
}.get(e['key'], e['key'])
|
||||||
if key == 'resolution':
|
if key == 'resolution':
|
||||||
order_by.append('%swidth'%operator)
|
order_by.append('%swidth'%operator)
|
||||||
order_by.append('%sheight'%operator)
|
order_by.append('%sheight'%operator)
|
||||||
else:
|
else:
|
||||||
|
if '__' not in key:
|
||||||
|
key = "%s%s" % (prefix, key)
|
||||||
order = '%s%s' % (operator, key)
|
order = '%s%s' % (operator, key)
|
||||||
order_by.append(order)
|
order_by.append(order)
|
||||||
if order_by:
|
if order_by:
|
||||||
|
@ -149,6 +172,24 @@ def _order_query(qs, sort, item=None):
|
||||||
qs = qs.distinct()
|
qs = qs.distinct()
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
def _order_by_group(query):
|
||||||
|
if 'sort' in query:
|
||||||
|
if len(query['sort']) == 1 and query['sort'][0]['key'] == 'items':
|
||||||
|
order_by = query['sort'][0]['operator'] == '-' and '-items' or 'items'
|
||||||
|
if query['group'] == "year":
|
||||||
|
secondary = query['sort'][0]['operator'] == '-' and '-sortvalue' or 'sortvalue'
|
||||||
|
order_by = (order_by, secondary)
|
||||||
|
elif query['group'] != "keyword":
|
||||||
|
order_by = (order_by, 'sortvalue')
|
||||||
|
else:
|
||||||
|
order_by = (order_by, 'value')
|
||||||
|
else:
|
||||||
|
order_by = query['sort'][0]['operator'] == '-' and '-sortvalue' or 'sortvalue'
|
||||||
|
order_by = (order_by, 'items')
|
||||||
|
else:
|
||||||
|
order_by = ('-sortvalue', 'items')
|
||||||
|
return order_by
|
||||||
|
|
||||||
def get_item(query):
|
def get_item(query):
|
||||||
for c in query.get('conditions', []):
|
for c in query.get('conditions', []):
|
||||||
if c.get('key') == 'item':
|
if c.get('key') == 'item':
|
||||||
|
@ -162,7 +203,7 @@ def parse_query(data, user):
|
||||||
for key in ('keys', 'group', 'file', 'range', 'position', 'positions', 'sort'):
|
for key in ('keys', 'group', 'file', 'range', 'position', 'positions', 'sort'):
|
||||||
if key in data:
|
if key in data:
|
||||||
query[key] = data[key]
|
query[key] = data[key]
|
||||||
query['qs'] = models.Document.objects.find(data, user).exclude(name='')
|
query['qs'] = models.Document.objects.find(data, user)
|
||||||
query['item'] = get_item(data.get('query', {}))
|
query['item'] = get_item(data.get('query', {}))
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
@ -192,7 +233,24 @@ def findDocuments(request, data):
|
||||||
#order
|
#order
|
||||||
qs = _order_query(query['qs'], query['sort'], query['item'])
|
qs = _order_query(query['qs'], query['sort'], query['item'])
|
||||||
response = json_response()
|
response = json_response()
|
||||||
if 'keys' in data:
|
if 'group' in query:
|
||||||
|
response['data']['items'] = []
|
||||||
|
items = 'items'
|
||||||
|
document_qs = query['qs']
|
||||||
|
order_by = _order_by_group(query)
|
||||||
|
qs = models.Facet.objects.filter(key=query['group']).filter(document__id__in=document_qs)
|
||||||
|
qs = qs.values('value').annotate(items=Count('id')).order_by(*order_by)
|
||||||
|
|
||||||
|
if 'positions' in query:
|
||||||
|
response['data']['positions'] = {}
|
||||||
|
ids = [j['value'] for j in qs]
|
||||||
|
response['data']['positions'] = utils.get_positions(ids, query['positions'])
|
||||||
|
elif 'range' in data:
|
||||||
|
qs = qs[query['range'][0]:query['range'][1]]
|
||||||
|
response['data']['items'] = [{'name': i['value'], 'items': i[items]} for i in qs]
|
||||||
|
else:
|
||||||
|
response['data']['items'] = qs.count()
|
||||||
|
elif 'keys' in data:
|
||||||
qs = qs[query['range'][0]:query['range'][1]]
|
qs = qs[query['range'][0]:query['range'][1]]
|
||||||
|
|
||||||
response['data']['items'] = [l.json(data['keys'], request.user, query['item']) for l in qs]
|
response['data']['items'] = [l.json(data['keys'], request.user, query['item']) for l in qs]
|
||||||
|
@ -330,23 +388,15 @@ def upload(request):
|
||||||
if 'chunk' in request.FILES:
|
if 'chunk' in request.FILES:
|
||||||
if file.editable(request.user):
|
if file.editable(request.user):
|
||||||
response = process_chunk(request, file.save_chunk)
|
response = process_chunk(request, file.save_chunk)
|
||||||
response['resultUrl'] = request.build_absolute_uri(file.get_absolute_url())
|
response['resultUrl'] = file.get_absolute_url()
|
||||||
# id is used to select document in dialog after upload
|
# id is used to select document in dialog after upload
|
||||||
response['id'] = file.get_id()
|
response['id'] = file.get_id()
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
#init upload
|
#init upload
|
||||||
else:
|
else:
|
||||||
if not file:
|
if not file:
|
||||||
created = False
|
file = models.Document(user=request.user, extension=extension)
|
||||||
num = 1
|
file.data['title'] = name
|
||||||
_name = name
|
|
||||||
while not created:
|
|
||||||
file, created = models.Document.objects.get_or_create(
|
|
||||||
user=request.user, name=name, extension=extension)
|
|
||||||
if not created:
|
|
||||||
num += 1
|
|
||||||
name = _name + ' [%d]' % num
|
|
||||||
file.name = name
|
|
||||||
file.extension = extension
|
file.extension = extension
|
||||||
file.uploading = True
|
file.uploading = True
|
||||||
file.save()
|
file.save()
|
||||||
|
@ -361,10 +411,81 @@ def upload(request):
|
||||||
file.width = -1
|
file.width = -1
|
||||||
file.pages = -1
|
file.pages = -1
|
||||||
file.save()
|
file.save()
|
||||||
upload_url = request.build_absolute_uri('/api/upload/document?id=%s' % file.get_id())
|
upload_url = '/api/upload/document?id=%s' % file.get_id()
|
||||||
return render_to_json_response({
|
return render_to_json_response({
|
||||||
'uploadUrl': upload_url,
|
'uploadUrl': upload_url,
|
||||||
'url': request.build_absolute_uri(file.get_absolute_url()),
|
'url': file.get_absolute_url(),
|
||||||
'result': 1
|
'result': 1
|
||||||
})
|
})
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
|
|
||||||
|
def autocompleteDocuments(request, data):
|
||||||
|
'''
|
||||||
|
Returns autocomplete strings for a given documeny key and search string
|
||||||
|
takes {
|
||||||
|
key: string, // document key
|
||||||
|
value: string, // search string
|
||||||
|
operator: string, // '=', '==', '^', '$'
|
||||||
|
query: object, // document query to limit results, see `find`
|
||||||
|
range: [int, int] // range of results to return
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
items: [string, ...] // list of matching strings
|
||||||
|
}
|
||||||
|
see: autocomplete, autocompleteEntities
|
||||||
|
'''
|
||||||
|
if 'range' not in data:
|
||||||
|
data['range'] = [0, 10]
|
||||||
|
op = data.get('operator', '=')
|
||||||
|
|
||||||
|
key = utils.get_by_id(settings.CONFIG['documentKeys'], data['key'])
|
||||||
|
order_by = key.get('autocompleteSort', False)
|
||||||
|
if order_by:
|
||||||
|
for o in order_by:
|
||||||
|
if o['operator'] != '-':
|
||||||
|
o['operator'] = ''
|
||||||
|
order_by = ['%(operator)ssort__%(key)s' % o for o in order_by]
|
||||||
|
else:
|
||||||
|
order_by = ['-items']
|
||||||
|
|
||||||
|
qs = parse_query({'query': data.get('query', {})}, request.user)['qs']
|
||||||
|
response = json_response({})
|
||||||
|
response['data']['items'] = []
|
||||||
|
'''
|
||||||
|
for d in qs:
|
||||||
|
value = d.json().get(data['key'])
|
||||||
|
add = False
|
||||||
|
if value:
|
||||||
|
if op == '=' and data['value'] in value:
|
||||||
|
add = True
|
||||||
|
elif op == '==' and data['value'].lower() == value.lower():
|
||||||
|
add = True
|
||||||
|
elif op == '^' and value.lower().startswith(data['value'].lower()):
|
||||||
|
add = True
|
||||||
|
if add and value not in response['data']['items']:
|
||||||
|
response['data']['items'].append(value)
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
sort_type = key.get('sortType', key.get('type', 'string'))
|
||||||
|
qs = models.Facet.objects.filter(key=data['key'])
|
||||||
|
if data['value']:
|
||||||
|
value = unicodedata.normalize('NFKD', data['value']).lower()
|
||||||
|
if op == '=':
|
||||||
|
qs = qs.filter(value__icontains=value)
|
||||||
|
elif op == '==':
|
||||||
|
qs = qs.filter(value__iexact=value)
|
||||||
|
elif op == '^':
|
||||||
|
qs = qs.filter(value__istartswith=value)
|
||||||
|
elif op == '$':
|
||||||
|
qs = qs.filter(value__iendswith=value)
|
||||||
|
if 'query' in data:
|
||||||
|
document_query = parse_query({'query': data.get('query', {})}, request.user)['qs']
|
||||||
|
qs = qs.filter(document__in=document_query)
|
||||||
|
qs = qs.values('value').annotate(items=Count('id'))
|
||||||
|
qs = qs.order_by(*order_by)
|
||||||
|
qs = qs[data['range'][0]:data['range'][1]]
|
||||||
|
response = json_response({})
|
||||||
|
response['data']['items'] = [i['value'] for i in qs]
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(autocompleteDocuments)
|
||||||
|
|
0
pandora/documentcollection/__init__.py
Normal file
0
pandora/documentcollection/__init__.py
Normal file
132
pandora/documentcollection/managers.py
Normal file
132
pandora/documentcollection/managers.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
|
||||||
|
from django.db.models import Q, Manager
|
||||||
|
|
||||||
|
from oxdjango.managers import get_operator
|
||||||
|
from oxdjango.query import QuerySet
|
||||||
|
|
||||||
|
keymap = {
|
||||||
|
'user': 'user__username',
|
||||||
|
}
|
||||||
|
default_key = 'name'
|
||||||
|
|
||||||
|
def parseCondition(condition, user):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
k = condition.get('key', default_key)
|
||||||
|
k = keymap.get(k, k)
|
||||||
|
if not k:
|
||||||
|
k = default_key
|
||||||
|
v = condition.get('value', '')
|
||||||
|
op = condition.get('operator')
|
||||||
|
if not op:
|
||||||
|
op = '='
|
||||||
|
if op.startswith('!'):
|
||||||
|
op = op[1:]
|
||||||
|
exclude = True
|
||||||
|
else:
|
||||||
|
exclude = False
|
||||||
|
if k == 'id':
|
||||||
|
v = v.split(":")
|
||||||
|
if len(v) >= 2:
|
||||||
|
v = (v[0], ":".join(v[1:]))
|
||||||
|
q = Q(user__username=v[0], name=v[1])
|
||||||
|
else:
|
||||||
|
q = Q(id__in=[])
|
||||||
|
return q
|
||||||
|
if k == 'subscribed':
|
||||||
|
key = 'subscribed_users__username'
|
||||||
|
v = user.username
|
||||||
|
elif isinstance(v, bool):
|
||||||
|
key = k
|
||||||
|
else:
|
||||||
|
key = k + get_operator(op, 'istr')
|
||||||
|
key = str(key)
|
||||||
|
if exclude:
|
||||||
|
q = ~Q(**{key: v})
|
||||||
|
else:
|
||||||
|
q = Q(**{key: v})
|
||||||
|
return q
|
||||||
|
|
||||||
|
def parseConditions(conditions, operator, user):
|
||||||
|
'''
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
value: "war"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: "year",
|
||||||
|
value: "1970-1980,
|
||||||
|
operator: "!="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "country",
|
||||||
|
value: "f",
|
||||||
|
operator: "^"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
operator: "&"
|
||||||
|
'''
|
||||||
|
conn = []
|
||||||
|
for condition in conditions:
|
||||||
|
if 'conditions' in condition:
|
||||||
|
q = parseConditions(condition['conditions'],
|
||||||
|
condition.get('operator', '&'), user)
|
||||||
|
if q:
|
||||||
|
conn.append(q)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
conn.append(parseCondition(condition, user))
|
||||||
|
if conn:
|
||||||
|
q = conn[0]
|
||||||
|
for c in conn[1:]:
|
||||||
|
if operator == '|':
|
||||||
|
q = q | c
|
||||||
|
else:
|
||||||
|
q = q & c
|
||||||
|
return q
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionManager(Manager):
|
||||||
|
|
||||||
|
def get_query_set(self):
|
||||||
|
return QuerySet(self.model)
|
||||||
|
|
||||||
|
def find(self, data, user):
|
||||||
|
'''
|
||||||
|
query: {
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
value: "war"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
key: "year",
|
||||||
|
value: "1970-1980,
|
||||||
|
operator: "!="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "country",
|
||||||
|
value: "f",
|
||||||
|
operator: "^"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
operator: "&"
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
#join query with operator
|
||||||
|
qs = self.get_query_set()
|
||||||
|
query = data.get('query', {})
|
||||||
|
conditions = parseConditions(query.get('conditions', []),
|
||||||
|
query.get('operator', '&'),
|
||||||
|
user)
|
||||||
|
if conditions:
|
||||||
|
qs = qs.filter(conditions)
|
||||||
|
|
||||||
|
if user.is_anonymous():
|
||||||
|
qs = qs.filter(Q(status='public') | Q(status='featured'))
|
||||||
|
else:
|
||||||
|
qs = qs.filter(Q(status='public') | Q(status='featured') | Q(user=user))
|
||||||
|
return qs
|
84
pandora/documentcollection/migrations/0001_initial.py
Normal file
84
pandora/documentcollection/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-08 12:34
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import documentcollection.models
|
||||||
|
import oxdjango.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('document', '0003_new_fields'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Collection',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('modified', models.DateTimeField(auto_now=True)),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('status', models.CharField(default=b'private', max_length=20)),
|
||||||
|
('query', oxdjango.fields.DictField(default={b'static': True})),
|
||||||
|
('type', models.CharField(default=b'static', max_length=255)),
|
||||||
|
('description', models.TextField(default=b'')),
|
||||||
|
('icon', models.ImageField(blank=True, default=None, upload_to=documentcollection.models.get_icon_path)),
|
||||||
|
('view', models.TextField(default=documentcollection.models.get_collectionview)),
|
||||||
|
('sort', oxdjango.fields.TupleField(default=documentcollection.models.get_collectionsort, editable=False)),
|
||||||
|
('poster_frames', oxdjango.fields.TupleField(default=[], editable=False)),
|
||||||
|
('numberofdocuments', models.IntegerField(default=0)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CollectionDocument',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('modified', models.DateTimeField(auto_now=True)),
|
||||||
|
('index', models.IntegerField(default=0)),
|
||||||
|
('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='documentcollection.Collection')),
|
||||||
|
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='document.Document')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Position',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('section', models.CharField(max_length=255)),
|
||||||
|
('position', models.IntegerField(default=0)),
|
||||||
|
('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='position', to='documentcollection.Collection')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='collection_positions', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='collection',
|
||||||
|
name='documents',
|
||||||
|
field=models.ManyToManyField(related_name='collections', through='documentcollection.CollectionDocument', to='document.Document'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='collection',
|
||||||
|
name='subscribed_users',
|
||||||
|
field=models.ManyToManyField(related_name='subscribed_collections', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='collection',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='collections', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='position',
|
||||||
|
unique_together=set([('user', 'collection', 'section')]),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='collection',
|
||||||
|
unique_together=set([('user', 'name')]),
|
||||||
|
),
|
||||||
|
]
|
0
pandora/documentcollection/migrations/__init__.py
Normal file
0
pandora/documentcollection/migrations/__init__.py
Normal file
321
pandora/documentcollection/models.py
Normal file
321
pandora/documentcollection/models.py
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Max
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.conf import settings
|
||||||
|
import ox
|
||||||
|
|
||||||
|
from oxdjango.fields import DictField, TupleField
|
||||||
|
|
||||||
|
from archive import extract
|
||||||
|
|
||||||
|
from . import managers
|
||||||
|
|
||||||
|
|
||||||
|
def get_path(f, x):
|
||||||
|
return f.path(x)
|
||||||
|
|
||||||
|
def get_icon_path(f, x):
|
||||||
|
return get_path(f, 'icon.jpg')
|
||||||
|
|
||||||
|
def get_collectionview():
|
||||||
|
return settings.CONFIG['user']['ui']['collectionView']
|
||||||
|
|
||||||
|
def get_collectionsort():
|
||||||
|
return tuple(settings.CONFIG['user']['ui']['collectionSort'])
|
||||||
|
|
||||||
|
class Collection(models.Model):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("user", "name")
|
||||||
|
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
user = models.ForeignKey(User, related_name='collections')
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
status = models.CharField(max_length=20, default='private')
|
||||||
|
_status = ['private', 'public', 'featured']
|
||||||
|
query = DictField(default={"static": True})
|
||||||
|
type = models.CharField(max_length=255, default='static')
|
||||||
|
description = models.TextField(default='')
|
||||||
|
|
||||||
|
icon = models.ImageField(default=None, blank=True, upload_to=get_icon_path)
|
||||||
|
|
||||||
|
view = models.TextField(default=get_collectionview)
|
||||||
|
sort = TupleField(default=get_collectionsort, editable=False)
|
||||||
|
|
||||||
|
poster_frames = TupleField(default=[], editable=False)
|
||||||
|
|
||||||
|
#is through table still required?
|
||||||
|
documents = models.ManyToManyField('document.Document', related_name='collections',
|
||||||
|
through='CollectionDocument')
|
||||||
|
|
||||||
|
numberofdocuments = models.IntegerField(default=0)
|
||||||
|
subscribed_users = models.ManyToManyField(User, related_name='subscribed_collections')
|
||||||
|
|
||||||
|
objects = managers.CollectionManager()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.query.get('static', False):
|
||||||
|
self.type = 'static'
|
||||||
|
else:
|
||||||
|
self.type = 'smart'
|
||||||
|
if self.id:
|
||||||
|
self.numberofdocuments = self.get_numberofdocuments(self.user)
|
||||||
|
super(Collection, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, id):
|
||||||
|
id = id.split(':')
|
||||||
|
username = id[0]
|
||||||
|
collectionname = ":".join(id[1:])
|
||||||
|
return cls.objects.get(user__username=username, name=collectionname)
|
||||||
|
|
||||||
|
def get_documents(self, user=None):
|
||||||
|
if self.query.get('static', False):
|
||||||
|
return self.documents
|
||||||
|
from document.models import Document
|
||||||
|
return Document.objects.find({'query': self.query}, user)
|
||||||
|
|
||||||
|
def get_numberofdocuments(self, user=None):
|
||||||
|
return self.get_documents(user).count()
|
||||||
|
|
||||||
|
def add(self, document):
|
||||||
|
q = self.documents.filter(id=document.id)
|
||||||
|
if q.count() == 0:
|
||||||
|
l = CollectionDocument()
|
||||||
|
l.collection = self
|
||||||
|
l.document = document
|
||||||
|
l.index = CollectionDocument.objects.filter(collection=self).aggregate(Max('index'))['index__max']
|
||||||
|
if l.index is None:
|
||||||
|
l.index = 0
|
||||||
|
else:
|
||||||
|
l.index += 1
|
||||||
|
l.save()
|
||||||
|
|
||||||
|
def remove(self, document=None, documents=None):
|
||||||
|
if document:
|
||||||
|
CollectionDocument.objects.all().filter(document=document, collection=self).delete()
|
||||||
|
if documents:
|
||||||
|
CollectionDocument.objects.all().filter(document__id__in=documents, collection=self).delete()
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.get_id()
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return u'%s:%s' % (self.user.username, self.name)
|
||||||
|
|
||||||
|
def accessible(self, user):
|
||||||
|
return self.user == user or self.status in ('public', 'featured')
|
||||||
|
|
||||||
|
def editable(self, user):
|
||||||
|
if user.is_anonymous():
|
||||||
|
return False
|
||||||
|
if self.user == user or \
|
||||||
|
user.is_staff or \
|
||||||
|
user.profile.capability('canEditFeaturedCollections') is True:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def edit(self, data, user):
|
||||||
|
for key in data:
|
||||||
|
if key == 'query' and not data['query']:
|
||||||
|
setattr(self, key, {"static": True})
|
||||||
|
elif key == 'query' and isinstance(data[key], dict):
|
||||||
|
setattr(self, key, data[key])
|
||||||
|
elif key == 'type':
|
||||||
|
if data[key] == 'static':
|
||||||
|
self.query = {"static": True}
|
||||||
|
self.type = 'static'
|
||||||
|
else:
|
||||||
|
self.type = 'smart'
|
||||||
|
if self.query.get('static', False):
|
||||||
|
self.query = {}
|
||||||
|
elif key == 'status':
|
||||||
|
value = data[key]
|
||||||
|
if value not in self._status:
|
||||||
|
value = self._status[0]
|
||||||
|
if value == 'private':
|
||||||
|
for user in self.subscribed_users.all():
|
||||||
|
self.subscribed_users.remove(user)
|
||||||
|
qs = Position.objects.filter(user=user, collection=self)
|
||||||
|
if qs.count() > 1:
|
||||||
|
pos = qs[0]
|
||||||
|
pos.section = 'personal'
|
||||||
|
pos.save()
|
||||||
|
elif value == 'featured':
|
||||||
|
if user.profile.capability('canEditFeaturedCollections'):
|
||||||
|
pos, created = Position.objects.get_or_create(collection=self, user=user,
|
||||||
|
section='featured')
|
||||||
|
if created:
|
||||||
|
qs = Position.objects.filter(user=user, section='featured')
|
||||||
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
||||||
|
pos.save()
|
||||||
|
Position.objects.filter(collection=self).exclude(id=pos.id).delete()
|
||||||
|
else:
|
||||||
|
value = self.status
|
||||||
|
elif self.status == 'featured' and value == 'public':
|
||||||
|
Position.objects.filter(collection=self).delete()
|
||||||
|
pos, created = Position.objects.get_or_create(collection=self,
|
||||||
|
user=self.user, section='personal')
|
||||||
|
qs = Position.objects.filter(user=self.user, section='personal')
|
||||||
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
||||||
|
pos.save()
|
||||||
|
for u in self.subscribed_users.all():
|
||||||
|
pos, created = Position.objects.get_or_create(collection=self, user=u,
|
||||||
|
section='public')
|
||||||
|
qs = Position.objects.filter(user=u, section='public')
|
||||||
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
||||||
|
pos.save()
|
||||||
|
|
||||||
|
self.status = value
|
||||||
|
elif key == 'name':
|
||||||
|
data['name'] = re.sub(' \[\d+\]$', '', data['name']).strip()
|
||||||
|
if not data['name']:
|
||||||
|
data['name'] = "Untitled"
|
||||||
|
name = data['name']
|
||||||
|
num = 1
|
||||||
|
while Collection.objects.filter(name=name, user=self.user).exclude(id=self.id).count() > 0:
|
||||||
|
num += 1
|
||||||
|
name = data['name'] + ' [%d]' % num
|
||||||
|
self.name = name
|
||||||
|
elif key == 'description':
|
||||||
|
self.description = ox.sanitize_html(data['description'])
|
||||||
|
|
||||||
|
if 'position' in data:
|
||||||
|
pos, created = Position.objects.get_or_create(collection=self, user=user)
|
||||||
|
pos.position = data['position']
|
||||||
|
pos.section = 'featured'
|
||||||
|
if self.status == 'private':
|
||||||
|
pos.section = 'personal'
|
||||||
|
pos.save()
|
||||||
|
if 'posterFrames' in data:
|
||||||
|
self.poster_frames = tuple(data['posterFrames'])
|
||||||
|
if 'view' in data:
|
||||||
|
self.view = data['view']
|
||||||
|
if 'sort' in data:
|
||||||
|
self.sort = tuple(data['sort'])
|
||||||
|
self.save()
|
||||||
|
if 'posterFrames' in data:
|
||||||
|
self.update_icon()
|
||||||
|
|
||||||
|
def json(self, keys=None, user=None):
|
||||||
|
if not keys:
|
||||||
|
keys = ['id', 'name', 'user', 'type', 'query', 'status', 'subscribed', 'posterFrames', 'description', 'view']
|
||||||
|
response = {}
|
||||||
|
for key in keys:
|
||||||
|
if key in ('items', 'documents'):
|
||||||
|
response[key] = self.get_numberofdocuments(user)
|
||||||
|
elif key == 'id':
|
||||||
|
response[key] = self.get_id()
|
||||||
|
elif key == 'user':
|
||||||
|
response[key] = self.user.username
|
||||||
|
elif key == 'query':
|
||||||
|
if not self.query.get('static', False):
|
||||||
|
response[key] = self.query
|
||||||
|
elif key == 'subscribers':
|
||||||
|
response[key] = self.subscribed_users.all().count()
|
||||||
|
elif key == 'subscribed':
|
||||||
|
if user and not user.is_anonymous():
|
||||||
|
response[key] = self.subscribed_users.filter(id=user.id).exists()
|
||||||
|
else:
|
||||||
|
response[key] = getattr(self, {
|
||||||
|
'posterFrames': 'poster_frames'
|
||||||
|
}.get(key, key))
|
||||||
|
return response
|
||||||
|
|
||||||
|
def path(self, name=''):
|
||||||
|
h = "%07d" % self.id
|
||||||
|
return os.path.join('collections', h[:2], h[2:4], h[4:6], h[6:], name)
|
||||||
|
|
||||||
|
def update_icon(self):
|
||||||
|
frames = []
|
||||||
|
#fixme
|
||||||
|
'''
|
||||||
|
if not self.poster_frames:
|
||||||
|
documents = self.get_documents(self.user)
|
||||||
|
if documents.count():
|
||||||
|
poster_frames = []
|
||||||
|
for i in range(0, documents.count(), max(1, int(documents.count()/4))):
|
||||||
|
poster_frames.append({
|
||||||
|
'document': documents[int(i)].id,
|
||||||
|
'position': documents[int(i)].poster_frame
|
||||||
|
})
|
||||||
|
self.poster_frames = tuple(poster_frames)
|
||||||
|
self.save()
|
||||||
|
for i in self.poster_frames:
|
||||||
|
from document.models import Document
|
||||||
|
qs = Document.objects.filter(id=i['document'])
|
||||||
|
if qs.count() > 0:
|
||||||
|
if i.get('position'):
|
||||||
|
frame = qs[0].frame(i['position'])
|
||||||
|
if frame:
|
||||||
|
frames.append(frame)
|
||||||
|
'''
|
||||||
|
self.icon.name = self.path('icon.jpg')
|
||||||
|
icon = self.icon.path
|
||||||
|
if frames:
|
||||||
|
while len(frames) < 4:
|
||||||
|
frames += frames
|
||||||
|
folder = os.path.dirname(icon)
|
||||||
|
ox.makedirs(folder)
|
||||||
|
for f in glob("%s/icon*.jpg" % folder):
|
||||||
|
os.unlink(f)
|
||||||
|
cmd = [
|
||||||
|
settings.collection_ICON,
|
||||||
|
'-f', ','.join(frames),
|
||||||
|
'-o', icon
|
||||||
|
]
|
||||||
|
p = subprocess.Popen(cmd, close_fds=True)
|
||||||
|
p.wait()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def get_icon(self, size=16):
|
||||||
|
path = self.path('icon%d.jpg' % size)
|
||||||
|
path = os.path.join(settings.MEDIA_ROOT, path)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
folder = os.path.dirname(path)
|
||||||
|
ox.makedirs(folder)
|
||||||
|
if self.icon and os.path.exists(self.icon.path):
|
||||||
|
source = self.icon.path
|
||||||
|
max_size = min(self.icon.width, self.icon.height)
|
||||||
|
else:
|
||||||
|
source = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
|
||||||
|
max_size = 256
|
||||||
|
if size < max_size:
|
||||||
|
extract.resize_image(source, path, size=size)
|
||||||
|
else:
|
||||||
|
path = source
|
||||||
|
return path
|
||||||
|
|
||||||
|
class CollectionDocument(models.Model):
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
collection = models.ForeignKey(Collection)
|
||||||
|
index = models.IntegerField(default=0)
|
||||||
|
document = models.ForeignKey('document.Document')
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u'%s in %s' % (self.document, self.collection)
|
||||||
|
|
||||||
|
class Position(models.Model):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("user", "collection", "section")
|
||||||
|
|
||||||
|
collection = models.ForeignKey(Collection, related_name='position')
|
||||||
|
user = models.ForeignKey(User, related_name='collection_positions')
|
||||||
|
section = models.CharField(max_length=255)
|
||||||
|
position = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u'%s/%s/%s' % (self.section, self.position, self.collection)
|
||||||
|
|
448
pandora/documentcollection/views.py
Normal file
448
pandora/documentcollection/views.py
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
|
from __future__ import division, print_function, absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.db.models import Max, Sum
|
||||||
|
from django.db import transaction
|
||||||
|
from django.conf import settings
|
||||||
|
from oxdjango.decorators import login_required_json
|
||||||
|
from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json, json_response
|
||||||
|
from oxdjango.http import HttpFileResponse
|
||||||
|
import ox
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from oxdjango.api import actions
|
||||||
|
from item import utils
|
||||||
|
from document.models import Document
|
||||||
|
from user.tasks import update_numberofcollections
|
||||||
|
from changelog.models import add_changelog
|
||||||
|
|
||||||
|
def get_collection_or_404_json(id):
|
||||||
|
id = id.split(':')
|
||||||
|
username = id[0]
|
||||||
|
collectionname = ":".join(id[1:])
|
||||||
|
return get_object_or_404_json(models.Collection, user__username=username, name=collectionname)
|
||||||
|
|
||||||
|
def _order_query(qs, sort):
|
||||||
|
order_by = []
|
||||||
|
for e in sort:
|
||||||
|
operator = e['operator']
|
||||||
|
if operator != '-':
|
||||||
|
operator = ''
|
||||||
|
key = {
|
||||||
|
'subscribed': 'subscribed_users',
|
||||||
|
'items': 'numberofitems'
|
||||||
|
}.get(e['key'], e['key'])
|
||||||
|
order = '%s%s' % (operator, key)
|
||||||
|
order_by.append(order)
|
||||||
|
if key == 'subscribers':
|
||||||
|
qs = qs.annotate(subscribers=Sum('subscribed_users'))
|
||||||
|
if order_by:
|
||||||
|
qs = qs.order_by(*order_by, nulls_last=True)
|
||||||
|
qs = qs.distinct()
|
||||||
|
return qs
|
||||||
|
|
||||||
|
def parse_query(data, user):
|
||||||
|
query = {}
|
||||||
|
query['range'] = [0, 100]
|
||||||
|
query['sort'] = [{'key':'user', 'operator':'+'}, {'key':'name', 'operator':'+'}]
|
||||||
|
for key in ('keys', 'group', 'collection', 'range', 'position', 'positions', 'sort'):
|
||||||
|
if key in data:
|
||||||
|
query[key] = data[key]
|
||||||
|
query['qs'] = models.Collection.objects.find(data, user)
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
def findCollections(request, data):
|
||||||
|
'''
|
||||||
|
Finds collections for a given query
|
||||||
|
takes {
|
||||||
|
query: object, // query object, see `find`
|
||||||
|
sort: [], // collection of sort objects, see `find`
|
||||||
|
range: [int, int], // range of results to return
|
||||||
|
keys: [string] // collection of properties to return
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
items: [object] // collection of collection objects
|
||||||
|
}
|
||||||
|
notes: Possible query keys are 'featured', 'name', 'subscribed' and 'user',
|
||||||
|
possible keys are 'featured', 'name', 'query', 'subscribed' and 'user'.
|
||||||
|
see: addCollection, editCollection, find, getCollection, removeCollection, sortCollections
|
||||||
|
'''
|
||||||
|
query = parse_query(data, request.user)
|
||||||
|
|
||||||
|
#order
|
||||||
|
is_section_request = query['sort'] == [{u'operator': u'+', u'key': u'position'}]
|
||||||
|
|
||||||
|
def is_featured_condition(x):
|
||||||
|
return x['key'] == 'status' and \
|
||||||
|
x['value'] == 'featured' and \
|
||||||
|
x['operator'] in ('=', '==')
|
||||||
|
|
||||||
|
is_featured = any(
|
||||||
|
is_featured_condition(x)
|
||||||
|
for x in data.get('query', {}).get('conditions', [])
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_section_request:
|
||||||
|
qs = query['qs']
|
||||||
|
if not is_featured and not request.user.is_anonymous():
|
||||||
|
qs = qs.filter(position__in=models.Position.objects.filter(user=request.user))
|
||||||
|
qs = qs.order_by('position__position')
|
||||||
|
else:
|
||||||
|
qs = _order_query(query['qs'], query['sort'])
|
||||||
|
|
||||||
|
response = json_response()
|
||||||
|
if 'keys' in data:
|
||||||
|
qs = qs[query['range'][0]:query['range'][1]]
|
||||||
|
|
||||||
|
response['data']['items'] = [l.json(data['keys'], request.user) for l in qs]
|
||||||
|
elif 'position' in data:
|
||||||
|
#FIXME: actually implement position requests
|
||||||
|
response['data']['position'] = 0
|
||||||
|
elif 'positions' in data:
|
||||||
|
ids = [i.get_id() for i in qs]
|
||||||
|
response['data']['positions'] = utils.get_positions(ids, query['positions'])
|
||||||
|
else:
|
||||||
|
response['data']['items'] = qs.count()
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(findCollections)
|
||||||
|
|
||||||
|
def getCollection(request, data):
|
||||||
|
'''
|
||||||
|
Gets a collection by id
|
||||||
|
takes {
|
||||||
|
id: string // collection id
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
id: string, // collection id
|
||||||
|
section: string, // collections section (like 'personal')
|
||||||
|
... // more key/value pairs
|
||||||
|
}
|
||||||
|
see: addCollection, editCollection, findCollections, removeCollection, sortCollections
|
||||||
|
'''
|
||||||
|
if 'id' in data:
|
||||||
|
response = json_response()
|
||||||
|
collection = get_collection_or_404_json(data['id'])
|
||||||
|
if collection.accessible(request.user):
|
||||||
|
response['data'] = collection.json(user=request.user)
|
||||||
|
else:
|
||||||
|
response = json_response(status=403, text='not allowed')
|
||||||
|
else:
|
||||||
|
response = json_response(status=404, text='not found')
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(getCollection)
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def addCollectionItems(request, data):
|
||||||
|
'''
|
||||||
|
Adds one or more items to a static collection
|
||||||
|
takes {
|
||||||
|
collection: string, // collection id
|
||||||
|
items: [string], // either collection of item ids
|
||||||
|
query: object // or query object, see `find`
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
see: find, orderCollectionItems, removeCollectionItems
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['collection'])
|
||||||
|
if 'items' in data:
|
||||||
|
if collection.editable(request.user):
|
||||||
|
with transaction.atomic():
|
||||||
|
items = [ox.fromAZ(id) for id in data['items']]
|
||||||
|
for item in Document.objects.filter(id__in=items):
|
||||||
|
collection.add(item)
|
||||||
|
response = json_response(status=200, text='items added')
|
||||||
|
add_changelog(request, data, data['collection'])
|
||||||
|
else:
|
||||||
|
response = json_response(status=403, text='not allowed')
|
||||||
|
elif 'query' in data:
|
||||||
|
response = json_response(status=501, text='not implemented')
|
||||||
|
else:
|
||||||
|
response = json_response(status=501, text='not implemented')
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(addCollectionItems, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def removeCollectionItems(request, data):
|
||||||
|
'''
|
||||||
|
Removes one or more items from a static collection
|
||||||
|
takes {
|
||||||
|
collection: string, // collection id
|
||||||
|
items: [itemId], // either collection of item ids
|
||||||
|
query: object // or query object, see `find`
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
see: addCollectionItems, find, orderCollectionItems
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['collection'])
|
||||||
|
if 'items' in data:
|
||||||
|
if collection.editable(request.user):
|
||||||
|
items = [ox.fromAZ(id) for id in data['items']]
|
||||||
|
collection.remove(documents=items)
|
||||||
|
response = json_response(status=200, text='items removed')
|
||||||
|
add_changelog(request, data, data['collection'])
|
||||||
|
else:
|
||||||
|
response = json_response(status=403, text='not allowed')
|
||||||
|
elif 'query' in data:
|
||||||
|
response = json_response(status=501, text='not implemented')
|
||||||
|
|
||||||
|
else:
|
||||||
|
response = json_response(status=501, text='not implemented')
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(removeCollectionItems, cache=False)
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def orderCollectionItems(request, data):
|
||||||
|
'''
|
||||||
|
Sets the manual ordering of items in a given collection
|
||||||
|
takes {
|
||||||
|
collection: string, // collection id
|
||||||
|
ids: [string] // ordered collection of item ids
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
notes: There is no UI for this yet.
|
||||||
|
see: addCollectionItems, removeCollectionItems
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['collection'])
|
||||||
|
response = json_response()
|
||||||
|
if collection.editable(request.user) and collection.type == 'static':
|
||||||
|
index = 0
|
||||||
|
with transaction.atomic():
|
||||||
|
for i in data['ids']:
|
||||||
|
models.CollectionItem.objects.filter(collection=collection, item__public_id=i).update(index=index)
|
||||||
|
index += 1
|
||||||
|
else:
|
||||||
|
response = json_response(status=403, text='permission denied')
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(orderCollectionItems, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def addCollection(request, data):
|
||||||
|
'''
|
||||||
|
Adds a new collection
|
||||||
|
takes {
|
||||||
|
name: value, // collection name (optional)
|
||||||
|
... // more key/value pairs
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
id: string, // collection id
|
||||||
|
name: string, // collection name
|
||||||
|
... // more key/value pairs
|
||||||
|
}
|
||||||
|
notes: Possible keys are 'description', 'items', 'name', 'query', 'sort',
|
||||||
|
'type' and 'view'.
|
||||||
|
see: editCollection, findCollections, getCollection, removeCollection, sortCollections
|
||||||
|
'''
|
||||||
|
data['name'] = re.sub(' \[\d+\]$', '', data.get('name', 'Untitled')).strip()
|
||||||
|
name = data['name']
|
||||||
|
if not name:
|
||||||
|
name = "Untitled"
|
||||||
|
num = 1
|
||||||
|
created = False
|
||||||
|
while not created:
|
||||||
|
collection, created = models.Collection.objects.get_or_create(name=name, user=request.user)
|
||||||
|
num += 1
|
||||||
|
name = data['name'] + ' [%d]' % num
|
||||||
|
|
||||||
|
del data['name']
|
||||||
|
if data:
|
||||||
|
collection.edit(data, request.user)
|
||||||
|
else:
|
||||||
|
collection.save()
|
||||||
|
update_numberofcollections.delay(request.user.username)
|
||||||
|
|
||||||
|
if 'items' in data:
|
||||||
|
items = [ox.fromAZ(id) for id in data['items']]
|
||||||
|
for item in Document.objects.filter(id__in=items):
|
||||||
|
collection.add(item)
|
||||||
|
|
||||||
|
if collection.status == 'featured':
|
||||||
|
pos, created = models.Position.objects.get_or_create(collection=collection,
|
||||||
|
user=request.user, section='featured')
|
||||||
|
qs = models.Position.objects.filter(section='featured')
|
||||||
|
else:
|
||||||
|
pos, created = models.Position.objects.get_or_create(collection=collection,
|
||||||
|
user=request.user, section='personal')
|
||||||
|
qs = models.Position.objects.filter(user=request.user, section='personal')
|
||||||
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
||||||
|
pos.save()
|
||||||
|
response = json_response(status=200, text='created')
|
||||||
|
response['data'] = collection.json()
|
||||||
|
add_changelog(request, data, collection.get_id())
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(addCollection, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def editCollection(request, data):
|
||||||
|
'''
|
||||||
|
Edits a collection
|
||||||
|
takes {
|
||||||
|
id: string, // collection id
|
||||||
|
key: value, // property id and new value
|
||||||
|
... // more key/value pairs
|
||||||
|
}
|
||||||
|
returns {
|
||||||
|
id: string, // collection id
|
||||||
|
... // more key/value pairs
|
||||||
|
}
|
||||||
|
notes: Possible keys are 'name', 'position', 'posterFrames', 'query' and
|
||||||
|
'status'. 'posterFrames' is an array of {item, position}. If you change
|
||||||
|
'status', you have to pass 'position' (the position of the collection in its new
|
||||||
|
collection folder).
|
||||||
|
see: addCollection, findCollections, getCollection, removeCollection, sortCollections
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['id'])
|
||||||
|
if collection.editable(request.user):
|
||||||
|
response = json_response()
|
||||||
|
collection.edit(data, request.user)
|
||||||
|
response['data'] = collection.json(user=request.user)
|
||||||
|
add_changelog(request, data)
|
||||||
|
else:
|
||||||
|
response = json_response(status=403, text='not allowed')
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(editCollection, cache=False)
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def removeCollection(request, data):
|
||||||
|
'''
|
||||||
|
Removes a collection
|
||||||
|
takes {
|
||||||
|
id: string // collection id
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
see: addCollection, editCollection, findCollections, getCollection, sortCollections
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['id'])
|
||||||
|
response = json_response()
|
||||||
|
if collection.editable(request.user):
|
||||||
|
add_changelog(request, data)
|
||||||
|
collection.delete()
|
||||||
|
update_numberofcollections.delay(request.user.username)
|
||||||
|
else:
|
||||||
|
response = json_response(status=403, text='not allowed')
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(removeCollection, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def subscribeToCollection(request, data):
|
||||||
|
'''
|
||||||
|
Adds a collection to favorites
|
||||||
|
takes {
|
||||||
|
id: string, // collection id
|
||||||
|
user: string // username (admin-only)
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
see: unsubscribeFromCollection
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['id'])
|
||||||
|
user = request.user
|
||||||
|
if collection.status == 'public' and \
|
||||||
|
collection.subscribed_users.filter(username=user.username).count() == 0:
|
||||||
|
collection.subscribed_users.add(user)
|
||||||
|
pos, created = models.Position.objects.get_or_create(collection=collection, user=user, section='public')
|
||||||
|
if created:
|
||||||
|
qs = models.Position.objects.filter(user=user, section='public')
|
||||||
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
||||||
|
pos.save()
|
||||||
|
add_changelog(request, data)
|
||||||
|
response = json_response()
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(subscribeToCollection, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def unsubscribeFromCollection(request, data):
|
||||||
|
'''
|
||||||
|
Removes a collection from favorites
|
||||||
|
takes {
|
||||||
|
id: string, // collection id
|
||||||
|
user: string // username (admin-only)
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
see: subscribeToCollection
|
||||||
|
'''
|
||||||
|
collection = get_collection_or_404_json(data['id'])
|
||||||
|
user = request.user
|
||||||
|
collection.subscribed_users.remove(user)
|
||||||
|
models.Position.objects.filter(collection=collection, user=user, section='public').delete()
|
||||||
|
response = json_response()
|
||||||
|
add_changelog(request, data)
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(unsubscribeFromCollection, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required_json
|
||||||
|
def sortCollections(request, data):
|
||||||
|
'''
|
||||||
|
Sets the order of collections in a given section
|
||||||
|
takes {
|
||||||
|
section: string, // collections section
|
||||||
|
ids: [string] // ordered collection of collections
|
||||||
|
}
|
||||||
|
returns {}
|
||||||
|
notes: Possible sections are 'personal', 'favorite' and 'featured'. Setting
|
||||||
|
the order of featured collections requires the appropriate capability.
|
||||||
|
see: addCollection, editCollection, findCollections, getCollection, removeCollection
|
||||||
|
'''
|
||||||
|
position = 0
|
||||||
|
section = data['section']
|
||||||
|
section = {
|
||||||
|
'favorite': 'public'
|
||||||
|
}.get(section, section)
|
||||||
|
#ids = collection(set(data['ids']))
|
||||||
|
ids = data['ids']
|
||||||
|
if section == 'featured' and not request.user.profile.capability('canEditFeaturedCollections'):
|
||||||
|
response = json_response(status=403, text='not allowed')
|
||||||
|
else:
|
||||||
|
user = request.user
|
||||||
|
if section == 'featured':
|
||||||
|
for i in ids:
|
||||||
|
l = get_collection_or_404_json(i)
|
||||||
|
qs = models.Position.objects.filter(section=section, collection=l)
|
||||||
|
if qs.count() > 0:
|
||||||
|
pos = qs[0]
|
||||||
|
else:
|
||||||
|
pos = models.Position(collection=l, user=user, section=section)
|
||||||
|
if pos.position != position:
|
||||||
|
pos.position = position
|
||||||
|
pos.save()
|
||||||
|
position += 1
|
||||||
|
models.Position.objects.filter(section=section, collection=l).exclude(id=pos.id).delete()
|
||||||
|
else:
|
||||||
|
for i in ids:
|
||||||
|
l = get_collection_or_404_json(i)
|
||||||
|
pos, created = models.Position.objects.get_or_create(collection=l,
|
||||||
|
user=request.user, section=section)
|
||||||
|
if pos.position != position:
|
||||||
|
pos.position = position
|
||||||
|
pos.save()
|
||||||
|
position += 1
|
||||||
|
|
||||||
|
response = json_response()
|
||||||
|
return render_to_json_response(response)
|
||||||
|
actions.register(sortCollections, cache=False)
|
||||||
|
|
||||||
|
|
||||||
|
def icon(request, id, size=16):
|
||||||
|
if not size:
|
||||||
|
size = 16
|
||||||
|
|
||||||
|
id = id.split(':')
|
||||||
|
username = id[0]
|
||||||
|
collectionname = ":".join(id[1:])
|
||||||
|
qs = models.Collection.objects.filter(user__username=username, name=collectionname)
|
||||||
|
if qs.count() == 1 and qs[0].accessible(request.user):
|
||||||
|
collection = qs[0]
|
||||||
|
icon = collection.get_icon(int(size))
|
||||||
|
else:
|
||||||
|
icon = os.path.join(settings.STATIC_ROOT, 'jpg/list256.jpg')
|
||||||
|
return HttpFileResponse(icon, content_type='image/jpeg')
|
|
@ -144,7 +144,7 @@ def autocompleteEntities(request, data):
|
||||||
values = leading_matches + leading_word_matches + matches
|
values = leading_matches + leading_word_matches + matches
|
||||||
else:
|
else:
|
||||||
values = [e.name for e in qs]
|
values = [e.name for e in qs]
|
||||||
values = values[data['range'][0]:data['range'][1]]
|
values = values[data['range'][0]:data['range'][1]]
|
||||||
response = json_response({})
|
response = json_response({})
|
||||||
response['data']['items'] = values
|
response['data']['items'] = values
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
|
|
|
@ -34,11 +34,13 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
if settings.DB_GIN_TRGM:
|
if settings.DB_GIN_TRGM:
|
||||||
import entity.models
|
import entity.models
|
||||||
|
import document.models
|
||||||
for table, column in (
|
for table, column in (
|
||||||
(models.ItemFind._meta.db_table, 'value'), # Item Find
|
(models.ItemFind._meta.db_table, 'value'), # Item Find
|
||||||
(models.Clip._meta.db_table, 'findvalue'), # Clip Find
|
(models.Clip._meta.db_table, 'findvalue'), # Clip Find
|
||||||
(models.Annotation._meta.db_table, 'findvalue'), # Annotation Find
|
(models.Annotation._meta.db_table, 'findvalue'), # Annotation Find
|
||||||
(entity.models.Find._meta.db_table, 'value'), # Entity Find
|
(entity.models.Find._meta.db_table, 'value'), # Entity Find
|
||||||
|
(document.models.Find._meta.db_table, 'value'), # Document Find
|
||||||
):
|
):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
indexes = connection.introspection.get_indexes(cursor, table)
|
indexes = connection.introspection.get_indexes(cursor, table)
|
||||||
|
|
|
@ -52,7 +52,7 @@ class Command(BaseCommand):
|
||||||
changes.append(sql)
|
changes.append(sql)
|
||||||
rebuild = True
|
rebuild = True
|
||||||
elif f.__class__.__name__ != db_types[name]:
|
elif f.__class__.__name__ != db_types[name]:
|
||||||
sql = 'ALTER TABLE "%s" DROP COLUMN "%s"' % (table_name, name )
|
sql = 'ALTER TABLE "%s" DROP COLUMN "%s"' % (table_name, name)
|
||||||
changes.append(sql)
|
changes.append(sql)
|
||||||
sql = 'ALTER TABLE "%s" ADD COLUMN "%s" %s' % (table_name, name, col_type)
|
sql = 'ALTER TABLE "%s" ADD COLUMN "%s" %s' % (table_name, name, col_type)
|
||||||
changes.append(sql)
|
changes.append(sql)
|
||||||
|
@ -116,3 +116,6 @@ class Command(BaseCommand):
|
||||||
if options['debug']:
|
if options['debug']:
|
||||||
print(i)
|
print(i)
|
||||||
i.update_sort()
|
i.update_sort()
|
||||||
|
# and udpate doucments
|
||||||
|
import document.sync_sort
|
||||||
|
document.sync_sort.update_tables(options['debug'])
|
||||||
|
|
|
@ -24,6 +24,7 @@ from django.utils import datetime_safe
|
||||||
|
|
||||||
import ox
|
import ox
|
||||||
from oxdjango import fields
|
from oxdjango import fields
|
||||||
|
from oxdjango.sortmodel import get_sort_field
|
||||||
import ox.web.imdb
|
import ox.web.imdb
|
||||||
import ox.image
|
import ox.image
|
||||||
|
|
||||||
|
@ -1741,27 +1742,9 @@ for key in filter(lambda k: k.get('sort', False) or k['type'] in ('integer', 'ti
|
||||||
sort_type = key.get('sortType', key['type'])
|
sort_type = key.get('sortType', key['type'])
|
||||||
if isinstance(sort_type, list):
|
if isinstance(sort_type, list):
|
||||||
sort_type = sort_type[0]
|
sort_type = sort_type[0]
|
||||||
model = {
|
field = get_sort_field(sort_type)
|
||||||
'char': (models.CharField, dict(null=True, max_length=1000, db_index=True)),
|
|
||||||
'year': (models.CharField, dict(null=True, max_length=4, db_index=True)),
|
|
||||||
'integer': (models.BigIntegerField, dict(null=True, blank=True, db_index=True)),
|
|
||||||
'float': (models.FloatField, dict(null=True, blank=True, db_index=True)),
|
|
||||||
'date': (models.DateTimeField, dict(null=True, blank=True, db_index=True))
|
|
||||||
}[{
|
|
||||||
'layer': 'char',
|
|
||||||
'string': 'char',
|
|
||||||
'title': 'char',
|
|
||||||
'person': 'char',
|
|
||||||
'year': 'year',
|
|
||||||
'words': 'integer',
|
|
||||||
'length': 'integer',
|
|
||||||
'date': 'date',
|
|
||||||
'hue': 'float',
|
|
||||||
'time': 'integer',
|
|
||||||
'enum': 'integer',
|
|
||||||
}.get(sort_type, sort_type)]
|
|
||||||
if name not in attrs:
|
if name not in attrs:
|
||||||
attrs[name] = model[0](**model[1])
|
attrs[name] = field[0](**field[1])
|
||||||
|
|
||||||
ItemSort = type('ItemSort', (models.Model,), attrs)
|
ItemSort = type('ItemSort', (models.Model,), attrs)
|
||||||
ItemSort.fields = [f.name for f in ItemSort._meta.fields]
|
ItemSort.fields = [f.name for f in ItemSort._meta.fields]
|
||||||
|
|
|
@ -326,7 +326,7 @@ def autocomplete(request, data):
|
||||||
returns {
|
returns {
|
||||||
items: [string, ...] // list of matching strings
|
items: [string, ...] // list of matching strings
|
||||||
}
|
}
|
||||||
see: autocompleteEntities
|
see: autocompleteDocuments, autocompleteEntities
|
||||||
'''
|
'''
|
||||||
if 'range' not in data:
|
if 'range' not in data:
|
||||||
data['range'] = [0, 10]
|
data['range'] = [0, 10]
|
||||||
|
|
25
pandora/oxdjango/sortmodel.py
Normal file
25
pandora/oxdjango/sortmodel.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
FIELDS = {
|
||||||
|
'char': (models.CharField, dict(null=True, max_length=1000, db_index=True)),
|
||||||
|
'year': (models.CharField, dict(null=True, max_length=4, db_index=True)),
|
||||||
|
'integer': (models.BigIntegerField, dict(null=True, blank=True, db_index=True)),
|
||||||
|
'float': (models.FloatField, dict(null=True, blank=True, db_index=True)),
|
||||||
|
'date': (models.DateTimeField, dict(null=True, blank=True, db_index=True))
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_sort_field(sort_type):
|
||||||
|
return FIELDS[{
|
||||||
|
'layer': 'char',
|
||||||
|
'string': 'char',
|
||||||
|
'text': 'char',
|
||||||
|
'title': 'char',
|
||||||
|
'person': 'char',
|
||||||
|
'year': 'year',
|
||||||
|
'words': 'integer',
|
||||||
|
'length': 'integer',
|
||||||
|
'date': 'date',
|
||||||
|
'hue': 'float',
|
||||||
|
'time': 'integer',
|
||||||
|
'enum': 'integer',
|
||||||
|
}.get(sort_type, sort_type)]
|
|
@ -138,6 +138,7 @@ INSTALLED_APPS = (
|
||||||
'user',
|
'user',
|
||||||
'urlalias',
|
'urlalias',
|
||||||
'tv',
|
'tv',
|
||||||
|
'documentcollection',
|
||||||
'document',
|
'document',
|
||||||
'entity',
|
'entity',
|
||||||
'websocket',
|
'websocket',
|
||||||
|
|
|
@ -19,6 +19,7 @@ import oxdjango.api.urls
|
||||||
import app.views
|
import app.views
|
||||||
import archive.views
|
import archive.views
|
||||||
import document.views
|
import document.views
|
||||||
|
import documentcollection.views
|
||||||
import text.views
|
import text.views
|
||||||
import user.views
|
import user.views
|
||||||
import edit.views
|
import edit.views
|
||||||
|
@ -42,6 +43,7 @@ urlpatterns = [
|
||||||
url(r'^file/(?P<oshash>.*)$', archive.views.lookup_file),
|
url(r'^file/(?P<oshash>.*)$', archive.views.lookup_file),
|
||||||
url(r'^api/?', include(oxdjango.api.urls)),
|
url(r'^api/?', include(oxdjango.api.urls)),
|
||||||
url(r'^resetUI$', user.views.reset_ui),
|
url(r'^resetUI$', user.views.reset_ui),
|
||||||
|
url(r'^collection/(?P<id>.*?)/icon(?P<size>\d*).jpg$', documentcollection.views.icon),
|
||||||
url(r'^documents/(?P<id>[A-Z0-9]+)/(?P<size>\d*)p(?P<page>[\d,]*).jpg$', document.views.thumbnail),
|
url(r'^documents/(?P<id>[A-Z0-9]+)/(?P<size>\d*)p(?P<page>[\d,]*).jpg$', document.views.thumbnail),
|
||||||
url(r'^documents/(?P<id>[A-Z0-9]+)/(?P<name>.*?\.[^\d]{3})$', document.views.file),
|
url(r'^documents/(?P<id>[A-Z0-9]+)/(?P<name>.*?\.[^\d]{3})$', document.views.file),
|
||||||
url(r'^edit/(?P<id>.*?)/icon(?P<size>\d*).jpg$', edit.views.icon),
|
url(r'^edit/(?P<id>.*?)/icon(?P<size>\d*).jpg$', edit.views.icon),
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-10-08 12:15
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('user', '0002_auto_20160219_1734'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='sessiondata',
|
||||||
|
name='numberofcollections',
|
||||||
|
field=models.IntegerField(default=0, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -18,6 +18,7 @@ from ox.utils import json
|
||||||
from itemlist.models import List, Position
|
from itemlist.models import List, Position
|
||||||
import text
|
import text
|
||||||
import edit
|
import edit
|
||||||
|
import documentcollection.models
|
||||||
|
|
||||||
from . import managers
|
from . import managers
|
||||||
from . import tasks
|
from . import tasks
|
||||||
|
@ -44,6 +45,7 @@ class SessionData(models.Model):
|
||||||
browser = models.CharField(max_length=255, null=True)
|
browser = models.CharField(max_length=255, null=True)
|
||||||
|
|
||||||
numberoflists = models.IntegerField(default=0, null=True)
|
numberoflists = models.IntegerField(default=0, null=True)
|
||||||
|
numberofcollections = models.IntegerField(default=0, null=True)
|
||||||
|
|
||||||
objects = managers.SessionDataManager()
|
objects = managers.SessionDataManager()
|
||||||
|
|
||||||
|
@ -96,6 +98,7 @@ class SessionData(models.Model):
|
||||||
else:
|
else:
|
||||||
self.groupssort = None
|
self.groupssort = None
|
||||||
self.numberoflists = self.user.lists.count()
|
self.numberoflists = self.user.lists.count()
|
||||||
|
self.numberofcollections = self.user.collections.count()
|
||||||
else:
|
else:
|
||||||
self.groupssort = None
|
self.groupssort = None
|
||||||
super(SessionData, self).save(*args, **kwargs)
|
super(SessionData, self).save(*args, **kwargs)
|
||||||
|
@ -157,6 +160,7 @@ class SessionData(models.Model):
|
||||||
'newsletter': False,
|
'newsletter': False,
|
||||||
'notes': '',
|
'notes': '',
|
||||||
'numberoflists': 0,
|
'numberoflists': 0,
|
||||||
|
'numberofcollections': 0,
|
||||||
'screensize': self.screensize,
|
'screensize': self.screensize,
|
||||||
'system': ua['system']['string'],
|
'system': ua['system']['string'],
|
||||||
'timesseen': self.timesseen,
|
'timesseen': self.timesseen,
|
||||||
|
@ -173,6 +177,7 @@ class SessionData(models.Model):
|
||||||
j['newsletter'] = p.newsletter
|
j['newsletter'] = p.newsletter
|
||||||
j['notes'] = p.notes
|
j['notes'] = p.notes
|
||||||
j['numberoflists'] = self.numberoflists
|
j['numberoflists'] = self.numberoflists
|
||||||
|
j['numberofcollections'] = self.numberofcollections
|
||||||
if keys:
|
if keys:
|
||||||
for key in list(j):
|
for key in list(j):
|
||||||
if key not in keys:
|
if key not in keys:
|
||||||
|
@ -249,9 +254,12 @@ def get_ui(user_ui, user=None):
|
||||||
ui[key] = new[key]
|
ui[key] = new[key]
|
||||||
return ui
|
return ui
|
||||||
ui = update_ui(ui, user_ui)
|
ui = update_ui(ui, user_ui)
|
||||||
if not 'lists' in ui:
|
if 'lists' not in ui:
|
||||||
ui['lists'] = {}
|
ui['lists'] = {}
|
||||||
|
|
||||||
|
if 'collections' not in ui:
|
||||||
|
ui['collections'] = {}
|
||||||
|
|
||||||
def add(lists, section):
|
def add(lists, section):
|
||||||
ids = []
|
ids = []
|
||||||
for l in lists:
|
for l in lists:
|
||||||
|
@ -278,7 +286,30 @@ def get_ui(user_ui, user=None):
|
||||||
'''
|
'''
|
||||||
ids.append(id)
|
ids.append(id)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
|
def add_collections(collections, section):
|
||||||
|
Position = documentcollection.models.Position
|
||||||
|
ids = []
|
||||||
|
for l in collections:
|
||||||
|
qs = Position.objects.filter(section=section)
|
||||||
|
if section == 'featured':
|
||||||
|
try:
|
||||||
|
pos = Position.objects.get(collection=l, section=section)
|
||||||
|
created = False
|
||||||
|
except Position.DoesNotExist:
|
||||||
|
pos = Position(collection=l, section=section, user=l.user)
|
||||||
|
pos.save()
|
||||||
|
created = True
|
||||||
|
else:
|
||||||
|
pos, created = Position.objects.get_or_create(collection=l, user=user, section=section)
|
||||||
|
qs = qs.filter(user=user)
|
||||||
|
if created:
|
||||||
|
pos.position = qs.aggregate(Max('position'))['position__max'] + 1
|
||||||
|
pos.save()
|
||||||
|
id = l.get_id()
|
||||||
|
ids.append(id)
|
||||||
|
return ids
|
||||||
|
|
||||||
def add_texts(texts, section):
|
def add_texts(texts, section):
|
||||||
P = text.models.Position
|
P = text.models.Position
|
||||||
ids = []
|
ids = []
|
||||||
|
@ -323,6 +354,7 @@ def get_ui(user_ui, user=None):
|
||||||
ids.append(t.get_id())
|
ids.append(t.get_id())
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
|
# lists (items)
|
||||||
ids = ['']
|
ids = ['']
|
||||||
if user:
|
if user:
|
||||||
ids += add(user.lists.exclude(status="featured"), 'personal')
|
ids += add(user.lists.exclude(status="featured"), 'personal')
|
||||||
|
@ -331,11 +363,26 @@ def get_ui(user_ui, user=None):
|
||||||
for i in list(ui['lists']):
|
for i in list(ui['lists']):
|
||||||
if i not in ids:
|
if i not in ids:
|
||||||
del ui['lists'][i]
|
del ui['lists'][i]
|
||||||
|
|
||||||
|
# collections (documents)
|
||||||
|
ids = ['']
|
||||||
|
if user:
|
||||||
|
ids += add_collections(user.collections.exclude(status="featured"), 'personal')
|
||||||
|
ids += add_collections(user.subscribed_collections.filter(status='public'), 'public')
|
||||||
|
ids += add_collections(documentcollection.models.Collection.objects.filter(status='featured'), 'featured')
|
||||||
|
for i in list(ui['collections']):
|
||||||
|
if i not in ids:
|
||||||
|
del ui['collections'][i]
|
||||||
|
|
||||||
|
# texts (remove)
|
||||||
tids = ['']
|
tids = ['']
|
||||||
if user:
|
if user:
|
||||||
tids += add_texts(user.texts.exclude(status="featured"), 'personal')
|
tids += add_texts(user.texts.exclude(status="featured"), 'personal')
|
||||||
tids += add_texts(user.subscribed_texts.filter(status='public'), 'public')
|
tids += add_texts(user.subscribed_texts.filter(status='public'), 'public')
|
||||||
tids += add_texts(text.models.Text.objects.filter(status='featured'), 'featured')
|
tids += add_texts(text.models.Text.objects.filter(status='featured'), 'featured')
|
||||||
|
|
||||||
|
# edits
|
||||||
|
tids = ['']
|
||||||
if user:
|
if user:
|
||||||
tids += add_edits(user.edits.exclude(status="featured"), 'personal')
|
tids += add_edits(user.edits.exclude(status="featured"), 'personal')
|
||||||
tids += add_edits(user.subscribed_edits.filter(status='public'), 'public')
|
tids += add_edits(user.subscribed_edits.filter(status='public'), 'public')
|
||||||
|
|
|
@ -46,3 +46,13 @@ def update_numberoflists(username):
|
||||||
).update(
|
).update(
|
||||||
numberoflists=user.lists.count()
|
numberoflists=user.lists.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@task(ignore_results=True, queue='default')
|
||||||
|
def update_numberofcollections(username):
|
||||||
|
from . import models
|
||||||
|
user = models.User.objects.get(username=username)
|
||||||
|
models.SessionData.objects.filter(
|
||||||
|
user=user
|
||||||
|
).update(
|
||||||
|
numberofcollections=user.collections.count()
|
||||||
|
)
|
||||||
|
|
|
@ -800,7 +800,10 @@ def setUI(request, data):
|
||||||
access, created = Access.objects.get_or_create(item=item, user=None)
|
access, created = Access.objects.get_or_create(item=item, user=None)
|
||||||
if not created:
|
if not created:
|
||||||
access.save()
|
access.save()
|
||||||
|
if data.get('document'):
|
||||||
|
import document.models
|
||||||
|
doc = get_object_or_404_json(document.models.Document, id=ox.fromAZ(data['document']))
|
||||||
|
doc.update_access(request.user)
|
||||||
response = json_response()
|
response = json_response()
|
||||||
return render_to_json_response(response)
|
return render_to_json_response(response)
|
||||||
actions.register(setUI, cache=False)
|
actions.register(setUI, cache=False)
|
||||||
|
|
|
@ -28,6 +28,12 @@ pandora.UI = (function() {
|
||||||
pandora.user.ui._findState = pandora.getFindState(
|
pandora.user.ui._findState = pandora.getFindState(
|
||||||
pandora.user.ui.find
|
pandora.user.ui.find
|
||||||
);
|
);
|
||||||
|
pandora.user.ui._collection = pandora.getCollectionState(
|
||||||
|
pandora.user.ui.findDocuments
|
||||||
|
);
|
||||||
|
pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(
|
||||||
|
pandora.user.ui.findDocuments
|
||||||
|
);
|
||||||
Ox.Theme(pandora.user.ui.theme);
|
Ox.Theme(pandora.user.ui.theme);
|
||||||
pandora.$ui.appPanel.reload();
|
pandora.$ui.appPanel.reload();
|
||||||
});
|
});
|
||||||
|
@ -40,6 +46,9 @@ pandora.UI = (function() {
|
||||||
|
|
||||||
var add = {},
|
var add = {},
|
||||||
args,
|
args,
|
||||||
|
collection,
|
||||||
|
collectionView,
|
||||||
|
collectionSettings = pandora.site.collectionSettings,
|
||||||
editSettings = pandora.site.editSettings,
|
editSettings = pandora.site.editSettings,
|
||||||
item,
|
item,
|
||||||
list,
|
list,
|
||||||
|
@ -69,6 +78,76 @@ pandora.UI = (function() {
|
||||||
} else if (args.section == 'edits') {
|
} else if (args.section == 'edits') {
|
||||||
trigger.section = args.section;
|
trigger.section = args.section;
|
||||||
trigger.edit = args.edit;
|
trigger.edit = args.edit;
|
||||||
|
} else if (pandora.user.ui.section == 'documents' || args.section == 'documents') {
|
||||||
|
if ('findDocuments' in args) {
|
||||||
|
// the challenge here is that find may change list,
|
||||||
|
// and list may then change listSort and listView,
|
||||||
|
// which we don't want to trigger, since find triggers
|
||||||
|
// (values we put in add will be changed, but won't trigger)
|
||||||
|
collection = pandora.getCollectionState(args.findDocuments);
|
||||||
|
pandora.user.ui._collection = collection;
|
||||||
|
pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(args.findDocuments);
|
||||||
|
if (pandora.$ui.appPanel && !pandora.stayInItemView) {
|
||||||
|
// if we're not on page load, and if find isn't a context change
|
||||||
|
// caused by an edit, then switch from item view to list view
|
||||||
|
args['document'] = '';
|
||||||
|
}
|
||||||
|
if (collection != self.previousUI._collection) {
|
||||||
|
Ox.Log('UI', 'FIND HAS CHANGED COLLECTION', self.previousUI._collection, '>', collection);
|
||||||
|
// if find has changed collection
|
||||||
|
Ox.forEach(collectionSettings, function(collectionSetting, setting) {
|
||||||
|
// then for each setting that corresponds to a collection setting
|
||||||
|
if (!Ox.isUndefined(args[setting])) {
|
||||||
|
add[setting] = args[setting];
|
||||||
|
} else if (
|
||||||
|
!pandora.user.ui.collections[collection]
|
||||||
|
|| Ox.isUndefined(pandora.user.ui.collections[collection][collectionSetting])
|
||||||
|
) {
|
||||||
|
// either add the default setting
|
||||||
|
add[setting] = pandora.site.user.ui[setting];
|
||||||
|
} else {
|
||||||
|
// or the existing collection setting
|
||||||
|
add[setting] = pandora.user.ui.collections[collection][collectionSetting];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collection = self.previousUI._collection;
|
||||||
|
}
|
||||||
|
if (!pandora.user.ui.collections[collection]) {
|
||||||
|
add['collections.' + that.encode(collection)] = {};
|
||||||
|
}
|
||||||
|
Ox.forEach(collectionSettings, function(collectionSetting, setting) {
|
||||||
|
// for each setting that corresponds to a collection setting
|
||||||
|
// set that collection setting to
|
||||||
|
var key = 'collections.' + that.encode(collection) + '.' + collectionSetting;
|
||||||
|
if (setting in args) {
|
||||||
|
// the setting passed to UI.set
|
||||||
|
add[key] = args[setting];
|
||||||
|
} else if (setting in add) {
|
||||||
|
// or the setting changed via find
|
||||||
|
add[key] = add[setting];
|
||||||
|
} else if (!pandora.user.ui.collections[collection]) {
|
||||||
|
// or the default setting
|
||||||
|
add[key] = pandora.site.user.ui[setting];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// set nested lisColumnWidth updates
|
||||||
|
Ox.forEach(args, function(value, key) {
|
||||||
|
if (Ox.startsWith(key, 'collectionColumnWidth.')) {
|
||||||
|
key = 'collections.' + that.encode(collection) + '.columnWidth.'
|
||||||
|
+ key.slice('collectionColumnWidth.'.length);
|
||||||
|
if (!(key in add)) {
|
||||||
|
add[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (args.document) {
|
||||||
|
// when switching to an item, update list selection
|
||||||
|
add['collectionSelection'] = [args.document];
|
||||||
|
add['collections.' + that.encode(collection) + '.selection'] = [args.document];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ('find' in args) {
|
if ('find' in args) {
|
||||||
// the challenge here is that find may change list,
|
// the challenge here is that find may change list,
|
||||||
|
@ -296,7 +375,7 @@ pandora.UI = (function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
pandora.URL.update(Object.keys(
|
pandora.URL.update(Object.keys(
|
||||||
!pandora.$ui.appPanel ? args : trigger
|
!pandora.$ui.appPanel ? args : trigger
|
||||||
));
|
));
|
||||||
|
|
|
@ -20,13 +20,15 @@ pandora.URL = (function() {
|
||||||
Ox.contains(Object.keys(pandora.site.user.ui.part), state.page)
|
Ox.contains(Object.keys(pandora.site.user.ui.part), state.page)
|
||||||
) {
|
) {
|
||||||
state.part = pandora.user.ui.part[state.page];
|
state.part = pandora.user.ui.part[state.page];
|
||||||
|
/*
|
||||||
if (
|
if (
|
||||||
state.page == 'documents'
|
state.page == 'documents'
|
||||||
&& pandora.user.ui.documents[state.part]
|
&& pandora.user.ui.documents[state.part]
|
||||||
&& pandora.user.ui.documents[state.part].position
|
&& pandora.user.ui.documents[state.part].position
|
||||||
) {
|
) {
|
||||||
state.span = pandora.user.ui.documents[state.part].position;
|
state.span = pandora.user.ui.documents[state.part].position;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,6 +67,25 @@ pandora.URL = (function() {
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (pandora.user.ui.section == 'documents') {
|
||||||
|
if (!pandora.user.ui.document) {
|
||||||
|
state.view = pandora.user.ui.collectionView;
|
||||||
|
state.sort = [pandora.user.ui.collectionSort[0]];
|
||||||
|
state.find = pandora.user.ui.findDocuments;
|
||||||
|
} else {
|
||||||
|
var documentState = pandora.user.ui.documents[state.item] || {},
|
||||||
|
position = documentState.position || 0;
|
||||||
|
if (pandora.user.ui.documentView == 'view') {
|
||||||
|
if (documentState.name) {
|
||||||
|
state.span = documentState.name;
|
||||||
|
} else if (position) {
|
||||||
|
state.span = [position];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state.view = pandora.user.ui.documentView;
|
||||||
|
}
|
||||||
|
state.sort = [pandora.user.ui.collectionSort[0]];
|
||||||
|
}
|
||||||
} else if (pandora.user.ui.section == 'edits') {
|
} else if (pandora.user.ui.section == 'edits') {
|
||||||
var editPoints = pandora.user.ui.edits[state.item] || {};
|
var editPoints = pandora.user.ui.edits[state.item] || {};
|
||||||
if (state.item) {
|
if (state.item) {
|
||||||
|
@ -118,10 +139,15 @@ pandora.URL = (function() {
|
||||||
var set = {};
|
var set = {};
|
||||||
|
|
||||||
Ox.Log('URL', 'setState:', state);
|
Ox.Log('URL', 'setState:', state);
|
||||||
|
if (state.type == 'texts') {
|
||||||
|
state.type = 'documents';
|
||||||
|
}
|
||||||
|
|
||||||
pandora.user.ui._list = pandora.getListState(pandora.user.ui.find);
|
pandora.user.ui._list = pandora.getListState(pandora.user.ui.find);
|
||||||
pandora.user.ui._filterState = pandora.getFilterState(pandora.user.ui.find);
|
pandora.user.ui._filterState = pandora.getFilterState(pandora.user.ui.find);
|
||||||
pandora.user.ui._findState = pandora.getFindState(pandora.user.ui.find);
|
pandora.user.ui._findState = pandora.getFindState(pandora.user.ui.find);
|
||||||
|
pandora.user.ui._collection = pandora.getCollectionState(pandora.user.ui.findDocuments);
|
||||||
|
pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(pandora.user.ui.findDocuments);
|
||||||
|
|
||||||
if (Ox.isEmpty(state)) {
|
if (Ox.isEmpty(state)) {
|
||||||
|
|
||||||
|
@ -148,9 +174,6 @@ pandora.URL = (function() {
|
||||||
) && state.part) {
|
) && state.part) {
|
||||||
set['part.' + state.page] = state.part;
|
set['part.' + state.page] = state.part;
|
||||||
}
|
}
|
||||||
if (state.span) {
|
|
||||||
set['documents.' + state.part] = {position: state.span};
|
|
||||||
}
|
|
||||||
pandora.UI.set(set);
|
pandora.UI.set(set);
|
||||||
callback && callback();
|
callback && callback();
|
||||||
|
|
||||||
|
@ -222,7 +245,16 @@ pandora.URL = (function() {
|
||||||
set.find = pandora.site.user.ui.find;
|
set.find = pandora.site.user.ui.find;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (state.type == 'documents') {
|
||||||
|
if (state.view) {
|
||||||
|
set[!state.item ? 'collectionView' : 'documentView'] = state.view;
|
||||||
|
}
|
||||||
|
if (state.span) {
|
||||||
|
set['documents.' + state.item] = {position: state.span};
|
||||||
|
}
|
||||||
|
if (!state.item && state.find) {
|
||||||
|
set.findDocuments = state.find;
|
||||||
|
}
|
||||||
} else if (state.type == 'edits') {
|
} else if (state.type == 'edits') {
|
||||||
|
|
||||||
if (state.view) {
|
if (state.view) {
|
||||||
|
@ -248,16 +280,6 @@ pandora.URL = (function() {
|
||||||
set[key + '.clip'] = state.span;
|
set[key + '.clip'] = state.span;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (state.type == 'texts') {
|
|
||||||
|
|
||||||
if (state.span) {
|
|
||||||
set['texts.' + pandora.UI.encode(state.item)] = {
|
|
||||||
position: Ox.isArray(state.span) ? state.span[0] : 0,
|
|
||||||
name: Ox.isArray(state.span) ? '' : state.span
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ox.Request.cancel();
|
Ox.Request.cancel();
|
||||||
|
@ -368,6 +390,19 @@ pandora.URL = (function() {
|
||||||
.concat(pandora.site.itemKeys);
|
.concat(pandora.site.itemKeys);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Documents
|
||||||
|
views['documents'] = {
|
||||||
|
list: ['grid', 'list'],
|
||||||
|
item: ['view', 'info']
|
||||||
|
};
|
||||||
|
sortKeys['documents'] = {
|
||||||
|
list: {
|
||||||
|
list: pandora.site.documentKeys,
|
||||||
|
grid: pandora.site.documentKeys
|
||||||
|
},
|
||||||
|
item: {}
|
||||||
|
};
|
||||||
|
|
||||||
// Texts
|
// Texts
|
||||||
views['texts'] = {
|
views['texts'] = {
|
||||||
list: [],
|
list: [],
|
||||||
|
@ -377,7 +412,6 @@ pandora.URL = (function() {
|
||||||
list: {},
|
list: {},
|
||||||
item: {}
|
item: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
views: views,
|
views: views,
|
||||||
sortKeys: sortKeys
|
sortKeys: sortKeys
|
||||||
|
@ -388,7 +422,7 @@ pandora.URL = (function() {
|
||||||
that.init = function() {
|
that.init = function() {
|
||||||
|
|
||||||
var itemsSection = pandora.site.itemsSection,
|
var itemsSection = pandora.site.itemsSection,
|
||||||
findKeys, spanType = {};
|
findKeys = {}, spanType = {};
|
||||||
|
|
||||||
spanType[itemsSection] = {
|
spanType[itemsSection] = {
|
||||||
list: {
|
list: {
|
||||||
|
@ -411,12 +445,25 @@ pandora.URL = (function() {
|
||||||
annotations: 'duration'
|
annotations: 'duration'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
spanType['documents'] = {
|
||||||
|
list: {},
|
||||||
|
item: {
|
||||||
|
view: 'string',
|
||||||
|
}
|
||||||
|
};
|
||||||
spanType['texts'] = {
|
spanType['texts'] = {
|
||||||
list: {},
|
list: {},
|
||||||
item: {text: 'string'}
|
item: {text: 'string'}
|
||||||
};
|
};
|
||||||
|
|
||||||
findKeys = [{id: 'list', type: 'string'}].concat(pandora.site.itemKeys);
|
findKeys[itemsSection] = [
|
||||||
|
{id: 'list', type: 'string'},
|
||||||
|
].concat(pandora.site.itemKeys);
|
||||||
|
|
||||||
|
findKeys['edits'] = [];
|
||||||
|
findKeys['documents'] = [
|
||||||
|
{id: 'collection', type: 'string'}
|
||||||
|
].concat(pandora.site.documentKeys);
|
||||||
|
|
||||||
self.URL = Ox.URL(Ox.extend({
|
self.URL = Ox.URL(Ox.extend({
|
||||||
findKeys: findKeys,
|
findKeys: findKeys,
|
||||||
|
@ -426,14 +473,14 @@ pandora.URL = (function() {
|
||||||
getSort: pandora.getSort,
|
getSort: pandora.getSort,
|
||||||
getSpan: pandora.getSpan,
|
getSpan: pandora.getSpan,
|
||||||
pages: [].concat(
|
pages: [].concat(
|
||||||
['home', 'software', 'api', 'help', 'tv', 'documents', 'entities'],
|
['home', 'software', 'api', 'help', 'tv', 'entities'],
|
||||||
pandora.site.sitePages.map(function(page) {
|
pandora.site.sitePages.map(function(page) {
|
||||||
return page.id;
|
return page.id;
|
||||||
}),
|
}),
|
||||||
['preferences', 'signup', 'signin', 'signout']
|
['preferences', 'signup', 'signin', 'signout']
|
||||||
),
|
),
|
||||||
spanType: spanType,
|
spanType: spanType,
|
||||||
types: [pandora.site.itemName.plural.toLowerCase(), 'edits', 'texts'],
|
types: [pandora.site.itemName.plural.toLowerCase(), 'edits', 'documents', 'texts'],
|
||||||
}, getOptions()));
|
}, getOptions()));
|
||||||
|
|
||||||
window.addEventListener('hashchange', function() {
|
window.addEventListener('hashchange', function() {
|
||||||
|
@ -508,7 +555,7 @@ pandora.URL = (function() {
|
||||||
that.push = function(stateOrURL, expandURL) {
|
that.push = function(stateOrURL, expandURL) {
|
||||||
var state,
|
var state,
|
||||||
title = pandora.getPageTitle(stateOrURL)
|
title = pandora.getPageTitle(stateOrURL)
|
||||||
|| pandora.getDocumentTitle(),
|
|| pandora.getWindowTitle(),
|
||||||
url;
|
url;
|
||||||
pandora.replaceURL = expandURL;
|
pandora.replaceURL = expandURL;
|
||||||
if (Ox.isObject(stateOrURL)) {
|
if (Ox.isObject(stateOrURL)) {
|
||||||
|
@ -524,7 +571,7 @@ pandora.URL = (function() {
|
||||||
that.replace = function(stateOrURL, title) {
|
that.replace = function(stateOrURL, title) {
|
||||||
var state,
|
var state,
|
||||||
title = pandora.getPageTitle(stateOrURL)
|
title = pandora.getPageTitle(stateOrURL)
|
||||||
|| pandora.getDocumentTitle(),
|
|| pandora.getWindowTitle(),
|
||||||
url;
|
url;
|
||||||
if (Ox.isObject(stateOrURL)) {
|
if (Ox.isObject(stateOrURL)) {
|
||||||
state = stateOrURL;
|
state = stateOrURL;
|
||||||
|
@ -576,7 +623,7 @@ pandora.URL = (function() {
|
||||||
state = getState();
|
state = getState();
|
||||||
self.URL[action](
|
self.URL[action](
|
||||||
state,
|
state,
|
||||||
pandora.getPageTitle(state) || pandora.getDocumentTitle()
|
pandora.getPageTitle(state) || pandora.getWindowTitle()
|
||||||
);
|
);
|
||||||
pandora.replaceURL = false;
|
pandora.replaceURL = false;
|
||||||
}
|
}
|
||||||
|
|
109
static/js/addDocumentDialog.js
Normal file
109
static/js/addDocumentDialog.js
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.addDocumentDialog = function(options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var input = '';
|
||||||
|
|
||||||
|
var selected = options.selected ? options.selected : 'upload';
|
||||||
|
|
||||||
|
var $button;
|
||||||
|
|
||||||
|
var $panel = Ox.TabPanel({
|
||||||
|
content: function(id) {
|
||||||
|
var $content = Ox.Element().css({padding: '8px'});
|
||||||
|
var $input = Ox.Input({
|
||||||
|
changeOnKeypress: true,
|
||||||
|
disabled: id == 'upload',
|
||||||
|
label: Ox._(id == 'add' ? 'Title' : id == 'upload' ? 'File': 'URL'),
|
||||||
|
labelWidth: 64,
|
||||||
|
placeholder: '',
|
||||||
|
width: 512
|
||||||
|
}).css({
|
||||||
|
margin: '8px'
|
||||||
|
}).bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
$button.options({disabled: !data.value});
|
||||||
|
input = data.value;
|
||||||
|
}
|
||||||
|
}).appendTo($content);
|
||||||
|
return $content;
|
||||||
|
},
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
id: 'add',
|
||||||
|
title: Ox._('Add {0}', [Ox._('Document')]),
|
||||||
|
disabled: false,
|
||||||
|
selected: selected == 'add'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'upload',
|
||||||
|
title: Ox._('Upload Documents'),
|
||||||
|
selected: selected == 'upload'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}).bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
selected = data.selected;
|
||||||
|
that.options({buttons: [createButton()]});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var $screen = Ox.LoadingScreen({
|
||||||
|
size: 16
|
||||||
|
});
|
||||||
|
|
||||||
|
var that = Ox.Dialog({
|
||||||
|
buttons: [createButton()],
|
||||||
|
closeButton: true,
|
||||||
|
content: $panel,
|
||||||
|
height: 72,
|
||||||
|
removeOnClose: true,
|
||||||
|
title: Ox._('Add {0}', [Ox._('Document')]),
|
||||||
|
width: 544
|
||||||
|
});
|
||||||
|
|
||||||
|
function createButton() {
|
||||||
|
$button = Ox[selected == 'upload' ? 'FileButton' : 'Button']({
|
||||||
|
disabled: selected != 'upload',
|
||||||
|
id: selected,
|
||||||
|
title: selected == 'add'
|
||||||
|
? Ox._('Add {0}', ['Document'])
|
||||||
|
: Ox._('Select Documents'),
|
||||||
|
width: selected == 'add' ? 192 : 128
|
||||||
|
}).bindEvent({
|
||||||
|
click: function(data) {
|
||||||
|
if (selected == 'add') {
|
||||||
|
that.options({content: $screen.start()});
|
||||||
|
$button.options({disabled: true});
|
||||||
|
pandora.api.addDocument({title: input}, function(result) {
|
||||||
|
Ox.Request.clearCache('find');
|
||||||
|
$screen.stop();
|
||||||
|
that.close();
|
||||||
|
pandora.UI.set({
|
||||||
|
document: result.data.id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (selected == 'upload' && data.files.length > 0) {
|
||||||
|
that.close();
|
||||||
|
pandora.ui.uploadDocumentDialog({
|
||||||
|
files: data.files
|
||||||
|
}, function(files) {
|
||||||
|
if (files) {
|
||||||
|
Ox.Request.clearCache('findDocuments');
|
||||||
|
if (pandora.user.ui.document) {
|
||||||
|
pandora.UI.set({document: ''});
|
||||||
|
} else {
|
||||||
|
pandora.$ui.list && pandora.$ui.list.reloadList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $button;
|
||||||
|
}
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
|
@ -8,8 +8,10 @@ pandora.ui.allItems = function(section) {
|
||||||
|
|
||||||
var canAddItems = !pandora.site.itemRequiresVideo && pandora.site.capabilities.canAddItems[pandora.user.level],
|
var canAddItems = !pandora.site.itemRequiresVideo && pandora.site.capabilities.canAddItems[pandora.user.level],
|
||||||
canUploadVideo = pandora.site.capabilities.canAddItems[pandora.user.level],
|
canUploadVideo = pandora.site.capabilities.canAddItems[pandora.user.level],
|
||||||
|
canAddDocuments = pandora.site.capabilities.canAddDocuments[pandora.user.level],
|
||||||
|
isSelected = pandora.user.ui._list || pandora.user.ui._collection,
|
||||||
that = Ox.Element()
|
that = Ox.Element()
|
||||||
.addClass('OxSelectableElement' + (pandora.user.ui._list ? '' : ' OxSelected'))
|
.addClass('OxSelectableElement' + (isSelected ? '' : ' OxSelected'))
|
||||||
.css({
|
.css({
|
||||||
height: '16px',
|
height: '16px',
|
||||||
cursor: 'default',
|
cursor: 'default',
|
||||||
|
@ -19,13 +21,21 @@ pandora.ui.allItems = function(section) {
|
||||||
click: function() {
|
click: function() {
|
||||||
that.gainFocus();
|
that.gainFocus();
|
||||||
if (section == 'items') {
|
if (section == 'items') {
|
||||||
pandora.user.ui._list && pandora.UI.set({find: {conditions: [], operator: '&'}});
|
pandora.user.ui._list && pandora.UI.set({
|
||||||
|
find: {conditions: [], operator: '&'}
|
||||||
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
pandora.user.ui._collection && pandora.UI.set({
|
||||||
|
findDocuments: {conditions: [], operator: '&'}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set(section.slice(0, -1), '');
|
pandora.UI.set(section.slice(0, -1), '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
|
pandora_document: updateSelected,
|
||||||
|
pandora_finddocuments: updateSelected,
|
||||||
pandora_edit: updateSelected,
|
pandora_edit: updateSelected,
|
||||||
pandora_find: updateSelected,
|
pandora_find: updateSelected,
|
||||||
pandora_section: updateSelected,
|
pandora_section: updateSelected,
|
||||||
|
@ -95,6 +105,56 @@ pandora.ui.allItems = function(section) {
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
that.update(result.data.items);
|
that.update(result.data.items);
|
||||||
});
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
$items = $('<div>')
|
||||||
|
.css({
|
||||||
|
float: 'left',
|
||||||
|
width: '42px',
|
||||||
|
margin: '1px 4px 1px 3px',
|
||||||
|
textAlign: 'right'
|
||||||
|
})
|
||||||
|
.appendTo(that);
|
||||||
|
$buttons[0] = Ox.Button({
|
||||||
|
style: 'symbol',
|
||||||
|
title: 'add',
|
||||||
|
tooltip: canAddDocuments ? Ox._('Add {0}', [Ox._('Document')]) : '',
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.css({opacity: canAddDocuments ? 1 : 0.25})
|
||||||
|
.hide()
|
||||||
|
.bindEvent({
|
||||||
|
click: function(data) {
|
||||||
|
if (canAddDocuments) {
|
||||||
|
pandora.$ui.addDocumentDialog = pandora.ui.addDocumentDialog({
|
||||||
|
selected: 'add'
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo(that);
|
||||||
|
$buttons[1] = Ox.Button({
|
||||||
|
style: 'symbol',
|
||||||
|
title: 'upload',
|
||||||
|
tooltip: canAddDocuments ? Ox._('Upload {0}', [Ox._('Document')]) : '',
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.css({opacity: canAddDocuments ? 1 : 0.25})
|
||||||
|
.hide()
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
if (canAddDocuments) {
|
||||||
|
pandora.$ui.addDocumentDialog = pandora.ui.addDocumentDialog({
|
||||||
|
selected: 'upload'
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo(that);
|
||||||
|
pandora.api.findDocuments({
|
||||||
|
query: {conditions: [], operator: '&'}
|
||||||
|
}, function(result) {
|
||||||
|
that.update(result.data.items);
|
||||||
|
});
|
||||||
} else if (section == 'texts') {
|
} else if (section == 'texts') {
|
||||||
$buttons[0] = Ox.Button({
|
$buttons[0] = Ox.Button({
|
||||||
style: 'symbol',
|
style: 'symbol',
|
||||||
|
@ -124,6 +184,7 @@ pandora.ui.allItems = function(section) {
|
||||||
function updateSelected() {
|
function updateSelected() {
|
||||||
that[
|
that[
|
||||||
(section == 'items' && pandora.user.ui._list)
|
(section == 'items' && pandora.user.ui._list)
|
||||||
|
|| (section == 'documents' && pandora.user.ui._collection)
|
||||||
|| (section == 'edits' && pandora.user.ui.edit)
|
|| (section == 'edits' && pandora.user.ui.edit)
|
||||||
|| (section == 'texts' && pandora.user.ui.text)
|
|| (section == 'texts' && pandora.user.ui.text)
|
||||||
? 'removeClass' : 'addClass'
|
? 'removeClass' : 'addClass'
|
||||||
|
|
|
@ -2,8 +2,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
pandora.ui.backButton = function() {
|
pandora.ui.backButton = function() {
|
||||||
var that = Ox.Button({
|
var that = Ox.Button({
|
||||||
title: Ox._('Back to {0}', [Ox._(pandora.site.itemName.plural)]),
|
title: Ox._('Back to {0}', [
|
||||||
width: 96
|
pandora.user.ui.section == 'items'
|
||||||
|
? Ox._(pandora.site.itemName.plural)
|
||||||
|
: Ox._(Ox.toTitleCase(pandora.user.ui.section))
|
||||||
|
]),
|
||||||
|
width: pandora.user.ui.section == 'documents' ? 124 : 96
|
||||||
}).css({
|
}).css({
|
||||||
float: 'left',
|
float: 'left',
|
||||||
margin: '4px 0 0 4px'
|
margin: '4px 0 0 4px'
|
||||||
|
@ -21,7 +25,11 @@ pandora.ui.backButton = function() {
|
||||||
if (['accessed', 'timesaccessed'].indexOf(pandora.user.ui.listSort[0].key) > -1) {
|
if (['accessed', 'timesaccessed'].indexOf(pandora.user.ui.listSort[0].key) > -1) {
|
||||||
Ox.Request.clearCache('find');
|
Ox.Request.clearCache('find');
|
||||||
}
|
}
|
||||||
pandora.UI.set({item: ''});
|
if (pandora.user.ui.section == 'documents') {
|
||||||
|
pandora.UI.set({document: ''});
|
||||||
|
} else {
|
||||||
|
pandora.UI.set({item: ''});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return that;
|
return that;
|
||||||
|
|
289
static/js/collection.js
Normal file
289
static/js/collection.js
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
pandora.ui.collection = function() {
|
||||||
|
|
||||||
|
var that,
|
||||||
|
ui = pandora.user.ui,
|
||||||
|
view = ui.collectionView,
|
||||||
|
keys = [
|
||||||
|
'description', 'dimensions', 'extension', 'id', 'title', 'ratio', 'size', 'user', 'entities', 'modified',
|
||||||
|
'editable'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (view == 'list') {
|
||||||
|
that = Ox.TableList({
|
||||||
|
keys: keys,
|
||||||
|
items: function(data, callback) {
|
||||||
|
pandora.api.findDocuments(Ox.extend(data, {
|
||||||
|
query: ui.findDocuments
|
||||||
|
}), callback);
|
||||||
|
return Ox.clone(data, true);
|
||||||
|
},
|
||||||
|
selected: ui.collectionSelection,
|
||||||
|
sort: ui.collectionSort.concat([
|
||||||
|
{key: 'extension', operator: '+'},
|
||||||
|
{key: 'title', operator: '+'}
|
||||||
|
]),
|
||||||
|
unique: 'id',
|
||||||
|
columns: pandora.site.documentSortKeys.filter(function(key) {
|
||||||
|
return !key.capability
|
||||||
|
|| pandora.site.capabilities[key.capability][pandora.user.level];
|
||||||
|
}).map(function(key) {
|
||||||
|
var position = ui.collectionColumns.indexOf(key.id);
|
||||||
|
return {
|
||||||
|
addable: key.id != 'random',
|
||||||
|
align: ['string', 'text'].indexOf(
|
||||||
|
Ox.isArray(key.type) ? key.type[0]: key.type
|
||||||
|
) > -1 ? 'left' : key.type == 'list' ? 'center' : 'right',
|
||||||
|
defaultWidth: key.columnWidth,
|
||||||
|
format: (function() {
|
||||||
|
return function(value, data) {
|
||||||
|
return pandora.formatDocumentKey(key, data);
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
id: key.id,
|
||||||
|
operator: pandora.getDocumentSortOperator(key.id),
|
||||||
|
position: position,
|
||||||
|
removable: !key.columnRequired,
|
||||||
|
title: Ox._(key.title),
|
||||||
|
type: key.type,
|
||||||
|
visible: position > -1,
|
||||||
|
width: ui.collectionColumnWidth[key.id] || key.columnWidth
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
columnsVisible: true,
|
||||||
|
scrollbarVisible: true,
|
||||||
|
})
|
||||||
|
} else if (view == 'grid') {
|
||||||
|
that = Ox.IconList({
|
||||||
|
borderRadius: 0,
|
||||||
|
defaultRatio: 640/1024,
|
||||||
|
draggable: true,
|
||||||
|
id: 'list',
|
||||||
|
item: function(data, sort, size) {
|
||||||
|
var sortKey = sort[0].key,
|
||||||
|
infoKey = sortKey == 'title' ? 'extension' : sortKey,
|
||||||
|
key = Ox.getObjectById(pandora.site.documentKeys, infoKey),
|
||||||
|
info = pandora.formatDocumentKey(key, data),
|
||||||
|
size = size || 128;
|
||||||
|
return {
|
||||||
|
height: Math.round(data.ratio > 1 ? size / data.ratio : size),
|
||||||
|
id: data.id,
|
||||||
|
info: info,
|
||||||
|
title: data.title,
|
||||||
|
url: pandora.getMediaURL('/documents/' + data.id + '/256p.jpg?' + data.modified),
|
||||||
|
width: Math.round(data.ratio > 1 ? size : size * data.ratio)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
items: function(data, callback) {
|
||||||
|
pandora.api.findDocuments(Ox.extend(data, {
|
||||||
|
query: ui.findDocuments
|
||||||
|
}), callback);
|
||||||
|
return Ox.clone(data, true);
|
||||||
|
},
|
||||||
|
keys: keys,
|
||||||
|
selected: ui.collectionSelection,
|
||||||
|
size: 128,
|
||||||
|
sort: ui.collectionSort.concat([
|
||||||
|
{key: 'extension', operator: '+'},
|
||||||
|
{key: 'title', operator: '+'}
|
||||||
|
]),
|
||||||
|
unique: 'id'
|
||||||
|
})
|
||||||
|
.addClass('OxMedia');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['list', 'grid'].indexOf(view) > -1) {
|
||||||
|
// react to the resize event of the split panel
|
||||||
|
that.bindEvent({
|
||||||
|
resize: function(data) {
|
||||||
|
that.size();
|
||||||
|
},
|
||||||
|
pandora_showbrowser: function(data) {
|
||||||
|
that.size();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['list', 'grid'].indexOf(view) > -1) {
|
||||||
|
|
||||||
|
//fixme
|
||||||
|
|
||||||
|
pandora.enableDragAndDrop(that, true);
|
||||||
|
|
||||||
|
that.bindEvent({
|
||||||
|
closepreview: function(data) {
|
||||||
|
pandora.$ui.previewDialog.close();
|
||||||
|
delete pandora.$ui.previewDialog;
|
||||||
|
},
|
||||||
|
copy: function(data) {
|
||||||
|
pandora.clipboard.copy(data.ids, 'document');
|
||||||
|
},
|
||||||
|
copyadd: function(data) {
|
||||||
|
pandora.clipboard.add(data.ids, 'document');
|
||||||
|
},
|
||||||
|
cut: function(data) {
|
||||||
|
var listData = pandora.getListData();
|
||||||
|
if (listData.editable && listData.type == 'static') {
|
||||||
|
pandora.clipboard.copy(data.ids, 'document');
|
||||||
|
pandora.doHistory('cut', data.ids, ui._collection, function() {
|
||||||
|
pandora.UI.set({collectionSelection: []});
|
||||||
|
pandora.reloadList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cutadd: function(data) {
|
||||||
|
var listData = pandora.getListData();
|
||||||
|
if (listData.editable && listData.type == 'static') {
|
||||||
|
pandora.clipboard.add(data.ids, 'document');
|
||||||
|
pandora.doHistory('cut', data.ids, ui._collection, function() {
|
||||||
|
pandora.UI.set({collectionSelection: []});
|
||||||
|
pandora.reloadList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'delete': function(data) {
|
||||||
|
var listData = pandora.getListData();
|
||||||
|
if (listData.editable && listData.type == 'static') {
|
||||||
|
//fixme use history
|
||||||
|
//pandora.doHistory('delete', data.ids, ui._collection, function() {
|
||||||
|
pandora.api.removeCollectionItems({
|
||||||
|
collection: ui._collection,
|
||||||
|
items: data.ids
|
||||||
|
|
||||||
|
}, function() {
|
||||||
|
pandora.UI.set({collectionSelection: []});
|
||||||
|
pandora.reloadList();
|
||||||
|
});
|
||||||
|
} else if (pandora.user.ui._collection == '' && data.ids.every(function(item) {
|
||||||
|
return pandora.$ui.list.value(item, 'editable');
|
||||||
|
})) {
|
||||||
|
pandora.ui.deleteDocumentDialog(
|
||||||
|
data.ids.map(function(id) {
|
||||||
|
return pandora.$ui.list.value(id);
|
||||||
|
}),
|
||||||
|
function() {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
if (ui.document) {
|
||||||
|
pandora.UI.set({document: ''});
|
||||||
|
} else {
|
||||||
|
pandora.$ui.list.reloadList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
init: function(data) {
|
||||||
|
var folder, list;
|
||||||
|
if (data.query.conditions.length == 0) {
|
||||||
|
pandora.$ui.allItems.update(data.items);
|
||||||
|
} else if (
|
||||||
|
data.query.conditions.length == 1
|
||||||
|
&& data.query.conditions[0].key == 'document'
|
||||||
|
&& data.query.conditions[0].operator == '=='
|
||||||
|
) {
|
||||||
|
list = data.query.conditions[0].value;
|
||||||
|
folder = pandora.getListData(list).folder;
|
||||||
|
if (pandora.$ui.folderList[folder]
|
||||||
|
&& !Ox.isEmpty(pandora.$ui.folderList[folder].value(list))) {
|
||||||
|
pandora.$ui.folderList[folder].value(
|
||||||
|
list, 'items', data.items
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pandora.$ui.statusbar.set('total', data);
|
||||||
|
data = [];
|
||||||
|
pandora.site.totals.forEach(function(v) {
|
||||||
|
data[v.id] = 0;
|
||||||
|
});
|
||||||
|
pandora.$ui.statusbar.set('selected', data);
|
||||||
|
},
|
||||||
|
open: function(data) {
|
||||||
|
var set = {document: data.ids[0]};
|
||||||
|
pandora.UI.set(set);
|
||||||
|
},
|
||||||
|
openpreview: function(data) {
|
||||||
|
/*
|
||||||
|
if (!pandora.$ui.previewDialog) {
|
||||||
|
pandora.$ui.previewDialog = pandora.ui.previewDialog()
|
||||||
|
.open()
|
||||||
|
.bindEvent({
|
||||||
|
close: function() {
|
||||||
|
that.closePreview();
|
||||||
|
delete pandora.$ui.previewDialog;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
pandora.$ui.previewDialog.update();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
paste: function(data) {
|
||||||
|
var items = pandora.clipboard.paste();
|
||||||
|
if (items.length && pandora.clipboard.type() == 'document' && pandora.getListData().editable) {
|
||||||
|
//fixme use history
|
||||||
|
//pandora.doHistory('paste', items, ui._collection, function() {
|
||||||
|
pandora.api.addCollectionItems({
|
||||||
|
collection: ui._collection,
|
||||||
|
items: items
|
||||||
|
|
||||||
|
}, function() {
|
||||||
|
pandora.UI.set({collectionSelection: items});
|
||||||
|
pandora.reloadList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: function(data) {
|
||||||
|
var query;
|
||||||
|
pandora.UI.set('collectionSelection', data.ids);
|
||||||
|
if (data.ids.length == 0) {
|
||||||
|
pandora.$ui.statusbar.set('selected', {items: 0});
|
||||||
|
} else {
|
||||||
|
if (Ox.isUndefined(data.rest)) {
|
||||||
|
query = {
|
||||||
|
conditions: data.ids.map(function(id) {
|
||||||
|
return {
|
||||||
|
key: 'id',
|
||||||
|
value: id,
|
||||||
|
operator: '=='
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
operator: '|'
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
query = {
|
||||||
|
conditions: [ui.find].concat(
|
||||||
|
data.rest.map(function(id) {
|
||||||
|
return {
|
||||||
|
key: 'id',
|
||||||
|
value: id,
|
||||||
|
operator: '!='
|
||||||
|
};
|
||||||
|
})
|
||||||
|
),
|
||||||
|
operator: '&'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pandora.api.find({
|
||||||
|
query: query
|
||||||
|
}, function(result) {
|
||||||
|
pandora.$ui.statusbar.set('selected', result.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
pandora_collectionsort: function(data) {
|
||||||
|
that.options({sort: data.value});
|
||||||
|
},
|
||||||
|
pandora_showdocument: function(data) {
|
||||||
|
isItemView && that.toggleElement(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
|
@ -31,7 +31,7 @@ pandora.ui.deleteDocumentDialog = function(files, callback) {
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
content: files.length == 1
|
content: files.length == 1
|
||||||
? Ox._('Are you sure you want to delete the document "{0}"?', [files[0].name + '.' + files[0].extension])
|
? Ox._('Are you sure you want to delete the document "{0}"?', [files[0].title + '.' + files[0].extension])
|
||||||
: Ox._('Are you sure you want to delete {0} documents?', [files.length]),
|
: Ox._('Are you sure you want to delete {0} documents?', [files.length]),
|
||||||
keys: {enter: 'delete', escape: 'keep'},
|
keys: {enter: 'delete', escape: 'keep'},
|
||||||
title: files.length == 1
|
title: files.length == 1
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
pandora.ui.deleteListDialog = function(list) {
|
pandora.ui.deleteListDialog = function(list) {
|
||||||
|
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
folderItems = pandora.getFolderItems(ui.section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
listData = pandora.getListData(list),
|
listData = pandora.getListData(list),
|
||||||
$folderList = pandora.$ui.folderList[listData.folder],
|
$folderList = pandora.$ui.folderList[listData.folder],
|
||||||
|
@ -42,6 +42,14 @@ pandora.ui.deleteListDialog = function(list) {
|
||||||
pandora.UI.set({
|
pandora.UI.set({
|
||||||
find: pandora.site.user.ui.find
|
find: pandora.site.user.ui.find
|
||||||
});
|
});
|
||||||
|
} else if (ui.section == 'documents') {
|
||||||
|
pandora.UI.set(
|
||||||
|
'collections.' + listData.id, null
|
||||||
|
);
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: pandora.site.user.ui.findDocuments
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set(
|
pandora.UI.set(
|
||||||
folderItem.toLowerCase(), ''
|
folderItem.toLowerCase(), ''
|
||||||
|
|
282
static/js/document.js
Normal file
282
static/js/document.js
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.document = function() {
|
||||||
|
var $toolbar = Ox.Bar({size: 16})
|
||||||
|
.bindEvent({
|
||||||
|
doubleclick: function(e) {
|
||||||
|
if ($(e.target).is('.OxBar')) {
|
||||||
|
pandora.$ui.text && pandora.$ui.text.animate({scrollTop:0}, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$content = Ox.Element(),
|
||||||
|
that = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
element: $toolbar,
|
||||||
|
size: 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: $content
|
||||||
|
}
|
||||||
|
],
|
||||||
|
orientation: 'vertical'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
pandora_showbrowser: function(data) {
|
||||||
|
that.update();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
item,
|
||||||
|
$find,
|
||||||
|
$nextButton,
|
||||||
|
$currentButton,
|
||||||
|
$previousButton;
|
||||||
|
|
||||||
|
pandora.api.getDocument({
|
||||||
|
id: pandora.user.ui.document
|
||||||
|
}, function(result) {
|
||||||
|
if (pandora.user.ui.document != result.data.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
item = result.data;
|
||||||
|
var documentTitle = pandora.getWindowTitle(item);
|
||||||
|
document.title = pandora.getPageTitle(document.location.pathname) || documentTitle;
|
||||||
|
pandora.$ui.itemTitle
|
||||||
|
.options({title: '<b>' + (pandora.getDocumentTitle(item)) + '</b>'})
|
||||||
|
.show();
|
||||||
|
|
||||||
|
if (pandora.user.ui.documentView == 'info') {
|
||||||
|
$content.replaceWith(
|
||||||
|
$content = pandora.ui.documentInfoView(result.data)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setContent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setContent() {
|
||||||
|
that.replaceElement(1, $content = (
|
||||||
|
item.extension == 'pdf'
|
||||||
|
? Ox.PDFViewer({
|
||||||
|
height: that.height(),
|
||||||
|
page: pandora.user.ui.documents[item.id]
|
||||||
|
? pandora.user.ui.documents[item.id].position
|
||||||
|
: 1,
|
||||||
|
url: '/documents/' + item.id + '/'
|
||||||
|
+ item.title + '.' + item.extension,
|
||||||
|
width: that.width(),
|
||||||
|
zoom: 'fit'
|
||||||
|
})
|
||||||
|
: item.extension == 'html'
|
||||||
|
? pandora.$ui.textPanel = textPanel(item).css({
|
||||||
|
})
|
||||||
|
: Ox.ImageViewer({
|
||||||
|
area: pandora.user.ui.documents[item.id]
|
||||||
|
? pandora.user.ui.documents[item.id].position
|
||||||
|
: [],
|
||||||
|
height: that.height(),
|
||||||
|
imageHeight: item.dimensions[1],
|
||||||
|
imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified),
|
||||||
|
imageURL: pandora.getMediaURL('/documents/' + item.id + '/'
|
||||||
|
+ item.title + '.' + item.extension + '?' + item.modified),
|
||||||
|
imageWidth: item.dimensions[0],
|
||||||
|
width: that.width()
|
||||||
|
}).css({
|
||||||
|
//prevents image overflow on zoom, fixme: fix in Ox.ImageViewer
|
||||||
|
position: 'absolute'
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.bindEvent({
|
||||||
|
center: function(data) {
|
||||||
|
pandora.UI.set(
|
||||||
|
'documents.' + item.id,
|
||||||
|
{position: $content.getArea().map(Math.round)}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
key_escape: function() {
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
page: function(data) {
|
||||||
|
pandora.UI.set(
|
||||||
|
'documents.' + item.id,
|
||||||
|
{position: data.page}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
zoom: function(data) {
|
||||||
|
pandora.UI.set(
|
||||||
|
'documents.' + item.id,
|
||||||
|
{position: $content.getArea().map(Math.round)}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (item.extension == 'html') {
|
||||||
|
that.css({
|
||||||
|
'overflow-y': 'auto'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function textPanel(text) {
|
||||||
|
var textElement,
|
||||||
|
embedURLs = getEmbedURLs(text.text),
|
||||||
|
that = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
element: pandora.$ui.text = textElement = pandora.ui.textHTML(text)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: pandora.$ui.textEmbed = pandora.ui.textEmbed(),
|
||||||
|
collapsed: !embedURLs.length,
|
||||||
|
size: pandora.user.ui.embedSize,
|
||||||
|
resizable: true,
|
||||||
|
resize: [192, 256, 320, 384, 448, 512]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
orientation: 'horizontal'
|
||||||
|
}),
|
||||||
|
selected = -1,
|
||||||
|
selectedURL;
|
||||||
|
/*
|
||||||
|
$find = Ox.Input({
|
||||||
|
clear: true,
|
||||||
|
placeholder: Ox._('Find in Texts'),
|
||||||
|
value: pandora.user.ui.textFind,
|
||||||
|
width: 188
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'right',
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
submit: function(data) {
|
||||||
|
Ox.print('SUBMIT', data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($toolbar);
|
||||||
|
*/
|
||||||
|
$nextButton = Ox.Button({
|
||||||
|
disabled: embedURLs.length < 2,
|
||||||
|
title: 'arrowRight',
|
||||||
|
tooltip: Ox._('Next Reference'),
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
'margin-right': (pandora.user.ui.embedSize + Ox.SCROLLBAR_SIZE) + 'px',
|
||||||
|
float: 'right',
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
that.selectEmbed(
|
||||||
|
selected < embedURLs.length - 1 ? selected + 1 : 0,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($toolbar);
|
||||||
|
|
||||||
|
$currentButton = Ox.Button({
|
||||||
|
disabled: embedURLs.length < 1,
|
||||||
|
title: 'center',
|
||||||
|
tooltip: Ox._('Current Reference'),
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'right',
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: scrollToSelectedEmbed
|
||||||
|
})
|
||||||
|
.appendTo($toolbar);
|
||||||
|
|
||||||
|
$previousButton = Ox.Button({
|
||||||
|
disabled: embedURLs.length < 2,
|
||||||
|
title: 'arrowLeft',
|
||||||
|
tooltip: Ox._('Previous Reference'),
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'right',
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
that.selectEmbed(
|
||||||
|
selected ? selected - 1 : embedURLs.length - 1,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($toolbar);
|
||||||
|
|
||||||
|
function getEmbedURLs(text) {
|
||||||
|
var matches = text.match(/<a [^<>]*?href="(.+?)".*?>/gi),
|
||||||
|
urls = [];
|
||||||
|
if (matches) {
|
||||||
|
matches.forEach(function(match) {
|
||||||
|
var url = match.match(/"(.+?)"/)[1];
|
||||||
|
if (pandora.isEmbedURL(url)) {
|
||||||
|
urls.push(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToSelectedEmbed() {
|
||||||
|
var scrollTop = Math.max(
|
||||||
|
textElement[0].scrollTop + $('#embed' + selected).offset().top - (
|
||||||
|
pandora.user.ui.showBrowser
|
||||||
|
? pandora.$ui.documentContentPanel.options().elements[0].size
|
||||||
|
: 0
|
||||||
|
) - 48,
|
||||||
|
0),
|
||||||
|
position = 100 * scrollTop / Math.max(1, textElement[0].scrollHeight);
|
||||||
|
textElement.scrollTo(position);
|
||||||
|
window.text = textElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
that.selectEmbed = function(index, scroll) {
|
||||||
|
if (index != selected) {
|
||||||
|
selected = index;
|
||||||
|
selectedURL = embedURLs[selected]
|
||||||
|
$('.OxSpecialLink').removeClass('OxActive');
|
||||||
|
selected > -1 && $('#embed' + selected).addClass('OxActive');
|
||||||
|
pandora.$ui.textEmbed.update(selectedURL);
|
||||||
|
scroll && scrollToSelectedEmbed();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
that.update = function(text) {
|
||||||
|
var index;
|
||||||
|
embedURLs = getEmbedURLs(text);
|
||||||
|
index = embedURLs.indexOf(selectedURL);
|
||||||
|
if (embedURLs.length && (index == -1 || index >= embedURLs.length)) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
selected = -1;
|
||||||
|
that.selectEmbed(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
embedURLs.length && that.selectEmbed(0);
|
||||||
|
return that;
|
||||||
|
}
|
||||||
|
|
||||||
|
that.info = function() {
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
|
||||||
|
that.update = function() {
|
||||||
|
$content.options({
|
||||||
|
height: that.height(),
|
||||||
|
width: that.width()
|
||||||
|
});
|
||||||
|
$nextButton && $nextButton.css({
|
||||||
|
'margin-right': (pandora.user.ui.embedSize + Ox.SCROLLBAR_SIZE) + 'px',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
94
static/js/documentBrowser.js
Normal file
94
static/js/documentBrowser.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
'use strict';
|
||||||
|
pandora.ui.documentBrowser = function() {
|
||||||
|
var that;
|
||||||
|
if (!pandora.user.ui.document) {
|
||||||
|
that = Ox.Element().html('fixme');
|
||||||
|
} else {
|
||||||
|
var that = Ox.IconList({
|
||||||
|
borderRadius: 0,
|
||||||
|
centered: true,
|
||||||
|
defaultRatio: 640/1024,
|
||||||
|
draggable: true,
|
||||||
|
id: 'list',
|
||||||
|
item: function(data, sort, size) {
|
||||||
|
size = size || 64;
|
||||||
|
var sortKey = sort[0].key,
|
||||||
|
infoKey = sortKey == 'title' ? 'extension' : sortKey,
|
||||||
|
key = Ox.getObjectById(pandora.site.documentKeys, infoKey),
|
||||||
|
info = pandora.formatDocumentKey(key, data),
|
||||||
|
size = size || 128;
|
||||||
|
return {
|
||||||
|
height: Math.round(data.ratio > 1 ? size / data.ratio : size),
|
||||||
|
id: data.id,
|
||||||
|
info: info,
|
||||||
|
title: data.title,
|
||||||
|
url: pandora.getMediaURL('/documents/' + data.id + '/256p.jpg?' + data.modified),
|
||||||
|
width: Math.round(data.ratio > 1 ? size : size * data.ratio)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
items: function(data, callback) {
|
||||||
|
pandora.api.findDocuments(Ox.extend(data, {
|
||||||
|
query: pandora.user.ui.findDocuments
|
||||||
|
}), callback);
|
||||||
|
return Ox.clone(data, true);
|
||||||
|
},
|
||||||
|
keys: ['description', 'dimensions', 'extension', 'id', 'title', 'ratio', 'size', 'user', 'entities', 'modified'],
|
||||||
|
max: 1,
|
||||||
|
min: 1,
|
||||||
|
orientation: 'horizontal',
|
||||||
|
pageLength: 32,
|
||||||
|
selected: [pandora.user.ui.document],
|
||||||
|
size: 64,
|
||||||
|
sort: getSort(),
|
||||||
|
unique: 'id'
|
||||||
|
})
|
||||||
|
.addClass('OxMedia')
|
||||||
|
.bindEvent({
|
||||||
|
copy: function() {
|
||||||
|
pandora.clipboard.copy(pandora.user.ui.item, 'document');
|
||||||
|
},
|
||||||
|
copyadd: function() {
|
||||||
|
pandora.clipboard.add(pandora.user.ui.item, 'document');
|
||||||
|
},
|
||||||
|
gainfocus: function() {
|
||||||
|
pandora.$ui.mainMenu.replaceItemMenu();
|
||||||
|
},
|
||||||
|
open: function() {
|
||||||
|
that.scrollToSelection();
|
||||||
|
},
|
||||||
|
openpreview: function() {
|
||||||
|
if (pandora.isVideoView()) {
|
||||||
|
pandora.$ui[pandora.user.ui.itemView].gainFocus().triggerEvent('key_space');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: function(data) {
|
||||||
|
data.ids.length && pandora.UI.set({
|
||||||
|
'document': data.ids[0]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggle: function(data) {
|
||||||
|
pandora.UI.set({showBrowser: !data.collapsed});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.bindEventOnce({
|
||||||
|
load: function() {
|
||||||
|
// gain focus if we're on page load or if we've just switched
|
||||||
|
// to an item and the not-yet-garbage-collected list still has
|
||||||
|
// focus
|
||||||
|
if (!Ox.Focus.focusedElement() || (
|
||||||
|
pandora.$ui.list && pandora.$ui.list.hasFocus()
|
||||||
|
)) {
|
||||||
|
that.gainFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
that.css({overflowY: 'hidden'}); // this fixes a bug in firefox
|
||||||
|
pandora.enableDragAndDrop(that, false);
|
||||||
|
}
|
||||||
|
function getSort() {
|
||||||
|
return pandora.user.ui.collectionSort;
|
||||||
|
}
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
63
static/js/documentContentPanel.js
Normal file
63
static/js/documentContentPanel.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.documentContentPanel = function() {
|
||||||
|
var that = Ox.SplitPanel({
|
||||||
|
elements: !pandora.user.ui.document ? [
|
||||||
|
{
|
||||||
|
collapsed: true,
|
||||||
|
collapsible: false, //fixme
|
||||||
|
element: pandora.$ui.documentBrowser = pandora.ui.documentBrowser(),
|
||||||
|
resizable: false, //fixme
|
||||||
|
resize: [96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256],
|
||||||
|
size: 96,
|
||||||
|
tooltip: '' /* fixme:
|
||||||
|
Ox._('filters') + ' <span class="OxBright">'
|
||||||
|
+ Ox.SYMBOLS.shift + 'F</span>'
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: pandora.$ui.list = pandora.ui.collection()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: pandora.$ui.statusbar = pandora.ui.statusbar(),
|
||||||
|
size: 16
|
||||||
|
}
|
||||||
|
] : [
|
||||||
|
{
|
||||||
|
collapsed: !pandora.user.ui.showBrowser,
|
||||||
|
collapsible: true,
|
||||||
|
element: pandora.$ui.documentBrowser = pandora.ui.documentBrowser(),
|
||||||
|
size: 112 + Ox.UI.SCROLLBAR_SIZE,
|
||||||
|
tooltip: Ox._('{0} browser', [Ox._('document')])
|
||||||
|
+ ' <span class="OxBright">'
|
||||||
|
+ Ox.SYMBOLS.shift + 'B</span>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: pandora.$ui.document = pandora.ui.document()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
orientation: 'vertical'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
resize: function(data) {
|
||||||
|
Ox.print('split resize');
|
||||||
|
},
|
||||||
|
pandora_document: function(data) {
|
||||||
|
if (data.value && data.previousValue) {
|
||||||
|
that.replaceElement(1, pandora.$ui.document = pandora.ui.document());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pandora_documentview: function(data) {
|
||||||
|
that.replaceElement(1, pandora.$ui.document = pandora.ui.document());
|
||||||
|
},
|
||||||
|
pandora_collectionview: function() {
|
||||||
|
!pandora.user.ui.document && that.replaceElement(1,
|
||||||
|
pandora.$ui.list = pandora.ui.collection());
|
||||||
|
},
|
||||||
|
pandora_showbrowser: function(data) {
|
||||||
|
data.value == that.options('elements')[0].collapsed && that.toggleElement(0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return that;
|
||||||
|
};
|
|
@ -19,7 +19,7 @@ pandora.openDocumentDialog = function(options) {
|
||||||
operator: '|'
|
operator: '|'
|
||||||
},
|
},
|
||||||
range: [0, options.ids.length],
|
range: [0, options.ids.length],
|
||||||
keys: ['description', 'dimensions', 'extension', 'id', 'name', 'modified']
|
keys: ['description', 'dimensions', 'extension', 'id', 'title', 'modified']
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
var i = 0,
|
var i = 0,
|
||||||
documents = Ox.sort(result.data.items, function(item) {
|
documents = Ox.sort(result.data.items, function(item) {
|
||||||
|
@ -173,7 +173,7 @@ pandora.ui.documentDialog = function(options) {
|
||||||
? pandora.user.ui.documents[item.id].position
|
? pandora.user.ui.documents[item.id].position
|
||||||
: 1,
|
: 1,
|
||||||
url: '/documents/' + item.id + '/'
|
url: '/documents/' + item.id + '/'
|
||||||
+ item.name + '.' + item.extension,
|
+ item.title + '.' + item.extension,
|
||||||
width: dialogWidth,
|
width: dialogWidth,
|
||||||
zoom: 'fit'
|
zoom: 'fit'
|
||||||
})
|
})
|
||||||
|
@ -185,7 +185,7 @@ pandora.ui.documentDialog = function(options) {
|
||||||
imageHeight: item.dimensions[1],
|
imageHeight: item.dimensions[1],
|
||||||
imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified),
|
imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified),
|
||||||
imageURL: pandora.getMediaURL('/documents/' + item.id + '/'
|
imageURL: pandora.getMediaURL('/documents/' + item.id + '/'
|
||||||
+ item.name + '.' + item.extension + '?' + item.modified),
|
+ item.title + '.' + item.extension + '?' + item.modified),
|
||||||
imageWidth: item.dimensions[0],
|
imageWidth: item.dimensions[0],
|
||||||
width: dialogWidth
|
width: dialogWidth
|
||||||
})
|
})
|
||||||
|
@ -217,7 +217,7 @@ pandora.ui.documentDialog = function(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTitle() {
|
function setTitle() {
|
||||||
that.options({title: item.name + '.' + item.extension});
|
that.options({title: item.title + '.' + item.extension});
|
||||||
}
|
}
|
||||||
|
|
||||||
that.getItems = function() {
|
that.getItems = function() {
|
||||||
|
|
98
static/js/documentFilterForm.js
Normal file
98
static/js/documentFilterForm.js
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.documentFilterForm = function(options) {
|
||||||
|
// mode can be find, collection, embed
|
||||||
|
var collection = options.list,
|
||||||
|
mode = options.mode,
|
||||||
|
that = Ox.Element();
|
||||||
|
|
||||||
|
if (mode == 'list') {
|
||||||
|
mode = 'collection';
|
||||||
|
}
|
||||||
|
|
||||||
|
pandora.api.findCollections({
|
||||||
|
query: {
|
||||||
|
conditions: [{key: 'type', value: 'static', operator: '='}],
|
||||||
|
operator: '&'
|
||||||
|
},
|
||||||
|
keys: ['id'],
|
||||||
|
range: [0, 1000],
|
||||||
|
sort: [{key: 'user', operator: '+'}, {key: 'name', operator: '+'}]
|
||||||
|
}, function(result) {
|
||||||
|
that.append(
|
||||||
|
that.$filter = Ox.Filter({
|
||||||
|
findKeys: pandora.site.documentKeys.map(function(documentKey) {
|
||||||
|
var key = Ox.clone(documentKey, true);
|
||||||
|
key.title = Ox._(key.title);
|
||||||
|
if (key.format && key.format.type == 'ColorPercent') {
|
||||||
|
key.format.type = 'percent';
|
||||||
|
}
|
||||||
|
Ox.print(key);
|
||||||
|
return key;
|
||||||
|
}).concat([{
|
||||||
|
id: 'collection',
|
||||||
|
title: Ox._('Collection'),
|
||||||
|
type: 'list',
|
||||||
|
values: result.data.items.map(function(item) {
|
||||||
|
return item.id;
|
||||||
|
})
|
||||||
|
}]),
|
||||||
|
list: mode == 'find' ? {
|
||||||
|
sort: pandora.user.ui.collectionSort,
|
||||||
|
view: pandora.user.ui.collectionView
|
||||||
|
} : null,
|
||||||
|
sortKeys: pandora.site.documentSortKeys,
|
||||||
|
value: Ox.clone(mode == 'collection' ? collection.query : pandora.user.ui.documentFind, true),
|
||||||
|
viewKeys: pandora.site.collectionViews
|
||||||
|
})
|
||||||
|
.css(mode == 'embed' ? {} : {padding: '16px'})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
if (mode == 'find') {
|
||||||
|
if (pandora.user.ui.updateAdvancedFindResults) {
|
||||||
|
that.updateResults();
|
||||||
|
}
|
||||||
|
} else if (mode == 'collection') {
|
||||||
|
pandora.api.editCollection({
|
||||||
|
id: collection.id,
|
||||||
|
query: data.value
|
||||||
|
}, function(result) {
|
||||||
|
if (pandora.user.ui.updateAdvancedFindResults) {
|
||||||
|
that.updateResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
that.triggerEvent('change', data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
that.getList = that.$filter.getList;
|
||||||
|
that.value = that.$filter.value;
|
||||||
|
});
|
||||||
|
that.updateResults = function() {
|
||||||
|
if (mode == 'collection') {
|
||||||
|
Ox.Request.clearCache(collection.id);
|
||||||
|
pandora.$ui.list && pandora.$ui.list
|
||||||
|
.bindEventOnce({
|
||||||
|
init: function(data) {
|
||||||
|
pandora.$ui.folderList[
|
||||||
|
pandora.getListData().folder
|
||||||
|
].value(collection.id, 'query', that.$filter.options('value'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.reloadList();
|
||||||
|
/*
|
||||||
|
pandora.$ui.filters && pandora.$ui.filters.forEach(function($filter) {
|
||||||
|
$filter.reloadList();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
pandora.UI.set({find: Ox.clone(that.$filter.options('value'), true)});
|
||||||
|
pandora.$ui.findElement.updateElement();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
572
static/js/documentInfoView.js
Normal file
572
static/js/documentInfoView.js
Normal file
|
@ -0,0 +1,572 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.documentInfoView = function(data) {
|
||||||
|
|
||||||
|
var ui = pandora.user.ui,
|
||||||
|
canEdit = pandora.site.capabilities.canEditMetadata[pandora.user.level] || data.editable,
|
||||||
|
canRemove = pandora.site.capabilities.canRemoveItems[pandora.user.level],
|
||||||
|
css = {
|
||||||
|
marginTop: '4px',
|
||||||
|
textAlign: 'justify'
|
||||||
|
},
|
||||||
|
html,
|
||||||
|
iconRatio = data.ratio,
|
||||||
|
iconSize = ui.infoIconSize,
|
||||||
|
iconWidth = iconRatio > 1 ? iconSize : Math.round(iconSize * iconRatio),
|
||||||
|
iconHeight = iconRatio < 1 ? iconSize : Math.round(iconSize / iconRatio),
|
||||||
|
iconLeft = iconSize == 256 ? Math.floor((iconSize - iconWidth) / 2) : 0,
|
||||||
|
borderRadius = ui.icons == 'posters' ? 0 : iconSize / 8,
|
||||||
|
margin = 16,
|
||||||
|
nameKeys = pandora.site.documentKeys.filter(function(key) {
|
||||||
|
return key.sortType == 'person';
|
||||||
|
}).map(function(key) {
|
||||||
|
return key.id;
|
||||||
|
}),
|
||||||
|
listKeys = pandora.site.documentKeys.filter(function(key) {
|
||||||
|
return Ox.isArray(key.type);
|
||||||
|
}).map(function(key){
|
||||||
|
return key.id;
|
||||||
|
}),
|
||||||
|
posterKeys = nameKeys.concat(['title', 'year']),
|
||||||
|
statisticsWidth = 128,
|
||||||
|
|
||||||
|
$bar = Ox.Bar({size: 16})
|
||||||
|
.bindEvent({
|
||||||
|
doubleclick: function(e) {
|
||||||
|
if ($(e.target).is('.OxBar')) {
|
||||||
|
$info.animate({scrollTop: 0}, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
$options = Ox.MenuButton({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'delete',
|
||||||
|
title: Ox._('Delete {0}...', [Ox._('Document')]),
|
||||||
|
disabled: !canRemove
|
||||||
|
}
|
||||||
|
],
|
||||||
|
style: 'square',
|
||||||
|
title: 'set',
|
||||||
|
tooltip: Ox._('Options'),
|
||||||
|
type: 'image',
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'left',
|
||||||
|
borderColor: 'rgba(0, 0, 0, 0)',
|
||||||
|
background: 'rgba(0, 0, 0, 0)'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function(data_) {
|
||||||
|
if (data_.id == 'delete') {
|
||||||
|
pandora.ui.deleteDocumentDialog(
|
||||||
|
[data],
|
||||||
|
function() {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
if (ui.document) {
|
||||||
|
pandora.UI.set({document: ''});
|
||||||
|
} else {
|
||||||
|
pandora.$ui.list.reloadList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($bar),
|
||||||
|
|
||||||
|
$edit = Ox.MenuButton({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 'insert',
|
||||||
|
title: Ox._('Insert HTML...'),
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
style: 'square',
|
||||||
|
title: 'edit',
|
||||||
|
tooltip: Ox._('Edit'),
|
||||||
|
type: 'image',
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'right',
|
||||||
|
borderColor: 'rgba(0, 0, 0, 0)',
|
||||||
|
background: 'rgba(0, 0, 0, 0)'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function(data) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($bar),
|
||||||
|
|
||||||
|
$info = Ox.Element().css({overflowY: 'auto'}),
|
||||||
|
|
||||||
|
that = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{element: $bar, size: 16},
|
||||||
|
{element: $info}
|
||||||
|
],
|
||||||
|
orientation: 'vertical'
|
||||||
|
}),
|
||||||
|
|
||||||
|
$icon = Ox.Element({
|
||||||
|
element: '<img>',
|
||||||
|
})
|
||||||
|
.attr({
|
||||||
|
src: '/documents/' + data.id + '/512p.jpg?' + data.modified
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: margin + iconLeft + 'px',
|
||||||
|
top: margin + 'px',
|
||||||
|
width: iconWidth + 'px',
|
||||||
|
height: iconHeight + 'px',
|
||||||
|
borderRadius: borderRadius + 'px',
|
||||||
|
cursor: 'pointer'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
singleclick: open
|
||||||
|
})
|
||||||
|
.appendTo($info),
|
||||||
|
|
||||||
|
$reflection = $('<div>')
|
||||||
|
.addClass('OxReflection')
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: margin + 'px',
|
||||||
|
top: margin + iconHeight + 'px',
|
||||||
|
width: iconSize + 'px',
|
||||||
|
height: iconSize / 2 + 'px',
|
||||||
|
overflow: 'hidden'
|
||||||
|
})
|
||||||
|
.appendTo($info),
|
||||||
|
|
||||||
|
$reflectionIcon = $('<img>')
|
||||||
|
.attr({
|
||||||
|
src: '/documents/' + data.id + '/512p.jpg?' + data.modified
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: iconLeft + 'px',
|
||||||
|
width: iconWidth + 'px',
|
||||||
|
height: iconHeight + 'px',
|
||||||
|
borderRadius: borderRadius + 'px'
|
||||||
|
})
|
||||||
|
.appendTo($reflection),
|
||||||
|
|
||||||
|
$reflectionGradient = $('<div>')
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
width: iconSize + 'px',
|
||||||
|
height: iconSize / 2 + 'px'
|
||||||
|
})
|
||||||
|
.appendTo($reflection),
|
||||||
|
|
||||||
|
$text = Ox.Element()
|
||||||
|
.addClass('OxTextPage')
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: margin + (iconSize == 256 ? 256 : iconWidth) + margin + 'px',
|
||||||
|
top: margin + 'px',
|
||||||
|
right: margin + statisticsWidth + margin + 'px',
|
||||||
|
})
|
||||||
|
.appendTo($info),
|
||||||
|
|
||||||
|
$statistics = $('<div>')
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
width: statisticsWidth + 'px',
|
||||||
|
top: margin + 'px',
|
||||||
|
right: margin + 'px'
|
||||||
|
})
|
||||||
|
.appendTo($info),
|
||||||
|
|
||||||
|
$capabilities;
|
||||||
|
|
||||||
|
[$options, $edit].forEach(function($element) {
|
||||||
|
$element.find('input').css({
|
||||||
|
borderWidth: 0,
|
||||||
|
borderRadius: 0,
|
||||||
|
padding: '3px'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
listKeys.forEach(function(key) {
|
||||||
|
if (Ox.isString(data[key])) {
|
||||||
|
data[key] = [data[key]];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!canEdit) {
|
||||||
|
pandora.createLinks($info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title -------------------------------------------------------------------
|
||||||
|
|
||||||
|
$('<div>')
|
||||||
|
.css({
|
||||||
|
marginTop: '-2px',
|
||||||
|
})
|
||||||
|
.append(
|
||||||
|
Ox.EditableContent({
|
||||||
|
editable: canEdit,
|
||||||
|
tooltip: canEdit ? pandora.getEditTooltip() : '',
|
||||||
|
value: data.title || ''
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
marginBottom: '-3px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fontSize: '13px'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
submit: function(event) {
|
||||||
|
editMetadata('title', event.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.appendTo($text);
|
||||||
|
|
||||||
|
// Director, Year and Country ----------------------------------------------
|
||||||
|
|
||||||
|
renderGroup(['author', 'type', 'date']);
|
||||||
|
renderGroup(['publisher', 'place', 'series', 'edition']);
|
||||||
|
renderGroup(['language']);
|
||||||
|
renderGroup(['extension', 'dimensions', 'size', 'user', 'matches']);
|
||||||
|
|
||||||
|
|
||||||
|
// Description -----------------------------------------------------------------
|
||||||
|
|
||||||
|
if (canEdit || data.description) {
|
||||||
|
$('<div>')
|
||||||
|
.append(
|
||||||
|
Ox.EditableContent({
|
||||||
|
clickLink: pandora.clickLink,
|
||||||
|
editable: canEdit,
|
||||||
|
format: function(value) {
|
||||||
|
return value.replace(
|
||||||
|
/<img src=/g,
|
||||||
|
'<img style="float: left; max-width: 256px; max-height: 256px; margin: 0 16px 16px 0" src='
|
||||||
|
);
|
||||||
|
},
|
||||||
|
maxHeight: Infinity,
|
||||||
|
placeholder: formatLight(Ox._('No Description')),
|
||||||
|
tooltip: canEdit ? pandora.getEditTooltip() : '',
|
||||||
|
type: 'textarea',
|
||||||
|
value: data.description || ''
|
||||||
|
})
|
||||||
|
.css(css)
|
||||||
|
.css({
|
||||||
|
marginTop: '12px',
|
||||||
|
overflow: 'hidden'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
submit: function(event) {
|
||||||
|
editMetadata('description', event.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.appendTo($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Created and Modified ----------------------------------------------------
|
||||||
|
|
||||||
|
['created', 'modified'].forEach(function(key) {
|
||||||
|
$('<div>')
|
||||||
|
.css({marginBottom: '4px'})
|
||||||
|
.append(formatKey(key, 'statistics'))
|
||||||
|
.append(
|
||||||
|
$('<div>').html(Ox.formatDate(data[key], '%B %e, %Y'))
|
||||||
|
)
|
||||||
|
.appendTo($statistics);
|
||||||
|
});
|
||||||
|
$('<div>')
|
||||||
|
.css({marginBottom: '4px'})
|
||||||
|
.append(formatKey('timesaccessed', 'statistics'))
|
||||||
|
.append(
|
||||||
|
$('<div>').html(data.timesaccessed)
|
||||||
|
)
|
||||||
|
.appendTo($statistics);
|
||||||
|
|
||||||
|
// Rights Level ------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
var $rightsLevel = $('<div>');
|
||||||
|
$('<div>')
|
||||||
|
.css({marginBottom: '4px'})
|
||||||
|
.append(formatKey('Rights Level', 'statistics'))
|
||||||
|
.append($rightsLevel)
|
||||||
|
.appendTo($statistics);
|
||||||
|
renderRightsLevel();
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
function editMetadata(key, value) {
|
||||||
|
if (value != data[key]) {
|
||||||
|
var edit = {id: data.id};
|
||||||
|
if (key == 'title') {
|
||||||
|
edit[key] = value;
|
||||||
|
} else if (listKeys.indexOf(key) >= 0) {
|
||||||
|
edit[key] = value ? value.split(', ') : [];
|
||||||
|
} else {
|
||||||
|
edit[key] = value ? value : null;
|
||||||
|
}
|
||||||
|
pandora.api.editDocument(edit, function(result) {
|
||||||
|
var src;
|
||||||
|
data[key] = result.data[key];
|
||||||
|
Ox.Request.clearCache(); // fixme: too much? can change filter/list etc
|
||||||
|
if (result.data.id != data.id) {
|
||||||
|
pandora.UI.set({document: result.data.id});
|
||||||
|
pandora.$ui.browser.value(data.id, 'id', result.data.id);
|
||||||
|
}
|
||||||
|
//pandora.updateItemContext();
|
||||||
|
//pandora.$ui.browser.value(result.data.id, key, result.data[key]);
|
||||||
|
pandora.$ui.itemTitle
|
||||||
|
.options({title: '<b>' + (pandora.getDocumentTitle(result.data)) + '</b>'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatKey(key, mode) {
|
||||||
|
var item = Ox.getObjectById(pandora.site.itemKeys, key);
|
||||||
|
key = Ox._(item ? item.title : key);
|
||||||
|
mode = mode || 'text';
|
||||||
|
return mode == 'text'
|
||||||
|
? '<span style="font-weight: bold">' + Ox.toTitleCase(key) + ':</span> '
|
||||||
|
: mode == 'description'
|
||||||
|
? Ox.toTitleCase(key)
|
||||||
|
: Ox.Element()
|
||||||
|
.css({marginBottom: '4px', fontWeight: 'bold'})
|
||||||
|
.html(Ox.toTitleCase(key)
|
||||||
|
.replace(' Per ', ' per '));
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLight(str) {
|
||||||
|
return '<span class="OxLight">' + str + '</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLink(value, key) {
|
||||||
|
return (Ox.isArray(value) ? value : [value]).map(function(value) {
|
||||||
|
return key
|
||||||
|
? '<a href="/documents/' + key + '=' + value + '">' + value + '</a>'
|
||||||
|
: value;
|
||||||
|
}).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatValue(key, value) {
|
||||||
|
var ret;
|
||||||
|
if (key == 'date') {
|
||||||
|
ret = value ? Ox.formatDate(value,
|
||||||
|
['', '%Y', '%B %Y', '%B %e, %Y'][value.split('-').length],
|
||||||
|
true
|
||||||
|
) : '';
|
||||||
|
} else if (nameKeys.indexOf(key) > -1) {
|
||||||
|
ret = formatLink(value.split(', '), key);
|
||||||
|
} else if (listKeys.indexOf(key) > -1) {
|
||||||
|
ret = formatLink(value.split(', '), key);
|
||||||
|
} else if (['type'].indexOf(key) > -1) {
|
||||||
|
ret = formatLink(value, key);
|
||||||
|
} else {
|
||||||
|
ret = pandora.formatDocumentKey(Ox.getObjectById(pandora.site.documentKeys, key), data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRightsLevelElement(rightsLevel) {
|
||||||
|
return Ox.Theme.formatColorLevel(
|
||||||
|
rightsLevel,
|
||||||
|
pandora.site.rightsLevels.map(function(rightsLevel) {
|
||||||
|
return rightsLevel.name;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValue(key, value) {
|
||||||
|
return !value ? ''
|
||||||
|
: Ox.contains(listKeys, key) ? value.join(', ')
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCapabilities(rightsLevel) {
|
||||||
|
var capabilities = [].concat(
|
||||||
|
canEdit ? [{name: 'canSeeItem', symbol: 'Find'}] : [],
|
||||||
|
[
|
||||||
|
{name: 'canPlayClips', symbol: 'PlayInToOut'},
|
||||||
|
{name: 'canPlayVideo', symbol: 'Play'},
|
||||||
|
{name: 'canDownloadVideo', symbol: 'Download'}
|
||||||
|
]
|
||||||
|
),
|
||||||
|
userLevels = canEdit ? pandora.site.userLevels : [pandora.user.level];
|
||||||
|
$capabilities.empty();
|
||||||
|
userLevels.forEach(function(userLevel, i) {
|
||||||
|
var $element,
|
||||||
|
$line = $('<div>')
|
||||||
|
.css({
|
||||||
|
height: '16px',
|
||||||
|
marginBottom: '4px'
|
||||||
|
})
|
||||||
|
.appendTo($capabilities);
|
||||||
|
if (canEdit) {
|
||||||
|
$element = Ox.Theme.formatColorLevel(i, userLevels.map(function(userLevel) {
|
||||||
|
return Ox.toTitleCase(userLevel);
|
||||||
|
}), [0, 240]);
|
||||||
|
Ox.Label({
|
||||||
|
textAlign: 'center',
|
||||||
|
title: Ox.toTitleCase(userLevel),
|
||||||
|
width: 60
|
||||||
|
})
|
||||||
|
.addClass('OxColor OxColorGradient')
|
||||||
|
.css({
|
||||||
|
float: 'left',
|
||||||
|
height: '12px',
|
||||||
|
paddingTop: '2px',
|
||||||
|
background: $element.css('background'),
|
||||||
|
fontSize: '8px',
|
||||||
|
color: $element.css('color')
|
||||||
|
})
|
||||||
|
.data({OxColor: $element.data('OxColor')})
|
||||||
|
.appendTo($line);
|
||||||
|
}
|
||||||
|
capabilities.forEach(function(capability) {
|
||||||
|
var hasCapability = pandora.site.capabilities[capability.name][userLevel] >= rightsLevel,
|
||||||
|
$element = Ox.Theme.formatColorLevel(hasCapability, ['', '']);
|
||||||
|
Ox.Button({
|
||||||
|
tooltip: (canEdit ? Ox.toTitleCase(userLevel) : 'You') + ' '
|
||||||
|
+ (hasCapability ? 'can' : 'can\'t') + ' '
|
||||||
|
+ Ox.toSlashes(capability.name)
|
||||||
|
.split('/').slice(1).join(' ')
|
||||||
|
.toLowerCase(),
|
||||||
|
title: capability.symbol,
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.addClass('OxColor OxColorGradient')
|
||||||
|
.css({background: $element.css('background')})
|
||||||
|
.css('margin' + (canEdit ? 'Left' : 'Right'), '4px')
|
||||||
|
.data({OxColor: $element.data('OxColor')})
|
||||||
|
.appendTo($line);
|
||||||
|
});
|
||||||
|
if (!canEdit) {
|
||||||
|
Ox.Button({
|
||||||
|
title: Ox._('Help'),
|
||||||
|
tooltip: Ox._('About Rights'),
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.css({marginLeft: '52px'})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
pandora.UI.set({page: 'rights'});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderGroup(keys) {
|
||||||
|
var $element;
|
||||||
|
if (canEdit || keys.filter(function(key) {
|
||||||
|
return data[key];
|
||||||
|
}).length) {
|
||||||
|
$element = $('<div>').addClass('OxSelectable').css(css);
|
||||||
|
keys.forEach(function(key, i) {
|
||||||
|
if (canEdit || data[key]) {
|
||||||
|
if ($element.children().length) {
|
||||||
|
$('<span>').html('; ').appendTo($element);
|
||||||
|
}
|
||||||
|
$('<span>').html(formatKey(key)).appendTo($element);
|
||||||
|
Ox.EditableContent({
|
||||||
|
clickLink: pandora.clickLink,
|
||||||
|
format: function(value) {
|
||||||
|
return formatValue(key, value);
|
||||||
|
},
|
||||||
|
placeholder: formatLight(Ox._('unknown')),
|
||||||
|
tooltip: canEdit ? pandora.getEditTooltip() : '',
|
||||||
|
value: getValue(key, data[key])
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
submit: function(data) {
|
||||||
|
editMetadata(key, data.value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($element);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$element.appendTo($text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRightsLevel() {
|
||||||
|
var $rightsLevelElement = getRightsLevelElement(data.rightslevel),
|
||||||
|
$rightsLevelSelect;
|
||||||
|
$rightsLevel.empty();
|
||||||
|
if (canEdit) {
|
||||||
|
$rightsLevelSelect = Ox.Select({
|
||||||
|
items: pandora.site.rightsLevels.map(function(rightsLevel, i) {
|
||||||
|
return {id: i, title: rightsLevel.name};
|
||||||
|
}),
|
||||||
|
width: 128,
|
||||||
|
value: data.rightslevel
|
||||||
|
})
|
||||||
|
.addClass('OxColor OxColorGradient')
|
||||||
|
.css({
|
||||||
|
marginBottom: '4px',
|
||||||
|
background: $rightsLevelElement.css('background')
|
||||||
|
})
|
||||||
|
.data({OxColor: $rightsLevelElement.data('OxColor')})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(event) {
|
||||||
|
var rightsLevel = event.value;
|
||||||
|
$rightsLevelElement = getRightsLevelElement(rightsLevel);
|
||||||
|
$rightsLevelSelect
|
||||||
|
.css({background: $rightsLevelElement.css('background')})
|
||||||
|
.data({OxColor: $rightsLevelElement.data('OxColor')})
|
||||||
|
renderCapabilities(rightsLevel);
|
||||||
|
pandora.api.editDocument({id: data.id, rightslevel: rightsLevel}, function(result) {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.appendTo($rightsLevel);
|
||||||
|
} else {
|
||||||
|
$rightsLevelElement
|
||||||
|
.css({
|
||||||
|
marginBottom: '4px'
|
||||||
|
})
|
||||||
|
.appendTo($rightsLevel);
|
||||||
|
}
|
||||||
|
$capabilities = $('<div>').appendTo($rightsLevel);
|
||||||
|
renderCapabilities(data.rightslevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
pandora.UI.set({
|
||||||
|
documentView: 'view',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
that.reload = function() {
|
||||||
|
var src = '/documents/' + data.id + '/512p.jpg?' + data.modified;
|
||||||
|
$icon.attr({src: src});
|
||||||
|
$reflectionIcon.attr({src: src});
|
||||||
|
iconSize = iconSize == 256 ? 512 : 256;
|
||||||
|
iconRatio = ui.icons == 'posters'
|
||||||
|
? (ui.showSitePosters ? pandora.site.posters.ratio : data.posterRatio) : 1;
|
||||||
|
toggleIconSize();
|
||||||
|
};
|
||||||
|
|
||||||
|
that.bindEvent({
|
||||||
|
mousedown: function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
!Ox.Focus.focusedElementIsInput() && that.gainFocus();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
pandora_icons: that.reload,
|
||||||
|
pandora_showsiteposters: function() {
|
||||||
|
ui.icons == 'posters' && that.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
};
|
30
static/js/documentPanel.js
Normal file
30
static/js/documentPanel.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.documentPanel = function() {
|
||||||
|
var that = Ox.SplitPanel({
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
element: pandora.$ui.toolbar = pandora.ui.documentToolbar(),
|
||||||
|
size: 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: pandora.$ui.documentContentPanel = pandora.ui.documentContentPanel()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
id: 'documentPanel',
|
||||||
|
orientation: 'vertical'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
resize: function(data) {
|
||||||
|
if (!pandora.user.ui.document) {
|
||||||
|
pandora.$ui.list && pandora.$ui.list.size();
|
||||||
|
} else {
|
||||||
|
pandora.$ui.document && pandora.$ui.document.update();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
208
static/js/documentToolbar.js
Normal file
208
static/js/documentToolbar.js
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.documentToolbar = function() {
|
||||||
|
var ui = pandora.user.ui,
|
||||||
|
isNavigationView = !ui.item,
|
||||||
|
that = Ox.Bar({
|
||||||
|
size: 24
|
||||||
|
}).css({
|
||||||
|
zIndex: 2 // fixme: remove later
|
||||||
|
}),
|
||||||
|
$viewSelect,
|
||||||
|
$sortView;
|
||||||
|
|
||||||
|
ui.document && that.append(
|
||||||
|
pandora.$ui.backButton = pandora.ui.backButton()
|
||||||
|
);
|
||||||
|
|
||||||
|
$viewSelect = documentViewSelect().appendTo(that);
|
||||||
|
if (!ui.document) {
|
||||||
|
$sortView = documentSortSelect().appendTo(that);
|
||||||
|
}
|
||||||
|
|
||||||
|
that.append(
|
||||||
|
!ui.document
|
||||||
|
? pandora.$ui.listTitle = Ox.Label({
|
||||||
|
textAlign: 'center',
|
||||||
|
title: getCollectionName(pandora.user.ui._collection)
|
||||||
|
})
|
||||||
|
.addClass('OxSelectable')
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: getListTitleLeft() + 'px',
|
||||||
|
top: '4px',
|
||||||
|
right: (ui._collection ? 340 : 326) + 'px',
|
||||||
|
width: 'auto'
|
||||||
|
})
|
||||||
|
: pandora.$ui.itemTitle = Ox.Label({
|
||||||
|
textAlign: 'center'
|
||||||
|
})
|
||||||
|
.addClass('OxSelectable')
|
||||||
|
.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: '266px',
|
||||||
|
top: '4px',
|
||||||
|
right: (ui._collection ? 340 : 326) + 'px',
|
||||||
|
width: 'auto'
|
||||||
|
})
|
||||||
|
.hide()
|
||||||
|
);
|
||||||
|
(!ui.document ? pandora.$ui.listTitle : pandora.$ui.itemTitle).bindEvent({
|
||||||
|
doubleclick: function() {
|
||||||
|
if (!ui.document) {
|
||||||
|
pandora.$ui.list && (
|
||||||
|
ui.collectionView == 'list'
|
||||||
|
? pandora.$ui.list.$body
|
||||||
|
: pandora.$ui.list
|
||||||
|
).animate({
|
||||||
|
scrollTop: 0
|
||||||
|
}, 250);
|
||||||
|
} else {
|
||||||
|
//fixme:
|
||||||
|
pandora.$ui.browser.scrollToSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
that.append(
|
||||||
|
pandora.$ui.findDocumentsElement = pandora.ui.findDocumentsElement(function(data) {
|
||||||
|
var key = data.key,
|
||||||
|
value = data.value,
|
||||||
|
conditions;
|
||||||
|
if (key == 'all') {
|
||||||
|
key = 'title'
|
||||||
|
}
|
||||||
|
conditions = [
|
||||||
|
{key: key, operator: '=', value: value}
|
||||||
|
];
|
||||||
|
if (pandora.user.ui._collection) {
|
||||||
|
conditions.push({
|
||||||
|
key: 'collection',
|
||||||
|
value: pandora.user.ui._collection,
|
||||||
|
operator: '=='
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pandora.UI.set({findDocuments: {conditions: conditions, operator: '&'}});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
that.bindEvent({
|
||||||
|
pandora_collectionsort: function(data) {
|
||||||
|
$sortView.updateElement();
|
||||||
|
},
|
||||||
|
pandora_documentview: function(data) {
|
||||||
|
$viewSelect.options({
|
||||||
|
value: data.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function getCollectionName(listId) {
|
||||||
|
return '<b>' + (
|
||||||
|
listId == ''
|
||||||
|
? Ox._('All {0}', [Ox._(Ox.toTitleCase(ui.section))])
|
||||||
|
: Ox.encodeHTMLEntities(listId.slice(listId.indexOf(':') + 1))
|
||||||
|
) + '</b>';
|
||||||
|
}
|
||||||
|
function getListTitleLeft() {
|
||||||
|
return 284;
|
||||||
|
}
|
||||||
|
|
||||||
|
function documentSortSelect() {
|
||||||
|
var $orderButton = Ox.Button({
|
||||||
|
overlap: 'left',
|
||||||
|
title: getOrderButtonTitle(),
|
||||||
|
tooltip: getOrderButtonTooltip(),
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
click: function() {
|
||||||
|
pandora.UI.set({collectionSort: [{
|
||||||
|
key: ui.collectionSort[0].key,
|
||||||
|
operator: ui.collectionSort[0].operator == '+' ? '-' : '+'
|
||||||
|
}]});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
$sortSelect = Ox.Select({
|
||||||
|
items: pandora.site.documentKeys.filter(function(key) {
|
||||||
|
return key.sort;
|
||||||
|
}).map(function(key) {
|
||||||
|
return {
|
||||||
|
id: key.id,
|
||||||
|
title: Ox._('Sort by {0}', [key.title])
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
value: ui.collectionSort[0].key,
|
||||||
|
width: 128
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
var key = data.value;
|
||||||
|
pandora.UI.set({collectionSort: [{
|
||||||
|
key: key,
|
||||||
|
operator: pandora.getDocumentSortOperator(key)
|
||||||
|
}]});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
that = Ox.FormElementGroup({
|
||||||
|
elements: [$sortSelect, $orderButton],
|
||||||
|
float: 'right'
|
||||||
|
})
|
||||||
|
.css({float: 'left', margin: '4px 2px'})
|
||||||
|
|
||||||
|
function getOrderButtonTitle() {
|
||||||
|
return ui.collectionSort[0].operator == '+' ? 'up' : 'down';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrderButtonTooltip() {
|
||||||
|
return Ox._(ui.collectionSort[0].operator == '+' ? 'Ascending' : 'Descending');
|
||||||
|
}
|
||||||
|
|
||||||
|
that.updateElement = function() {
|
||||||
|
$sortSelect.value(ui.collectionSort[0].key);
|
||||||
|
$orderButton.options({
|
||||||
|
title: getOrderButtonTitle(),
|
||||||
|
tooltip: getOrderButtonTooltip()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return that;
|
||||||
|
}
|
||||||
|
|
||||||
|
function documentViewSelect() {
|
||||||
|
var that;
|
||||||
|
if (!ui.document) {
|
||||||
|
that = Ox.Select({
|
||||||
|
items: pandora.site.collectionViews,
|
||||||
|
value: ui.collectionView,
|
||||||
|
width: 128
|
||||||
|
})
|
||||||
|
.css({float: 'left', margin: '4px 2px 4px 4px'})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
pandora.UI.set({collectionView: data.value});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
that = Ox.Select({
|
||||||
|
items: [
|
||||||
|
{id: 'info', title: Ox._('View Info')},
|
||||||
|
{id: 'view', title: Ox._('View Document')}
|
||||||
|
],
|
||||||
|
value: ui.documentView,
|
||||||
|
width: 128
|
||||||
|
})
|
||||||
|
.css({float: 'left', margin: '4px 2px 4px 4px'})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
pandora.UI.set({documentView: data.value});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return that;
|
||||||
|
}
|
||||||
|
|
||||||
|
that.updateListName = function(listId) {
|
||||||
|
pandora.$ui.listTitle.options({title: getCollectionName(listId)});
|
||||||
|
};
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
|
@ -2,151 +2,108 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
pandora.ui.documentsPanel = function(options) {
|
pandora.documentColumns = [
|
||||||
|
{
|
||||||
|
id: 'title',
|
||||||
|
operator: '+',
|
||||||
|
title: Ox._('Title'),
|
||||||
|
find: true,
|
||||||
|
visible: true,
|
||||||
|
width: 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'id',
|
||||||
|
operator: '+',
|
||||||
|
title: Ox._('ID'),
|
||||||
|
visible: true,
|
||||||
|
width: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: function(value) {
|
||||||
|
return value.toUpperCase();
|
||||||
|
},
|
||||||
|
id: 'extension',
|
||||||
|
operator: '+',
|
||||||
|
title: Ox._('Extension'),
|
||||||
|
find: true,
|
||||||
|
visible: true,
|
||||||
|
width: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'right',
|
||||||
|
format: function(value, data) {
|
||||||
|
return Ox.isArray(value)
|
||||||
|
? Ox.formatDimensions(value, 'px')
|
||||||
|
: Ox.formatCount(value, data.extension == 'html' ? 'word' : 'page');
|
||||||
|
},
|
||||||
|
id: 'dimensions',
|
||||||
|
operator: '-',
|
||||||
|
title: Ox._('Dimensions'),
|
||||||
|
visible: true,
|
||||||
|
width: 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'right',
|
||||||
|
format: function(value) {
|
||||||
|
return Ox.formatValue(value, 'B');
|
||||||
|
},
|
||||||
|
id: 'size',
|
||||||
|
operator: '-',
|
||||||
|
title: Ox._('Size'),
|
||||||
|
visible: true,
|
||||||
|
width: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'description',
|
||||||
|
operator: '+',
|
||||||
|
title: Ox._('Description'),
|
||||||
|
find: true,
|
||||||
|
visible: true,
|
||||||
|
width: 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'right',
|
||||||
|
id: 'matches',
|
||||||
|
operator: '-',
|
||||||
|
title: Ox._('Matches'),
|
||||||
|
visible: true,
|
||||||
|
width: 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'user',
|
||||||
|
operator: '+',
|
||||||
|
title: Ox._('User'),
|
||||||
|
find: true,
|
||||||
|
visible: true,
|
||||||
|
width: 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'right',
|
||||||
|
format: function(value) {
|
||||||
|
return Ox.formatDate(value, '%F %T');
|
||||||
|
},
|
||||||
|
id: 'created',
|
||||||
|
operator: '-',
|
||||||
|
title: Ox._('Created'),
|
||||||
|
visible: true,
|
||||||
|
width: 144
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'right',
|
||||||
|
format: function(value) {
|
||||||
|
return Ox.formatDate(value, '%F %T');
|
||||||
|
},
|
||||||
|
id: 'modified',
|
||||||
|
operator: '-',
|
||||||
|
title: Ox._('Modified'),
|
||||||
|
visible: true,
|
||||||
|
width: 144
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
pandora.ui.documentSortSelect = function() {
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
hasItemView = ui.section == 'items' && ui.item,
|
$orderButton = Ox.Button({
|
||||||
hasListSelection = ui.section == 'items' && !ui.item && ui.listSelection.length,
|
|
||||||
isItemView = options.isItemView,
|
|
||||||
listLoaded = false,
|
|
||||||
allFindKeys = ['user', 'name', 'entity', 'extension', 'description'].filter(function(key) {
|
|
||||||
return key != 'entity' || pandora.site.entities.length;
|
|
||||||
}),
|
|
||||||
|
|
||||||
columns = [
|
|
||||||
{
|
|
||||||
id: 'name',
|
|
||||||
operator: '+',
|
|
||||||
title: Ox._('Name'),
|
|
||||||
visible: true,
|
|
||||||
width: 256
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'id',
|
|
||||||
operator: '+',
|
|
||||||
title: Ox._('ID'),
|
|
||||||
visible: true,
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
format: function(value) {
|
|
||||||
return value.toUpperCase();
|
|
||||||
},
|
|
||||||
id: 'extension',
|
|
||||||
operator: '+',
|
|
||||||
title: Ox._('Extension'),
|
|
||||||
visible: true,
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align: 'right',
|
|
||||||
format: function(value) {
|
|
||||||
return Ox.isArray(value)
|
|
||||||
? Ox.formatDimensions(value, 'px')
|
|
||||||
: Ox.formatCount(value, 'page');
|
|
||||||
},
|
|
||||||
id: 'dimensions',
|
|
||||||
operator: '-',
|
|
||||||
title: Ox._('Dimensions'),
|
|
||||||
visible: true,
|
|
||||||
width: 128
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align: 'right',
|
|
||||||
format: function(value) {
|
|
||||||
return Ox.formatValue(value, 'B');
|
|
||||||
},
|
|
||||||
id: 'size',
|
|
||||||
operator: '-',
|
|
||||||
title: Ox._('Size'),
|
|
||||||
visible: true,
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'description',
|
|
||||||
operator: '+',
|
|
||||||
title: Ox._('Description'),
|
|
||||||
visible: true,
|
|
||||||
width: 256
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align: 'right',
|
|
||||||
id: 'matches',
|
|
||||||
operator: '-',
|
|
||||||
title: Ox._('Matches'),
|
|
||||||
visible: true,
|
|
||||||
width: 64
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'user',
|
|
||||||
operator: '+',
|
|
||||||
title: Ox._('User'),
|
|
||||||
visible: true,
|
|
||||||
width: 128
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align: 'right',
|
|
||||||
format: function(value) {
|
|
||||||
return Ox.formatDate(value, '%F %T');
|
|
||||||
},
|
|
||||||
id: 'created',
|
|
||||||
operator: '-',
|
|
||||||
title: Ox._('Created'),
|
|
||||||
visible: true,
|
|
||||||
width: 144
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align: 'right',
|
|
||||||
format: function(value) {
|
|
||||||
return Ox.formatDate(value, '%F %T');
|
|
||||||
},
|
|
||||||
id: 'modified',
|
|
||||||
operator: '-',
|
|
||||||
title: Ox._('Modified'),
|
|
||||||
visible: true,
|
|
||||||
width: 144
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
$listBar = Ox.Bar({size: 24}),
|
|
||||||
|
|
||||||
$viewSelect = Ox.Select({
|
|
||||||
items: [
|
|
||||||
{id: 'list', title: Ox._('View as List')},
|
|
||||||
{id: 'grid', title: Ox._('View as Grid')}
|
|
||||||
],
|
|
||||||
value: ui.documentsView,
|
|
||||||
width: 128
|
|
||||||
})
|
|
||||||
.css({float: 'left', margin: '4px 2px 4px 4px'})
|
|
||||||
.bindEvent({
|
|
||||||
change: function(data) {
|
|
||||||
pandora.UI.set({documentsView: data.value});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.appendTo($listBar),
|
|
||||||
|
|
||||||
$sortSelect = Ox.Select({
|
|
||||||
items: columns.map(function(column) {
|
|
||||||
return {
|
|
||||||
id: column.id,
|
|
||||||
title: Ox._('Sort by {0}', [column.title])
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
value: ui.documentsSort[0].key,
|
|
||||||
width: 128
|
|
||||||
})
|
|
||||||
.bindEvent({
|
|
||||||
change: function(data) {
|
|
||||||
var key = data.value;
|
|
||||||
pandora.UI.set({documentsSort: [{
|
|
||||||
key: key,
|
|
||||||
operator: Ox.getObjectById(columns, key).operator
|
|
||||||
}]});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
$orderButton = Ox.Button({
|
|
||||||
overlap: 'left',
|
overlap: 'left',
|
||||||
title: getOrderButtonTitle(),
|
title: getOrderButtonTitle(),
|
||||||
tooltip: getOrderButtonTooltip(),
|
tooltip: getOrderButtonTooltip(),
|
||||||
|
@ -160,55 +117,85 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
}]});
|
}]});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
$sortSelect = Ox.Select({
|
||||||
$sortElement = Ox.FormElementGroup({
|
items: pandora.documentColumns.map(function(column) {
|
||||||
elements: [$sortSelect, $orderButton],
|
return {
|
||||||
float: 'right'
|
id: column.id,
|
||||||
})
|
title: Ox._('Sort by {0}', [column.title])
|
||||||
.css({float: 'left', margin: '4px 2px'})
|
};
|
||||||
.appendTo($listBar),
|
|
||||||
|
|
||||||
$findSelect = Ox.Select({
|
|
||||||
items: [
|
|
||||||
{id: 'all', title: Ox._('Find: All')},
|
|
||||||
{id: 'name', title: Ox._('Find: Name')},
|
|
||||||
{id: 'user', title: Ox._('Find: User')},
|
|
||||||
{id: 'entity', title: Ox._('Find: Entity')}
|
|
||||||
].filter(function(item) {
|
|
||||||
if (item.id == 'user') {
|
|
||||||
return !isItemView;
|
|
||||||
} else if (item.id == 'entity') {
|
|
||||||
return pandora.site.entities.length;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}),
|
}),
|
||||||
overlap: 'right',
|
value: ui.documentsSort[0].key,
|
||||||
type: 'image'
|
width: 128
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: function(data) {
|
change: function(data) {
|
||||||
$findInput.options({placeholder: data.title}).focusInput();
|
var key = data.value;
|
||||||
|
pandora.UI.set({documentsSort: [{
|
||||||
|
key: key,
|
||||||
|
operator: Ox.getObjectById(pandora.documentColumns, key).operator
|
||||||
|
}]});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
that = Ox.FormElementGroup({
|
||||||
$findInput = Ox.Input({
|
elements: [$sortSelect, $orderButton],
|
||||||
changeOnKeypress: true,
|
float: 'right'
|
||||||
clear: true,
|
|
||||||
placeholder: Ox._('Find: All'),
|
|
||||||
width: 192
|
|
||||||
})
|
})
|
||||||
|
.css({float: 'left', margin: '4px 2px'});
|
||||||
|
|
||||||
|
function getOrderButtonTitle() {
|
||||||
|
return ui.documentsSort[0].operator == '+' ? 'up' : 'down';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOrderButtonTooltip() {
|
||||||
|
return Ox._(ui.documentsSort[0].operator == '+' ? 'Ascending' : 'Descending');
|
||||||
|
}
|
||||||
|
|
||||||
|
that.sortValue = function(value) {
|
||||||
|
$sortSelect.value(value);
|
||||||
|
$orderButton.options({
|
||||||
|
title: getOrderButtonTitle(),
|
||||||
|
tooltip: getOrderButtonTooltip()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
|
pandora.ui.documentViewSelect = function() {
|
||||||
|
var ui = pandora.user.ui,
|
||||||
|
that = Ox.Select({
|
||||||
|
items: [
|
||||||
|
{id: 'list', title: Ox._('View as List')},
|
||||||
|
{id: 'grid', title: Ox._('View as Grid')}
|
||||||
|
],
|
||||||
|
value: ui.documentsView,
|
||||||
|
width: 128
|
||||||
|
})
|
||||||
|
.css({float: 'left', margin: '4px 2px 4px 4px'})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
change: updateList
|
change: function(data) {
|
||||||
|
pandora.UI.set({documentsView: data.value});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
|
pandora.ui.documentsPanel = function(options) {
|
||||||
|
|
||||||
|
var ui = pandora.user.ui,
|
||||||
|
hasItemView = ui.section == 'items' && ui.item,
|
||||||
|
hasListSelection = ui.section == 'items' && !ui.item && ui.listSelection.length,
|
||||||
|
isItemView = options.isItemView,
|
||||||
|
listLoaded = false,
|
||||||
|
allFindKeys = ['user', 'title', 'entity', 'extension', 'description'].filter(function(key) {
|
||||||
|
return key != 'entity' || pandora.site.entities.length;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$findElement = Ox.FormElementGroup({
|
$listBar = Ox.Bar({size: 24}),
|
||||||
elements: [
|
|
||||||
$findSelect,
|
$viewSelect = pandora.ui.documentViewSelect().appendTo($listBar),
|
||||||
$findInput
|
$sortElement = pandora.ui.documentSortSelect().appendTo($listBar),
|
||||||
]
|
|
||||||
})
|
$findElement = findElement(updateList, isItemView).appendTo($listBar),
|
||||||
.css({float: 'right', margin: '4px 4px 4px 2px'})
|
|
||||||
.appendTo($listBar),
|
|
||||||
|
|
||||||
$list = renderList(),
|
$list = renderList(),
|
||||||
|
|
||||||
|
@ -389,7 +376,7 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
that.size(1, data.value);
|
that.size(1, data.value);
|
||||||
},
|
},
|
||||||
pandora_documentssort: function(data) {
|
pandora_documentssort: function(data) {
|
||||||
updateSortElement();
|
$sortElement.sortValue(ui.documentsSort[0].key);
|
||||||
$list.options({sort: data.value});
|
$list.options({sort: data.value});
|
||||||
},
|
},
|
||||||
pandora_documentsview: function(data) {
|
pandora_documentsview: function(data) {
|
||||||
|
@ -410,12 +397,6 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
pandora.$ui.documentsList = $list;
|
pandora.$ui.documentsList = $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
// to determine the width of the find input inside
|
|
||||||
// the documents dialog, that dialog has to be present
|
|
||||||
setTimeout(function() {
|
|
||||||
$findInput.options({width: getFindInputWidth()});
|
|
||||||
});
|
|
||||||
|
|
||||||
function addDocuments() {
|
function addDocuments() {
|
||||||
var ids = ui.documentsSelection[''];
|
var ids = ui.documentsSelection[''];
|
||||||
pandora.api.addDocument({
|
pandora.api.addDocument({
|
||||||
|
@ -460,12 +441,59 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
openDocumentsDialog();
|
openDocumentsDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOrderButtonTitle() {
|
function findElement(callback, isItemView) {
|
||||||
return ui.documentsSort[0].operator == '+' ? 'up' : 'down';
|
var $findSelect = Ox.Select({
|
||||||
}
|
items: [
|
||||||
|
{id: 'all', title: Ox._('Find: All')},
|
||||||
|
{id: 'title', title: Ox._('Find: Title')},
|
||||||
|
{id: 'user', title: Ox._('Find: User')},
|
||||||
|
{id: 'entity', title: Ox._('Find: Entity')}
|
||||||
|
].filter(function(item) {
|
||||||
|
if (item.id == 'user') {
|
||||||
|
return !isItemView;
|
||||||
|
} else if (item.id == 'entity') {
|
||||||
|
return pandora.site.entities.length;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
overlap: 'right',
|
||||||
|
type: 'image'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
$findInput.options({placeholder: data.title}).focusInput();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
function getOrderButtonTooltip() {
|
$findInput = Ox.Input({
|
||||||
return Ox._(ui.documentsSort[0].operator == '+' ? 'Ascending' : 'Descending');
|
changeOnKeypress: true,
|
||||||
|
clear: true,
|
||||||
|
placeholder: Ox._('Find: All'),
|
||||||
|
width: 192
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
data.key = $findSelect.value();
|
||||||
|
data.value = $findInput.value()
|
||||||
|
callback(data);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
that = Ox.FormElementGroup({
|
||||||
|
elements: [
|
||||||
|
$findSelect,
|
||||||
|
$findInput
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.css({float: 'right', margin: '4px 4px 4px 2px'});
|
||||||
|
|
||||||
|
// to determine the width of the find input inside
|
||||||
|
// the documents dialog, that dialog has to be present
|
||||||
|
setTimeout(function() {
|
||||||
|
$findInput.options({width: getFindInputWidth()});
|
||||||
|
});
|
||||||
|
|
||||||
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFindInputWidth() {
|
function getFindInputWidth() {
|
||||||
|
@ -564,8 +592,8 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
.append(
|
.append(
|
||||||
$name = Ox.EditableContent({
|
$name = Ox.EditableContent({
|
||||||
editable: editable,
|
editable: editable,
|
||||||
tooltip: editable ? pandora.getEditTooltip('name') : '',
|
tooltip: editable ? pandora.getEditTooltip('title') : '',
|
||||||
value: item.name,
|
value: item.title,
|
||||||
width: width
|
width: width
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
|
@ -580,11 +608,11 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
},
|
},
|
||||||
submit: function(data) {
|
submit: function(data) {
|
||||||
pandora.api.editDocument({
|
pandora.api.editDocument({
|
||||||
name: data.value,
|
title: data.value,
|
||||||
id: item.id,
|
id: item.id,
|
||||||
item: ui.item,
|
item: ui.item,
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
$name.options({value: result.data.name});
|
$name.options({value: result.data.title});
|
||||||
Ox.Request.clearCache('findDocuments');
|
Ox.Request.clearCache('findDocuments');
|
||||||
$list.reloadList();
|
$list.reloadList();
|
||||||
});
|
});
|
||||||
|
@ -631,10 +659,10 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
items: [
|
items: [
|
||||||
Ox.Input({
|
Ox.Input({
|
||||||
disabled: !editable,
|
disabled: !editable,
|
||||||
id: 'name',
|
id: 'title',
|
||||||
label: Ox._('Name'),
|
label: Ox._('Title'),
|
||||||
labelWidth: labelWidth,
|
labelWidth: labelWidth,
|
||||||
value: item.name,
|
value: item.title,
|
||||||
width: width
|
width: width
|
||||||
}),
|
}),
|
||||||
Ox.Input({
|
Ox.Input({
|
||||||
|
@ -839,7 +867,7 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
function renderList() {
|
function renderList() {
|
||||||
var options = {
|
var options = {
|
||||||
items: pandora.api.findDocuments,
|
items: pandora.api.findDocuments,
|
||||||
keys: ['description', 'dimensions', 'extension', 'id', 'name', 'ratio', 'size', 'user', 'entities', 'modified'],
|
keys: ['description', 'dimensions', 'extension', 'id', 'title', 'ratio', 'size', 'user', 'entities', 'modified'],
|
||||||
query: {
|
query: {
|
||||||
conditions: isItemView ? [{ key: 'item', value: ui.item, operator: '==' }] : [],
|
conditions: isItemView ? [{ key: 'item', value: ui.item, operator: '==' }] : [],
|
||||||
operator: '&'
|
operator: '&'
|
||||||
|
@ -847,27 +875,27 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
selected: ui.documentsSelection[isItemView ? ui.item : ''] || [],
|
selected: ui.documentsSelection[isItemView ? ui.item : ''] || [],
|
||||||
sort: ui.documentsSort.concat([
|
sort: ui.documentsSort.concat([
|
||||||
{key: 'extension', operator: '+'},
|
{key: 'extension', operator: '+'},
|
||||||
{key: 'name', operator: '+'}
|
{key: 'title', operator: '+'}
|
||||||
]),
|
]),
|
||||||
unique: 'id'
|
unique: 'id'
|
||||||
};
|
};
|
||||||
return (ui.documentsView == 'list' ? Ox.TableList(Ox.extend(options, {
|
return (ui.documentsView == 'list' ? Ox.TableList(Ox.extend(options, {
|
||||||
columns: columns,
|
columns: pandora.documentColumns,
|
||||||
columnsVisible: true,
|
columnsVisible: true,
|
||||||
scrollbarVisible: true,
|
scrollbarVisible: true,
|
||||||
})) : Ox.IconList(Ox.extend(options, {
|
})) : Ox.IconList(Ox.extend(options, {
|
||||||
item: function(data, sort, size) {
|
item: function(data, sort, size) {
|
||||||
var sortKey = sort[0].key,
|
var sortKey = sort[0].key,
|
||||||
infoKey = sortKey == 'name' ? 'extension' : sortKey,
|
infoKey = sortKey == 'title' ? 'extension' : sortKey,
|
||||||
info = (
|
info = (
|
||||||
Ox.getObjectById(columns, infoKey).format || Ox.identity
|
Ox.getObjectById(pandora.documentColumns, infoKey).format || Ox.identity
|
||||||
)(data[infoKey]),
|
)(data[infoKey]),
|
||||||
size = size || 128;
|
size = size || 128;
|
||||||
return {
|
return {
|
||||||
height: Math.round(data.ratio > 1 ? size / data.ratio : size),
|
height: Math.round(data.ratio > 1 ? size / data.ratio : size),
|
||||||
id: data.id,
|
id: data.id,
|
||||||
info: info,
|
info: info,
|
||||||
title: data.name,
|
title: data.title,
|
||||||
url: pandora.getMediaURL('/documents/' + data.id + '/256p.jpg?' + data.modified),
|
url: pandora.getMediaURL('/documents/' + data.id + '/256p.jpg?' + data.modified),
|
||||||
width: Math.round(data.ratio > 1 ? size : size * data.ratio)
|
width: Math.round(data.ratio > 1 ? size : size * data.ratio)
|
||||||
};
|
};
|
||||||
|
@ -879,6 +907,9 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
// we can't open upload dialog via control+n
|
// we can't open upload dialog via control+n
|
||||||
isItemView && openDocumentsDialog();
|
isItemView && openDocumentsDialog();
|
||||||
},
|
},
|
||||||
|
copy: function(data) {
|
||||||
|
pandora.clipboard.copy(data.ids, 'document');
|
||||||
|
},
|
||||||
closepreview: closeDocuments,
|
closepreview: closeDocuments,
|
||||||
'delete': isItemView ? removeDocuments : deleteDocuments,
|
'delete': isItemView ? removeDocuments : deleteDocuments,
|
||||||
init: function(data) {
|
init: function(data) {
|
||||||
|
@ -892,6 +923,22 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
},
|
},
|
||||||
open: openDocuments,
|
open: openDocuments,
|
||||||
openpreview: openDocuments,
|
openpreview: openDocuments,
|
||||||
|
paste: function(data) {
|
||||||
|
if (isItemView) {
|
||||||
|
//fixme permissions!
|
||||||
|
var items = pandora.clipboard.paste();
|
||||||
|
if (items.length && pandora.clipboard.type() == 'document') {
|
||||||
|
//fixme use history
|
||||||
|
pandora.api.addDocument({
|
||||||
|
item: ui.item,
|
||||||
|
ids: items
|
||||||
|
}, function(result) {
|
||||||
|
Ox.Request.clearCache('findDocuments');
|
||||||
|
$list.reloadList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
select: function(data) {
|
select: function(data) {
|
||||||
pandora.UI.set(
|
pandora.UI.set(
|
||||||
'documentsSelection.' + (isItemView ? ui.item : ''), data.ids
|
'documentsSelection.' + (isItemView ? ui.item : ''), data.ids
|
||||||
|
@ -1017,9 +1064,10 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateList() {
|
function updateList(data) {
|
||||||
var key = $findSelect.value(),
|
|
||||||
value = $findInput.value(),
|
var key = data.key,
|
||||||
|
value = data.value,
|
||||||
itemCondition = isItemView
|
itemCondition = isItemView
|
||||||
? {key: 'item', operator: '==', value: ui.item}
|
? {key: 'item', operator: '==', value: ui.item}
|
||||||
: null,
|
: null,
|
||||||
|
@ -1040,11 +1088,7 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSortElement() {
|
function updateSortElement() {
|
||||||
$sortSelect.value(ui.documentsSort[0].key);
|
$sortElement.sortValue(ui.documentsSort[0].key);
|
||||||
$orderButton.options({
|
|
||||||
title: getOrderButtonTitle(),
|
|
||||||
tooltip: getOrderButtonTooltip()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadDocuments(data) {
|
function uploadDocuments(data) {
|
||||||
|
|
159
static/js/findDocumentsElement.js
Normal file
159
static/js/findDocumentsElement.js
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
// vim: et:ts=4:sw=4:sts=4:ft=javascript
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
pandora.ui.findDocumentsElement = function() {
|
||||||
|
var findIndex = pandora.user.ui._findDocumentsState.index,
|
||||||
|
findKey = pandora.user.ui._findDocumentsState.key,
|
||||||
|
findValue = pandora.user.ui._findDocumentsState.value,
|
||||||
|
hasPressedClear = false,
|
||||||
|
previousFindKey = findKey,
|
||||||
|
$findCollectionSelect,
|
||||||
|
$findSelect,
|
||||||
|
$findInput,
|
||||||
|
that = Ox.FormElementGroup({
|
||||||
|
elements: [].concat(pandora.user.ui._collection ? [
|
||||||
|
$findCollectionSelect = Ox.Select({
|
||||||
|
items: [
|
||||||
|
{id: 'all', title: Ox._('Find: All {0}', [Ox._('Documents')])},
|
||||||
|
{id: 'collection', title: Ox._('Find: This Collection')}
|
||||||
|
],
|
||||||
|
overlap: 'right',
|
||||||
|
type: 'image',
|
||||||
|
tooltip: Ox._('Find: This Collection'),
|
||||||
|
value: 'collection'
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
$findCollectionSelect.options({
|
||||||
|
tooltip: Ox.getObjectById(
|
||||||
|
$findCollectionSelect.options('items'),
|
||||||
|
data.value
|
||||||
|
).title
|
||||||
|
});
|
||||||
|
$findInput.focusInput(true);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
] : [], [
|
||||||
|
$findSelect = Ox.Select({
|
||||||
|
id: 'select',
|
||||||
|
items: pandora.site.documentKeys.filter(function(key) {
|
||||||
|
return key.find;
|
||||||
|
}).map(function(key) {
|
||||||
|
return {
|
||||||
|
id: key.id,
|
||||||
|
title: Ox._('Find: {0}', [Ox._(key.title)])
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
overlap: 'right',
|
||||||
|
value: findKey,
|
||||||
|
width: 128
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
change: function(data) {
|
||||||
|
//pandora.$ui.mainMenu.checkItem('findMenu_find_' + data.value);
|
||||||
|
$findInput.options({
|
||||||
|
autocomplete: autocompleteFunction(),
|
||||||
|
placeholder: ''
|
||||||
|
}).focusInput(true);
|
||||||
|
previousFindKey = data.value;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
$findInput = Ox.Input({
|
||||||
|
autocomplete: autocompleteFunction(),
|
||||||
|
autocompleteSelect: true,
|
||||||
|
autocompleteSelectHighlight: true,
|
||||||
|
autocompleteSelectMaxWidth: 256,
|
||||||
|
autocompleteSelectSubmit: true,
|
||||||
|
clear: true,
|
||||||
|
clearTooltip: Ox._('Click to clear or doubleclick to reset query'),
|
||||||
|
id: 'input',
|
||||||
|
placeholder: findKey == 'advanced' ? Ox._('Edit Query...') : '',
|
||||||
|
value: findValue,
|
||||||
|
width: 192
|
||||||
|
})
|
||||||
|
.bindEvent({
|
||||||
|
clear: function() {
|
||||||
|
hasPressedClear = true;
|
||||||
|
},
|
||||||
|
focus: function(data) {
|
||||||
|
if ($findSelect.value() == 'advanced') {
|
||||||
|
if (hasPressedClear) {
|
||||||
|
pandora.UI.set({find: pandora.site.user.ui.find});
|
||||||
|
that.updateElement();
|
||||||
|
hasPressedClear = false;
|
||||||
|
}
|
||||||
|
$findInput.blurInput();
|
||||||
|
//fixme advanced find dialog for documents
|
||||||
|
//pandora.$ui.filterDialog = pandora.ui.filterDialog().open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit: function(data) {
|
||||||
|
var findInList = pandora.user.ui._collection
|
||||||
|
&& $findCollectionSelect.value() == 'collection',
|
||||||
|
key = $findSelect.value(),
|
||||||
|
conditions = [].concat(
|
||||||
|
findInList ? [{
|
||||||
|
key: 'collection',
|
||||||
|
value: pandora.user.ui._collection,
|
||||||
|
operator: '=='
|
||||||
|
}] : [],
|
||||||
|
data.value ? [{
|
||||||
|
key: key,
|
||||||
|
value: data.value,
|
||||||
|
operator: '='
|
||||||
|
}] : []
|
||||||
|
);
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: {conditions: conditions, operator: '&'}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
id: 'findElement'
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
float: 'right',
|
||||||
|
margin: '4px'
|
||||||
|
});
|
||||||
|
function autocompleteFunction() {
|
||||||
|
var key = !that
|
||||||
|
? pandora.user.ui._findDocumentsState.key
|
||||||
|
: that.value()[pandora.user.ui._collection ? 1 : 0],
|
||||||
|
findKey = Ox.getObjectById(pandora.site.documentFindKeys, key);
|
||||||
|
return findKey && findKey.autocomplete ? function(value, callback) {
|
||||||
|
value === '' && Ox.Log('', 'Warning: autocomplete function should never be called with empty value');
|
||||||
|
pandora.api.autocompleteDocuments({
|
||||||
|
key: key,
|
||||||
|
query: {
|
||||||
|
conditions: pandora.user.ui._collection
|
||||||
|
&& $findCollectionSelect.value() == 'collection'
|
||||||
|
? [{key: 'collection', value: pandora.user.ui._collection, operator: '=='}] : [],
|
||||||
|
operator: '&'
|
||||||
|
},
|
||||||
|
range: [0, 20],
|
||||||
|
sort: findKey.autocompleteSort,
|
||||||
|
value: value
|
||||||
|
}, function(result) {
|
||||||
|
callback(result.data.items.map(function(item) {
|
||||||
|
return Ox.decodeHTMLEntities(item);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
} : null;
|
||||||
|
}
|
||||||
|
that.updateElement = function() {
|
||||||
|
var findState = pandora.user.ui._findDocumentsState;
|
||||||
|
$findSelect.value(findState.key);
|
||||||
|
$findInput.options(
|
||||||
|
findState.key == 'advanced' ? {
|
||||||
|
placeholder: Ox._('Edit Query...'),
|
||||||
|
value: ''
|
||||||
|
} : {
|
||||||
|
autocomplete: autocompleteFunction(),
|
||||||
|
placeholder: '',
|
||||||
|
value: findState.value
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
pandora.ui.folderBrowserBar = function(id, section) {
|
pandora.ui.folderBrowserBar = function(id, section) {
|
||||||
section = section || pandora.user.ui.section;
|
section = section || pandora.user.ui.section;
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
folderItems = section == 'items' ? 'Lists' : Ox.toTitleCase(section),
|
folderItems = pandora.getFolderItems(section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
that = Ox.Bar({
|
that = Ox.Bar({
|
||||||
size: 24
|
size: 24
|
||||||
|
|
|
@ -7,7 +7,7 @@ pandora.ui.folderBrowserList = function(id, section) {
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
columnWidth = (ui.sidebarSize - Ox.UI.SCROLLBAR_SIZE - (section != 'texts' ? 96 : 48)) / 2,
|
columnWidth = (ui.sidebarSize - Ox.UI.SCROLLBAR_SIZE - (section != 'texts' ? 96 : 48)) / 2,
|
||||||
i = Ox.getIndexById(pandora.site.sectionFolders[section], id),
|
i = Ox.getIndexById(pandora.site.sectionFolders[section], id),
|
||||||
folderItems = section == 'items' ? 'Lists' : Ox.toTitleCase(section),
|
folderItems = pandora.getFolderItems(section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
that = Ox.TableList({
|
that = Ox.TableList({
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -152,7 +152,12 @@ pandora.ui.folderBrowserList = function(id, section) {
|
||||||
// not-featured list may be in the user's favorites folder
|
// not-featured list may be in the user's favorites folder
|
||||||
keys: id == 'featured' ? ['subscribed'] : [],
|
keys: id == 'featured' ? ['subscribed'] : [],
|
||||||
pageLength: 1000,
|
pageLength: 1000,
|
||||||
selected: pandora.getListData().folder == id ? [section == 'items' ? ui._list : ui[section.slice(0, -1)]] : [],
|
selected: pandora.getListData().folder == id
|
||||||
|
? [{
|
||||||
|
items: ui._list,
|
||||||
|
documents: ui._documentlist
|
||||||
|
}[section] || ui[section.slice(0, -1)]]
|
||||||
|
: [],
|
||||||
sort: [{key: 'name', operator: '+'}],
|
sort: [{key: 'name', operator: '+'}],
|
||||||
unique: 'id'
|
unique: 'id'
|
||||||
})
|
})
|
||||||
|
@ -226,6 +231,15 @@ pandora.ui.folderBrowserList = function(id, section) {
|
||||||
operator: '&'
|
operator: '&'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: {
|
||||||
|
conditions: list ? [
|
||||||
|
{key: 'list', value: data.ids[0], operator: '=='}
|
||||||
|
] : [],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set(section.slice(0, -1), list);
|
pandora.UI.set(section.slice(0, -1), list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ pandora.ui.folderList = function(id, section) {
|
||||||
section = section || pandora.user.section;
|
section = section || pandora.user.section;
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
i = Ox.getIndexById(pandora.site.sectionFolders[section], id),
|
i = Ox.getIndexById(pandora.site.sectionFolders[section], id),
|
||||||
folderItems = section == 'items' ? 'Lists' : Ox.toTitleCase(section),
|
folderItems = pandora.getFolderItems(section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
canEditFeatured = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
|
canEditFeatured = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
|
||||||
$placeholder,
|
$placeholder,
|
||||||
|
@ -20,9 +20,7 @@ pandora.ui.folderList = function(id, section) {
|
||||||
},
|
},
|
||||||
format: function(value, data) {
|
format: function(value, data) {
|
||||||
return $('<img>').attr({
|
return $('<img>').attr({
|
||||||
src: '/' + folderItem.toLowerCase() + '/'
|
src: pandora.getListIcon(section, data.id, '', data.modified)
|
||||||
+ encodeURIComponent(data.id) + '/icon.jpg?'
|
|
||||||
+ data.modified
|
|
||||||
}).css({
|
}).css({
|
||||||
width: '14px',
|
width: '14px',
|
||||||
height: '14px',
|
height: '14px',
|
||||||
|
@ -337,7 +335,7 @@ pandora.ui.folderList = function(id, section) {
|
||||||
pandora.api['unsubscribeFrom' + folderItem]({
|
pandora.api['unsubscribeFrom' + folderItem]({
|
||||||
id: data.ids[0]
|
id: data.ids[0]
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
Ox.Request.clearCache('findList');
|
Ox.Request.clearCache('find' + folderItems);
|
||||||
that.reloadList();
|
that.reloadList();
|
||||||
});
|
});
|
||||||
} else if (id == 'featured' && canEditFeatured) {
|
} else if (id == 'featured' && canEditFeatured) {
|
||||||
|
@ -346,7 +344,7 @@ pandora.ui.folderList = function(id, section) {
|
||||||
id: data.ids[0],
|
id: data.ids[0],
|
||||||
status: 'public'
|
status: 'public'
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
Ox.Request.clearCache('findList');
|
Ox.Request.clearCache('find' + folderItems);
|
||||||
// fixme: duplicated
|
// fixme: duplicated
|
||||||
if (result.data.user == pandora.user.username || result.data.subscribed) {
|
if (result.data.user == pandora.user.username || result.data.subscribed) {
|
||||||
pandora.$ui.folderList[
|
pandora.$ui.folderList[
|
||||||
|
@ -415,6 +413,20 @@ pandora.ui.folderList = function(id, section) {
|
||||||
: that.value(list).view
|
: that.value(list).view
|
||||||
: void 0
|
: void 0
|
||||||
});
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: {
|
||||||
|
conditions: list ? [
|
||||||
|
{key: 'collection', value: list, operator: '=='}
|
||||||
|
] : [],
|
||||||
|
operator: '&'
|
||||||
|
},
|
||||||
|
collectionView: list
|
||||||
|
? pandora.user.ui.collections[list]
|
||||||
|
? pandora.user.ui.collections[list].view
|
||||||
|
: that.value(list).view
|
||||||
|
: void 0
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set(section.slice(0, -1), list);
|
pandora.UI.set(section.slice(0, -1), list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,13 @@ pandora.ui.folderPlaceholder = function(id, section) {
|
||||||
.css({
|
.css({
|
||||||
height: '14px',
|
height: '14px',
|
||||||
padding: '1px 4px',
|
padding: '1px 4px',
|
||||||
});
|
}),
|
||||||
|
folderItems = pandora.getFolderItems(section);
|
||||||
that.updateText = function(string, isFind) {
|
that.updateText = function(string, isFind) {
|
||||||
return that.html(
|
return that.html(
|
||||||
string != 'volumes'
|
string != 'volumes'
|
||||||
? Ox._('No {0} {1}' + (isFind ? ' found' : ''),
|
? Ox._('No {0} {1}' + (isFind ? ' found' : ''),
|
||||||
[Ox._(string), Ox._(section == 'items' ? 'lists' : section)])
|
[Ox._(string), Ox._(folderItems.toLowerCase())])
|
||||||
: Ox._('No local volumes')
|
: Ox._('No local volumes')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,10 +10,13 @@ pandora.ui.folders = function(section) {
|
||||||
pandora.resizeFolders();
|
pandora.resizeFolders();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
editable = (ui[
|
editable = (ui[{
|
||||||
section == 'items' ? '_list' : section.slice(0, -1)
|
items: '_list',
|
||||||
] || '').split(':')[0] == pandora.user.username,
|
edits: 'edit',
|
||||||
folderItems = section == 'items' ? 'Lists' : Ox.toTitleCase(section),
|
documents: '_collection',
|
||||||
|
texts: 'text'
|
||||||
|
}[section]] || '').split(':')[0] == pandora.user.username,
|
||||||
|
folderItems = pandora.getFolderItems(section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
canEditFeatured = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
|
canEditFeatured = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
|
||||||
initCounter = 0,
|
initCounter = 0,
|
||||||
|
@ -38,21 +41,21 @@ pandora.ui.folders = function(section) {
|
||||||
: Ox._('To create and share your own {0}, please sign up or sign in.', [section])
|
: Ox._('To create and share your own {0}, please sign up or sign in.', [section])
|
||||||
)];
|
)];
|
||||||
} else {
|
} else {
|
||||||
if (section == 'items') {
|
if (Ox.contains(pandora.site.listSections, section)) {
|
||||||
extras = [
|
extras = [
|
||||||
pandora.$ui.personalListsMenu = Ox.MenuButton({
|
pandora.$ui.personalListsMenu = Ox.MenuButton({
|
||||||
items: [
|
items: [
|
||||||
{ id: 'newlist', title: Ox._('New List'), keyboard: 'control n' },
|
{ id: 'newlist', title: Ox._('New {0}', [Ox._(folderItem)]), keyboard: 'control n' },
|
||||||
{ id: 'newlistfromselection', title: Ox._('New List from Selection'), keyboard: 'shift control n', disabled: ui.listSelection.length == 0 },
|
{ id: 'newlistfromselection', title: Ox._('New {0} from Selection', [Ox._(folderItem)]), keyboard: 'shift control n', disabled: ui.listSelection.length == 0 },
|
||||||
{ id: 'newsmartlist', title: Ox._('New Smart List'), keyboard: 'alt control n' },
|
{ id: 'newsmartlist', title: Ox._('New Smart {0}', [Ox._(folderItem)]), keyboard: 'alt control n' },
|
||||||
{ id: 'newsmartlistfromresults', title: Ox._('New Smart List from Results'), keyboard: 'shift alt control n' },
|
{ id: 'newsmartlistfromresults', title: Ox._('New Smart {0} from Results', [Ox._(folderItem)]), keyboard: 'shift alt control n' },
|
||||||
{},
|
{},
|
||||||
{ id: 'duplicatelist', title: Ox._('Duplicate Selected List'), keyboard: 'control d', disabled: !ui._list },
|
{ id: 'duplicatelist', title: Ox._('Duplicate Selected {0}', [Ox._(folderItem)]), keyboard: 'control d', disabled: !ui._list },
|
||||||
{ id: 'editlist', title: Ox._('Edit Selected List...'), keyboard: 'control e', disabled: !editable },
|
{ id: 'editlist', title: Ox._('Edit Selected {0}...', [Ox._(folderItem)]), keyboard: 'control e', disabled: !editable },
|
||||||
{ id: 'deletelist', title: Ox._('Delete Selected List...'), keyboard: 'delete', disabled: !editable }
|
{ id: 'deletelist', title: Ox._('Delete Selected {0}...', [Ox._(folderItem)]), keyboard: 'delete', disabled: !editable }
|
||||||
],
|
],
|
||||||
title: 'edit',
|
title: 'edit',
|
||||||
tooltip: Ox._('Manage Personal Lists'),
|
tooltip: Ox._('Manage Personal ' + folderItems),
|
||||||
type: 'image'
|
type: 'image'
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
|
@ -64,7 +67,7 @@ pandora.ui.folders = function(section) {
|
||||||
], data.id)) {
|
], data.id)) {
|
||||||
pandora.addList(data.id.indexOf('smart') > -1, data.id.indexOf('from') > -1);
|
pandora.addList(data.id.indexOf('smart') > -1, data.id.indexOf('from') > -1);
|
||||||
} else if (data.id == 'duplicatelist') {
|
} else if (data.id == 'duplicatelist') {
|
||||||
pandora.addList(pandora.user.ui._list);
|
pandora.addList(ui._list);
|
||||||
} else if (data.id == 'editlist') {
|
} else if (data.id == 'editlist') {
|
||||||
pandora.ui.listDialog().open();
|
pandora.ui.listDialog().open();
|
||||||
} else if (data.id == 'deletelist') {
|
} else if (data.id == 'deletelist') {
|
||||||
|
@ -222,9 +225,17 @@ pandora.ui.folders = function(section) {
|
||||||
pandora.$ui.folderList.featured.options({selected: [listData.id]});
|
pandora.$ui.folderList.featured.options({selected: [listData.id]});
|
||||||
} else {
|
} else {
|
||||||
// and nowhere else
|
// and nowhere else
|
||||||
pandora.UI.set({
|
if (section == 'items') {
|
||||||
find: pandora.site.user.ui.find
|
pandora.UI.set({
|
||||||
});
|
find: pandora.site.user.ui.find
|
||||||
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: pandora.site.user.ui.findDocuments
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ox.print('unknown section', section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pandora.$ui.folderBrowser.favorite.replaceWith(
|
pandora.$ui.folderBrowser.favorite.replaceWith(
|
||||||
|
@ -280,9 +291,17 @@ pandora.ui.folders = function(section) {
|
||||||
pandora.$ui.folderList.favorite.options({selected: [listData.id]});
|
pandora.$ui.folderList.favorite.options({selected: [listData.id]});
|
||||||
} else {
|
} else {
|
||||||
// and nowhere else
|
// and nowhere else
|
||||||
pandora.UI.set({
|
if (section == 'items') {
|
||||||
find: pandora.site.user.ui.find
|
pandora.UI.set({
|
||||||
});
|
find: pandora.site.user.ui.find
|
||||||
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: pandora.site.user.ui.findDocuments
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ox.print('unknown section', section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pandora.$ui.folderBrowser.featured.replaceWith(
|
pandora.$ui.folderBrowser.featured.replaceWith(
|
||||||
|
@ -336,7 +355,7 @@ pandora.ui.folders = function(section) {
|
||||||
},
|
},
|
||||||
toggle: function(data) {
|
toggle: function(data) {
|
||||||
data.collapsed && pandora.$ui.folderList[folder.id].loseFocus();
|
data.collapsed && pandora.$ui.folderList[folder.id].loseFocus();
|
||||||
pandora.UI.set('showFolder.items.' + folder.id, !data.collapsed);
|
pandora.UI.set('showFolder.' + section + '.' + folder.id, !data.collapsed);
|
||||||
pandora.resizeFolders();
|
pandora.resizeFolders();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -378,7 +397,7 @@ pandora.ui.folders = function(section) {
|
||||||
}).bindEvent({
|
}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
var $dialog = pandora.ui.iconDialog({
|
var $dialog = pandora.ui.iconDialog({
|
||||||
buttons: title != Ox._('Featured Lists') ? [
|
buttons: title != Ox._('Featured ' + folderItems) ? [
|
||||||
Ox.Button({title: Ox._('Sign Up...')}).bindEvent({
|
Ox.Button({title: Ox._('Sign Up...')}).bindEvent({
|
||||||
click: function() {
|
click: function() {
|
||||||
$dialog.close();
|
$dialog.close();
|
||||||
|
@ -441,6 +460,17 @@ pandora.ui.folders = function(section) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
|
pandora_finddocuments: function() {
|
||||||
|
var folder = pandora.getListData().folder,
|
||||||
|
list = pandora.user.ui._collection,
|
||||||
|
previousList = pandora.UI.getPrevious()._collection;
|
||||||
|
if (list != previousList) {
|
||||||
|
Ox.forEach(pandora.$ui.folderList, function($list, id) {
|
||||||
|
id != folder && $list.options('selected', []);
|
||||||
|
});
|
||||||
|
folder && pandora.$ui.folderList[folder].options({selected: [list]});
|
||||||
|
}
|
||||||
|
},
|
||||||
pandora_text: function() {
|
pandora_text: function() {
|
||||||
if (!pandora.user.ui.text) {
|
if (!pandora.user.ui.text) {
|
||||||
Ox.forEach(pandora.$ui.folderList, function($list, id) {
|
Ox.forEach(pandora.$ui.folderList, function($list, id) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
pandora.ui.info = function() {
|
pandora.ui.info = function() {
|
||||||
|
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
folderItems = pandora.getFolderItems(ui.section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
view = getView(),
|
view = getView(),
|
||||||
|
|
||||||
|
@ -13,6 +13,11 @@ pandora.ui.info = function() {
|
||||||
toggle: function(data) {
|
toggle: function(data) {
|
||||||
pandora.UI.set({showInfo: !data.collapsed});
|
pandora.UI.set({showInfo: !data.collapsed});
|
||||||
},
|
},
|
||||||
|
pandora_documentlist: function() {
|
||||||
|
if (pandora.user.ui._collection != pandora.UI.getPrevious('_collection')) {
|
||||||
|
updateInfo();
|
||||||
|
}
|
||||||
|
},
|
||||||
pandora_edit: updateInfo,
|
pandora_edit: updateInfo,
|
||||||
pandora_find: function() {
|
pandora_find: function() {
|
||||||
if (pandora.user.ui._list != pandora.UI.getPrevious('_list')) {
|
if (pandora.user.ui._list != pandora.UI.getPrevious('_list')) {
|
||||||
|
@ -29,6 +34,8 @@ pandora.ui.info = function() {
|
||||||
updateInfo();
|
updateInfo();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
pandora_document: updateInfo,
|
||||||
|
pandora_collectionselection: updateInfo,
|
||||||
pandora_text: updateInfo
|
pandora_text: updateInfo
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -184,16 +191,14 @@ pandora.ui.info = function() {
|
||||||
|
|
||||||
pandora.ui.listInfo = function() {
|
pandora.ui.listInfo = function() {
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
folderItems = pandora.getFolderItems(ui.section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
list = pandora.user.ui.section == 'items' ? pandora.user.ui._list : ui[folderItem.toLowerCase()],
|
list = pandora.user.ui.section == 'items' ? pandora.user.ui._list : ui[folderItem.toLowerCase()],
|
||||||
canEditFeaturedLists = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
|
canEditFeaturedLists = pandora.site.capabilities['canEditFeatured' + folderItems][pandora.user.level],
|
||||||
that = Ox.Element().css({padding: '16px', textAlign: 'center'}),
|
that = Ox.Element().css({padding: '16px', textAlign: 'center'}),
|
||||||
$icon = Ox.Element('<img>')
|
$icon = Ox.Element('<img>')
|
||||||
.attr({
|
.attr({
|
||||||
src: list
|
src: list ? pandora.getListIcon(ui.section, list, 256) : '/static/png/icon.png'
|
||||||
? '/' + folderItem.toLowerCase() + '/' + encodeURIComponent(list) + '/icon256.jpg?' + Ox.uid()
|
|
||||||
: '/static/png/icon.png'
|
|
||||||
})
|
})
|
||||||
.css(getIconCSS())
|
.css(getIconCSS())
|
||||||
.appendTo(that),
|
.appendTo(that),
|
||||||
|
|
|
@ -22,7 +22,7 @@ pandora.ui.item = function() {
|
||||||
if (result.status.code == 200) {
|
if (result.status.code == 200) {
|
||||||
// we want to cache the title in any way, so that after closing
|
// we want to cache the title in any way, so that after closing
|
||||||
// a dialog and getting to this item, the title is correct
|
// a dialog and getting to this item, the title is correct
|
||||||
var documentTitle = pandora.getDocumentTitle(result.data);
|
var documentTitle = pandora.getWindowTitle(result.data);
|
||||||
document.title = pandora.getPageTitle(document.location.pathname) || documentTitle;
|
document.title = pandora.getPageTitle(document.location.pathname) || documentTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ pandora.ui.listDialog = function(section) {
|
||||||
),
|
),
|
||||||
ui = pandora.user.ui,
|
ui = pandora.user.ui,
|
||||||
width = getWidth(section),
|
width = getWidth(section),
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
folderItems = pandora.getFolderItems(pandora.user.ui.section),
|
||||||
folderItem = folderItems.slice(0, -1);
|
folderItem = folderItems.slice(0, -1);
|
||||||
Ox.getObjectById(tabs, section).selected = true;
|
Ox.getObjectById(tabs, section).selected = true;
|
||||||
|
|
||||||
|
@ -26,7 +26,10 @@ pandora.ui.listDialog = function(section) {
|
||||||
} else if (id == 'icon') {
|
} else if (id == 'icon') {
|
||||||
return pandora.$ui.listIconPanel = pandora.ui.listIconPanel(listData);
|
return pandora.$ui.listIconPanel = pandora.ui.listIconPanel(listData);
|
||||||
} else if (id == 'query') {
|
} else if (id == 'query') {
|
||||||
return pandora.$ui.filterForm = pandora.ui.filterForm({
|
return pandora.$ui.filterForm = (pandora.user.ui.section == 'documents'
|
||||||
|
? pandora.ui.documentFilterForm
|
||||||
|
: pandora.ui.filterForm
|
||||||
|
)({
|
||||||
mode: 'list',
|
mode: 'list',
|
||||||
list: listData
|
list: listData
|
||||||
})
|
})
|
||||||
|
@ -158,7 +161,7 @@ pandora.ui.listDialog = function(section) {
|
||||||
pandora.ui.listGeneralPanel = function(listData) {
|
pandora.ui.listGeneralPanel = function(listData) {
|
||||||
var that = Ox.Element(),
|
var that = Ox.Element(),
|
||||||
ui = pandora.user.ui,
|
ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
folderItems = pandora.getFolderItems(ui.section),
|
||||||
folderItem = folderItems.slice(0, -1);
|
folderItem = folderItems.slice(0, -1);
|
||||||
pandora.api['find' + folderItems]({
|
pandora.api['find' + folderItems]({
|
||||||
query: {conditions: [{key: 'id', value: listData.id, operator: '=='}]},
|
query: {conditions: [{key: 'id', value: listData.id, operator: '=='}]},
|
||||||
|
@ -171,7 +174,7 @@ pandora.ui.listGeneralPanel = function(listData) {
|
||||||
tooltip: Ox._('Doubleclick to edit icon')
|
tooltip: Ox._('Doubleclick to edit icon')
|
||||||
})
|
})
|
||||||
.attr({
|
.attr({
|
||||||
src: pandora.getMediaURL('/' + folderItem.toLowerCase() + '/' + encodeURIComponent(listData.id) + '/icon256.jpg?' + Ox.uid())
|
src: pandora.getListIcon(ui.section, listData.id, 256)
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -382,13 +385,16 @@ pandora.ui.listIconPanel = function(listData) {
|
||||||
quarters = ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
quarters = ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
||||||
|
|
||||||
ui = pandora.user.ui,
|
ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
folderItems = pandora.getFolderItems(ui.section),
|
||||||
folderItem = folderItems.slice(0, -1),
|
folderItem = folderItems.slice(0, -1),
|
||||||
|
|
||||||
|
|
||||||
$iconPanel = Ox.Element(),
|
$iconPanel = Ox.Element(),
|
||||||
|
|
||||||
$icon = $('<img>')
|
$icon = $('<img>')
|
||||||
.attr({src: pandora.getMediaURL('/' + folderItem.toLowerCase() + '/' + encodeURIComponent(listData.id) + '/icon256.jpg?' + Ox.uid())})
|
.attr({
|
||||||
|
src: pandora.getListIcon(ui.section, listData.id, 256)
|
||||||
|
})
|
||||||
.css({position: 'absolute', borderRadius: '64px', margin: '16px'})
|
.css({position: 'absolute', borderRadius: '64px', margin: '16px'})
|
||||||
.appendTo($iconPanel),
|
.appendTo($iconPanel),
|
||||||
|
|
||||||
|
@ -399,8 +405,6 @@ pandora.ui.listIconPanel = function(listData) {
|
||||||
$list = Ox.Element(),
|
$list = Ox.Element(),
|
||||||
|
|
||||||
ui = pandora.user.ui,
|
ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items' ? 'Lists' : Ox.toTitleCase(ui.section),
|
|
||||||
folderItem = folderItems.slice(0, -1),
|
|
||||||
|
|
||||||
that = Ox.SplitPanel({
|
that = Ox.SplitPanel({
|
||||||
elements: [
|
elements: [
|
||||||
|
@ -586,9 +590,7 @@ pandora.ui.listIconPanel = function(listData) {
|
||||||
posterFrames: posterFrames
|
posterFrames: posterFrames
|
||||||
}, function() {
|
}, function() {
|
||||||
$icon.attr({
|
$icon.attr({
|
||||||
src: pandora.getMediaURL('/' + folderItem.toLowerCase()
|
src: pandora.getListIcon(ui.section, listData.id, 256)
|
||||||
+ '/' + encodeURIComponent(listData.id) + '/icon256.jpg?' + Ox.uid()
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
pandora.$ui.folderList[listData.folder].$element
|
pandora.$ui.folderList[listData.folder].$element
|
||||||
.find('img[src*="'
|
.find('img[src*="'
|
||||||
|
@ -596,10 +598,7 @@ pandora.ui.listIconPanel = function(listData) {
|
||||||
+ '/"]'
|
+ '/"]'
|
||||||
)
|
)
|
||||||
.attr({
|
.attr({
|
||||||
src: pandora.getMediaURL('/' + folderItem.toLowerCase()
|
src: pandora.getListIcon(ui.section, listData.id, 256)
|
||||||
+ '/' + encodeURIComponent(listData.id)
|
|
||||||
+ '/icon.jpg?' + Ox.uid()
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
pandora.$ui.info.updateListInfo();
|
pandora.$ui.info.updateListInfo();
|
||||||
});
|
});
|
||||||
|
@ -619,7 +618,7 @@ pandora.ui.listIconPanel = function(listData) {
|
||||||
pandora.api.find(Ox.extend(data, {
|
pandora.api.find(Ox.extend(data, {
|
||||||
query: {
|
query: {
|
||||||
conditions: (
|
conditions: (
|
||||||
ui.section == 'items'
|
Ox.contains(pandora.site.listSections, ui.section)
|
||||||
? [{key: 'list', value: listData.id, operator: '=='}]
|
? [{key: 'list', value: listData.id, operator: '=='}]
|
||||||
: []).concat(
|
: []).concat(
|
||||||
value !== ''
|
value !== ''
|
||||||
|
|
|
@ -135,22 +135,22 @@ pandora.ui.mainMenu = function() {
|
||||||
}) }
|
}) }
|
||||||
] },
|
] },
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
id: 'showsidebar',
|
id: 'showsidebar',
|
||||||
title: Ox._((ui.showSidebar ? 'Hide' : 'Show') + ' Sidebar'),
|
title: Ox._((ui.showSidebar ? 'Hide' : 'Show') + ' Sidebar'),
|
||||||
keyboard: 'shift s'
|
keyboard: 'shift s'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'showinfo',
|
id: 'showinfo',
|
||||||
title: Ox._((ui.showInfo ? 'Hide' : 'Show') + ' Info'),
|
title: Ox._((ui.showInfo ? 'Hide' : 'Show') + ' Info'),
|
||||||
disabled: !ui.showSidebar, keyboard: 'shift i'
|
disabled: !ui.showSidebar, keyboard: 'shift i'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'showfilters',
|
id: 'showfilters',
|
||||||
title: Ox._((ui.showFilters ? 'Hide' : 'Show') + ' Filters'),
|
title: Ox._((ui.showFilters ? 'Hide' : 'Show') + ' Filters'),
|
||||||
disabled: ui.section != 'items' || !!ui.item, keyboard: 'shift f'
|
disabled: ui.section != 'items' || !!ui.item, keyboard: 'shift f'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'showbrowser',
|
id: 'showbrowser',
|
||||||
title: Ox._((ui.showBrowser ? 'Hide': 'Show') + ' {0} Browser', [Ox._(pandora.site.itemName.singular)]),
|
title: Ox._((ui.showBrowser ? 'Hide': 'Show') + ' {0} Browser', [Ox._(pandora.site.itemName.singular)]),
|
||||||
disabled: !ui.item, keyboard: 'shift b'
|
disabled: !ui.item, keyboard: 'shift b'
|
||||||
|
@ -165,18 +165,18 @@ pandora.ui.mainMenu = function() {
|
||||||
title: Ox._((ui.showTimeline ? 'Hide' : 'Show') + ' Timeline'),
|
title: Ox._((ui.showTimeline ? 'Hide' : 'Show') + ' Timeline'),
|
||||||
disabled: !hasTimeline(), keyboard: 'shift t'
|
disabled: !hasTimeline(), keyboard: 'shift t'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'showannotations',
|
id: 'showannotations',
|
||||||
title: Ox._((ui.showAnnotations ? 'Hide' : 'Show') + ' Annotations'),
|
title: Ox._((ui.showAnnotations ? 'Hide' : 'Show') + ' Annotations'),
|
||||||
disabled: !hasAnnotations(), keyboard: 'shift a'
|
disabled: !hasAnnotations(), keyboard: 'shift a'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'showclips',
|
id: 'showclips',
|
||||||
title: Ox._((ui.showClips ? 'Hide' : 'Show') + ' Clips'),
|
title: Ox._((ui.showClips ? 'Hide' : 'Show') + ' Clips'),
|
||||||
disabled: !hasClips(), keyboard: 'shift c'
|
disabled: !hasClips(), keyboard: 'shift c'
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
id: 'togglefullscreen',
|
id: 'togglefullscreen',
|
||||||
title: Ox._((fullscreenState ? 'Exit' : 'Enter') + ' Fullscreen'),
|
title: Ox._((fullscreenState ? 'Exit' : 'Enter') + ' Fullscreen'),
|
||||||
disabled: fullscreenState === void 0,
|
disabled: fullscreenState === void 0,
|
||||||
|
@ -185,7 +185,7 @@ pandora.ui.mainMenu = function() {
|
||||||
: 'F11'
|
: 'F11'
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'entervideofullscreen',
|
id: 'entervideofullscreen',
|
||||||
title: Ox._('Enter Video Fullscreen'),
|
title: Ox._('Enter Video Fullscreen'),
|
||||||
disabled: !ui.item || ui.itemView != 'player'
|
disabled: !ui.item || ui.itemView != 'player'
|
||||||
|
@ -253,6 +253,14 @@ pandora.ui.mainMenu = function() {
|
||||||
} else {
|
} else {
|
||||||
that.checkItem('allitems');
|
that.checkItem('allitems');
|
||||||
}
|
}
|
||||||
|
} else if (ui.section == 'documents') {
|
||||||
|
if (data.checked) {
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: {conditions: [], operator: '&'}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
that.checkItem('allitems');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set(ui.section.slice(0, -1), '');
|
pandora.UI.set(ui.section.slice(0, -1), '');
|
||||||
}
|
}
|
||||||
|
@ -268,6 +276,10 @@ pandora.ui.mainMenu = function() {
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set({itemSort: [{key: value, operator: pandora.getSortOperator(value)}]});
|
pandora.UI.set({itemSort: [{key: value, operator: pandora.getSortOperator(value)}]});
|
||||||
}
|
}
|
||||||
|
} else if (data.id == 'documentorder') {
|
||||||
|
pandora.UI.set({collectionSort: [{key: ui.collectionSort[0].key, operator: value == 'ascending' ? '+' : '-'}]});
|
||||||
|
} else if (data.id == 'documentsort') {
|
||||||
|
pandora.UI.set({collectionSort: [{key: value, operator: pandora.getDocumentSortOperator(value)}]});
|
||||||
} else if (data.id == 'find') {
|
} else if (data.id == 'find') {
|
||||||
if (value) {
|
if (value) {
|
||||||
pandora.$ui.findSelect.value(value);
|
pandora.$ui.findSelect.value(value);
|
||||||
|
@ -351,6 +363,15 @@ pandora.ui.mainMenu = function() {
|
||||||
operator: '&'
|
operator: '&'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (ui.section == 'documents') {
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: {
|
||||||
|
conditions: data.checked ? [
|
||||||
|
{key: 'collection', value: data.id.slice(8).replace(/\t/g, '_'), operator: '=='}
|
||||||
|
] : [],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
pandora.UI.set(ui.section.slice(0, -1), data.id.slice(8).replace(/\t/g, '_'));
|
pandora.UI.set(ui.section.slice(0, -1), data.id.slice(8).replace(/\t/g, '_'));
|
||||||
}
|
}
|
||||||
|
@ -389,7 +410,11 @@ pandora.ui.mainMenu = function() {
|
||||||
} else if (data.id == 'editlist') {
|
} else if (data.id == 'editlist') {
|
||||||
pandora.ui.listDialog().open();
|
pandora.ui.listDialog().open();
|
||||||
} else if (data.id == 'add') {
|
} else if (data.id == 'add') {
|
||||||
pandora.$ui.addItemDialog = pandora.ui.addItemDialog().open();
|
if (ui.section == 'documents') {
|
||||||
|
pandora.$ui.addDocumentDialog = pandora.ui.addDocumentDialog().open();
|
||||||
|
} else {
|
||||||
|
pandora.$ui.addItemDialog = pandora.ui.addItemDialog().open();
|
||||||
|
}
|
||||||
} else if (data.id == 'edit') {
|
} else if (data.id == 'edit') {
|
||||||
pandora.ui.editItemDialog().open();
|
pandora.ui.editItemDialog().open();
|
||||||
} else if (data.id == 'deletelist') {
|
} else if (data.id == 'deletelist') {
|
||||||
|
@ -454,6 +479,13 @@ pandora.ui.mainMenu = function() {
|
||||||
pandora.UI.set({listSelection: items});
|
pandora.UI.set({listSelection: items});
|
||||||
pandora.reloadList();
|
pandora.reloadList();
|
||||||
});
|
});
|
||||||
|
} else if (ui.section == 'documents') {
|
||||||
|
var items = pandora.clipboard.paste('document');
|
||||||
|
items.length && pandora.doHistory('paste', items, ui._collection, function() {
|
||||||
|
//fixme:
|
||||||
|
//pandora.UI.set({listSelection: items});
|
||||||
|
//pandora.reloadList();
|
||||||
|
});
|
||||||
} else if (ui.section == 'edits') {
|
} else if (ui.section == 'edits') {
|
||||||
var clips = pandora.clipboard.paste('clip');
|
var clips = pandora.clipboard.paste('clip');
|
||||||
clips.length && pandora.doHistory('paste', clips, ui.edit, function(result) {
|
clips.length && pandora.doHistory('paste', clips, ui.edit, function(result) {
|
||||||
|
@ -489,6 +521,26 @@ pandora.ui.mainMenu = function() {
|
||||||
}).open();
|
}).open();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (ui.section == 'documents') {
|
||||||
|
var files;
|
||||||
|
if (ui.document) {
|
||||||
|
files = [pandora.$ui.document.info()];
|
||||||
|
} else {
|
||||||
|
files = pandora.$ui.list.options('selected').map(function(id) {
|
||||||
|
return pandora.$ui.list.value(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pandora.ui.deleteDocumentDialog(
|
||||||
|
files,
|
||||||
|
function() {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
if (ui.document) {
|
||||||
|
pandora.UI.set({document: ''});
|
||||||
|
} else {
|
||||||
|
pandora.$ui.list.reloadList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).open();
|
||||||
} else if (ui.section == 'edits') {
|
} else if (ui.section == 'edits') {
|
||||||
var clips = pandora.$ui.editPanel.getSelectedClips();
|
var clips = pandora.$ui.editPanel.getSelectedClips();
|
||||||
pandora.doHistory('delete', clips, ui.edit, function(result) {
|
pandora.doHistory('delete', clips, ui.edit, function(result) {
|
||||||
|
@ -594,6 +646,30 @@ pandora.ui.mainMenu = function() {
|
||||||
pandora.$ui.errorlogsDialog = pandora.ui.errorlogsDialog().open();
|
pandora.$ui.errorlogsDialog = pandora.ui.errorlogsDialog().open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
pandora_collectionsort: function(data) {
|
||||||
|
that.checkItem('sortMenu_sortitems_' + data.value[0].key);
|
||||||
|
that.checkItem('sortMenu_orderitems_' + (
|
||||||
|
data.value[0].operator == '+' ? 'ascending' : 'descending')
|
||||||
|
);
|
||||||
|
},
|
||||||
|
pandora_finddocuments: function() {
|
||||||
|
var action = pandora.getListData().editable ? 'enableItem' : 'disableItem',
|
||||||
|
list = ui._collection,
|
||||||
|
previousList = pandora.UI.getPrevious()._collection;
|
||||||
|
if (list != previousList) {
|
||||||
|
that.uncheckItem(previousList == '' ? 'allitems' : 'viewlist' + previousList.replace(/_/g, Ox.char(9)));
|
||||||
|
that.checkItem(list == '' ? 'allitems' : 'viewlist' + list.replace(/_/g, '\t'));
|
||||||
|
}
|
||||||
|
that[ui._list ? 'enableItem' : 'disableItem']('duplicatelist');
|
||||||
|
that[action]('editlist');
|
||||||
|
that[action]('deletelist');
|
||||||
|
that[ui.listSelection.length ? 'enableItem' : 'disableItem']('newlistfromselection');
|
||||||
|
that.replaceMenu('itemMenu', getItemMenu());
|
||||||
|
that[ui.find.conditions.length ? 'enableItem' : 'disableItem']('clearquery');
|
||||||
|
that[Ox.sum(ui._filterState.map(function(filterState) {
|
||||||
|
return filterState.selected.length;
|
||||||
|
})) > 0 ? 'enableItem' : 'disableItem']('clearfilters');
|
||||||
|
},
|
||||||
pandora_edit: function() {
|
pandora_edit: function() {
|
||||||
var action = pandora.getListData().editable ? 'enableItem' : 'disableItem',
|
var action = pandora.getListData().editable ? 'enableItem' : 'disableItem',
|
||||||
edit = ui.edit,
|
edit = ui.edit,
|
||||||
|
@ -690,6 +766,14 @@ pandora.ui.mainMenu = function() {
|
||||||
pandora.getItemIdAndPosition() ? 'enableItem' : 'disableItem'
|
pandora.getItemIdAndPosition() ? 'enableItem' : 'disableItem'
|
||||||
]('findsimilar');
|
]('findsimilar');
|
||||||
},
|
},
|
||||||
|
pandora_collectionselection: function(data) {
|
||||||
|
var action = data.value.length ? 'enableItem' : 'disableItem';
|
||||||
|
that[action]('newlistfromselection');
|
||||||
|
that.replaceMenu('itemMenu', getItemMenu());
|
||||||
|
that[
|
||||||
|
pandora.getItemIdAndPosition() ? 'enableItem' : 'disableItem'
|
||||||
|
]('findsimilar');
|
||||||
|
},
|
||||||
pandora_listselection: function(data) {
|
pandora_listselection: function(data) {
|
||||||
var action = data.value.length ? 'enableItem' : 'disableItem';
|
var action = data.value.length ? 'enableItem' : 'disableItem';
|
||||||
that[action]('newlistfromselection');
|
that[action]('newlistfromselection');
|
||||||
|
@ -983,6 +1067,156 @@ pandora.ui.mainMenu = function() {
|
||||||
elements[Ox.mod((index + direction), elements.length)].gainFocus();
|
elements[Ox.mod((index + direction), elements.length)].gainFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDocumentMenu() {
|
||||||
|
var listData = pandora.getListData(),
|
||||||
|
deleteVerb = ui._collection ? Ox._('Remove') : Ox._('Delete'),
|
||||||
|
isEditable = listData.editable && listData.type == 'static',
|
||||||
|
isListView = !ui.document,
|
||||||
|
listName = ui._collection ? Ox._('from List') : Ox._('from Archive'),
|
||||||
|
listItemsName = 'Documents',
|
||||||
|
selectionItems = ui.collectionSelection.length,
|
||||||
|
selectionItemName = (
|
||||||
|
selectionItems > 1 ? Ox.formatNumber(selectionItems) + ' ' : ''
|
||||||
|
) + Ox._(selectionItems == 1 ? 'Document' : 'Documents'),
|
||||||
|
clipboardItems = pandora.clipboard.items('document'),
|
||||||
|
clipboardItemName = clipboardItems == 0 ? ''
|
||||||
|
: (
|
||||||
|
clipboardItems > 1 ? Ox.formatNumber(clipboardItems) + ' ' : ''
|
||||||
|
) + Ox._(clipboardItems == 1 ? 'Document' : 'Documents'),
|
||||||
|
canEdit = false, //fixme
|
||||||
|
canDelete = (
|
||||||
|
ui.document || ui.collectionSelection.length
|
||||||
|
) && (
|
||||||
|
pandora.site.capabilities.canRemoveDocuments[pandora.user.level] ||
|
||||||
|
ui.collectionSelection.every(function(item) {
|
||||||
|
return pandora.$ui.list.value(item, 'editable');
|
||||||
|
})
|
||||||
|
),
|
||||||
|
canSelect = isListView,
|
||||||
|
canCopy = ui.collectionSelection.length,
|
||||||
|
canCut = canCopy && isEditable,
|
||||||
|
canPaste = isListView && isEditable,
|
||||||
|
canAdd = canCopy && clipboardItems > 0,
|
||||||
|
historyItems = pandora.history.items(),
|
||||||
|
undoText = pandora.history.undoText(),
|
||||||
|
redoText = pandora.history.redoText();
|
||||||
|
return { id: 'itemMenu', title: Ox._('Item'), items: [
|
||||||
|
{ id: 'add', title: Ox._('Add {0}...', [Ox._('Document')]), disabled: !pandora.site.capabilities.canAddItems[pandora.user.level] },
|
||||||
|
{ id: 'edit', title: Ox._('Edit {0}...', [Ox._('Document')]), disabled: true /*fixme: !canEdit */ },
|
||||||
|
{},
|
||||||
|
{ id: 'selectall', title: Ox._('Select All {0}', [listItemsName]), disabled: !canSelect, keyboard: 'control a' },
|
||||||
|
{ id: 'selectnone', title: Ox._('Select None'), disabled: !canSelect, keyboard: 'shift control a' },
|
||||||
|
{ id: 'invertselection', title: Ox._('Invert Selection'), disabled: !canSelect, keyboard: 'alt control a' },
|
||||||
|
{},
|
||||||
|
{ id: 'cut', title: Ox._('Cut {0}', [selectionItemName]), disabled: !canCut, keyboard: 'control x' },
|
||||||
|
{ id: 'cutadd', title: Ox._('Cut and Add to Clipboard'), disabled: !canCut || !canAdd, keyboard: 'shift control x' },
|
||||||
|
{ id: 'copy', title: Ox._('Copy {0}', [selectionItemName]), disabled: !canCopy, keyboard: 'control c' },
|
||||||
|
{ id: 'copyadd', title: Ox._('Copy and Add to Clipboard'), disabled: !canCopy || !canAdd, keyboard: 'shift control c' },
|
||||||
|
{ id: 'paste', title: clipboardItems == 0 ? Ox._('Paste') : Ox._('Paste {0}', [clipboardItemName]), disabled: !canPaste, keyboard: 'control v' },
|
||||||
|
{ id: 'clearclipboard', title: Ox._('Clear Clipboard'), disabled: !clipboardItems},
|
||||||
|
{},
|
||||||
|
{ id: 'delete', title: Ox._('{0} {1} {2}', [deleteVerb, selectionItemName, listName]), disabled: !canDelete, keyboard: 'delete' },
|
||||||
|
{},
|
||||||
|
{ id: 'undo', title: undoText ? Ox._('Undo {0}', [undoText]) : Ox._('Undo'), disabled: !undoText, keyboard: 'control z' },
|
||||||
|
{ id: 'redo', title: redoText ? Ox._('Redo {0}', [redoText]) : Ox._('Redo'), disabled: !redoText, keyboard: 'shift control z' },
|
||||||
|
{ id: 'clearhistory', title: Ox._('Clear History'), disabled: !historyItems }
|
||||||
|
] };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollectionMenu() {
|
||||||
|
var itemNamePlural = pandora.getFolderItems(ui.section),
|
||||||
|
itemNameSingular = itemNamePlural.slice(0, -1),
|
||||||
|
disableEdit = isGuest || !ui._collection,
|
||||||
|
disableFromSelection = isGuest || ui.collectionSelection.length == 0;
|
||||||
|
|
||||||
|
return { id: 'listMenu', title: Ox._(itemNameSingular == 'Collection' ? 'File' : itemNameSingular), items: [].concat(
|
||||||
|
{
|
||||||
|
id: 'allitems',
|
||||||
|
title: pandora.getAllItemsTitle(),
|
||||||
|
checked: !ui._collection,
|
||||||
|
keyboard: 'shift control w'
|
||||||
|
},
|
||||||
|
['personal', 'favorite', 'featured'].map(function(folder) {
|
||||||
|
return {
|
||||||
|
id: folder + 'lists',
|
||||||
|
title: Ox._(Ox.toTitleCase(folder) + ' ' + itemNamePlural),
|
||||||
|
items: Ox.isUndefined(lists[folder])
|
||||||
|
? [{id: 'loading', title: Ox._('Loading...'), disabled: true}]
|
||||||
|
: lists[folder].length == 0
|
||||||
|
? [{id: 'nolists', title: Ox._('No {0} {1}',
|
||||||
|
[Ox._(Ox.toTitleCase(folder)), Ox._(itemNamePlural)]), disabled: true}]
|
||||||
|
: lists[folder].map(function(list) {
|
||||||
|
return {
|
||||||
|
id: 'viewlist' + list.id.replace(/_/g, Ox.char(9)),
|
||||||
|
title: Ox.encodeHTMLEntities((
|
||||||
|
folder == 'favorite' ? list.user + ': ' : ''
|
||||||
|
) + list.name),
|
||||||
|
checked: list.id == ui._collection
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
{},
|
||||||
|
{ id: 'newlist', title: Ox._('New ' + itemNameSingular), disabled: isGuest, keyboard: 'control n' },
|
||||||
|
{ id: 'newlistfromselection', title: Ox._('New ' + itemNameSingular + ' from Selection'), disabled: disableFromSelection, keyboard: 'shift control n' },
|
||||||
|
{ id: 'newsmartlist', title: Ox._('New Smart ' + itemNameSingular), disabled: isGuest, keyboard: 'alt control n' },
|
||||||
|
{ id: 'newsmartlistfromresults', title: Ox._('New Smart ' + itemNameSingular + ' from Results'), disabled: isGuest, keyboard: 'shift alt control n' },
|
||||||
|
{},
|
||||||
|
{ id: 'duplicatelist', title: Ox._('Duplicate Selected ' + itemNameSingular), disabled: disableEdit, keyboard: 'control d' },
|
||||||
|
{ id: 'editlist', title: Ox._('Edit Selected ' + itemNameSingular + '...'), disabled: disableEdit, keyboard: 'control e' },
|
||||||
|
{ id: 'deletelist', title: Ox._('Delete Selected ' + itemNameSingular + '...'), disabled: disableEdit, keyboard: 'delete' },
|
||||||
|
{},
|
||||||
|
{ id: 'print', title: Ox._('Print'), keyboard: 'control p' }
|
||||||
|
]
|
||||||
|
)};
|
||||||
|
};
|
||||||
|
|
||||||
|
function getEditMenu() {
|
||||||
|
var itemNameSingular = 'Edit',
|
||||||
|
itemNamePlural = 'Edits',
|
||||||
|
disableEdit = isGuest || !ui.edit;
|
||||||
|
return { id: 'listMenu', title: Ox._(itemNameSingular), items: [].concat(
|
||||||
|
{
|
||||||
|
id: 'allitems',
|
||||||
|
title: pandora.getAllItemsTitle(),
|
||||||
|
checked: !ui.edit,
|
||||||
|
keyboard: 'shift control w'
|
||||||
|
},
|
||||||
|
['personal', 'favorite', 'featured'].map(function(folder) {
|
||||||
|
return {
|
||||||
|
id: folder + 'lists',
|
||||||
|
title: Ox._(Ox.toTitleCase(folder) + ' ' + itemNamePlural),
|
||||||
|
items: Ox.isUndefined(lists[folder])
|
||||||
|
? [{id: 'loading', title: Ox._('Loading...'), disabled: true}]
|
||||||
|
: lists[folder].length == 0
|
||||||
|
? [{id: 'nolists', title: Ox._('No {0} {1}',
|
||||||
|
[Ox._(Ox.toTitleCase(folder)), Ox._(itemNamePlural)]), disabled: true}]
|
||||||
|
: lists[folder].map(function(list) {
|
||||||
|
return {
|
||||||
|
id: 'viewlist' + list.id.replace(/_/g, Ox.char(9)),
|
||||||
|
title: Ox.encodeHTMLEntities((
|
||||||
|
folder == 'favorite' ? list.user + ': ' : ''
|
||||||
|
) + list.name),
|
||||||
|
checked: list.id == ui.edit
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
{},
|
||||||
|
{ id: 'newlist', title: Ox._('New ' + itemNameSingular), disabled: isGuest, keyboard: 'control n' },
|
||||||
|
{ id: 'newlistfromselection', title: Ox._('New ' + itemNameSingular + ' from Selection'), disabled: disableEdit, keyboard: 'shift control n' },
|
||||||
|
{ id: 'newsmartlist', title: Ox._('New Smart ' + itemNameSingular), disabled: isGuest, keyboard: 'alt control n' },
|
||||||
|
{},
|
||||||
|
{ id: 'duplicatelist', title: Ox._('Duplicate Selected ' + itemNameSingular), disabled: disableEdit, keyboard: 'control d' },
|
||||||
|
{ id: 'editlist', title: Ox._('Edit Selected ' + itemNameSingular + '...'), disabled: disableEdit, keyboard: 'control e' },
|
||||||
|
{ id: 'deletelist', title: Ox._('Delete Selected ' + itemNameSingular + '...'), disabled: disableEdit, keyboard: 'delete' }
|
||||||
|
]
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
|
||||||
function getFindMenu() {
|
function getFindMenu() {
|
||||||
return { id: 'findMenu', title: Ox._('Find'), items: [
|
return { id: 'findMenu', title: Ox._('Find'), items: [
|
||||||
{ id: 'find', title: Ox._('Find'), items: [
|
{ id: 'find', title: Ox._('Find'), items: [
|
||||||
|
@ -1005,7 +1239,62 @@ pandora.ui.mainMenu = function() {
|
||||||
] };
|
] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getItemListMenu() {
|
||||||
|
var itemNameSingular = 'List',
|
||||||
|
itemNamePlural = 'Lists',
|
||||||
|
disableEdit = isGuest || !ui._list,
|
||||||
|
disableFromSelection = isGuest || ui.listSelection.length == 0;
|
||||||
|
|
||||||
|
return { id: 'listMenu', title: Ox._(itemNameSingular), items: [].concat(
|
||||||
|
{
|
||||||
|
id: 'allitems',
|
||||||
|
title: pandora.getAllItemsTitle(),
|
||||||
|
checked: !ui._list,
|
||||||
|
keyboard: 'shift control w'
|
||||||
|
},
|
||||||
|
['personal', 'favorite', 'featured'].map(function(folder) {
|
||||||
|
return {
|
||||||
|
id: folder + 'lists',
|
||||||
|
title: Ox._(Ox.toTitleCase(folder) + ' ' + itemNamePlural),
|
||||||
|
items: Ox.isUndefined(lists[folder])
|
||||||
|
? [{id: 'loading', title: Ox._('Loading...'), disabled: true}]
|
||||||
|
: lists[folder].length == 0
|
||||||
|
? [{id: 'nolists', title: Ox._('No {0} {1}',
|
||||||
|
[Ox._(Ox.toTitleCase(folder)), Ox._(itemNamePlural)]), disabled: true}]
|
||||||
|
: lists[folder].map(function(list) {
|
||||||
|
return {
|
||||||
|
id: 'viewlist' + list.id.replace(/_/g, Ox.char(9)),
|
||||||
|
title: Ox.encodeHTMLEntities((
|
||||||
|
folder == 'favorite' ? list.user + ': ' : ''
|
||||||
|
) + list.name),
|
||||||
|
checked: list.id == ui._list
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
{},
|
||||||
|
{ id: 'newlist', title: Ox._('New ' + itemNameSingular), disabled: isGuest, keyboard: 'control n' },
|
||||||
|
{ id: 'newlistfromselection', title: Ox._('New ' + itemNameSingular + ' from Selection'), disabled: disableFromSelection, keyboard: 'shift control n' },
|
||||||
|
{ id: 'newsmartlist', title: Ox._('New Smart ' + itemNameSingular), disabled: isGuest, keyboard: 'alt control n' },
|
||||||
|
{ id: 'newsmartlistfromresults', title: Ox._('New Smart ' + itemNameSingular + ' from Results'), disabled: isGuest, keyboard: 'shift alt control n' },
|
||||||
|
{ id: 'neweditfromselection', title: Ox._('New Edit from Selection'), disabled: disableFromSelection },
|
||||||
|
{ id: 'newsmarteditfromresults', title: Ox._('New Smart Edit from Results'), disabled: isGuest },
|
||||||
|
{},
|
||||||
|
{ id: 'duplicatelist', title: Ox._('Duplicate Selected ' + itemNameSingular), disabled: disableEdit, keyboard: 'control d' },
|
||||||
|
{ id: 'editlist', title: Ox._('Edit Selected ' + itemNameSingular + '...'), disabled: disableEdit, keyboard: 'control e' },
|
||||||
|
{ id: 'deletelist', title: Ox._('Delete Selected ' + itemNameSingular + '...'), disabled: disableEdit, keyboard: 'delete' },
|
||||||
|
{},
|
||||||
|
{ id: 'print', title: Ox._('Print'), keyboard: 'control p' },
|
||||||
|
{ id: 'tv', title: Ox._('TV'), keyboard: 'control space' }
|
||||||
|
]
|
||||||
|
)};
|
||||||
|
};
|
||||||
|
|
||||||
function getItemMenu() {
|
function getItemMenu() {
|
||||||
|
if (ui.section == 'documents') {
|
||||||
|
return getDocumentMenu();
|
||||||
|
}
|
||||||
var listData = pandora.getListData(),
|
var listData = pandora.getListData(),
|
||||||
deleteVerb = ui._list ? Ox._('Remove') : Ox._('Delete'),
|
deleteVerb = ui._list ? Ox._('Remove') : Ox._('Delete'),
|
||||||
isEditable = listData.editable && listData.type == 'static',
|
isEditable = listData.editable && listData.type == 'static',
|
||||||
|
@ -1021,7 +1310,7 @@ pandora.ui.mainMenu = function() {
|
||||||
&& ui.editView != 'annotations', // FIXME: focus
|
&& ui.editView != 'annotations', // FIXME: focus
|
||||||
listName = isVideoView || isClipView ? ''
|
listName = isVideoView || isClipView ? ''
|
||||||
: ui.section == 'items' ? (
|
: ui.section == 'items' ? (
|
||||||
ui._list ? Ox._('from List') : Ox._('from Archive')
|
ui._? Ox._('from List') : Ox._('from Archive')
|
||||||
)
|
)
|
||||||
: Ox._('from Edit'),
|
: Ox._('from Edit'),
|
||||||
listItemsName = Ox._(
|
listItemsName = Ox._(
|
||||||
|
@ -1057,15 +1346,18 @@ pandora.ui.mainMenu = function() {
|
||||||
)
|
)
|
||||||
) && pandora.$ui.list.value(ui.listSelection[0], 'editable')
|
) && pandora.$ui.list.value(ui.listSelection[0], 'editable')
|
||||||
),
|
),
|
||||||
canDelete = pandora.site.capabilities.canRemoveItems[pandora.user.level] || (
|
canDelete = (
|
||||||
ui.section == 'items' && (
|
ui.section == 'items' && (
|
||||||
ui.item || (
|
ui.item || (
|
||||||
Ox.contains(['list', 'grid', 'clips', 'timelines'], ui.listView)
|
Ox.contains(['list', 'grid', 'clips', 'timelines'], ui.listView)
|
||||||
&& ui.listSelection.length
|
&& ui.listSelection.length
|
||||||
)
|
)
|
||||||
) && ui.listSelection.every(function(item) {
|
) && (
|
||||||
return pandora.$ui.list.value(item, 'editable');
|
pandora.site.capabilities.canRemoveItems[pandora.user.level] ||
|
||||||
})
|
ui.listSelection.every(function(item) {
|
||||||
|
return pandora.$ui.list.value(item, 'editable');
|
||||||
|
})
|
||||||
|
)
|
||||||
),
|
),
|
||||||
canSelect = isListView || isClipView || isEditView,
|
canSelect = isListView || isClipView || isEditView,
|
||||||
canCopy = isListView ? ui.listSelection.length
|
canCopy = isListView ? ui.listSelection.length
|
||||||
|
@ -1106,15 +1398,22 @@ pandora.ui.mainMenu = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getListMenu() {
|
function getListMenu() {
|
||||||
var itemNameSingular = ui.section == 'items' ? 'List' : ui.section == 'edits' ? 'Edit' : 'Text',
|
return ({
|
||||||
itemNamePlural = ui.section == 'items' ? 'Lists' : ui.section == 'edits' ? 'Edits' : 'Texts';
|
items: getItemListMenu,
|
||||||
|
documents: getCollectionMenu,
|
||||||
|
edits: getEditMenu,
|
||||||
|
texts: getTextMenu
|
||||||
|
}[ui.section])();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTextMenu() {
|
||||||
|
var itemNameSingular = 'Text',
|
||||||
|
itemNamePlural = 'Texts';
|
||||||
return { id: 'listMenu', title: Ox._(itemNameSingular), items: [].concat(
|
return { id: 'listMenu', title: Ox._(itemNameSingular), items: [].concat(
|
||||||
{
|
{
|
||||||
id: 'allitems',
|
id: 'allitems',
|
||||||
title: pandora.getAllItemsTitle(),
|
title: pandora.getAllItemsTitle(),
|
||||||
checked: ui.section == 'items' ? !ui.item && !ui._list
|
checked: !ui.text,
|
||||||
: ui.section == 'edits' ? !ui.edit
|
|
||||||
: !ui.text,
|
|
||||||
keyboard: 'shift control w'
|
keyboard: 'shift control w'
|
||||||
},
|
},
|
||||||
['personal', 'favorite', 'featured'].map(function(folder) {
|
['personal', 'favorite', 'featured'].map(function(folder) {
|
||||||
|
@ -1132,9 +1431,7 @@ pandora.ui.mainMenu = function() {
|
||||||
title: Ox.encodeHTMLEntities((
|
title: Ox.encodeHTMLEntities((
|
||||||
folder == 'favorite' ? list.user + ': ' : ''
|
folder == 'favorite' ? list.user + ': ' : ''
|
||||||
) + list.name),
|
) + list.name),
|
||||||
checked: ui.section == 'items' ? list.id == ui._list
|
checked: list.id == ui.text
|
||||||
: ui.section == 'edits' ? list.id == ui.edit
|
|
||||||
: list.id == ui.text
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
@ -1142,38 +1439,47 @@ pandora.ui.mainMenu = function() {
|
||||||
[
|
[
|
||||||
{},
|
{},
|
||||||
{ id: 'newlist', title: Ox._('New ' + itemNameSingular), disabled: isGuest, keyboard: 'control n' },
|
{ id: 'newlist', title: Ox._('New ' + itemNameSingular), disabled: isGuest, keyboard: 'control n' },
|
||||||
],
|
|
||||||
ui.section == 'items' ? [
|
|
||||||
{ id: 'newlistfromselection', title: Ox._('New ' + itemNameSingular + ' from Selection'), disabled: isGuest || ui.listSelection.length == 0, keyboard: 'shift control n' },
|
|
||||||
{ id: 'newsmartlist', title: Ox._('New Smart ' + itemNameSingular), disabled: isGuest, keyboard: 'alt control n' },
|
|
||||||
{ id: 'newsmartlistfromresults', title: Ox._('New Smart ' + itemNameSingular + ' from Results'), disabled: isGuest, keyboard: 'shift alt control n' },
|
|
||||||
{ id: 'neweditfromselection', title: Ox._('New Edit from Selection'), disabled: isGuest || ui.listSelection.length == 0 },
|
|
||||||
{ id: 'newsmarteditfromresults', title: Ox._('New Smart Edit from Results'), disabled: isGuest }
|
|
||||||
] : ui.section == 'edits' ? [
|
|
||||||
{ id: 'newlistfromselection', title: Ox._('New ' + itemNameSingular + ' from Selection'), disabled: isGuest || !ui.edit, keyboard: 'shift control n' },
|
|
||||||
{ id: 'newsmartlist', title: Ox._('New Smart ' + itemNameSingular), disabled: isGuest, keyboard: 'alt control n' }
|
|
||||||
] : [
|
|
||||||
{ id: 'newpdf', title: Ox._('New PDF'), disabled: isGuest, keyboard: 'alt control n' },
|
{ id: 'newpdf', title: Ox._('New PDF'), disabled: isGuest, keyboard: 'alt control n' },
|
||||||
],
|
|
||||||
[
|
|
||||||
{}
|
|
||||||
],
|
|
||||||
ui.section != 'texts' ? [
|
|
||||||
{ id: 'duplicatelist', title: Ox._('Duplicate Selected ' + itemNameSingular), disabled: isGuest || (ui.section == 'items' && !ui._list) || (ui.section == 'edits' && !ui.edit), keyboard: 'control d' }
|
|
||||||
] : [],
|
|
||||||
[
|
|
||||||
{ id: 'editlist', title: Ox._('Edit Selected ' + itemNameSingular + '...'), disabled: isGuest || (ui.section == 'items' && !ui._list) || (ui.section == 'edits' && !ui.edit), keyboard: 'control e' },
|
|
||||||
{ id: 'deletelist', title: Ox._('Delete Selected ' + itemNameSingular + '...'), disabled: isGuest || (ui.section == 'items' && !ui._list) || (ui.section == 'edits' && !ui.edit), keyboard: 'delete' }
|
|
||||||
],
|
|
||||||
ui.section == 'items' ? [
|
|
||||||
{},
|
{},
|
||||||
{ id: 'print', title: Ox._('Print'), keyboard: 'control p' },
|
{ id: 'editlist', title: Ox._('Edit Selected ' + itemNameSingular + '...'), disabled: isGuest, keyboard: 'control e' },
|
||||||
{ id: 'tv', title: Ox._('TV'), keyboard: 'control space' }
|
{ id: 'deletelist', title: Ox._('Delete Selected ' + itemNameSingular + '...'), disabled: isGuest, keyboard: 'delete' }
|
||||||
] : []
|
]
|
||||||
)};
|
)};
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function getCollectionSortMenu() {
|
||||||
|
var isClipView = false,
|
||||||
|
clipItems = [].concat(!ui.document ? pandora.site.documentSortKeys.map(function(key) {
|
||||||
|
return Ox.extend({
|
||||||
|
checked: ui.collectionSort[0].key == key.id
|
||||||
|
}, key);
|
||||||
|
}) : []);
|
||||||
|
return { id: 'sortMenu', title: Ox._('Sort'), items: [
|
||||||
|
{ id: 'sortitems', title: Ox._('Sort {0} by', [Ox._('Documents')]), disabled: ui.document, items: [
|
||||||
|
{ group: 'documentsort', min: 1, max: 1, items: pandora.site.documentSortKeys.map(function(key) {
|
||||||
|
return Ox.extend({
|
||||||
|
checked: ui.collectionSort[0].key == key.id
|
||||||
|
}, key, {
|
||||||
|
title: Ox._(key.title)
|
||||||
|
});
|
||||||
|
}) }
|
||||||
|
] },
|
||||||
|
{ id: 'orderitems', title: Ox._('Order {0}', [Ox._('Documents')]), disabled: ui.document, items: [
|
||||||
|
{ group: 'documentorder', min: 1, max: 1, items: [
|
||||||
|
{ id: 'ascending', title: Ox._('Ascending'), checked: (ui.collectionSort[0].operator || pandora.getSortOperator(ui.collectionSort[0].key)) == '+' },
|
||||||
|
{ id: 'descending', title: Ox._('Descending'), checked: (ui.collectionSort[0].operator || pandora.getSortOperator(ui.collectionSort[0].key)) == '-' }
|
||||||
|
]}
|
||||||
|
] },
|
||||||
|
{ id: 'advancedsort', title: Ox._('Advanced Sort...'), keyboard: 'shift control s', disabled: true },
|
||||||
|
] };
|
||||||
|
}
|
||||||
|
|
||||||
function getSortMenu() {
|
function getSortMenu() {
|
||||||
|
|
||||||
|
if (ui.section == 'documents') {
|
||||||
|
return getCollectionSortMenu();
|
||||||
|
}
|
||||||
|
//fixme split items/clips menu
|
||||||
var isClipView = pandora.isClipView(),
|
var isClipView = pandora.isClipView(),
|
||||||
clipItems = (isClipView ? pandora.site.clipKeys.map(function(key) {
|
clipItems = (isClipView ? pandora.site.clipKeys.map(function(key) {
|
||||||
return Ox.extend(Ox.clone(key), {
|
return Ox.extend(Ox.clone(key), {
|
||||||
|
@ -1211,10 +1517,10 @@ pandora.ui.mainMenu = function() {
|
||||||
title: Ox._('Ascending'),
|
title: Ox._('Ascending'),
|
||||||
checked: (ui.listSort[0].operator || pandora.getSortOperator(ui.listSort[0].key)) == '+'
|
checked: (ui.listSort[0].operator || pandora.getSortOperator(ui.listSort[0].key)) == '+'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'descending',
|
id: 'descending',
|
||||||
title: Ox._('Descending'),
|
title: Ox._('Descending'),
|
||||||
checked: (ui.listSort[0].operator || pandora.getSortOperator(ui.listSort[0].key)) == '-'
|
checked: (ui.listSort[0].operator || pandora.getSortOperator(ui.listSort[0].key)) == '-'
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
] },
|
] },
|
||||||
|
|
|
@ -21,6 +21,17 @@ pandora.ui.mainPanel = function() {
|
||||||
orientation: 'horizontal'
|
orientation: 'horizontal'
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
|
pandora_finddocuments: function() {
|
||||||
|
var previousUI = pandora.UI.getPrevious();
|
||||||
|
if (!previousUI.document && ui._list == previousUI._list) {
|
||||||
|
that.replaceElement(1, pandora.$ui.documentPanel = pandora.ui.documentPanel());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pandora_document: function(data) {
|
||||||
|
if (!data.value || !data.previousValue) {
|
||||||
|
that.replaceElement(1, pandora.$ui.documentPanel = pandora.ui.documentPanel());
|
||||||
|
}
|
||||||
|
},
|
||||||
pandora_edit: function(data) {
|
pandora_edit: function(data) {
|
||||||
that.replaceElement(1, pandora.$ui.editPanel = pandora.ui.editPanel());
|
that.replaceElement(1, pandora.$ui.editPanel = pandora.ui.editPanel());
|
||||||
},
|
},
|
||||||
|
@ -92,6 +103,7 @@ pandora.ui.mainPanel = function() {
|
||||||
function getRightPanel() {
|
function getRightPanel() {
|
||||||
return ui.section == 'items' ? pandora.$ui.rightPanel = pandora.ui.rightPanel()
|
return ui.section == 'items' ? pandora.$ui.rightPanel = pandora.ui.rightPanel()
|
||||||
: ui.section == 'edits' ? pandora.$ui.editPanel = pandora.ui.editPanel()
|
: ui.section == 'edits' ? pandora.$ui.editPanel = pandora.ui.editPanel()
|
||||||
|
: ui.section == 'documents' ? pandora.$ui.documentPanel = pandora.ui.documentPanel()
|
||||||
: pandora.$ui.textPanel = pandora.ui.textPanel();
|
: pandora.$ui.textPanel = pandora.ui.textPanel();
|
||||||
}
|
}
|
||||||
return that;
|
return that;
|
||||||
|
|
|
@ -351,7 +351,11 @@ appPanel
|
||||||
findKeys: data.site.itemKeys.filter(function(key) {
|
findKeys: data.site.itemKeys.filter(function(key) {
|
||||||
return key.find;
|
return key.find;
|
||||||
}),
|
}),
|
||||||
|
documentFindKeys: data.site.documentKeys.filter(function(key) {
|
||||||
|
return key.find;
|
||||||
|
}),
|
||||||
itemsSection: pandora.site.itemName.plural.toLowerCase(),
|
itemsSection: pandora.site.itemName.plural.toLowerCase(),
|
||||||
|
listSections: ['items', 'documents'],
|
||||||
map: data.site.layers.some(function(layer) {
|
map: data.site.layers.some(function(layer) {
|
||||||
return layer.type == 'place'
|
return layer.type == 'place'
|
||||||
}) ? 'manual' : data.site.layers.some(function(layer) {
|
}) ? 'manual' : data.site.layers.some(function(layer) {
|
||||||
|
@ -364,6 +368,11 @@ appPanel
|
||||||
{id: 'featured', title: 'Featured Lists', showBrowser: false},
|
{id: 'featured', title: 'Featured Lists', showBrowser: false},
|
||||||
{id: 'volumes', title: 'Local Volumes'}
|
{id: 'volumes', title: 'Local Volumes'}
|
||||||
],
|
],
|
||||||
|
documents: [
|
||||||
|
{id: 'personal', title: 'Personal Collections'},
|
||||||
|
{id: 'favorite', title: 'Favorite Collections', showBrowser: false},
|
||||||
|
{id: 'featured', title: 'Featured Collections', showBrowser: false}
|
||||||
|
],
|
||||||
edits: [
|
edits: [
|
||||||
{id: 'personal', title: 'Personal Edits'},
|
{id: 'personal', title: 'Personal Edits'},
|
||||||
{id: 'favorite', title: 'Favorite Edits', showBrowser: false},
|
{id: 'favorite', title: 'Favorite Edits', showBrowser: false},
|
||||||
|
@ -375,7 +384,12 @@ appPanel
|
||||||
{id: 'featured', title: 'Featured Texts', showBrowser: false}
|
{id: 'featured', title: 'Featured Texts', showBrowser: false}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
sortKeys: pandora.getSortKeys()
|
sortKeys: pandora.getSortKeys(),
|
||||||
|
documentSortKeys: pandora.getDocumentSortKeys(),
|
||||||
|
collectionViews: [
|
||||||
|
{id: 'list', title: Ox._('View as List')},
|
||||||
|
{id: 'grid', title: Ox._('View as Grid')}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
pandora.site.listSettings = {};
|
pandora.site.listSettings = {};
|
||||||
Ox.forEach(pandora.site.user.ui, function(val, key) {
|
Ox.forEach(pandora.site.user.ui, function(val, key) {
|
||||||
|
@ -383,6 +397,12 @@ appPanel
|
||||||
pandora.site.listSettings[key] = key[4].toLowerCase()+ key.slice(5);
|
pandora.site.listSettings[key] = key[4].toLowerCase()+ key.slice(5);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
pandora.site.collectionSettings = {};
|
||||||
|
Ox.forEach(pandora.site.user.ui, function(val, key) {
|
||||||
|
if (/^collection[A-Z]/.test(key)) {
|
||||||
|
pandora.site.collectionSettings[key] = key[10].toLowerCase()+ key.slice(11);
|
||||||
|
}
|
||||||
|
});
|
||||||
pandora.site.editSettings = {
|
pandora.site.editSettings = {
|
||||||
clip: '',
|
clip: '',
|
||||||
'in': 0,
|
'in': 0,
|
||||||
|
|
|
@ -5,7 +5,7 @@ pandora.ui.sectionButtons = function(section) {
|
||||||
buttons: [
|
buttons: [
|
||||||
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
|
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
|
||||||
{id: 'edits', title: Ox._('Edits')},
|
{id: 'edits', title: Ox._('Edits')},
|
||||||
{id: 'texts', title: Ox._('Texts')}
|
{id: 'documents', title: Ox._('Documents')}
|
||||||
],
|
],
|
||||||
id: 'sectionButtons',
|
id: 'sectionButtons',
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
|
|
@ -7,7 +7,7 @@ pandora.ui.sectionSelect = function(section) {
|
||||||
items: [
|
items: [
|
||||||
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
|
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
|
||||||
{id: 'edits', title: Ox._('Edits')},
|
{id: 'edits', title: Ox._('Edits')},
|
||||||
{id: 'texts', title: Ox._('Texts')}
|
{id: 'documents', title: Ox._('Documents')}
|
||||||
],
|
],
|
||||||
value: section || pandora.user.ui.section
|
value: section || pandora.user.ui.section
|
||||||
}).css({
|
}).css({
|
||||||
|
|
|
@ -11,6 +11,7 @@ pandora.ui.textPanel = function() {
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
}),
|
}),
|
||||||
embedURLs,
|
embedURLs,
|
||||||
|
scrolling = false,
|
||||||
selected = -1,
|
selected = -1,
|
||||||
selectedURL;
|
selectedURL;
|
||||||
|
|
||||||
|
@ -233,12 +234,23 @@ pandora.ui.textHTML = function(text) {
|
||||||
scroll: function(event) {
|
scroll: function(event) {
|
||||||
var position = Math.round(100 * that[0]. scrollTop / Math.max(1,
|
var position = Math.round(100 * that[0]. scrollTop / Math.max(1,
|
||||||
that[0].scrollHeight - that.height())),
|
that[0].scrollHeight - that.height())),
|
||||||
|
settings;
|
||||||
|
if (pandora.user.ui.section == 'texts') {
|
||||||
settings = pandora.user.ui.texts[pandora.user.ui.text];
|
settings = pandora.user.ui.texts[pandora.user.ui.text];
|
||||||
|
} else {
|
||||||
|
settings = pandora.user.ui.documents[pandora.user.ui.document] || {};
|
||||||
|
}
|
||||||
position = position - position % 10;
|
position = position - position % 10;
|
||||||
if (!scrolling && settings && (settings.name || (position != settings.position))) {
|
if (!scrolling && settings && (settings.name || (position != settings.position))) {
|
||||||
pandora.UI.set('texts.' + pandora.UI.encode(pandora.user.ui.text), {
|
if (pandora.user.ui.section == 'documents') {
|
||||||
position: position ? position : 0
|
pandora.UI.set('documents.' + pandora.user.ui.document, {
|
||||||
});
|
position: position ? position : 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
pandora.UI.set('texts.' + pandora.UI.encode(pandora.user.ui.text), {
|
||||||
|
position: position ? position : 0
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
scrolling = false;
|
scrolling = false;
|
||||||
},
|
},
|
||||||
|
@ -248,6 +260,9 @@ pandora.ui.textHTML = function(text) {
|
||||||
that.update();
|
that.update();
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
.bindEvent('pandora_documents.' + text.id.toLowerCase(), function(data) {
|
||||||
|
data.value && data.value.name && scrollToPosition();
|
||||||
|
})
|
||||||
.bindEvent('pandora_texts.' + text.id.toLowerCase(), function(data) {
|
.bindEvent('pandora_texts.' + text.id.toLowerCase(), function(data) {
|
||||||
data.value && data.value.name && scrollToPosition();
|
data.value && data.value.name && scrollToPosition();
|
||||||
}),
|
}),
|
||||||
|
@ -257,10 +272,10 @@ pandora.ui.textHTML = function(text) {
|
||||||
.appendTo(that),
|
.appendTo(that),
|
||||||
|
|
||||||
$title = Ox.EditableContent({
|
$title = Ox.EditableContent({
|
||||||
editable: text.name ? text.editable : false,
|
editable: text.title ? text.editable : false,
|
||||||
placeholder: text.editable ? Ox._('Doubleclick to edit title') : Ox._('Untitled'),
|
placeholder: text.editable ? Ox._('Doubleclick to edit title') : Ox._('Untitled'),
|
||||||
tooltip: text.editable ? pandora.getEditTooltip('title') : '',
|
tooltip: text.editable ? pandora.getEditTooltip('title') : '',
|
||||||
value: text.name || Ox._('{0} Texts', [pandora.site.site.name]),
|
value: text.title || Ox._('{0} Texts', [pandora.site.site.name]),
|
||||||
width: width
|
width: width
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
|
@ -271,17 +286,31 @@ pandora.ui.textHTML = function(text) {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
submit: function(data) {
|
submit: function(data) {
|
||||||
Ox.Request.clearCache('getText');
|
if (pandora.user.ui.section == 'documents') {
|
||||||
pandora.api.editText({
|
pandora.api.editDocument({
|
||||||
id: pandora.user.ui.text,
|
id: pandora.user.ui.document,
|
||||||
name: data.value
|
name: data.value
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
if (result.data.id != pandora.user.ui.text) {
|
if (result.data.name != data.value) {
|
||||||
Ox.Request.clearCache();
|
Ox.Request.clearCache();
|
||||||
pandora.renameList(pandora.user.ui.text, result.data.id, result.data.name);
|
$title.options({
|
||||||
pandora.$ui.info.updateListInfo();
|
value: result.data.title
|
||||||
}
|
})
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ox.Request.clearCache('getText');
|
||||||
|
pandora.api.editText({
|
||||||
|
id: pandora.user.ui.text,
|
||||||
|
name: data.value
|
||||||
|
}, function(result) {
|
||||||
|
if (result.data.id != pandora.user.ui.text) {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
pandora.renameList(pandora.user.ui.text, result.data.id, result.data.name);
|
||||||
|
pandora.$ui.info.updateListInfo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.appendTo($content),
|
.appendTo($content),
|
||||||
|
@ -373,12 +402,23 @@ pandora.ui.textHTML = function(text) {
|
||||||
})
|
})
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
submit: function(data) {
|
submit: function(data) {
|
||||||
Ox.Request.clearCache('getText');
|
if (pandora.user.ui.section == 'documents') {
|
||||||
pandora.api.editText({
|
Ox.Request.clearCache('getDocument');
|
||||||
id: pandora.user.ui.text,
|
pandora.api.editDocument({
|
||||||
text: data.value
|
id: pandora.user.ui.document,
|
||||||
});
|
text: data.value
|
||||||
pandora.$ui.textPanel.update(data.value);
|
}, function(result) {
|
||||||
|
//fixme: just reload as it was done with textPanel
|
||||||
|
pandora.$ui.document = pandora.ui.document();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Ox.Request.clearCache('getText');
|
||||||
|
pandora.api.editText({
|
||||||
|
id: pandora.user.ui.text,
|
||||||
|
text: data.value
|
||||||
|
});
|
||||||
|
pandora.$ui.textPanel.update(data.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.appendTo($content);
|
.appendTo($content);
|
||||||
|
@ -404,7 +444,9 @@ pandora.ui.textHTML = function(text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollToPosition() {
|
function scrollToPosition() {
|
||||||
var settings = pandora.user.ui.texts[pandora.user.ui.text] || {},
|
var settings = (pandora.user.ui.section == 'documents'
|
||||||
|
? pandora.user.ui.documents[pandora.user.ui.document]
|
||||||
|
: pandora.user.ui.texts[pandora.user.ui.text]) || {},
|
||||||
position = settings.position || 0,
|
position = settings.position || 0,
|
||||||
element,
|
element,
|
||||||
scrollTop;
|
scrollTop;
|
||||||
|
@ -505,6 +547,7 @@ pandora.ui.textEmbed = function() {
|
||||||
resize: function(data) {
|
resize: function(data) {
|
||||||
pandora.user.ui.embedSize = data.size;
|
pandora.user.ui.embedSize = data.size;
|
||||||
pandora.$ui.text.update();
|
pandora.$ui.text.update();
|
||||||
|
pandora.$ui.document && pandora.$ui.document.update();
|
||||||
},
|
},
|
||||||
resizeend: function(data) {
|
resizeend: function(data) {
|
||||||
$iframe.attr('src') && $overlay.hide();
|
$iframe.attr('src') && $overlay.hide();
|
||||||
|
|
|
@ -46,7 +46,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
|
||||||
if (title == Ox._('Cancel Upload')) {
|
if (title == Ox._('Cancel Upload')) {
|
||||||
upload && upload.abort();
|
upload && upload.abort();
|
||||||
} else if (title == Ox._('Done')) {
|
} else if (title == Ox._('Done')) {
|
||||||
callback({
|
callback && callback({
|
||||||
ids: ids
|
ids: ids
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
|
||||||
+ (extension == 'jpeg' ? 'jpg' : extension);
|
+ (extension == 'jpeg' ? 'jpg' : extension);
|
||||||
valid && Ox.oshash(file, function(oshash) {
|
valid && Ox.oshash(file, function(oshash) {
|
||||||
pandora.api.findDocuments({
|
pandora.api.findDocuments({
|
||||||
keys: ['id', 'user', 'name', 'extension'],
|
keys: ['id', 'user', 'title', 'extension'],
|
||||||
query: {
|
query: {
|
||||||
conditions: [{
|
conditions: [{
|
||||||
key: 'oshash',
|
key: 'oshash',
|
||||||
|
@ -91,10 +91,10 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
|
||||||
operator: '&'
|
operator: '&'
|
||||||
},
|
},
|
||||||
range: [0, 1],
|
range: [0, 1],
|
||||||
sort: [{key: 'name', operator: '+'}]
|
sort: [{key: 'title', operator: '+'}]
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
if (result.data.items.length) {
|
if (result.data.items.length) {
|
||||||
var id = result.data.items[0].name + '.'
|
var id = result.data.items[0].title + '.'
|
||||||
+ result.data.items[0].extension;
|
+ result.data.items[0].extension;
|
||||||
valid && errorDialog(
|
valid && errorDialog(
|
||||||
filename == id
|
filename == id
|
||||||
|
@ -161,7 +161,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
|
||||||
ids.push(data.response.id);
|
ids.push(data.response.id);
|
||||||
if (part == files.length) {
|
if (part == files.length) {
|
||||||
$progress.options({progress: data.progress});
|
$progress.options({progress: data.progress});
|
||||||
callback({ids: ids});
|
callback && callback({ids: ids});
|
||||||
$uploadDialog.close();
|
$uploadDialog.close();
|
||||||
} else {
|
} else {
|
||||||
uploadFile(part);
|
uploadFile(part);
|
||||||
|
|
|
@ -40,6 +40,9 @@ pandora.addFolderItem = function(section) {
|
||||||
if (!isSmart) {
|
if (!isSmart) {
|
||||||
if (isItems) {
|
if (isItems) {
|
||||||
data.items = ui.listSelection;
|
data.items = ui.listSelection;
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
//fixme
|
||||||
|
data.items = ui.collectionSelection;
|
||||||
} else {
|
} else {
|
||||||
data.clips = pandora.getClipData(
|
data.clips = pandora.getClipData(
|
||||||
ui.section == 'items'
|
ui.section == 'items'
|
||||||
|
@ -66,7 +69,11 @@ pandora.addFolderItem = function(section) {
|
||||||
if (data.type == 'smart') {
|
if (data.type == 'smart') {
|
||||||
data.query = listData.query;
|
data.query = listData.query;
|
||||||
}
|
}
|
||||||
pandora.api[isItems ? 'findLists' : 'findEdits']({
|
pandora.api[{
|
||||||
|
items: 'findLists',
|
||||||
|
documents: 'findCollections',
|
||||||
|
edits: 'findEdits',
|
||||||
|
}[section]]({
|
||||||
query: {conditions: [{
|
query: {conditions: [{
|
||||||
key: 'id',
|
key: 'id',
|
||||||
operator: '==',
|
operator: '==',
|
||||||
|
@ -103,6 +110,9 @@ pandora.addFolderItem = function(section) {
|
||||||
addList();
|
addList();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if(section == 'documents') {
|
||||||
|
//fixme
|
||||||
|
addList();
|
||||||
} else {
|
} else {
|
||||||
pandora.api.getEdit({
|
pandora.api.getEdit({
|
||||||
id: list,
|
id: list,
|
||||||
|
@ -127,7 +137,11 @@ pandora.addFolderItem = function(section) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function addList() {
|
function addList() {
|
||||||
pandora.api[isItems ? 'addList' : 'addEdit'](data, function(result) {
|
pandora.api[{
|
||||||
|
items: 'addList',
|
||||||
|
documents: 'addCollection',
|
||||||
|
edits: 'addEdit'
|
||||||
|
}[section]](data, function(result) {
|
||||||
getPosterFrames(result.data.id);
|
getPosterFrames(result.data.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -136,23 +150,41 @@ pandora.addFolderItem = function(section) {
|
||||||
sortKey = Ox.getObjectById(pandora.site.itemKeys, 'votes')
|
sortKey = Ox.getObjectById(pandora.site.itemKeys, 'votes')
|
||||||
? 'votes' : 'timesaccessed';
|
? 'votes' : 'timesaccessed';
|
||||||
if (!isDuplicate) {
|
if (!isDuplicate) {
|
||||||
(isItems ? Ox.noop : pandora.api.getEdit)({
|
({
|
||||||
|
items: Ox.noop,
|
||||||
|
documents: Ox.noop,
|
||||||
|
edits: pandora.api.getEdit
|
||||||
|
}[section])({
|
||||||
id: newList,
|
id: newList,
|
||||||
keys: ['clips']
|
keys: ['clips']
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
query = isItems ? {
|
if (Ox.contains(pandora.site.listSections, section)) {
|
||||||
conditions: [{key: 'list', value: newList, operator: '=='}],
|
query = {
|
||||||
operator: '&'
|
conditions: [{
|
||||||
} : {
|
key: section == 'documents' ? 'collection' : 'list',
|
||||||
conditions: Ox.unique(result.data.clips.map(function(clip) {
|
value: newList, operator: '=='
|
||||||
return {key: 'id', value: clip.item, operator: '=='};
|
}],
|
||||||
})),
|
operator: '&'
|
||||||
operator: '|'
|
};
|
||||||
};
|
} else{
|
||||||
(isItems ? pandora.api.find : Ox.noop)({
|
query = {
|
||||||
|
conditions: Ox.unique(result.data.clips.map(function(clip) {
|
||||||
|
return {key: 'id', value: clip.item, operator: '=='};
|
||||||
|
})),
|
||||||
|
operator: '|'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
({
|
||||||
|
items: pandora.api.find,
|
||||||
|
documents: pandora.api.findDocuments,
|
||||||
|
edits: Ox.noop
|
||||||
|
}[section])({
|
||||||
query: {
|
query: {
|
||||||
conditions: [
|
conditions: [
|
||||||
{key: 'list', value: newList, operator: '=='}
|
{
|
||||||
|
key: section == 'documents' ? 'collection' : 'list',
|
||||||
|
value: newList, operator: '=='
|
||||||
|
}
|
||||||
],
|
],
|
||||||
operator: '&'
|
operator: '&'
|
||||||
},
|
},
|
||||||
|
@ -181,7 +213,11 @@ pandora.addFolderItem = function(section) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
pandora.api[isItems ? 'findLists' : 'findEdits']({
|
pandora.api[{
|
||||||
|
items: 'findLists',
|
||||||
|
documents: 'findCollections',
|
||||||
|
edits: 'findEdits'
|
||||||
|
}[section]]({
|
||||||
query: {
|
query: {
|
||||||
conditions: [{key: 'id', value: list, operator: '=='}],
|
conditions: [{key: 'id', value: list, operator: '=='}],
|
||||||
operator: '&'
|
operator: '&'
|
||||||
|
@ -193,7 +229,11 @@ pandora.addFolderItem = function(section) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setPosterFrames(newList, posterFrames) {
|
function setPosterFrames(newList, posterFrames) {
|
||||||
pandora.api[isItems ? 'editList' : 'editEdit']({
|
pandora.api[{
|
||||||
|
items: 'editList',
|
||||||
|
documents: 'editCollection',
|
||||||
|
edits: 'editEdit'
|
||||||
|
}[section]]({
|
||||||
id: newList,
|
id: newList,
|
||||||
posterFrames: posterFrames
|
posterFrames: posterFrames
|
||||||
}, function() {
|
}, function() {
|
||||||
|
@ -206,22 +246,37 @@ pandora.addFolderItem = function(section) {
|
||||||
// (same applies to addText, below)
|
// (same applies to addText, below)
|
||||||
$folderList = pandora.$ui.folderList.personal;
|
$folderList = pandora.$ui.folderList.personal;
|
||||||
pandora.$ui.folder[0].options({collapsed: false});
|
pandora.$ui.folder[0].options({collapsed: false});
|
||||||
Ox.Request.clearCache(isItems ? 'findLists' : 'findEdits');
|
Ox.Request.clearCache({
|
||||||
|
items: 'findLists',
|
||||||
|
documents: 'findCollections',
|
||||||
|
edits: 'findEdits'
|
||||||
|
}[section]);
|
||||||
$folderList.bindEventOnce({
|
$folderList.bindEventOnce({
|
||||||
load: function() {
|
load: function() {
|
||||||
$folderList.gainFocus()
|
$folderList.gainFocus()
|
||||||
.options({selected: [newList]})
|
.options({selected: [newList]})
|
||||||
.editCell(newList, 'name', true);
|
.editCell(newList, 'name', true);
|
||||||
pandora.UI.set(isItems ? {
|
pandora.UI.set({
|
||||||
find: {
|
items: {
|
||||||
conditions: [
|
find: {
|
||||||
{key: 'list', value: newList, operator: '=='}
|
conditions: [
|
||||||
],
|
{key: 'list', value: newList, operator: '=='}
|
||||||
operator: '&'
|
],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
documents: {
|
||||||
|
findDocuments: {
|
||||||
|
conditions: [
|
||||||
|
{key: 'collection', value: newList, operator: '=='}
|
||||||
|
],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edits: {
|
||||||
|
edit: newList
|
||||||
}
|
}
|
||||||
} : {
|
}[section]);
|
||||||
edit: newList
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}).reloadList();
|
}).reloadList();
|
||||||
}
|
}
|
||||||
|
@ -229,7 +284,7 @@ pandora.addFolderItem = function(section) {
|
||||||
|
|
||||||
pandora.addList = function() {
|
pandora.addList = function() {
|
||||||
// addList(isSmart, isFrom) or addList(list) [=duplicate]
|
// addList(isSmart, isFrom) or addList(list) [=duplicate]
|
||||||
pandora.addFolderItem.apply(null, ['items'].concat(Ox.slice(arguments)));
|
pandora.addFolderItem.apply(null, [pandora.user.ui.section].concat(Ox.slice(arguments)));
|
||||||
};
|
};
|
||||||
|
|
||||||
pandora.addText = function(options) {
|
pandora.addText = function(options) {
|
||||||
|
@ -271,8 +326,7 @@ pandora.beforeUnloadWindow = function() {
|
||||||
|
|
||||||
pandora.changeFolderItemStatus = function(id, status, callback) {
|
pandora.changeFolderItemStatus = function(id, status, callback) {
|
||||||
var ui = pandora.user.ui,
|
var ui = pandora.user.ui,
|
||||||
folderItems = ui.section == 'items'
|
folderItems = pandora.getFolderItems(ui.section),
|
||||||
? 'Lists' : Ox.toTitleCase(ui.section),
|
|
||||||
folderItem = folderItems.slice(0, -1);
|
folderItem = folderItems.slice(0, -1);
|
||||||
if (status == 'private') {
|
if (status == 'private') {
|
||||||
pandora.api['find' + folderItems]({
|
pandora.api['find' + folderItems]({
|
||||||
|
@ -491,7 +545,31 @@ pandora.createLinks = function($element) {
|
||||||
list: target
|
list: target
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
callback(result, addedItems);
|
callback(result, addedItems);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
callback(null, []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type == 'documents') {
|
||||||
|
//fixme
|
||||||
|
pandora.api.findDocuments({
|
||||||
|
query: {
|
||||||
|
conditions: [{key: 'collection', operator: '==', value: target}],
|
||||||
|
operator: '&'
|
||||||
|
},
|
||||||
|
positions: items
|
||||||
|
}, function(result) {
|
||||||
|
var existingItems = Object.keys(result.data.positions),
|
||||||
|
addedItems = items.filter(function(item) {
|
||||||
|
return !Ox.contains(existingItems, item);
|
||||||
|
});
|
||||||
|
if (addedItems.length) {
|
||||||
|
pandora.api.addCollectionItems({
|
||||||
|
items: addedItems,
|
||||||
|
collection: target
|
||||||
|
}, function(result) {
|
||||||
|
callback(result, addedItems);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
callback(null, []);
|
callback(null, []);
|
||||||
}
|
}
|
||||||
|
@ -540,6 +618,31 @@ pandora.createLinks = function($element) {
|
||||||
// FIXME: Why is this timeout needed?
|
// FIXME: Why is this timeout needed?
|
||||||
setTimeout(pandora.reloadList, 250);
|
setTimeout(pandora.reloadList, 250);
|
||||||
}
|
}
|
||||||
|
} else if (type == 'document' && ui.section == 'documents') {
|
||||||
|
Ox.Request.clearCache('findDocuments');
|
||||||
|
object.targets.filter(function(list) {
|
||||||
|
return list != ui._list;
|
||||||
|
}).forEach(function(list) {
|
||||||
|
listData = pandora.getListData(list);
|
||||||
|
pandora.api.findDocuments({
|
||||||
|
query: {
|
||||||
|
conditions: [{
|
||||||
|
key: 'collection',
|
||||||
|
operator: '==',
|
||||||
|
value: list
|
||||||
|
}],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
}, function(result) {
|
||||||
|
pandora.$ui.folderList[listData.folder].value(
|
||||||
|
list, 'items', result.data.items
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (Ox.contains(object.targets, ui._list)) {
|
||||||
|
// FIXME: Why is this timeout needed?
|
||||||
|
setTimeout(pandora.reloadList, 250);
|
||||||
|
}
|
||||||
} else if (type == 'clip' && ui.section == 'edits') {
|
} else if (type == 'clip' && ui.section == 'edits') {
|
||||||
// FIXME: update edit list (once it has item count)
|
// FIXME: update edit list (once it has item count)
|
||||||
if (Ox.contains(object.targets, ui.edit)) {
|
if (Ox.contains(object.targets, ui.edit)) {
|
||||||
|
@ -743,6 +846,36 @@ pandora.enableDragAndDrop = function($list, canMove, section, getItems) {
|
||||||
});
|
});
|
||||||
drag.action == 'move' && pandora.reloadList();
|
drag.action == 'move' && pandora.reloadList();
|
||||||
});
|
});
|
||||||
|
} else if (section == 'documents') {
|
||||||
|
var targets = drag.action == 'copy' ? drag.target.id
|
||||||
|
: [pandora.user.ui._collection, drag.target.id];
|
||||||
|
//fixme use history
|
||||||
|
//pandora.doHistory(drag.action, drag.ids, targets, function() {
|
||||||
|
pandora.api.addCollectionItems({
|
||||||
|
collection: drag.target.id,
|
||||||
|
items: drag.ids
|
||||||
|
|
||||||
|
}, function() {
|
||||||
|
Ox.Request.clearCache('find');
|
||||||
|
pandora.api.findDocuments({
|
||||||
|
query: {
|
||||||
|
conditions: [{
|
||||||
|
key: 'collection',
|
||||||
|
operator: '==',
|
||||||
|
value: drag.target.id
|
||||||
|
}],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
}, function(result) {
|
||||||
|
var folder = drag.target.status != 'featured'
|
||||||
|
? 'personal' : 'featured';
|
||||||
|
pandora.$ui.folderList[folder].value(
|
||||||
|
drag.target.id, 'items', result.data.items
|
||||||
|
);
|
||||||
|
cleanup(250);
|
||||||
|
});
|
||||||
|
drag.action == 'move' && pandora.reloadList();
|
||||||
|
});
|
||||||
} else if (section == 'edits') {
|
} else if (section == 'edits') {
|
||||||
var targets = drag.action == 'copy' ? drag.target.id
|
var targets = drag.action == 'copy' ? drag.target.id
|
||||||
: [pandora.user.ui.edit, drag.target.id];
|
: [pandora.user.ui.edit, drag.target.id];
|
||||||
|
@ -751,7 +884,10 @@ pandora.enableDragAndDrop = function($list, canMove, section, getItems) {
|
||||||
pandora.$ui.editPanel && pandora.$ui.editPanel.updatePanel();
|
pandora.$ui.editPanel && pandora.$ui.editPanel.updatePanel();
|
||||||
cleanup(250);
|
cleanup(250);
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
|
Ox.print('no drop support for', section);
|
||||||
|
cleanup(250);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cleanup(0);
|
cleanup(0);
|
||||||
|
@ -781,13 +917,20 @@ pandora.enableDragAndDrop = function($list, canMove, section, getItems) {
|
||||||
itemName = section == 'items' ? {
|
itemName = section == 'items' ? {
|
||||||
plural: Ox._(pandora.site.itemName.plural.toLowerCase()),
|
plural: Ox._(pandora.site.itemName.plural.toLowerCase()),
|
||||||
singular: Ox._(pandora.site.itemName.singular.toLowerCase())
|
singular: Ox._(pandora.site.itemName.singular.toLowerCase())
|
||||||
} : {
|
} : section == 'documents' ? {
|
||||||
|
plural: Ox._('Documents'),
|
||||||
|
singular: Ox._('Document')
|
||||||
|
|
||||||
|
} :{
|
||||||
plural: Ox._('clips'),
|
plural: Ox._('clips'),
|
||||||
singular: Ox._('clip')
|
singular: Ox._('clip')
|
||||||
},
|
},
|
||||||
targetName = section == 'items' ? {
|
targetName = section == 'items' ? {
|
||||||
plural: Ox._('lists'),
|
plural: Ox._('lists'),
|
||||||
singular: Ox._('list')
|
singular: Ox._('list')
|
||||||
|
} : section == 'documents' ? {
|
||||||
|
plural: Ox._('collections'),
|
||||||
|
singular: Ox._('collection')
|
||||||
} : {
|
} : {
|
||||||
plural: Ox._('edits'),
|
plural: Ox._('edits'),
|
||||||
singular: Ox._('edit')
|
singular: Ox._('edit')
|
||||||
|
@ -946,11 +1089,36 @@ pandora.exitFullscreen = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pandora.formatDocumentKey = function(key, data) {
|
||||||
|
var value;
|
||||||
|
if (key.format) {
|
||||||
|
value = (
|
||||||
|
/^color/.test(key.format.type.toLowerCase()) ? Ox.Theme : Ox
|
||||||
|
)['format' + Ox.toTitleCase(key.format.type)].apply(
|
||||||
|
this, [data[key.id]].concat(key.format.args || [])
|
||||||
|
);
|
||||||
|
if (key.id == 'rightslevel') {
|
||||||
|
value.css({width: size * 0.75 + 'px'});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = data[key.id];
|
||||||
|
if (key.id == 'extension') {
|
||||||
|
value = value.toUpperCase();
|
||||||
|
} else if (key.id == 'dimensions') {
|
||||||
|
value = Ox.isArray(value)
|
||||||
|
? Ox.formatDimensions(value, 'px')
|
||||||
|
: Ox.formatCount(value, data.extension == 'html' ? 'word' : 'page');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
pandora.getAllItemsTitle = function(section) {
|
pandora.getAllItemsTitle = function(section) {
|
||||||
section = section || pandora.user.ui.section;
|
section = section || pandora.user.ui.section;
|
||||||
return section == 'items'
|
return {
|
||||||
? Ox._('All {0}', [Ox._(pandora.site.itemName.plural)])
|
items: Ox._('All {0}', [Ox._(pandora.site.itemName.plural)]),
|
||||||
: Ox._('{0} ' + Ox.toTitleCase(section), [pandora.site.site.name]);
|
documents: Ox._('All {0}', [Ox._('Documents')])
|
||||||
|
}[section] || Ox._('{0} ' + Ox.toTitleCase(section), [pandora.site.site.name]);
|
||||||
};
|
};
|
||||||
|
|
||||||
pandora.getClipData = function(items) {
|
pandora.getClipData = function(items) {
|
||||||
|
@ -1068,13 +1236,19 @@ pandora.getClipVideos = function(clip, resolution) {
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var itemTitles = {};
|
var itemTitles = {}, documentTitles = {};
|
||||||
pandora.getDocumentTitle = function(itemData) {
|
pandora.getWindowTitle = function(itemData) {
|
||||||
var parts = [];
|
var parts = [];
|
||||||
if (itemData) {
|
if (itemData) {
|
||||||
itemTitles[pandora.user.ui.item] = Ox.decodeHTMLEntities(
|
if (pandora.user.ui.section == 'documents') {
|
||||||
pandora.getItemTitle(itemData)
|
documentTitles[pandora.user.ui.document] = Ox.decodeHTMLEntities(
|
||||||
);
|
pandora.getDocumentTitle(itemData)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
itemTitles[pandora.user.ui.item] = Ox.decodeHTMLEntities(
|
||||||
|
pandora.getItemTitle(itemData)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pandora.user.ui.section == 'items') {
|
if (pandora.user.ui.section == 'items') {
|
||||||
if (!pandora.user.ui.item) {
|
if (!pandora.user.ui.item) {
|
||||||
|
@ -1094,16 +1268,33 @@ pandora.getClipVideos = function(clip, resolution) {
|
||||||
Ox._(Ox.toTitleCase(pandora.user.ui.itemView))
|
Ox._(Ox.toTitleCase(pandora.user.ui.itemView))
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
} else if (pandora.user.ui.section == 'documents') {
|
||||||
|
if (!pandora.user.ui.document) {
|
||||||
|
parts.push(
|
||||||
|
pandora.user.ui._collection
|
||||||
|
? pandora.user.ui._collection.split(':').slice(1).join(':')
|
||||||
|
: pandora.getAllItemsTitle('documents')
|
||||||
|
);
|
||||||
|
parts.push(Ox._('{0} View', [
|
||||||
|
Ox._(Ox.toTitleCase(pandora.user.ui.collectionView))
|
||||||
|
]));
|
||||||
|
parts.push(Ox._('Documents'));
|
||||||
|
} else {
|
||||||
|
parts.push(
|
||||||
|
documentTitles[pandora.user.ui.document] || pandora.user.ui.document
|
||||||
|
);
|
||||||
|
/*
|
||||||
|
parts.push(Ox._('{0} View', [
|
||||||
|
Ox._(Ox.toTitleCase(pandora.user.ui.documentView))
|
||||||
|
]));
|
||||||
|
*/
|
||||||
|
parts.push(Ox._('Document'));
|
||||||
|
}
|
||||||
} else if (pandora.user.ui.section == 'edits') {
|
} else if (pandora.user.ui.section == 'edits') {
|
||||||
if (pandora.user.ui.edit) {
|
if (pandora.user.ui.edit) {
|
||||||
parts.push(pandora.user.ui.edit.split(':').slice(1).join(':'));
|
parts.push(pandora.user.ui.edit.split(':').slice(1).join(':'));
|
||||||
}
|
}
|
||||||
parts.push(Ox._('Edits'));
|
parts.push(Ox._('Edits'));
|
||||||
} else if (pandora.user.ui.section == 'texts') {
|
|
||||||
if (pandora.user.ui.text) {
|
|
||||||
parts.push(pandora.user.ui.text.split(':').slice(1).join(':'));
|
|
||||||
}
|
|
||||||
parts.push(Ox._('Texts'));
|
|
||||||
}
|
}
|
||||||
parts.push(pandora.site.site.name);
|
parts.push(pandora.site.site.name);
|
||||||
return parts.join(' – ');
|
return parts.join(' – ');
|
||||||
|
@ -1138,6 +1329,12 @@ pandora.getFilterSizes = function() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pandora.getFolderItems = function(section) {
|
||||||
|
return section == 'items' ? 'Lists'
|
||||||
|
: section == 'documents' ? 'Collections'
|
||||||
|
: Ox.toTitleCase(section);
|
||||||
|
}
|
||||||
|
|
||||||
pandora.getFoldersHeight = function(section) {
|
pandora.getFoldersHeight = function(section) {
|
||||||
section = section || pandora.user.ui.section;
|
section = section || pandora.user.ui.section;
|
||||||
var height = 0;
|
var height = 0;
|
||||||
|
@ -1299,6 +1496,16 @@ pandora.getItem = function(state, str, callback) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (state.type == 'documents') {
|
||||||
|
pandora.api.getDocument({id: str, keys: ['id']}, function(result) {
|
||||||
|
if (result.status.code == 200) {
|
||||||
|
state.item = result.data.id;
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
state.item = '';
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (state.type == 'edits') {
|
} else if (state.type == 'edits') {
|
||||||
pandora.api.getEdit({id: str, keys: ['id']}, function(result) {
|
pandora.api.getEdit({id: str, keys: ['id']}, function(result) {
|
||||||
if (result.status.code == 200) {
|
if (result.status.code == 200) {
|
||||||
|
@ -1310,21 +1517,23 @@ pandora.getItem = function(state, str, callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (state.type == 'texts') {
|
} else if (state.type == 'texts') {
|
||||||
pandora.api.getText({
|
pandora.api.findDocuments({
|
||||||
id: str,
|
query: {
|
||||||
keys: ['id', 'names', 'pages', 'type']
|
conditions: [
|
||||||
|
{key: 'user', value: str.split(':')[0]},
|
||||||
|
{key: 'title', value: str.split(':').slice(1).join(':')}
|
||||||
|
],
|
||||||
|
operator: '&'},
|
||||||
|
keys: ['id', 'extension'],
|
||||||
|
range: [0, 2]
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
if (result.status.code == 200) {
|
state.type = 'documents';
|
||||||
state.item = result.data.id;
|
if (result.data.items.length == 1) {
|
||||||
callback();
|
state.item = result.data.items[0].id;
|
||||||
} else {
|
} else {
|
||||||
// FIXME: add findText call here?
|
|
||||||
// FIXME: it's obscure that in the texts case,
|
|
||||||
// we have to set item to '', while for videos,
|
|
||||||
// it remains undefined
|
|
||||||
state.item = '';
|
state.item = '';
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
|
@ -1471,12 +1680,19 @@ pandora.getLargeEditTimelineURL = function(edit, type, i, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
pandora.getListData = function(list) {
|
pandora.getListData = function(list) {
|
||||||
var data = {}, folder;
|
var data = {}, folder, _list = pandora.user.ui._list;
|
||||||
if (Ox.isUndefined(list)) {
|
if (Ox.isUndefined(list)) {
|
||||||
list = pandora.user.ui[
|
if (pandora.user.ui.section == 'items') {
|
||||||
pandora.user.ui.section == 'items' ? '_list'
|
list = pandora.user.ui._list;
|
||||||
: pandora.user.ui.section.slice(0, -1)
|
} else if (pandora.user.ui.section == 'documents') {
|
||||||
];
|
list = pandora.user.ui._collection;
|
||||||
|
_list = pandora.user.ui._collection;
|
||||||
|
} else {
|
||||||
|
list = pandora.user.ui.section.slice(0, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pandora.user.ui.section == 'documents') {
|
||||||
|
_list = pandora.user.ui._collection;
|
||||||
}
|
}
|
||||||
if (list && pandora.$ui.folderList) {
|
if (list && pandora.$ui.folderList) {
|
||||||
Ox.forEach(pandora.$ui.folderList, function($list, id) {
|
Ox.forEach(pandora.$ui.folderList, function($list, id) {
|
||||||
|
@ -1485,7 +1701,7 @@ pandora.getListData = function(list) {
|
||||||
// folder it is selected, since for example, a personal
|
// folder it is selected, since for example, a personal
|
||||||
// list may appear again in the featured lists browser
|
// list may appear again in the featured lists browser
|
||||||
if (
|
if (
|
||||||
(list == pandora.user.ui._list && $list.options('selected').length)
|
(list == _list && $list.options('selected').length)
|
||||||
|| !Ox.isEmpty($list.value(list))
|
|| !Ox.isEmpty($list.value(list))
|
||||||
) {
|
) {
|
||||||
folder = id;
|
folder = id;
|
||||||
|
@ -1506,6 +1722,16 @@ pandora.getListData = function(list) {
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pandora.getListIcon = function(section, id, size, modified) {
|
||||||
|
var folderItems = pandora.getFolderItems(section),
|
||||||
|
folderItem = folderItems.slice(0, -1);
|
||||||
|
size = size || '';
|
||||||
|
modified = modified || Ox.uid();
|
||||||
|
return pandora.getMediaURL('/'
|
||||||
|
+ folderItem.toLowerCase() + '/'
|
||||||
|
+ encodeURIComponent(id) + '/icon' + size + '.jpg?' + modified);
|
||||||
|
};
|
||||||
|
|
||||||
pandora.getPageTitle = function(stateOrURL) {
|
pandora.getPageTitle = function(stateOrURL) {
|
||||||
var pages = [
|
var pages = [
|
||||||
{id: '', title: ''},
|
{id: '', title: ''},
|
||||||
|
@ -1529,6 +1755,7 @@ pandora.getPageTitle = function(stateOrURL) {
|
||||||
};
|
};
|
||||||
|
|
||||||
pandora.getPart = function(state, str, callback) {
|
pandora.getPart = function(state, str, callback) {
|
||||||
|
Ox.Log('URL', 'getPart', state, str);
|
||||||
if (state.page == 'api') {
|
if (state.page == 'api') {
|
||||||
pandora.api.api(function(result) {
|
pandora.api.api(function(result) {
|
||||||
if (Ox.contains(Object.keys(result.data.actions), str)) {
|
if (Ox.contains(Object.keys(result.data.actions), str)) {
|
||||||
|
@ -1656,6 +1883,8 @@ pandora.getSort = function(state, val, callback) {
|
||||||
// TODO in the future: If str is index, fall back if list is smart
|
// TODO in the future: If str is index, fall back if list is smart
|
||||||
// (but this can only be tested after find has been parsed)
|
// (but this can only be tested after find has been parsed)
|
||||||
callback();
|
callback();
|
||||||
|
} else if (state.type == 'documents') {
|
||||||
|
callback();
|
||||||
} else if (state.type == 'edits') {
|
} else if (state.type == 'edits') {
|
||||||
if (val[0].key == 'index') {
|
if (val[0].key == 'index') {
|
||||||
pandora.api.getEdit({id: state.item, keys: ['id', 'type']}, function(result) {
|
pandora.api.getEdit({id: state.item, keys: ['id', 'type']}, function(result) {
|
||||||
|
@ -1675,8 +1904,6 @@ pandora.getSort = function(state, val, callback) {
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
} else if (state.type == 'texts') {
|
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1705,6 +1932,29 @@ pandora.getSortOperator = function(key) {
|
||||||
) > -1 ? '+' : '-';
|
) > -1 ? '+' : '-';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pandora.getDocumentSortKeys = function() {
|
||||||
|
return pandora.site.documentKeys.filter(function(key) {
|
||||||
|
return key.sort && (
|
||||||
|
!key.capability
|
||||||
|
|| pandora.site.capabilities[key.capability][pandora.user.level]
|
||||||
|
);
|
||||||
|
}).map(function(key) {
|
||||||
|
return Ox.extend(key, {
|
||||||
|
operator: pandora.getDocumentSortOperator(key.id)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
pandora.getDocumentSortOperator = function(key) {
|
||||||
|
var data = Ox.getObjectById(pandora.site.documentKeys, key);
|
||||||
|
return data.sortOperator || ['string', 'text'].indexOf(
|
||||||
|
Ox.isArray(data.type) ? data.type[0] : data.type
|
||||||
|
) > -1 ? '+' : '-';
|
||||||
|
};
|
||||||
|
|
||||||
|
pandora.getDocumentTitle = function(data) {
|
||||||
|
return data.title || Ox._('Untitled');
|
||||||
|
};
|
||||||
|
|
||||||
pandora.getSpan = function(state, val, callback) {
|
pandora.getSpan = function(state, val, callback) {
|
||||||
// For a given item, or none (state.item), and a given view, or any
|
// For a given item, or none (state.item), and a given view, or any
|
||||||
// (state.view), this takes a value (array of numbers or string) and checks
|
// (state.view), this takes a value (array of numbers or string) and checks
|
||||||
|
@ -1712,9 +1962,10 @@ pandora.getSpan = function(state, val, callback) {
|
||||||
// event/place name (string), and in that case sets state.span, and may
|
// event/place name (string), and in that case sets state.span, and may
|
||||||
// modify state.view.
|
// modify state.view.
|
||||||
// fixme: "subtitles:23" is still missing
|
// fixme: "subtitles:23" is still missing
|
||||||
if (state.page == 'documents') {
|
Ox.Log('URL', 'getSpan', state, val);
|
||||||
|
if (state.type == 'documents') {
|
||||||
pandora.api.getDocument({
|
pandora.api.getDocument({
|
||||||
id: state.part,
|
id: state.item,
|
||||||
keys: ['dimensions', 'extension']
|
keys: ['dimensions', 'extension']
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
var dimensions = result.data.dimensions,
|
var dimensions = result.data.dimensions,
|
||||||
|
@ -1722,6 +1973,8 @@ pandora.getSpan = function(state, val, callback) {
|
||||||
values;
|
values;
|
||||||
if (Ox.contains(['epub', 'pdf', 'txt'], extension)) {
|
if (Ox.contains(['epub', 'pdf', 'txt'], extension)) {
|
||||||
state.span = Ox.limit(parseInt(val), 1, dimensions);
|
state.span = Ox.limit(parseInt(val), 1, dimensions);
|
||||||
|
} else if (Ox.contains(['html'], extension)) {
|
||||||
|
state.span = Ox.limit(parseInt(val), 0, 100);
|
||||||
} else if (Ox.contains(['gif', 'jpg', 'png'], extension)) {
|
} else if (Ox.contains(['gif', 'jpg', 'png'], extension)) {
|
||||||
values = val.split(',');
|
values = val.split(',');
|
||||||
if (values.length == 4) {
|
if (values.length == 4) {
|
||||||
|
@ -1738,6 +1991,7 @@ pandora.getSpan = function(state, val, callback) {
|
||||||
state.span = '';
|
state.span = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ox.Log('URL', 'getSpan result', state);
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
} else if (state.type == pandora.site.itemName.plural.toLowerCase()) {
|
} else if (state.type == pandora.site.itemName.plural.toLowerCase()) {
|
||||||
|
@ -1817,24 +2071,6 @@ pandora.getSpan = function(state, val, callback) {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (state.type == 'texts') {
|
|
||||||
pandora.api.getText({id: state.item}, function(result) {
|
|
||||||
if (isArray) {
|
|
||||||
if (result.data.type == 'html') {
|
|
||||||
state.span = Ox.limit(val[0], 0, 100);
|
|
||||||
} else {
|
|
||||||
state.span = Math.floor(
|
|
||||||
Ox.limit(val[0], 1, result.data.pages)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
result.data.type == 'html'
|
|
||||||
&& Ox.contains(result.data.names, val)
|
|
||||||
) {
|
|
||||||
state.span = val;
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getId(type, callback) {
|
function getId(type, callback) {
|
||||||
|
@ -1898,6 +2134,9 @@ pandora.getStatusText = function(data) {
|
||||||
data.items == 1 ? 'singular' : 'plural'
|
data.items == 1 ? 'singular' : 'plural'
|
||||||
]),
|
]),
|
||||||
parts = [];
|
parts = [];
|
||||||
|
if (ui.section == 'documents') {
|
||||||
|
itemName = Ox._(Ox.toTitleCase(data.items == 1 ? ui.section.slice(0, -1) : ui.section));
|
||||||
|
}
|
||||||
parts.push(Ox.formatNumber(data.items) + ' '+ itemName);
|
parts.push(Ox.formatNumber(data.items) + ' '+ itemName);
|
||||||
if (data.runtime) {
|
if (data.runtime) {
|
||||||
parts.push(Ox.formatDuration(data.runtime, 'short'));
|
parts.push(Ox.formatDuration(data.runtime, 'short'));
|
||||||
|
@ -2263,6 +2502,8 @@ pandora.signin = function(data) {
|
||||||
pandora.user.ui._list = pandora.getListState(pandora.user.ui.find);
|
pandora.user.ui._list = pandora.getListState(pandora.user.ui.find);
|
||||||
pandora.user.ui._filterState = pandora.getFilterState(pandora.user.ui.find);
|
pandora.user.ui._filterState = pandora.getFilterState(pandora.user.ui.find);
|
||||||
pandora.user.ui._findState = pandora.getFindState(pandora.user.ui.find);
|
pandora.user.ui._findState = pandora.getFindState(pandora.user.ui.find);
|
||||||
|
pandora.user.ui._collection = pandora.getCollectionState(pandora.user.ui.findDocuments);
|
||||||
|
pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(pandora.user.ui.findDocuments);
|
||||||
pandora.site.sortKeys = pandora.getSortKeys();
|
pandora.site.sortKeys = pandora.getSortKeys();
|
||||||
pandora.URL.init();
|
pandora.URL.init();
|
||||||
pandora.URL.update();
|
pandora.URL.update();
|
||||||
|
@ -2277,6 +2518,8 @@ pandora.signout = function(data) {
|
||||||
pandora.user.ui._list = pandora.getListState(pandora.user.ui.find);
|
pandora.user.ui._list = pandora.getListState(pandora.user.ui.find);
|
||||||
pandora.user.ui._filterState = pandora.getFilterState(pandora.user.ui.find);
|
pandora.user.ui._filterState = pandora.getFilterState(pandora.user.ui.find);
|
||||||
pandora.user.ui._findState = pandora.getFindState(pandora.user.ui.find);
|
pandora.user.ui._findState = pandora.getFindState(pandora.user.ui.find);
|
||||||
|
pandora.user.ui._collection = pandora.getCollectionState(pandora.user.ui.findDocuments);
|
||||||
|
pandora.user.ui._findDocumentsState = pandora.getFindDocumentsState(pandora.user.ui.findDocuments);
|
||||||
pandora.site.sortKeys = pandora.getSortKeys();
|
pandora.site.sortKeys = pandora.getSortKeys();
|
||||||
pandora.URL.init();
|
pandora.URL.init();
|
||||||
pandora.URL.update();
|
pandora.URL.update();
|
||||||
|
@ -2288,9 +2531,11 @@ pandora.reloadList = function() {
|
||||||
Ox.Log('', 'reloadList')
|
Ox.Log('', 'reloadList')
|
||||||
var listData = pandora.getListData();
|
var listData = pandora.getListData();
|
||||||
Ox.Request.clearCache(); // fixme: remove
|
Ox.Request.clearCache(); // fixme: remove
|
||||||
pandora.$ui.filters.forEach(function($filter) {
|
if (pandora.$ui.filters) {
|
||||||
$filter.reloadList();
|
pandora.$ui.filters.forEach(function($filter) {
|
||||||
});
|
$filter.reloadList();
|
||||||
|
});
|
||||||
|
}
|
||||||
pandora.$ui.list
|
pandora.$ui.list
|
||||||
.bindEvent({
|
.bindEvent({
|
||||||
init: function(data) {
|
init: function(data) {
|
||||||
|
@ -2336,6 +2581,20 @@ pandora.renameList = function(oldId, newId, newName, folder) {
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
pandora.UI.set('lists.' + pandora.UI.encode(oldId), null, false);
|
pandora.UI.set('lists.' + pandora.UI.encode(oldId), null, false);
|
||||||
|
} else if (pandora.user.ui.section == 'documents') {
|
||||||
|
pandora.replaceURL = true;
|
||||||
|
pandora.UI.set(
|
||||||
|
'collections.' + pandora.UI.encode(newId),
|
||||||
|
pandora.user.ui.lists[oldId],
|
||||||
|
false
|
||||||
|
);
|
||||||
|
pandora.UI.set({
|
||||||
|
findDocuments: {
|
||||||
|
conditions: [{key: 'collection', value: newId, operator: '=='}],
|
||||||
|
operator: '&'
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
pandora.UI.set('collections.' + pandora.UI.encode(oldId), null, false);
|
||||||
} else {
|
} else {
|
||||||
pandora.replaceURL = true;
|
pandora.replaceURL = true;
|
||||||
pandora.UI.set(
|
pandora.UI.set(
|
||||||
|
@ -2380,7 +2639,7 @@ pandora.resizeFolders = function(section) {
|
||||||
userColumnWidth = Math.round(columnWidth * 0.4),
|
userColumnWidth = Math.round(columnWidth * 0.4),
|
||||||
nameColumnWidth = columnWidth - userColumnWidth;
|
nameColumnWidth = columnWidth - userColumnWidth;
|
||||||
pandora.$ui.allItems && pandora.$ui.allItems.resizeElement((
|
pandora.$ui.allItems && pandora.$ui.allItems.resizeElement((
|
||||||
section == 'items' ? columnWidth
|
Ox.contains(pandora.site.listSections, section) ? columnWidth
|
||||||
: section == 'edits' ? width - 16
|
: section == 'edits' ? width - 16
|
||||||
: width - 48
|
: width - 48
|
||||||
) - 8);
|
) - 8);
|
||||||
|
@ -2489,6 +2748,12 @@ pandora.resizeWindow = function() {
|
||||||
pandora.$ui.calendar.resizeCalendar();
|
pandora.$ui.calendar.resizeCalendar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (pandora.user.ui.section == 'documents') {
|
||||||
|
if (pandora.user.ui.document) {
|
||||||
|
pandora.$ui.document && pandora.$ui.document.update();
|
||||||
|
} else {
|
||||||
|
pandora.$ui.list && pandora.$ui.list.size();
|
||||||
|
}
|
||||||
} else if (pandora.user.ui.section == 'edits') {
|
} else if (pandora.user.ui.section == 'edits') {
|
||||||
if (!pandora.user.ui.edit) {
|
if (!pandora.user.ui.edit) {
|
||||||
// ...
|
// ...
|
||||||
|
@ -2537,6 +2802,38 @@ pandora.selectList = function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (pandora.user.ui.section == 'documents') {
|
||||||
|
if (pandora.user.ui._collection) {
|
||||||
|
pandora.api.findCollections({
|
||||||
|
keys: ['status', 'user'],
|
||||||
|
query: {
|
||||||
|
conditions: [{
|
||||||
|
key: 'id',
|
||||||
|
operator: '==',
|
||||||
|
value: pandora.user.ui._collection
|
||||||
|
}],
|
||||||
|
operator: ''
|
||||||
|
},
|
||||||
|
range: [0, 1]
|
||||||
|
}, function(result) {
|
||||||
|
var folder, list;
|
||||||
|
if (result.data.items.length) {
|
||||||
|
list = result.data.items[0];
|
||||||
|
folder = list.status == 'featured' ? 'featured' : (
|
||||||
|
list.user == pandora.user.username
|
||||||
|
? 'personal' : 'favorite'
|
||||||
|
);
|
||||||
|
pandora.$ui.folderList[folder]
|
||||||
|
.options({selected: [pandora.user.ui._collection]});
|
||||||
|
if (
|
||||||
|
!pandora.hasDialogOrScreen()
|
||||||
|
&& !Ox.Focus.focusedElementIsInput()
|
||||||
|
) {
|
||||||
|
pandora.$ui.folderList[folder].gainFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var id = pandora.user.ui[pandora.user.ui.section.slice(0,-1)],
|
var id = pandora.user.ui[pandora.user.ui.section.slice(0,-1)],
|
||||||
section = Ox.toTitleCase(pandora.user.ui.section.slice(0, -1));
|
section = Ox.toTitleCase(pandora.user.ui.section.slice(0, -1));
|
||||||
|
@ -2891,18 +3188,76 @@ pandora.wait = function(id, callback, timeout) {
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
function getState(find, key) {
|
||||||
pandora.getListState = function(find) {
|
|
||||||
// A list is selected if exactly one condition in an & query has "list"
|
|
||||||
// as key and "==" as operator
|
|
||||||
var index, state = '';
|
var index, state = '';
|
||||||
if (find.operator == '&') {
|
if (find.operator == '&') {
|
||||||
index = oneCondition(find.conditions, 'list', '==');
|
index = oneCondition(find.conditions, key, '==');
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
state = find.conditions[index].value;
|
state = find.conditions[index].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
pandora.getCollectionState = function(find) {
|
||||||
|
// A collection is selected if exactly one condition in an & query has "collection"
|
||||||
|
// as key and "==" as operator
|
||||||
|
return getState(find, 'collection');
|
||||||
|
};
|
||||||
|
|
||||||
|
pandora.getListState = function(find) {
|
||||||
|
// A list is selected if exactly one condition in an & query has "list"
|
||||||
|
// as key and "==" as operator
|
||||||
|
return getState(find, 'list');
|
||||||
|
};
|
||||||
|
|
||||||
|
pandora.getFindDocumentsState = function(find) {
|
||||||
|
// The find element is populated if exactly one condition in an & query
|
||||||
|
// has a findKey as key and "=" as operator (and all other conditions
|
||||||
|
// are either list or filters), or if all conditions in an | query have
|
||||||
|
// the same filter id as key and "==" as operator
|
||||||
|
Ox.Log('Find', 'getFindDocumentsState', find)
|
||||||
|
var conditions, indices, state = {index: -1, key: '*', value: ''};
|
||||||
|
if (find.operator == '&') {
|
||||||
|
// number of conditions that are not list or filters
|
||||||
|
conditions = find.conditions.length
|
||||||
|
- !!pandora.user.ui._collection;
|
||||||
|
/*
|
||||||
|
- pandora.user.ui._filterState.filter(function(filter) {
|
||||||
|
return filter.index > -1;
|
||||||
|
}).length;
|
||||||
|
*/
|
||||||
|
// indices of non-advanced find queries
|
||||||
|
indices = pandora.site.documentKeys.map(function(findKey) {
|
||||||
|
return oneCondition(find.conditions, findKey.id, '=');
|
||||||
|
}).filter(function(index) {
|
||||||
|
return index > -1;
|
||||||
|
});
|
||||||
|
state = conditions == 1 && indices.length == 1 ? {
|
||||||
|
index: indices[0],
|
||||||
|
key: find.conditions[indices[0]].key,
|
||||||
|
value: Ox.decodeURIComponent(find.conditions[indices[0]].value)
|
||||||
|
} : {
|
||||||
|
index: -1,
|
||||||
|
key: conditions == 0 && indices.length == 0 ? '*' : 'advanced',
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
state = {
|
||||||
|
index: -1,
|
||||||
|
key: 'advanced',
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
Ox.forEach(pandora.user.ui.documentFilters, function(key) {
|
||||||
|
if (everyCondition(find.conditions, key, '==')) {
|
||||||
|
state.key = '*';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|
BIN
static/png/cover.png
Normal file
BIN
static/png/cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
|
@ -216,6 +216,8 @@ if __name__ == "__main__":
|
||||||
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
run('./bin/pip', 'install', '-r', 'requirements.txt')
|
||||||
update_service('pandora-encoding')
|
update_service('pandora-encoding')
|
||||||
update_service('pandora-tasks')
|
update_service('pandora-tasks')
|
||||||
|
if old <= 5673:
|
||||||
|
run('./pandora/manage.py', 'rebuild_documentfind')
|
||||||
else:
|
else:
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
release = get_release()
|
release = get_release()
|
||||||
|
|
Loading…
Reference in a new issue