From 9d747db095195307b1492d4b22d9641b57002d15 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Wed, 10 Mar 2021 13:25:53 +0100 Subject: [PATCH] Ability to disable the trainings module --- CHANGELOG.md | 2 + .../javascript/controllers/admin/graphs.js | 3 +- .../javascript/controllers/admin/pricing.js | 56 ++++++++++--------- .../controllers/admin/statistics.js | 7 ++- .../src/javascript/controllers/main_nav.js | 30 ++++++---- app/frontend/src/javascript/router.js | 11 +++- .../templates/admin/calendar/eventModal.html | 2 +- .../templates/admin/members/edit.html | 2 +- .../templates/admin/pricing/index.html | 2 +- .../templates/admin/settings/general.html | 8 +++ app/models/setting.rb | 3 +- app/policies/setting_policy.rb | 2 +- app/views/application/index.html.erb | 1 + config/locales/app.admin.en.yml | 4 ++ config/locales/app.admin.fr.yml | 4 ++ db/seeds.rb | 2 + 16 files changed, 89 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdceb6411..199f7af95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,14 @@ # Changelog Fab-manager ## Next release +- Ability to disable the trainings module - 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 - Send pre-compressed assets to the browsers instead of the regular ones - Fix a bug: subscriptions tab is selected by default in statistics, even if the module is disabled - [TODO DEPLOY] `\curl -sSL https://raw.githubusercontent.com/sleede/fab-manager/master/scripts/nginx-packs-directive.sh | bash` +- [TODO DEPLOY] `rails db:seed` ## 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 d92f76366..c7320006b 100644 --- a/app/frontend/src/javascript/controllers/admin/graphs.js +++ b/app/frontend/src/javascript/controllers/admin/graphs.js @@ -124,7 +124,8 @@ Application.Controllers.controller('GraphsController', ['$scope', '$state', '$ro */ $scope.hiddenTab = function (tab) { if (tab.graph) { - return !((tab.es_type_key === 'subscription') && !$rootScope.modules.plans); + return !((tab.es_type_key === 'subscription' && !$rootScope.modules.plans) || + (tab.es_type_key === 'training' && !$rootScope.modules.trainings)); } return false; }; diff --git a/app/frontend/src/javascript/controllers/admin/pricing.js b/app/frontend/src/javascript/controllers/admin/pricing.js index f3c08ab5b..da31903eb 100644 --- a/app/frontend/src/javascript/controllers/admin/pricing.js +++ b/app/frontend/src/javascript/controllers/admin/pricing.js @@ -111,7 +111,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @returns {float} */ $scope.findTrainingsPricing = function (trainingsPricings, trainingId, groupId) { - for (let trainingsPricing of Array.from(trainingsPricings)) { + for (const trainingsPricing of Array.from(trainingsPricings)) { if ((trainingsPricing.training_id === trainingId) && (trainingsPricing.group_id === groupId)) { return trainingsPricing; } @@ -138,7 +138,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @returns {Object} Plan, inherits from $resource */ $scope.getPlanFromId = function (id) { - for (let plan of Array.from($scope.plans)) { + for (const plan of Array.from($scope.plans)) { if (plan.id === parseInt(id)) { return plan; } @@ -151,7 +151,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @returns {Object} Group, inherits from $resource */ $scope.getGroupFromId = function (groups, id) { - for (let group of Array.from(groups)) { + for (const group of Array.from(groups)) { if (group.id === parseInt(id)) { return group; } @@ -313,7 +313,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @param [id] {number} credit id for edition, create a new credit object if not provided */ $scope.saveMachineCredit = function (data, id) { - for (let mc of Array.from($scope.machineCredits)) { + for (const mc of Array.from($scope.machineCredits)) { if ((mc.plan_id === data.plan_id) && (mc.creditable_id === data.creditable_id) && ((id === null) || (mc.id !== id))) { growl.error(_t('app.admin.pricing.error_a_credit_linking_this_machine_with_that_subscription_already_exists')); if (!id) { @@ -383,7 +383,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @param [id] {number} credit id for edition, create a new credit object if not provided */ $scope.saveSpaceCredit = function (data, id) { - for (let sc of Array.from($scope.spaceCredits)) { + for (const sc of Array.from($scope.spaceCredits)) { if ((sc.plan_id === data.plan_id) && (sc.creditable_id === data.creditable_id) && ((id === null) || (sc.id !== id))) { growl.error(_t('app.admin.pricing.error_a_credit_linking_this_space_with_that_subscription_already_exists')); if (!id) { @@ -459,7 +459,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * Retrieve a price from prices array by a machineId and a groupId */ $scope.findPriceBy = function (prices, machineId, groupId) { - for (let price of Array.from(prices)) { + for (const price of Array.from(prices)) { if ((price.priceable_id === machineId) && (price.group_id === groupId)) { return price; } @@ -603,7 +603,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', /** * Load the next 10 coupons */ - $scope.loadMore = function() { + $scope.loadMore = function () { $scope.couponsPage++; Coupon.query({ page: $scope.couponsPage, filter: $scope.filter.coupon }, function (data) { $scope.coupons = $scope.coupons.concat(data); @@ -613,19 +613,19 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', /** * Reset the list of coupons according to the newly selected filter */ - $scope.updateCouponFilter = function() { + $scope.updateCouponFilter = function () { $scope.couponsPage = 1; Coupon.query({ page: $scope.couponsPage, filter: $scope.filter.coupon }, function (data) { $scope.coupons = data; }); - } + }; /** * Return the exemple price based on the configuration of the default slot duration. * @param type {string} 'hourly_rate' | * * @returns {number} price for "SLOT_DURATION" minutes. */ - $scope.examplePrice = function(type) { + $scope.examplePrice = function (type) { const hourlyRate = 10; if (type === 'hourly_rate') { @@ -634,7 +634,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', const price = (hourlyRate / 60) * $scope.slotDuration; return $filter('currency')(price); - } + }; /** * Setup the feature-tour for the admin/pricing page. @@ -660,14 +660,16 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', content: _t('app.admin.tour.pricing.new_plan.content'), placement: 'bottom' }); - uitour.createStep({ - selector: '.plans-pricing .trainings-tab', - stepId: 'trainings', - order: 2, - title: _t('app.admin.tour.pricing.trainings.title'), - content: _t('app.admin.tour.pricing.trainings.content'), - placement: 'bottom' - }); + if ($scope.$root.modules.trainings) { + uitour.createStep({ + selector: '.plans-pricing .trainings-tab', + stepId: 'trainings', + order: 2, + title: _t('app.admin.tour.pricing.trainings.title'), + content: _t('app.admin.tour.pricing.trainings.content'), + placement: 'bottom' + }); + } uitour.createStep({ selector: '.plans-pricing .machines-tab', stepId: 'machines', @@ -733,7 +735,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', if (settingsPromise.feature_tour_display !== 'manual' && $scope.currentUser.profile.tours.indexOf('pricing') < 0) { uitour.start(); } - } + }; /* PRIVATE SCOPE */ @@ -746,7 +748,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', // adds empty array for plan which hasn't any credits yet return (function () { const result = []; - for (let plan of Array.from($scope.plans)) { + for (const plan of Array.from($scope.plans)) { if ($scope.trainingCreditsGroups[plan.id] == null) { result.push($scope.trainingCreditsGroups[plan.id] = []); } else { @@ -763,7 +765,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @param id {number} * @returns {number} item index in the provided array */ - var findItemIdxById = function (items, id) { + const findItemIdxById = function (items, id) { return (items.map(function (item) { return item.id; })).indexOf(id); }; @@ -771,7 +773,7 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * Group the given credits array into a map associating the plan ID with its associated trainings/machines * @return {Object} the association map */ - var groupCreditsByPlan = function (credits) { + const groupCreditsByPlan = function (credits) { const creditsMap = {}; angular.forEach(credits, function (c) { if (!creditsMap[c.plan_id]) { @@ -787,11 +789,11 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @param trainingId {number|string} training ID * @param planId {number|string} plan ID */ - var findTrainingCredit = function (trainingId, planId) { + const findTrainingCredit = function (trainingId, planId) { trainingId = parseInt(trainingId); planId = parseInt(planId); - for (let credit of Array.from($scope.trainingCredits)) { + for (const credit of Array.from($scope.trainingCredits)) { if ((credit.plan_id === planId) && (credit.creditable_id === trainingId)) { return credit; } @@ -803,8 +805,8 @@ Application.Controllers.controller('EditPricingController', ['$scope', '$state', * @param id {number} training ID * @returns {Object} Training inherited from $resource */ - var getTrainingFromId = function (id) { - for (let training of Array.from($scope.trainings)) { + const getTrainingFromId = function (id) { + for (const training of Array.from($scope.trainings)) { if (training.id === parseInt(id)) { return training; } diff --git a/app/frontend/src/javascript/controllers/admin/statistics.js b/app/frontend/src/javascript/controllers/admin/statistics.js index dd99e2d1f..a08a5c2cb 100644 --- a/app/frontend/src/javascript/controllers/admin/statistics.js +++ b/app/frontend/src/javascript/controllers/admin/statistics.js @@ -184,9 +184,10 @@ Application.Controllers.controller('StatisticsController', ['$scope', '$state', */ $scope.hiddenTab = function (tab) { if (tab.table) { - if ((tab.es_type_key === 'subscription') && !$rootScope.modules.plans) { - return true; - } else return (tab.es_type_key === 'space') && !$rootScope.modules.spaces; + return ((tab.es_type_key === 'subscription' && !$rootScope.modules.plans) || + (tab.es_type_key === 'training' && !$rootScope.modules.trainings) || + (tab.es_type_key === 'space' && !$rootScope.modules.spaces) + ); } else { return true; } diff --git a/app/frontend/src/javascript/controllers/main_nav.js b/app/frontend/src/javascript/controllers/main_nav.js index dfce96ad5..3479d4223 100644 --- a/app/frontend/src/javascript/controllers/main_nav.js +++ b/app/frontend/src/javascript/controllers/main_nav.js @@ -35,12 +35,6 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc linkIcon: 'cogs', class: 'reserve-machine-link' }, - { - state: 'app.public.trainings_list', - linkText: 'app.public.common.trainings_registrations', - linkIcon: 'graduation-cap', - class: 'reserve-training-link' - }, { state: 'app.public.events_list', linkText: 'app.public.common.events_registrations', @@ -67,6 +61,15 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc }); } + if ($scope.$root.modules.trainings) { + $scope.navLinks.splice(4, 0, { + state: 'app.public.trainings_list', + linkText: 'app.public.common.trainings_registrations', + linkIcon: 'graduation-cap', + class: 'reserve-training-link' + }); + } + if ($scope.$root.modules.spaces) { $scope.navLinks.splice(4, 0, { state: 'app.public.spaces_list', @@ -90,12 +93,6 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc linkIcon: 'cogs', authorizedRoles: ['admin', 'manager'] }, - { - state: 'app.admin.trainings', - linkText: 'app.public.common.trainings_monitoring', - linkIcon: 'graduation-cap', - authorizedRoles: ['admin', 'manager'] - }, { state: 'app.admin.events', linkText: 'app.public.common.manage_the_events', @@ -147,6 +144,15 @@ Application.Controllers.controller('MainNavController', ['$scope', function ($sc $scope.adminNavLinks = adminNavLinks; + if ($scope.$root.modules.trainings) { + $scope.adminNavLinks.splice(3, 0, { + state: 'app.admin.trainings', + linkText: 'app.public.common.trainings_monitoring', + linkIcon: 'graduation-cap', + authorizedRoles: ['admin', 'manager'] + }); + } + if ($scope.$root.modules.spaces) { $scope.adminNavLinks.splice(3, 0, { state: 'app.public.spaces_list', diff --git a/app/frontend/src/javascript/router.js b/app/frontend/src/javascript/router.js index c6dfb5702..5b2b67dbd 100644 --- a/app/frontend/src/javascript/router.js +++ b/app/frontend/src/javascript/router.js @@ -37,7 +37,7 @@ angular.module('application.router', ['ui.router']) logoFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-file' }).$promise; }], logoBlackFile: ['CustomAsset', function (CustomAsset) { return CustomAsset.get({ name: 'logo-black-file' }).$promise; }], sharedTranslations: ['Translations', function (Translations) { return Translations.query(['app.shared', 'app.public.common']).$promise; }], - modulesPromise: ['Setting', function (Setting) { return Setting.query({ names: "['spaces_module', 'plans_module', 'invoicing_module', 'wallet_module', 'statistics_module']" }).$promise; }] + modulesPromise: ['Setting', function (Setting) { return Setting.query({ names: "['spaces_module', 'plans_module', 'invoicing_module', 'wallet_module', 'statistics_module', 'trainings_module']" }).$promise; }] }, onEnter: ['$rootScope', 'logoFile', 'logoBlackFile', 'modulesPromise', 'CSRF', function ($rootScope, logoFile, logoBlackFile, modulesPromise, CSRF) { // Retrieve Anti-CSRF tokens from cookies @@ -48,6 +48,7 @@ angular.module('application.router', ['ui.router']) $rootScope.modules = { spaces: (modulesPromise.spaces_module === 'true'), plans: (modulesPromise.plans_module === 'true'), + trainings: (modulesPromise.trainings_module === 'true'), invoicing: (modulesPromise.invoicing_module === 'true'), wallet: (modulesPromise.wallet_module === 'true'), statistics: (modulesPromise.statistics_module === 'true') @@ -458,6 +459,7 @@ angular.module('application.router', ['ui.router']) // trainings .state('app.public.trainings_list', { url: '/trainings', + abstract: !Fablab.trainingsModule, views: { 'main@': { templateUrl: '/trainings/index.html', @@ -470,6 +472,7 @@ angular.module('application.router', ['ui.router']) }) .state('app.public.training_show', { url: '/trainings/:id', + abstract: !Fablab.trainingsModule, views: { 'main@': { templateUrl: '/trainings/show.html', @@ -482,6 +485,7 @@ angular.module('application.router', ['ui.router']) }) .state('app.logged.trainings_reserve', { url: '/trainings/:id/reserve', + abstract: !Fablab.trainingsModule, views: { 'main@': { templateUrl: '/trainings/reserve.html', @@ -652,6 +656,7 @@ angular.module('application.router', ['ui.router']) // trainings .state('app.admin.trainings', { url: '/admin/trainings', + abstract: !Fablab.trainingsModule, views: { 'main@': { templateUrl: '/admin/trainings/index.html', @@ -666,6 +671,7 @@ angular.module('application.router', ['ui.router']) }) .state('app.admin.trainings_new', { url: '/admin/trainings/new', + abstract: !Fablab.trainingsModule, views: { 'main@': { templateUrl: '/admin/trainings/new.html', @@ -678,6 +684,7 @@ angular.module('application.router', ['ui.router']) }) .state('app.admin.trainings_edit', { url: '/admin/trainings/:id/edit', + abstract: !Fablab.trainingsModule, views: { 'main@': { templateUrl: '/admin/trainings/edit.html', @@ -1054,7 +1061,7 @@ angular.module('application.router', ['ui.router']) "'booking_move_enable', 'booking_move_delay', 'booking_cancel_enable', 'feature_tour_display', " + "'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', " + + "'reminder_delay', 'visibility_yearly', 'visibility_others', 'wallet_module', 'trainings_module', " + "'display_name_enable', 'machines_sort_by', 'fab_analytics', 'statistics_module', " + "'link_name', 'home_content', 'home_css', 'phone_required', 'upcoming_events_shown']" }).$promise; diff --git a/app/frontend/templates/admin/calendar/eventModal.html b/app/frontend/templates/admin/calendar/eventModal.html index a5bfc133b..8ed8f6184 100644 --- a/app/frontend/templates/admin/calendar/eventModal.html +++ b/app/frontend/templates/admin/calendar/eventModal.html @@ -6,7 +6,7 @@