diff --git a/back/methods/image/download.js b/back/methods/image/download.js new file mode 100644 index 000000000..6f1e0b8e7 --- /dev/null +++ b/back/methods/image/download.js @@ -0,0 +1,96 @@ +const UserError = require('vn-loopback/util/user-error'); +const fs = require('fs-extra'); + +module.exports = Self => { + Self.remoteMethod('download', { + description: 'Get the user image', + accessType: 'READ', + accepts: [ + { + arg: 'collection', + type: 'String', + description: 'The image collection', + http: {source: 'path'} + }, + { + arg: 'size', + type: 'String', + description: 'The image size', + http: {source: 'path'} + }, + { + arg: 'id', + type: 'Number', + description: 'The user id', + http: {source: 'path'} + } + + ], + returns: [ + { + arg: 'body', + type: 'file', + root: true + }, + { + arg: 'Content-Type', + type: 'String', + http: {target: 'header'} + }, + { + arg: 'Content-Disposition', + type: 'String', + http: {target: 'header'} + } + ], + http: { + path: `/:collection/:size/:id/download`, + verb: 'GET' + } + }); + + Self.download = async function(collection, size, id) { + const models = Self.app.models; + const filter = { + where: { + name: collection}, + include: { + relation: 'readRole' + } + }; + const imageCollection = await models.ImageCollection.findOne(filter); + const entity = await models[imageCollection.model].findById(id, { + fields: ['id', imageCollection.property] + }); + const image = await models.Image.findOne({where: { + collectionFk: collection, + name: entity[imageCollection.property]} + }); + + if (!image) return false; + + const imageRole = imageCollection.readRole().name; + const hasRole = await models.Account.hasRole(id, imageRole); + if (!hasRole) + 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` + }; + } + await fs.access(file.path); + let stream = fs.createReadStream(file.path); + return [stream, file.contentType, `filename="${file.name}"`]; + }; +}; diff --git a/back/methods/image/specs/download.spec.js b/back/methods/image/specs/download.spec.js new file mode 100644 index 000000000..e646d829d --- /dev/null +++ b/back/methods/image/specs/download.spec.js @@ -0,0 +1,21 @@ +const app = require('vn-loopback/server/server'); + +describe('image download()', () => { + const collection = 'user'; + const size = '160x160'; + + it('should return the image content-type of the user', async() => { + const userId = 9; + const image = await app.models.Image.download(collection, size, userId); + const contentType = image[1]; + + expect(contentType).toEqual('image/png'); + }); + + it(`should return false if the user doesn't have image`, async() => { + const userId = 110; + const image = await app.models.Image.download(collection, size, userId); + + expect(image).toBeFalse(); + }); +}); diff --git a/back/models/account.json b/back/models/account.json index 364fe77d2..9cac20bc0 100644 --- a/back/models/account.json +++ b/back/models/account.json @@ -45,6 +45,9 @@ }, "updated": { "type": "date" + }, + "image": { + "type": "String" } }, "relations": { diff --git a/back/models/image-collection.json b/back/models/image-collection.json index 2234766c9..75faaf722 100644 --- a/back/models/image-collection.json +++ b/back/models/image-collection.json @@ -43,7 +43,12 @@ "model": "ImageCollectionSize", "foreignKey": "collectionFk", "property": "id" - } + }, + "readRole": { + "type": "belongsTo", + "model": "Role", + "foreignKey": "readRoleFk" + } }, "acls": [ { diff --git a/back/models/image.js b/back/models/image.js index 113bc7084..f6f4cf5db 100644 --- a/back/models/image.js +++ b/back/models/image.js @@ -3,6 +3,8 @@ const sharp = require('sharp'); const path = require('path'); module.exports = Self => { + require('../methods/image/download')(Self); + Self.getPath = function() { return '/var/lib/salix/image'; }; diff --git a/back/models/image.json b/back/models/image.json index 5b8c76cf1..047e5f5e4 100644 --- a/back/models/image.json +++ b/back/models/image.json @@ -29,6 +29,14 @@ "default": 0 } }, + "relations": { + "collection": { + "type": "belongsTo", + "model": "ImageCollection", + "foreignKey": "collectionFk", + "primaryKey": "name" + } + }, "acls": [ { "accessType": "READ", diff --git a/db/changes/10250-curfew/00-imageCollection.sql b/db/changes/10250-curfew/00-imageCollection.sql new file mode 100644 index 000000000..f08f2f670 --- /dev/null +++ b/db/changes/10250-curfew/00-imageCollection.sql @@ -0,0 +1,5 @@ + +ALTER TABLE `hedera`.`imageCollection` +ADD COLUMN `readRoleFk` VARCHAR(45) NULL DEFAULT NULL AFTER `column`; + +update `hedera`.`imageCollection` set `readRoleFk` = 1; \ No newline at end of file diff --git a/db/changes/10250-curfew/00-user.sql b/db/changes/10250-curfew/00-user.sql new file mode 100644 index 000000000..90bcf8fc5 --- /dev/null +++ b/db/changes/10250-curfew/00-user.sql @@ -0,0 +1,2 @@ +ALTER TABLE `account`.`user` +ADD COLUMN `image` VARCHAR(255) NULL AFTER `password`; diff --git a/db/dump/dumpedFixtures.sql b/db/dump/dumpedFixtures.sql index 2c44b81ba..879b1eb42 100644 --- a/db/dump/dumpedFixtures.sql +++ b/db/dump/dumpedFixtures.sql @@ -443,7 +443,7 @@ USE `hedera`; LOCK TABLES `imageCollection` WRITE; /*!40000 ALTER TABLE `imageCollection` DISABLE KEYS */; -INSERT INTO `imageCollection` VALUES (1,'catalog','Artículo',3840,2160,'Item','image','vn','item','image'),(4,'link','Enlace',200,200,'Link','image','hedera','link','image'),(5,'news','Noticias',800,1200,'New','image','hedera','news','image'); +INSERT INTO `imageCollection` VALUES (1,'catalog','Artículo',3840,2160,'Item','image','vn','item','image'),(4,'link','Enlace',200,200,'Link','image','hedera','link','image'),(5,'news','Noticias',800,1200,'New','image','hedera','news','image'),('6','user','Usuario','800','1200','Account','image','account','user','image'); /*!40000 ALTER TABLE `imageCollection` ENABLE KEYS */; UNLOCK TABLES; diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 2e00878d2..ad8474e38 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -29,8 +29,8 @@ INSERT INTO `vn`.`packagingConfig`(`upperGap`) UPDATE `account`.`role` SET id = 100 WHERE id = 0; CALL `account`.`role_sync`; -INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`) - SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en' +INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`,`email`, `lang`, `image`) + SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en', 'e7723f0b24ff05b32ed09d95196f2f29' FROM `account`.`role` WHERE id <> 20 ORDER BY id; @@ -51,20 +51,20 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType` VALUES (1, 978, 1, 0, 2000, 9, 0); -INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`) +INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`, `image`) VALUES - (101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es'), - (102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en'), - (103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr'), - (104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es'), - (105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt'), - (106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en'), - (107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en'), - (108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en'), - (109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en'), - (110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en'), - (111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'), - (112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'); + (101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'), + (102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), + (103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'), + (104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'), + (105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'), + (106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), + (107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), + (108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), + (109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), + (110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL), + (111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL), + (112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL); INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`) VALUES @@ -2148,3 +2148,10 @@ INSERT INTO `vn`.`campaign`(`code`, `dated`) ('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-11-01')), ('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 YEAR)), '-11-01')); +INSERT INTO `hedera`.`image`(`collectionFk`, `name`) + VALUES + ('user', 'e7723f0b24ff05b32ed09d95196f2f29'); + +INSERT INTO `hedera`.`imageCollectionSize`(`id`, `collectionFk`,`width`, `height`) + VALUES + (1, 4, 160, 160); \ No newline at end of file diff --git a/front/salix/components/layout/index.html b/front/salix/components/layout/index.html index 8c716b84b..cb0fb93b4 100644 --- a/front/salix/components/layout/index.html +++ b/front/salix/components/layout/index.html @@ -31,12 +31,15 @@ ng-if="$ctrl.rightMenu" ng-click="$ctrl.rightMenu.show()"> - + - + translate-attr="{title: 'Account'}" + on-error-src/> + + diff --git a/front/salix/components/layout/index.js b/front/salix/components/layout/index.js index c6bc80734..986f61622 100644 --- a/front/salix/components/layout/index.js +++ b/front/salix/components/layout/index.js @@ -18,6 +18,14 @@ export class Layout extends Component { window.localStorage.currentUserWorkerId = json.data.id; }); } + + getImageUrl() { + if (!this.$.$root.user) return; + + const userId = this.$.$root.user.id; + const token = this.vnToken.token; + return `/api/Images/user/160x160/${userId}/download?access_token=${token}`; + } } Layout.$inject = ['$element', '$scope', 'vnModules']; diff --git a/front/salix/components/layout/index.spec.js b/front/salix/components/layout/index.spec.js index 18aca1f01..71dbb9192 100644 --- a/front/salix/components/layout/index.spec.js +++ b/front/salix/components/layout/index.spec.js @@ -22,4 +22,19 @@ describe('Component vnLayout', () => { expect(controller.$.$root.user.name).toEqual('batman'); }); }); + + describe('getImageUrl()', () => { + it('should return the url image if the user is defined', () => { + controller.$.$root.user = {id: 1}; + const url = controller.getImageUrl(); + + expect(url).not.toBe(3); + }); + + it('should return undefined if the user is not defined', () => { + const url = controller.getImageUrl(); + + expect(url).not.toBeDefined(); + }); + }); }); diff --git a/front/salix/components/layout/style.scss b/front/salix/components/layout/style.scss index 2b879040f..05858b3b1 100644 --- a/front/salix/components/layout/style.scss +++ b/front/salix/components/layout/style.scss @@ -109,6 +109,14 @@ vn-layout { } } } + img { + width: 40px; + border-radius: 50%; + } + .buttonAccount { + background: none; + border: none; + } @media screen and (max-width: $mobile-width) { & > vn-topbar { & > .start > .logo { @@ -147,3 +155,4 @@ vn-layout { font-size: 1.5rem; height: auto; } + diff --git a/front/salix/components/user-popover/index.html b/front/salix/components/user-popover/index.html index 4b02edd9f..9bd0f1411 100644 --- a/front/salix/components/user-popover/index.html +++ b/front/salix/components/user-popover/index.html @@ -13,7 +13,9 @@
- +
diff --git a/front/salix/components/user-popover/index.js b/front/salix/components/user-popover/index.js index c2fb6a130..e4d7b4466 100644 --- a/front/salix/components/user-popover/index.js +++ b/front/salix/components/user-popover/index.js @@ -3,12 +3,13 @@ import './style.scss'; import config from '../../config.json'; class Controller { - constructor($, $translate, vnConfig, vnAuth) { + constructor($, $translate, vnConfig, vnAuth, vnToken) { Object.assign(this, { $, $translate, vnConfig, vnAuth, + vnToken, lang: $translate.use(), langs: [] }); @@ -77,8 +78,12 @@ 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']; +Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth', 'vnToken']; ngModule.vnComponent('vnUserPopover', { template: require('./index.html'), diff --git a/front/salix/components/user-popover/index.spec.js b/front/salix/components/user-popover/index.spec.js index a0f1a953c..7a088fc51 100644 --- a/front/salix/components/user-popover/index.spec.js +++ b/front/salix/components/user-popover/index.spec.js @@ -57,5 +57,13 @@ describe('Salix', () => { expect(controller.companyFk).toBe(4); }); }); + + describe('getImageUrl()', () => { + it('should return de url image', () => { + const url = controller.getImageUrl(); + + expect(url).toBeDefined(); + }); + }); }); }); diff --git a/front/salix/components/user-popover/style.scss b/front/salix/components/user-popover/style.scss index 5f17ed293..9050c6654 100644 --- a/front/salix/components/user-popover/style.scss +++ b/front/salix/components/user-popover/style.scss @@ -11,6 +11,10 @@ font-size: 5rem; color: $color-font-bg-marginal; } + img { + width: 80px; + border-radius: 50%; + } & > div { display: flex; flex-direction: column; diff --git a/modules/ticket/back/models/ticket-last-state.json b/modules/ticket/back/models/ticket-last-state.json index 890d800af..6c8bc66d5 100644 --- a/modules/ticket/back/models/ticket-last-state.json +++ b/modules/ticket/back/models/ticket-last-state.json @@ -11,11 +11,10 @@ "type": "string" }, "ticketFk": { - "id": 1, + "id": true, "type": "Number" }, "ticketTrackingFk": { - "id": 2, "type": "Number" } }, diff --git a/storage/image/user/160x160/e7723f0b24ff05b32ed09d95196f2f29.png b/storage/image/user/160x160/e7723f0b24ff05b32ed09d95196f2f29.png new file mode 100644 index 000000000..2085c85f3 Binary files /dev/null and b/storage/image/user/160x160/e7723f0b24ff05b32ed09d95196f2f29.png differ