From 8742d0b5f4ab4e8e89b90f21d8004547f8d9167c Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Fri, 13 Apr 2018 20:01:55 +0100 Subject: [PATCH 01/23] Initial work adding Cypress e2e testing to Xibo. --- .dockerignore | 8 +- cypress.json | 3 + cypress/integration/login_spec.js | 36 + cypress/integration/unauthed_spec.js | 9 + cypress/plugins/index.js | 17 + cypress/support/commands.js | 28 + cypress/support/index.js | 20 + package-lock.json | 1579 ++++++++++++++++++++++++++ package.json | 6 +- 9 files changed, 1702 insertions(+), 4 deletions(-) create mode 100644 cypress.json create mode 100644 cypress/integration/login_spec.js create mode 100644 cypress/integration/unauthed_spec.js create mode 100644 cypress/plugins/index.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/index.js create mode 100644 package-lock.json diff --git a/.dockerignore b/.dockerignore index b120a31961..1593ff0014 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,6 +15,9 @@ results.xml #vendor/ node_modules/ +# Don't include the e2e test suite +cypress/ + # Cache should be empty so that it gets built internally cache/ @@ -35,5 +38,6 @@ web/settings.php # Package.json - note we will likely want to remove this in 2.0 package.json -# Our own build file -build.sh \ No newline at end of file +# Our own build files +build.sh +build-composer.sh \ No newline at end of file diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000000..c202974ea1 --- /dev/null +++ b/cypress.json @@ -0,0 +1,3 @@ +{ + "baseUrl": "http://localhost" +} diff --git a/cypress/integration/login_spec.js b/cypress/integration/login_spec.js new file mode 100644 index 0000000000..27f43f1403 --- /dev/null +++ b/cypress/integration/login_spec.js @@ -0,0 +1,36 @@ +describe('Login', function() { + + beforeEach(function() { + cy.visit('/'); + }); + + it('should be able to login the default user', function () { + + cy.get('input#username') + .type('xibo_admin'); + + cy.get('input#password') + .type('password'); + + cy.get('button[type=submit]') + .click(); + + cy.url().should('include', '/dashboard'); + + cy.contains('xibo_admin'); + }); + + it('should fail to login an invalid user', function () { + + cy.get('input#username') + .type('xibo_admin'); + + cy.get('input#password') + .type('wrongpassword'); + + cy.get('button[type=submit]') + .click(); + + cy.contains('Username or Password incorrect'); + }) +}); \ No newline at end of file diff --git a/cypress/integration/unauthed_spec.js b/cypress/integration/unauthed_spec.js new file mode 100644 index 0000000000..3318b123e7 --- /dev/null +++ b/cypress/integration/unauthed_spec.js @@ -0,0 +1,9 @@ +describe('Unauthenticated CMS access', function () { + it('should visit the login page and check the version', function () { + cy.visit('/'); + + cy.url().should('include', '/login'); + + cy.contains('Version 1.8.8'); + }); +}); \ No newline at end of file diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000000..fd170fba69 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,17 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 0000000000..40b76d63f6 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,28 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) +Cypress.Commands.add('login', function() { + +}); \ No newline at end of file diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 0000000000..d68db96df2 --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..62f0dc6233 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1579 @@ +{ + "name": "xibo-cms", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@cypress/listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "date-fns": "1.29.0", + "figures": "1.7.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@cypress/xvfb": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.1.3.tgz", + "integrity": "sha512-EfRzw+wgI0Zdb4ZlhSvjh3q7I+oenqEYPXvr7oH/2RnzQqGDrPr7IU1Pi2yzGwoXmkNUQbo6qvntnItvQj0F4Q==", + "dev": true, + "requires": { + "lodash.once": "4.1.1" + } + }, + "@types/blob-util": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/blob-util/-/blob-util-1.3.3.tgz", + "integrity": "sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==", + "dev": true + }, + "@types/bluebird": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.18.tgz", + "integrity": "sha512-OTPWHmsyW18BhrnG5x8F7PzeZ2nFxmHGb42bZn79P9hl+GI5cMzyPgQTwNjbem0lJhoru/8vtjAFCUOu3+gE2w==", + "dev": true + }, + "@types/chai": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.8.tgz", + "integrity": "sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg==", + "dev": true + }, + "@types/chai-jquery": { + "version": "1.1.35", + "resolved": "https://registry.npmjs.org/@types/chai-jquery/-/chai-jquery-1.1.35.tgz", + "integrity": "sha512-7aIt9QMRdxuagLLI48dPz96YJdhu64p6FCa6n4qkGN5DQLHnrIjZpD9bXCvV2G0NwgZ1FAmfP214dxc5zNCfgQ==", + "dev": true, + "requires": { + "@types/chai": "4.0.8", + "@types/jquery": "3.2.16" + } + }, + "@types/jquery": { + "version": "3.2.16", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.2.16.tgz", + "integrity": "sha512-q2WC02YxQoX2nY1HRKlYGHpGP1saPmD7GN0pwCDlTz35a4eOtJG+aHRlXyjCuXokUukSrR2aXyBhSW3j+jPc0A==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.87", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.87.tgz", + "integrity": "sha512-AqRC+aEF4N0LuNHtcjKtvF9OTfqZI0iaBoe3dA6m/W+/YZJBZjBmW/QIZ8fBeXC6cnytSY9tBoFBqZ9uSCeVsw==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", + "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", + "dev": true + }, + "@types/mocha": { + "version": "2.2.44", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.44.tgz", + "integrity": "sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw==", + "dev": true + }, + "@types/sinon": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.0.0.tgz", + "integrity": "sha512-cuK4xM8Lg2wd8cxshcQa8RG4IK/xfyB6TNE6tNVvkrShR4xdrYgsV04q6Dp6v1Lp6biEFdzD8k8zg/ujQeiw+A==", + "dev": true + }, + "@types/sinon-chai": { + "version": "2.7.29", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-2.7.29.tgz", + "integrity": "sha512-EkI/ZvJT4hglWo7Ipf9SX+J+R9htNOMjW8xiOhce7+0csqvgoF5IXqY5Ae1GqRgNtWCuaywR5HjVa1snkTqpOw==", + "dev": true, + "requires": { + "@types/chai": "4.0.8", + "@types/sinon": "4.0.0" + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "async": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", + "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", + "dev": true + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "2.5.5", + "regenerator-runtime": "0.11.1" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "dev": true + }, + "ci-info": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", + "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "1.0.1" + } + }, + "cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "1.0.2" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "common-tags": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.4.0.tgz", + "integrity": "sha1-EYe+Tz1M8MBCfUP3Tu8fc1AWFMA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" + } + }, + "core-js": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "cypress": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-2.1.0.tgz", + "integrity": "sha512-EKXGjKFKUkhXXfAkYixBN2Lo2Gji4ZGC+ezWflRf/co49+OyHarZaXp7Y/n826WjmMdpHTmkOw4wUWBgyFHEHQ==", + "dev": true, + "requires": { + "@cypress/listr-verbose-renderer": "0.4.1", + "@cypress/xvfb": "1.1.3", + "@types/blob-util": "1.3.3", + "@types/bluebird": "3.5.18", + "@types/chai": "4.0.8", + "@types/chai-jquery": "1.1.35", + "@types/jquery": "3.2.16", + "@types/lodash": "4.14.87", + "@types/minimatch": "3.0.1", + "@types/mocha": "2.2.44", + "@types/sinon": "4.0.0", + "@types/sinon-chai": "2.7.29", + "bluebird": "3.5.0", + "chalk": "2.1.0", + "check-more-types": "2.24.0", + "commander": "2.11.0", + "common-tags": "1.4.0", + "debug": "3.1.0", + "extract-zip": "1.6.6", + "fs-extra": "4.0.1", + "getos": "2.8.4", + "glob": "7.1.2", + "is-ci": "1.0.10", + "is-installed-globally": "0.1.0", + "lazy-ass": "1.6.0", + "listr": "0.12.0", + "lodash": "4.17.4", + "minimist": "1.2.0", + "progress": "1.1.8", + "ramda": "0.24.1", + "request": "2.81.0", + "request-progress": "0.3.1", + "supports-color": "5.1.0", + "tmp": "0.0.31", + "url": "0.11.0", + "yauzl": "2.8.0" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "date-fns": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", + "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extract-zip": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", + "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", + "dev": true, + "requires": { + "concat-stream": "1.6.0", + "debug": "2.6.9", + "mkdirp": "0.5.0", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "1.0.1" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "fs-extra": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.1.tgz", + "integrity": "sha1-f8DGyJV/mD9X8waiTlud3Y0N2IA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "jsonfile": "3.0.1", + "universalify": "0.1.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "getos": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/getos/-/getos-2.8.4.tgz", + "integrity": "sha1-e4YD02GcKOOMsP56T2PDrLgNUWM=", + "dev": true, + "requires": { + "async": "2.1.4" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "1.3.5" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.14.1" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "is-ci": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", + "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", + "dev": true, + "requires": { + "ci-info": "1.1.3" + } + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true + }, + "listr": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", + "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "cli-truncate": "0.2.1", + "figures": "1.7.0", + "indent-string": "2.1.0", + "is-promise": "2.1.0", + "is-stream": "1.1.0", + "listr-silent-renderer": "1.1.1", + "listr-update-renderer": "0.2.0", + "listr-verbose-renderer": "0.4.1", + "log-symbols": "1.0.2", + "log-update": "1.0.2", + "ora": "0.2.3", + "p-map": "1.2.0", + "rxjs": "5.5.10", + "stream-to-observable": "0.1.0", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", + "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "cli-truncate": "0.2.1", + "elegant-spinner": "1.0.1", + "figures": "1.7.0", + "indent-string": "3.2.0", + "log-symbols": "1.0.2", + "log-update": "1.0.2", + "strip-ansi": "3.0.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "date-fns": "1.29.0", + "figures": "1.7.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "1.1.3" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "log-update": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", + "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", + "dev": true, + "requires": { + "ansi-escapes": "1.4.0", + "cli-cursor": "1.0.2" + } + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "requires": { + "mime-db": "1.33.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-spinners": "0.1.2", + "object-assign": "4.1.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "ramda": { + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", + "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" + } + }, + "request-progress": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-0.3.1.tgz", + "integrity": "sha1-ByHBBdipasayzossia4tXs/Pazo=", + "dev": true, + "requires": { + "throttleit": "0.0.2" + } + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "1.1.1", + "onetime": "1.1.0" + } + }, + "rxjs": { + "version": "5.5.10", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz", + "integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", + "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "stream-to-observable": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", + "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "throttleit": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", + "dev": true + }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "universalify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yauzl": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", + "integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.0.1" + } + } + } +} diff --git a/package.json b/package.json index 184c48b647..f9f4eae611 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "xibo-cms", "version": "1.0.0", - "description": "Xibo - Digital Signage - http://www.xibo.org.uk Copyright (C) 2006-2016 Daniel Garner and Contributors.", + "description": "Xibo - Digital Signage - http://www.xibo.org.uk Copyright (C) 2006-2018 Daniel Garner and Contributors.", "main": "index.js", "directories": { "test": "tests" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "cypress:open": "cypress open" }, "repository": { "type": "git", @@ -20,6 +21,7 @@ }, "homepage": "https://github.com/xibosignage/xibo-cms#readme", "devDependencies": { + "cypress": "^2.1.0", "del": "^2.2.1", "gulp": "^3.9.1", "gulp-composer": "^0.4.0", From 980a1a58a5836f0a128e0528675ec58b498a45ee Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Sat, 14 Apr 2018 10:43:53 +0100 Subject: [PATCH 02/23] implement cy.login to bypass the login page with each test. --- cypress/integration/dashboard_spec.js | 12 ++++++++++++ cypress/support/commands.js | 10 ++++++++++ docker/etc/apache2/conf.d/cms.conf | 1 + lib/Helper/Environment.php | 9 +++++++++ lib/Middleware/Actions.php | 27 +++++++++++++++++---------- lib/Middleware/CsrfGuard.php | 10 ++++++++-- lib/Service/LogServiceInterface.php | 2 +- 7 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 cypress/integration/dashboard_spec.js diff --git a/cypress/integration/dashboard_spec.js b/cypress/integration/dashboard_spec.js new file mode 100644 index 0000000000..2d5aa0296a --- /dev/null +++ b/cypress/integration/dashboard_spec.js @@ -0,0 +1,12 @@ +describe('Dashboard', function () { + + beforeEach(function () { + cy.login(); + + cy.visit('/'); + }); + + it('should be at the dashboard page', function() { + cy.url().should('include', '/dashboard'); + }); +}); \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 40b76d63f6..87e37cba0d 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -24,5 +24,15 @@ // -- This is will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) Cypress.Commands.add('login', function() { + cy.request({ + method: 'POST', + url: '/login', + form: true, + body: { + 'username': 'xibo_admin', + 'password': 'password' + } + }); + cy.getCookie('PHPSESSID').should('exist'); }); \ No newline at end of file diff --git a/docker/etc/apache2/conf.d/cms.conf b/docker/etc/apache2/conf.d/cms.conf index 401fa3021a..73dd0fb4ef 100644 --- a/docker/etc/apache2/conf.d/cms.conf +++ b/docker/etc/apache2/conf.d/cms.conf @@ -11,6 +11,7 @@ LoadModule rewrite_module modules/mod_rewrite.so PassEnv MYSQL_PORT PassEnv MYSQL_PASSWORD PassEnv CMS_SERVER_NAME + PassEnv CMS_DEV_MODE ServerName ${CMS_SERVER_NAME} diff --git a/lib/Helper/Environment.php b/lib/Helper/Environment.php index a0fc399b87..11bcda7539 100644 --- a/lib/Helper/Environment.php +++ b/lib/Helper/Environment.php @@ -244,4 +244,13 @@ public static function checkUrl($url) { return (stripos($url, '/web/') === false); } + + /** + * Is the CMS in DEV mode? + * @return bool + */ + public static function isDevMode() + { + return (isset($_SERVER['CMS_DEV_MODE']) && $_SERVER['CMS_DEV_MODE'] == true); + } } \ No newline at end of file diff --git a/lib/Middleware/Actions.php b/lib/Middleware/Actions.php index aabdc95b46..53122d7bbc 100644 --- a/lib/Middleware/Actions.php +++ b/lib/Middleware/Actions.php @@ -106,20 +106,27 @@ public function call() /** @var UserNotificationFactory $factory */ $factory = $app->userNotificationFactory; - if ($app->user->userTypeId == 1 && file_exists(PROJECT_ROOT . '/web/install/index.php')) { - $app->logService->notice('Install.php exists and shouldn\'t'); - - $notifications[] = $factory->create(__('There is a problem with this installation. "install.php" should be deleted.')); + // Is the CMS Docker stack in DEV mode? (this will be true for dev and test) + if (Environment::isDevMode()) { + $notifications[] = $factory->create('CMS IN DEV MODE'); $extraNotifications++; + } else { + // We're not in DEV mode and therefore install/index.php shouldn't be there. + if ($app->user->userTypeId == 1 && file_exists(PROJECT_ROOT . '/web/install/index.php')) { + $app->logService->notice('Install.php exists and shouldn\'t'); - // Test for web in the URL. - $url = $app->request()->getUrl() . $app->request()->getPathInfo(); + $notifications[] = $factory->create(__('There is a problem with this installation. "install.php" should be deleted.')); + $extraNotifications++; - if (!Environment::checkUrl($url)) { - $app->logService->notice('Suspicious URL detected - it is very unlikely that /web/ should be in the URL. URL is ' . $url); + // Test for web in the URL. + $url = $app->request()->getUrl() . $app->request()->getPathInfo(); - $notifications[] = $factory->create(__('CMS configuration warning, it is very unlikely that /web/ should be in the URL. This usually means that the DocumentRoot of the web server is wrong and may put your CMS at risk if not corrected.')); - $extraNotifications++; + if (!Environment::checkUrl($url)) { + $app->logService->notice('Suspicious URL detected - it is very unlikely that /web/ should be in the URL. URL is ' . $url); + + $notifications[] = $factory->create(__('CMS configuration warning, it is very unlikely that /web/ should be in the URL. This usually means that the DocumentRoot of the web server is wrong and may put your CMS at risk if not corrected.')); + $extraNotifications++; + } } } diff --git a/lib/Middleware/CsrfGuard.php b/lib/Middleware/CsrfGuard.php index 9cc8911cee..169749620f 100644 --- a/lib/Middleware/CsrfGuard.php +++ b/lib/Middleware/CsrfGuard.php @@ -24,6 +24,7 @@ use Slim\Middleware; use Xibo\Exception\TokenExpiredException; +use Xibo\Helper\Environment; class CsrfGuard extends Middleware { @@ -82,8 +83,13 @@ public function check() // Validate the token unless we are on an excluded route $route = $this->app->router()->getCurrentRoute()->getPattern(); - if ($this->app->excludedCsrfRoutes == null || ($route != null && !in_array($route, $this->app->excludedCsrfRoutes))) { - + $excludedRoutes = $this->app->excludedCsrfRoutes; + if (($excludedRoutes !== null && is_array($excludedRoutes) && in_array($route, $excludedRoutes)) + || (Environment::isDevMode() && $route === '/login') + ) { + $this->app->getLog()->info('Route excluded from CSRF: ' . $route); + } else { + // Checking CSRF $userToken = $this->app->request()->headers('X-XSRF-TOKEN'); if ($userToken == '') { $userToken = $this->app->request()->params($this->key); diff --git a/lib/Service/LogServiceInterface.php b/lib/Service/LogServiceInterface.php index 3d02e85b27..9ce78d43d7 100644 --- a/lib/Service/LogServiceInterface.php +++ b/lib/Service/LogServiceInterface.php @@ -50,7 +50,7 @@ public function audit($entity, $entityId, $message, $object); public function sql($sql, $params); /** - * @param string ...$object + * @param string * @return mixed */ public function debug($object); From 1477db7b61f3fd9863def1e1d68d1ee09b20f9c7 Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 11:25:30 +0100 Subject: [PATCH 03/23] CI for cypress --- .dockerignore | 6 ------ .gitignore | 3 ++- Dockerfile | 6 +++++- Dockerfile.cypress | 8 ++++++++ cypress.json | 3 ++- cypress/integration/dashboard_spec.js | 4 ++++ cypress/integration/layout_designer_spec.js | 11 +++++++++++ cypress/integration/modules_spec.js | 14 ++++++++++++++ cypress/integration/unauthed_spec.js | 6 ++++++ 9 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 Dockerfile.cypress create mode 100644 cypress/integration/layout_designer_spec.js create mode 100644 cypress/integration/modules_spec.js diff --git a/.dockerignore b/.dockerignore index 1593ff0014..de74213479 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,9 +15,6 @@ results.xml #vendor/ node_modules/ -# Don't include the e2e test suite -cypress/ - # Cache should be empty so that it gets built internally cache/ @@ -35,9 +32,6 @@ CONTRIBUTING.md # Our own container's settings.php shouldn't be included web/settings.php -# Package.json - note we will likely want to remove this in 2.0 -package.json - # Our own build files build.sh build-composer.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore index 262783de3d..02763e6150 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ custom web/theme/custom node_modules results.xml -containers/ \ No newline at end of file +containers/ +cypress/videos/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4f3bded5e2..3ebcecad4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,11 @@ RUN composer install --no-interaction --no-dev --ignore-platform-reqs --optimize # the next state RUN rm /app/composer.* && \ rm -r /app/docker && \ - rm .dockerignore + rm .dockerignore && \ + rm package.json && \ + rm package-lock.json && \ + rm cypress.json && \ + rm -r /app/cypress WORKDIR /app/vendor RUN find -type d -name '.git' -exec rm -r {} + && \ diff --git a/Dockerfile.cypress b/Dockerfile.cypress new file mode 100644 index 0000000000..2293fb5f7d --- /dev/null +++ b/Dockerfile.cypress @@ -0,0 +1,8 @@ +FROM cypress/base + +COPY cypress.json ./ +COPY cypress/ ./ + +RUN npm install --save-dev cypress + +RUN $(npm bin)/cypress verify \ No newline at end of file diff --git a/cypress.json b/cypress.json index c202974ea1..931949998f 100644 --- a/cypress.json +++ b/cypress.json @@ -1,3 +1,4 @@ { - "baseUrl": "http://localhost" + "baseUrl": "http://localhost", + "viewportWidth": 1200 } diff --git a/cypress/integration/dashboard_spec.js b/cypress/integration/dashboard_spec.js index 2d5aa0296a..21cc4ec184 100644 --- a/cypress/integration/dashboard_spec.js +++ b/cypress/integration/dashboard_spec.js @@ -8,5 +8,9 @@ describe('Dashboard', function () { it('should be at the dashboard page', function() { cy.url().should('include', '/dashboard'); + + cy.contains('xibo_admin'); + + cy.contains('Dashboard'); }); }); \ No newline at end of file diff --git a/cypress/integration/layout_designer_spec.js b/cypress/integration/layout_designer_spec.js new file mode 100644 index 0000000000..2817ba195b --- /dev/null +++ b/cypress/integration/layout_designer_spec.js @@ -0,0 +1,11 @@ +describe('Layout Designer', function() { + beforeEach(function() { + cy.login(); + }); + + it('should navigate to the Layout Designer page and have a Layout with 2 regions', function() { + cy.visit('/layout/designer/1'); + + + }); +}); \ No newline at end of file diff --git a/cypress/integration/modules_spec.js b/cypress/integration/modules_spec.js new file mode 100644 index 0000000000..9870b54377 --- /dev/null +++ b/cypress/integration/modules_spec.js @@ -0,0 +1,14 @@ +describe('Modules Page', function () { + beforeEach(function () { + cy.login(); + }); + + it('should load the modules page and show a complete table of modules', function () { + cy.visit('/module/view'); + + cy.contains('Modules'); + + // TODO: How many modules are we expecting by default? + cy.contains('Showing 1 to 10 of 20 entries'); + }); +}); \ No newline at end of file diff --git a/cypress/integration/unauthed_spec.js b/cypress/integration/unauthed_spec.js index 3318b123e7..e689b5a9b9 100644 --- a/cypress/integration/unauthed_spec.js +++ b/cypress/integration/unauthed_spec.js @@ -6,4 +6,10 @@ describe('Unauthenticated CMS access', function () { cy.contains('Version 1.8.8'); }); + + it('should redirect to login when an authenticated page is requested', function() { + + cy.visit('/layout/view'); + cy.url().should('include', '/login'); + }); }); \ No newline at end of file From 2627a825a173aaf6ca1498ceb470fb3240c7123e Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 12:22:11 +0100 Subject: [PATCH 04/23] Silent the cypress install --- Dockerfile.cypress | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.cypress b/Dockerfile.cypress index 2293fb5f7d..f606bd4471 100644 --- a/Dockerfile.cypress +++ b/Dockerfile.cypress @@ -3,6 +3,6 @@ FROM cypress/base COPY cypress.json ./ COPY cypress/ ./ -RUN npm install --save-dev cypress +RUN npm install --save-dev --slient cypress RUN $(npm bin)/cypress verify \ No newline at end of file From 18183c83f4386f4b2d073519f216877964b6722e Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 13:20:55 +0100 Subject: [PATCH 05/23] Cypress in sub-folder. --- Dockerfile.cypress | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile.cypress b/Dockerfile.cypress index f606bd4471..bbf613b948 100644 --- a/Dockerfile.cypress +++ b/Dockerfile.cypress @@ -1,7 +1,9 @@ FROM cypress/base +WORKDIR /app + COPY cypress.json ./ -COPY cypress/ ./ +COPY cypress ./cypress RUN npm install --save-dev --slient cypress From d819203f065de472cf029ccdb082281099ea4020 Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 14:10:37 +0100 Subject: [PATCH 06/23] Relax module count test. --- cypress/integration/modules_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/modules_spec.js b/cypress/integration/modules_spec.js index 9870b54377..6a4ada07a2 100644 --- a/cypress/integration/modules_spec.js +++ b/cypress/integration/modules_spec.js @@ -9,6 +9,6 @@ describe('Modules Page', function () { cy.contains('Modules'); // TODO: How many modules are we expecting by default? - cy.contains('Showing 1 to 10 of 20 entries'); + cy.contains('Showing 1 to 10 of'); }); }); \ No newline at end of file From 410297387df7c5681f092c3586298e9f30031ceb Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 15:31:31 +0100 Subject: [PATCH 07/23] Don't double encode the search term. xibosignage/xibo#1481 --- lib/Widget/TwitterBase.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Widget/TwitterBase.php b/lib/Widget/TwitterBase.php index 40dcd170ce..f66707ae5b 100644 --- a/lib/Widget/TwitterBase.php +++ b/lib/Widget/TwitterBase.php @@ -98,13 +98,14 @@ protected function getToken() * @param string $geoCode * @param int $count * @return bool|mixed + * @throws \GuzzleHttp\Exception\GuzzleException */ protected function searchApi($token, $term, $resultType = 'mixed', $geoCode = '', $count = 15) { $client = new Client($this->getConfig()->getGuzzleProxy()); $query = [ - 'q' => urlencode(trim($term)), + 'q' => trim($term), 'result_type' => $resultType, 'count' => $count, 'include_entities' => true, @@ -114,6 +115,8 @@ protected function searchApi($token, $term, $resultType = 'mixed', $geoCode = '' if ($geoCode != '') $query['geocode'] = $geoCode; + $this->getLog()->debug('Query is: ' . json_encode($query)); + try { $request = $client->request('GET', 'https://api.twitter.com/1.1/search/tweets.json', [ 'headers' => [ From 321d3b544c253d22bf39f5056d3baed2a4e8b1de Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 15:31:31 +0100 Subject: [PATCH 08/23] Don't double encode the search term. xibosignage/xibo#1481 --- lib/Widget/TwitterBase.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Widget/TwitterBase.php b/lib/Widget/TwitterBase.php index 40dcd170ce..f66707ae5b 100644 --- a/lib/Widget/TwitterBase.php +++ b/lib/Widget/TwitterBase.php @@ -98,13 +98,14 @@ protected function getToken() * @param string $geoCode * @param int $count * @return bool|mixed + * @throws \GuzzleHttp\Exception\GuzzleException */ protected function searchApi($token, $term, $resultType = 'mixed', $geoCode = '', $count = 15) { $client = new Client($this->getConfig()->getGuzzleProxy()); $query = [ - 'q' => urlencode(trim($term)), + 'q' => trim($term), 'result_type' => $resultType, 'count' => $count, 'include_entities' => true, @@ -114,6 +115,8 @@ protected function searchApi($token, $term, $resultType = 'mixed', $geoCode = '' if ($geoCode != '') $query['geocode'] = $geoCode; + $this->getLog()->debug('Query is: ' . json_encode($query)); + try { $request = $client->request('GET', 'https://api.twitter.com/1.1/search/tweets.json', [ 'headers' => [ From 04321bb164553f1206ce3a07f2bc319d19b82385 Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 16:33:06 +0100 Subject: [PATCH 09/23] Possible solution to memory issues on large stats exports xibosignage/xibo#1482 --- lib/Controller/Stats.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Controller/Stats.php b/lib/Controller/Stats.php index 3da03e4865..29fe075f7c 100644 --- a/lib/Controller/Stats.php +++ b/lib/Controller/Stats.php @@ -643,8 +643,15 @@ public function export() $out = fopen('php://output', 'w'); fputcsv($out, ['Type', 'FromDT', 'ToDT', 'Layout', 'Display', 'Media', 'Tag']); + // Run our query using a connection object (to save memory) + $connection = $this->store->getConnection(); + $statement = $connection->prepare($sql); + + // Execute + $statement->execute(); + // Do some post processing - foreach ($this->store->select($sql, $params) as $row) { + while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) { // Read the columns $type = $this->getSanitizer()->string($row['Type']); $fromDt = $this->getSanitizer()->string($row['start']); From c47b1d3f960c21dd60e004676d279b90109d8fdb Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Mon, 16 Apr 2018 16:55:37 +0100 Subject: [PATCH 10/23] Possible fix for welcome wizard with lock positions ON xibosignage/xibo#1478 --- views/user-welcome.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/user-welcome.twig b/views/user-welcome.twig index 26c52f5857..f4f11c7671 100644 --- a/views/user-welcome.twig +++ b/views/user-welcome.twig @@ -105,7 +105,7 @@ }, delay: 1000, template: next_template - }, { + }, {% if currentUser.getOptionValue("lockPosition", "false") == "true" %}{ title: "", content: "{% trans "Grab the Region handle to click and drag to resize" %}", element: ".ui-icon-gripsmall-diagonal-se:eq(0)", @@ -126,7 +126,7 @@ placement: "bottom", reflex: true, template: no_nav_template - }, { + }, {% endif %}{ title: "", content: "{% trans "You now have an empty Region on a black background on your Layout, so lets add some content!" %}", element: ".preview:eq(0)", From a2a8de2ed0f7e6df25350a7eff52458303dbb35e Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Tue, 17 Apr 2018 10:05:45 +0100 Subject: [PATCH 11/23] Possible fix for currencies module. xibosignage/xibo#1474 --- lib/Widget/AlphaVantageBase.php | 155 ++++++++++++++++++++++------- lib/Widget/Currencies.php | 158 ++++++++++++++---------------- lib/Widget/ModuleWidget.php | 15 +++ lib/Widget/Stocks.php | 108 +++++++++----------- modules/currencies-form-add.twig | 2 +- modules/currencies-form-edit.twig | 2 +- modules/stocks-form-add.twig | 2 +- modules/stocks-form-edit.twig | 2 +- 8 files changed, 262 insertions(+), 182 deletions(-) diff --git a/lib/Widget/AlphaVantageBase.php b/lib/Widget/AlphaVantageBase.php index fa6459b7c4..9ba5a25494 100644 --- a/lib/Widget/AlphaVantageBase.php +++ b/lib/Widget/AlphaVantageBase.php @@ -10,7 +10,9 @@ use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; use Jenssegers\Date\Date; +use Stash\Invalidation; use Xibo\Exception\ConfigurationException; use Xibo\Exception\XiboException; @@ -25,43 +27,98 @@ abstract class AlphaVantageBase extends ModuleWidget * @param $fromCurrency * @param $toCurrency * @return array + * @throws ConfigurationException * @throws XiboException */ protected function getCurrencyExchangeRate($fromCurrency, $toCurrency) { - // Use a web request - $client = new Client(); - - $request = $client->request('GET', 'https://www.alphavantage.co/query', $this->getConfig()->getGuzzleProxy([ - 'query' => [ - 'function' => 'CURRENCY_EXCHANGE_RATE', - 'from_currency' => $fromCurrency, - 'to_currency' => $toCurrency, - 'apikey' => $this->getApiKey() - ] - ])); - - return json_decode($request->getBody(), true); + try { + $cache = $this->getPool()->getItem($this->makeCacheKey(md5($fromCurrency . $toCurrency))); + $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15); + + $data = $cache->get(); + + if ($cache->isMiss()) { + + $this->getLog()->debug('getCurrencyExchangeRate is served from the API.'); + + $cache->lock(); + + // Use a web request + $client = new Client(); + + $request = $client->request('GET', 'https://www.alphavantage.co/query', $this->getConfig()->getGuzzleProxy([ + 'query' => [ + 'function' => 'CURRENCY_EXCHANGE_RATE', + 'from_currency' => $fromCurrency, + 'to_currency' => $toCurrency, + 'apikey' => $this->getApiKey() + ] + ])); + + $data = json_decode($request->getBody(), true); + + // Cache this and expire in the cache period + $cache->set($data); + $cache->expiresAt(Date::now()->addSeconds($this->getSetting('cachePeriod', 14400))); + + $this->getPool()->save($cache); + } else { + $this->getLog()->debug('getCurrencyExchangeRate is served from the cache.'); + } + + return $data; + + } catch (GuzzleException $guzzleException) { + throw new XiboException('Guzzle exception getting currency exchange rate. E = ' . $guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException); + } } /** * @param $symbol * @return array + * @throws ConfigurationException * @throws XiboException */ protected function getStockQuote($symbol) { - // Use a web request - $client = new Client(); - - $request = $client->request('GET', 'https://www.alphavantage.co/query', $this->getConfig()->getGuzzleProxy([ - 'query' => [ - 'function' => 'TIME_SERIES_DAILY', - 'symbol' => $symbol, - 'apikey' => $this->getApiKey() - ] - ])); - - return json_decode($request->getBody(), true); + try { + $cache = $this->getPool()->getItem($this->makeCacheKey(md5($symbol))); + $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15); + + $data = $cache->get(); + + if ($cache->isMiss()) { + + $this->getLog()->debug('getStockQuote is served from the API.'); + + $cache->lock(); + + // Use a web request + $client = new Client(); + + $request = $client->request('GET', 'https://www.alphavantage.co/query', $this->getConfig()->getGuzzleProxy([ + 'query' => [ + 'function' => 'TIME_SERIES_DAILY', + 'symbol' => $symbol, + 'apikey' => $this->getApiKey() + ] + ])); + + $data = json_decode($request->getBody(), true); + + // Cache this and expire in the cache period + $cache->set($data); + $cache->expiresAt(Date::now()->addSeconds($this->getSetting('cachePeriod', 14400))); + + $this->getPool()->save($cache); + } else { + $this->getLog()->debug('getStockQuote is served from the cache.'); + } + + return $data; + } catch (GuzzleException $guzzleException) { + throw new XiboException('Guzzle exception getting currency exchange rate. E = ' . $guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException); + } } /** @@ -83,21 +140,49 @@ protected function getApiKey() * @param $base * @param $pairs * @return mixed + * @throws XiboException */ protected function getPriorDay($base, $pairs) { - // Use a web request - $client = new Client(); + try { + $cache = $this->getPool()->getItem($this->makeCacheKey(md5(var_export([$base, $pairs], true)))); + $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15); + + $data = $cache->get(); + + if ($cache->isMiss()) { + + $this->getLog()->debug('getPriorDay is served from the API.'); + + $cache->lock(); + + // Use a web request + $client = new Client(); + + $yesterday = Date::yesterday()->format('Y-m-d'); + + $request = $client->request('GET', 'https://api.fixer.io/' . $yesterday, $this->getConfig()->getGuzzleProxy([ + 'query' => [ + 'base' => $base, + 'symbols' => is_array($pairs) ? implode(',', $pairs) : $pairs + ] + ])); + + $data = json_decode($request->getBody(), true)['rates']; + + // Cache this and expire tomorrow (results are valid for the entire day regardless of settings) + $cache->set($data); + $cache->expiresAt(Date::tomorrow()); - $yesterday = Date::yesterday()->format('Y-m-d'); + $this->getPool()->save($cache); + } else { + $this->getLog()->debug('getPriorDay is served from the cache.'); + } - $request = $client->request('GET', 'https://api.fixer.io/' . $yesterday, $this->getConfig()->getGuzzleProxy([ - 'query' => [ - 'base' => $base, - 'symbols' => is_array($pairs) ? implode(',', $pairs) : $pairs - ] - ])); + return $data; - return json_decode($request->getBody(), true)['rates']; + } catch (GuzzleException $guzzleException) { + throw new XiboException('Guzzle exception getting currency exchange rate. E = ' . $guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException); + } } } \ No newline at end of file diff --git a/lib/Widget/Currencies.php b/lib/Widget/Currencies.php index 4a0dbdb4ad..70f7d9a6fc 100644 --- a/lib/Widget/Currencies.php +++ b/lib/Widget/Currencies.php @@ -21,10 +21,10 @@ */ namespace Xibo\Widget; -use GuzzleHttp\Exception\RequestException; -use Stash\Invalidation; +use Xibo\Exception\ConfigurationException; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\NotFoundException; +use Xibo\Exception\XiboException; use Xibo\Factory\ModuleFactory; /** @@ -88,11 +88,15 @@ public function settingsForm() /** * Process any module settings + * @throws InvalidArgumentException */ public function settings() { $this->module->settings['apiKey'] = $this->getSanitizer()->getString('apiKey'); - $this->module->settings['cachePeriod'] = $this->getSanitizer()->getInt('cachePeriod', 300); + $this->module->settings['cachePeriod'] = $this->getSanitizer()->getInt('cachePeriod', 14400); + + if ($this->module->settings['cachePeriod'] < 3600) + throw new InvalidArgumentException(__('Cache Period must be 3600 or greater for this Module'), 'cachePeriod'); // Return an array of the processed settings. return $this->module->settings; @@ -339,7 +343,7 @@ public function setCommonOptions() $this->setRawNode('javaScript', $this->getSanitizer()->getParam('javaScript', '')); $this->setOption('overrideTemplate', $this->getSanitizer()->getCheckbox('overrideTemplate')); - if( $this->getOption('overrideTemplate') == 1 ){ + if ($this->getOption('overrideTemplate') == 1) { $this->setRawNode('mainTemplate', $this->getSanitizer()->getParam('mainTemplate', $this->getSanitizer()->getParam('mainTemplate', null))); $this->setRawNode('itemTemplate', $this->getSanitizer()->getParam('itemTemplate', $this->getSanitizer()->getParam('itemTemplate', null))); $this->setRawNode('styleSheet', $this->getSanitizer()->getParam('styleSheet', $this->getSanitizer()->getParam('styleSheet', null))); @@ -350,8 +354,12 @@ public function setCommonOptions() } /** - * Get Results + * Get FX Results + * PLEASE NOTE: This method does not cache results directly as the AlphaVantageBase class handles caching individual + * requests. + * This request uses a combination of AlphaVantage and Fixer.IO * @return array|bool an array of results. false if an invalid value is returned. + * @throws ConfigurationException */ protected function getResults() { @@ -386,100 +394,84 @@ protected function getResults() // Does the template require a percentage change calculation. $percentageChangeRequested = stripos($itemTemplate, '[ChangePercentage]') > -1; - // Our cache key is based on the base/items/changepercentage - /** @var \Stash\Item $cache */ - $cache = $this->getPool()->getItem($this->makeCacheKey(md5($base . implode(',', $items) . $percentageChangeRequested . $reverseConversion))); - $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15); - - $data = $cache->get(); - - if ($cache->isMiss()) { - // Lock this cache record - $cache->lock(); - - // Start fresh - $data = []; - $priorDay = []; + $data = []; + $priorDay = []; - // Do we need to get the data for percentage change? - if ($percentageChangeRequested && !$reverseConversion) { - try { - // Get the prior day - $priorDay = $this->getPriorDay($base, $items); + // Do we need to get the data for percentage change? + if ($percentageChangeRequested && !$reverseConversion) { + try { + // Get the prior day + $priorDay = $this->getPriorDay($base, $items); - $this->getLog()->debug('Percentage change requested, prior day is ' . var_export($priorDay, true)); + $this->getLog()->debug('Percentage change requested, prior day is ' . var_export($priorDay, true)); - } catch (RequestException $requestException) { - $this->getLog()->error('Problem getting percentage change currency information. E = ' . $requestException->getMessage()); - $this->getLog()->debug($requestException->getTraceAsString()); - } + } catch (XiboException $requestException) { + $this->getLog()->error('Problem getting percentage change currency information. E = ' . $requestException->getMessage()); + $this->getLog()->debug($requestException->getTraceAsString()); } + } - // Each item we want is a call to the results API - try { - foreach ($items as $currency) { - // Remove the multiplier if there's one (this is handled when we substitute the results into the template) - $currency = explode('|', $currency)[0]; - - // Do we need to reverse the from/to currency for this comparison? - if ($reverseConversion) { - $result = $this->getCurrencyExchangeRate($currency, $base); + // Each item we want is a call to the results API + try { + foreach ($items as $currency) { + // Remove the multiplier if there's one (this is handled when we substitute the results into the template) + $currency = explode('|', $currency)[0]; - // We need to get the proir day for this pair only (reversed) - $priorDay = $this->getPriorDay($currency, $base); + // Do we need to reverse the from/to currency for this comparison? + if ($reverseConversion) { + $result = $this->getCurrencyExchangeRate($currency, $base); - $this->getLog()->debug('Percentage change requested, prior day is ' . var_export($priorDay, true)); + // We need to get the prior day for this pair only (reversed) + $priorDay = $this->getPriorDay($currency, $base); - } else { - $result = $this->getCurrencyExchangeRate($base, $currency); - } + $this->getLog()->debug('Percentage change requested, prior day is ' . var_export($priorDay, true)); - $this->getLog()->debug('Results are: ' . var_export($result, true)); - - $parsedResult = [ - 'time' => $result['Realtime Currency Exchange Rate']['6. Last Refreshed'], - 'ToName' => $result['Realtime Currency Exchange Rate']['3. To_Currency Code'], - 'ToCurrency' => $result['Realtime Currency Exchange Rate']['4. To_Currency Name'], - 'FromName' => $result['Realtime Currency Exchange Rate']['1. From_Currency Code'], - 'FromCurrency' => $result['Realtime Currency Exchange Rate']['2. From_Currency Name'], - 'Bid' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4), - 'Ask' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4), - 'LastTradePriceOnly' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4), - 'RawLastTradePriceOnly' => $result['Realtime Currency Exchange Rate']['5. Exchange Rate'], - 'TimeZone' => $result['Realtime Currency Exchange Rate']['7. Time Zone'], - ]; - - // Set the name/currency to be the full name including the base currency - $parsedResult['Name'] = $parsedResult['FromName'] . '/' . $parsedResult['ToName']; - $parsedResult['Currency'] = $parsedResult['FromCurrency'] . '/' . $parsedResult['ToCurrency']; - - // work out the change when compared to the previous day - if ($percentageChangeRequested && isset($priorDay[$parsedResult['ToName']]) && is_numeric($priorDay[$parsedResult['ToName']])) { - $parsedResult['YesterdayTradePriceOnly'] = $priorDay[$parsedResult['ToName']]; - $parsedResult['Change'] = $parsedResult['RawLastTradePriceOnly'] - $parsedResult['YesterdayTradePriceOnly']; - } else { - $parsedResult['YesterdayTradePriceOnly'] = 0; - $parsedResult['Change'] = 0; - } + } else { + $result = $this->getCurrencyExchangeRate($base, $currency); + } - // Parse the result and add it to our data array - $data[] = $parsedResult; + $this->getLog()->debug('Results are: ' . var_export($result, true)); + + $parsedResult = [ + 'time' => $result['Realtime Currency Exchange Rate']['6. Last Refreshed'], + 'ToName' => $result['Realtime Currency Exchange Rate']['3. To_Currency Code'], + 'ToCurrency' => $result['Realtime Currency Exchange Rate']['4. To_Currency Name'], + 'FromName' => $result['Realtime Currency Exchange Rate']['1. From_Currency Code'], + 'FromCurrency' => $result['Realtime Currency Exchange Rate']['2. From_Currency Name'], + 'Bid' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4), + 'Ask' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4), + 'LastTradePriceOnly' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4), + 'RawLastTradePriceOnly' => $result['Realtime Currency Exchange Rate']['5. Exchange Rate'], + 'TimeZone' => $result['Realtime Currency Exchange Rate']['7. Time Zone'], + ]; + + // Set the name/currency to be the full name including the base currency + $parsedResult['Name'] = $parsedResult['FromName'] . '/' . $parsedResult['ToName']; + $parsedResult['Currency'] = $parsedResult['FromCurrency'] . '/' . $parsedResult['ToCurrency']; + + // work out the change when compared to the previous day + if ($percentageChangeRequested && isset($priorDay[$parsedResult['ToName']]) && is_numeric($priorDay[$parsedResult['ToName']])) { + $parsedResult['YesterdayTradePriceOnly'] = $priorDay[$parsedResult['ToName']]; + $parsedResult['Change'] = $parsedResult['RawLastTradePriceOnly'] - $parsedResult['YesterdayTradePriceOnly']; + } else { + $parsedResult['YesterdayTradePriceOnly'] = 0; + $parsedResult['Change'] = 0; } - } catch (RequestException $requestException) { - $this->getLog()->error('Problem getting currency information. E = ' . $requestException->getMessage()); - $this->getLog()->debug($requestException->getTraceAsString()); - return false; + // Parse the result and add it to our data array + $data[] = $parsedResult; } + } catch (ConfigurationException $configurationException) { + throw $configurationException; + } catch (XiboException $requestException) { + $this->getLog()->error('Problem getting currency information. E = ' . $requestException->getMessage()); + $this->getLog()->debug($requestException->getTraceAsString()); - $this->getLog()->debug('Parsed Results are: ' . var_export($data, true)); - - // Cache it - $cache->set($data); - $cache->expiresAfter($this->getSetting('cachePeriod', 3600)); - $this->getPool()->saveDeferred($cache); + return false; } + $this->getLog()->debug('Parsed Results are: ' . var_export($data, true)); + return $data; } diff --git a/lib/Widget/ModuleWidget.php b/lib/Widget/ModuleWidget.php index 6ad6c0f586..b1be8ce72c 100644 --- a/lib/Widget/ModuleWidget.php +++ b/lib/Widget/ModuleWidget.php @@ -29,6 +29,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Xibo\Entity\Media; use Xibo\Entity\User; +use Xibo\Exception\ConfigurationException; use Xibo\Exception\ControllerNotImplemented; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\NotFoundException; @@ -1384,6 +1385,10 @@ public final function getResourceOrCache($displayId, $region = null) // Generate the resource $resource = $this->getResource($displayId); + // If the resource is false, then don't cache it for as long (most likely an error) + if ($resource === false) + throw new XiboException('GetResource generated FALSE'); + // Cache to the library $hash = null; if (file_exists($cachePath . $cacheFile)) { @@ -1410,6 +1415,16 @@ public final function getResourceOrCache($displayId, $region = null) $this->getLog()->debug('Regenerate complete'); + } catch (ConfigurationException $configurationException) { + // If we have something wrong with the module and we are in the preview, then we should present the error + // on screen + if ($displayId === 0) { + throw $configurationException; + } else { + // Don't cache, just log + $this->getLog()->error('Configuration error with Widget ' . $this->getWidgetId() . ' for displayId ' . $displayId . '. E = ' . $configurationException->getMessage()); + } + } catch (\Exception $e) { $this->getLog()->error('Problem with Widget ' . $this->getWidgetId() . ' for displayId ' . $displayId . '. E = ' . $e->getMessage()); $this->getLog()->debug($e->getTraceAsString()); diff --git a/lib/Widget/Stocks.php b/lib/Widget/Stocks.php index d53cb83d4d..d7b7fb5053 100644 --- a/lib/Widget/Stocks.php +++ b/lib/Widget/Stocks.php @@ -21,10 +21,10 @@ */ namespace Xibo\Widget; -use GuzzleHttp\Exception\RequestException; -use Stash\Invalidation; +use Xibo\Exception\ConfigurationException; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\NotFoundException; +use Xibo\Exception\XiboException; use Xibo\Factory\ModuleFactory; /** @@ -88,11 +88,15 @@ public function settingsForm() /** * Process any module settings + * @throws InvalidArgumentException */ public function settings() { $this->module->settings['apiKey'] = $this->getSanitizer()->getString('apiKey'); - $this->module->settings['cachePeriod'] = $this->getSanitizer()->getInt('cachePeriod', 300); + $this->module->settings['cachePeriod'] = $this->getSanitizer()->getInt('cachePeriod', 14400); + + if ($this->module->settings['cachePeriod'] < 3600) + throw new InvalidArgumentException(__('Cache Period must be 3600 or greater for this Module'), 'cachePeriod'); // Return an array of the processed settings. return $this->module->settings; @@ -318,8 +322,11 @@ public function setCommonOptions() } /** - * Get YQL Data + * Get Stock Results + * PLEASE NOTE: This method does not cache results directly as the AlphaVantageBase class handles caching individual + * requests. * @return array|bool an array of results according to the key specified by result identifier. false if an invalid value is returned. + * @throws ConfigurationException */ protected function getResults() { @@ -332,79 +339,60 @@ protected function getResults() return false; } - // Fire off a request for the data - /** @var \Stash\Item $cache */ - $cache = $this->getPool()->getItem($this->makeCacheKey(md5($items))); - $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15); - - $data = $cache->get(); - - if ($cache->isMiss()) { - - // Start fresh - $data = []; - - // Lock - $cache->lock(); - - $this->getLog()->debug('Querying API for ' . $items); - - // Parse items out into an array - $items = explode(',', $items); + $data = []; - try { - foreach ($items as $symbol) { + // Parse items out into an array + $items = explode(',', $items); - // Does this symbol have any additional data - $parsedSymbol = explode('|', $symbol); + try { + foreach ($items as $symbol) { - $symbol = $parsedSymbol[0]; - $name = (isset($parsedSymbol[1]) ? $parsedSymbol[1] : $symbol); - $currency = (isset($parsedSymbol[2]) ? $parsedSymbol[2] : ''); + // Does this symbol have any additional data + $parsedSymbol = explode('|', $symbol); - $result = $this->getStockQuote($symbol); + $symbol = $parsedSymbol[0]; + $name = (isset($parsedSymbol[1]) ? $parsedSymbol[1] : $symbol); + $currency = (isset($parsedSymbol[2]) ? $parsedSymbol[2] : ''); - $this->getLog()->debug('Results are: ' . var_export($result, true)); + $result = $this->getStockQuote($symbol); - $parsedResult = []; + $this->getLog()->debug('Results are: ' . var_export($result, true)); - foreach ($result['Time Series (Daily)'] as $series) { + $parsedResult = []; - $parsedResult = [ - 'Name' => $name, - 'Symbol' => $symbol, - 'time' => $result['Meta Data']['3. Last Refreshed'], - 'LastTradePriceOnly' => round($series['4. close'], 4), - 'RawLastTradePriceOnly' => $series['4. close'], - 'YesterdayTradePriceOnly' => round($series['1. open'], 4), - 'RawYesterdayTradePriceOnly' => $series['1. open'], - 'TimeZone' => $result['Meta Data']['5. Time Zone'], - 'Currency' => $currency - ]; + foreach ($result['Time Series (Daily)'] as $series) { - $parsedResult['Change'] = round($parsedResult['RawYesterdayTradePriceOnly'] - $parsedResult['RawLastTradePriceOnly'], 4); + $parsedResult = [ + 'Name' => $name, + 'Symbol' => $symbol, + 'time' => $result['Meta Data']['3. Last Refreshed'], + 'LastTradePriceOnly' => round($series['4. close'], 4), + 'RawLastTradePriceOnly' => $series['4. close'], + 'YesterdayTradePriceOnly' => round($series['1. open'], 4), + 'RawYesterdayTradePriceOnly' => $series['1. open'], + 'TimeZone' => $result['Meta Data']['5. Time Zone'], + 'Currency' => $currency + ]; - break; - } + $parsedResult['Change'] = round($parsedResult['RawYesterdayTradePriceOnly'] - $parsedResult['RawLastTradePriceOnly'], 4); - // Parse the result and add it to our data array - $data[] = $parsedResult; + break; } - } catch (RequestException $requestException) { - $this->getLog()->error('Problem getting stock information. E = ' . $requestException->getMessage()); - $this->getLog()->debug($requestException->getTraceAsString()); - return false; + // Parse the result and add it to our data array + $data[] = $parsedResult; } + } catch (ConfigurationException $configurationException) { + throw $configurationException; + } catch (XiboException $requestException) { + $this->getLog()->error('Problem getting stock information. E = ' . $requestException->getMessage()); + $this->getLog()->debug($requestException->getTraceAsString()); - $this->getLog()->debug('Parsed Results are: ' . var_export($data, true)); - - // Cache it - $cache->set($data); - $cache->expiresAfter($this->getSetting('cachePeriod', 3600)); - $this->getPool()->saveDeferred($cache); + return false; } + $this->getLog()->debug('Parsed Results are: ' . var_export($data, true)); + return $data; } diff --git a/modules/currencies-form-add.twig b/modules/currencies-form-add.twig index 3ddb89b8b9..6f85eb1880 100644 --- a/modules/currencies-form-add.twig +++ b/modules/currencies-form-add.twig @@ -161,7 +161,7 @@ {{ forms.dropdown("templateId", "single", title, module.getOption("templateId"), module.templatesAvailable(),"id", "value", helptext, "template-selector-control selectPicker selectPickerWithImage", null, null, null, null, null, "image") }} {% set title %}{% trans "Items" %}{% endset %} - {% set helpText %}{% trans "The items wanted, can be comma separated." %}{% endset %} + {% set helpText %}{% trans "The items wanted, can be comma separated. For the best results enter no more than 5 items." %}{% endset %} {{ forms.input("items", title, "", helpText, "", "required") }} {% set title %}{% trans "Base" %}{% endset %} diff --git a/modules/currencies-form-edit.twig b/modules/currencies-form-edit.twig index 3c7ebd8d88..21492b2b74 100644 --- a/modules/currencies-form-edit.twig +++ b/modules/currencies-form-edit.twig @@ -163,7 +163,7 @@ {{ forms.dropdown("templateId", "single", title, module.getOption("templateId"), module.templatesAvailable(), "id", "value", helptext, "template-selector-control selectPicker selectPickerWithImage", null, null, null, null, null, "image") }} {% set title %}{% trans "Items" %}{% endset %} - {% set helpText %}{% trans "The items wanted, can be comma separated." %}{% endset %} + {% set helpText %}{% trans "The items wanted, can be comma separated. For the best results enter no more than 5 items." %}{% endset %} {{ forms.input("items", title, module.getOption("items"), helpText, "", "required") }} {% set title %}{% trans "Base" %}{% endset %} diff --git a/modules/stocks-form-add.twig b/modules/stocks-form-add.twig index 8f224af6bd..71bf4a0856 100644 --- a/modules/stocks-form-add.twig +++ b/modules/stocks-form-add.twig @@ -167,7 +167,7 @@ {{ forms.checkbox("overrideTemplate", title, module.getOption("overrideTemplate", 0), helpText) }} {% set title %}{% trans "Items" %}{% endset %} - {% set helpText %}{% trans "The items wanted, can be comma separated." %}{% endset %} + {% set helpText %}{% trans "The items wanted, can be comma separated. For the best results enter no more than 5 items." %}{% endset %} {{ forms.input("items", title, "", helpText, "", "required") }} {% set title %}{% trans "Items per Page" %}{% endset %} diff --git a/modules/stocks-form-edit.twig b/modules/stocks-form-edit.twig index ef4b8aa93c..07236c3015 100644 --- a/modules/stocks-form-edit.twig +++ b/modules/stocks-form-edit.twig @@ -167,7 +167,7 @@ {{ forms.checkbox("overrideTemplate", title, module.getOption("overrideTemplate", 0), helpText) }} {% set title %}{% trans "Items" %}{% endset %} - {% set helpText %}{% trans "The items wanted, can be comma separated." %}{% endset %} + {% set helpText %}{% trans "The items wanted, can be comma separated. For the best results enter no more than 5 items." %}{% endset %} {{ forms.input("items", title, module.getOption("items"), helpText, "", "required") }} {% set title %}{% trans "Items per Page" %}{% endset %} From a2fb327cac924be08b29a5140703694a763f0bfc Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Tue, 17 Apr 2018 10:23:04 +0100 Subject: [PATCH 12/23] Reverse logic and test welcome guide fix. xibosignage/xibo#1478 --- views/user-welcome.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/user-welcome.twig b/views/user-welcome.twig index f4f11c7671..31fc709a8f 100644 --- a/views/user-welcome.twig +++ b/views/user-welcome.twig @@ -105,7 +105,7 @@ }, delay: 1000, template: next_template - }, {% if currentUser.getOptionValue("lockPosition", "false") == "true" %}{ + }, {% if currentUser.getOptionValue("lockPosition", "false") == "false" %}{ title: "", content: "{% trans "Grab the Region handle to click and drag to resize" %}", element: ".ui-icon-gripsmall-diagonal-se:eq(0)", From 071d65bf4e5727e02d73ab527be4efe98f8810be Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Tue, 17 Apr 2018 11:42:33 +0100 Subject: [PATCH 13/23] Switched to exchangeratesapi.io xibosignage/xibo#1485 --- lib/Widget/AlphaVantageBase.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/Widget/AlphaVantageBase.php b/lib/Widget/AlphaVantageBase.php index 9ba5a25494..9a6f000b58 100644 --- a/lib/Widget/AlphaVantageBase.php +++ b/lib/Widget/AlphaVantageBase.php @@ -144,8 +144,10 @@ protected function getApiKey() */ protected function getPriorDay($base, $pairs) { + $yesterday = Date::yesterday()->format('Y-m-d'); + try { - $cache = $this->getPool()->getItem($this->makeCacheKey(md5(var_export([$base, $pairs], true)))); + $cache = $this->getPool()->getItem($this->makeCacheKey(md5($base . $yesterday))); $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15); $data = $cache->get(); @@ -159,12 +161,10 @@ protected function getPriorDay($base, $pairs) // Use a web request $client = new Client(); - $yesterday = Date::yesterday()->format('Y-m-d'); - $request = $client->request('GET', 'https://api.fixer.io/' . $yesterday, $this->getConfig()->getGuzzleProxy([ + $request = $client->request('GET', 'https://exchangeratesapi.io/api/' . $yesterday, $this->getConfig()->getGuzzleProxy([ 'query' => [ - 'base' => $base, - 'symbols' => is_array($pairs) ? implode(',', $pairs) : $pairs + 'base' => $base ] ])); @@ -179,7 +179,13 @@ protected function getPriorDay($base, $pairs) $this->getLog()->debug('getPriorDay is served from the cache.'); } - return $data; + $return = []; + + foreach ($pairs as $pair) { + $return[$pair] = isset($data[$pair]) ? $data[$pair] : null; + } + + return $return; } catch (GuzzleException $guzzleException) { throw new XiboException('Guzzle exception getting currency exchange rate. E = ' . $guzzleException->getMessage(), $guzzleException->getCode(), $guzzleException); From 0919a9560484cf467e7f406f6e3fce2b62675f1d Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Tue, 17 Apr 2018 11:59:20 +0100 Subject: [PATCH 14/23] Fix missing params pass in proof of play export xibosignage/xibo#1482 --- lib/Controller/Stats.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Controller/Stats.php b/lib/Controller/Stats.php index 29fe075f7c..9394cef80b 100644 --- a/lib/Controller/Stats.php +++ b/lib/Controller/Stats.php @@ -648,7 +648,7 @@ public function export() $statement = $connection->prepare($sql); // Execute - $statement->execute(); + $statement->execute($params); // Do some post processing while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) { From f7f82d3e7ab05b9054a3d738161be59f3d6089fe Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Tue, 17 Apr 2018 13:58:32 +0100 Subject: [PATCH 15/23] Fix for CMS dev mode message, bump versions. --- install/master/data.sql | 2 +- lib/Helper/Environment.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/install/master/data.sql b/install/master/data.sql index 81352a205d..d310dd79c1 100644 --- a/install/master/data.sql +++ b/install/master/data.sql @@ -1,5 +1,5 @@ INSERT INTO `version` (`app_ver`, `XmdsVersion`, `XlfVersion`, `DBVersion`) VALUES -('1.8.8', 5, 2, 139); +('1.8.9', 5, 2, 140); INSERT INTO `group` (`groupID`, `group`, `IsUserSpecific`, `IsEveryone`, `isSystemNotification`) VALUES (1, 'Users', 0, 0, 0), diff --git a/lib/Helper/Environment.php b/lib/Helper/Environment.php index 11bcda7539..c0d65be985 100644 --- a/lib/Helper/Environment.php +++ b/lib/Helper/Environment.php @@ -11,8 +11,8 @@ class Environment { - public static $WEBSITE_VERSION_NAME = '1.8.8'; - public static $WEBSITE_VERSION = 139; + public static $WEBSITE_VERSION_NAME = '1.8.9'; + public static $WEBSITE_VERSION = 140; public static $VERSION_REQUIRED = '5.5'; public static $VERSION_UNSUPPORTED = '8.0'; @@ -251,6 +251,6 @@ public static function checkUrl($url) */ public static function isDevMode() { - return (isset($_SERVER['CMS_DEV_MODE']) && $_SERVER['CMS_DEV_MODE'] == true); + return (isset($_SERVER['CMS_DEV_MODE']) && $_SERVER['CMS_DEV_MODE'] === 'true'); } } \ No newline at end of file From 2c696b501d74577c4632ba712532e3f9c4fe20cf Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 17 Apr 2018 14:38:38 +0100 Subject: [PATCH 16/23] fixes noDataMessage in Ticker xibosignage/xibo#1484 --- views/base.twig | 1 + web/theme/default/js/xibo-forms.js | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/views/base.twig b/views/base.twig index 037b4e7d04..15a25e95d5 100644 --- a/views/base.twig +++ b/views/base.twig @@ -144,6 +144,7 @@ translations.success = "{{ "Success"|trans }}"; translations.failure = "{{ "Failure"|trans }}"; translations.enterText = "{{ "Enter text..."|trans }}"; + translations.noDataMessage = "{{ "No Data returned from the source"|trans }}"; translations.statusPending = "{{ "Status Pending"|trans }}"; translations.duplicate = "{{ "Duplicate"|trans }}"; translations.spacesWarning = "{{ "Warning - starts with or ends with a space, or contains double spaces"|trans }}"; diff --git a/web/theme/default/js/xibo-forms.js b/web/theme/default/js/xibo-forms.js index 2ad7ad5421..ff2ff16dc2 100644 --- a/web/theme/default/js/xibo-forms.js +++ b/web/theme/default/js/xibo-forms.js @@ -14,8 +14,9 @@ var text_callback = function(dialog, extraData) { var scale = $layout.attr('designer_scale'); var regionWidth = $("#region_" + $layout.data().currentRegionId).attr("width"); var regionHeight = $("#region_" + $layout.data().currentRegionId).attr("height"); - var applyContentsToIframe = function() { - $("#cke_ta_text iframe").contents().find("head").append("" + + var applyContentsToIframe = function(field) { + console.log('Applying iframe adjustments to ' + field); + $("#cke_" + field + " iframe").contents().find("head").append("" + "