@oat-sa/tao-core-ui 3.23.0 → 3.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/figure/FigureStateActive.js +64 -32
- package/dist/image/ImgStateActive/initHelper.js +64 -32
- package/dist/image/ImgStateActive.js +64 -32
- package/dist/previewer.js +1 -1
- package/dist/resourcemgr/assetSearch.js +793 -0
- package/dist/resourcemgr/assetSearchContract.js +477 -0
- package/dist/resourcemgr/css/resourcemgr.css +1137 -97
- package/dist/resourcemgr/css/resourcemgr.css.map +1 -1
- package/dist/resourcemgr/fileBrowser.js +456 -79
- package/dist/resourcemgr/filePreview.js +40 -4
- package/dist/resourcemgr/fileSelector.js +362 -92
- package/dist/resourcemgr.js +309 -57
- package/dist/searchModal/advancedSearch.js +233 -31
- package/package.json +15 -6
- package/src/image/ImgStateActive/initHelper.js +69 -31
- package/src/previewer.js +1 -1
- package/src/resourcemgr/assetSearch.js +847 -0
- package/src/resourcemgr/assetSearchContract.js +489 -0
- package/src/resourcemgr/css/resourcemgr.css +1137 -97
- package/src/resourcemgr/css/resourcemgr.css.map +1 -1
- package/src/resourcemgr/fileBrowser.js +470 -91
- package/src/resourcemgr/filePreview.js +46 -4
- package/src/resourcemgr/fileSelector.js +338 -46
- package/src/resourcemgr/scss/resourcemgr.scss +1351 -88
- package/src/resourcemgr/tpl/advanced-search.tpl +10 -0
- package/src/resourcemgr/tpl/fileSelect.tpl +25 -28
- package/src/resourcemgr/tpl/folder.tpl +4 -1
- package/src/resourcemgr/tpl/layout.tpl +113 -54
- package/src/resourcemgr/tpl/rootFolder.tpl +3 -1
- package/src/resourcemgr.js +289 -47
- package/src/searchModal/advancedSearch.js +242 -31
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This program is free software; you can redistribute it and/or
|
|
3
|
+
* modify it under the terms of the GNU General Public License
|
|
4
|
+
* as published by the Free Software Foundation; under version 2
|
|
5
|
+
* of the License (non-upgradable).
|
|
6
|
+
*
|
|
7
|
+
* This program is distributed in the hope that it will be useful,
|
|
8
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10
|
+
* GNU General Public License for more details.
|
|
11
|
+
*
|
|
12
|
+
* You should have received a copy of the GNU General Public License
|
|
13
|
+
* along with this program; if not, write to the Free Software
|
|
14
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
15
|
+
*
|
|
16
|
+
* Copyright (c) 2018-2026 (original work) Open Assessment Technologies SA;
|
|
17
|
+
*/
|
|
1
18
|
import $ from 'jquery';
|
|
2
19
|
import _ from 'lodash';
|
|
3
20
|
import bytes from 'util/bytes';
|
|
@@ -5,6 +22,7 @@ import context from 'context';
|
|
|
5
22
|
import 'ui/previewer';
|
|
6
23
|
|
|
7
24
|
const ns = 'resourcemgr';
|
|
25
|
+
const EMPTY_META = '-';
|
|
8
26
|
|
|
9
27
|
export default function(options) {
|
|
10
28
|
const $container = options.$target;
|
|
@@ -43,11 +61,35 @@ export default function(options) {
|
|
|
43
61
|
$container.trigger(`select.${ns}`, [[data]]);
|
|
44
62
|
});
|
|
45
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @param {*} value
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function metaOrDash(value) {
|
|
69
|
+
if (value === null || typeof value === 'undefined' || value === '') {
|
|
70
|
+
return EMPTY_META;
|
|
71
|
+
}
|
|
72
|
+
return String(value);
|
|
73
|
+
}
|
|
74
|
+
|
|
46
75
|
function startPreview(file, preview, download, select) {
|
|
47
76
|
if (preview) {
|
|
48
77
|
$previewer.previewer(file);
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
if (file.type || file.mime) {
|
|
79
|
+
$propType.text(
|
|
80
|
+
file.type && file.mime ? `${file.type} (${file.mime})` : metaOrDash(file.type || file.mime)
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
$propType.text(EMPTY_META);
|
|
84
|
+
}
|
|
85
|
+
$propSize.text(
|
|
86
|
+
file.size === null || typeof file.size === 'undefined' || file.size === ''
|
|
87
|
+
? EMPTY_META
|
|
88
|
+
: bytes.hrSize(file.size)
|
|
89
|
+
);
|
|
90
|
+
} else {
|
|
91
|
+
$propType.text(EMPTY_META);
|
|
92
|
+
$propSize.text(EMPTY_META);
|
|
51
93
|
}
|
|
52
94
|
if(download) {
|
|
53
95
|
$link.attr('href', file.download).attr('download', file.file);
|
|
@@ -65,8 +107,8 @@ export default function(options) {
|
|
|
65
107
|
|
|
66
108
|
function stopPreview() {
|
|
67
109
|
$previewer.previewer('update', { url: false });
|
|
68
|
-
$propType.
|
|
69
|
-
$propSize.
|
|
110
|
+
$propType.text(EMPTY_META);
|
|
111
|
+
$propSize.text(EMPTY_META);
|
|
70
112
|
$('a', $propUrl).addClass('hidden');
|
|
71
113
|
$selectButton.attr('disabled', 'disabled');
|
|
72
114
|
}
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* You should have received a copy of the GNU General Public License
|
|
13
13
|
* along with this program; if not, write to the Free Software
|
|
14
|
-
* Foundation, Inc.,
|
|
14
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
15
15
|
*
|
|
16
|
-
* Copyright (c) 2015-
|
|
16
|
+
* Copyright (c) 2015-2026 (original work) Open Assessment Technologies SA;
|
|
17
17
|
*
|
|
18
18
|
*/
|
|
19
19
|
|
|
@@ -32,9 +32,11 @@ import context from 'context';
|
|
|
32
32
|
import 'ui/uploader';
|
|
33
33
|
import updatePermissions from './util/updatePermissions';
|
|
34
34
|
import loggerFactory from 'core/logger';
|
|
35
|
+
import { DEFAULT_SORT, sortAssetItems } from 'ui/resourcemgr/assetSearchContract';
|
|
35
36
|
|
|
36
37
|
const ns = 'resourcemgr';
|
|
37
38
|
const logger = loggerFactory(`ui/${ns}`);
|
|
39
|
+
const ROW_SELECTOR = '.files-list tr';
|
|
38
40
|
|
|
39
41
|
function shortenPath(path) {
|
|
40
42
|
let tokens = path.replace(/\/$/, '').split('/');
|
|
@@ -47,6 +49,70 @@ function shortenPath(path) {
|
|
|
47
49
|
return title.join('/');
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Normalize a catalog folder path for the Location column.
|
|
54
|
+
* Strips URI schemes and media-root labels (Assets/Media); keeps directory path only.
|
|
55
|
+
* @param {String} location
|
|
56
|
+
* @returns {String}
|
|
57
|
+
*/
|
|
58
|
+
function formatLocationDisplay(location) {
|
|
59
|
+
if (location === null || typeof location === 'undefined' || location === '') {
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
let path = String(location).trim().replace(/\\/g, '/');
|
|
63
|
+
if (!path) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Drop URI schemes (taomedia://…, https://…, asset://…)
|
|
68
|
+
path = path.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '');
|
|
69
|
+
// Residual host/path from http(s) after scheme strip
|
|
70
|
+
if (path.indexOf('/') > 0 && /^[^/]+\.[^/]+\//.test(path)) {
|
|
71
|
+
path = path.slice(path.indexOf('/'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const segments = path.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
|
75
|
+
if (!segments.length) {
|
|
76
|
+
return '/';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Directory path only: drop trailing filename-like segment
|
|
80
|
+
const last = segments[segments.length - 1];
|
|
81
|
+
if (segments.length > 1 && /\.[a-z0-9]{1,8}$/i.test(last)) {
|
|
82
|
+
segments.pop();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Strip media-tree root label prefix (Assets, Media, …)
|
|
86
|
+
const rootLabels = ['assets', 'media', 'mediamanager'];
|
|
87
|
+
if (segments.length && rootLabels.indexOf(segments[0].toLowerCase()) !== -1) {
|
|
88
|
+
segments.shift();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return segments.length ? `/${segments.join('/')}` : '/';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Format updatedAt as a fixed UTC date-time (YYYY-MM-DD HH:mm).
|
|
96
|
+
* @param {String} value
|
|
97
|
+
* @returns {String}
|
|
98
|
+
*/
|
|
99
|
+
function formatUpdatedAtUtc(value) {
|
|
100
|
+
if (!value) {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
const date = new Date(value);
|
|
104
|
+
if (Number.isNaN(date.getTime()) || date.getTime() <= 0) {
|
|
105
|
+
return '—';
|
|
106
|
+
}
|
|
107
|
+
const pad = function (n) {
|
|
108
|
+
return String(n).padStart(2, '0');
|
|
109
|
+
};
|
|
110
|
+
return (
|
|
111
|
+
`${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())} ` +
|
|
112
|
+
`${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
50
116
|
function isTextLarger($element, text) {
|
|
51
117
|
let $dummy = $element
|
|
52
118
|
.clone()
|
|
@@ -70,12 +136,21 @@ export default function (options) {
|
|
|
70
136
|
let disableUpload = options.disableUpload || false;
|
|
71
137
|
let $container = options.$target;
|
|
72
138
|
let $fileSelector = $('.file-selector', $container);
|
|
73
|
-
let $
|
|
139
|
+
let $filesWrapper = $('.files-wrapper', $fileSelector);
|
|
140
|
+
let $fileContainer = $('.files-list', $fileSelector);
|
|
74
141
|
let $placeholder = $('.empty', $fileSelector);
|
|
75
142
|
let $uploader = $('.file-upload-container', $fileSelector);
|
|
76
143
|
let parentSelector = `#${$container.attr('id')} .file-selector`;
|
|
77
144
|
let $pathTitle = $fileSelector.find('h1 > .title');
|
|
78
|
-
let $browserTitle = $('.file-browser > h1', $container);
|
|
145
|
+
let $browserTitle = $('.file-browser > h1.resources-title', $container);
|
|
146
|
+
if (!$browserTitle.length) {
|
|
147
|
+
$browserTitle = $('.file-browser > h1', $container);
|
|
148
|
+
}
|
|
149
|
+
let searchMode = false;
|
|
150
|
+
let initialSelectionApplied = false;
|
|
151
|
+
let pendingContextSelection = null;
|
|
152
|
+
let sort = Object.assign({}, DEFAULT_SORT);
|
|
153
|
+
let lastFiles = [];
|
|
79
154
|
|
|
80
155
|
//set up the uploader
|
|
81
156
|
if (disableUpload) {
|
|
@@ -84,16 +159,19 @@ export default function (options) {
|
|
|
84
159
|
} else {
|
|
85
160
|
setUpUploader(root);
|
|
86
161
|
}
|
|
162
|
+
|
|
163
|
+
$container.on(`searchmode.${ns}`, function (e, enabled) {
|
|
164
|
+
searchMode = !!enabled;
|
|
165
|
+
$fileSelector.toggleClass('search-mode', searchMode);
|
|
166
|
+
});
|
|
167
|
+
|
|
87
168
|
//update current folder
|
|
88
169
|
$container.on(`folderselect.${ns}`, function (e, fullPath, data, activePath, content) {
|
|
89
170
|
let files;
|
|
90
171
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
return dataItem;
|
|
96
|
-
});
|
|
172
|
+
if (searchMode) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
97
175
|
|
|
98
176
|
//update title
|
|
99
177
|
if ($container[0].querySelector('.upload')) {
|
|
@@ -108,47 +186,245 @@ export default function (options) {
|
|
|
108
186
|
|
|
109
187
|
//update content here
|
|
110
188
|
if (_.isArray(data)) {
|
|
111
|
-
files = _.filter(data, function (item) {
|
|
189
|
+
files = _.filter(data.map(ensurePermissions), function (item) {
|
|
112
190
|
return !!item.uri;
|
|
113
191
|
}).map(function (file) {
|
|
114
|
-
file
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
192
|
+
return prepareFileForDisplay(file, fullPath, activePath);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
updateFiles(files);
|
|
196
|
+
if (pendingContextSelection) {
|
|
197
|
+
const pending = pendingContextSelection;
|
|
198
|
+
initialSelectionApplied = false;
|
|
199
|
+
applyInitialSelection(pending, false);
|
|
200
|
+
if (initialSelectionApplied) {
|
|
201
|
+
pendingContextSelection = null;
|
|
119
202
|
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
applyInitialSelection();
|
|
206
|
+
}
|
|
207
|
+
});
|
|
120
208
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
209
|
+
// Render scoped search results from the service as-is (no client MIME/auth filtering).
|
|
210
|
+
$container.on(`searchresults.${ns}`, function (e, result) {
|
|
211
|
+
const items = (result && result.items) || [];
|
|
212
|
+
const files = items
|
|
213
|
+
.filter(function (item) {
|
|
214
|
+
return item && !!item.uri;
|
|
215
|
+
})
|
|
216
|
+
.map(function (file) {
|
|
217
|
+
return prepareFileForDisplay(ensureSearchPermissions(file), result.path || '', result.path);
|
|
126
218
|
});
|
|
127
219
|
|
|
128
|
-
|
|
220
|
+
$pathTitle.text(__('Search results'));
|
|
221
|
+
updateFiles(files, {
|
|
222
|
+
emptyMessage: result && result.emptyMessage
|
|
223
|
+
});
|
|
129
224
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
225
|
+
if (!(result && result.error)) {
|
|
226
|
+
applyInitialSelection(result && result.initialSelection);
|
|
133
227
|
}
|
|
134
228
|
});
|
|
135
229
|
|
|
230
|
+
// Reopen resolve (AC3): defer preselect until folderselect loads the resolved parent folder.
|
|
231
|
+
$container.on(`applycontext.${ns}`, function (e, ctx) {
|
|
232
|
+
initialSelectionApplied = false;
|
|
233
|
+
options.initialSelection = ctx && ctx.selection ? ctx.selection : null;
|
|
234
|
+
options.currentAssetItem = ctx && ctx.currentAssetItem ? ctx.currentAssetItem : null;
|
|
235
|
+
pendingContextSelection = options.initialSelection || null;
|
|
236
|
+
$fileContainer.find('tr.active').removeClass('active');
|
|
237
|
+
});
|
|
238
|
+
|
|
136
239
|
function injectTranscriptionMetadata(transcriptionUrl, metadataUri, resourceUri) {
|
|
137
240
|
return `${transcriptionUrl}?metadataUri=${encodeURIComponent(metadataUri)}&resourceUri=${
|
|
138
241
|
resourceUri.replace('taomedia://mediamanager/', '')
|
|
139
242
|
}`;
|
|
140
243
|
}
|
|
141
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Normalize ACL flags unless they are already a boolean permission map.
|
|
247
|
+
* @param {Object} item
|
|
248
|
+
* @returns {Object}
|
|
249
|
+
*/
|
|
250
|
+
function ensurePermissions(item) {
|
|
251
|
+
if (item.permissions && !Array.isArray(item.permissions) && typeof item.permissions === 'object') {
|
|
252
|
+
return item;
|
|
253
|
+
}
|
|
254
|
+
return updatePermissions(item);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Search results often omit ACL (indexed gateway). Missing/empty ACL must stay pickable
|
|
259
|
+
* (read/preview/download) without inventing write/delete/upload. Explicit ACL arrays and
|
|
260
|
+
* boolean maps are preserved / mapped via updatePermissions.
|
|
261
|
+
* @param {Object} item
|
|
262
|
+
* @returns {Object}
|
|
263
|
+
*/
|
|
264
|
+
function ensureSearchPermissions(item) {
|
|
265
|
+
if (!item) {
|
|
266
|
+
return item;
|
|
267
|
+
}
|
|
268
|
+
if (item.permissions && !Array.isArray(item.permissions) && typeof item.permissions === 'object') {
|
|
269
|
+
return item;
|
|
270
|
+
}
|
|
271
|
+
if (!item.permissions || (Array.isArray(item.permissions) && item.permissions.length === 0)) {
|
|
272
|
+
item.permissions = {
|
|
273
|
+
read: true,
|
|
274
|
+
write: false,
|
|
275
|
+
preview: true,
|
|
276
|
+
download: true,
|
|
277
|
+
upload: false,
|
|
278
|
+
delete: false
|
|
279
|
+
};
|
|
280
|
+
return item;
|
|
281
|
+
}
|
|
282
|
+
return updatePermissions(item);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Map a service file record to the selector display model.
|
|
287
|
+
* @param {Object} file
|
|
288
|
+
* @param {String} fullPath
|
|
289
|
+
* @param {String} [folderPath]
|
|
290
|
+
* @returns {Object}
|
|
291
|
+
*/
|
|
292
|
+
function prepareFileForDisplay(file, fullPath, folderPath) {
|
|
293
|
+
file.type = mimeType.getFileType(file);
|
|
294
|
+
if (typeof file.identifier === 'undefined') {
|
|
295
|
+
file.display = `${fullPath}/${file.name}`.replace('//', '/');
|
|
296
|
+
} else {
|
|
297
|
+
file.display = file.identifier + file.name;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
file.viewUrl = `${options.downloadUrl}?${$.param(options.params)}&${
|
|
301
|
+
options.pathParam || 'path'
|
|
302
|
+
}=${encodeURIComponent(file.uri)}`;
|
|
303
|
+
file.downloadUrl = `${file.viewUrl}&svgzsupport=true`;
|
|
304
|
+
if (!file.location) {
|
|
305
|
+
file.location = folderPath || fullPath || '';
|
|
306
|
+
}
|
|
307
|
+
if (!file.updatedAt && file.updated_at) {
|
|
308
|
+
file.updatedAt = file.updated_at;
|
|
309
|
+
}
|
|
310
|
+
file.locationDisplay = formatLocationDisplay(file.location);
|
|
311
|
+
file.updatedAtDisplay = formatUpdatedAtUtc(file.updatedAt);
|
|
312
|
+
return file;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Sync sort classes and aria-sort on table headers.
|
|
317
|
+
*/
|
|
318
|
+
function applySortHeader() {
|
|
319
|
+
$fileSelector.find('.files thead th[data-sort-by]').each(function () {
|
|
320
|
+
const $th = $(this);
|
|
321
|
+
const field = $th.attr('data-sort-by');
|
|
322
|
+
$th.removeClass('sorted sorted_asc sorted_desc');
|
|
323
|
+
if (field === sort.field) {
|
|
324
|
+
$th.addClass('sorted').addClass(sort.direction === 'desc' ? 'sorted_desc' : 'sorted_asc');
|
|
325
|
+
$th.attr('aria-sort', sort.direction === 'desc' ? 'descending' : 'ascending');
|
|
326
|
+
} else {
|
|
327
|
+
$th.attr('aria-sort', 'none');
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Apply a column click to sort state and notify listeners.
|
|
334
|
+
* Skip optimistic re-sort of lastFiles (current page only) — that flashed the
|
|
335
|
+
* wrong order before browse/search refetch; updateFiles still sorts the response.
|
|
336
|
+
* @param {String} field
|
|
337
|
+
*/
|
|
338
|
+
function changeSort(field) {
|
|
339
|
+
if (!field) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (field === sort.field) {
|
|
343
|
+
sort = {
|
|
344
|
+
field,
|
|
345
|
+
direction: sort.direction === 'asc' ? 'desc' : 'asc'
|
|
346
|
+
};
|
|
347
|
+
} else {
|
|
348
|
+
sort = {
|
|
349
|
+
field,
|
|
350
|
+
direction: 'asc'
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
applySortHeader();
|
|
354
|
+
$container.trigger(`sortchange.${ns}`, [Object.assign({}, sort)]);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Preselect initialSelection once when the matching row is present.
|
|
359
|
+
* When resolve returned a currentAssetItem missing from the page, inject it.
|
|
360
|
+
* @param {String} [selection]
|
|
361
|
+
* @param {Boolean} [allowInject=true] - inject currentAssetItem when missing (skip during pending reopen)
|
|
362
|
+
*/
|
|
363
|
+
function applyInitialSelection(selection, allowInject) {
|
|
364
|
+
const target = selection || options.initialSelection;
|
|
365
|
+
if (!target || initialSelectionApplied) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const mayInject = allowInject !== false;
|
|
369
|
+
let $item = $fileContainer.find('tr').filter(function () {
|
|
370
|
+
return $(this).attr('data-file') === String(target);
|
|
371
|
+
});
|
|
372
|
+
if (
|
|
373
|
+
!$item.length
|
|
374
|
+
&& mayInject
|
|
375
|
+
&& options.currentAssetItem
|
|
376
|
+
&& String(options.currentAssetItem.uri) === String(target)
|
|
377
|
+
) {
|
|
378
|
+
const injected = prepareFileForDisplay(
|
|
379
|
+
ensurePermissions(Object.assign({}, options.currentAssetItem)),
|
|
380
|
+
options.currentAssetItem.location || '',
|
|
381
|
+
options.currentAssetItem.location || ''
|
|
382
|
+
);
|
|
383
|
+
updateFiles([injected].concat(lastFiles || []));
|
|
384
|
+
$item = $fileContainer.find('tr').filter(function () {
|
|
385
|
+
return $(this).attr('data-file') === String(target);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
if ($item.length) {
|
|
389
|
+
initialSelectionApplied = true;
|
|
390
|
+
$item.first().trigger('click');
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
applySortHeader();
|
|
395
|
+
|
|
396
|
+
$(parentSelector)
|
|
397
|
+
.off('click.resourcemgr', '.files thead th[data-sort-by]')
|
|
398
|
+
.on('click.resourcemgr', '.files thead th[data-sort-by]', function (e) {
|
|
399
|
+
e.preventDefault();
|
|
400
|
+
e.stopImmediatePropagation();
|
|
401
|
+
changeSort($(this).attr('data-sort-by'));
|
|
402
|
+
})
|
|
403
|
+
.off('keydown.resourcemgr', '.files thead th[data-sort-by]')
|
|
404
|
+
.on('keydown.resourcemgr', '.files thead th[data-sort-by]', function (e) {
|
|
405
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
406
|
+
e.preventDefault();
|
|
407
|
+
e.stopImmediatePropagation();
|
|
408
|
+
changeSort($(this).attr('data-sort-by'));
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
142
412
|
//listen for file activation
|
|
143
413
|
$(parentSelector)
|
|
144
|
-
.off('click',
|
|
145
|
-
.on('click',
|
|
414
|
+
.off('click', ROW_SELECTOR)
|
|
415
|
+
.on('click', ROW_SELECTOR, function (e) {
|
|
416
|
+
if ($(e.target).closest('.row-actions').length) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if ($(e.target).closest('a.select, a.download, a.delete').length) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
146
422
|
const clickedItem = e.target;
|
|
147
423
|
if (clickedItem.hasAttribute('data-delete') || $(clickedItem).hasClass('icon-bin')) {
|
|
148
424
|
return;
|
|
149
425
|
}
|
|
150
426
|
let $selected = $(this);
|
|
151
|
-
let $files = $(
|
|
427
|
+
let $files = $(ROW_SELECTOR, $fileSelector);
|
|
152
428
|
let data = _.clone($selected.data());
|
|
153
429
|
if (
|
|
154
430
|
options.resourceMetadataUrl
|
|
@@ -168,10 +444,10 @@ export default function (options) {
|
|
|
168
444
|
|
|
169
445
|
//select a file
|
|
170
446
|
$(parentSelector)
|
|
171
|
-
.off('click',
|
|
172
|
-
.on('click',
|
|
447
|
+
.off('click', `${ROW_SELECTOR} a.select`)
|
|
448
|
+
.on('click', `${ROW_SELECTOR} a.select`, function (e) {
|
|
173
449
|
e.preventDefault();
|
|
174
|
-
let data = _.pick($(this).
|
|
450
|
+
let data = _.pick($(this).closest('tr').data(), ['file', 'type', 'mime', 'size', 'alt']);
|
|
175
451
|
if (context.mediaSources && context.mediaSources.length === 0 && data.file.indexOf('local/') > -1) {
|
|
176
452
|
data.file = data.file.substring(6);
|
|
177
453
|
}
|
|
@@ -180,8 +456,8 @@ export default function (options) {
|
|
|
180
456
|
|
|
181
457
|
//delete a file
|
|
182
458
|
$(parentSelector)
|
|
183
|
-
.off('click',
|
|
184
|
-
.on('click',
|
|
459
|
+
.off('click', `${ROW_SELECTOR} a.delete`)
|
|
460
|
+
.on('click', `${ROW_SELECTOR} a.delete`, function (e) {
|
|
185
461
|
// This function replaces ui/deleter and must follow the same logic.
|
|
186
462
|
// The main difference is that it insert a confirmation dialog before deleting the file.
|
|
187
463
|
e.preventDefault();
|
|
@@ -190,7 +466,7 @@ export default function (options) {
|
|
|
190
466
|
return;
|
|
191
467
|
}
|
|
192
468
|
|
|
193
|
-
const $target = $elt.closest('
|
|
469
|
+
const $target = $elt.closest('tr');
|
|
194
470
|
const path = $target.data('file');
|
|
195
471
|
const hooks = [];
|
|
196
472
|
|
|
@@ -219,7 +495,7 @@ export default function (options) {
|
|
|
219
495
|
params = {};
|
|
220
496
|
if (e.namespace === 'deleter' && $target.length) {
|
|
221
497
|
path = $target.data('file');
|
|
222
|
-
params[options.pathParam] = path;
|
|
498
|
+
params[options.pathParam || 'path'] = path;
|
|
223
499
|
$.getJSON(options.deleteUrl, _.merge(params, options.params), function (response) {
|
|
224
500
|
if (response.deleted) {
|
|
225
501
|
$container.trigger(`filedelete.${ns}`, [path]);
|
|
@@ -237,7 +513,9 @@ export default function (options) {
|
|
|
237
513
|
$container.trigger(`filenew.${ns}`, [result, currentPath]);
|
|
238
514
|
});
|
|
239
515
|
$uploader.on('fail.uploader', function (e, file, err) {
|
|
240
|
-
errors.push(
|
|
516
|
+
errors.push(
|
|
517
|
+
__('Unable to upload file %s : %s', _.escape(file && file.name), _.escape(err && err.message))
|
|
518
|
+
);
|
|
241
519
|
});
|
|
242
520
|
|
|
243
521
|
$uploader.on('end.uploader', function () {
|
|
@@ -253,11 +531,11 @@ export default function (options) {
|
|
|
253
531
|
$uploader.uploader({
|
|
254
532
|
upload: true,
|
|
255
533
|
multiple: true,
|
|
256
|
-
uploadUrl: `${options.uploadUrl}?${$.param(options.params)}&${options.pathParam}=${currentPath}`,
|
|
534
|
+
uploadUrl: `${options.uploadUrl}?${$.param(options.params)}&${options.pathParam || 'path'}=${currentPath}`,
|
|
257
535
|
fileSelect: function (files, done) {
|
|
258
536
|
let givenLength = files.length;
|
|
259
537
|
let fileNames = [];
|
|
260
|
-
$fileContainer.find('
|
|
538
|
+
$fileContainer.find('.desc').each(function () {
|
|
261
539
|
fileNames.push($(this).text().toLowerCase());
|
|
262
540
|
});
|
|
263
541
|
|
|
@@ -297,7 +575,7 @@ export default function (options) {
|
|
|
297
575
|
let pathParam = `${currentPath}/${file.name}`;
|
|
298
576
|
pathParam.replace('//', '/');
|
|
299
577
|
$.getJSON(
|
|
300
|
-
`${options.fileExistsUrl}?${$.param(options.params)}&${options.pathParam}=${pathParam}`,
|
|
578
|
+
`${options.fileExistsUrl}?${$.param(options.params)}&${options.pathParam || 'path'}=${pathParam}`,
|
|
301
579
|
function (response) {
|
|
302
580
|
if (response && response.exists === true) {
|
|
303
581
|
//eslint-disable-next-line no-alert
|
|
@@ -324,7 +602,7 @@ export default function (options) {
|
|
|
324
602
|
currentPath = uri;
|
|
325
603
|
$uploader.uploader('options', {
|
|
326
604
|
uploadUrl: `${options.uploadUrl}?${$.param(options.params)}&${
|
|
327
|
-
options.pathParam
|
|
605
|
+
options.pathParam || 'path'
|
|
328
606
|
}=${currentPath}&relPath=${currentPath}`
|
|
329
607
|
});
|
|
330
608
|
});
|
|
@@ -332,14 +610,18 @@ export default function (options) {
|
|
|
332
610
|
function setUploadMode(enableUploadMode) {
|
|
333
611
|
if (!enableUploadMode) {
|
|
334
612
|
isUploadMode = false;
|
|
613
|
+
$fileSelector.removeClass('upload-mode');
|
|
335
614
|
$uploader.hide();
|
|
615
|
+
$filesWrapper.show();
|
|
336
616
|
$fileContainer.show();
|
|
337
617
|
// Note: show() would display as inline, not inline-block!
|
|
338
618
|
$switcher.filter('.upload').css({ display: 'inline-block' });
|
|
339
619
|
$switcher.filter('.listing').hide();
|
|
340
|
-
$browserTitle.text(__('
|
|
620
|
+
$browserTitle.text(__('Resources'));
|
|
341
621
|
} else {
|
|
342
622
|
isUploadMode = true;
|
|
623
|
+
$fileSelector.addClass('upload-mode');
|
|
624
|
+
$filesWrapper.hide();
|
|
343
625
|
$fileContainer.hide();
|
|
344
626
|
$placeholder.hide();
|
|
345
627
|
$uploader.show();
|
|
@@ -363,17 +645,27 @@ export default function (options) {
|
|
|
363
645
|
setUploadMode(isUploadMode);
|
|
364
646
|
}
|
|
365
647
|
|
|
366
|
-
function updateFiles(
|
|
648
|
+
function updateFiles(files, updateOptions) {
|
|
649
|
+
const displayOptions = updateOptions || {};
|
|
650
|
+
const emptyLabel = displayOptions.emptyMessage || __('No files');
|
|
651
|
+
lastFiles = Array.isArray(files) ? files.slice() : [];
|
|
652
|
+
const sorted = sortAssetItems(lastFiles, sort);
|
|
367
653
|
$fileContainer.empty();
|
|
368
|
-
if (
|
|
654
|
+
if (sorted.length) {
|
|
369
655
|
$placeholder.hide();
|
|
656
|
+
$filesWrapper.show();
|
|
370
657
|
$fileContainer.append(
|
|
371
658
|
fileSelectTpl({
|
|
372
|
-
files:
|
|
659
|
+
files: sorted
|
|
373
660
|
})
|
|
374
661
|
);
|
|
375
|
-
} else if ($
|
|
376
|
-
$
|
|
662
|
+
} else if ($fileSelector.find('.asset-search-error:not([hidden])').length === 0) {
|
|
663
|
+
$filesWrapper.hide();
|
|
664
|
+
// Stylesheet defaults `.empty` to display:none; force visible for empty states.
|
|
665
|
+
$placeholder.text(emptyLabel).css('display', 'block');
|
|
666
|
+
} else {
|
|
667
|
+
$filesWrapper.hide();
|
|
668
|
+
$placeholder.hide();
|
|
377
669
|
}
|
|
378
670
|
}
|
|
379
671
|
}
|