alchemy-media 0.6.3 → 0.6.4
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/CHANGELOG.md +8 -0
- package/assets/stylesheets/element/alchemy_file.scss +26 -0
- package/bootstrap.js +3 -1
- package/controller/media_files_controller.js +31 -20
- package/element/al_ico_element.js +5 -6
- package/helper/media_helper.js +24 -1
- package/helper/widgets/image_widget.js +8 -8
- package/lib/media_types/image_media_type.js +3 -3
- package/model/media_file_model.js +44 -13
- package/package.json +1 -1
- package/view/form/inputs/edit/file_preview.hwk +5 -0
- package/view/form/inputs/view_inline/file.hwk +8 -0
- package/view/form/inputs/view_inline/file_preview.hwk +8 -0
- package/view/form/wrappers/edit/file_preview.hwk +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 0.6.4 (2022-10-13)
|
|
2
|
+
|
|
3
|
+
* Add the `Media#loadIconFont()` helper method
|
|
4
|
+
* Make the `image` route serve up filetype thumbnails for non-images
|
|
5
|
+
* Add file preview image to the chimera edit view of MediaFile
|
|
6
|
+
* Add thumbnail column to MediaFile chimera index view
|
|
7
|
+
* Add `prefix` parameter to the `MediaFile#data` route
|
|
8
|
+
|
|
1
9
|
## 0.6.3 (2022-07-23)
|
|
2
10
|
|
|
3
11
|
* Fix `al-file` element showing wrong buttons on load
|
|
@@ -92,4 +92,30 @@ al-file {
|
|
|
92
92
|
button {
|
|
93
93
|
min-height: 2rem;
|
|
94
94
|
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
alchemy-field[field-view="file_preview"] {
|
|
98
|
+
[data-he-name="field"] .field {
|
|
99
|
+
padding: 0;
|
|
100
|
+
|
|
101
|
+
img {
|
|
102
|
+
max-height: 25rem;
|
|
103
|
+
object-fit: contain;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
alchemy-field[field-view="file_preview"],
|
|
109
|
+
alchemy-field[field-view="file"],
|
|
110
|
+
alchemy-field[field-type="file"] {
|
|
111
|
+
|
|
112
|
+
&[mode="inline"] {
|
|
113
|
+
.field img {
|
|
114
|
+
object-fit: contain;
|
|
115
|
+
width: 100px;
|
|
116
|
+
height: 100px;
|
|
117
|
+
display: block;
|
|
118
|
+
margin: auto;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
95
121
|
}
|
package/bootstrap.js
CHANGED
|
@@ -43,7 +43,9 @@ alchemy.createDir(options.cache);
|
|
|
43
43
|
Router.get('Media::static', /\/media\/static\/(.*)*/, 'MediaFile#serveStatic');
|
|
44
44
|
Router.get('Media::image', options.url + '/{id}', 'MediaFile#image');
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// The prefix is added at the end of the route so it does not
|
|
47
|
+
// change the user's active_prefix
|
|
48
|
+
Router.get('MediaFile#data', '/media/data/{prefix}/{id}', 'MediaFile#data');
|
|
47
49
|
Router.get('MediaFile#info', '/media/info', 'MediaFile#info');
|
|
48
50
|
|
|
49
51
|
// Allow dummy extensions
|
|
@@ -13,16 +13,14 @@ var fs = require('fs'),
|
|
|
13
13
|
* @since 0.0.1
|
|
14
14
|
* @version 0.3.0
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
MediaFile.super.call(this, conduit, options);
|
|
18
|
-
});
|
|
16
|
+
const MediaFiles = Function.inherits('Alchemy.Controller', 'MediaFile');
|
|
19
17
|
|
|
20
18
|
/**
|
|
21
19
|
* Serve a thumbnail
|
|
22
20
|
*
|
|
23
21
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
24
22
|
* @since 0.0.1
|
|
25
|
-
* @version 0.4
|
|
23
|
+
* @version 0.6.4
|
|
26
24
|
*
|
|
27
25
|
* @param {Conduit} conduit
|
|
28
26
|
*/
|
|
@@ -35,21 +33,15 @@ MediaFiles.setAction(function thumbnail(conduit, id) {
|
|
|
35
33
|
// Get the requested file
|
|
36
34
|
this.getModel('MediaFile').getFile(id, function gotFile(err, record) {
|
|
37
35
|
|
|
38
|
-
var Type;
|
|
39
|
-
|
|
40
36
|
if (err) {
|
|
41
37
|
return conduit.notFound(err);
|
|
42
38
|
}
|
|
43
39
|
|
|
44
|
-
Type = MediaTypes[record.type];
|
|
45
|
-
|
|
46
|
-
if (!Type) {
|
|
47
|
-
Type = Classes.Alchemy.MediaType;
|
|
48
|
-
}
|
|
40
|
+
const Type = MediaTypes[record.type] || Classes.Alchemy.MediaType;
|
|
49
41
|
|
|
50
42
|
if (Type) {
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
let instance = new Type();
|
|
44
|
+
instance.thumbnail(conduit, record);
|
|
53
45
|
} else {
|
|
54
46
|
conduit.error('Error generating thumbnail of ' + record.type);
|
|
55
47
|
}
|
|
@@ -100,7 +92,7 @@ MediaFiles.setAction(function serveStatic(conduit, path) {
|
|
|
100
92
|
*
|
|
101
93
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
102
94
|
* @since 0.0.1
|
|
103
|
-
* @version 0.4
|
|
95
|
+
* @version 0.6.4
|
|
104
96
|
*
|
|
105
97
|
* @param {Conduit} conduit
|
|
106
98
|
*/
|
|
@@ -112,12 +104,22 @@ MediaFiles.setAction(function image(conduit, id) {
|
|
|
112
104
|
|
|
113
105
|
this.getModel('MediaFile').getFile(id, function gotFile(err, record) {
|
|
114
106
|
|
|
115
|
-
|
|
107
|
+
if (err) {
|
|
108
|
+
return conduit.error(err);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const Image = new MediaTypes.image;
|
|
116
112
|
|
|
117
113
|
if (!record) {
|
|
118
114
|
return Image.placeholder(conduit, {text: 404, status: 404});
|
|
119
115
|
}
|
|
120
116
|
|
|
117
|
+
if (record.type != 'image') {
|
|
118
|
+
const Type = MediaTypes[record.type] || Classes.Alchemy.MediaType;
|
|
119
|
+
let instance = new Type();
|
|
120
|
+
return instance.thumbnail(conduit, record);
|
|
121
|
+
}
|
|
122
|
+
|
|
121
123
|
Image.serve(conduit, record);
|
|
122
124
|
});
|
|
123
125
|
});
|
|
@@ -315,14 +317,23 @@ MediaFiles.setAction(function uploadsingle(conduit) {
|
|
|
315
317
|
/**
|
|
316
318
|
* Get the file data
|
|
317
319
|
*
|
|
318
|
-
* @author Jelle De Loecker <jelle@
|
|
320
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
319
321
|
* @since 0.5.1
|
|
320
|
-
* @version 0.
|
|
322
|
+
* @version 0.6.4
|
|
321
323
|
*
|
|
322
|
-
* @param {Conduit}
|
|
323
|
-
* @param {
|
|
324
|
+
* @param {Conduit} conduit
|
|
325
|
+
* @param {ObjectId} media_file_id
|
|
324
326
|
*/
|
|
325
|
-
MediaFiles.setAction(function data(conduit,
|
|
327
|
+
MediaFiles.setAction(async function data(conduit, prefix, media_file_id) {
|
|
328
|
+
|
|
329
|
+
conduit.prefix = prefix;
|
|
330
|
+
|
|
331
|
+
let media_file = await this.model.findById(media_file_id);
|
|
332
|
+
|
|
333
|
+
if (!media_file) {
|
|
334
|
+
return conduit.notFound();
|
|
335
|
+
}
|
|
336
|
+
|
|
326
337
|
conduit.setHeader('cache-control', 'public, max-age=3600, must-revalidate');
|
|
327
338
|
|
|
328
339
|
conduit.end({
|
|
@@ -112,7 +112,7 @@ Icon.setMethod(function setIcon(info) {
|
|
|
112
112
|
*
|
|
113
113
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
114
114
|
* @since 0.6.3
|
|
115
|
-
* @version 0.6.
|
|
115
|
+
* @version 0.6.4
|
|
116
116
|
*/
|
|
117
117
|
Icon.setMethod(function setCssClasses() {
|
|
118
118
|
|
|
@@ -120,14 +120,13 @@ Icon.setMethod(function setCssClasses() {
|
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// Load the appropriate font style
|
|
124
|
+
this.hawkejs_renderer.helpers.Media.loadIconFont();
|
|
125
|
+
|
|
123
126
|
let fa_pro = this.hawkejs_renderer.expose('fontawesome_pro'),
|
|
124
127
|
style = this.icon_style || 'regular';
|
|
125
128
|
|
|
126
|
-
if (fa_pro) {
|
|
127
|
-
this.hawkejs_renderer.style(fa_pro);
|
|
128
|
-
} else {
|
|
129
|
-
this.hawkejs_renderer.style('alchemy_icons_fafree');
|
|
130
|
-
|
|
129
|
+
if (!fa_pro) {
|
|
131
130
|
if (style == 'duotone' || style == 'light' || style == 'thin' || style == 'regular') {
|
|
132
131
|
style = 'solid';
|
|
133
132
|
}
|
package/helper/media_helper.js
CHANGED
|
@@ -88,6 +88,28 @@ Media.setStatic(function loadImagesBasedOnSize() {
|
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Load the icon font
|
|
93
|
+
*
|
|
94
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
95
|
+
* @since 0.6.4
|
|
96
|
+
* @version 0.6.4
|
|
97
|
+
*
|
|
98
|
+
* @return {string}
|
|
99
|
+
*/
|
|
100
|
+
Media.setMethod(function loadIconFont() {
|
|
101
|
+
|
|
102
|
+
let font_style = this.hawkejs_renderer.expose('fontawesome_pro');
|
|
103
|
+
|
|
104
|
+
if (!font_style) {
|
|
105
|
+
font_style = 'alchemy_icons_fafree';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.hawkejs_renderer.style(font_style);
|
|
109
|
+
|
|
110
|
+
return font_style;
|
|
111
|
+
});
|
|
112
|
+
|
|
91
113
|
/**
|
|
92
114
|
* Apply directive to an element
|
|
93
115
|
*
|
|
@@ -173,7 +195,8 @@ Media.setMethod(function applyDirective(element, image, options) {
|
|
|
173
195
|
this.view.helpers.Alchemy.getResource({
|
|
174
196
|
name: 'MediaFile#data',
|
|
175
197
|
params: {
|
|
176
|
-
id: image
|
|
198
|
+
id : image,
|
|
199
|
+
prefix : this.view.expose('active_prefix') || '__',
|
|
177
200
|
}
|
|
178
201
|
}, function gotResult(err, data) {
|
|
179
202
|
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @constructor
|
|
5
5
|
*
|
|
6
6
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
7
|
-
* @since 0.
|
|
8
|
-
* @version 0.
|
|
7
|
+
* @since 0.6.0
|
|
8
|
+
* @version 0.6.0
|
|
9
9
|
*
|
|
10
10
|
* @param {Object} data
|
|
11
11
|
*/
|
|
@@ -15,8 +15,8 @@ const Image = Function.inherits('Alchemy.Widget', 'Image');
|
|
|
15
15
|
* Prepare the schema
|
|
16
16
|
*
|
|
17
17
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
18
|
-
* @since 0.
|
|
19
|
-
* @version 0.
|
|
18
|
+
* @since 0.6.0
|
|
19
|
+
* @version 0.6.0
|
|
20
20
|
*/
|
|
21
21
|
Image.constitute(function prepareSchema() {
|
|
22
22
|
|
|
@@ -29,8 +29,8 @@ Image.constitute(function prepareSchema() {
|
|
|
29
29
|
* Populate the widget
|
|
30
30
|
*
|
|
31
31
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
32
|
-
* @since 0.
|
|
33
|
-
* @version 0.
|
|
32
|
+
* @since 0.6.0
|
|
33
|
+
* @version 0.6.4
|
|
34
34
|
*
|
|
35
35
|
* @param {HTMLElement} widget
|
|
36
36
|
*/
|
|
@@ -40,7 +40,7 @@ Image.setMethod(function populateWidget() {
|
|
|
40
40
|
|
|
41
41
|
this.hawkejs_renderer.helpers.Media.applyDirective(img, this.config.image);
|
|
42
42
|
|
|
43
|
-
populateWidget.super.call(this);
|
|
44
|
-
|
|
45
43
|
this.widget.append(img);
|
|
44
|
+
|
|
45
|
+
return populateWidget.super.call(this);
|
|
46
46
|
});
|
|
@@ -14,10 +14,10 @@ var exiv2 = alchemy.use('@11ways/exiv2'),
|
|
|
14
14
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
15
15
|
* @since 0.0.1
|
|
16
16
|
* @version 0.2.0
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} options
|
|
17
19
|
*/
|
|
18
|
-
|
|
19
|
-
ImageMediaType.super.call(this, options);
|
|
20
|
-
});
|
|
20
|
+
const ImageMedia = Function.inherits('Alchemy.MediaType', 'ImageMediaType');
|
|
21
21
|
|
|
22
22
|
ImageMedia.setProperty('exivPath', alchemy.plugins.media.exiv2);
|
|
23
23
|
ImageMedia.setProperty('hashType', alchemy.plugins.media.hash);
|
|
@@ -19,32 +19,48 @@ var MediaFile = Function.inherits('Alchemy.Model', function MediaFile(options) {
|
|
|
19
19
|
|
|
20
20
|
MediaFile.setProperty('types', alchemy.shared('Media.types'));
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* The default sort options
|
|
24
|
+
*
|
|
25
|
+
* @type {Object}
|
|
26
|
+
*/
|
|
27
|
+
MediaFile.prepareProperty('sort', function sort() {
|
|
28
|
+
return {created: -1};
|
|
29
|
+
});
|
|
30
|
+
|
|
22
31
|
/**
|
|
23
32
|
* Constitute the class wide schema
|
|
24
33
|
*
|
|
25
|
-
* @author Jelle De Loecker <jelle@
|
|
34
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
26
35
|
* @since 0.2.0
|
|
27
|
-
* @version 0.
|
|
36
|
+
* @version 0.6.4
|
|
28
37
|
*/
|
|
29
38
|
MediaFile.constitute(function addFields() {
|
|
30
39
|
|
|
31
|
-
this.addField('name', 'String'
|
|
32
|
-
|
|
40
|
+
this.addField('name', 'String', {
|
|
41
|
+
description: 'The name of the file',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.addField('filename', 'String', {
|
|
45
|
+
description : 'The actual filename',
|
|
46
|
+
});
|
|
33
47
|
|
|
34
48
|
this.addField('type', 'Enum', {
|
|
35
|
-
values: alchemy.getClassGroup('media_type')
|
|
49
|
+
values: alchemy.getClassGroup('media_type'),
|
|
50
|
+
description: 'The type of file',
|
|
36
51
|
});
|
|
37
52
|
|
|
38
53
|
this.addField('extra', 'Object');
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
55
|
+
this.addField('title', 'String', {
|
|
56
|
+
translatable : alchemy.plugins.media.translatable,
|
|
57
|
+
description : 'The title of the file (will be used in the title attribute)',
|
|
58
|
+
});
|
|
45
59
|
|
|
46
|
-
this.addField('
|
|
47
|
-
|
|
60
|
+
this.addField('alt', 'String', {
|
|
61
|
+
translatable : alchemy.plugins.media.translatable,
|
|
62
|
+
description : 'The alternative information of the file (will be used in the alt attribute)',
|
|
63
|
+
});
|
|
48
64
|
|
|
49
65
|
this.belongsTo('MediaRaw');
|
|
50
66
|
});
|
|
@@ -54,7 +70,7 @@ MediaFile.constitute(function addFields() {
|
|
|
54
70
|
*
|
|
55
71
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
56
72
|
* @since 0.2.0
|
|
57
|
-
* @version 0.
|
|
73
|
+
* @version 0.6.4
|
|
58
74
|
*/
|
|
59
75
|
MediaFile.constitute(function chimeraConfig() {
|
|
60
76
|
|
|
@@ -69,6 +85,15 @@ MediaFile.constitute(function chimeraConfig() {
|
|
|
69
85
|
// Get the list group
|
|
70
86
|
list = this.chimera.getActionFields('list');
|
|
71
87
|
|
|
88
|
+
list.addField('_id', {
|
|
89
|
+
view : 'file_preview',
|
|
90
|
+
wrapper : 'file_preview',
|
|
91
|
+
title : 'Thumbnail',
|
|
92
|
+
filter : false,
|
|
93
|
+
sortable : false,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
list.addField('created');
|
|
72
97
|
list.addField('name');
|
|
73
98
|
list.addField('filename');
|
|
74
99
|
list.addField('type');
|
|
@@ -78,6 +103,12 @@ MediaFile.constitute(function chimeraConfig() {
|
|
|
78
103
|
// Get the edit group
|
|
79
104
|
edit = this.chimera.getActionFields('edit');
|
|
80
105
|
|
|
106
|
+
edit.addField('_id', {
|
|
107
|
+
view : 'file_preview',
|
|
108
|
+
wrapper : 'file_preview',
|
|
109
|
+
title : 'Preview',
|
|
110
|
+
});
|
|
111
|
+
|
|
81
112
|
edit.addField('name');
|
|
82
113
|
edit.addField('filename');
|
|
83
114
|
edit.addField('type');
|
package/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<div class="form-field-info">
|
|
2
|
+
<alchemy-label>
|
|
3
|
+
<span
|
|
4
|
+
data-he-name="field-title"
|
|
5
|
+
data-he-slot="field-title"
|
|
6
|
+
><%= alchemy_field.field_title %></span>
|
|
7
|
+
<small
|
|
8
|
+
data-he-name="field-description"
|
|
9
|
+
data-he-slot="field-description"
|
|
10
|
+
><%= alchemy_field.field_description %></small>
|
|
11
|
+
</alchemy-label>
|
|
12
|
+
</div>
|
|
13
|
+
<div data-he-name="field"></div>
|