feat: refs #7346 #2864

Merged
jgallego merged 10 commits from 7346 into dev 2024-08-22 09:30:28 +00:00
174 changed files with 107 additions and 5819 deletions
Showing only changes of commit ccfb53891f - Show all commits

View File

@ -118,6 +118,9 @@
"NotificationSubscription": { "NotificationSubscription": {
"dataSource": "vn" "dataSource": "vn"
}, },
"OrmConfig": {
"dataSource": "vn"
},
"Province": { "Province": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -0,0 +1,26 @@
{
"name": "OrmConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "ormConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"selectLimit": {
"type": "number"
}
},
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
}
]
}

View File

@ -3,4 +3,5 @@ apps:
name: salix-back name: salix-back
instances: 1 instances: 1
max_restarts: 0 max_restarts: 0
autorestart: false
node_args: --tls-min-v1.0 --openssl-legacy-provider node_args: --tls-min-v1.0 --openssl-legacy-provider

View File

@ -21,6 +21,7 @@ BEGIN
AND a.id IS NULL AND a.id IS NULL
AND u.active AND u.active
AND c.created < util.VN_CURDATE() - INTERVAL vMonths MONTH AND c.created < util.VN_CURDATE() - INTERVAL vMonths MONTH
AND NOT u.role = (SELECT id FROM `role` WHERE name = 'supplier')
AND u.id NOT IN ( AND u.id NOT IN (
SELECT DISTINCT c.id SELECT DISTINCT c.id
FROM client c FROM client c

View File

@ -12,7 +12,7 @@ BEGIN
DECLARE vSaleFk INT; DECLARE vSaleFk INT;
DECLARE vSectorFk INT; DECLARE vSectorFk INT;
DECLARE vSales CURSOR FOR DECLARE vSales CURSOR FOR
SELECT s.id SELECT DISTINCT s.id
FROM sectorCollectionSaleGroup sc FROM sectorCollectionSaleGroup sc
JOIN saleGroupDetail sg ON sg.saleGroupFk = sc.saleGroupFk JOIN saleGroupDetail sg ON sg.saleGroupFk = sc.saleGroupFk
JOIN sale s ON sg.saleFk = s.id JOIN sale s ON sg.saleFk = s.id

View File

@ -0,0 +1,8 @@
USE vn;
CREATE TABLE IF NOT EXISTS ormConfig (
id int(5) NOT NULL AUTO_INCREMENT primary key,
selectLimit int(5) NOT NULL
);
INSERT IGNORE INTO ormConfig SET selectLimit = 1000;

View File

@ -1,28 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn summary path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'invoiceIn');
await page.accessToSearchResult('1');
});
afterAll(async() => {
await browser.close();
});
it('should reach the summary section', async() => {
await page.waitForState('invoiceIn.card.summary');
});
it('should contain some basic data from the invoice', async() => {
const result = await page.waitToGetProperty(selectors.invoiceInSummary.supplierRef, 'innerText');
expect(result).toEqual('1234');
});
});

View File

@ -1,52 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn descriptor path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'invoiceIn');
await page.accessToSearchResult('10');
await page.accessToSection('invoiceIn.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it('should clone the invoiceIn using the descriptor more menu', async() => {
await page.waitToClick(selectors.invoiceInDescriptor.moreMenu);
await page.waitToClick(selectors.invoiceInDescriptor.moreMenuCloneInvoiceIn);
await page.waitToClick(selectors.invoiceInDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('InvoiceIn cloned');
});
it('should have been redirected to the created invoiceIn summary', async() => {
await page.waitForState('invoiceIn.card.summary');
});
it('should delete the cloned invoiceIn using the descriptor more menu', async() => {
await page.waitToClick(selectors.invoiceInDescriptor.moreMenu);
await page.waitToClick(selectors.invoiceInDescriptor.moreMenuDeleteInvoiceIn);
await page.waitToClick(selectors.invoiceInDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('InvoiceIn deleted');
});
it('should have been relocated to the invoiceOut index', async() => {
await page.waitForState('invoiceIn.index');
});
it(`should search for the deleted invouceOut to find no results`, async() => {
await page.doSearch('10');
const nResults = await page.countElement(selectors.invoiceOutIndex.searchResult);
expect(nResults).toEqual(0);
});
});

View File

@ -1,196 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn basic data path', () => {
let browser;
let page;
let newDms;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'invoiceIn');
await page.accessToSearchResult('1');
await page.accessToSection('invoiceIn.card.basicData');
});
afterAll(async() => {
await browser.close();
});
it(`should edit the invoiceIn basic data`, async() => {
const now = Date.vnNew();
await page.pickDate(selectors.invoiceInBasicData.issued, now);
await page.pickDate(selectors.invoiceInBasicData.operated, now);
await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura');
await page.clearInput(selectors.invoiceInBasicData.supplierRef);
await page.write(selectors.invoiceInBasicData.supplierRef, '9999');
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.write(selectors.invoiceInBasicData.dms, '2');
await page.pickDate(selectors.invoiceInBasicData.bookEntried, now);
await page.pickDate(selectors.invoiceInBasicData.booked, now);
await page.autocompleteSearch(selectors.invoiceInBasicData.currency, 'USD');
await page.autocompleteSearch(selectors.invoiceInBasicData.company, 'ORN');
await page.waitToClick(selectors.invoiceInBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the invoiceIn supplier was edited`, async() => {
await page.reloadSection('invoiceIn.card.basicData');
const result = await page.waitToGetProperty(selectors.invoiceInBasicData.supplier, 'value');
expect(result).toContain('Verdnatura');
});
it(`should confirm the invoiceIn supplierRef was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.supplierRef, 'value');
expect(result).toEqual('9999');
});
it(`should confirm the invoiceIn currency was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.currency, 'value');
expect(result).toEqual('USD');
});
it(`should confirm the invoiceIn company was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.company, 'value');
expect(result).toEqual('ORN');
});
it(`should confirm the invoiceIn dms was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
expect(result).toEqual('2');
});
it(`should create a new invoiceIn dms and save the changes`, async() => {
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.waitToClick(selectors.invoiceInBasicData.create);
await page.clearInput(selectors.invoiceInBasicData.reference);
await page.write(selectors.invoiceInBasicData.reference, 'New Dms');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
let message = await page.waitForSnackbar();
await page.clearInput(selectors.invoiceInBasicData.companyId);
await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'VNL');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
await page.clearInput(selectors.invoiceInBasicData.warehouseId);
await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Warehouse One');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
await page.clearInput(selectors.invoiceInBasicData.dmsTypeId);
await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Ticket');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
await page.waitToClick(selectors.invoiceInBasicData.description);
await page.write(selectors.invoiceInBasicData.description, 'Dms without edition.');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('The files can\'t be empty');
let currentDir = process.cwd();
let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`;
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.waitToClick(selectors.invoiceInBasicData.inputFile)
]);
await fileChooser.accept([filePath]);
await page.waitToClick(selectors.invoiceInBasicData.confirm);
message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
newDms = await page
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
});
it(`should confirm the invoiceIn was edited with the new dms`, async() => {
await page.reloadSection('invoiceIn.card.basicData');
const result = await page
.waitToGetProperty(selectors.invoiceInBasicData.dms, 'value');
expect(result).toEqual(newDms);
});
it(`should edit the invoiceIn`, async() => {
await page.waitToClick(selectors.invoiceInBasicData.edit);
await page.clearInput(selectors.invoiceInBasicData.reference);
await page.write(selectors.invoiceInBasicData.reference, 'Dms Edited');
await page.clearInput(selectors.invoiceInBasicData.companyId);
await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'CCs');
await page.clearInput(selectors.invoiceInBasicData.warehouseId);
await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Algemesi');
await page.clearInput(selectors.invoiceInBasicData.dmsTypeId);
await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Basura');
await page.waitToClick(selectors.invoiceInBasicData.description);
await page.write(selectors.invoiceInBasicData.description, ' Nevermind, now is edited.');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
let message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it(`should confirm the new dms has been edited`, async() => {
await page.reloadSection('invoiceIn.card.basicData');
await page.waitToClick(selectors.invoiceInBasicData.edit);
const reference = await page
.waitToGetProperty(selectors.invoiceInBasicData.reference, 'value');
const companyId = await page
.waitToGetProperty(selectors.invoiceInBasicData.companyId, 'value');
const warehouseId = await page
.waitToGetProperty(selectors.invoiceInBasicData.warehouseId, 'value');
const dmsTypeId = await page
.waitToGetProperty(selectors.invoiceInBasicData.dmsTypeId, 'value');
const description = await page
.waitToGetProperty(selectors.invoiceInBasicData.description, 'value');
expect(reference).toEqual('Dms Edited');
expect(companyId).toEqual('CCs');
expect(warehouseId).toEqual('Algemesi');
expect(dmsTypeId).toEqual('Basura');
expect(description).toEqual('Dms without edition. Nevermind, now is edited.');
await page.waitToClick(selectors.invoiceInBasicData.confirm);
});
it(`should disable edit and download if dms doesn't exists, and set back the original dms`, async() => {
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.write(selectors.invoiceInBasicData.dms, '9999');
await page.waitForSelector(`${selectors.invoiceInBasicData.download}.disabled`);
await page.waitForSelector(`${selectors.invoiceInBasicData.edit}.disabled`);
await page.clearInput(selectors.invoiceInBasicData.dms);
await page.write(selectors.invoiceInBasicData.dms, '1');
await page.waitToClick(selectors.invoiceInBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,59 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn tax path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('developer', 'invoiceIn');
await page.accessToSearchResult('2');
await page.accessToSection('invoiceIn.card.tax');
});
afterAll(async() => {
await browser.close();
});
it('should add a new tax and check it', async() => {
await page.waitToClick(selectors.invoiceInTax.addTaxButton);
await page.autocompleteSearch(selectors.invoiceInTax.thirdExpense, '6210000567');
await page.write(selectors.invoiceInTax.thirdTaxableBase, '100');
await page.autocompleteSearch(selectors.invoiceInTax.thirdTaxType, 'H.P. IVA');
await page.autocompleteSearch(selectors.invoiceInTax.thirdTransactionType, 'Operaciones exentas');
await page.waitToClick(selectors.invoiceInTax.saveButton);
const message = await page.waitForSnackbar();
await page.waitToClick(selectors.invoiceInDescriptor.summaryIcon);
await page.waitForState('invoiceIn.card.summary');
const total = await page.waitToGetProperty(selectors.invoiceInSummary.totalTaxableBase, 'innerText');
await page.accessToSection('invoiceIn.card.tax');
const thirdExpense = await page.waitToGetProperty(selectors.invoiceInTax.thirdExpense, 'value');
const thirdTaxableBase = await page.waitToGetProperty(selectors.invoiceInTax.thirdTaxableBase, 'value');
const thirdTaxType = await page.waitToGetProperty(selectors.invoiceInTax.thirdTaxType, 'value');
const thirdTransactionType = await page.waitToGetProperty(selectors.invoiceInTax.thirdTransactionType, 'value');
const thirdRate = await page.waitToGetProperty(selectors.invoiceInTax.thirdRate, 'value');
expect(message.text).toContain('Data saved!');
expect(total).toEqual('Taxable base €1,323.16');
expect(thirdExpense).toEqual('6210000567');
expect(thirdTaxableBase).toEqual('100');
expect(thirdTaxType).toEqual('H.P. IVA 4% CEE');
expect(thirdTransactionType).toEqual('Operaciones exentas');
expect(thirdRate).toEqual('€4.00');
});
it('should delete the added line', async() => {
await page.waitToClick(selectors.invoiceInTax.thirdDeleteButton);
await page.waitToClick(selectors.invoiceInTax.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,48 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn serial path', () => {
let browser;
let page;
let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'invoiceIn');
await page.accessToSection('invoiceIn.serial');
page.on('request', req => {
if (req.url().includes(`InvoiceIns/getSerial`))
httpRequest = req.url();
});
});
afterAll(async() => {
await browser.close();
});
it('should check that passes the correct params to back', async() => {
await page.overwrite(selectors.invoiceInSerial.daysAgo, '30');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('daysAgo=30');
await page.overwrite(selectors.invoiceInSerial.serial, 'R');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('serial=R');
await page.click(selectors.invoiceInSerial.chip);
});
it('should go to index and check if the search-panel has the correct params', async() => {
await page.waitToClick(selectors.invoiceInSerial.goToIndex);
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
const serial = await params[0].getProperty('title');
const isBooked = await params[1].getProperty('title');
const from = await params[2].getProperty('title');
expect(await serial.jsonValue()).toContain('serial');
expect(await isBooked.jsonValue()).toContain('not isBooked');
expect(await from.jsonValue()).toContain('from');
});
});

View File

@ -1,164 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account create and basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('itManagement', 'account');
});
afterAll(async() => {
await browser.close();
});
it('should open the new account form by clicking the add button', async() => {
await page.waitToClick(selectors.accountIndex.addAccount);
await page.waitForState('account.create');
});
it('should fill the form and then save it by clicking the create button', async() => {
await page.write(selectors.accountIndex.newName, 'remy');
await page.write(selectors.accountIndex.newNickname, 'Gambit');
await page.write(selectors.accountIndex.newEmail, 'RemyEtienneLeBeau@verdnatura.es');
await page.autocompleteSearch(selectors.accountIndex.newRole, 'Trainee');
await page.write(selectors.accountIndex.newPassword, 'cestlavie');
await page.waitToClick(selectors.accountIndex.createAccountButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should redirect the user to the created account basic data section', async() => {
await page.waitForState('account.card.basicData');
});
it('should check the name is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.name, 'value');
expect(result).toEqual('remy');
});
it('should check the nickname is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.nickname, 'value');
expect(result).toEqual('Gambit');
});
it('should check the email is as expected', async() => {
const result = await page.waitToGetProperty(selectors.accountBasicData.email, 'value');
expect(result).toEqual('RemyEtienneLeBeau@verdnatura.es');
});
it('should navigate to the roles section to check the roles are correct', async() => {
await page.accessToSection('account.card.roles');
const rolesCount = await page.countElement(selectors.accountRoles.anyResult);
expect(rolesCount).toEqual(3);
});
describe('Descriptor option', () => {
describe('activate account', () => {
it(`should check the active account icon isn't present in the descriptor`, async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeAccountIcon, 0);
});
it('should activate the account using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.activateAccount);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Account enabled!');
});
it('should check the active account icon is now present in the descriptor', async() => {
await page.waitForSelector(selectors.accountDescriptor.activeAccountIcon, {visible: false});
});
});
describe('deactivate user', () => {
it(`should check the inactive user icon isn't present in the descriptor just yet`, async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
});
it('should deactivate the user using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.deactivateUser);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('User deactivated!');
});
it('should check the inactive user icon is now present', async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 1);
});
});
describe('activate user', () => {
it('should activate the user using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.activateUser);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('User activated!');
});
it('should check the inactive user icon is not present anymore', async() => {
await page.waitForNumberOfElements(selectors.accountDescriptor.activeUserIcon, 0);
});
});
describe('mail forwarding', () => {
it('should activate the mail forwarding and set the recipent email', async() => {
await page.accessToSection('account.card.mailForwarding');
await page.waitToClick(selectors.accountMailForwarding.mailForwardingCheckbox);
await page.write(selectors.accountMailForwarding.email, 'someEmail@someDomain.es');
await page.waitToClick(selectors.accountMailForwarding.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});
describe('Set password', () => {
it('should set the password using the descriptor menu', async() => {
const newPassword = 'quantum.12345';
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.setPassword);
await page.write(selectors.accountDescriptor.newPassword, newPassword);
await page.write(selectors.accountDescriptor.repeatPassword, newPassword);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Password changed succesfully!');
});
// cant log into created account for unknown reasons
// it('should login into the created account with the new password', async() => {
// await page.loginAndModule('Remy', 'quantum.crypt0graphy');
// });
});
describe('delete account', () => {
// it('should navigate to the account basic data section', async() => {
// });
it('should delete the account using the descriptor menu', async() => {
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.deleteAccount);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('User removed');
});
});
});
});

View File

@ -1,66 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Alias create and basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('itManagement', 'account');
await page.accessToSection('account.alias');
});
afterAll(async() => {
await browser.close();
});
it('should open the new account alias form by clicking the add button', async() => {
await page.waitToClick(selectors.accountAliasIndex.addAlias);
await page.waitForState('account.alias.create');
});
it('should fill the form and then save it by clicking the create alias button', async() => {
await page.write(selectors.accountAliasIndex.newName, 'Boring alias');
await page.write(selectors.accountAliasIndex.newDescription, 'Boring description');
await page.waitToClick(selectors.accountAliasIndex.createAliasButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should redirect the user to the created account alias basic data section', async() => {
await page.waitForState('account.alias.card.basicData');
});
it('should edit the alias basic data', async() => {
await page.overwrite(selectors.accountAliasBasicData.name, 'Psykers');
await page.overwrite(selectors.accountAliasBasicData.description, 'Email group for psykers');
await page.waitToClick(selectors.accountAliasBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the basicData section and check the name was edited successfully', async() => {
await page.reloadSection('account.alias.card.basicData');
const result = await page.waitToGetProperty(selectors.accountAliasBasicData.name, 'value');
expect(result).toEqual('Psykers');
});
it('should check the alias description was edited successfully', async() => {
const result = await page.waitToGetProperty(selectors.accountAliasBasicData.description, 'value');
expect(result).toContain('psykers');
});
it('should search IT alias then access the user section to check the role listed is the expected one', async() => {
await page.accessToSearchResult('IT');
await page.accessToSection('account.alias.card.users');
const rolesCount = await page.countElement(selectors.accountAliasUsers.anyResult);
expect(rolesCount).toEqual(1);
});
});

View File

@ -1,86 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Role create and basic data path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('it', 'account');
await page.accessToSection('account.role');
});
afterAll(async() => {
await browser.close();
});
it('should open the new account role form by clicking the add button', async() => {
await page.waitToClick(selectors.accountRoleIndex.addRole);
await page.waitForState('account.role.create');
});
it('should fill the form and then save it by clicking the create role button', async() => {
await page.write(selectors.accountRoleIndex.newName, 'boringRole');
await page.write(selectors.accountRoleIndex.newDescription, 'Boring description');
await page.waitToClick(selectors.accountRoleIndex.createRoleButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should redirect the user to the created role basic data section', async() => {
await page.waitForState('account.role.card.basicData');
});
it('should edit the role basic data', async() => {
await page.overwrite(selectors.accountRoleBasicData.name, 'psyker');
await page.overwrite(selectors.accountRoleBasicData.description, 'A role just for psykers');
await page.waitToClick(selectors.accountRoleBasicData.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the role basicData section and check the name was edited successfully', async() => {
await page.reloadSection('account.role.card.basicData');
const result = await page.waitToGetProperty(selectors.accountRoleBasicData.name, 'value');
expect(result).toEqual('psyker');
});
it('should check the role description was edited successfully', async() => {
const result = await page.waitToGetProperty(selectors.accountRoleBasicData.description, 'value');
expect(result).toContain('psykers');
});
it('should navigate to the subroles section', async() => {
await page.accessToSection('account.role.card.subroles');
});
it('should asign a subrole', async() => {
await page.waitToClick(selectors.accountSubroles.addSubrole);
await page.autocompleteSearch(selectors.accountSubroles.role, 'teamManager');
await page.waitToClick(selectors.accountSubroles.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Role added!');
});
it('should reload the subroles section and check a role was added', async() => {
await page.reloadSection('account.role.card.subroles');
const subrolesCount = await page.countElement(selectors.accountSubroles.anyResult);
expect(subrolesCount).toEqual(1);
});
it('should access the employee roles inheritance then check the roles listed are the expected ones', async() => {
await page.accessToSearchResult('employee');
await page.accessToSection('account.role.card.inherited');
const rolesCount = await page.countElement(selectors.accountRoleInheritance.anyResult);
expect(rolesCount).toEqual(7);
});
});

View File

@ -1,60 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account ACL path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('developer', 'account');
await page.accessToSection('account.acl');
});
afterAll(async() => {
await browser.close();
});
it('should go to create new acl', async() => {
await page.waitToClick(selectors.accountAcl.addAcl);
await page.waitForState('account.acl.create');
});
it('should create new acl', async() => {
await page.autocompleteSearch(selectors.accountAcl.role, 'sysadmin');
await page.autocompleteSearch(selectors.accountAcl.model, 'Account');
await page.autocompleteSearch(selectors.accountAcl.accessType, '*');
await page.autocompleteSearch(selectors.accountAcl.permission, 'ALLOW');
await page.waitToClick(selectors.accountAcl.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should navigate to edit', async() => {
await page.doSearch();
await page.waitToClick(selectors.accountAcl.thirdAcl);
await page.waitForState('account.acl.edit');
});
it('should edit the third acl', async() => {
await page.autocompleteSearch(selectors.accountAcl.model, 'Supplier');
await page.autocompleteSearch(selectors.accountAcl.accessType, 'READ');
await page.waitToClick(selectors.accountAcl.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should delete the third result', async() => {
const result = await page.waitToGetProperty(selectors.accountAcl.thirdAcl, 'innerText');
await page.waitToClick(selectors.accountAcl.deleteThirdAcl);
await page.waitToClick(selectors.globalItems.acceptButton);
const message = await page.waitForSnackbar();
const newResult = await page.waitToGetProperty(selectors.accountAcl.thirdAcl, 'innerText');
expect(message.text).toContain('ACL removed');
expect(result).not.toEqual(newResult);
});
});

View File

@ -1,25 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Connections path', () => {
let browser;
let page;
const account = 'sysadmin';
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule(account, 'account');
await page.accessToSection('account.connections');
});
afterAll(async() => {
await browser.close();
});
it('should check this is the last connection', async() => {
const firstResult = await page.waitToGetProperty(selectors.accountConnections.firstConnection, 'innerText');
expect(firstResult).toContain(account);
});
});

View File

@ -1,37 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Accounts path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.accounts');
});
afterAll(async() => {
await browser.close();
});
it('should sync roles', async() => {
await page.waitToClick(selectors.accountAccounts.syncRoles);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Roles synchronized!');
});
it('should relogin', async() => {
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.accounts');
});
it('should sync all', async() => {
await page.waitToClick(selectors.accountAccounts.syncAll);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Synchronizing in the background');
});
});

View File

@ -1,41 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account LDAP path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.ldap');
});
afterAll(async() => {
await browser.close();
});
it('should set data and save', async() => {
await page.waitToClick(selectors.accountLdap.checkEnable);
await page.write(selectors.accountLdap.server, '1234');
await page.write(selectors.accountLdap.rdn, '1234');
await page.write(selectors.accountLdap.password, 'nightmare');
await page.write(selectors.accountLdap.userDn, 'sysadmin');
await page.write(selectors.accountLdap.groupDn, '1234');
await page.waitToClick(selectors.accountLdap.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reset data', async() => {
await page.waitToClick(selectors.accountLdap.checkEnable);
await page.waitToClick(selectors.accountLdap.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,42 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account Samba path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('sysadmin', 'account');
await page.accessToSection('account.samba');
});
afterAll(async() => {
await browser.close();
});
it('should set data and save', async() => {
await page.waitToClick(selectors.accountSamba.checkEnable);
await page.write(selectors.accountSamba.adDomain, '1234');
await page.write(selectors.accountSamba.adController, '1234');
await page.write(selectors.accountSamba.adUser, 'sysadmin');
await page.write(selectors.accountSamba.adPassword, 'nightmare');
await page.write(selectors.accountSamba.userDn, 'testDn');
await page.waitToClick(selectors.accountSamba.verifyCert);
await page.waitToClick(selectors.accountSamba.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reset data', async() => {
await page.waitToClick(selectors.accountSamba.checkEnable);
await page.waitToClick(selectors.accountSamba.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
});

View File

@ -1,112 +0,0 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Account privileges path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('developer', 'account');
await page.accessToSearchResult('1101');
await page.accessToSection('account.card.privileges');
});
afterAll(async() => {
await browser.close();
});
describe('as developer', () => {
it('should throw error when give privileges', async() => {
await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have grant privilege`);
});
it('should throw error when change role', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'employee');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't have grant privilege`);
});
});
describe('as sysadmin', () => {
beforeAll(async() => {
await page.loginAndModule('sysadmin', 'account');
await page.accessToSearchResult('9');
await page.accessToSection('account.card.privileges');
});
it('should give privileges', async() => {
await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.checkboxState(selectors.accountPrivileges.checkHasGrant);
expect(message.text).toContain(`Data saved!`);
expect(result).toBe('checked');
});
it('should throw error when change role and not own role', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'itBoss');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`You don't own the role and you can't assign it to another user`);
});
it('should change role to employee', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'employee');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.waitToGetProperty(selectors.accountPrivileges.role, 'value');
expect(message.text).toContain(`Data saved!`);
expect(result).toContain('employee');
});
it('should return role to developer', async() => {
await page.autocompleteSearch(selectors.accountPrivileges.role, 'developer');
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
await page.reloadSection('account.card.privileges');
const result = await page.waitToGetProperty(selectors.accountPrivileges.role, 'value');
expect(message.text).toContain(`Data saved!`);
expect(result).toContain('developer');
});
});
describe('as developer again', () => {
it('should remove privileges', async() => {
await page.accessToSearchResult('9');
await page.accessToSection('account.card.privileges');
await page.waitToClick(selectors.accountPrivileges.checkHasGrant);
await page.waitToClick(selectors.accountPrivileges.save);
const message = await page.waitForSnackbar();
expect(message.text).toContain(`Data saved!`);
});
it('should logIn in developer', async() => {
await page.reloadSection('account.card.privileges');
const result = await page.checkboxState(selectors.accountPrivileges.checkHasGrant);
expect(result).toBe('unchecked');
});
});
});

View File

@ -38,7 +38,7 @@
</vn-icon-button> </vn-icon-button>
</div> </div>
<a <a
ui-sref="worker.card.summary({id: $root.user.id})" ng-click="$ctrl.redirect($root.user.id)"
class="vn-button colored" class="vn-button colored"
translate> translate>
My account My account

View File

@ -82,6 +82,9 @@ class Controller {
? {id: $search} ? {id: $search}
: {bank: {like: '%' + $search + '%'}}; : {bank: {like: '%' + $search + '%'}};
} }
async redirect(id) {
window.location.href = await this.vnConfig.vnApp.getUrl(`worker/${id}`);
}
} }
Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth', 'vnToken']; Controller.$inject = ['$scope', '$translate', 'vnConfig', 'vnAuth', 'vnToken'];

View File

@ -13,7 +13,6 @@ module.exports = function(Self) {
Object.assign(Self, { Object.assign(Self, {
setup() { setup() {
Self.super_.setup.call(this); Self.super_.setup.call(this);
/** /**
* Setting a global transaction timeout to find out if the service * Setting a global transaction timeout to find out if the service
* is blocked because the connection pool is empty. * is blocked because the connection pool is empty.
@ -28,6 +27,26 @@ module.exports = function(Self) {
}; };
}); });
this.beforeRemote('**', async ctx => {
if (!this.hasFilter(ctx)) return;
const defaultLimit = this.app.orm.selectLimit;
const filter = ctx.args.filter || {limit: defaultLimit};
if (filter.limit > defaultLimit) {
filter.limit = defaultLimit;
ctx.args.filter = filter;
}
});
this.afterRemote('**', async ctx => {
if (!this.hasFilter(ctx)) return;
const {result} = ctx;
const length = Array.isArray(result) ? result.length : result ? 1 : 0;
if (length >= this.app.orm.selectLimit) throw new UserError('Too many records');
});
// Register field ACL validation // Register field ACL validation
/* /*
this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx)); this.beforeRemote('prototype.patchAttributes', ctx => this.checkUpdateAcls(ctx));
@ -327,6 +346,12 @@ module.exports = function(Self) {
checkInsertAcls(ctx) { checkInsertAcls(ctx) {
return this.checkAcls(ctx, 'insert'); return this.checkAcls(ctx, 'insert');
},
hasFilter(ctx) {
return ctx.req.method.toUpperCase() === 'GET' &&
ctx.method.accepts.some(x => x.arg === 'filter' && x.type.toLowerCase() === 'object');
} }
}); });
}; };

View File

@ -369,5 +369,6 @@
"Cannot send mail": "Não é possível enviar o email", "Cannot send mail": "Não é possível enviar o email",
"CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos", "CONSTRAINT `supplierAccountTooShort` failed for `vn`.`supplier`": "La cuenta debe tener exactamente 10 dígitos",
"The sale not exists in the item shelving": "La venta no existe en la estantería del artículo", "The sale not exists in the item shelving": "La venta no existe en la estantería del artículo",
"The entry not have stickers": "La entrada no tiene etiquetas" "The entry not have stickers": "La entrada no tiene etiquetas",
"Too many records": "Demasiados registros"
} }

View File

@ -0,0 +1,6 @@
module.exports = async function(app) {
if (!app.orm) {
const ormConfig = await app.models.OrmConfig.findOne();
app.orm = ormConfig;
}
};

View File

@ -1,75 +0,0 @@
<vn-watcher
vn-id="watcher"
url="AccountConfigs"
data="$ctrl.config"
id-value="1"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg" vn-focus>
<vn-vertical>
<vn-textfield
label="Homedir base"
ng-model="$ctrl.config.homedir"
rule="AccountConfig">
</vn-textfield>
<vn-textfield
label="Shell"
ng-model="$ctrl.config.shell"
rule="AccountConfig">
</vn-textfield>
<vn-input-number
label="User and role base id"
ng-model="$ctrl.config.idBase"
rule="AccountConfig">
</vn-input-number>
<vn-horizontal>
<vn-input-number
label="Min"
ng-model="$ctrl.config.min"
rule="AccountConfig">
</vn-input-number>
<vn-input-number
label="Max"
ng-model="$ctrl.config.max"
rule="AccountConfig">
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
label="Warn"
ng-model="$ctrl.config.warn"
rule="AccountConfig">
</vn-input-number>
<vn-input-number
label="Inact"
ng-model="$ctrl.config.inact"
rule="AccountConfig">
</vn-input-number>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
label="Synchronize all"
ng-click="$ctrl.onSynchronizeAll()">
</vn-button>
<vn-button
label="Synchronize roles"
ng-click="$ctrl.onSynchronizeRoles()">
</vn-button>
<vn-button
disabled="!watcher.dataChanged()"
class="cancel"
label="Undo changes"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,19 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSynchronizeAll() {
this.vnApp.showSuccess(this.$t('Synchronizing in the background'));
this.$http.patch(`Accounts/syncAll`);
}
onSynchronizeRoles() {
this.$http.patch(`RoleInherits/sync`)
.then(() => this.vnApp.showSuccess(this.$t('Roles synchronized!')));
}
}
ngModule.component('vnAccountAccounts', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,14 +0,0 @@
Accounts: Cuentas
Homedir base: Directorio base para carpetas de usuario
Shell: Intérprete de línea de comandos
User and role base id: Id base usuarios y roles
Synchronize all: Sincronizar todo
Synchronize roles: Sincronizar roles
If password is not specified, just user attributes are synchronized: >-
Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
Synchronizing in the background: Sincronizando en segundo plano
Users synchronized!: ¡Usuarios sincronizados!
Username: Nombre de usuario
Synchronize: Sincronizar
Please enter the username: Por favor introduce el nombre de usuario
Roles synchronized!: ¡Roles sincronizados!

View File

@ -1,70 +0,0 @@
<vn-watcher
vn-id="watcher"
url="ACLs"
data="$ctrl.acl"
id-value="$ctrl.$params.id"
insert-mode="!$ctrl.$params.id"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="watcher.submitGo('account.acl')"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete
label="Role"
ng-model="$ctrl.acl.principalId"
url="VnRoles"
id-field="name"
value-field="name"
vn-focus>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Model"
ng-model="$ctrl.acl.model"
data="$ctrl.models"
id-field="name"
value-field="name">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Property"
ng-model="$ctrl.acl.property"
info="Use * to match all properties">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Access type"
ng-model="$ctrl.acl.accessType"
data="$ctrl.accessTypes"
id-field="name"
value-field="name">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Permission"
ng-model="$ctrl.acl.permission"
data="$ctrl.permissions"
id-field="name"
value-field="name">
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="account.acl">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,33 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor(...args) {
super(...args);
this.accessTypes = [
{name: '*'},
{name: 'READ'},
{name: 'WRITE'}
];
this.permissions = [
{name: 'ALLOW'},
{name: 'DENY'}
];
this.models = [];
for (let model in window.validations)
this.models.push({name: model});
this.acl = {
property: '*',
principalType: 'ROLE',
accessType: 'READ',
permission: 'ALLOW'
};
}
}
ngModule.component('vnAclCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,4 +0,0 @@
import './main';
import './index/';
import './create';
import './search-panel';

View File

@ -1,51 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<vn-list class="separated">
<a
ng-repeat="row in model.data track by row.id"
ui-sref="account.acl.edit(::{id: row.id})"
translate-attr="{title: 'Edit ACL'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::row.model}}.{{::row.property}}</h6>
<vn-label-value
label="Role"
value="{{::row.principalId}}">
</vn-label-value>
<vn-label-value
label="Access type"
value="{{::row.accessType}}">
</vn-label-value>
<vn-label-value
label="Permission"
value="{{::row.permission}}">
</vn-label-value>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="deleteAcl.show(row)"
vn-tooltip="Delete"
icon="delete">
</vn-icon-button>
</vn-item-section>
</a>
</vn-list>
</vn-card>
</vn-data-viewer>
<a ui-sref="account.acl.create"
vn-tooltip="New ACL"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-confirm
vn-id="deleteAcl"
on-accept="$ctrl.onDelete($data)"
question="Are you sure you want to continue?"
message="ACL will be removed">
</vn-confirm>

View File

@ -1,15 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onDelete(row) {
return this.$http.delete(`ACLs/${row.id}`)
.then(() => this.$.model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('ACL removed')));
}
}
ngModule.component('vnAclIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,4 +0,0 @@
New ACL: Nuevo ACL
Edit ACL: Editar ACL
ACL will be removed: El ACL será eliminado
ACL removed: ACL eliminado

View File

@ -1,4 +0,0 @@
Model: Modelo
Property: Propiedad
Access type: Tipo de acceso
Permission: Permiso

View File

@ -1,20 +0,0 @@
<vn-crud-model
vn-id="model"
url="ACLs"
limit="20"
expr-builder="$ctrl.exprBuilder(param, value)"
auto-save="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search ACL by model name"
panel="vn-acl-search-panel"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="account.acl"
entity-state="edit">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-acl-index></vn-acl-index>
</ui-view>

View File

@ -1,18 +0,0 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class ACL extends ModuleMain {
exprBuilder(param, value) {
switch (param) {
case 'search':
return {model: {like: `%${value}%`}};
default:
return {[param]: value};
}
}
}
ngModule.vnComponent('vnAclComponent', {
controller: ACL,
template: require('./index.html')
});

View File

@ -1,39 +0,0 @@
<div class="search-panel">
<form ng-submit="$ctrl.onSearch()">
<vn-vertical>
<vn-autocomplete
label="Role"
ng-model="filter.principalId"
url="VnRoles"
value-field="name">
</vn-autocomplete>
<vn-autocomplete
label="Model"
ng-model="filter.model"
data="$ctrl.models"
id-field="name"
value-field="name">
</vn-autocomplete>
<vn-textfield
label="Property"
ng-model="filter.property">
</vn-textfield>
<vn-autocomplete
label="Access type"
ng-model="filter.accessType"
data="$ctrl.accessTypes"
value-field="name">
</vn-autocomplete>
<vn-autocomplete
label="Permission"
ng-model="$ctrl.acl.permission"
data="$ctrl.permissions"
value-field="name">
</vn-autocomplete>
<vn-submit
label="Search"
class="vn-mt-lg">
</vn-submit>
</vn-vertical>
</form>
</div>

View File

@ -1,26 +0,0 @@
import ngModule from '../../module';
import SearchPanel from 'core/components/searchbar/search-panel';
export default class Controller extends SearchPanel {
constructor(...args) {
super(...args);
this.accessTypes = [
{name: '*'},
{name: 'READ'},
{name: 'WRITE'}
];
this.permissions = [
{name: 'ALLOW'},
{name: 'DENY'}
];
this.models = [];
for (let model in window.validations)
this.models.push({name: model});
}
}
ngModule.component('vnAclSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,43 +0,0 @@
<vn-watcher
vn-id="watcher"
url="MailAliases"
data="$ctrl.alias"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.alias.alias"
rule="MailAlias"
vn-focus>
</vn-textfield>
<vn-textfield
label="Description"
ng-model="$ctrl.alias.description"
rule="MailAlias">
</vn-textfield>
<vn-check
label="Public"
ng-model="$ctrl.alias.isPublic"
rule="MailAlias">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,12 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnAliasBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
alias: '<'
}
});

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-alias-descriptor alias="$ctrl.alias"></vn-alias-descriptor>
<vn-left-menu source="alias"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,14 +0,0 @@
import ngModule from '../../module';
import ModuleCard from 'salix/components/module-card';
class Controller extends ModuleCard {
reload() {
this.$http.get(`MailAliases/${this.$params.id}`)
.then(res => this.alias = res.data);
}
}
ngModule.vnComponent('vnAliasCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,38 +0,0 @@
<vn-watcher
vn-id="watcher"
url="MailAliases"
data="$ctrl.alias"
insert-mode="true"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.alias.alias"
rule="MailAlias"
vn-focus>
</vn-textfield>
<vn-textfield
label="Description"
ng-model="$ctrl.alias.description"
rule="MailAlias">
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="account.alias">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,15 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onSubmit() {
return this.$.watcher.submit().then(res =>
this.$state.go('account.alias.card.basicData', {id: res.data.id})
);
}
}
ngModule.component('vnAliasCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,27 +0,0 @@
<vn-descriptor-content
module="account"
base-state="account.alias"
description="$ctrl.alias.alias">
<slot-menu>
<vn-item
ng-click="deleteAlias.show()"
name="deleteAlias"
translate>
Delete
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Description"
value="{{$ctrl.alias.description}}">
</vn-label-value>
</div>
</slot-body>
</vn-descriptor-content>
<vn-confirm
vn-id="delete-alias"
on-accept="$ctrl.onDelete()"
question="Are you sure you want to continue?"
message="Alias will be removed">
</vn-confirm>

View File

@ -1,26 +0,0 @@
import ngModule from '../../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get alias() {
return this.entity;
}
set alias(value) {
this.entity = value;
}
onDelete() {
return this.$http.delete(`MailAliases/${this.id}`)
.then(() => this.$state.go('account.alias'))
.then(() => this.vnApp.showSuccess(this.$t('Alias removed')));
}
}
ngModule.component('vnAliasDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
alias: '<'
}
});

View File

@ -1,2 +0,0 @@
Alias will be removed: El alias será eliminado
Alias removed: Alias eliminado

View File

@ -1,9 +0,0 @@
import './main';
import './index/';
import './create';
import './summary';
import './card';
import './descriptor';
import './create';
import './basic-data';
import './users';

View File

@ -1,39 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<vn-list class="separated">
<a
ng-repeat="alias in model.data track by alias.id"
ui-sref="account.alias.card.summary(::{id: alias.id})"
ui-sref-opts="{inherit: false}"
translate-attr="{title: 'View alias'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::alias.alias}}</h6>
<div>{{::alias.description}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(alias)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</vn-list>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-alias-summary alias="$ctrl.selectedAlias"></vn-alias-summary>
</vn-popup>
<a ui-sref="account.alias.create"
ui-sref-opts="{inherit: false}"
vn-tooltip="New alias"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,14 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(alias) {
this.selectedAlias = alias;
this.$.summary.show();
}
}
ngModule.component('vnAliasIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
New alias: Nuevo alias
View alias: Ver alias

View File

@ -1 +0,0 @@
Public: Público

View File

@ -1,17 +0,0 @@
<vn-crud-model
vn-id="model"
url="MailAliases"
limit="20"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
info="Search alias by id or name"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
base-state="account.alias">
</vn-searchbar>
</vn-portal>
<ui-view>
<vn-alias-index></vn-alias-index>
</ui-view>

View File

@ -1,18 +0,0 @@
import ngModule from '../../module';
import ModuleMain from 'salix/components/module-main';
export default class Alias extends ModuleMain {
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {alias: {like: `%${value}%`}};
}
}
}
ngModule.vnComponent('vnAlias', {
controller: Alias,
template: require('./index.html')
});

View File

@ -1,16 +0,0 @@
<vn-card class="summary">
<h5>{{summary.alias}}</h5>
<vn-horizontal class="vn-pa-md">
<vn-one>
<h4 translate>Basic data</h4>
<vn-label-value
label="Id"
value="{{summary.id}}">
</vn-label-value>
<vn-label-value
label="Description"
value="{{summary.description}}">
</vn-label-value>
</vn-one>
</vn-horizontal>
</vn-card>

View File

@ -1,25 +0,0 @@
import ngModule from '../../module';
import Component from 'core/lib/component';
class Controller extends Component {
set alias(value) {
this._alias = value;
this.$.summary = null;
if (!value) return;
this.$http.get(`MailAliases/${value.id}`)
.then(res => this.$.summary = res.data);
}
get alias() {
return this._alias;
}
}
ngModule.component('vnAliasSummary', {
template: require('./index.html'),
controller: Controller,
bindings: {
alias: '<'
}
});

View File

@ -1,26 +0,0 @@
<vn-data-viewer
data="data"
class="vn-w-xs">
<vn-card>
<vn-table>
<vn-tbody>
<vn-tr ng-repeat="row in data | orderBy:'user.name'">
<vn-td>{{::row.user.name}}</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"
translate-attr="{title: 'Remove'}"
ng-click="removeConfirm.show(row)">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-confirm
vn-id="removeConfirm"
message="User will be removed from alias"
question="Are you sure you want to continue?"
on-accept="$ctrl.onRemove($data)">
</vn-confirm>

View File

@ -1,31 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
let filter = {
include: {
relation: 'user',
scope: {
fields: ['id', 'name']
}
}
};
this.$http.get(`MailAliases/${this.$params.id}/accounts`, {filter})
.then(res => this.$.data = res.data);
}
onRemove(row) {
return this.$http.delete(`MailAliases/${this.$params.id}/accounts/${row.id}`)
.then(() => {
let index = this.$.data.indexOf(row);
if (index !== -1) this.$.data.splice(index, 1);
this.vnApp.showSuccess(this.$t('User removed'));
});
}
}
ngModule.component('vnAliasUsers', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,42 +0,0 @@
import './index';
describe('component vnAliasUsers', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnAliasUsers', {$element: null});
controller.$params.id = 1;
}));
describe('$onInit()', () => {
it('should delete entity and go to index', () => {
$httpBackend.expectGET('MailAliases/1/accounts').respond('foo');
controller.$onInit();
$httpBackend.flush();
expect(controller.$.data).toBe('foo');
});
});
describe('onRemove()', () => {
it('should call backend method to change role', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
controller.$.data = [
{id: 1, alias: 'foo'},
{id: 2, alias: 'bar'}
];
$httpBackend.expectDELETE('MailAliases/1/accounts/1').respond();
controller.onRemove(controller.$.data[0]);
$httpBackend.flush();
expect(controller.$.data).toEqual([{id: 2, alias: 'bar'}]);
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});

View File

@ -1,2 +0,0 @@
User will be removed from alias: El usuario será borrado del alias
User removed: Usuario borrado

View File

@ -1,64 +0,0 @@
<div ng-if="$ctrl.card.hasAccount">
<vn-data-viewer
data="data"
class="vn-w-xs">
<vn-card>
<vn-list class="separated">
<vn-item ng-repeat="row in data | orderBy:'alias.alias'">
<vn-item-section>
<div>
{{::row.alias.alias}}
</div>
<div class="text-caption text-secondary">
{{::row.alias.description}}
</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
icon="delete"
translate-attr="{title: 'Unsubscribe'}"
ng-click="removeConfirm.show(row)">
</vn-icon-button>
</vn-item-section>
</vn-item>
</vn-list>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-float-button
icon="add"
translate-attr="{title: 'Add'}"
vn-bind="+"
ng-click="$ctrl.onAddClick()"
fixed-bottom-right>
</vn-float-button>
<vn-dialog
vn-id="dialog"
on-accept="$ctrl.onAddSave()">
<tpl-body>
<vn-autocomplete
label="Alias"
ng-model="$ctrl.addData.mailAlias"
url="MailAliases"
show-field="alias"
vn-focus>
</vn-autocomplete>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>
<vn-confirm
vn-id="removeConfirm"
message="User will be removed from alias"
question="Are you sure you want to continue?"
on-accept="$ctrl.onRemove($data)">
</vn-confirm>
</div>
<div
ng-if="!$ctrl.card.hasAccount"
class="bg-title"
translate>
Account not enabled
</div>

View File

@ -1,51 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
this.refresh();
}
refresh() {
let filter = {
where: {account: this.$params.id},
include: {
relation: 'alias',
scope: {
fields: ['id', 'alias', 'description']
}
}
};
return this.$http.get(`MailAliasAccounts`, {filter})
.then(res => this.$.data = res.data);
}
onAddClick() {
this.addData = {account: this.$params.id};
this.$.dialog.show();
}
onAddSave() {
return this.$http.post(`MailAliasAccounts`, this.addData)
.then(() => this.refresh())
.then(() => this.vnApp.showSuccess(
this.$t('Subscribed to alias!'))
);
}
onRemove(row) {
return this.$http.delete(`MailAliasAccounts/${row.id}`)
.then(() => {
this.$.data.splice(this.$.data.indexOf(row), 1);
this.vnApp.showSuccess(this.$t('Unsubscribed from alias!'));
});
}
}
ngModule.component('vnUserAliases', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnUserCard'
}
});

View File

@ -1,53 +0,0 @@
import './index';
describe('component vnUserAliases', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnUserAliases', {$element: null});
jest.spyOn(controller.vnApp, 'showSuccess');
}));
describe('refresh()', () => {
it('should refresh the controller data', () => {
$httpBackend.expectGET('MailAliasAccounts').respond('foo');
controller.refresh();
$httpBackend.flush();
expect(controller.$.data).toBe('foo');
});
});
describe('onAddSave()', () => {
it('should add the new row', () => {
controller.addData = {account: 1};
$httpBackend.expectPOST('MailAliasAccounts').respond();
$httpBackend.expectGET('MailAliasAccounts').respond('foo');
controller.onAddSave();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('onRemove()', () => {
it('shoud remove the passed row remote and locally', () => {
controller.$.data = [
{id: 1, alias: 'foo'},
{id: 2, alias: 'bar'}
];
$httpBackend.expectDELETE('MailAliasAccounts/1').respond();
controller.onRemove(controller.$.data[0]);
$httpBackend.flush();
expect(controller.$.data).toEqual([{id: 2, alias: 'bar'}]);
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});

View File

@ -1,3 +0,0 @@
Unsubscribe: Desuscribir
Subscribed to alias!: ¡Suscrito al alias!
Unsubscribed from alias!: ¡Desuscrito del alias!

View File

@ -1,51 +0,0 @@
<mg-ajax path="VnUsers/{{patch.params.id}}/update-user" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.user"
form="form"
save="patch">
</vn-watcher>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="User"
ng-model="$ctrl.user.name"
rule="VnUser"
vn-focus>
</vn-textfield>
<vn-textfield
label="Nickname"
ng-model="$ctrl.user.nickname"
rule="VnUser">
</vn-textfield>
<vn-textfield
label="Personal email"
ng-model="$ctrl.user.email"
rule="VnUser">
</vn-textfield>
<vn-autocomplete
label="Language"
ng-model="$ctrl.user.lang"
url="Languages"
value-field="code"
rule="VnUser">
</vn-autocomplete>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,25 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
$onInit() {
if (this.$params.emailConfirmed)
this.vnApp.showSuccess(this.$t('Email verified successfully!'));
}
onSubmit() {
this.$.watcher.submit()
.then(() => this.card.reload());
}
}
ngModule.component('vnUserBasicData', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnUserCard'
},
bindings: {
user: '<'
}
});

View File

@ -1 +0,0 @@
Email verified successfully!: Correo verificado correctamente!

View File

@ -1,8 +0,0 @@
<vn-portal slot="menu">
<vn-user-descriptor
user="$ctrl.user"
on-change="$ctrl.reload()">
</vn-user-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,32 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
import './style.scss';
class Controller extends ModuleCard {
reload() {
const filter = {
where: {id: this.$params.id},
include: {
relation: 'role',
scope: {
fields: ['id', 'name']
}
}
};
return Promise.all([
this.$http.get(`VnUsers/preview`, {filter})
.then(res => {
const [user] = res.data;
this.user = user;
}),
this.$http.get(`Accounts/${this.$params.id}/exists`)
.then(res => this.hasAccount = res.data.exists)
]);
}
}
ngModule.vnComponent('vnUserCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,27 +0,0 @@
import './index';
describe('component vnUserCard', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnUserCard', {$element: null});
}));
describe('reload()', () => {
it('should reload the controller data', () => {
controller.$params.id = 1;
$httpBackend.expectGET('VnUsers/preview').respond('foo');
$httpBackend.expectGET('Accounts/1/exists').respond({exists: true});
controller.reload();
$httpBackend.flush();
expect(controller.user).toBe('f');
expect(controller.hasAccount).toBeTruthy();
});
});
});

View File

@ -1,10 +0,0 @@
@import "variables";
.bg-title {
display: block;
text-align: center;
padding: 24px;
box-sizing: border-box;
color: $color-font-secondary;
font-size: 1.375rem;
}

View File

@ -1,45 +0,0 @@
<vn-crud-model
vn-id="model"
url="AccessTokens"
filter="::$ctrl.filter"
limit="20"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<vn-list class="separated">
<a
ng-repeat="row in model.data"
ui-sref="account.card.summary(::{id: row.user.id})"
translate-attr="{title: 'Go to user'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::row.user.username}}</h6>
<div>{{::row.created | date:'dd/MM HH:mm'}}</div>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="disconnect.show(row)"
vn-tooltip="Kill session"
icon="exit_to_app">
</vn-icon-button>
</vn-item-section>
</a>
</vn-list>
</vn-card>
</vn-data-viewer>
<vn-confirm
vn-id="disconnect"
on-accept="$ctrl.onDisconnect($data)"
question="Are you sure you want to continue?"
message="Session will be killed">
</vn-confirm>
<vn-float-button
vn-tooltip="Refresh"
icon="refresh"
ng-click="model.refresh()"
vn-bind="r"
fixed-bottom-right>
</vn-float-button>

View File

@ -1,29 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor(...args) {
super(...args);
this.filter = {
fields: ['id', 'created', 'userId'],
include: {
relation: 'user',
scope: {
fields: ['username']
}
},
order: 'created DESC'
};
}
onDisconnect(row) {
return this.$http.delete(`AccessTokens/${row.id}`)
.then(() => this.$.model.refresh())
.then(() => this.vnApp.showSuccess(this.$t('Session killed')));
}
}
ngModule.component('vnConnections', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,5 +0,0 @@
Go to user: Ir al usuario
Refresh: Actualizar
Session will be killed: Se va a matar la sesión
Kill session: Matar sesión
Session killed: Sesión matada

View File

@ -1,57 +0,0 @@
<vn-watcher
vn-id="watcher"
url="VnUsers"
data="$ctrl.user"
insert-mode="true"
form="form">
</vn-watcher>
<form
name="form"
vn-http-submit="$ctrl.onSubmit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.user.name"
rule="VnUser"
vn-focus>
</vn-textfield>
<vn-textfield
label="Nickname"
ng-model="$ctrl.user.nickname"
rule="VnUser">
</vn-textfield>
<vn-textfield
label="Email"
ng-model="$ctrl.user.email"
rule="VnUser">
</vn-textfield>
<vn-autocomplete
label="Role"
ng-model="$ctrl.user.roleFk"
url="VnRoles"
rule="VnUser">
</vn-autocomplete>
<vn-textfield
label="Password"
ng-model="$ctrl.user.password"
type="password">
</vn-textfield>
<vn-check
label="Active"
ng-model="$ctrl.user.active">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="account.index">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,20 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.user = {active: true};
}
onSubmit() {
return this.$.watcher.submit().then(res => {
this.$state.go('account.card.basicData', {id: res.data.id});
});
}
}
ngModule.component('vnUserCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,5 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component vnUserDescriptor onPassChange() should throw an error when password is empty 1`] = `"You must enter a new password"`;
exports[`component vnUserDescriptor onPassChange() should throw an error when repeat password not matches new password 1`] = `"Passwords don't match"`;

View File

@ -1,97 +0,0 @@
import './index';
describe('component vnUserDescriptor', () => {
let controller;
let $httpBackend;
let user = {id: 1, name: 'foo'};
beforeEach(ngModule('account'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET('Accounts/1/exists').respond({exists: true});
controller = $componentController('vnUserDescriptor', {$element: null}, {user});
jest.spyOn(controller, 'emit');
jest.spyOn(controller.vnApp, 'showSuccess');
}));
describe('onDelete()', () => {
it('should delete entity and go to index', () => {
controller.$state.go = jest.fn();
$httpBackend.expectDELETE('VnUsers/1').respond();
controller.onDelete();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('account.index');
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('onPassChange()', () => {
it('should throw an error when password is empty', () => {
expect(() => {
controller.onPassChange();
}).toThrowErrorMatchingSnapshot();
});
it('should throw an error when repeat password not matches new password', () => {
controller.newPassword = 'foo';
controller.repeatPassword = 'bar';
expect(() => {
controller.onPassChange();
}).toThrowErrorMatchingSnapshot();
});
it('should make a request when password checks passes', () => {
controller.newPassword = 'foo';
controller.repeatPassword = 'foo';
$httpBackend.expectPATCH('Accounts/1/setPassword').respond();
controller.onPassChange();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
describe('onEnableAccount()', () => {
it('should make request to enable account', () => {
$httpBackend.expectPOST('Accounts', {id: 1}).respond();
controller.onEnableAccount();
$httpBackend.flush();
expect(controller.hasAccount).toBeTruthy();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
describe('onDisableAccount()', () => {
it('should make request to disable account', () => {
$httpBackend.expectDELETE('Accounts/1').respond();
controller.onDisableAccount();
$httpBackend.flush();
expect(controller.hasAccount).toBeFalsy();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
describe('onSetActive()', () => {
it('should make request to activate/deactivate the user', () => {
$httpBackend.expectPATCH('VnUsers/1', {active: true}).respond();
controller.onSetActive(true);
$httpBackend.flush();
expect(controller.user.active).toBeTruthy();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.emit).toHaveBeenCalledWith('change');
});
});
});

View File

@ -1,24 +1,6 @@
export * from './module'; export * from './module';
import './main'; import './main';
import './index/';
import './role';
import './alias';
import './connections';
import './acl';
import './summary';
import './card';
import './descriptor'; import './descriptor';
import './descriptor-popover'; import './descriptor-popover';
import './search-panel'; import './summary';
import './create';
import './basic-data';
import './mail-forwarding';
import './aliases';
import './roles';
import './ldap';
import './samba';
import './accounts';
import './privileges';
import './user-log';
import './role-log';

View File

@ -1,47 +0,0 @@
<vn-auto-search
model="model">
</vn-auto-search>
<vn-data-viewer
model="model"
class="vn-w-sm">
<vn-card>
<div class="vn-list separated">
<a
ng-repeat="user in model.data track by user.id"
ui-sref="account.card.summary(::{id: user.id})"
translate-attr="{title: 'View user'}"
class="vn-item search-result">
<vn-item-section>
<h6>{{::user.nickname}}</h6>
<vn-label-value
label="Id"
value="{{::user.id}}">
</vn-label-value>
<vn-label-value
label="User"
value="{{::user.name}}">
</vn-label-value>
</vn-item-section>
<vn-item-section side>
<vn-icon-button
vn-click-stop="$ctrl.preview(user)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-item-section>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-popup vn-id="summary">
<vn-user-summary user="$ctrl.selectedUser"></vn-user-summary>
</vn-popup>
<a
fixed-bottom-right
ui-sref="account.create"
vn-tooltip="New user"
vn-bind="+"
vn-acl="itManagement"
vn-acl-action="remove">
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,14 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
preview(user) {
this.selectedUser = user;
this.$.summary.show();
}
}
ngModule.component('vnUserIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
New user: Nuevo usuario
View user: Ver usuario

View File

@ -1,66 +0,0 @@
<vn-watcher
vn-id="watcher"
url="LdapConfigs"
data="$ctrl.config"
id-value="1"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg" vn-focus>
<vn-vertical>
<vn-check
label="Enable synchronization"
ng-model="watcher.hasData">
</vn-check>
</vn-vertical>
<vn-vertical
ng-if="watcher.hasData"
class="vn-mt-md">
<vn-textfield
label="Server"
ng-model="$ctrl.config.server"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="RDN"
ng-model="$ctrl.config.rdn"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="Password"
ng-model="$ctrl.config.password"
type="password"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="User DN"
ng-model="$ctrl.config.userDn"
rule="LdapConfig">
</vn-textfield>
<vn-textfield
label="Group DN"
ng-model="$ctrl.config.groupDn"
rule="LdapConfig">
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
label="Test connection"
ng-click="$ctrl.onTestConection()">
</vn-button>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,14 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
onTestConection() {
this.$http.get(`LdapConfigs/test`)
.then(() => this.vnApp.showSuccess(this.$t('LDAP connection established!')));
}
}
ngModule.component('vnAccountLdap', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,8 +0,0 @@
Enable synchronization: Habilitar sincronización
Server: Servidor
RDN: RDN
User DN: DN usuarios
Filter: Filtro
Group DN: DN grupos
Test connection: Probar conexión
LDAP connection established!: ¡Conexión con LDAP establecida!

View File

@ -1,49 +0,0 @@
<div ng-if="$ctrl.card.hasAccount">
<vn-watcher
vn-id="watcher"
url="MailForwards"
id-field="account"
id-value="$ctrl.$params.id"
data="data"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-check
label="Enable mail forwarding"
ng-model="watcher.hasData">
</vn-check>
<vn-textfield
ng-if="watcher.hasData"
label="Forward email"
ng-model="data.forwardTo"
info="All emails will be forwarded to the specified address."
rule="MailForward"
vn-focus>
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>
</div>
<div
ng-if="!$ctrl.card.hasAccount"
class="bg-title"
translate>
Account not enabled
</div>

View File

@ -1,12 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnUserMailForwarding', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnUserCard'
},
});

View File

@ -1,7 +0,0 @@
Mail forwarding: Reenvío de correo
Forward email: Dirección de reenvío
Enable mail forwarding: Habilitar redirección de correo
All emails will be forwarded to the specified address.: >
Todos los correos serán reenviados a la dirección especificada, no se
mantendrá copia de los mismos en el buzón del usuario.
You don't have enough privileges: No tienes suficientes permisos

View File

@ -1,19 +0,0 @@
<vn-crud-model
vn-id="model"
url="VnUsers/preview"
filter="::$ctrl.filter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-user-search-panel"
info="Search user by id, name or nickname"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
</vn-searchbar>
</vn-portal>
<vn-portal slot="menu">
<vn-left-menu></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -4,32 +4,10 @@ import ModuleMain from 'salix/components/module-main';
export default class User extends ModuleMain { export default class User extends ModuleMain {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.filter = {
fields: ['id', 'nickname', 'name', 'role'],
include: {
relation: 'role',
scope: {
fields: ['id', 'name']
}
}
};
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {or: [
{name: {like: `%${value}%`}},
{nickname: {like: `%${value}%`}}
]};
case 'name':
case 'nickname':
return {[param]: {like: `%${value}%`}};
case 'roleFk':
return {[param]: value};
} }
async $onInit() {
this.$state.go('home');
window.location.href = await this.vnApp.getUrl(`account/`);
} }
} }

View File

@ -1,28 +0,0 @@
import './index';
describe('component vnUser', () => {
let controller;
beforeEach(ngModule('account'));
beforeEach(inject($componentController => {
controller = $componentController('vnUser', {$element: null});
}));
describe('exprBuilder()', () => {
it('should search by id when only digits string is passed', () => {
let expr = controller.exprBuilder('search', '1');
expect(expr).toEqual({id: '1'});
});
it('should search by name when non-only digits string is passed', () => {
let expr = controller.exprBuilder('search', '1foo');
expect(expr).toEqual({or: [
{name: {like: '%1foo%'}},
{nickname: {like: '%1foo%'}}
]});
});
});
});

View File

@ -1,41 +0,0 @@
<mg-ajax path="VnUsers/{{post.params.id}}/privileges" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.user"
form="form"
save="post">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-check
label="Has grant"
ng-model="$ctrl.user.hasGrant">
</vn-check>
</vn-vertical>
</vn-card>
<vn-card class="vn-pa-lg vn-mt-md">
<vn-vertical>
<vn-autocomplete
label="Role"
ng-model="$ctrl.user.roleFk"
url="VnRoles">
</vn-autocomplete>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,21 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
get user() {
return this._user;
}
set user(value) {
this._user = value;
if (!value) return;
}
}
ngModule.component('vnUserPrivileges', {
template: require('./index.html'),
controller: Controller,
bindings: {
user: '<'
}
});

View File

@ -1,2 +0,0 @@
Privileges: Privilegios
Has grant: Puede delegar privilegios

View File

@ -1 +0,0 @@
<vn-log url="RoleLogs" origin-id="$ctrl.$params.id"></vn-log>

View File

@ -1,7 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
ngModule.vnComponent('vnRoleLog', {
template: require('./index.html'),
controller: Section,
});

View File

@ -1,40 +0,0 @@
<vn-watcher
vn-id="watcher"
url="VnRoles"
data="$ctrl.role"
form="form">
</vn-watcher>
<form
name="form"
ng-submit="watcher.submit()"
class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-vertical>
<vn-textfield
label="Name"
ng-model="$ctrl.role.name"
rule="VnRole.name"
vn-focus>
</vn-textfield>
<vn-textfield
label="Description"
ng-model="$ctrl.role.description"
rule="VnRole.description"
>
</vn-textfield>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,12 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {}
ngModule.component('vnRoleBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
role: '<'
}
});

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-role-descriptor role="$ctrl.role"></vn-role-descriptor>
<vn-left-menu source="role"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

Some files were not shown because too many files have changed in this diff Show More