@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,4 +1,4 @@
|
|
|
1
|
-
define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib/handlebars/helpers', 'ui/feedback', 'context', 'ui/uploader', 'core/logger'], function ($$1, _, async, __, mimeType, Handlebars, Helpers0, feedback, context, uploader, loggerFactory) { 'use strict';
|
|
1
|
+
define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib/handlebars/helpers', 'ui/feedback', 'context', 'ui/uploader', 'core/logger', 'ui/resourcemgr/assetSearchContract'], function ($$1, _, async, __, mimeType, Handlebars, Helpers0, feedback, context, uploader, loggerFactory, assetSearchContract) { 'use strict';
|
|
2
2
|
|
|
3
3
|
$$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
|
|
4
4
|
_ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
|
|
@@ -18,12 +18,12 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
18
18
|
var Template = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
|
|
19
19
|
this.compilerInfo = [4,'>= 1.0.0'];
|
|
20
20
|
helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
|
|
21
|
-
var buffer = "", stack1,
|
|
21
|
+
var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
|
|
22
22
|
|
|
23
23
|
function program1(depth0,data) {
|
|
24
24
|
|
|
25
25
|
var buffer = "", stack1, helper;
|
|
26
|
-
buffer += "\n <
|
|
26
|
+
buffer += "\n <tr data-type=\"";
|
|
27
27
|
if (helper = helpers.type) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
28
28
|
else { helper = (depth0 && depth0.type); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
29
29
|
buffer += escapeExpression(stack1)
|
|
@@ -48,93 +48,98 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
48
48
|
else { helper = (depth0 && depth0.viewUrl); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
49
49
|
buffer += escapeExpression(stack1)
|
|
50
50
|
+ "\"\n ";
|
|
51
|
-
stack1 = helpers['if'].call(depth0, (
|
|
51
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.location), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
|
|
52
52
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
53
53
|
buffer += "\n ";
|
|
54
|
-
stack1 = helpers['if'].call(depth0, (
|
|
54
|
+
stack1 = helpers['if'].call(depth0, (depth0 && depth0.updatedAt), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
|
|
55
55
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
56
56
|
buffer += "\n ";
|
|
57
|
-
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1.
|
|
57
|
+
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1.download), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
|
|
58
|
+
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
59
|
+
buffer += "\n ";
|
|
60
|
+
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1.preview), {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data});
|
|
61
|
+
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
62
|
+
buffer += "\n ";
|
|
63
|
+
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1.read), {hash:{},inverse:self.noop,fn:self.program(10, program10, data),data:data});
|
|
58
64
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
59
65
|
buffer += "\n data-alt=\"";
|
|
60
66
|
if (helper = helpers.alt) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
61
67
|
else { helper = (depth0 && depth0.alt); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
62
68
|
buffer += escapeExpression(stack1)
|
|
63
|
-
+ "\">\n <span class=\"desc truncate\">";
|
|
69
|
+
+ "\">\n <td class=\"files-label\">\n <span class=\"files-label-inner\">\n <span class=\"file-icon\" aria-hidden=\"true\"></span>\n <span class=\"desc truncate\">";
|
|
64
70
|
if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
65
71
|
else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
66
72
|
buffer += escapeExpression(stack1)
|
|
67
|
-
+ "</span>\n
|
|
68
|
-
|
|
73
|
+
+ "</span>\n </span>\n </td>\n <td class=\"files-location\">\n <span class=\"meta location truncate\" title=\"";
|
|
74
|
+
if (helper = helpers.locationDisplay) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
75
|
+
else { helper = (depth0 && depth0.locationDisplay); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
76
|
+
buffer += escapeExpression(stack1)
|
|
77
|
+
+ "\">";
|
|
78
|
+
if (helper = helpers.locationDisplay) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
79
|
+
else { helper = (depth0 && depth0.locationDisplay); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
80
|
+
buffer += escapeExpression(stack1)
|
|
81
|
+
+ "</span>\n </td>\n <td class=\"files-updated\">\n <span class=\"files-updated-inner\">\n <span class=\"meta updated truncate\" title=\"";
|
|
82
|
+
if (helper = helpers.updatedAtDisplay) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
83
|
+
else { helper = (depth0 && depth0.updatedAtDisplay); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
84
|
+
buffer += escapeExpression(stack1)
|
|
85
|
+
+ "\">";
|
|
86
|
+
if (helper = helpers.updatedAtDisplay) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
87
|
+
else { helper = (depth0 && depth0.updatedAtDisplay); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
88
|
+
buffer += escapeExpression(stack1)
|
|
89
|
+
+ "</span>\n <span class=\"row-actions\">\n ";
|
|
90
|
+
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1['delete']), {hash:{},inverse:self.noop,fn:self.program(12, program12, data),data:data});
|
|
69
91
|
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
70
|
-
buffer += "\n
|
|
92
|
+
buffer += "\n </span>\n </span>\n </td>\n </tr>\n";
|
|
71
93
|
return buffer;
|
|
72
94
|
}
|
|
73
95
|
function program2(depth0,data) {
|
|
74
96
|
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
var buffer = "", stack1, helper;
|
|
98
|
+
buffer += " data-location=\"";
|
|
99
|
+
if (helper = helpers.location) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
100
|
+
else { helper = (depth0 && depth0.location); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
101
|
+
buffer += escapeExpression(stack1)
|
|
102
|
+
+ "\" ";
|
|
103
|
+
return buffer;
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
function program4(depth0,data) {
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
var buffer = "", stack1, helper;
|
|
109
|
+
buffer += " data-updated=\"";
|
|
110
|
+
if (helper = helpers.updatedAt) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
111
|
+
else { helper = (depth0 && depth0.updatedAt); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
112
|
+
buffer += escapeExpression(stack1)
|
|
113
|
+
+ "\" ";
|
|
114
|
+
return buffer;
|
|
83
115
|
}
|
|
84
116
|
|
|
85
117
|
function program6(depth0,data) {
|
|
86
118
|
|
|
87
119
|
|
|
88
|
-
return " data-
|
|
120
|
+
return " data-download=\"true\" ";
|
|
89
121
|
}
|
|
90
122
|
|
|
91
123
|
function program8(depth0,data) {
|
|
92
124
|
|
|
93
|
-
var buffer = "", stack1;
|
|
94
|
-
buffer += "\n <span class=\"tlb-group\">\n ";
|
|
95
|
-
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1.read), {hash:{},inverse:self.noop,fn:self.program(9, program9, data),data:data});
|
|
96
|
-
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
97
|
-
buffer += "\n ";
|
|
98
|
-
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1.download), {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data});
|
|
99
|
-
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
100
|
-
buffer += "\n ";
|
|
101
|
-
stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.permissions)),stack1 == null || stack1 === false ? stack1 : stack1['delete']), {hash:{},inverse:self.noop,fn:self.program(13, program13, data),data:data});
|
|
102
|
-
if(stack1 || stack1 === 0) { buffer += stack1; }
|
|
103
|
-
buffer += "\n </span>\n ";
|
|
104
|
-
return buffer;
|
|
105
|
-
}
|
|
106
|
-
function program9(depth0,data) {
|
|
107
125
|
|
|
108
|
-
|
|
109
|
-
buffer += "\n <a href=\"#\" class=\"tlb-button-off select\" title=\""
|
|
110
|
-
+ escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Select this file", options) : helperMissing.call(depth0, "__", "Select this file", options)))
|
|
111
|
-
+ "\"><span class=\"icon-move-item\"></span></a>\n ";
|
|
112
|
-
return buffer;
|
|
126
|
+
return " data-preview=\"true\" ";
|
|
113
127
|
}
|
|
114
128
|
|
|
115
|
-
function
|
|
129
|
+
function program10(depth0,data) {
|
|
116
130
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (helper = helpers.downloadUrl) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
120
|
-
else { helper = (depth0 && depth0.downloadUrl); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
121
|
-
buffer += escapeExpression(stack1)
|
|
122
|
-
+ "\" download=\"";
|
|
123
|
-
if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
|
|
124
|
-
else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
|
|
125
|
-
buffer += escapeExpression(stack1)
|
|
126
|
-
+ "\" target=\"_blank\" class=\"tlb-button-off download\" title=\""
|
|
127
|
-
+ escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Download this file", options) : helperMissing.call(depth0, "__", "Download this file", options)))
|
|
128
|
-
+ "\"><span class=\"icon-download\"></span></a>\n ";
|
|
129
|
-
return buffer;
|
|
131
|
+
|
|
132
|
+
return " data-select=\"true\" ";
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
function
|
|
135
|
+
function program12(depth0,data) {
|
|
133
136
|
|
|
134
137
|
var buffer = "", helper, options;
|
|
135
|
-
buffer += "\n
|
|
138
|
+
buffer += "\n <a href=\"#\" class=\"delete\" title=\""
|
|
136
139
|
+ escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Remove this file", options) : helperMissing.call(depth0, "__", "Remove this file", options)))
|
|
137
|
-
+ "\"
|
|
140
|
+
+ "\" aria-label=\""
|
|
141
|
+
+ escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Remove this file", options) : helperMissing.call(depth0, "__", "Remove this file", options)))
|
|
142
|
+
+ "\">\n <span class=\"icon-bin\" aria-hidden=\"true\"></span>\n </a>\n ";
|
|
138
143
|
return buffer;
|
|
139
144
|
}
|
|
140
145
|
|
|
@@ -213,13 +218,14 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
213
218
|
*
|
|
214
219
|
* You should have received a copy of the GNU General Public License
|
|
215
220
|
* along with this program; if not, write to the Free Software
|
|
216
|
-
* Foundation, Inc.,
|
|
221
|
+
* Foundation, Inc., 31 Milk St # 960789 Boston, MA 02196 USA.
|
|
217
222
|
*
|
|
218
|
-
* Copyright (c) 2015-
|
|
223
|
+
* Copyright (c) 2015-2026 (original work) Open Assessment Technologies SA;
|
|
219
224
|
*
|
|
220
225
|
*/
|
|
221
226
|
const ns = 'resourcemgr';
|
|
222
227
|
const logger = loggerFactory(`ui/${ns}`);
|
|
228
|
+
const ROW_SELECTOR = '.files-list tr';
|
|
223
229
|
function shortenPath(path) {
|
|
224
230
|
let tokens = path.replace(/\/$/, '').split('/');
|
|
225
231
|
let start = tokens.length - 3;
|
|
@@ -230,6 +236,65 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
230
236
|
title = title.filter(Boolean);
|
|
231
237
|
return title.join('/');
|
|
232
238
|
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Normalize a catalog folder path for the Location column.
|
|
242
|
+
* Strips URI schemes and media-root labels (Assets/Media); keeps directory path only.
|
|
243
|
+
* @param {String} location
|
|
244
|
+
* @returns {String}
|
|
245
|
+
*/
|
|
246
|
+
function formatLocationDisplay(location) {
|
|
247
|
+
if (location === null || typeof location === 'undefined' || location === '') {
|
|
248
|
+
return '';
|
|
249
|
+
}
|
|
250
|
+
let path = String(location).trim().replace(/\\/g, '/');
|
|
251
|
+
if (!path) {
|
|
252
|
+
return '';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Drop URI schemes (taomedia://…, https://…, asset://…)
|
|
256
|
+
path = path.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '');
|
|
257
|
+
// Residual host/path from http(s) after scheme strip
|
|
258
|
+
if (path.indexOf('/') > 0 && /^[^/]+\.[^/]+\//.test(path)) {
|
|
259
|
+
path = path.slice(path.indexOf('/'));
|
|
260
|
+
}
|
|
261
|
+
const segments = path.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
|
262
|
+
if (!segments.length) {
|
|
263
|
+
return '/';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Directory path only: drop trailing filename-like segment
|
|
267
|
+
const last = segments[segments.length - 1];
|
|
268
|
+
if (segments.length > 1 && /\.[a-z0-9]{1,8}$/i.test(last)) {
|
|
269
|
+
segments.pop();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Strip media-tree root label prefix (Assets, Media, …)
|
|
273
|
+
const rootLabels = ['assets', 'media', 'mediamanager'];
|
|
274
|
+
if (segments.length && rootLabels.indexOf(segments[0].toLowerCase()) !== -1) {
|
|
275
|
+
segments.shift();
|
|
276
|
+
}
|
|
277
|
+
return segments.length ? `/${segments.join('/')}` : '/';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Format updatedAt as a fixed UTC date-time (YYYY-MM-DD HH:mm).
|
|
282
|
+
* @param {String} value
|
|
283
|
+
* @returns {String}
|
|
284
|
+
*/
|
|
285
|
+
function formatUpdatedAtUtc(value) {
|
|
286
|
+
if (!value) {
|
|
287
|
+
return '';
|
|
288
|
+
}
|
|
289
|
+
const date = new Date(value);
|
|
290
|
+
if (Number.isNaN(date.getTime()) || date.getTime() <= 0) {
|
|
291
|
+
return '—';
|
|
292
|
+
}
|
|
293
|
+
const pad = function (n) {
|
|
294
|
+
return String(n).padStart(2, '0');
|
|
295
|
+
};
|
|
296
|
+
return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())} ` + `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}`;
|
|
297
|
+
}
|
|
233
298
|
function isTextLarger($element, text) {
|
|
234
299
|
let $dummy = $element.clone().detach().css({
|
|
235
300
|
position: 'absolute',
|
|
@@ -246,12 +311,21 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
246
311
|
let disableUpload = options.disableUpload || false;
|
|
247
312
|
let $container = options.$target;
|
|
248
313
|
let $fileSelector = $$1('.file-selector', $container);
|
|
249
|
-
let $
|
|
314
|
+
let $filesWrapper = $$1('.files-wrapper', $fileSelector);
|
|
315
|
+
let $fileContainer = $$1('.files-list', $fileSelector);
|
|
250
316
|
let $placeholder = $$1('.empty', $fileSelector);
|
|
251
317
|
let $uploader = $$1('.file-upload-container', $fileSelector);
|
|
252
318
|
let parentSelector = `#${$container.attr('id')} .file-selector`;
|
|
253
319
|
let $pathTitle = $fileSelector.find('h1 > .title');
|
|
254
|
-
let $browserTitle = $$1('.file-browser > h1', $container);
|
|
320
|
+
let $browserTitle = $$1('.file-browser > h1.resources-title', $container);
|
|
321
|
+
if (!$browserTitle.length) {
|
|
322
|
+
$browserTitle = $$1('.file-browser > h1', $container);
|
|
323
|
+
}
|
|
324
|
+
let searchMode = false;
|
|
325
|
+
let initialSelectionApplied = false;
|
|
326
|
+
let pendingContextSelection = null;
|
|
327
|
+
let sort = Object.assign({}, assetSearchContract.DEFAULT_SORT);
|
|
328
|
+
let lastFiles = [];
|
|
255
329
|
|
|
256
330
|
//set up the uploader
|
|
257
331
|
if (disableUpload) {
|
|
@@ -260,15 +334,17 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
260
334
|
} else {
|
|
261
335
|
setUpUploader(root);
|
|
262
336
|
}
|
|
337
|
+
$container.on(`searchmode.${ns}`, function (e, enabled) {
|
|
338
|
+
searchMode = !!enabled;
|
|
339
|
+
$fileSelector.toggleClass('search-mode', searchMode);
|
|
340
|
+
});
|
|
341
|
+
|
|
263
342
|
//update current folder
|
|
264
343
|
$container.on(`folderselect.${ns}`, function (e, fullPath, data, activePath, content) {
|
|
265
344
|
let files;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
return dataItem;
|
|
271
|
-
});
|
|
345
|
+
if (searchMode) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
272
348
|
|
|
273
349
|
//update title
|
|
274
350
|
if ($container[0].querySelector('.upload')) {
|
|
@@ -282,37 +358,217 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
282
358
|
|
|
283
359
|
//update content here
|
|
284
360
|
if (_.isArray(data)) {
|
|
285
|
-
files = _.filter(data, function (item) {
|
|
361
|
+
files = _.filter(data.map(ensurePermissions), function (item) {
|
|
286
362
|
return !!item.uri;
|
|
287
363
|
}).map(function (file) {
|
|
288
|
-
file
|
|
289
|
-
if (typeof file.identifier === 'undefined') {
|
|
290
|
-
file.display = `${fullPath}/${file.name}`.replace('//', '/');
|
|
291
|
-
} else {
|
|
292
|
-
file.display = file.identifier + file.name;
|
|
293
|
-
}
|
|
294
|
-
file.viewUrl = `${options.downloadUrl}?${$$1.param(options.params)}&${options.pathParam}=${encodeURIComponent(file.uri)}`;
|
|
295
|
-
file.downloadUrl = `${file.viewUrl}&svgzsupport=true`;
|
|
296
|
-
return file;
|
|
364
|
+
return prepareFileForDisplay(file, fullPath, activePath);
|
|
297
365
|
});
|
|
298
|
-
updateFiles(
|
|
299
|
-
if (
|
|
300
|
-
|
|
366
|
+
updateFiles(files);
|
|
367
|
+
if (pendingContextSelection) {
|
|
368
|
+
const pending = pendingContextSelection;
|
|
369
|
+
initialSelectionApplied = false;
|
|
370
|
+
applyInitialSelection(pending, false);
|
|
371
|
+
if (initialSelectionApplied) {
|
|
372
|
+
pendingContextSelection = null;
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
301
375
|
}
|
|
376
|
+
applyInitialSelection();
|
|
302
377
|
}
|
|
303
378
|
});
|
|
379
|
+
|
|
380
|
+
// Render scoped search results from the service as-is (no client MIME/auth filtering).
|
|
381
|
+
$container.on(`searchresults.${ns}`, function (e, result) {
|
|
382
|
+
const items = result && result.items || [];
|
|
383
|
+
const files = items.filter(function (item) {
|
|
384
|
+
return item && !!item.uri;
|
|
385
|
+
}).map(function (file) {
|
|
386
|
+
return prepareFileForDisplay(ensureSearchPermissions(file), result.path || '', result.path);
|
|
387
|
+
});
|
|
388
|
+
$pathTitle.text(__('Search results'));
|
|
389
|
+
updateFiles(files, {
|
|
390
|
+
emptyMessage: result && result.emptyMessage
|
|
391
|
+
});
|
|
392
|
+
if (!(result && result.error)) {
|
|
393
|
+
applyInitialSelection(result && result.initialSelection);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Reopen resolve (AC3): defer preselect until folderselect loads the resolved parent folder.
|
|
398
|
+
$container.on(`applycontext.${ns}`, function (e, ctx) {
|
|
399
|
+
initialSelectionApplied = false;
|
|
400
|
+
options.initialSelection = ctx && ctx.selection ? ctx.selection : null;
|
|
401
|
+
options.currentAssetItem = ctx && ctx.currentAssetItem ? ctx.currentAssetItem : null;
|
|
402
|
+
pendingContextSelection = options.initialSelection || null;
|
|
403
|
+
$fileContainer.find('tr.active').removeClass('active');
|
|
404
|
+
});
|
|
304
405
|
function injectTranscriptionMetadata(transcriptionUrl, metadataUri, resourceUri) {
|
|
305
406
|
return `${transcriptionUrl}?metadataUri=${encodeURIComponent(metadataUri)}&resourceUri=${resourceUri.replace('taomedia://mediamanager/', '')}`;
|
|
306
407
|
}
|
|
307
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Normalize ACL flags unless they are already a boolean permission map.
|
|
411
|
+
* @param {Object} item
|
|
412
|
+
* @returns {Object}
|
|
413
|
+
*/
|
|
414
|
+
function ensurePermissions(item) {
|
|
415
|
+
if (item.permissions && !Array.isArray(item.permissions) && typeof item.permissions === 'object') {
|
|
416
|
+
return item;
|
|
417
|
+
}
|
|
418
|
+
return updatePermissions(item);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Search results often omit ACL (indexed gateway). Missing/empty ACL must stay pickable
|
|
423
|
+
* (read/preview/download) without inventing write/delete/upload. Explicit ACL arrays and
|
|
424
|
+
* boolean maps are preserved / mapped via updatePermissions.
|
|
425
|
+
* @param {Object} item
|
|
426
|
+
* @returns {Object}
|
|
427
|
+
*/
|
|
428
|
+
function ensureSearchPermissions(item) {
|
|
429
|
+
if (!item) {
|
|
430
|
+
return item;
|
|
431
|
+
}
|
|
432
|
+
if (item.permissions && !Array.isArray(item.permissions) && typeof item.permissions === 'object') {
|
|
433
|
+
return item;
|
|
434
|
+
}
|
|
435
|
+
if (!item.permissions || Array.isArray(item.permissions) && item.permissions.length === 0) {
|
|
436
|
+
item.permissions = {
|
|
437
|
+
read: true,
|
|
438
|
+
write: false,
|
|
439
|
+
preview: true,
|
|
440
|
+
download: true,
|
|
441
|
+
upload: false,
|
|
442
|
+
delete: false
|
|
443
|
+
};
|
|
444
|
+
return item;
|
|
445
|
+
}
|
|
446
|
+
return updatePermissions(item);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Map a service file record to the selector display model.
|
|
451
|
+
* @param {Object} file
|
|
452
|
+
* @param {String} fullPath
|
|
453
|
+
* @param {String} [folderPath]
|
|
454
|
+
* @returns {Object}
|
|
455
|
+
*/
|
|
456
|
+
function prepareFileForDisplay(file, fullPath, folderPath) {
|
|
457
|
+
file.type = mimeType.getFileType(file);
|
|
458
|
+
if (typeof file.identifier === 'undefined') {
|
|
459
|
+
file.display = `${fullPath}/${file.name}`.replace('//', '/');
|
|
460
|
+
} else {
|
|
461
|
+
file.display = file.identifier + file.name;
|
|
462
|
+
}
|
|
463
|
+
file.viewUrl = `${options.downloadUrl}?${$$1.param(options.params)}&${options.pathParam || 'path'}=${encodeURIComponent(file.uri)}`;
|
|
464
|
+
file.downloadUrl = `${file.viewUrl}&svgzsupport=true`;
|
|
465
|
+
if (!file.location) {
|
|
466
|
+
file.location = folderPath || fullPath || '';
|
|
467
|
+
}
|
|
468
|
+
if (!file.updatedAt && file.updated_at) {
|
|
469
|
+
file.updatedAt = file.updated_at;
|
|
470
|
+
}
|
|
471
|
+
file.locationDisplay = formatLocationDisplay(file.location);
|
|
472
|
+
file.updatedAtDisplay = formatUpdatedAtUtc(file.updatedAt);
|
|
473
|
+
return file;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Sync sort classes and aria-sort on table headers.
|
|
478
|
+
*/
|
|
479
|
+
function applySortHeader() {
|
|
480
|
+
$fileSelector.find('.files thead th[data-sort-by]').each(function () {
|
|
481
|
+
const $th = $$1(this);
|
|
482
|
+
const field = $th.attr('data-sort-by');
|
|
483
|
+
$th.removeClass('sorted sorted_asc sorted_desc');
|
|
484
|
+
if (field === sort.field) {
|
|
485
|
+
$th.addClass('sorted').addClass(sort.direction === 'desc' ? 'sorted_desc' : 'sorted_asc');
|
|
486
|
+
$th.attr('aria-sort', sort.direction === 'desc' ? 'descending' : 'ascending');
|
|
487
|
+
} else {
|
|
488
|
+
$th.attr('aria-sort', 'none');
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Apply a column click to sort state and notify listeners.
|
|
495
|
+
* Skip optimistic re-sort of lastFiles (current page only) — that flashed the
|
|
496
|
+
* wrong order before browse/search refetch; updateFiles still sorts the response.
|
|
497
|
+
* @param {String} field
|
|
498
|
+
*/
|
|
499
|
+
function changeSort(field) {
|
|
500
|
+
if (!field) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (field === sort.field) {
|
|
504
|
+
sort = {
|
|
505
|
+
field,
|
|
506
|
+
direction: sort.direction === 'asc' ? 'desc' : 'asc'
|
|
507
|
+
};
|
|
508
|
+
} else {
|
|
509
|
+
sort = {
|
|
510
|
+
field,
|
|
511
|
+
direction: 'asc'
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
applySortHeader();
|
|
515
|
+
$container.trigger(`sortchange.${ns}`, [Object.assign({}, sort)]);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Preselect initialSelection once when the matching row is present.
|
|
520
|
+
* When resolve returned a currentAssetItem missing from the page, inject it.
|
|
521
|
+
* @param {String} [selection]
|
|
522
|
+
* @param {Boolean} [allowInject=true] - inject currentAssetItem when missing (skip during pending reopen)
|
|
523
|
+
*/
|
|
524
|
+
function applyInitialSelection(selection, allowInject) {
|
|
525
|
+
const target = selection || options.initialSelection;
|
|
526
|
+
if (!target || initialSelectionApplied) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const mayInject = allowInject !== false;
|
|
530
|
+
let $item = $fileContainer.find('tr').filter(function () {
|
|
531
|
+
return $$1(this).attr('data-file') === String(target);
|
|
532
|
+
});
|
|
533
|
+
if (!$item.length && mayInject && options.currentAssetItem && String(options.currentAssetItem.uri) === String(target)) {
|
|
534
|
+
const injected = prepareFileForDisplay(ensurePermissions(Object.assign({}, options.currentAssetItem)), options.currentAssetItem.location || '', options.currentAssetItem.location || '');
|
|
535
|
+
updateFiles([injected].concat(lastFiles || []));
|
|
536
|
+
$item = $fileContainer.find('tr').filter(function () {
|
|
537
|
+
return $$1(this).attr('data-file') === String(target);
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
if ($item.length) {
|
|
541
|
+
initialSelectionApplied = true;
|
|
542
|
+
$item.first().trigger('click');
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
applySortHeader();
|
|
546
|
+
$$1(parentSelector).off('click.resourcemgr', '.files thead th[data-sort-by]').on('click.resourcemgr', '.files thead th[data-sort-by]', function (e) {
|
|
547
|
+
e.preventDefault();
|
|
548
|
+
e.stopImmediatePropagation();
|
|
549
|
+
changeSort($$1(this).attr('data-sort-by'));
|
|
550
|
+
}).off('keydown.resourcemgr', '.files thead th[data-sort-by]').on('keydown.resourcemgr', '.files thead th[data-sort-by]', function (e) {
|
|
551
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
552
|
+
e.preventDefault();
|
|
553
|
+
e.stopImmediatePropagation();
|
|
554
|
+
changeSort($$1(this).attr('data-sort-by'));
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
308
558
|
//listen for file activation
|
|
309
|
-
$$1(parentSelector).off('click',
|
|
559
|
+
$$1(parentSelector).off('click', ROW_SELECTOR).on('click', ROW_SELECTOR, function (e) {
|
|
560
|
+
if ($$1(e.target).closest('.row-actions').length) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if ($$1(e.target).closest('a.select, a.download, a.delete').length) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
310
566
|
const clickedItem = e.target;
|
|
311
567
|
if (clickedItem.hasAttribute('data-delete') || $$1(clickedItem).hasClass('icon-bin')) {
|
|
312
568
|
return;
|
|
313
569
|
}
|
|
314
570
|
let $selected = $$1(this);
|
|
315
|
-
let $files = $$1(
|
|
571
|
+
let $files = $$1(ROW_SELECTOR, $fileSelector);
|
|
316
572
|
let data = _.clone($selected.data());
|
|
317
573
|
if (options.resourceMetadataUrl && options.transcriptionMetadata && data.file.includes('taomedia://mediamanager/')) {
|
|
318
574
|
data.transcriptionUrl = injectTranscriptionMetadata(options.resourceMetadataUrl, options.transcriptionMetadata, data.file);
|
|
@@ -323,9 +579,9 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
323
579
|
});
|
|
324
580
|
|
|
325
581
|
//select a file
|
|
326
|
-
$$1(parentSelector).off('click',
|
|
582
|
+
$$1(parentSelector).off('click', `${ROW_SELECTOR} a.select`).on('click', `${ROW_SELECTOR} a.select`, function (e) {
|
|
327
583
|
e.preventDefault();
|
|
328
|
-
let data = _.pick($$1(this).
|
|
584
|
+
let data = _.pick($$1(this).closest('tr').data(), ['file', 'type', 'mime', 'size', 'alt']);
|
|
329
585
|
if (context.mediaSources && context.mediaSources.length === 0 && data.file.indexOf('local/') > -1) {
|
|
330
586
|
data.file = data.file.substring(6);
|
|
331
587
|
}
|
|
@@ -333,7 +589,7 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
333
589
|
});
|
|
334
590
|
|
|
335
591
|
//delete a file
|
|
336
|
-
$$1(parentSelector).off('click',
|
|
592
|
+
$$1(parentSelector).off('click', `${ROW_SELECTOR} a.delete`).on('click', `${ROW_SELECTOR} a.delete`, function (e) {
|
|
337
593
|
// This function replaces ui/deleter and must follow the same logic.
|
|
338
594
|
// The main difference is that it insert a confirmation dialog before deleting the file.
|
|
339
595
|
e.preventDefault();
|
|
@@ -341,7 +597,7 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
341
597
|
if ($elt.hasClass(options.disableClass)) {
|
|
342
598
|
return;
|
|
343
599
|
}
|
|
344
|
-
const $target = $elt.closest('
|
|
600
|
+
const $target = $elt.closest('tr');
|
|
345
601
|
const path = $target.data('file');
|
|
346
602
|
const hooks = [];
|
|
347
603
|
if (options.hooks && 'function' === typeof options.hooks.deleteFile) {
|
|
@@ -364,7 +620,7 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
364
620
|
params = {};
|
|
365
621
|
if (e.namespace === 'deleter' && $target.length) {
|
|
366
622
|
path = $target.data('file');
|
|
367
|
-
params[options.pathParam] = path;
|
|
623
|
+
params[options.pathParam || 'path'] = path;
|
|
368
624
|
$$1.getJSON(options.deleteUrl, _.merge(params, options.params), function (response) {
|
|
369
625
|
if (response.deleted) {
|
|
370
626
|
$container.trigger(`filedelete.${ns}`, [path]);
|
|
@@ -380,7 +636,7 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
380
636
|
$container.trigger(`filenew.${ns}`, [result, currentPath]);
|
|
381
637
|
});
|
|
382
638
|
$uploader.on('fail.uploader', function (e, file, err) {
|
|
383
|
-
errors.push(__('Unable to upload file %s : %s', file.name, err.message));
|
|
639
|
+
errors.push(__('Unable to upload file %s : %s', _.escape(file && file.name), _.escape(err && err.message)));
|
|
384
640
|
});
|
|
385
641
|
$uploader.on('end.uploader', function () {
|
|
386
642
|
if (errors.length === 0) {
|
|
@@ -396,11 +652,11 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
396
652
|
$uploader.uploader({
|
|
397
653
|
upload: true,
|
|
398
654
|
multiple: true,
|
|
399
|
-
uploadUrl: `${options.uploadUrl}?${$$1.param(options.params)}&${options.pathParam}=${currentPath}`,
|
|
655
|
+
uploadUrl: `${options.uploadUrl}?${$$1.param(options.params)}&${options.pathParam || 'path'}=${currentPath}`,
|
|
400
656
|
fileSelect: function (files, done) {
|
|
401
657
|
let givenLength = files.length;
|
|
402
658
|
let fileNames = [];
|
|
403
|
-
$fileContainer.find('
|
|
659
|
+
$fileContainer.find('.desc').each(function () {
|
|
404
660
|
fileNames.push($$1(this).text().toLowerCase());
|
|
405
661
|
});
|
|
406
662
|
|
|
@@ -434,7 +690,7 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
434
690
|
if (options.fileExistsUrl) {
|
|
435
691
|
let pathParam = `${currentPath}/${file.name}`;
|
|
436
692
|
pathParam.replace('//', '/');
|
|
437
|
-
$$1.getJSON(`${options.fileExistsUrl}?${$$1.param(options.params)}&${options.pathParam}=${pathParam}`, function (response) {
|
|
693
|
+
$$1.getJSON(`${options.fileExistsUrl}?${$$1.param(options.params)}&${options.pathParam || 'path'}=${pathParam}`, function (response) {
|
|
438
694
|
if (response && response.exists === true) {
|
|
439
695
|
//eslint-disable-next-line no-alert
|
|
440
696
|
result = window.confirm(__('Do you want to override "%s"?', file.name));
|
|
@@ -455,22 +711,26 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
455
711
|
$container.on(`folderselect.${ns}`, function (e, fullPath, data, uri) {
|
|
456
712
|
currentPath = uri;
|
|
457
713
|
$uploader.uploader('options', {
|
|
458
|
-
uploadUrl: `${options.uploadUrl}?${$$1.param(options.params)}&${options.pathParam}=${currentPath}&relPath=${currentPath}`
|
|
714
|
+
uploadUrl: `${options.uploadUrl}?${$$1.param(options.params)}&${options.pathParam || 'path'}=${currentPath}&relPath=${currentPath}`
|
|
459
715
|
});
|
|
460
716
|
});
|
|
461
717
|
function setUploadMode(enableUploadMode) {
|
|
462
718
|
if (!enableUploadMode) {
|
|
463
719
|
isUploadMode = false;
|
|
720
|
+
$fileSelector.removeClass('upload-mode');
|
|
464
721
|
$uploader.hide();
|
|
722
|
+
$filesWrapper.show();
|
|
465
723
|
$fileContainer.show();
|
|
466
724
|
// Note: show() would display as inline, not inline-block!
|
|
467
725
|
$switcher.filter('.upload').css({
|
|
468
726
|
display: 'inline-block'
|
|
469
727
|
});
|
|
470
728
|
$switcher.filter('.listing').hide();
|
|
471
|
-
$browserTitle.text(__('
|
|
729
|
+
$browserTitle.text(__('Resources'));
|
|
472
730
|
} else {
|
|
473
731
|
isUploadMode = true;
|
|
732
|
+
$fileSelector.addClass('upload-mode');
|
|
733
|
+
$filesWrapper.hide();
|
|
474
734
|
$fileContainer.hide();
|
|
475
735
|
$placeholder.hide();
|
|
476
736
|
$uploader.show();
|
|
@@ -494,15 +754,25 @@ define(['jquery', 'lodash', 'async', 'i18n', 'core/mimetype', 'handlebars', 'lib
|
|
|
494
754
|
});
|
|
495
755
|
setUploadMode(isUploadMode);
|
|
496
756
|
}
|
|
497
|
-
function updateFiles(
|
|
757
|
+
function updateFiles(files, updateOptions) {
|
|
758
|
+
const displayOptions = updateOptions || {};
|
|
759
|
+
const emptyLabel = displayOptions.emptyMessage || __('No files');
|
|
760
|
+
lastFiles = Array.isArray(files) ? files.slice() : [];
|
|
761
|
+
const sorted = assetSearchContract.sortAssetItems(lastFiles, sort);
|
|
498
762
|
$fileContainer.empty();
|
|
499
|
-
if (
|
|
763
|
+
if (sorted.length) {
|
|
500
764
|
$placeholder.hide();
|
|
765
|
+
$filesWrapper.show();
|
|
501
766
|
$fileContainer.append(fileSelectTpl({
|
|
502
|
-
files:
|
|
767
|
+
files: sorted
|
|
503
768
|
}));
|
|
504
|
-
} else if ($
|
|
505
|
-
$
|
|
769
|
+
} else if ($fileSelector.find('.asset-search-error:not([hidden])').length === 0) {
|
|
770
|
+
$filesWrapper.hide();
|
|
771
|
+
// Stylesheet defaults `.empty` to display:none; force visible for empty states.
|
|
772
|
+
$placeholder.text(emptyLabel).css('display', 'block');
|
|
773
|
+
} else {
|
|
774
|
+
$filesWrapper.hide();
|
|
775
|
+
$placeholder.hide();
|
|
506
776
|
}
|
|
507
777
|
}
|
|
508
778
|
}
|