diff --git a/Gemfile.lock b/Gemfile.lock index 4606b128b..3f76d9e08 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -269,6 +269,8 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.8) + nokogiri (1.14.3-x86_64-darwin) + racc (~> 1.4) nokogiri (1.14.3-x86_64-linux) racc (~> 1.4) oauth2 (1.4.4) @@ -524,6 +526,7 @@ GEM zeitwerk (2.6.7) PLATFORMS + x86_64-darwin-21 x86_64-linux DEPENDENCIES diff --git a/app/frontend/src/javascript/controllers/projects.js b/app/frontend/src/javascript/controllers/projects.js index 6aa7d8112..f09615e07 100644 --- a/app/frontend/src/javascript/controllers/projects.js +++ b/app/frontend/src/javascript/controllers/projects.js @@ -281,8 +281,8 @@ class ProjectsController { /** * Controller used on projects listing page */ -Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'Project', 'machinesPromise', 'themesPromise', 'componentsPromise', 'paginationService', 'OpenlabProject', '$window', 'growl', '_t', '$location', '$timeout', 'settingsPromise', 'openLabActive', - function ($scope, $state, Project, machinesPromise, themesPromise, componentsPromise, paginationService, OpenlabProject, $window, growl, _t, $location, $timeout, settingsPromise, openLabActive) { +Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'Project', 'machinesPromise', 'themesPromise', 'componentsPromise', 'paginationService', 'OpenlabProject', '$window', 'growl', '_t', '$location', '$timeout', 'settingsPromise', 'openLabActive', 'Member', 'Diacritics', + function ($scope, $state, Project, machinesPromise, themesPromise, componentsPromise, paginationService, OpenlabProject, $window, growl, _t, $location, $timeout, settingsPromise, openLabActive, Member, Diacritics) { /* PRIVATE STATIC CONSTANTS */ // Number of projects added to the page when the user clicks on 'load more projects' @@ -294,12 +294,18 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P // Fab-manager's instance ID in the openLab network $scope.openlabAppId = settingsPromise.openlab_app_id; + $scope.memberFilterPresence = settingsPromise.projects_list_member_filter_presence !== 'false'; + // Is openLab enabled on the instance? $scope.openlab = { projectsActive: openLabActive.isPresent, searchOverWholeNetwork: settingsPromise.openlab_default === 'true' }; + if (!$scope.memberFilterPresence) { + $location.$$search.member_id = ''; + } + // default search parameters $scope.search = { q: ($location.$$search.q || ''), @@ -307,7 +313,24 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P machine_id: (parseInt($location.$$search.machine_id) || undefined), component_id: (parseInt($location.$$search.component_id) || undefined), theme_id: (parseInt($location.$$search.theme_id) || undefined), - status_id: (parseInt($location.$$search.status_id) || undefined) + status_id: (parseInt($location.$$search.status_id) || undefined), + member_id: (parseInt($location.$$search.member_id) || undefined) + }; + + $scope.autoCompleteMemberName = function (nameLookup) { + if (!nameLookup) { + return; + } + $scope.isLoadingMembers = true; + const asciiName = Diacritics.remove(nameLookup); + + const q = { query: asciiName }; + + Member.search(q, function (users) { + $scope.matchingMembers = users; + $scope.isLoadingMembers = false; + } + , function (error) { console.error(error); }); }; // list of projects to display @@ -361,6 +384,7 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P $scope.search.component_id = undefined; $scope.search.theme_id = undefined; $scope.search.status_id = undefined; + $scope.search.member_id = undefined; $scope.$apply(); $scope.setUrlQueryParams($scope.search); $scope.triggerSearch(); @@ -420,6 +444,16 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P updateUrlParam('component_id', search.component_id); updateUrlParam('machine_id', search.machine_id); updateUrlParam('status_id', search.status_id); + updateUrlParam('member_id', search.member_id); + return true; + }; + + $scope.setSearchMemberId = function (searchMember) { + if (searchMember) { + $scope.search.member_id = searchMember.id; + } else { + $scope.search.member_id = undefined; + } return true; }; @@ -450,6 +484,11 @@ Application.Controllers.controller('ProjectsController', ['$scope', '$state', 'P } else { $scope.openlab.searchOverWholeNetwork = $scope.openlab.projectsActive; } + if ($location.$$search.member_id && $scope.memberFilterPresence) { + Member.get({ id: $location.$$search.member_id }, function (member) { + $scope.searchMember = member; + }); + } return $scope.triggerSearch(); }; diff --git a/app/frontend/src/javascript/models/setting.ts b/app/frontend/src/javascript/models/setting.ts index 27f84db14..37ffd2fdb 100644 --- a/app/frontend/src/javascript/models/setting.ts +++ b/app/frontend/src/javascript/models/setting.ts @@ -198,7 +198,8 @@ export const fabHubSettings = [ export const projectsSettings = [ 'allowed_cad_extensions', 'allowed_cad_mime_types', - 'disqus_shortname' + 'disqus_shortname', + 'projects_list_member_filter_presence' ] as const; export const prepaidPacksSettings = [ diff --git a/app/frontend/src/javascript/router.js b/app/frontend/src/javascript/router.js index 26ce00a93..a400a3323 100644 --- a/app/frontend/src/javascript/router.js +++ b/app/frontend/src/javascript/router.js @@ -301,7 +301,7 @@ angular.module('application.router', ['ui.router']) themesPromise: ['Theme', function (Theme) { return Theme.query().$promise; }], componentsPromise: ['Component', function (Component) { return Component.query().$promise; }], machinesPromise: ['Machine', function (Machine) { return Machine.query().$promise; }], - settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['openlab_app_id', 'openlab_default']" }).$promise; }], + settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['openlab_app_id', 'openlab_default', 'projects_list_member_filter_presence']" }).$promise; }], openLabActive: ['Setting', function (Setting) { return Setting.isPresent({ name: 'openlab_app_secret' }).$promise; }] } }) diff --git a/app/frontend/templates/admin/projects/settings.html b/app/frontend/templates/admin/projects/settings.html index 73bd51897..e7757928f 100644 --- a/app/frontend/templates/admin/projects/settings.html +++ b/app/frontend/templates/admin/projects/settings.html @@ -95,3 +95,18 @@ + +
+
+ {{ 'app.admin.projects.settings.filters' }} +
+
+
+ +
+
+
diff --git a/app/frontend/templates/projects/index.html b/app/frontend/templates/projects/index.html index 22d8f8143..1ee31bddf 100644 --- a/app/frontend/templates/projects/index.html +++ b/app/frontend/templates/projects/index.html @@ -65,6 +65,15 @@ + + + + + + + + + diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 1520bd44d..5dc8cea10 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -196,6 +196,7 @@ module SettingsHelper events_banner_cta_active events_banner_cta_label events_banner_cta_url + projects_list_member_filter_presence ].freeze end # rubocop:enable Metrics/ModuleLength diff --git a/app/policies/setting_policy.rb b/app/policies/setting_policy.rb index 1593beb55..1e11dfe90 100644 --- a/app/policies/setting_policy.rb +++ b/app/policies/setting_policy.rb @@ -46,7 +46,7 @@ class SettingPolicy < ApplicationPolicy external_id machines_banner_active machines_banner_text machines_banner_cta_active machines_banner_cta_label machines_banner_cta_url trainings_banner_active trainings_banner_text trainings_banner_cta_active trainings_banner_cta_label trainings_banner_cta_url events_banner_active events_banner_text events_banner_cta_active events_banner_cta_label - events_banner_cta_url] + events_banner_cta_url projects_list_member_filter_presence] end ## diff --git a/app/services/project_service.rb b/app/services/project_service.rb index 823d9365d..3e01d5c16 100644 --- a/app/services/project_service.rb +++ b/app/services/project_service.rb @@ -23,6 +23,14 @@ class ProjectService records = records.with_theme(query_params['theme_id']) if query_params['theme_id'].present? records = records.with_space(query_params['space_id']) if query_params['space_id'].present? records = records.with_status(query_params['status_id']) if query_params['status_id'].present? + + if query_params['member_id'].present? + member = User.find(query_params['member_id']) + if member + records = records.where(id: Project.user_projects(member.statistic_profile.id)).or(Project.where(id: Project.collaborations(member.id))) + end + end + records = if query_params['q'].present? records.search(query_params['q']) else diff --git a/app/views/application/index.html.erb b/app/views/application/index.html.erb index 159ec0737..87396cdcf 100644 --- a/app/views/application/index.html.erb +++ b/app/views/application/index.html.erb @@ -61,6 +61,9 @@ buttons: <%= I18n.t('app.shared.buttons').to_json.html_safe %>, messages: <%= I18n.t('app.shared.messages').to_json.html_safe %> } + }, + date: { + month_names: <%= I18n.t('date.month_names').to_json.html_safe %> } }; Fablab.weekStartingDay = <%= Date.parse(Rails.application.secrets.week_starting_day).strftime('%w') %>; diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index 8714ac56d..8d211c2b0 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -445,6 +445,7 @@ en: open_lab_app_secret: "Secret" openlab_default_info_html: "In the projects gallery, visitors can switch between two views: all shared projects from the whole OpenLab network, or only the projects documented in your Fab Lab.
Here, you can choose which view is shown by default." default_to_openlab: "Display OpenLab by default" + filters: Projects list filters projects_setting: add: "Add" actions_controls: "Actions" @@ -1773,6 +1774,7 @@ en: extended_prices_in_same_day: "Extended prices in the same day" public_registrations: "Public registrations" show_username_in_admin_list: "Show the username in the list" + projects_list_member_filter_presence: "Presence of member filter on projects list" overlapping_options: training_reservations: "Trainings" machine_reservations: "Machines" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index e7082ef58..003ab199c 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -445,6 +445,7 @@ fr: open_lab_app_secret: "Secret" openlab_default_info_html: "Dans la galerie de projets, les visiteurs peuvent choisir entre deux vues : tous les projets de l'ensemble du réseau OpenLab, ou uniquement les projets documentés dans votre Fab Lab.
Ici, vous pouvez choisir quelle vue est affichée par défaut." default_to_openlab: "Afficher OpenLab par défaut" + filters: Filtres de la vue liste projects_setting: add: "Ajouter" actions_controls: "Actions" @@ -1773,6 +1774,7 @@ fr: extended_prices_in_same_day: "Prix étendus le même jour" public_registrations: "Inscriptions publiques" show_username_in_admin_list: "Afficher le nom d'utilisateur dans la liste" + projects_list_member_filter_presence: "Présence du filtre par membre dans la vue liste des projets" overlapping_options: training_reservations: "Formations" machine_reservations: "Machines" diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index 7dfc61f69..389775687 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -183,6 +183,7 @@ en: all_materials: "All materials" load_next_projects: "Load next projects" rough_draft: "Rough draft" + filter_by_member: "Filter by member" status_filter: all_statuses: "All statuses" select_status: "Select a status" diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index 09a07a116..82a6b7f15 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -183,6 +183,7 @@ fr: all_materials: "Tous les matériaux" load_next_projects: "Charger les projets suivants" rough_draft: "Brouillon" + filter_by_member: "Filter par membre" status_filter: all_statuses: "Tous les statuts" select_status: "Sélectionnez un statut" diff --git a/config/locales/en.yml b/config/locales/en.yml index 3bd7aae37..7c3443d36 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -697,6 +697,7 @@ en: trainings_authorization_validity_duration: "Trainings validity period duration" trainings_invalidation_rule: "Trainings automatic invalidation" trainings_invalidation_rule_period: "Grace period before invalidating a training" + projects_list_member_filter_presence: "Presence of member filter on projects list" #statuses of projects statuses: new: "New" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 5331975c3..af049d102 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -697,6 +697,7 @@ fr: trainings_authorization_validity_duration: "Durée de la période de validité des formations" trainings_invalidation_rule: "Invalidation automatique des formations" trainings_invalidation_rule_period: "Période de grâce avant d'invalider une formation" + projects_list_member_filter_presence: "Présence du filtre par membre dans la vue liste des projets" #statuses of projects statuses: new: "Nouveau" diff --git a/db/seeds/settings.rb b/db/seeds/settings.rb index 4df47c23c..28beafcda 100644 --- a/db/seeds/settings.rb +++ b/db/seeds/settings.rb @@ -728,3 +728,5 @@ Setting.set('accounting_Error_code', 'ERROR') unless Setting.find_by(name: 'acco Setting.set('accounting_Error_label', 'Erroneous invoices to refund') unless Setting.find_by(name: 'accounting_Error_label').try(:value) Setting.set('external_id', false) unless Setting.find_by(name: 'external_id').try(:value) + +Setting.set('projects_list_member_filter_presence', false) unless Setting.find_by(name: 'projects_list_member_filter_presence') diff --git a/test/fixtures/history_values.yml b/test/fixtures/history_values.yml index 4ce437af6..0ba0f4645 100644 --- a/test/fixtures/history_values.yml +++ b/test/fixtures/history_values.yml @@ -852,3 +852,10 @@ history_value_100: updated_at: 2023-04-05 09:16:08.000511500 Z invoicing_profile_id: 1 +history_value_103: + id: 103 + setting_id: 102 + value: 'false' + created_at: 2023-04-05 09:16:08.000511500 Z + updated_at: 2023-04-05 09:16:08.000511500 Z + invoicing_profile_id: 1 \ No newline at end of file diff --git a/test/fixtures/settings.yml b/test/fixtures/settings.yml index eb21fa7f8..7b26448ac 100644 --- a/test/fixtures/settings.yml +++ b/test/fixtures/settings.yml @@ -586,3 +586,9 @@ setting_99: name: home_css created_at: 2023-04-05 09:16:08.000511500 Z updated_at: 2023-04-05 09:16:08.000511500 Z + +setting_102: + id: 102 + name: projects_list_member_filter_presence + created_at: 2023-04-05 09:16:08.000511500 Z + updated_at: 2023-04-05 09:16:08.000511500 Z diff --git a/test/frontend/__fixtures__/settings.ts b/test/frontend/__fixtures__/settings.ts index 03dd134be..246191ea4 100644 --- a/test/frontend/__fixtures__/settings.ts +++ b/test/frontend/__fixtures__/settings.ts @@ -825,6 +825,12 @@ export const settings: Array = [ value: 'https://www.sleede.com/', last_update: '2022-12-23T14:39:12+0100', localized: 'Url' + }, + { + name: 'projects_list_member_filter_presence', + value: 'false', + last_update: '2022-12-23T14:39:12+0100', + localized: 'Projects list member filter presence' } ];