2575-image_download #448

Merged
joan merged 14 commits from 2575-image_download into dev 2020-11-18 10:36:17 +00:00
20 changed files with 228 additions and 26 deletions

View File

@ -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}"`];
};
};

View File

@ -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];
bernat marked this conversation as resolved
Review

expect(contentType).toEqual....

expect(contentType).toEqual....
expect(contentType).toEqual('image/png');
});
bernat marked this conversation as resolved Outdated

doesn't

doesn't
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();
});
});

View File

@ -45,6 +45,9 @@
}, },
"updated": { "updated": {
"type": "date" "type": "date"
},
"image": {
"type": "String"
} }
}, },
"relations": { "relations": {

View File

@ -43,7 +43,12 @@
"model": "ImageCollectionSize", "model": "ImageCollectionSize",
"foreignKey": "collectionFk", "foreignKey": "collectionFk",
"property": "id" "property": "id"
} },
"readRole": {
"type": "belongsTo",
"model": "Role",
"foreignKey": "readRoleFk"
}
}, },
"acls": [ "acls": [
{ {

View File

@ -3,6 +3,8 @@ const sharp = require('sharp');
const path = require('path'); const path = require('path');
module.exports = Self => { module.exports = Self => {
require('../methods/image/download')(Self);
Self.getPath = function() { Self.getPath = function() {
return '/var/lib/salix/image'; return '/var/lib/salix/image';
}; };

View File

@ -29,6 +29,14 @@
"default": 0 "default": 0
} }
}, },
"relations": {
"collection": {
"type": "belongsTo",
"model": "ImageCollection",
"foreignKey": "collectionFk",
"primaryKey": "name"
}
},
"acls": [ "acls": [
{ {
"accessType": "READ", "accessType": "READ",

View File

@ -0,0 +1,5 @@
ALTER TABLE `hedera`.`imageCollection`
ADD COLUMN `readRoleFk` VARCHAR(45) NULL DEFAULT NULL AFTER `column`;
update `hedera`.`imageCollection` set `readRoleFk` = 1;

View File

@ -0,0 +1,2 @@
ALTER TABLE `account`.`user`
ADD COLUMN `image` VARCHAR(255) NULL AFTER `password`;

View File

@ -443,7 +443,7 @@ USE `hedera`;
LOCK TABLES `imageCollection` WRITE; LOCK TABLES `imageCollection` WRITE;
/*!40000 ALTER TABLE `imageCollection` DISABLE KEYS */; /*!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 */; /*!40000 ALTER TABLE `imageCollection` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;

View File

@ -29,8 +29,8 @@ INSERT INTO `vn`.`packagingConfig`(`upperGap`)
UPDATE `account`.`role` SET id = 100 WHERE id = 0; UPDATE `account`.`role` SET id = 100 WHERE id = 0;
CALL `account`.`role_sync`; CALL `account`.`role_sync`;
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`)
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en' SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'en', 'e7723f0b24ff05b32ed09d95196f2f29'
FROM `account`.`role` WHERE id <> 20 FROM `account`.`role` WHERE id <> 20
ORDER BY id; ORDER BY id;
@ -51,20 +51,20 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType`
VALUES VALUES
(1, 978, 1, 0, 2000, 9, 0); (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 VALUES
(101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es'), (101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
(102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en'), (102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr'), (103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'),
(104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es'), (104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
(105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt'), (105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'),
(106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en'), (106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en'), (107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en'), (108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en'), (109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en'), (110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL),
(111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'), (111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL),
(112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en'); (112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL);
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`) INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
VALUES 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 -2 YEAR)), '-11-01')),
('allSaints', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -3 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);

View File

@ -31,12 +31,15 @@
ng-if="$ctrl.rightMenu" ng-if="$ctrl.rightMenu"
ng-click="$ctrl.rightMenu.show()"> ng-click="$ctrl.rightMenu.show()">
</vn-icon-button> </vn-icon-button>
<vn-icon-button <button class="buttonAccount">
<img
bernat marked this conversation as resolved
Review

please add the id to the element.

please add the id to the element.
id="user" id="user"
icon="account_circle" ng-src="{{$ctrl.getImageUrl()}}"
ng-click="userPopover.show($event)" ng-click="userPopover.show($event)"
translate-attr="{title: 'Account'}"> translate-attr="{title: 'Account'}"
</vn-icon-button> on-error-src/>
</button>
</div> </div>
<vn-menu vn-id="apps-menu"> <vn-menu vn-id="apps-menu">
<vn-list class="modules-menu"> <vn-list class="modules-menu">

View File

@ -18,6 +18,14 @@ export class Layout extends Component {
window.localStorage.currentUserWorkerId = json.data.id; 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']; Layout.$inject = ['$element', '$scope', 'vnModules'];

View File

@ -22,4 +22,19 @@ describe('Component vnLayout', () => {
expect(controller.$.$root.user.name).toEqual('batman'); 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();
});
});
}); });

View File

@ -109,6 +109,14 @@ vn-layout {
} }
} }
} }
img {
width: 40px;
border-radius: 50%;
}
.buttonAccount {
background: none;
border: none;
}
@media screen and (max-width: $mobile-width) { @media screen and (max-width: $mobile-width) {
& > vn-topbar { & > vn-topbar {
& > .start > .logo { & > .start > .logo {
@ -147,3 +155,4 @@ vn-layout {
font-size: 1.5rem; font-size: 1.5rem;
height: auto; height: auto;
} }

View File

@ -13,7 +13,9 @@
<vn-popover vn-id="popover"> <vn-popover vn-id="popover">
<vn-vertical class="user-popover vn-pa-md"> <vn-vertical class="user-popover vn-pa-md">
<div class="profile-card vn-pb-md"> <div class="profile-card vn-pb-md">
<vn-icon icon="account_circle"></vn-icon> <img
ng-src="{{$ctrl.getImageUrl($root.user.id)}}"
on-error-src/>
<div class="vn-pl-sm"> <div class="vn-pl-sm">
<div> <div>
<div class="user"> <div class="user">

View File

@ -3,12 +3,13 @@ import './style.scss';
import config from '../../config.json'; import config from '../../config.json';
class Controller { class Controller {
constructor($, $translate, vnConfig, vnAuth) { constructor($, $translate, vnConfig, vnAuth, vnToken) {
Object.assign(this, { Object.assign(this, {
$, $,
$translate, $translate,
vnConfig, vnConfig,
vnAuth, vnAuth,
vnToken,
lang: $translate.use(), lang: $translate.use(),
langs: [] langs: []
}); });
@ -77,8 +78,12 @@ class Controller {
this.$.companies.refresh(); this.$.companies.refresh();
this.$.popover.show(event.target); 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', { ngModule.vnComponent('vnUserPopover', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -57,5 +57,13 @@ describe('Salix', () => {
expect(controller.companyFk).toBe(4); expect(controller.companyFk).toBe(4);
}); });
}); });
describe('getImageUrl()', () => {
it('should return de url image', () => {
const url = controller.getImageUrl();
expect(url).toBeDefined();
});
});
}); });
}); });

View File

@ -11,6 +11,10 @@
font-size: 5rem; font-size: 5rem;
color: $color-font-bg-marginal; color: $color-font-bg-marginal;
} }
img {
width: 80px;
border-radius: 50%;
}
& > div { & > div {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -11,11 +11,10 @@
"type": "string" "type": "string"
}, },
"ticketFk": { "ticketFk": {
"id": 1, "id": true,
"type": "Number" "type": "Number"
}, },
"ticketTrackingFk": { "ticketTrackingFk": {
"id": 2,
"type": "Number" "type": "Number"
} }
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB