diff --git a/.gitignore b/.gitignore index 35172e5d2f..b7064cdbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ coverage node_modules dist -e2e/dms/*/ -!e2e/dms/c4c -!e2e/dms/c81 -!e2e/dms/ecc -!e2e/dms/a87 +storage +!storage/dms/c4c +!storage/dms/c81 +!storage/dms/ecc +!storage/dms/a87 npm-debug.log .eslintcache datasources.*.json diff --git a/back/methods/dms/updateFile.js b/back/methods/dms/updateFile.js index 7585dd1b08..9f8f4f2938 100644 --- a/back/methods/dms/updateFile.js +++ b/back/methods/dms/updateFile.js @@ -84,7 +84,7 @@ module.exports = Self => { }; async function uploadNewFile(ctx, dms, myOptions) { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const models = Self.app.models; const fileOptions = {}; diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js index 27e5169c98..8e5c4eb638 100644 --- a/back/methods/dms/uploadFile.js +++ b/back/methods/dms/uploadFile.js @@ -46,7 +46,7 @@ module.exports = Self => { }); Self.uploadFile = async(ctx, options) => { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const models = Self.app.models; const fileOptions = {}; const args = ctx.args; @@ -98,7 +98,7 @@ module.exports = Self => { async function createDms(ctx, file, myOptions) { const models = Self.app.models; - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const myUserId = ctx.req.accessToken.userId; const myWorker = await models.Worker.findOne({where: {userFk: myUserId}}, myOptions); const args = ctx.args; @@ -121,7 +121,6 @@ module.exports = Self => { return newDms.updateAttribute('file', fileName, myOptions); } - /** * Returns a container instance * If doesn't exists creates a new one diff --git a/back/methods/image/download.js b/back/methods/image/download.js index 6f1e0b8e79..ba709aff88 100644 --- a/back/methods/image/download.js +++ b/back/methods/image/download.js @@ -2,7 +2,7 @@ const UserError = require('vn-loopback/util/user-error'); const fs = require('fs-extra'); module.exports = Self => { - Self.remoteMethod('download', { + Self.remoteMethodCtx('download', { description: 'Get the user image', accessType: 'READ', accepts: [ @@ -49,15 +49,9 @@ module.exports = Self => { } }); - Self.download = async function(collection, size, id) { + Self.download = async function(ctx, collection, size, id) { const models = Self.app.models; - const filter = { - where: { - name: collection}, - include: { - relation: 'readRole' - } - }; + const filter = {where: {name: collection}}; const imageCollection = await models.ImageCollection.findOne(filter); const entity = await models[imageCollection.model].findById(id, { fields: ['id', imageCollection.property] @@ -69,28 +63,22 @@ module.exports = Self => { if (!image) return false; - const imageRole = imageCollection.readRole().name; - const hasRole = await models.Account.hasRole(id, imageRole); - if (!hasRole) + const hasReadRole = models.ImageCollection.hasReadRole(ctx, collection); + if (!hasReadRole) throw new UserError(`You don't have enough privileges`); - let file; - let env = process.env.NODE_ENV; - if (env && env != 'development') { - file = { - path: `/var/lib/salix/image/${collection}/${size}/${image.name}.png`, - contentType: 'image/png', - name: `${image.name}.png` - }; - } else { - file = { - path: `${process.cwd()}/storage/image/${collection}/${size}/${image.name}.png`, - contentType: 'image/png', - name: `${image.name}.png` - }; - } + const container = await models.ImageContainer.getContainer(collection); + const rootPath = container.client.root; + const file = { + path: `${rootPath}/${collection}/${size}/${image.name}.png`, + contentType: 'image/png', + name: `${image.name}.png` + }; + + if (!fs.existsSync(file.path)) return []; + await fs.access(file.path); - let stream = fs.createReadStream(file.path); + const stream = fs.createReadStream(file.path); return [stream, file.contentType, `filename="${file.name}"`]; }; }; diff --git a/back/methods/image/upload.js b/back/methods/image/upload.js new file mode 100644 index 0000000000..b50a55b346 --- /dev/null +++ b/back/methods/image/upload.js @@ -0,0 +1,77 @@ +const UserError = require('vn-loopback/util/user-error'); +const fs = require('fs-extra'); + +module.exports = Self => { + Self.remoteMethodCtx('upload', { + description: 'Uploads a file and inserts into dms model', + accessType: 'WRITE', + accepts: [ + { + arg: 'id', + type: 'Number', + description: 'The entity id', + required: true + }, + { + arg: 'collection', + type: 'string', + description: 'The collection name', + required: true + }], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/upload`, + verb: 'POST' + } + }); + + Self.upload = async ctx => { + const models = Self.app.models; + const fileOptions = {}; + const args = ctx.args; + + const hasWriteRole = await models.ImageCollection.hasWriteRole(ctx, args.collection); + if (!hasWriteRole) + throw new UserError(`You don't have enough privileges`); + + if (process.env.NODE_ENV == 'test') + throw new UserError(`You can't upload images on the test instance`); + + // Upload file to temporary path + const container = await getContainer(args.collection); + const uploaded = await models.ImageContainer.upload(container.name, ctx.req, ctx.result, fileOptions); + const [uploadedFile] = Object.values(uploaded.files).map(file => { + return file[0]; + }); + + const file = await models.ImageContainer.getFile(container.name, uploadedFile.name); + const srcFile = `${file.client.root}/${file.container}/${file.name}`; + await models.Image.registerImage(container.name, srcFile, args.id); + }; + + /** + * Returns a container instance + * If doesn't exists creates a new one + * + * @param {String} name Container name + * @return {Object} Container instance + */ + async function getContainer(name) { + const models = Self.app.models; + let container; + try { + container = await models.ImageContainer.getContainer(name); + } catch (err) { + if (err.code === 'ENOENT') { + container = await models.ImageContainer.createContainer({ + name: name + }); + } else throw err; + } + + return container; + } +}; diff --git a/back/model-config.json b/back/model-config.json index adb67eaed4..3db249f0fe 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -18,7 +18,7 @@ "dataSource": "vn" }, "Container": { - "dataSource": "storage" + "dataSource": "dmsStorage" }, "Continent": { "dataSource": "vn" @@ -44,6 +44,9 @@ "ImageCollectionSize": { "dataSource": "vn" }, + "ImageContainer": { + "dataSource": "imageStorage" + }, "Language": { "dataSource": "vn" }, diff --git a/back/models/dms.js b/back/models/dms.js index 8be539a0ff..9e767904e9 100644 --- a/back/models/dms.js +++ b/back/models/dms.js @@ -14,7 +14,7 @@ module.exports = Self => { }; Self.getFile = async function(id) { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const models = Self.app.models; const dms = await Self.findById(id); const pathHash = storageConnector.getPathHash(dms.id); diff --git a/back/models/image-collection.js b/back/models/image-collection.js new file mode 100644 index 0000000000..2b541dcd6d --- /dev/null +++ b/back/models/image-collection.js @@ -0,0 +1,63 @@ +module.exports = Self => { + /** + * Checks if current user has + * read privileges over a collection + * + * @param {object} ctx - Request context + * @param {interger} name - Collection name + * @param {object} options - Query options + * @return {boolean} True for user with read privileges + */ + Self.hasReadRole = async(ctx, name, options) => { + const collection = await Self.findOne({where: {name}}, { + include: { + relation: 'readRole' + } + }, options); + + return await hasRole(ctx, collection, options); + }; + + /** + * Checks if current user has + * write privileges over a collection + * + * @param {object} ctx - Request context + * @param {string} name - Collection name + * @param {object} options - Query options + * @return {boolean} True for user with write privileges + */ + Self.hasWriteRole = async(ctx, name, options) => { + const collection = await Self.findOne({where: {name}}, { + include: { + relation: 'writeRole' + } + }, options); + + return await hasRole(ctx, collection, options); + }; + + /** + * Checks if current user has + * read or write privileges + * @param {Object} ctx - Context + * @param {Object} collection - Collection [read/write] + * @param {Object} options - Query options + */ + async function hasRole(ctx, collection, options) { + const models = Self.app.models; + const myUserId = ctx.req.accessToken.userId; + + const readRole = collection.readRole() && collection.readRole().name; + const writeRole = collection.writeRole() && collection.writeRole().name; + const requiredRole = readRole || writeRole; + + const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole, options); + const isRoot = await models.Account.hasRole(myUserId, 'root', options); + + if (isRoot || hasRequiredRole) + return true; + + return false; + } +}; diff --git a/back/models/image-collection.json b/back/models/image-collection.json index 75faaf7220..fd019ecc37 100644 --- a/back/models/image-collection.json +++ b/back/models/image-collection.json @@ -48,7 +48,12 @@ "type": "belongsTo", "model": "Role", "foreignKey": "readRoleFk" - } + }, + "writeRole": { + "type": "belongsTo", + "model": "Role", + "foreignKey": "writeRoleFk" + } }, "acls": [ { diff --git a/back/models/image-container.json b/back/models/image-container.json new file mode 100644 index 0000000000..22cea05f53 --- /dev/null +++ b/back/models/image-container.json @@ -0,0 +1,13 @@ +{ + "name": "ImageContainer", + "base": "VnModel", + "idInjection": true, + "options": { + "validateUpsert": true + }, + "properties": {}, + "validations": [], + "relations": {}, + "acls": [], + "methods": [] + } \ No newline at end of file diff --git a/back/models/image.js b/back/models/image.js index f6f4cf5dbf..cbe2cfdac4 100644 --- a/back/models/image.js +++ b/back/models/image.js @@ -4,12 +4,9 @@ const path = require('path'); module.exports = Self => { require('../methods/image/download')(Self); + require('../methods/image/upload')(Self); - Self.getPath = function() { - return '/var/lib/salix/image'; - }; - - Self.registerImage = async(collectionName, file, srcFilePath) => { + Self.registerImage = async(collectionName, srcFile, entityId) => { const models = Self.app.models; const tx = await Self.beginTransaction({}); const myOptions = {transaction: tx}; @@ -33,8 +30,8 @@ module.exports = Self => { } }, myOptions); + const file = srcFile.split('/').pop(); const fileName = file.split('.')[0]; - const rootPath = Self.getPath(); const data = { name: fileName, collectionFk: collectionName @@ -47,6 +44,8 @@ module.exports = Self => { }, myOptions); // Resizes and saves the image + const container = await models.ImageContainer.getContainer(collectionName); + const rootPath = container.client.root; const collectionDir = path.join(rootPath, collectionName); const dstDir = path.join(collectionDir, 'full'); const dstFile = path.join(dstDir, file); @@ -57,7 +56,7 @@ module.exports = Self => { }; await fs.mkdir(dstDir, {recursive: true}); - await sharp(srcFilePath, {failOnError: false}) + await sharp(srcFile, {failOnError: false}) .resize(collection.maxWidth, collection.maxHeight, resizeOpts) .png() .toFile(dstFile); @@ -72,7 +71,7 @@ module.exports = Self => { }; await fs.mkdir(dstDir, {recursive: true}); - await sharp(srcFilePath, {failOnError: false}) + await sharp(srcFile, {failOnError: false}) .resize(size.width, size.height, resizeOpts) .png() .toFile(dstFile); @@ -83,7 +82,7 @@ module.exports = Self => { if (!model) throw new Error('Matching model not found'); - const item = await model.findById(fileName, null, myOptions); + const item = await model.findById(entityId, null, myOptions); if (item) { await item.updateAttribute( collection.property, @@ -92,8 +91,8 @@ module.exports = Self => { ); } - if (fs.existsSync(srcFilePath)) - await fs.unlink(srcFilePath); + if (fs.existsSync(srcFile)) + await fs.unlink(srcFile); await tx.commit(); return newImage; diff --git a/db/changes/10260-holidays/00-ACL.sql b/db/changes/10260-holidays/00-ACL.sql new file mode 100644 index 0000000000..1f8045bc7c --- /dev/null +++ b/db/changes/10260-holidays/00-ACL.sql @@ -0,0 +1 @@ +INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) VALUES ('Image', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee') \ No newline at end of file diff --git a/db/changes/10260-holidays/00-imageCollection.sql b/db/changes/10260-holidays/00-imageCollection.sql new file mode 100644 index 0000000000..28ffdff9ca --- /dev/null +++ b/db/changes/10260-holidays/00-imageCollection.sql @@ -0,0 +1,8 @@ +ALTER TABLE `hedera`.`imageCollection` + ADD writeRoleFk INT NULL DEFAULT 1; + +ALTER TABLE `hedera`.`imageCollection` + ADD CONSTRAINT role_id___fk + FOREIGN KEY (writeRoleFk) REFERENCES account.role (id) + ON UPDATE CASCADE; + diff --git a/db/dump/dumpedFixtures.sql b/db/dump/dumpedFixtures.sql index 879b1eb427..abc6bb8c10 100644 --- a/db/dump/dumpedFixtures.sql +++ b/db/dump/dumpedFixtures.sql @@ -447,6 +447,25 @@ INSERT INTO `imageCollection` VALUES (1,'catalog','Artículo',3840,2160,'Item',' /*!40000 ALTER TABLE `imageCollection` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Dumping data for table `imageCollectionSize` +-- +LOCK TABLES `imageCollectionSize` WRITE; +/*!40000 ALTER TABLE `imageCollectionSize` DISABLE KEYS */; +INSERT INTO `imageCollectionSize` (`id`, `collectionFk`, `width`, `height`, `crop`) + VALUES + (2, 1, 50, 50, 1), + (3, 1, 200, 200, 1), + (5, 5, 200, 200, 1), + (6, 1, 70, 70, 1), + (8, 5, 50, 50, 1), + (9, 1, 1600, 900, 0), + (13, 6, 160, 160, 1), + (14, 6, 520, 520, 1), + (15, 6, 1600, 1600, 1); +/*!40000 ALTER TABLE `imageCollectionSize` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Dumping data for table `tpvError` -- diff --git a/e2e/dms/a87/4.txt b/e2e/dms/a87/4.txt deleted file mode 100644 index a46bfbeda2..0000000000 --- a/e2e/dms/a87/4.txt +++ /dev/null @@ -1 +0,0 @@ -File: 4.txt. It works! \ No newline at end of file diff --git a/e2e/dms/c4c/1.txt b/e2e/dms/c4c/1.txt deleted file mode 100644 index 5ef648a885..0000000000 --- a/e2e/dms/c4c/1.txt +++ /dev/null @@ -1 +0,0 @@ -It works! \ No newline at end of file diff --git a/e2e/dms/c81/2.txt b/e2e/dms/c81/2.txt deleted file mode 100644 index 5ef648a885..0000000000 --- a/e2e/dms/c81/2.txt +++ /dev/null @@ -1 +0,0 @@ -It works! \ No newline at end of file diff --git a/e2e/dms/ecc/3.jpeg b/e2e/dms/ecc/3.jpeg deleted file mode 100644 index fb2483f696..0000000000 Binary files a/e2e/dms/ecc/3.jpeg and /dev/null differ diff --git a/e2e/dms/ecc/3.txt b/e2e/dms/ecc/3.txt deleted file mode 100644 index 5ef648a885..0000000000 --- a/e2e/dms/ecc/3.txt +++ /dev/null @@ -1 +0,0 @@ -It works! \ No newline at end of file diff --git a/front/core/directives/zoom-image.js b/front/core/directives/zoom-image.js index 607dbe3378..a5d57bd14f 100644 --- a/front/core/directives/zoom-image.js +++ b/front/core/directives/zoom-image.js @@ -46,7 +46,7 @@ export function directive($timeout) { $element.on('click', function(event) { if (event.defaultPrevented) return; - let src = $attrs.zoomImage || $attrs.src; + let src = $element[0].getAttribute('zoom-image') || $element[0].src; if (src) createContainers(src); else diff --git a/front/salix/components/descriptor/style.scss b/front/salix/components/descriptor/style.scss index 814df2ca50..bad94a9345 100644 --- a/front/salix/components/descriptor/style.scss +++ b/front/salix/components/descriptor/style.scss @@ -5,6 +5,24 @@ vn-descriptor-content { display: block; + .photo { + position: relative; + + & > img[ng-src] { + min-height: 16em; + display: block; + height: 100%; + width: 100%; + } + + vn-float-button { + position: absolute; + margin: 1em; + bottom: 0; + right: 0 + } + } + & > vn-spinner { display: block; height: 40px; diff --git a/front/salix/components/index.js b/front/salix/components/index.js index 1586272c09..13f8366cd1 100644 --- a/front/salix/components/index.js +++ b/front/salix/components/index.js @@ -13,3 +13,4 @@ import './section'; import './summary'; import './topbar/topbar'; import './user-popover'; +import './upload-photo'; diff --git a/front/salix/components/upload-photo/index.html b/front/salix/components/upload-photo/index.html new file mode 100644 index 0000000000..fc5b6c7352 --- /dev/null +++ b/front/salix/components/upload-photo/index.html @@ -0,0 +1,23 @@ + + + +
+
+ + + + +
+ + + + +
\ No newline at end of file diff --git a/front/salix/components/upload-photo/index.js b/front/salix/components/upload-photo/index.js new file mode 100644 index 0000000000..aa3c1a22a1 --- /dev/null +++ b/front/salix/components/upload-photo/index.js @@ -0,0 +1,87 @@ +import ngModule from '../../module'; +import Component from 'core/lib/component'; +import './style.scss'; + +/** + * Small card with basing entity information and actions. + */ +export default class UploadPhoto extends Component { + /** + * Opens the dialog and sets the default data + * @param {*} collection - Collection name + * @param {*} id - Entity id + */ + show(collection, id) { + this.newPhoto = {id, collection}; + this.$.dialog.show(); + } + + /** + * Updates the image preview + * + * @param {string} value + */ + updatePhotoPreview(value) { + if (value && value[0]) { + const reader = new FileReader(); + reader.onload = e => this.$.photo.src = e.target.result; + reader.readAsDataURL(value[0]); + } + } + + /** + * Dialog response handler + * + * @return {boolean} Response + */ + onUploadAccept() { + try { + if (!this.newPhoto.files) + throw new Error(`Select an image`); + + this.makeRequest(); + } catch (e) { + this.vnApp.showError(this.$t(e.message)); + return false; + } + return true; + } + + /** + * Performs a cancellable request. + * + */ + makeRequest() { + if (this.canceler) this.canceler.resolve(); + this.canceler = this.$q.defer(); + + const options = { + method: 'POST', + url: `Images/upload`, + params: this.newPhoto, + headers: {'Content-Type': undefined}, + timeout: this.canceler.promise, + transformRequest: files => { + const formData = new FormData(); + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.newPhoto.files + }; + + this.$http(options) + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) + .then(() => this.emit('response')) + .finally(() => this.canceler = null); + } +} + +ngModule.vnComponent('vnUploadPhoto', { + controller: UploadPhoto, + template: require('./index.html'), + bindings: { + data: '<' + } +}); diff --git a/front/salix/components/upload-photo/locale/es.yml b/front/salix/components/upload-photo/locale/es.yml new file mode 100644 index 0000000000..10271cf921 --- /dev/null +++ b/front/salix/components/upload-photo/locale/es.yml @@ -0,0 +1,2 @@ +Upload new photo: Subir una nueva foto +Select an image: Selecciona una imagen \ No newline at end of file diff --git a/front/salix/components/upload-photo/style.scss b/front/salix/components/upload-photo/style.scss new file mode 100644 index 0000000000..cdf35341fa --- /dev/null +++ b/front/salix/components/upload-photo/style.scss @@ -0,0 +1,31 @@ +@import "./variables"; + +.upload-photo { + .photo { + position: relative; + margin: 0 auto; + text-align: center; + + & > div { + border: 3px solid $color-primary; + max-width: 256px; + max-height: 256px; + border-radius: 50%; + overflow: hidden + } + + & > div > img[ng-src] { + width: 256px; + height: 256px; + display: block; + height: 100%; + width: 100%; + } + } + + & > vn-spinner { + display: block; + height: 40px; + padding: $spacing-md; + } +} diff --git a/front/salix/components/user-popover/index.html b/front/salix/components/user-popover/index.html index 9bd0f14117..22d86f1aaa 100644 --- a/front/salix/components/user-popover/index.html +++ b/front/salix/components/user-popover/index.html @@ -14,7 +14,7 @@
diff --git a/front/salix/components/user-popover/index.js b/front/salix/components/user-popover/index.js index e4d7b44664..0d35d89950 100644 --- a/front/salix/components/user-popover/index.js +++ b/front/salix/components/user-popover/index.js @@ -78,10 +78,6 @@ class Controller { this.$.companies.refresh(); this.$.popover.show(event.target); } - - getImageUrl(userId) { - return '/api/Images/user/160x160/' + userId + '/download?access_token=' + this.vnToken.token; - } } Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth', 'vnToken']; diff --git a/front/salix/module.js b/front/salix/module.js index 2c61af4d1d..a8de61ae07 100644 --- a/front/salix/module.js +++ b/front/salix/module.js @@ -7,9 +7,14 @@ export const appName = 'salix'; const ngModule = ng.module('salix', ['vnCore']); export default ngModule; -run.$inject = ['$window', '$rootScope', 'vnAuth', 'vnApp', '$state']; -export function run($window, $rootScope, vnAuth, vnApp, $state) { - $rootScope.imagePath = appConfig.imagePath; +run.$inject = ['$window', '$rootScope', 'vnAuth', 'vnApp', 'vnToken', '$state']; +export function run($window, $rootScope, vnAuth, vnApp, vnToken, $state) { + $rootScope.imagePath = (collection, size, id) => { + if (!collection || !size || !id) return; + + const basePath = `/api/Images/${collection}/${size}/${id}`; + return `${basePath}/download?access_token=${vnToken.token}`; + }; $window.validations = {}; vnApp.name = appName; diff --git a/loopback/locale/es.json b/loopback/locale/es.json index f75b1778eb..0080228aec 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -160,5 +160,6 @@ "The social name cannot be empty": "La razón social no puede quedar en blanco", "The nif cannot be empty": "El NIF no puede quedar en blanco", "You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados", - "ASSIGN_ZONE_FIRST": "Asigna una zona primero" + "ASSIGN_ZONE_FIRST": "Asigna una zona primero", + "You can't upload images on the test environment": "No puedes subir imágenes en el entorno de pruebas" } \ No newline at end of file diff --git a/loopback/server/boot/storage.js b/loopback/server/boot/storage.js index 12662ab73f..7559a7dec5 100644 --- a/loopback/server/boot/storage.js +++ b/loopback/server/boot/storage.js @@ -2,7 +2,7 @@ const uuid = require('uuid/v1'); const md5 = require('md5'); module.exports = app => { - const storageConnector = app.dataSources.storage.connector; + const storageConnector = app.dataSources.dmsStorage.connector; storageConnector.getFilename = function(file) { return `${uuid()}.${storageConnector.getFileExtension(file.name)}`; @@ -15,4 +15,17 @@ module.exports = app => { storageConnector.getPathHash = function(id) { return md5(id.toString()).substring(0, 3); }; + + const imageStorageConnector = app.dataSources.imageStorage.connector; + imageStorageConnector.getFilename = function(file) { + return `${uuid()}.png`; + }; + + /* imageStorageConnector.getFileExtension = function(fileName) { + return fileName.split('.').pop().toLowerCase(); + }; + + imageStorageConnector.getPathHash = function(id) { + return md5(id.toString()).substring(0, 3); + }; */ }; diff --git a/loopback/server/datasources.json b/loopback/server/datasources.json index 0ea6344848..793dcaea8e 100644 --- a/loopback/server/datasources.json +++ b/loopback/server/datasources.json @@ -17,11 +17,11 @@ "connectTimeout": 40000, "acquireTimeout": 20000 }, - "storage": { - "name": "storage", + "dmsStorage": { + "name": "dmsStorage", "connector": "loopback-component-storage", "provider": "filesystem", - "root": "./e2e/dms", + "root": "./storage/dms", "maxFileSize": "262144000", "allowedContentTypes": [ "application/x-7z-compressed", @@ -36,5 +36,17 @@ "image/jpeg", "image/jpg" ] + }, + "imageStorage": { + "name": "imageStorage", + "connector": "loopback-component-storage", + "provider": "filesystem", + "root": "./storage/image", + "maxFileSize": "52428800", + "allowedContentTypes": [ + "image/png", + "image/jpeg", + "image/jpg" + ] } } diff --git a/modules/claim/back/methods/claim-dms/allowedContentTypes.js b/modules/claim/back/methods/claim-dms/allowedContentTypes.js index 2f5183f926..3d4b908767 100644 --- a/modules/claim/back/methods/claim-dms/allowedContentTypes.js +++ b/modules/claim/back/methods/claim-dms/allowedContentTypes.js @@ -13,7 +13,7 @@ module.exports = Self => { }); Self.allowedContentTypes = async() => { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const allowedContentTypes = storageConnector.allowedContentTypes; const modelAllowedContentTypes = Self.definition.settings.allowedContentTypes; diff --git a/modules/client/back/methods/client-dms/allowedContentTypes.js b/modules/client/back/methods/client-dms/allowedContentTypes.js index 2f5183f926..3d4b908767 100644 --- a/modules/client/back/methods/client-dms/allowedContentTypes.js +++ b/modules/client/back/methods/client-dms/allowedContentTypes.js @@ -13,7 +13,7 @@ module.exports = Self => { }); Self.allowedContentTypes = async() => { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const allowedContentTypes = storageConnector.allowedContentTypes; const modelAllowedContentTypes = Self.definition.settings.allowedContentTypes; diff --git a/modules/entry/front/latest-buys/index.html b/modules/entry/front/latest-buys/index.html index 4e0c6ded84..6ab675d76b 100644 --- a/modules/entry/front/latest-buys/index.html +++ b/modules/entry/front/latest-buys/index.html @@ -70,8 +70,8 @@ diff --git a/modules/item/back/methods/item-image-queue/downloadImages.js b/modules/item/back/methods/item-image-queue/downloadImages.js index d953d19383..ec9177505a 100644 --- a/modules/item/back/methods/item-image-queue/downloadImages.js +++ b/modules/item/back/methods/item-image-queue/downloadImages.js @@ -57,7 +57,7 @@ module.exports = Self => { writeStream.on('finish', async function() { try { - await models.Image.registerImage('catalog', fileName, filePath); + await models.Image.registerImage('catalog', filePath, image.itemFk); await image.destroy(); } catch (error) { await errorHandler(image.itemFk, error, filePath); diff --git a/modules/item/front/card/index.html b/modules/item/front/card/index.html index f547a9e7a4..b7513a42a5 100644 --- a/modules/item/front/card/index.html +++ b/modules/item/front/card/index.html @@ -1,5 +1,5 @@ - + diff --git a/modules/item/front/descriptor/index.html b/modules/item/front/descriptor/index.html index 8363a652f2..168325a103 100644 --- a/modules/item/front/descriptor/index.html +++ b/modules/item/front/descriptor/index.html @@ -16,19 +16,15 @@ -
- - - - - +
+ + +
@@ -102,4 +98,10 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/modules/item/front/descriptor/index.js b/modules/item/front/descriptor/index.js index 2791e960a6..195a97d13a 100644 --- a/modules/item/front/descriptor/index.js +++ b/modules/item/front/descriptor/index.js @@ -3,6 +3,11 @@ import Descriptor from 'salix/components/descriptor'; import './style.scss'; class Controller extends Descriptor { + constructor($element, $, $rootScope) { + super($element, $); + this.$rootScope = $rootScope; + } + get item() { return this.entity; } @@ -65,13 +70,27 @@ class Controller extends Descriptor { this.$http.post(`Items/${this.item.id}/clone`) .then(res => this.$state.go('item.card.tags', {id: res.data.id})); } + + onUploadResponse() { + const timestamp = new Date().getTime(); + const src = this.$rootScope.imagePath('catalog', '200x200', this.item.id); + const zoomSrc = this.$rootScope.imagePath('catalog', '1600x900', this.item.id); + const newSrc = `${src}&t=${timestamp}`; + const newZoomSrc = `${zoomSrc}&t=${timestamp}`; + + this.$.photo.setAttribute('src', newSrc); + this.$.photo.setAttribute('zoom-image', newZoomSrc); + } } +Controller.$inject = ['$element', '$scope', '$rootScope']; + ngModule.vnComponent('vnItemDescriptor', { template: require('./index.html'), controller: Controller, bindings: { item: '<', - dated: '<' + dated: '<', + cardReload: '&' } }); diff --git a/modules/item/front/index/index.html b/modules/item/front/index/index.html index 0d4ae61f34..8b03bc0d6b 100644 --- a/modules/item/front/index/index.html +++ b/modules/item/front/index/index.html @@ -35,8 +35,8 @@ ui-sref="item.card.summary({id: item.id})"> @@ -44,7 +44,7 @@ - {{::item.id | zeroFill:6}} + {{::item.id}} {{::item.grouping | dashIfEmpty}} diff --git a/modules/item/front/summary/index.html b/modules/item/front/summary/index.html index e9c835dac9..5fb556bd46 100644 --- a/modules/item/front/summary/index.html +++ b/modules/item/front/summary/index.html @@ -11,8 +11,8 @@ + ng-src="{{$root.imagePath('catalog', '200x200', $ctrl.item.id)}}" + zoom-image="{{$root.imagePath('catalog', '1600x900', $ctrl.item.id)}}" on-error-src/>

Visible

diff --git a/modules/order/front/catalog-view/index.html b/modules/order/front/catalog-view/index.html index 2d7492d87d..25d84db759 100644 --- a/modules/order/front/catalog-view/index.html +++ b/modules/order/front/catalog-view/index.html @@ -5,11 +5,11 @@
-
+
diff --git a/modules/order/front/line/index.html b/modules/order/front/line/index.html index 1ba6e3f9c6..51702e16ed 100644 --- a/modules/order/front/line/index.html +++ b/modules/order/front/line/index.html @@ -32,8 +32,8 @@ diff --git a/modules/ticket/back/methods/ticket-dms/allowedContentTypes.js b/modules/ticket/back/methods/ticket-dms/allowedContentTypes.js index 2f5183f926..3d4b908767 100644 --- a/modules/ticket/back/methods/ticket-dms/allowedContentTypes.js +++ b/modules/ticket/back/methods/ticket-dms/allowedContentTypes.js @@ -13,7 +13,7 @@ module.exports = Self => { }); Self.allowedContentTypes = async() => { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const allowedContentTypes = storageConnector.allowedContentTypes; const modelAllowedContentTypes = Self.definition.settings.allowedContentTypes; diff --git a/modules/ticket/front/picture/index.html b/modules/ticket/front/picture/index.html index 52cab5b31b..c95e604dd0 100644 --- a/modules/ticket/front/picture/index.html +++ b/modules/ticket/front/picture/index.html @@ -19,8 +19,8 @@
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html index 90cfa39d13..ed3cbc02b9 100644 --- a/modules/ticket/front/sale/index.html +++ b/modules/ticket/front/sale/index.html @@ -93,8 +93,8 @@ diff --git a/modules/travel/back/methods/travel-thermograph/allowedContentTypes.js b/modules/travel/back/methods/travel-thermograph/allowedContentTypes.js index 2f5183f926..3d4b908767 100644 --- a/modules/travel/back/methods/travel-thermograph/allowedContentTypes.js +++ b/modules/travel/back/methods/travel-thermograph/allowedContentTypes.js @@ -13,7 +13,7 @@ module.exports = Self => { }); Self.allowedContentTypes = async() => { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const allowedContentTypes = storageConnector.allowedContentTypes; const modelAllowedContentTypes = Self.definition.settings.allowedContentTypes; diff --git a/modules/travel/front/thermograph/create/index.js b/modules/travel/front/thermograph/create/index.js index 4b4cebb9f0..df8d0fe687 100644 --- a/modules/travel/front/thermograph/create/index.js +++ b/modules/travel/front/thermograph/create/index.js @@ -46,7 +46,7 @@ class Controller extends Section { warehouseId: warehouseId, companyId: companyId, dmsTypeId: dmsTypeId, - description: this.$t('FileDescription', { + description: this.$t('TravelFileDescription', { travelId: this.travel.id }).toUpperCase() }; diff --git a/modules/travel/front/thermograph/locale/es.yml b/modules/travel/front/thermograph/locale/es.yml index 0e3bc99fc7..1fdb98c8e7 100644 --- a/modules/travel/front/thermograph/locale/es.yml +++ b/modules/travel/front/thermograph/locale/es.yml @@ -8,7 +8,7 @@ Upload file: Subir fichero Edit file: Editar fichero Upload: Subir File: Fichero -FileDescription: Travel id {{travelId}} +TravelFileDescription: Travel id {{travelId}} ContentTypesInfo: 'Tipos de archivo permitidos: {{allowedContentTypes}}' Are you sure you want to continue?: ¿Seguro que quieres continuar? Add thermograph: Añadir termógrafo diff --git a/modules/worker/back/methods/worker-dms/allowedContentTypes.js b/modules/worker/back/methods/worker-dms/allowedContentTypes.js index 2f5183f926..3d4b908767 100644 --- a/modules/worker/back/methods/worker-dms/allowedContentTypes.js +++ b/modules/worker/back/methods/worker-dms/allowedContentTypes.js @@ -13,7 +13,7 @@ module.exports = Self => { }); Self.allowedContentTypes = async() => { - const storageConnector = Self.app.dataSources.storage.connector; + const storageConnector = Self.app.dataSources.dmsStorage.connector; const allowedContentTypes = storageConnector.allowedContentTypes; const modelAllowedContentTypes = Self.definition.settings.allowedContentTypes; diff --git a/modules/worker/front/descriptor/index.html b/modules/worker/front/descriptor/index.html index fb22644941..bceadff333 100644 --- a/modules/worker/front/descriptor/index.html +++ b/modules/worker/front/descriptor/index.html @@ -1,6 +1,18 @@ + +
+ + + +
+
- \ No newline at end of file + + + + + \ No newline at end of file diff --git a/modules/worker/front/descriptor/index.js b/modules/worker/front/descriptor/index.js index 98f8f2f723..c5dc1ea2c8 100644 --- a/modules/worker/front/descriptor/index.js +++ b/modules/worker/front/descriptor/index.js @@ -2,6 +2,11 @@ import ngModule from '../module'; import Descriptor from 'salix/components/descriptor'; class Controller extends Descriptor { + constructor($element, $, $rootScope) { + super($element, $); + this.$rootScope = $rootScope; + } + get worker() { return this.entity; } @@ -48,8 +53,21 @@ class Controller extends Descriptor { return this.getData(`Workers/${this.id}`, {filter}) .then(res => this.entity = res.data); } + + onUploadResponse() { + const timestamp = new Date().getTime(); + const src = this.$rootScope.imagePath('user', '520x520', this.worker.id); + const zoomSrc = this.$rootScope.imagePath('user', '1600x900', this.worker.id); + const newSrc = `${src}&t=${timestamp}`; + const newZoomSrc = `${zoomSrc}&t=${timestamp}`; + + this.$.photo.setAttribute('src', newSrc); + this.$.photo.setAttribute('zoom-image', newZoomSrc); + } } +Controller.$inject = ['$element', '$scope', '$rootScope']; + ngModule.vnComponent('vnWorkerDescriptor', { template: require('./index.html'), controller: Controller,