diff --git a/.overcommit.yml b/.overcommit.yml index e11b266f1..2bc07d644 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -12,7 +12,7 @@ CommitMsg: MessageFormat: enabled: true - pattern: ^(\([a-z]+\) [\w ]+(\n\n.+)?)|(Version (\d+\.?)+)|(Merge branch .*) + pattern: ^(\([a-z0-9]+\) [\w ]+(\n\n.+)?)|(Version (\d+\.?)+)|(Merge branch .*) expected_pattern_message: (type) title\n\ndescription sample_message: (bug) no validation on date\n\nThe birthdate was not validated... diff --git a/CHANGELOG.md b/CHANGELOG.md index 50298e90e..3dce351e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog Fab-manager +## v5.4.18 2022 September 12 + +- Script to download translations from Crowdin +- Fix a bug: admin and managers can't cancel or move event reservations +- Fix a bug: phone numbers with hyphens and spaces prevent profile completion when the data is provided by an SSO +- Fix a bug: unable to complete profile from SSO when the account validation is enabled + ## v5.4.17 2022 September 06 - OpenAPI spaces endpoints (index/show) @@ -15,8 +22,8 @@ - Fix a bug: the automated test on statistics generation was not running - Fix a bug: the events times are not displayed - Fix a security issue: disable log4j format message lookup by default for new installations -- Fix a security issue: updated omniauth to 1.9.2 to fix (CVE-2020-36599)[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-36599] -- Fix a security issue: updated moment-timezone to 0.5.35 to fix (GHSA-v78c-4p63-2j6c)[https://github.com/advisories/GHSA-v78c-4p63-2j6c] and (GHSA-56x4-j7p9-fcf9)[https://github.com/advisories/GHSA-56x4-j7p9-fcf9] +- Fix a security issue: updated omniauth to 1.9.2 to fix [CVE-2020-36599](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-36599) +- Fix a security issue: updated moment-timezone to 0.5.35 to fix [GHSA-v78c-4p63-2j6c](https://github.com/advisories/GHSA-v78c-4p63-2j6c) and [GHSA-56x4-j7p9-fcf9](https://github.com/advisories/GHSA-56x4-j7p9-fcf9) - [TODO DEPLOY] `rails fablab:maintenance:regenerate_statistics[2022,07]` ## v5.4.16 2022 August 24 diff --git a/app/frontend/src/javascript/components/user/user-profile-form.tsx b/app/frontend/src/javascript/components/user/user-profile-form.tsx index 1c449c0e5..f4aedbb08 100644 --- a/app/frontend/src/javascript/components/user/user-profile-form.tsx +++ b/app/frontend/src/javascript/components/user/user-profile-form.tsx @@ -59,7 +59,7 @@ export const UserProfileForm: React.FC = ({ action, size, const { t } = useTranslation('shared'); // regular expression to validate the input fields - const phoneRegex = /^((00|\+)\d{2,3})?\d{4,14}$/; + const phoneRegex = /^((00|\+)\d{2,3})?[\d -]{4,14}$/; const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/; const { handleSubmit, register, control, formState, setValue, reset } = useForm({ defaultValues: { ...user } }); diff --git a/app/frontend/src/javascript/controllers/events.js.erb b/app/frontend/src/javascript/controllers/events.js.erb index c7957ca3f..d933bc438 100644 --- a/app/frontend/src/javascript/controllers/events.js.erb +++ b/app/frontend/src/javascript/controllers/events.js.erb @@ -483,6 +483,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' * @param reservation {Reservation} */ $scope.reservationCanModify = function (reservation) { + if (AuthService.isAuthorized(['admin', 'manager'])) return true; + const slotStart = moment(reservation.slots_reservations_attributes[0].slot_attributes.start_at); const now = moment(); @@ -498,6 +500,8 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', ' * @param reservation {Reservation} */ $scope.reservationCanCancel = function(reservation) { + if (AuthService.isAuthorized(['admin', 'manager'])) return true; + const slotStart = moment(reservation.slots_reservations_attributes[0].slot_attributes.start_at); const now = moment(); return $scope.enableBookingCancel && slotStart.diff(now, "hours") >= $scope.cancelBookingDelay; diff --git a/app/services/members/members_service.rb b/app/services/members/members_service.rb index 48ca5101e..c1009d214 100644 --- a/app/services/members/members_service.rb +++ b/app/services/members/members_service.rb @@ -9,19 +9,19 @@ class Members::MembersService end def update(params) - if params[:group_id] && @member.group_id != params[:group_id].to_i && !@member.subscribed_plan.nil? + if subscriber_group_change?(params) # here a group change is requested but unprocessable, handle the exception @member.errors.add(:group_id, I18n.t('members.unable_to_change_the_group_while_a_subscription_is_running')) return false end - if params[:group_id] && params[:group_id].to_i != Group.find_by(slug: 'admins').id && @member.admin? + if admin_group_change?(params) # an admin cannot change his group @member.errors.add(:group_id, I18n.t('members.admins_cant_change_group')) return false end - group_changed = params[:group_id] && @member.group_id != params[:group_id].to_i + group_changed = user_group_change?(params) ex_group = @member.group user_validation_required = Setting.get('user_validation_required') @@ -80,7 +80,7 @@ class Members::MembersService end def validate(is_valid) - is_updated = member.update(validated_at: is_valid ? Time.now : nil) + is_updated = member.update(validated_at: is_valid ? DateTime.current : nil) if is_updated if is_valid NotificationCenter.call type: 'notify_user_is_validated', @@ -133,4 +133,16 @@ class Members::MembersService params[:password] end end + + def subscriber_group_change?(params) + params[:group_id] && @member.group_id != params[:group_id].to_i && !@member.subscribed_plan.nil? + end + + def admin_group_change?(params) + params[:group_id] && params[:group_id].to_i != Group.find_by(slug: 'admins').id && @member.admin? + end + + def user_group_change?(params) + @member.group_id && params[:group_id] && @member.group_id != params[:group_id].to_i + end end diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index f67cab2ff..4009232af 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -1134,7 +1134,7 @@ fr: data_mapping: "Correspondance des données" TYPE_expected: "{TYPE} attendu" types: - integer: "integer" + integer: "entier" string: "chaîne" text: "texte" date: "date" @@ -1152,7 +1152,7 @@ fr: openid_connect_form: issuer: "Émetteur" issuer_help: "URL racine du serveur d'autorisation." - discovery: "Discovery" + discovery: "Découverte" discovery_help: "La découverte automatique OpenID doit-elle être utilisée ? Ceci est recommandé si l'IDP fournit un point d'accès de découverte automatique." discovery_unavailable: "La découverte automatique n'est pas disponible pour l'émetteur configuré." discovery_enabled: "Activer la découverte" diff --git a/config/locales/app.admin.pt.yml b/config/locales/app.admin.pt.yml old mode 100755 new mode 100644 index 732d6621a..15a2bd7b1 --- a/config/locales/app.admin.pt.yml +++ b/config/locales/app.admin.pt.yml @@ -521,7 +521,7 @@ pt: warning_invoices_disabled: "Aviso: As faturas não estão ativadas. Nenhuma fatura será gerada pelo Fab-manager. No entanto, você deve preencher corretamente as informações abaixo, especialmente o IVA." change_logo: "Mudar o logo" john_smith: "João da Silva" - john_smith_at_example_com: "jean.smith@example.com" + john_smith_at_example_com: "joao.smith@example.com" invoice_reference_: "Referencia de fatura:" code_: "Código:" code_disabled: "Código desabilitado" diff --git a/config/locales/app.logged.pt.yml b/config/locales/app.logged.pt.yml old mode 100755 new mode 100644 diff --git a/config/locales/app.public.pt.yml b/config/locales/app.public.pt.yml old mode 100755 new mode 100644 diff --git a/config/locales/app.shared.pt.yml b/config/locales/app.shared.pt.yml old mode 100755 new mode 100644 diff --git a/config/locales/mails.pt.yml b/config/locales/mails.pt.yml old mode 100755 new mode 100644 diff --git a/config/locales/pt.yml b/config/locales/pt.yml old mode 100755 new mode 100644 diff --git a/package.json b/package.json index dc778a1f6..85c8c01f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fab-manager", - "version": "5.4.17", + "version": "5.4.18", "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", diff --git a/scripts/translations/download.sh b/scripts/translations/download.sh new file mode 100755 index 000000000..aed041301 --- /dev/null +++ b/scripts/translations/download.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +jq() { + docker run --rm -i -v "${PWD}:/data" --user "$UID" imega/jq "$@" +} + +config() { + PROJECT_ID="382064" + DIRECTORY_ID="5" + TEMP_FILE="/tmp/Fab-manager-translations-$RANDOM.zip" + SCRIPT_PATH=$(dirname "$0") + TOKEN_FILE="$SCRIPT_PATH/../../.crowdin" + if test -f "$TOKEN_FILE"; then + ACCESS_TOKEN=$(cat "$TOKEN_FILE") + else + printf "\e[91m[ ❌] file %s does not exists.\e[0m Please configure your API access token first.\n" "$(basename "$TOKEN_FILE")" + echo + exit 1 + fi + +} + +authorization() { + echo "Authorization: Bearer $ACCESS_TOKEN" +} + +build_translations() { + data=$(curl -s -X POST "https://api.crowdin.com/api/v2/projects/$PROJECT_ID/translations/builds/directories/$DIRECTORY_ID" -H "$(authorization)" -H "Content-Type: application/json" -d "{}") + echo "$data" | jq -r '.data.id' +} + +check_build_status() { + # param: BUILD_ID + data=$(curl -s "https://api.crowdin.com/api/v2/projects/$PROJECT_ID/translations/builds/$1" -H "$(authorization)") + echo "$data" | jq -r '.data.status' +} + +download_translations() { + # param: BUILD_ID + data=$(curl -s "https://api.crowdin.com/api/v2/projects/$PROJECT_ID/translations/builds/$1/download" -H "$(authorization)") + echo "$data" | jq -r '.data.url' +} + + +function trap_ctrlc() +{ + echo "Ctrl^C, exiting..." + exit 2 +} + +run() { + trap "trap_ctrlc" 2 # SIGINT + config + printf "\n\e[0;33m 🛠 building the translations...\e[0m" + BUILD_ID=$(build_translations) + printf "\n\e[0;33m ↻ waiting for the translations build to complete...\e[0m" + while [[ $(check_build_status "$BUILD_ID") != 'finished' ]]; do + printf "." + sleep 1 + done + printf "\n\e[0;33m ⇩ downloading translations...\n\e[0m" + DOWNLOAD_URL=$(download_translations "$BUILD_ID") + curl -L -o "$TEMP_FILE" "$DOWNLOAD_URL" + printf "\n\e[0;33m 📦 extracting translations...\n\e[0m" + unzip -o "$TEMP_FILE" -d "$SCRIPT_PATH/../../" + rm "$TEMP_FILE" +} + + +run "$@"