From c18f130cd35dce6b580343f25baaf01dbb748fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 16:44:48 +0000 Subject: [PATCH 01/34] Bump elliptic from 6.5.3 to 6.5.4 Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4) Signed-off-by: dependabot[bot] --- yarn.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7fc779bb9..fdfd131d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2041,10 +2041,10 @@ bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: - version "4.11.9" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" - integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== bn.js@^5.1.1: version "5.1.3" @@ -2125,7 +2125,7 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.0.1: +brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= @@ -3404,17 +3404,17 @@ electron-to-chromium@^1.3.562: integrity sha512-WhRe6liQ2q/w1MZc8mD8INkenHivuHdrr4r5EQHNomy3NJux+incP6M6lDMd0paShP3MD0WGe5R1TWmEClf+Bg== elliptic@^6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" - integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" + bn.js "^4.11.9" + brorand "^1.1.0" hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" emoji-regex@^7.0.1: version "7.0.3" @@ -4527,7 +4527,7 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -hmac-drbg@^1.0.0: +hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= @@ -5864,7 +5864,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: +minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= From 5439dc14f53da6eba43359e03572548b0c347eee Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 9 Mar 2021 09:25:27 +0100 Subject: [PATCH 02/34] cleaner version check durng upgrade --- CHANGELOG.md | 3 +++ setup/upgrade.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d79f2ba4..036e7b32f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog Fab-manager +## Next release +- Prevent showing error message when testing for old versions during upgrade + ## v4.7.5 2021 March 08 - Fix a bug: unable to compile the assets during the upgrade, if the env file has some whitespaces around the equal sign diff --git a/setup/upgrade.sh b/setup/upgrade.sh index 4a99e2cef..ea8e8914f 100644 --- a/setup/upgrade.sh +++ b/setup/upgrade.sh @@ -67,7 +67,7 @@ version_error() version_check() { - VERSION=$(docker-compose exec -T "$SERVICE" cat .fabmanager-version) + VERSION=$(docker-compose exec -T "$SERVICE" cat .fabmanager-version 2>/dev/null) if [[ $? = 1 ]]; then VERSION=$(docker-compose exec -T "$SERVICE" cat package.json | jq -r '.version') fi From 8bc040ae8b5fb062664f2cefabaa884932c31321 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 9 Mar 2021 11:34:39 +0100 Subject: [PATCH 03/34] show group in email notif on account creation --- CHANGELOG.md | 1 + .../notify_admin_when_user_is_created.html.erb | 9 ++++++++- config/locales/mails.en.yml | 1 + config/locales/mails.fr.yml | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 036e7b32f..c3e5d15ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next release - Prevent showing error message when testing for old versions during upgrade +- In the email notification, sent to admins on account creation, show the group of the user ## v4.7.5 2021 March 08 - Fix a bug: unable to compile the assets during the upgrade, if the env file has some whitespaces around the equal sign diff --git a/app/views/notifications_mailer/notify_admin_when_user_is_created.html.erb b/app/views/notifications_mailer/notify_admin_when_user_is_created.html.erb index f7db42522..f994e2be5 100644 --- a/app/views/notifications_mailer/notify_admin_when_user_is_created.html.erb +++ b/app/views/notifications_mailer/notify_admin_when_user_is_created.html.erb @@ -1,6 +1,13 @@ <%= render 'notifications_mailer/shared/hello', recipient: @recipient %> -

<%= t('.body.new_account_created') %> "<%= @attached_object&.profile&.full_name || t('api.notifications.deleted_user') %> <<%= @attached_object.email%>>"

+

+ <%= t('.body.new_account_created') %> + "<%= @attached_object&.profile&.full_name || t('api.notifications.deleted_user') %> <<%= @attached_object.email%>>" +

+ +

+ <%= t('.body.user_of_group_html', { GROUP: @attached_object&.group&.name }) %> +

<% if @attached_object.invoicing_profile.organization %>

<%= t('.body.account_for_organization') %> <%= @attached_object.invoicing_profile.organization.name %>

diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index bca9a14a6..decb0c21f 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -100,6 +100,7 @@ en: subject: "A user account has been created" body: new_account_created: "A new user account has been created on the website:" + user_of_group_html: "The user has registered in the group %{GROUP}" account_for_organization: "This account manage an organization:" notify_admin_subscribed_plan: subject: "A subscription has been purchased" diff --git a/config/locales/mails.fr.yml b/config/locales/mails.fr.yml index 210d8cda2..d01d14f91 100644 --- a/config/locales/mails.fr.yml +++ b/config/locales/mails.fr.yml @@ -100,6 +100,7 @@ fr: subject: "Un compte utilisateur a été créé" body: new_account_created: "Un nouveau compte utilisateur vient d'être créé sur la plateforme :" + user_of_group_html: "L'utilisateur s'est inscrit dans le groupe %{GROUP}" account_for_organization: "Ce compte gère une structure :" notify_admin_subscribed_plan: subject: "Un abonnement a été souscrit" From 3d5484918c52155634d2c9aa7b23d66ac088c46e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 9 Mar 2021 11:41:24 +0100 Subject: [PATCH 04/34] eslint controllers/admin/statistics.js --- .../controllers/admin/statistics.js | 97 +++++++++---------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/app/frontend/src/javascript/controllers/admin/statistics.js b/app/frontend/src/javascript/controllers/admin/statistics.js index 997ee4b68..679f55255 100644 --- a/app/frontend/src/javascript/controllers/admin/statistics.js +++ b/app/frontend/src/javascript/controllers/admin/statistics.js @@ -135,7 +135,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', */ $scope.customFieldName = function (field) { return _t(`app.admin.statistics.${field}`); - } + }; /** * Callback to open the datepicker (interval start) @@ -292,8 +292,8 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', return refreshStats(); } else { return es.scroll({ - 'scroll': ES_SCROLL_TIME + 'm', - 'body': { scrollId: $scope.scrollId } + scroll: ES_SCROLL_TIME + 'm', + body: { scrollId: $scope.scrollId } } , function (error, response) { if (error) { @@ -335,7 +335,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', }; return $uibModal.open(options) - .result['finally'](null).then(function (info) { console.log(info); }); + .result.finally(null).then(function (info) { console.log(info); }); }; /** @@ -391,7 +391,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('statistics') < 0) { uitour.start(); } - } + }; /* PRIVATE SCOPE */ @@ -471,15 +471,15 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', // run query return es.search({ - 'index': 'stats', - 'type': index, - 'size': RESULTS_PER_PAGE, - 'scroll': ES_SCROLL_TIME + 'm', + index: 'stats', + type: index, + size: RESULTS_PER_PAGE, + scroll: ES_SCROLL_TIME + 'm', 'stat-type': type, 'custom-query': custom ? JSON.stringify(Object.assign({ exclude: custom.exclude }, buildElasticCustomCriterion(custom))) : '', 'start-date': moment($scope.datePickerStart.selected).format(), 'end-date': moment($scope.datePickerEnd.selected).format(), - 'body': buildElasticDataQuery(type, custom, $scope.agePicker.start, $scope.agePicker.end, moment($scope.datePickerStart.selected), moment($scope.datePickerEnd.selected), $scope.sorting) + body: buildElasticDataQuery(type, custom, $scope.agePicker.start, $scope.agePicker.end, moment($scope.datePickerStart.selected), moment($scope.datePickerEnd.selected), $scope.sorting) } , function (error, response) { if (error) { @@ -503,19 +503,19 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', */ const buildElasticDataQuery = function (type, custom, ageMin, ageMax, intervalBegin, intervalEnd, sortings) { const q = { - 'query': { - 'bool': { - 'must': [ + query: { + bool: { + must: [ { - 'term': { - 'type': type + term: { + type: type } }, { - 'range': { - 'date': { - 'gte': intervalBegin.format(), - 'lte': intervalEnd.format() + range: { + date: { + gte: intervalBegin.format(), + lte: intervalEnd.format() } } } @@ -526,10 +526,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', // optional date range if ((typeof ageMin === 'number') && (typeof ageMax === 'number')) { q.query.bool.must.push({ - 'range': { - 'age': { - 'gte': ageMin, - 'lte': ageMax + range: { + age: { + gte: ageMin, + lte: ageMax } } }); @@ -539,7 +539,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', const criterion = buildElasticCustomCriterion(custom); if (custom.exclude) { q.query.bool.must_not = [ - { 'term': criterion.match } + { term: criterion.match } ]; } else { q.query.bool.must.push(criterion); @@ -547,24 +547,24 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', } if (sortings) { - q['sort'] = buildElasticSortCriteria(sortings); + q.sort = buildElasticSortCriteria(sortings); } // aggregations (avg age & CA sum) - q['aggs'] = { - 'total_ca': { - 'sum': { - 'field': 'ca' + q.aggs = { + total_ca: { + sum: { + field: 'ca' } }, - 'average_age': { - 'avg': { - 'field': 'age' + average_age: { + avg: { + field: 'age' } }, - 'total_stat': { - 'sum': { - 'field': 'stat' + total_stat: { + sum: { + field: 'stat' } } }; @@ -579,7 +579,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', const buildElasticCustomCriterion = function (custom) { if (custom) { const criterion = { - 'match': {} + match: {} }; switch ($scope.getCustomValueInputType($scope.customFilter.criterion)) { case 'input_date': criterion.match[custom.key] = moment(custom.value).format('YYYY-MM-DD'); break; @@ -602,7 +602,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', angular.forEach(criteria, function (value, key) { if ((typeof value !== 'undefined') && (value !== null) && (value !== 'none')) { const c = {}; - c[key] = { 'order': value }; + c[key] = { order: value }; return crits.push(c); } }); @@ -624,8 +624,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', // if no plans were created, there's no types for statisticIndex=subscriptions if ($scope.type.active) { - $scope.filters.splice(4, 0, { key: 'subType', label: _t('app.admin.statistics.type'), values: $scope.type.active.subtypes }) - + $scope.filters.splice(4, 0, { key: 'subType', label: _t('app.admin.statistics.type'), values: $scope.type.active.subtypes }); if (!$scope.type.active.simple) { const f = { key: 'stat', label: $scope.type.active.label, values: ['input_number'] }; @@ -670,7 +669,7 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', ]); -Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$uibModalInstance', 'Export', 'dates', 'query', 'index', 'type', 'CSRF', 'growl', '_t', +Application.Controllers.controller('ExportStatisticsController', ['$scope', '$uibModalInstance', 'Export', 'dates', 'query', 'index', 'type', 'CSRF', 'growl', '_t', function ($scope, $uibModalInstance, Export, dates, query, index, type, CSRF, growl, _t) { // Retrieve Anti-CSRF tokens from cookies CSRF.setMetaTags(); @@ -739,14 +738,14 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u if ($scope.export.type === 'global') { $scope.actionUrl = '/stats/global/export'; return $scope.query = JSON.stringify({ - 'query': { - 'bool': { - 'must': [ + query: { + bool: { + must: [ { - 'range': { - 'date': { - 'gte': moment($scope.dates.start).format(), - 'lte': moment($scope.dates.end).format() + range: { + date: { + gte: moment($scope.dates.start).format(), + lte: moment($scope.dates.end).format() } } } @@ -766,8 +765,8 @@ Application.Controllers.controller('ExportStatisticsController', [ '$scope', '$u $scope.exportData = function () { const statusQry = { category: 'statistics', type: $scope.export.type, query: $scope.query }; if ($scope.export.type !== 'global') { - statusQry['type'] = index.key; - statusQry['key'] = type.key; + statusQry.type = index.key; + statusQry.key = type.key; } Export.status(statusQry).then(function (res) { From cd30e0d8d833e72d50586a07b076ff3497052bca Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 9 Mar 2021 12:39:25 +0100 Subject: [PATCH 05/34] [bug] statistics: select machines as default tab if subscriptions is disabled --- CHANGELOG.md | 1 + .../javascript/controllers/admin/graphs.js | 29 +++++++++++++++++-- .../controllers/admin/statistics.js | 16 +++++++++- .../templates/admin/statistics/graphs.html | 2 +- .../templates/admin/statistics/index.html | 4 +-- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3e5d15ce..cf4f1a0c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Next release - Prevent showing error message when testing for old versions during upgrade - In the email notification, sent to admins on account creation, show the group of the user +- Fix a bug: subscriptions tab is selected by default in statistics, even if the module is disabled ## v4.7.5 2021 March 08 - Fix a bug: unable to compile the assets during the upgrade, if the env file has some whitespaces around the equal sign diff --git a/app/frontend/src/javascript/controllers/admin/graphs.js b/app/frontend/src/javascript/controllers/admin/graphs.js index 3883405e0..d92f76366 100644 --- a/app/frontend/src/javascript/controllers/admin/graphs.js +++ b/app/frontend/src/javascript/controllers/admin/graphs.js @@ -51,6 +51,9 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro // active tab will be set here $scope.selectedIndex = null; + // ui-bootstrap active tab index + $scope.selectedTab = 0; + // for palmares graphs, filters values are stored here $scope.ranking = { sortCriterion: 'ca', @@ -101,9 +104,11 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro * Callback called when the active tab is changed. * Recover the current tab and store its value in $scope.selectedIndex * @param tab {Object} elasticsearch statistic structure + * @param index {number} index of the tab in the $scope.statistics array */ - $scope.setActiveTab = function (tab) { + $scope.setActiveTab = function (tab, index) { $scope.selectedIndex = tab; + $scope.selectedTab = index; $scope.ranking.groupCriterion = 'subType'; if (tab.ca) { $scope.ranking.sortCriterion = 'ca'; @@ -113,6 +118,17 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro return refreshChart(); }; + /** + * Returns true if the provided tab must be hidden due to some global or local configuration + * @param tab {Object} elasticsearch statistic structure (from statistic_indices table) + */ + $scope.hiddenTab = function (tab) { + if (tab.graph) { + return !((tab.es_type_key === 'subscription') && !$rootScope.modules.plans); + } + return false; + }; + /** * Callback to close the date-picking popup and refresh the results */ @@ -137,11 +153,20 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro $scope.$watch(scope => scope.ranking.groupCriterion , (newValue, oldValue) => refreshChart()); return refreshChart(); + + // set the default tab to "machines" if "subscriptions" are disabled + if (!$rootScope.modules.plans) { + const idx = $scope.statistics.findIndex(s => s.es_type_key === 'machine'); + $scope.setActiveTab($scope.statistics[idx], idx); + } else { + const idx = $scope.statistics.findIndex(s => s.es_type_key === 'subscription'); + $scope.setActiveTab($scope.statistics[idx], idx); + } }); // workaround for angular-bootstrap::tabs behavior: on tab deletion, another tab will be selected // which will cause every tabs to reload, one by one, when the view is closed - return $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { + $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { if ((fromState.name === 'app.admin.stats_graphs') && (Object.keys(fromParams).length === 0)) { return $scope.preventRefresh = true; } diff --git a/app/frontend/src/javascript/controllers/admin/statistics.js b/app/frontend/src/javascript/controllers/admin/statistics.js index 679f55255..dd99e2d1f 100644 --- a/app/frontend/src/javascript/controllers/admin/statistics.js +++ b/app/frontend/src/javascript/controllers/admin/statistics.js @@ -76,6 +76,9 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', // active tab will be set here $scope.selectedIndex = null; + // ui-bootstrap active tab index + $scope.selectedTab = 0; + // type filter binding $scope.type = { selected: null, @@ -159,9 +162,11 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', * Callback called when the active tab is changed. * recover the current tab and store its value in $scope.selectedIndex * @param tab {Object} elasticsearch statistic structure (from statistic_indices table) + * @param index {number} index of the tab in the $scope.statistics array */ - $scope.setActiveTab = function (tab) { + $scope.setActiveTab = function (tab, index) { $scope.selectedIndex = tab; + $scope.selectedTab = index; $scope.type.selected = tab.types[0]; $scope.type.active = $scope.type.selected; $scope.customFilter.criterion = {}; @@ -406,6 +411,15 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', return $scope.preventRefresh = true; } }); + + // set the default tab to "machines" if "subscriptions" are disabled + if (!$rootScope.modules.plans) { + const idx = $scope.statistics.findIndex(s => s.es_type_key === 'machine'); + $scope.setActiveTab($scope.statistics[idx], idx); + } else { + const idx = $scope.statistics.findIndex(s => s.es_type_key === 'subscription'); + $scope.setActiveTab($scope.statistics[idx], idx); + } }; /** diff --git a/app/frontend/templates/admin/statistics/graphs.html b/app/frontend/templates/admin/statistics/graphs.html index 69043bfbf..21ad2f331 100644 --- a/app/frontend/templates/admin/statistics/graphs.html +++ b/app/frontend/templates/admin/statistics/graphs.html @@ -102,7 +102,7 @@ - +
diff --git a/app/frontend/templates/admin/statistics/index.html b/app/frontend/templates/admin/statistics/index.html index c465058f9..0928da2d8 100644 --- a/app/frontend/templates/admin/statistics/index.html +++ b/app/frontend/templates/admin/statistics/index.html @@ -31,8 +31,8 @@
- - + +
-
+
{{ 'app.admin.settings.shift_enter_to_force_carriage_return' | translate }} diff --git a/app/frontend/templates/admin/settings/general.html b/app/frontend/templates/admin/settings/general.html index 611235b51..ff9c0e9aa 100644 --- a/app/frontend/templates/admin/settings/general.html +++ b/app/frontend/templates/admin/settings/general.html @@ -48,52 +48,76 @@

{{ 'app.admin.settings.message_of_the_machine_booking_page' }}

-
+

{{ 'app.admin.settings.warning_message_of_the_training_booking_page'}}

-
+

{{ 'app.admin.settings.information_message_of_the_training_reservation_page'}}

-
+

{{ 'app.admin.settings.message_of_the_subscriptions_page' }}

-
+

{{ 'app.admin.settings.message_of_the_events_page' }}

-
+

{{ 'app.admin.settings.message_of_the_spaces_page' }}

-
+
diff --git a/app/frontend/templates/admin/settings/home_page.html b/app/frontend/templates/admin/settings/home_page.html index 40de310f7..4b4c858c1 100644 --- a/app/frontend/templates/admin/settings/home_page.html +++ b/app/frontend/templates/admin/settings/home_page.html @@ -14,8 +14,15 @@

{{ 'app.admin.settings.news_of_the_home_page' }}

-
+
+
{{ 'app.admin.settings.leave_it_empty_to_not_bring_up_any_news_on_the_home_page' | translate }}
From 39a7e238811c4414e3dcf14903ee4670640b6790 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 22 Mar 2021 13:13:51 +0100 Subject: [PATCH 26/34] Improved style of public plans page --- CHANGELOG.md | 2 + .../src/stylesheets/app.components.scss | 130 +++--------------- app/frontend/src/stylesheets/application.scss | 1 + .../src/stylesheets/modules/plan-card.scss | 102 ++++++++++++++ app/frontend/templates/plans/_plan.html | 34 ++--- app/frontend/templates/plans/index.html | 17 +-- app/themes/casemate/style.scss.erb | 24 ++-- test/fixtures/stylesheets.yml | 6 +- 8 files changed, 157 insertions(+), 159 deletions(-) create mode 100644 app/frontend/src/stylesheets/modules/plan-card.scss diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7790417..2dee75bf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ - More explanations in the setup script - Send pre-compressed assets to the browsers instead of the regular ones - Links created using "medium editor" opens in new tabs +- Improved style of public plans page - Fix a bug: subscriptions tab is selected by default in statistics, even if the module is disabled - Fix a bug: select all plans for slot restriction (through the dedicated button) also selects the disabled plans - Fix a bug: recurring availabilities are not restricted to subscribers - Fix a security issue: updated elliptic to 6.5.4 to fix [CVE-2020-28498](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28498) - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/nginx-packs-directive.sh | bash` - [TODO DEPLOY] `rails db:seed` +- [TODO DEPLOY] `rails fablab:maintenance:rebuild_stylesheet` ## v4.7.5 2021 March 08 - Fix a bug: unable to compile the assets during the upgrade, if the env file has some whitespaces around the equal sign diff --git a/app/frontend/src/stylesheets/app.components.scss b/app/frontend/src/stylesheets/app.components.scss index 3d52a94c2..04d1e6691 100644 --- a/app/frontend/src/stylesheets/app.components.scss +++ b/app/frontend/src/stylesheets/app.components.scss @@ -267,125 +267,31 @@ } } -.pricing-panel { - border: 1px solid $border-color; - height: 391px; - - &:first-child { - border-right: none; - - @include border-radius(3px 0 0 3px); +.list-of-plans { + .group-title { + width: 83.33%; + border-bottom: 1px solid; + padding-bottom: 2em; + margin: auto auto 1em; } - &:last-child { - @include border-radius(0 3px 3px 0); - } - - .plan-card { - height: 100%; - display: flex; - flex-direction: column; - justify-content: flex-start; - } - - .title { - margin: 10px 0; - font-size: rem-calc(16); - text-transform: uppercase; - color: black; - } - - .content { - padding: 15px 0; - background-color: $bg-gray; - - .wrap, .wrap-monthly { - width: 130px; - height: 130px; - display: inline-block; - background: white; - - @include border-radius(50%, 50%, 50%, 50%); - - border: 3px solid; - - .price { - width: 114px; - display: flex; - flex-direction: column; - justify-content: center; - - @include border-radius(50%, 50%, 50%, 50%); - } + .plans-per-group { + & { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + border: 1px solid transparent; } - .wrap-monthly { - & > .price { - & > .amount { - padding-top: 4px; - line-height: 1.2em; - } - - & > .period { - padding-top: 4px; - } - } - } - .price { - position: relative; - top: 5px; - left: 5px; - height: 114px; - background-color: black; - - .amount { - padding-left: 4px; - padding-right: 4px; - font-weight: bold; - font-size: rem-calc(17); - color: white; - } - - .period { - position: relative; - top: -6px; - font-size: rem-calc(14); - color: white; - } - } - } - - .card-footer { - display: flex; - flex-direction: column; - justify-content: space-around; - height: 100%; - - .plan-description { - max-height: 5.2em; - overflow: hidden; + & > * { + width: 50%; } - .cta-button { - margin: 20px 0; - - .subscribe-button { - @extend .btn; - @extend .rounded; - - outline: 0; - font-weight: 600; - font-size: rem-calc(16); - background-color: white; - padding-left: 30px; - padding-right: 30px; + @media screen and (max-width: 992px) { + & > * { + width: 100%; } - button.subscribe-button:focus, button.subscribe-button:hover { - outline: 0; - } - } - .info-link { - margin-top: 1em; } } } diff --git a/app/frontend/src/stylesheets/application.scss b/app/frontend/src/stylesheets/application.scss index fac4aacf6..e07995746 100644 --- a/app/frontend/src/stylesheets/application.scss +++ b/app/frontend/src/stylesheets/application.scss @@ -32,5 +32,6 @@ @import "modules/payment-schedules-list"; @import "modules/stripe-confirm"; @import "modules/payment-schedule-dashboard"; +@import "modules/plan-card"; @import "app.responsive"; diff --git a/app/frontend/src/stylesheets/modules/plan-card.scss b/app/frontend/src/stylesheets/modules/plan-card.scss new file mode 100644 index 000000000..71c4fcb0b --- /dev/null +++ b/app/frontend/src/stylesheets/modules/plan-card.scss @@ -0,0 +1,102 @@ +.plan-card { + display: block; + height: 100%; + width: 100%; + + .title { + margin: 10px 0; + font-size: 1.6rem; + text-transform: uppercase; + } + + .content { + & { + padding: 15px 0; + background-color: #f5f5f5; + } + + .wrap, .wrap-monthly { + width: 130px; + height: 130px; + display: inline-block; + background: white; + + @include border-radius(50%, 50%, 50%, 50%); + + border: 3px solid; + + .price { + width: 114px; + display: flex; + flex-direction: column; + justify-content: center; + + @include border-radius(50%, 50%, 50%, 50%); + } + } + + .wrap-monthly { + & > .price { + & > .amount { + padding-top: 4px; + line-height: 1.2em; + } + + & > .period { + padding-top: 4px; + } + } + } + .price { + position: relative; + top: 5px; + left: 5px; + height: 114px; + background-color: black; + + .amount { + padding-left: 4px; + padding-right: 4px; + font-weight: bold; + font-size: rem-calc(17); + color: white; + } + + .period { + position: relative; + top: -6px; + font-size: rem-calc(14); + color: white; + } + } + } + + .card-footer { + .plan-description { + + & p:nth-child(2n+3), p:nth-child(2n+4) { + display: none; + } + } + .cta-button { + margin: 20px 0; + + .subscribe-button { + @extend .btn; + @extend .rounded; + outline: 0; + font-weight: 600; + font-size: rem-calc(16); + background-color: white; + padding-left: 30px; + padding-right: 30px; + } + button.subscribe-button:focus, button.subscribe-button:hover { + outline: 0; + } + } + .info-link { + margin-top: 1em; + } + } +} diff --git a/app/frontend/templates/plans/_plan.html b/app/frontend/templates/plans/_plan.html index 359b55525..56a3b0f70 100644 --- a/app/frontend/templates/plans/_plan.html +++ b/app/frontend/templates/plans/_plan.html @@ -1,33 +1,25 @@ -
+
-

{{ plansGroup.name }}

+

{{ plansGroup.name }}

-
+ + {{ 'app.shared.plan_subscribe.do_not_subscribe' | translate }} +
diff --git a/app/frontend/templates/plans/index.html b/app/frontend/templates/plans/index.html index 0706c9c6e..a5d81fbfa 100644 --- a/app/frontend/templates/plans/index.html +++ b/app/frontend/templates/plans/index.html @@ -14,31 +14,26 @@ -
+
-

{{plansGroup.name}}

+

{{plansGroup.name}}

-
+
- -
- - + is-selected="isSelected(plan)" -
diff --git a/app/themes/casemate/style.scss.erb b/app/themes/casemate/style.scss.erb index 28e1e9b6a..0caae8bc3 100644 --- a/app/themes/casemate/style.scss.erb +++ b/app/themes/casemate/style.scss.erb @@ -88,7 +88,7 @@ a { .app-generator a, .home-events h4 a, a.reinit-filters, -.pricing-panel a, +.plan-card a, .calendar-url a, .article a, a.project-author, @@ -103,7 +103,7 @@ a.collected-infos { .app-generator a:hover, .home-events h4 a:hover, a.reinit-filters:hover, -.pricing-panel a:hover, +.plan-card a:hover, .calendar-url a:hover, .article a:hover, a.project-author:hover, @@ -254,19 +254,19 @@ h5:after { color: $secondary-text-color; } -.pricing-panel .plan-card .content .wrap, -.pricing-panel .plan-card .content .wrap-monthly { - border-color: $secondary; +.plan-card .content .wrap, +.plan-card .content .wrap-monthly { + border-color: $secondary !important; } -.pricing-panel .plan-card .content .price { - background-color: $primary; - color: $primary-text-color; +.plan-card .content .price { + background-color: $primary !important; + color: $primary-text-color !important; } -.pricing-panel .card-footer .cta-button .btn:hover, -.pricing-panel .card-footer .cta-button .custom-invoice .modal-body .elements li:hover, -.custom-invoice .modal-body .elements .pricing-panel .card-footer .cta-button li:hover { +.plan-card .card-footer .cta-button .btn:hover, +.plan-card .card-footer .cta-button .custom-invoice .modal-body .elements li:hover, +.custom-invoice .modal-body .elements .plan-card .card-footer .cta-button li:hover { background-color: $secondary !important; color: $secondary-text-color; } @@ -306,7 +306,7 @@ section#cookies-modal div.cookies-consent .cookies-actions button.accept { color: $secondary-text-color; } -.pricing-panel { +.plan-card { .card-footer { .cta-button { button.subscribe-button { diff --git a/test/fixtures/stylesheets.yml b/test/fixtures/stylesheets.yml index 182cefe56..a0a42cbca 100644 --- a/test/fixtures/stylesheets.yml +++ b/test/fixtures/stylesheets.yml @@ -31,8 +31,8 @@ stylesheet_1: .btn-warning:hover, .editable-buttons button[type=submit].btn-primary:hover, .btn-warning:focus, .editable-buttons button[type=submit].btn-primary:focus, .btn-warning.focus, .editable-buttons button.focus[type=submit].btn-primary, .btn-warning:active, .editable-buttons button[type=submit].btn-primary:active, .btn-warning.active, .editable-buttons button.active[type=submit].btn-primary, .open > .btn-warning.dropdown-toggle, .editable-buttons .open > button.dropdown-toggle[type=submit].btn-primary { background-color: #998500 !important; border-color: #998500 !important; } .btn-warning-full { border-color: #ffdd00; background-color: #ffdd00; } .heading .heading-btn a:hover { background-color: #ffdd00; } - .pricing-panel .content .wrap { border-color: #ffdd00; } - .pricing-panel .cta-button .btn:hover, .pricing-panel .cta-button .custom-invoice .modal-body .elements li:hover, .custom-invoice .modal-body .elements .pricing-panel .cta-button li:hover { background-color: #ffdd00 !important; } + .plan-card .content .wrap { border-color: #ffdd00; } + .plan-card .cta-button .btn:hover, .plan-card .cta-button .custom-invoice .modal-body .elements li:hover, .custom-invoice .modal-body .elements .plan-card .cta-button li:hover { background-color: #ffdd00 !important; } a.label:hover, .form-control.form-control-ui-select .select2-choices a.select2-search-choice:hover, a.label:focus, .form-control.form-control-ui-select .select2-choices a.select2-search-choice:focus { color: #cb1117; } .about-picture { background: linear-gradient( rgba(255,255,255,0.12), rgba(255,255,255,0.13) ), linear-gradient( rgba(203, 17, 23, 0.78), rgba(203, 17, 23, 0.82) ), url('/about-fablab.jpg') no-repeat; } .social-icons > div:hover { background-color: #ffdd00; } @@ -46,4 +46,4 @@ stylesheet_1: stylesheet_2: id: 2 contents: ".home-page { }" - name: home_page \ No newline at end of file + name: home_page From a5b3728f8cb1dd30507be436ddbdc87b6318adcb Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 22 Mar 2021 17:42:54 +0100 Subject: [PATCH 27/34] [bug] various fixes on accouting exports - accounting exports may ignore some invoices for the first and last days - file caching for accounring exports is not used --- CHANGELOG.md | 2 ++ app/controllers/api/accounting_exports_controller.rb | 2 +- app/frontend/src/javascript/controllers/admin/invoices.js | 5 ++--- app/services/accounting_export_service.rb | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dee75bf6..5e5dab41e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - Fix a bug: subscriptions tab is selected by default in statistics, even if the module is disabled - Fix a bug: select all plans for slot restriction (through the dedicated button) also selects the disabled plans - Fix a bug: recurring availabilities are not restricted to subscribers +- Fix a bug: accounting exports may ignore some invoices for the first and last days +- Fix a bug: accounting export caching is not working - Fix a security issue: updated elliptic to 6.5.4 to fix [CVE-2020-28498](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28498) - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/nginx-packs-directive.sh | bash` - [TODO DEPLOY] `rails db:seed` diff --git a/app/controllers/api/accounting_exports_controller.rb b/app/controllers/api/accounting_exports_controller.rb index 24d15492a..20dd342dd 100644 --- a/app/controllers/api/accounting_exports_controller.rb +++ b/app/controllers/api/accounting_exports_controller.rb @@ -8,7 +8,7 @@ class API::AccountingExportsController < API::ApiController def export authorize :accounting_export - export = Export.where(category: 'accounting', export_type: 'accounting-software', key: params[:key]) + export = Export.where(category: 'accounting', export_type: params[:type], key: params[:key]) .where(extension: params[:extension], query: params[:query]) .where('created_at > ?', Invoice.maximum('updated_at')) .last diff --git a/app/frontend/src/javascript/controllers/admin/invoices.js b/app/frontend/src/javascript/controllers/admin/invoices.js index 168e42089..31a0a240a 100644 --- a/app/frontend/src/javascript/controllers/admin/invoices.js +++ b/app/frontend/src/javascript/controllers/admin/invoices.js @@ -1309,8 +1309,8 @@ Application.Controllers.controller('AccountingExportModalController', ['$scope', columns: $scope.exportTarget.settings.columns, encoding: $scope.exportTarget.settings.encoding, date_format: $scope.exportTarget.settings.dateFormat, - start_date: $scope.exportTarget.startDate, - end_date: $scope.exportTarget.endDate, + start_date: moment.utc($scope.exportTarget.startDate).startOf('day').toISOString(), + end_date: moment.utc($scope.exportTarget.endDate).endOf('day').toISOString(), label_max_length: $scope.exportTarget.settings.labelMaxLength, decimal_separator: $scope.exportTarget.settings.decimalSeparator, export_invoices_at_zero: $scope.exportTarget.settings.exportInvoicesAtZero @@ -1370,7 +1370,6 @@ Application.Controllers.controller('StripeKeysModalController', ['$scope', '$uib $scope.publicKeyStatus = false; return; } - const today = new Date(); $http({ method: 'POST', url: 'https://api.stripe.com/v1/tokens', diff --git a/app/services/accounting_export_service.rb b/app/services/accounting_export_service.rb index f3986f75a..c86797b1a 100644 --- a/app/services/accounting_export_service.rb +++ b/app/services/accounting_export_service.rb @@ -33,6 +33,7 @@ class AccountingExportService invoices = Invoice.where('created_at >= ? AND created_at <= ?', start_date, end_date).order('created_at ASC') invoices = invoices.where('total > 0') unless export_zeros invoices.each do |i| + puts "processing invoice #{i.id}..." content << generate_rows(i) end From f535be5b0561c25dc840bcc38460b8ff3c3532bf Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 22 Mar 2021 18:02:56 +0100 Subject: [PATCH 28/34] WIP: address_required --- .../src/javascript/controllers/application.js.erb | 5 ++++- app/frontend/src/javascript/controllers/profile.js | 9 ++++++--- app/frontend/src/javascript/models/setting.ts | 5 ++++- app/frontend/src/javascript/router.js | 5 ++--- app/frontend/templates/admin/settings/general.html | 12 ++++++++++++ app/models/invoicing_profile.rb | 2 ++ app/models/setting.rb | 3 ++- app/models/user.rb | 3 ++- app/policies/setting_policy.rb | 2 +- config/locales/app.admin.en.yml | 4 ++++ config/locales/app.admin.fr.yml | 3 +++ 11 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/frontend/src/javascript/controllers/application.js.erb b/app/frontend/src/javascript/controllers/application.js.erb index c25601eb5..e3dd67d92 100644 --- a/app/frontend/src/javascript/controllers/application.js.erb +++ b/app/frontend/src/javascript/controllers/application.js.erb @@ -92,7 +92,7 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco templateUrl: '/shared/signupModal.html', size: 'md', resolve: { - settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'recaptcha_site_key', 'confirmation_required']" }).$promise; }] + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'recaptcha_site_key', 'confirmation_required', 'address_required']" }).$promise; }] }, controller: ['$scope', '$uibModalInstance', 'Group', 'CustomAsset', 'settingsPromise', 'growl', '_t', function ($scope, $uibModalInstance, Group, CustomAsset, settingsPromise, growl, _t) { // default parameters for the date picker in the account creation modal @@ -107,6 +107,9 @@ Application.Controllers.controller('ApplicationController', ['$rootScope', '$sco // is the phone number required to sign-up? $scope.phoneRequired = (settingsPromise.phone_required === 'true'); + // is the address required to sign-up? + $scope.addressRequired = (settingsPromise.address_required === 'true'); + // reCaptcha v2 site key (or undefined) $scope.recaptchaSiteKey = settingsPromise.recaptcha_site_key; diff --git a/app/frontend/src/javascript/controllers/profile.js b/app/frontend/src/javascript/controllers/profile.js index e7fcf4788..cba08dba6 100644 --- a/app/frontend/src/javascript/controllers/profile.js +++ b/app/frontend/src/javascript/controllers/profile.js @@ -13,8 +13,8 @@ 'use strict'; -Application.Controllers.controller('CompleteProfileController', ['$scope', '$rootScope', '$state', '$window', '_t', 'growl', 'CSRF', 'Auth', 'Member', 'settingsPromise', 'activeProviderPromise', 'groupsPromise', 'cguFile', 'memberPromise', 'Session', 'dialogs', 'AuthProvider', 'phoneRequiredPromise', - function ($scope, $rootScope, $state, $window, _t, growl, CSRF, Auth, Member, settingsPromise, activeProviderPromise, groupsPromise, cguFile, memberPromise, Session, dialogs, AuthProvider, phoneRequiredPromise) { +Application.Controllers.controller('CompleteProfileController', ['$scope', '$rootScope', '$state', '$window', '_t', 'growl', 'CSRF', 'Auth', 'Member', 'settingsPromise', 'activeProviderPromise', 'groupsPromise', 'cguFile', 'memberPromise', 'Session', 'dialogs', 'AuthProvider', + function ($scope, $rootScope, $state, $window, _t, growl, CSRF, Auth, Member, settingsPromise, activeProviderPromise, groupsPromise, cguFile, memberPromise, Session, dialogs, AuthProvider) { /* PUBLIC SCOPE */ // API URL where the form will be posted @@ -48,7 +48,10 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo $scope.cgu = cguFile.custom_asset; // is the phone number required in _member_form? - $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); + $scope.phoneRequired = (settingsPromise.phone_required === 'true'); + + // is the address required in _member_form? + $scope.addressRequired = (settingsPromise.address_required === 'true'); // Angular-Bootstrap datepicker configuration for birthday $scope.datePicker = { diff --git a/app/frontend/src/javascript/models/setting.ts b/app/frontend/src/javascript/models/setting.ts index b42653b85..ccde19dc3 100644 --- a/app/frontend/src/javascript/models/setting.ts +++ b/app/frontend/src/javascript/models/setting.ts @@ -98,7 +98,10 @@ export enum SettingName { ConfirmationRequired = 'confirmation_required', WalletModule = 'wallet_module', StatisticsModule = 'statistics_module', - UpcomingEventsShown = 'upcoming_events_shown' + UpcomingEventsShown = 'upcoming_events_shown', + PaymentSchedulePrefix = 'payment_schedule_prefix', + TrainingsModule = 'trainings_module', + AddressRequired = 'address_required' } export interface Setting { diff --git a/app/frontend/src/javascript/router.js b/app/frontend/src/javascript/router.js index 5b2b67dbd..b1f5540ba 100644 --- a/app/frontend/src/javascript/router.js +++ b/app/frontend/src/javascript/router.js @@ -130,12 +130,11 @@ angular.module('application.router', ['ui.router']) } }, resolve: { - settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['fablab_name', 'name_genre']" }).$promise; }], + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['fablab_name', 'name_genre', 'phone_required', 'address_required']" }).$promise; }], activeProviderPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.active().$promise; }], groupsPromise: ['Group', function (Group) { return Group.query().$promise; }], cguFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'cgu-file' }).$promise; }], - memberPromise: ['Member', 'currentUser', function (Member, currentUser) { return Member.get({ id: currentUser.id }).$promise; }], - phoneRequiredPromise: ['Setting', function (Setting) { return Setting.get({ name: 'phone_required' }).$promise; }] + memberPromise: ['Member', 'currentUser', function (Member, currentUser) { return Member.get({ id: currentUser.id }).$promise; }] } }) diff --git a/app/frontend/templates/admin/settings/general.html b/app/frontend/templates/admin/settings/general.html index ff9c0e9aa..454672502 100644 --- a/app/frontend/templates/admin/settings/general.html +++ b/app/frontend/templates/admin/settings/general.html @@ -424,6 +424,18 @@
+
+

{{ 'app.admin.settings.address' }}

+

+ {{ 'app.admin.settings.address_required_info' }} +

+
+ + +
+

{{ 'app.admin.settings.captcha' }}

diff --git a/app/models/invoicing_profile.rb b/app/models/invoicing_profile.rb index ed2b21fe0..963e8790f 100644 --- a/app/models/invoicing_profile.rb +++ b/app/models/invoicing_profile.rb @@ -20,6 +20,8 @@ class InvoicingProfile < ApplicationRecord has_many :operated_invoices, foreign_key: :operator_profile_id, class_name: 'Invoice', dependent: :nullify has_many :operated_payment_schedules, foreign_key: :operator_profile_id, class_name: 'PaymentSchedule', dependent: :nullify + validates :address, presence: true, if: -> { Setting.get('address_required') } + def full_name # if first_name or last_name is nil, the empty string will be used as a temporary replacement (first_name || '').humanize.titleize + ' ' + (last_name || '').humanize.titleize diff --git a/app/models/setting.rb b/app/models/setting.rb index d83d35101..f2e34af1a 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -108,7 +108,8 @@ class Setting < ApplicationRecord statistics_module upcoming_events_shown payment_schedule_prefix - trainings_module] } + trainings_module + address_required] } # WARNING: when adding a new key, you may also want to add it in app/policies/setting_policy.rb#public_whitelist def value diff --git a/app/models/user.rb b/app/models/user.rb index 23372aa39..01307ab03 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -203,7 +203,8 @@ class User < ApplicationRecord def need_completion? statistic_profile.gender.nil? || profile.first_name.blank? || profile.last_name.blank? || username.blank? || email.blank? || encrypted_password.blank? || group_id.nil? || statistic_profile.birthday.blank? || - (Setting.get('phone_required') && profile.phone.blank?) + (Setting.get('phone_required') && profile.phone.blank?) || + (Setting.get('address_required') && invoicing_profile.address&.address&.blank?) end ## Retrieve the requested data in the User and user's Profile tables diff --git a/app/policies/setting_policy.rb b/app/policies/setting_policy.rb index c24709daf..491f8c8a7 100644 --- a/app/policies/setting_policy.rb +++ b/app/policies/setting_policy.rb @@ -38,7 +38,7 @@ class SettingPolicy < ApplicationPolicy fablab_name name_genre event_explications_alert space_explications_alert link_name home_content phone_required tracking_id book_overlapping_slots slot_duration events_in_calendar spaces_module plans_module invoicing_module recaptcha_site_key feature_tour_display disqus_shortname allowed_cad_extensions openlab_app_id openlab_default - online_payment_module stripe_public_key confirmation_required wallet_module trainings_module] + online_payment_module stripe_public_key confirmation_required wallet_module trainings_module address_required] end ## diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 77f427439..921822901 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -1073,6 +1073,7 @@ en: machines_sort_by: "machines display order" fab_analytics: "Fab Analytics" phone_required: "phone required" + address_required: "address required" tracking_id: "tracking ID" facebook_app_id: "Facebook App ID" twitter_analytics: "Twitter analytics account" @@ -1109,6 +1110,9 @@ en: phone: "Phone" phone_is_required: "Phone required" phone_required_info: "You can define if the phone number should be required to register a new user on Fab-manager." + address: "Address" + address_required_info: "You can define if the address should be required to register a new user on Fab-manager. Please note that, depending on your country, the regulations may requires addresses for the invoices to be valid." + address_is_required: "Address is required" captcha: "Captcha" captcha_info_html: "You can setup a protection against robots, to prevent them creating members accounts. This protection is using Google reCAPTCHA. Sign up for an API key pair to start using the captcha." site_key: "Site key" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 1af569356..a98083288 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -1109,6 +1109,9 @@ fr: phone: "Téléphone" phone_is_required: "Téléphone requis" phone_required_info: "Vous pouvez définir si le numéro de téléphone doit être requis, lors de l'enregistrement d'un nouvel utilisateur sur Fab-manager." + address: "Adresse" + address_required_info: "Vous pouvez définir si l'adresse doit être requise, lors de l'enregistrement d'un nouvel utilisateur sur Fab-manager. Veuillez noter que, selon votre pays, la réglementation peut exiger des adresses pour que les factures soient valides." + address_is_required: "Adresse requise" captcha: "Captcha" captcha_info_html: "Vous pouvez mettre en place une protection contre les robots, pour les empêcher de créer des comptes membre. Cette protection utilise Google reCAPTCHA. Inscrivez vous pour obtenir une paire de clefs d'API afin d'utiliser le captcha." site_key: "Clef de site" From 7099f1f317d2584eb2c31bce32779206d8ba185a Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 23 Mar 2021 11:49:05 +0100 Subject: [PATCH 29/34] address required - Ability to set the address as a mandatory field - The address is new requested when creating an account - The profile completion page is less fuzzy for people landing on it without enabled SSO --- CHANGELOG.md | 3 +++ .../api/auth_providers_controller.rb | 1 + app/controllers/application_controller.rb | 3 ++- .../javascript/controllers/admin/members.js | 27 ++++++++++++------- .../src/javascript/controllers/members.js | 9 ++++--- .../src/javascript/controllers/profile.js | 8 ++++++ app/frontend/src/javascript/router.js | 10 +++---- .../templates/admin/settings/general.html | 2 +- app/frontend/templates/profile/complete.html | 16 +++++------ .../templates/shared/_member_form.html | 5 ++-- .../templates/shared/signupModal.html | 18 +++++++++++++ app/models/auth_provider.rb | 5 ++++ .../_auth_provider.json.jbuilder | 4 ++- .../api/auth_providers/active.json.jbuilder | 5 ++++ config/locales/app.admin.en.yml | 2 +- config/locales/app.admin.fr.yml | 3 ++- config/locales/app.public.en.yml | 2 ++ config/locales/app.public.fr.yml | 2 ++ lib/tasks/fablab/auth.rake | 2 +- 19 files changed, 94 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e5dab41e..ee6ab44c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Next release - Ability to disable the trainings module +- Ability to set the address as a mandatory field +- The address is new requested when creating an account +- The profile completion page is less fuzzy for people landing on it without enabled SSO - Prevent showing error message when testing for old versions during upgrade - In the email notification, sent to admins on account creation, show the group of the user - More explanations in the setup script diff --git a/app/controllers/api/auth_providers_controller.rb b/app/controllers/api/auth_providers_controller.rb index 1efd31c44..6a4b5bea0 100644 --- a/app/controllers/api/auth_providers_controller.rb +++ b/app/controllers/api/auth_providers_controller.rb @@ -49,6 +49,7 @@ class API::AuthProvidersController < API::ApiController def active authorize AuthProvider @provider = AuthProvider.active + @previous = AuthProvider.previous end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 181777e43..10b7c224b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -41,7 +41,8 @@ class ApplicationController < ActionController::Base { profile_attributes: %i[phone last_name first_name interest software_mastered], invoicing_profile_attributes: [ - organization_attributes: [:name, address_attributes: [:address]] + organization_attributes: [:name, address_attributes: [:address]], + address_attributes: [:address] ], statistic_profile_attributes: %i[gender birthday] }, diff --git a/app/frontend/src/javascript/controllers/admin/members.js b/app/frontend/src/javascript/controllers/admin/members.js index 505076714..ad5e718db 100644 --- a/app/frontend/src/javascript/controllers/admin/members.js +++ b/app/frontend/src/javascript/controllers/admin/members.js @@ -650,8 +650,8 @@ Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', /** * Controller used in the member edition page */ -Application.Controllers.controller('EditMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise', 'activeProviderPromise', 'Wallet', 'phoneRequiredPromise', - function ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise, activeProviderPromise, Wallet, phoneRequiredPromise) { +Application.Controllers.controller('EditMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise', 'activeProviderPromise', 'Wallet', 'settingsPromise', + function ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise, activeProviderPromise, Wallet, settingsPromise) { /* PUBLIC SCOPE */ // API URL where the form will be posted @@ -670,7 +670,10 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', $scope.password = { change: false }; // is the phone number required in _member_form? - $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); + $scope.phoneRequired = (settingsPromise.phone_required === 'true'); + + // is the address required in _member_form? + $scope.addressRequired = (settingsPromise.address_required === 'true'); // the user subscription if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) { @@ -990,8 +993,8 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', /** * Controller used in the member's creation page (admin view) */ -Application.Controllers.controller('NewMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'Group', 'CSRF', 'phoneRequiredPromise', - function ($scope, $state, $stateParams, Member, Training, Group, CSRF, phoneRequiredPromise) { +Application.Controllers.controller('NewMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'Group', 'CSRF', 'settingsPromise', + function ($scope, $state, $stateParams, Member, Training, Group, CSRF, settingsPromise) { CSRF.setMetaTags(); /* PUBLIC SCOPE */ @@ -1006,7 +1009,10 @@ Application.Controllers.controller('NewMemberController', ['$scope', '$state', ' $scope.password = { change: false }; // is the phone number required in _member_form? - $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); + $scope.phoneRequired = (settingsPromise.phone_required === 'true'); + + // is the address required to sign-up? + $scope.addressRequired = (settingsPromise.address_required === 'true'); // Default member's profile parameters $scope.user = { @@ -1109,8 +1115,8 @@ Application.Controllers.controller('ImportMembersResultController', ['$scope', ' /** * Controller used in the admin creation page (admin view) */ -Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', 'phoneRequiredPromise', - function ($state, $scope, Admin, growl, _t, phoneRequiredPromise) { +Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', 'settingsPromise', + function ($state, $scope, Admin, growl, _t, settingsPromise) { // default admin profile let getGender; $scope.admin = { @@ -1131,7 +1137,10 @@ Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'A }; // is the phone number required in _admin_form? - $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); + $scope.phoneRequired = (settingsPromise.phone_required === 'true'); + + // is the address required in _admin_form? + $scope.addressRequired = (settingsPromise.address_required === 'true'); /** * Shows the birthday datepicker diff --git a/app/frontend/src/javascript/controllers/members.js b/app/frontend/src/javascript/controllers/members.js index 2b23102ee..05c233fe0 100644 --- a/app/frontend/src/javascript/controllers/members.js +++ b/app/frontend/src/javascript/controllers/members.js @@ -72,8 +72,8 @@ Application.Controllers.controller('MembersController', ['$scope', 'Member', 'me /** * Controller used when editing the current user's profile (in dashboard) */ -Application.Controllers.controller('EditProfileController', ['$scope', '$rootScope', '$state', '$window', '$sce', '$cookies', '$injector', 'Member', 'Auth', 'Session', 'activeProviderPromise', 'phoneRequiredPromise', 'growl', 'dialogs', 'CSRF', 'memberPromise', 'groups', '_t', - function ($scope, $rootScope, $state, $window, $sce, $cookies, $injector, Member, Auth, Session, activeProviderPromise, phoneRequiredPromise, growl, dialogs, CSRF, memberPromise, groups, _t) { +Application.Controllers.controller('EditProfileController', ['$scope', '$rootScope', '$state', '$window', '$sce', '$cookies', '$injector', 'Member', 'Auth', 'Session', 'activeProviderPromise', 'settingsPromise', 'growl', 'dialogs', 'CSRF', 'memberPromise', 'groups', '_t', + function ($scope, $rootScope, $state, $window, $sce, $cookies, $injector, Member, Auth, Session, activeProviderPromise, settingsPromise, growl, dialogs, CSRF, memberPromise, groups, _t) { /* PUBLIC SCOPE */ // API URL where the form will be posted @@ -111,7 +111,10 @@ Application.Controllers.controller('EditProfileController', ['$scope', '$rootSco $scope.password = { change: false }; // is the phone number required in _member_form? - $scope.phoneRequired = (phoneRequiredPromise.setting.value === 'true'); + $scope.phoneRequired = (settingsPromise.phone_required === 'true'); + + // is the address required in _member_form? + $scope.addressRequired = (settingsPromise.address_required === 'true'); // Angular-Bootstrap datepicker configuration for birthday $scope.datePicker = { diff --git a/app/frontend/src/javascript/controllers/profile.js b/app/frontend/src/javascript/controllers/profile.js index cba08dba6..5056c8a1a 100644 --- a/app/frontend/src/javascript/controllers/profile.js +++ b/app/frontend/src/javascript/controllers/profile.js @@ -203,6 +203,14 @@ Application.Controllers.controller('CompleteProfileController', ['$scope', '$roo }); }; + /** + * Hide the new account messages. + * If hidden, the page will be used only to complete the current user's profile. + */ + $scope.hideNewAccountConfirmation = function () { + return !$scope.activeProvider.previous_provider || $scope.activeProvider.previous_provider.id === $scope.activeProvider.id; + }; + /* PRIVATE SCOPE */ /** diff --git a/app/frontend/src/javascript/router.js b/app/frontend/src/javascript/router.js index b1f5540ba..a0e85c3a1 100644 --- a/app/frontend/src/javascript/router.js +++ b/app/frontend/src/javascript/router.js @@ -167,7 +167,7 @@ angular.module('application.router', ['ui.router']) resolve: { groups: ['Group', function (Group) { return Group.query().$promise; }], activeProviderPromise: ['AuthProvider', function (AuthProvider) { return AuthProvider.active().$promise; }], - phoneRequiredPromise: ['Setting', function (Setting) { return Setting.get({ name: 'phone_required' }).$promise; }] + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'address_required']" }).$promise; }] } }) .state('app.logged.dashboard.projects', { @@ -915,7 +915,7 @@ angular.module('application.router', ['ui.router']) } }, resolve: { - phoneRequiredPromise: ['Setting', function (Setting) { return Setting.get({ name: 'phone_required' }).$promise; }] + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'address_required']" }).$promise; }] } }) .state('app.admin.members_import', { @@ -956,7 +956,7 @@ angular.module('application.router', ['ui.router']) walletPromise: ['Wallet', '$stateParams', function (Wallet, $stateParams) { return Wallet.getWalletByUser({ user_id: $stateParams.id }).$promise; }], transactionsPromise: ['Wallet', 'walletPromise', function (Wallet, walletPromise) { return Wallet.transactions({ id: walletPromise.id }).$promise; }], tagsPromise: ['Tag', function (Tag) { return Tag.query().$promise; }], - phoneRequiredPromise: ['Setting', function (Setting) { return Setting.get({ name: 'phone_required' }).$promise; }] + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'address_required']" }).$promise; }] } }) .state('app.admin.admins_new', { @@ -968,7 +968,7 @@ angular.module('application.router', ['ui.router']) } }, resolve: { - phoneRequiredPromise: ['Setting', function (Setting) { return Setting.get({ name: 'phone_required' }).$promise; }] + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'address_required']" }).$promise; }] } }) .state('app.admin.managers_new', { @@ -1061,7 +1061,7 @@ angular.module('application.router', ['ui.router']) "'booking_cancel_delay', 'main_color', 'secondary_color', 'spaces_module', 'twitter_analytics', " + "'fablab_name', 'name_genre', 'reminder_enable', 'plans_module', 'confirmation_required', " + "'reminder_delay', 'visibility_yearly', 'visibility_others', 'wallet_module', 'trainings_module', " + - "'display_name_enable', 'machines_sort_by', 'fab_analytics', 'statistics_module', " + + "'display_name_enable', 'machines_sort_by', 'fab_analytics', 'statistics_module', 'address_required', " + "'link_name', 'home_content', 'home_css', 'phone_required', 'upcoming_events_shown']" }).$promise; }], diff --git a/app/frontend/templates/admin/settings/general.html b/app/frontend/templates/admin/settings/general.html index 454672502..6948ffaae 100644 --- a/app/frontend/templates/admin/settings/general.html +++ b/app/frontend/templates/admin/settings/general.html @@ -427,7 +427,7 @@

{{ 'app.admin.settings.address' }}

- {{ 'app.admin.settings.address_required_info' }} + {{ 'app.admin.settings.address_required_info_html' }}

-
+
@@ -34,19 +34,19 @@
-
-

{{ 'app.logged.profile_completion.new_on_this_platform' }}

+

{{ 'app.logged.profile_completion.new_on_this_platform' }}

{{ 'app.logged.profile_completion.please_fill_the_following_form'}}.

{{ 'app.logged.profile_completion.some_data_may_have_already_been_provided_by_provider_and_cannot_be_modified' | translate:{NAME:activeProvider.name} }}.
{{ 'app.logged.profile_completion.then_click_on_' | translate }} {{ 'app.shared.buttons.confirm_changes' }} {{ 'app.logged.profile_completion._to_start_using_the_application' | translate }}.

@@ -145,14 +145,14 @@
-
diff --git a/app/frontend/templates/shared/_member_form.html b/app/frontend/templates/shared/_member_form.html index f277940d4..fbf336c10 100644 --- a/app/frontend/templates/shared/_member_form.html +++ b/app/frontend/templates/shared/_member_form.html @@ -224,7 +224,7 @@
- + @@ -234,7 +234,8 @@ class="form-control" id="user_address" ng-disabled="preventField['profile.address'] && user.invoicing_profile.address.address && !userForm['user[invoicing_profile_attributes][address_attributes][address]'].$dirty" - placeholder="{{ 'app.shared.user.address' | translate }}"/> + placeholder="{{ 'app.shared.user.address' | translate }}" + ng-required="addressRequired"/>
diff --git a/app/frontend/templates/shared/signupModal.html b/app/frontend/templates/shared/signupModal.html index b9f2639a8..a91c05f7d 100644 --- a/app/frontend/templates/shared/signupModal.html +++ b/app/frontend/templates/shared/signupModal.html @@ -213,6 +213,24 @@
+
+
+
+ + +
+ + + + {{ 'app.public.common.address_is_required' }} +
+
+
Please note that, depending on your country, the regulations may requires addresses for the invoices to be valid." address_is_required: "Address is required" captcha: "Captcha" captcha_info_html: "You can setup a protection against robots, to prevent them creating members accounts. This protection is using Google reCAPTCHA. Sign up for an API key pair to start using the captcha." diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index a98083288..d1110ac75 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -1073,6 +1073,7 @@ fr: machines_sort_by: "l'ordre d'affichage des machines" fab_analytics: "Fab Analytics" phone_required: "téléphone requis" + address_required: "adresse requise" tracking_id: "l'ID de suivi" facebook_app_id: "l'App ID Facebook" twitter_analytics: "compte Twitter analytics" @@ -1110,7 +1111,7 @@ fr: phone_is_required: "Téléphone requis" phone_required_info: "Vous pouvez définir si le numéro de téléphone doit être requis, lors de l'enregistrement d'un nouvel utilisateur sur Fab-manager." address: "Adresse" - address_required_info: "Vous pouvez définir si l'adresse doit être requise, lors de l'enregistrement d'un nouvel utilisateur sur Fab-manager. Veuillez noter que, selon votre pays, la réglementation peut exiger des adresses pour que les factures soient valides." + address_required_info_html: "Vous pouvez définir si l'adresse doit être requise, lors de l'enregistrement d'un nouvel utilisateur sur Fab-manager.
Veuillez noter que, selon votre pays, la réglementation peut exiger des adresses pour que les factures soient valides." address_is_required: "Adresse requise" captcha: "Captcha" captcha_info_html: "Vous pouvez mettre en place une protection contre les robots, pour les empêcher de créer des comptes membre. Cette protection utilise Google reCAPTCHA. Inscrivez vous pour obtenir une paire de clefs d'API afin d'utiliser le captcha." diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index ff55a8efd..f3c8b1244 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -84,6 +84,8 @@ en: birth_date_is_required: "Birth date is required." phone_number: "Phone number" phone_number_is_required: "Phone number is required." + address: "Address" + address_is_required: "Address is required" i_authorize_Fablab_users_registered_on_the_site_to_contact_me: "I authorize FabLab users, registered on the site, to contact me" i_accept_to_receive_information_from_the_fablab: "I accept to receive information from the FabLab" i_ve_read_and_i_accept_: "I've read and I accept" diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index ab65878a2..0a6e258d9 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -84,6 +84,8 @@ fr: birth_date_is_required: "La date de naissance est requise." phone_number: "Numéro de téléphone" phone_number_is_required: "Le numéro de téléphone est requis." + address: "Adresse" + address_is_required: "L'adresse est requise" i_authorize_Fablab_users_registered_on_the_site_to_contact_me: "J'autorise les utilisateurs du Fab Lab inscrits sur le site à me contacter" i_accept_to_receive_information_from_the_fablab: "J'accepte de recevoir des informations du Fab Lab" i_ve_read_and_i_accept_: "J'ai lu et j'accepte" diff --git a/lib/tasks/fablab/auth.rake b/lib/tasks/fablab/auth.rake index a555900ad..7d2c1d033 100644 --- a/lib/tasks/fablab/auth.rake +++ b/lib/tasks/fablab/auth.rake @@ -16,7 +16,7 @@ namespace :fablab do raise "FATAL ERROR: the provider '#{args.provider}' is already enabled" if AuthProvider.active.name == args.provider # disable previous provider - prev_prev = AuthProvider.find_by(status: 'previous') + prev_prev = AuthProvider.previous prev_prev&.update_attribute(:status, 'pending') AuthProvider.active.update_attribute(:status, 'previous') From 1749dea0e2ff86726d22a9657a277a75f4018104 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 23 Mar 2021 12:32:59 +0100 Subject: [PATCH 30/34] fix tests --- app/services/accounting_export_service.rb | 2 +- test/integration/availabilities/as_admin_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/accounting_export_service.rb b/app/services/accounting_export_service.rb index c86797b1a..81be782a7 100644 --- a/app/services/accounting_export_service.rb +++ b/app/services/accounting_export_service.rb @@ -33,7 +33,7 @@ class AccountingExportService invoices = Invoice.where('created_at >= ? AND created_at <= ?', start_date, end_date).order('created_at ASC') invoices = invoices.where('total > 0') unless export_zeros invoices.each do |i| - puts "processing invoice #{i.id}..." + puts "processing invoice #{i.id}..." unless Rails.env.test? content << generate_rows(i) end diff --git a/test/integration/availabilities/as_admin_test.rb b/test/integration/availabilities/as_admin_test.rb index 41df807fe..37b378768 100644 --- a/test/integration/availabilities/as_admin_test.rb +++ b/test/integration/availabilities/as_admin_test.rb @@ -124,8 +124,8 @@ module Availabilities 'expected end_at = start_at + 4 slots of 90 minutes' # Check the recurrence - assert_equal (availability[:start_at].to_date + 2.weeks), - availability[:end_date].to_date, + assert_equal (availability[:start_at].to_datetime + 2.weeks).to_date, + availability[:end_date].to_datetime.utc.to_date, 'expected end_date = start_at + 2 weeks' end end From 7001888437ae1c708874a2d34d69323b15053b33 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 24 Mar 2021 09:47:22 +0100 Subject: [PATCH 31/34] [bug] unable to run the setup script if sudoers belong to another group than sudo --- CHANGELOG.md | 1 + setup/setup.sh | 64 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee6ab44c1..2a8122c25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Fix a bug: recurring availabilities are not restricted to subscribers - Fix a bug: accounting exports may ignore some invoices for the first and last days - Fix a bug: accounting export caching is not working +- Fix a bug: unable to run the setup script if sudoers belong to another group than sudo - Fix a security issue: updated elliptic to 6.5.4 to fix [CVE-2020-28498](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-28498) - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/nginx-packs-directive.sh | bash` - [TODO DEPLOY] `rails db:seed` diff --git a/setup/setup.sh b/setup/setup.sh index 17aad2a7c..d8c12fdc9 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -26,12 +26,16 @@ welcome_message() system_requirements() { - if [ "$(whoami)" = "root" ]; then + if is_root; then echo "It is not recommended to run this script as root. As a normal user, elevation will be prompted if needed." read -rp "Continue anyway? (Y/n) " confirm &1) + if [ $? -eq 0 ]; then + echo "has_sudo__pass_set" + elif echo $prompt | grep -q '^sudo:'; then + echo "has_sudo__needs_pass" + else + echo "no_sudo" + fi +} + +elevate_cmd() +{ + local cmd=$@ + + HAS_SUDO=$(has_sudo) + + case "$HAS_SUDO" in + has_sudo__pass_set) + sudo $cmd + ;; + has_sudo__needs_pass) + echo "Please supply sudo password for the following command: sudo $cmd" + sudo $cmd + ;; + *) + echo "Please supply root password for the following command: su -c \"$cmd\"" + su -c "$cmd" + ;; + esac +} + read_email() { local email @@ -113,8 +157,8 @@ prepare_files() read -rp "Continue? (Y/n) " confirm Date: Wed, 24 Mar 2021 09:50:22 +0100 Subject: [PATCH 32/34] improved setup script style --- setup/setup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/setup.sh b/setup/setup.sh index d8c12fdc9..093e062af 100755 --- a/setup/setup.sh +++ b/setup/setup.sh @@ -209,7 +209,7 @@ prepare_nginx() echo "Removing nginx..." yq -i eval 'del(.services.nginx)' docker-compose.yml printf "The two following configurations are useful if you want to install Fab-manager behind a reverse proxy...\n" - read -rp "Do you want to map the Fab-manager's service to an external network? (Y/n) " confirm Date: Wed, 24 Mar 2021 11:04:59 +0100 Subject: [PATCH 33/34] improved upgrade script output --- setup/upgrade.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup/upgrade.sh b/setup/upgrade.sh index ea8e8914f..47f21f4dc 100644 --- a/setup/upgrade.sh +++ b/setup/upgrade.sh @@ -87,7 +87,7 @@ add_environments() { for ENV in "${ENVIRONMENTS[@]}"; do if [[ "$ENV" =~ ^[A-Z0-9_]+=.*$ ]]; then - printf "Inserting variable %s...\n" "$ENV" + printf "\e[91m::\e[0m \e[1mInserting variable %s..\e[0m.\n" "$ENV" printf "# added on %s\n%s\n" "$(date +%Y-%m-%d\ %R)" "$ENV" >> "config/env" else echo "Ignoring invalid option: -e $ENV. Given value is not valid environment variable, please see https://huit.re/environment-doc" @@ -133,7 +133,7 @@ upgrade() BRANCH='master' if yq eval '.services.*.image | select(. == "sleede/fab-manager*")' docker-compose.yml | grep -q ':dev'; then BRANCH='dev'; fi for SCRIPT in "${SCRIPTS[@]}"; do - printf "Running script %s from branch %s...\n" "$SCRIPT" "$BRANCH" + printf "\e[91m::\e[0m \e[1mRunning script %s from branch %s...\e[0m\n" "$SCRIPT" "$BRANCH" if [[ "$YES_ALL" = "true" ]]; then \curl -sSL "https://raw.githubusercontent.com/sleede/fab-manager/$BRANCH/scripts/$SCRIPT.sh" | bash -s -- -y else @@ -143,7 +143,7 @@ upgrade() compile_assets docker-compose run --rm "$SERVICE" bundle exec rake db:migrate for COMMAND in "${COMMANDS[@]}"; do - printf "Running command %s...\n" "$COMMAND" + printf "\e[91m::\e[0m \e[1mRunning command %s...\e[0m\n" "$COMMAND" docker-compose run --rm "$SERVICE" bundle exec "$COMMAND" done docker-compose up -d @@ -152,7 +152,7 @@ upgrade() clean() { - echo "Current disk usage:" + echo -e "\e[91m::\e[0m \e[1mCurrent disk usage:\e[0m" df -h / [[ "$YES_ALL" = "true" ]] && confirm="y" || read -rp "Clean previous docker images? (y/N) " confirm Date: Wed, 24 Mar 2021 11:19:10 +0100 Subject: [PATCH 34/34] Version 4.7.6 --- CHANGELOG.md | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a8122c25..9d79ab451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog Fab-manager -## Next release +## v4.7.6 2021 March 24 - Ability to disable the trainings module - Ability to set the address as a mandatory field - The address is new requested when creating an account @@ -11,6 +11,7 @@ - Send pre-compressed assets to the browsers instead of the regular ones - Links created using "medium editor" opens in new tabs - Improved style of public plans page +- Improved the upgrade script - Fix a bug: subscriptions tab is selected by default in statistics, even if the module is disabled - Fix a bug: select all plans for slot restriction (through the dedicated button) also selects the disabled plans - Fix a bug: recurring availabilities are not restricted to subscribers diff --git a/package.json b/package.json index a776ce1b8..81980e6eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fab-manager", - "version": "4.7.5", + "version": "4.7.6", "description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.", "keywords": [ "fablab",