From b1def48eac05e1f214f60e5382a06ee34a8f5b70 Mon Sep 17 00:00:00 2001
From: Priyavrat Sharma <52522330+priyavrat7@users.noreply.github.com>
Date: Tue, 23 Jun 2026 12:54:28 -0400
Subject: [PATCH 1/7] [user_accounts] to add SweetAlert2 reject user (#10475)
**Situation:**
In the LORIS `user_accounts` module (`edit_user` interface), clicking
the "Reject User" button triggers an immediate AJAX call to reject the
account. There is no confirmation dialog, making it extremely easy for
an administrator to accidentally reject a user with a misclick.
**Task:**
My objective was to introduce a confirmation step before executing the
rejection AJAX call, ensuring a better and safer user experience. The
implementation needed to adhere strictly to modern LORIS framework
standards, specifically utilizing the SweetAlert2 library.
**Action:**
- **Refactored Legacy Code:** Converted the plain jQuery script
`modules/user_accounts/js/rejectUser.js` into a modern proper source
file at `modules/user_accounts/jsx/rejectUser.js`.
- **Integrated SweetAlert2:** Used modern Webpack imports (`import swal
from 'sweetalert2';`) to implement a `swal.fire()` confirmation modal
with a 'warning' type, standardizing the UI with other LORIS modules.
- **Updated Webpack Entry Point:** Registered the new `rejectUser` file
in `webpack.config.ts` under the `user_accounts` entry.
- **Compiled Bundle:** Built the new asset via `npm run compile`,
resulting in a self-contained Webpack JS bundle that replaces the old
jQuery file.
**Result:**
Administrators can now see SweetAlert2 warning dialog when attempting to
reject a user. The AJAX call is correctly blocked until the user
explicitly clicks "Yes, reject user!", completely preventing accidental
rejections and fixes #10474.
---
modules/user_accounts/.gitignore | 2 +-
modules/user_accounts/js/rejectUser.js | 29 ---------
modules/user_accounts/jsx/rejectUser.js | 64 +++++++++++++++++++
.../locale/fr/LC_MESSAGES/user_accounts.po | 9 +++
.../locale/hi/LC_MESSAGES/user_accounts.po | 9 +++
.../locale/ja/LC_MESSAGES/user_accounts.po | 8 +++
.../locale/zh/LC_MESSAGES/user_accounts.po | 9 +++
webpack.config.ts | 2 +-
8 files changed, 101 insertions(+), 31 deletions(-)
delete mode 100644 modules/user_accounts/js/rejectUser.js
create mode 100644 modules/user_accounts/jsx/rejectUser.js
diff --git a/modules/user_accounts/.gitignore b/modules/user_accounts/.gitignore
index 0683785912..1034f44c2f 100644
--- a/modules/user_accounts/.gitignore
+++ b/modules/user_accounts/.gitignore
@@ -1,2 +1,2 @@
js/*
-!js/edit_user_helper.js
+!js/edit_user_helper.js
\ No newline at end of file
diff --git a/modules/user_accounts/js/rejectUser.js b/modules/user_accounts/js/rejectUser.js
deleted file mode 100644
index 1c0d63b633..0000000000
--- a/modules/user_accounts/js/rejectUser.js
+++ /dev/null
@@ -1,29 +0,0 @@
-$(document).ready(function() {
- $("#btn_reject").click(function() {
- const userID = document.getElementById("UserID").value;
- const baseurl = loris.BaseURL;
- const lorisFetch = window.lorisFetch || fetch;
-
- lorisFetch(baseurl + '/user_accounts/ajax/rejectUser.php', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
- },
- body: new URLSearchParams({identifier: userID}),
- credentials: 'same-origin',
- })
- .then((response) => {
- if (!response.ok) {
- return response.text().then((text) => {
- let error = new Error('request_failed');
- error.lorisMessage = text;
- throw error;
- });
- }
- location.href = baseurl + '/user_accounts/';
- })
- .catch((error) => {
- alert(error.lorisMessage || '');
- });
- });
-});
diff --git a/modules/user_accounts/jsx/rejectUser.js b/modules/user_accounts/jsx/rejectUser.js
new file mode 100644
index 0000000000..a051860275
--- /dev/null
+++ b/modules/user_accounts/jsx/rejectUser.js
@@ -0,0 +1,64 @@
+import swal from 'sweetalert2';
+import i18n from 'I18nSetup';
+import hiStrings from '../locale/hi/LC_MESSAGES/user_accounts.json';
+import jaStrings from '../locale/ja/LC_MESSAGES/user_accounts.json';
+import frStrings from '../locale/fr/LC_MESSAGES/user_accounts.json';
+import zhStrings from '../locale/zh/LC_MESSAGES/user_accounts.json';
+
+window.addEventListener('load', () => {
+ i18n.addResourceBundle('ja', 'user_accounts', jaStrings);
+ i18n.addResourceBundle('hi', 'user_accounts', hiStrings);
+ i18n.addResourceBundle('fr', 'user_accounts', frStrings);
+ i18n.addResourceBundle('zh', 'user_accounts', zhStrings);
+ const btn = document.getElementById('btn_reject');
+
+ if (!btn) return;
+
+ btn.addEventListener('click', () => {
+ const pathParts = window.location.pathname.split('/');
+ const userID = pathParts[pathParts.length - 1];
+ const baseurl = loris.BaseURL;
+
+ swal.fire({
+ title: i18n.t('Are you sure?', {ns: 'loris'}),
+ html: i18n.t(
+ 'Do you really want to reject this user?',
+ {ns: 'user_accounts'}
+ ) + ' ' + i18n.t(
+ 'This action cannot be undone.',
+ {ns: 'user_accounts'}
+ ),
+ type: 'warning',
+ showCancelButton: true,
+ confirmButtonText: i18n.t(
+ 'Yes, reject user!',
+ {ns: 'user_accounts'}
+ ),
+ cancelButtonText: i18n.t('Cancel', {ns: 'loris'}),
+ }).then((result) => {
+ if (result.value) {
+ fetch(`${baseurl}/user_accounts/ajax/rejectUser.php`, {
+ method: 'POST',
+ credentials: 'same-origin',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: new URLSearchParams({
+ identifier: userID,
+ }),
+ })
+ .then((resp) => {
+ if (!resp.ok) {
+ return resp.text().then((text) => {
+ throw new Error(text);
+ });
+ }
+ window.location.href = `${baseurl}/user_accounts/`;
+ })
+ .catch((error) => {
+ swal.fire(i18n.t('Error', {ns: 'loris'}), error.message, 'error');
+ });
+ }
+ });
+ });
+});
diff --git a/modules/user_accounts/locale/fr/LC_MESSAGES/user_accounts.po b/modules/user_accounts/locale/fr/LC_MESSAGES/user_accounts.po
index 494aeca361..a87f123561 100644
--- a/modules/user_accounts/locale/fr/LC_MESSAGES/user_accounts.po
+++ b/modules/user_accounts/locale/fr/LC_MESSAGES/user_accounts.po
@@ -232,3 +232,12 @@ msgstr "Les nouveaux et anciens mots de passe sont identiques."
msgid "This email address is already in use"
msgstr "Cette adresse email est déjà utilisée"
+
+msgid "Do you really want to reject this user?"
+msgstr "Voulez-vous vraiment rejeter cet utilisateur ?"
+
+msgid "This action cannot be undone."
+msgstr "Cette action ne peut pas être annulée."
+
+msgid "Yes, reject user!"
+msgstr "Oui, rejeter l’utilisateur !"
diff --git a/modules/user_accounts/locale/hi/LC_MESSAGES/user_accounts.po b/modules/user_accounts/locale/hi/LC_MESSAGES/user_accounts.po
index d9f8aaaa6a..65a7f3cbe6 100644
--- a/modules/user_accounts/locale/hi/LC_MESSAGES/user_accounts.po
+++ b/modules/user_accounts/locale/hi/LC_MESSAGES/user_accounts.po
@@ -191,3 +191,12 @@ msgstr "नया और पुराना पासवर्ड समान
msgid "This email address is already in use"
msgstr "यह ईमेल पता पहले से उपयोग में है।"
+
+msgid "Do you really want to reject this user?"
+msgstr "क्या आप वाकई इस उपयोगकर्ता को अस्वीकार करना चाहते हैं?"
+
+msgid "This action cannot be undone."
+msgstr "यह क्रिया पूर्ववत नहीं की जा सकती।"
+
+msgid "Yes, reject user!"
+msgstr "हाँ, उपयोगकर्ता को अस्वीकार करें!"
diff --git a/modules/user_accounts/locale/ja/LC_MESSAGES/user_accounts.po b/modules/user_accounts/locale/ja/LC_MESSAGES/user_accounts.po
index d8e0a60d40..27b8af1951 100644
--- a/modules/user_accounts/locale/ja/LC_MESSAGES/user_accounts.po
+++ b/modules/user_accounts/locale/ja/LC_MESSAGES/user_accounts.po
@@ -198,3 +198,11 @@ msgstr "新しいパスワードと古いパスワードは同一です。"
msgid "This email address is already in use"
msgstr "このメールアドレスはすでに使用されています。"
+msgid "Do you really want to reject this user?"
+msgstr "本当にこのユーザーを拒否しますか?"
+
+msgid "This action cannot be undone."
+msgstr "この操作は取り消せません。"
+
+msgid "Yes, reject user!"
+msgstr "はい、ユーザーを拒否します!"
diff --git a/modules/user_accounts/locale/zh/LC_MESSAGES/user_accounts.po b/modules/user_accounts/locale/zh/LC_MESSAGES/user_accounts.po
index 83a0eaa27e..86e90fa0fb 100644
--- a/modules/user_accounts/locale/zh/LC_MESSAGES/user_accounts.po
+++ b/modules/user_accounts/locale/zh/LC_MESSAGES/user_accounts.po
@@ -211,3 +211,12 @@ msgstr "新旧密码相同"
msgid "This email address is already in use"
msgstr "该电子邮件地址已被使用"
+
+msgid "Do you really want to reject this user?"
+msgstr "您确定要拒绝该用户吗?"
+
+msgid "This action cannot be undone."
+msgstr "此操作无法撤销。"
+
+msgid "Yes, reject user!"
+msgstr "是的,拒绝用户!"
diff --git a/webpack.config.ts b/webpack.config.ts
index 871d56839c..21cdbb0e93 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -65,7 +65,7 @@ const lorisModules: Record = {
instrument_manager: ['instrumentManagerIndex'],
survey_accounts: ['surveyAccountsIndex'],
mri_violations: ['mriViolationsIndex'],
- user_accounts: ['userAccountsIndex'],
+ user_accounts: ['userAccountsIndex', 'rejectUser'],
examiner: ['examinerIndex'],
help_editor: ['help_editor', 'helpEditorForm'],
brainbrowser: ['Brainbrowser'],
From 291d827d26e057d5b3fe40e47aba11f3bb25fbc0 Mon Sep 17 00:00:00 2001
From: Michael Lapadula
Date: Thu, 25 Jun 2026 13:46:14 -0400
Subject: [PATCH 2/7] [dataquery] Updating TestPlan.md to specify admin pinned
queries (#10685)
## Brief summary of changes
The dataquery TestPlan specifies that queries should only be able to be
pinned by users with "Data Query Tool: Admin dataquery queries"
permission.
- [x] Have you updated related documentation?
#### Testing instructions (if applicable)
1. Verify the TestPlan
#### Link(s) to related issue(s)
* Resolves [10602](https://github.com/aces/Loris/issues/10602).
Co-authored-by: Michael Lapadula
---
modules/dataquery/test/TestPlan.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/modules/dataquery/test/TestPlan.md b/modules/dataquery/test/TestPlan.md
index e0d59f88e2..8162eac0cd 100644
--- a/modules/dataquery/test/TestPlan.md
+++ b/modules/dataquery/test/TestPlan.md
@@ -5,7 +5,8 @@
1. Ensure the module loads only for a user that has the `dataquery_view` permission. They must also have access to the dictionary module.
2. Assert that: `Instructions` panel, `Recent Queries` panel, and `Next Steps` panel (bottom-right corner) collapse as expected.
3. Assert that: `Continue to Define Fields` button in the main panel, and `Choose Fields` button in the `Next Steps` panel are redirecting to the same page.
-4. `Recent Queries` panel
+4. Ensure the pinning icon only appears for users that have the `dataquery_admin` (Data Query Tool: Admin dataquery queries) permission. Users without it should not be able to pin queries.
+5. `Recent Queries` panel
1. If not queries are available, make some so they will be added to this section.
2. Assert that: queries you made have their parameters correctly displayed (i.e. fields and filters).
3. Assert that: `text filter` immediately filter the queries.
From 533a0bbf28a8994f63c26891d4f3834a64a9e616 Mon Sep 17 00:00:00 2001
From: Camille Beaudoin <51176779+CamilleBeau@users.noreply.github.com>
Date: Thu, 25 Jun 2026 13:56:01 -0400
Subject: [PATCH 3/7] [NDB_Notifier] Fix 'active_to' query (#10686)
## Brief summary of changes
No notifications were being sent out because the "Active_to" condition
in the query for users to be notified had the opposite logic than it
should (it was checking users who have already had their access expired
instead of users who are still active).
- [ ] Have you updated related documentation?
#### Testing instructions (if applicable)
1. Create a user and grant the user each permission from the
my_preferences page. Make sure the user is active and does not have an
active_to date set
2. Test each operation that sends notifications to make sure the
notifications are properly sent
3. Edit the user to have an active_to date set before now
4. Make sure the notifications are not sent
5. Edit the user to have an active_to date set after now
6. Make sure the notifications are sent
7. Remove all of the notification settings from my_preferences page
8. Make sure that the notifications are not sent
#### Link(s) to related issue(s)
* Resolves #10588
---
php/libraries/NDB_Notifier_Abstract.class.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/php/libraries/NDB_Notifier_Abstract.class.inc b/php/libraries/NDB_Notifier_Abstract.class.inc
index 7592c3027c..209c649868 100644
--- a/php/libraries/NDB_Notifier_Abstract.class.inc
+++ b/php/libraries/NDB_Notifier_Abstract.class.inc
@@ -244,7 +244,7 @@ abstract class NDB_Notifier_Abstract
AND nm.operation_type=:ot
AND u.Pending_approval = 'N'
AND u.Active = 'Y'
- AND u.active_to < NOW()
+ AND (u.active_to > NOW() OR u.active_to IS NULL)
";
$params = [
"nm" => $this->module,
From 30b999b8cdca8ccb315365a59ca0945db214227b Mon Sep 17 00:00:00 2001
From: George Murad
Date: Fri, 26 Jun 2026 09:57:56 -0400
Subject: [PATCH 4/7] [survey_accounts]Add French Language To Survey Accounts
---
.../ajax/ValidateEmailSubmitInput.php | 13 +-
.../js/survey_accounts_helper.js | 6 +-
.../jsx/surveyAccountsIndex.js | 21 ++-
.../fr_CA/LC_MESSAGES/survey_accounts.po | 129 ++++++++++++++++++
.../locale/survey_accounts.pot | 33 ++++-
.../survey_accounts/php/addsurvey.class.inc | 34 +++--
.../php/survey_accounts.class.inc | 8 +-
.../templates/form_addSurvey.tpl | 6 +-
8 files changed, 217 insertions(+), 33 deletions(-)
create mode 100644 modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.po
diff --git a/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php b/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php
index fd16cbe134..4d9ac8b390 100644
--- a/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php
+++ b/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php
@@ -69,7 +69,7 @@
if ($numSessions != 1) {
echo json_encode(
[
- 'error_msg' => dgettext('loris', 'Visit').' '. $_REQUEST['VL'].' '.
+ 'error_msg' => dgettext('survey_accounts', 'Visit').' '. $_REQUEST['VL'].' '.
dgettext('survey_accounts', 'does not exist for given candidate'),
]
);
@@ -78,7 +78,7 @@
if (empty($_REQUEST['TN'])) {
echo json_encode(
- ['error_msg' => dgettext('survey_accounts', 'Please choose an instrument')]
+ ['error_msg' => dgettext('survey_accounts', 'Please choose an instrument.')]
);
exit(0);
}
@@ -100,8 +100,13 @@
if ($_REQUEST['TN'] == $instrument['Test_name']) {
echo json_encode(
[
- 'error_msg' => dgettext('loris', 'Instrument').' '. $_REQUEST['TN']
- .' ' .dgettext(
+ 'error_msg' => dcngettext(
+ 'survey_accounts',
+ 'Instrument',
+ 'Instruments',
+ 1,
+ LC_MESSAGES
+ ).' '. $_REQUEST['TN'].' ' .dgettext(
'survey_accounts',
'already exists for given candidate for visit'
).' '. $_REQUEST['VL'],
diff --git a/modules/survey_accounts/js/survey_accounts_helper.js b/modules/survey_accounts/js/survey_accounts_helper.js
index e458048385..69d4834d23 100644
--- a/modules/survey_accounts/js/survey_accounts_helper.js
+++ b/modules/survey_accounts/js/survey_accounts_helper.js
@@ -41,7 +41,9 @@ $(document).ready(function () {
}
if (email.length > 0 && email2.length > 0 && email !== email2)
{
- $("#email-error").show().html("Emails do not match");
+ $("#email-error")
+ .show()
+ .html($("#email-error").data("email-mismatch"));
}
} );
$("#emailData").click(function(){
@@ -60,7 +62,7 @@ $(document).ready(function () {
}).appendTo("#participant_accounts_form");
$("#participant_accounts_form").submit();
});
- $("input[type=submit]").click(function (e) {
+ $("input[type=submit], button[type=submit]").click(function (e) {
if(e.currentTarget.classList.contains('email')) {
$.get(loris.BaseURL + "/survey_accounts/ajax/ValidateEmailSubmitInput.php", {
dccid: $("input[name=CandID]").val(),
diff --git a/modules/survey_accounts/jsx/surveyAccountsIndex.js b/modules/survey_accounts/jsx/surveyAccountsIndex.js
index 96c40f9fe5..01cc2f4acb 100644
--- a/modules/survey_accounts/jsx/surveyAccountsIndex.js
+++ b/modules/survey_accounts/jsx/surveyAccountsIndex.js
@@ -9,6 +9,9 @@ import Loader from 'Loader';
import FilterableDataTable from 'FilterableDataTable';
import hiStrings from '../locale/hi/LC_MESSAGES/survey_accounts.json';
+import jaStrings from '../locale/ja/LC_MESSAGES/survey_accounts.json';
+import frCAStrings from '../locale/fr_CA/LC_MESSAGES/survey_accounts.json';
+import zhStrings from '../locale/zh/LC_MESSAGES/survey_accounts.json';
/**
* Survey Account React Component
*/
@@ -73,7 +76,7 @@ class SurveyAccountsIndex extends Component {
const url = loris.BaseURL + '/survey.php?key=' + row.URL;
result =
;
break;
- case t('Instrument', {ns: 'loris', count: 1}):
+ case t('Instrument', {ns: 'survey_accounts', count: 1}):
result =
{this.state.data.fieldOptions.instruments[cell]}
;
break;
}
@@ -110,20 +113,20 @@ class SurveyAccountsIndex extends Component {
const options = this.state.data.fieldOptions;
const fields = [
{
- label: t('PSCID', {ns: 'loris'}), show: true, filter: {
+ label: t('PSCID', {ns: 'survey_accounts'}), show: true, filter: {
name: 'pscid',
type: 'text',
},
},
{
- label: t('Visit', {ns: 'loris'}), show: true, filter: {
+ label: t('Visit', {ns: 'survey_accounts'}), show: true, filter: {
name: 'visit',
type: 'select',
options: options.visits,
},
},
{
- label: t('Instrument', {ns: 'loris', count: 1}), show: true, filter: {
+ label: t('Instrument', {ns: 'survey_accounts', count: 1}), show: true, filter: {
name: 'instrument',
type: 'select',
options: options.instruments,
@@ -139,7 +142,10 @@ class SurveyAccountsIndex extends Component {
},
];
const addSurvey = () => {
- location.href = '/survey_accounts/addSurvey/';
+ const params = new URLSearchParams(window.location.search);
+ const lang = params.get('lang');
+ const query = lang ? `?lang=${encodeURIComponent(lang)}` : '';
+ location.href = `${loris.BaseURL}/survey_accounts/addSurvey/${query}`;
};
const actions = [
{label: t('Add Survey', {ns: 'survey_accounts'}), action: addSurvey},
@@ -167,8 +173,9 @@ SurveyAccountsIndex.propTypes = {
window.addEventListener(
'load', () => {
i18n.addResourceBundle('hi', 'survey_accounts', hiStrings);
- i18n.addResourceBundle('ja', 'survey_accounts', {});
- i18n.addResourceBundle('zh', 'survey_accounts', {});
+ i18n.addResourceBundle('ja', 'survey_accounts', jaStrings);
+ i18n.addResourceBundle('fr_CA', 'survey_accounts', frCAStrings);
+ i18n.addResourceBundle('zh', 'survey_accounts', zhStrings);
const Index = withTranslation(
['survey_accounts']
)(SurveyAccountsIndex);
diff --git a/modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.po b/modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.po
new file mode 100644
index 0000000000..97b151dca6
--- /dev/null
+++ b/modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.po
@@ -0,0 +1,129 @@
+# Default LORIS strings to be translated (French Canadian).
+# Copy this to a language specific file and add translations to the
+# new file.
+# Copyright (C) 2025
+# This file is distributed under the same license as the LORIS package.
+# Dave MacFarlane , 2025.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: LORIS 27\n"
+"Report-Msgid-Bugs-To: https://github.com/aces/Loris/issues\n"
+"POT-Creation-Date: 2025-04-08 14:37-0400\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: French Canadian \n"
+"Language: fr_CA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Survey Accounts"
+msgstr "Comptes de sondage"
+
+msgid "Survey was added successfully."
+msgstr "Le sondage a été ajouté avec succès."
+
+msgid "Click here to go back to view the list of survey's created"
+msgstr "Cliquez ici pour revenir à la liste des sondages créés"
+
+msgid "Survey List"
+msgstr "Liste des sondages"
+
+msgid "Usage"
+msgstr "Utilisation"
+
+msgid "Use this form to create a link for a study participant to use in order to directly enter a form/data into Loris."
+msgstr "Utilisez ce formulaire pour créer un lien permettant à un participant à l'étude de saisir directement un formulaire/des données dans LORIS."
+
+msgid "Add Survey"
+msgstr "Ajouter un sondage"
+
+msgid "Create survey"
+msgstr "Créer un sondage"
+
+msgid "Email survey"
+msgstr "Envoyer le sondage par courriel"
+
+msgid "Close"
+msgstr "Fermer"
+
+msgid "Email to Study Participant"
+msgstr "Courriel au participant à l'étude"
+
+msgid "Optionally enter a customized message here. A default email will be sent if left blank."
+msgstr "Saisissez éventuellement un message personnalisé ici. Un courriel par défaut sera envoyé si ce champ est laissé vide."
+
+msgid "This is where your message goes."
+msgstr "Votre message s'inscrit ici."
+
+msgid "Email"
+msgstr "Adresse courriel"
+
+msgid "DCCID"
+msgstr "DCCID"
+
+msgid "PSCID"
+msgstr "PSCID"
+
+msgid "Visit"
+msgstr "Visite"
+
+msgid "Visit Label"
+msgstr "Libellé de visite"
+
+msgid "Email address"
+msgstr "Adresse courriel"
+
+msgid "Instrument"
+msgid_plural "Instruments"
+msgstr[0] "Instrument"
+msgstr[1] "Instruments"
+
+msgid "URL"
+msgstr "URL"
+
+msgid "Status"
+msgstr "Statut"
+
+msgid "Created"
+msgstr "Créé"
+
+msgid "Sent"
+msgstr "Envoyé"
+
+msgid "In Progress"
+msgstr "En cours"
+
+msgid "Complete"
+msgstr "Complété"
+
+msgid "PSCID and DCCID do not match or candidate does not exist."
+msgstr "Le PSCID et le DCCID ne correspondent pas ou le candidat n'existe pas."
+
+msgid "does not exist for given candidate"
+msgstr "n'existe pas pour le candidat donné"
+
+msgid "Please choose an instrument."
+msgstr "Veuillez choisir un instrument."
+
+msgid "already exists for given candidate for visit"
+msgstr "existe déjà pour le candidat donné pour la visite"
+
+msgid "The email address is not valid."
+msgstr "L'adresse courriel n'est pas valide."
+
+msgid "Please confirm the email address."
+msgstr "Veuillez confirmer l'adresse courriel."
+
+msgid "The email addresses do not match."
+msgstr "Les adresses courriel ne correspondent pas."
+
+msgid "You must specify a valid Visit Label."
+msgstr "Vous devez préciser un libellé de visite valide."
+
+msgid "You are not affiliated with this session's project"
+msgstr "Vous n'êtes pas affilié au projet de cette session"
+
+msgid "Confirm Email address"
+msgstr "Confirmer l'adresse courriel"
diff --git a/modules/survey_accounts/locale/survey_accounts.pot b/modules/survey_accounts/locale/survey_accounts.pot
index 9bd3b188f9..31ae876ad1 100644
--- a/modules/survey_accounts/locale/survey_accounts.pot
+++ b/modules/survey_accounts/locale/survey_accounts.pot
@@ -60,15 +60,43 @@ msgstr ""
msgid "Email"
msgstr ""
-msgid "Instrument"
+msgid "DCCID"
+msgstr ""
+
+msgid "PSCID"
msgstr ""
+msgid "Visit"
+msgstr ""
+
+msgid "Visit Label"
+msgstr ""
+
+msgid "Email address"
+msgstr ""
+
+msgid "Instrument"
+msgid_plural "Instruments"
+msgstr[0] ""
+
msgid "URL"
msgstr ""
msgid "Status"
msgstr ""
+msgid "Created"
+msgstr ""
+
+msgid "Sent"
+msgstr ""
+
+msgid "In Progress"
+msgstr ""
+
+msgid "Complete"
+msgstr ""
+
msgid "PSCID and DCCID do not match or candidate does not exist."
msgstr ""
@@ -96,8 +124,5 @@ msgstr ""
msgid "You are not affiliated with this session's project"
msgstr ""
-msgid "Email is not valid."
-msgstr ""
-
msgid "Confirm Email address"
msgstr ""
diff --git a/modules/survey_accounts/php/addsurvey.class.inc b/modules/survey_accounts/php/addsurvey.class.inc
index 6635d4ed0e..d8271899f0 100644
--- a/modules/survey_accounts/php/addsurvey.class.inc
+++ b/modules/survey_accounts/php/addsurvey.class.inc
@@ -139,7 +139,7 @@ class AddSurvey extends \NDB_Form
if ($numSessions != 1) {
return [
- 'VL' => dgettext('loris', "Visit")." ".
+ 'VL' => dgettext('survey_accounts', "Visit")." ".
$values['VL']." ".
dgettext(
'survey_accounts',
@@ -193,14 +193,20 @@ class AddSurvey extends \NDB_Form
$instrument['Test_name']
);
return [
- 'Test_name' => "Instrument ".
+ 'Test_name' => dcngettext(
+ 'survey_accounts',
+ 'Instrument',
+ 'Instruments',
+ 1,
+ LC_MESSAGES
+ )." ".
$instrument_instance->getFullName().
$reminder. $values['VL'],
];
}
}
if (!isset($_REQUEST['fire_away'])
- || ($_REQUEST['fire_away'] !== 'Create survey')
+ || ($_REQUEST['fire_away'] !== 'create_survey')
) {
if (!filter_var(
$values['Email'],
@@ -209,7 +215,7 @@ class AddSurvey extends \NDB_Form
) {
return ['Email' => dgettext(
'survey_accounts',
- 'Email is not valid.'
+ 'The email address is not valid.'
)
];
@@ -326,18 +332,28 @@ class AddSurvey extends \NDB_Form
parent::setup();
$this->redirect = "test_name=$this->name";
- $this->addBasicText("CandID", "DCCID");
- $this->addBasicText("PSCID", "PSCID");
- $this->addSelect("VL", "Visit Label", \Utility::getVisitList());
+ $this->addBasicText("CandID", dgettext('survey_accounts', 'DCCID'));
+ $this->addBasicText("PSCID", dgettext('survey_accounts', 'PSCID'));
+ $this->addSelect(
+ "VL",
+ dgettext('survey_accounts', 'Visit Label'),
+ \Utility::getVisitList()
+ );
$this->addSelect(
"Test_name",
- "Instrument",
+ dcngettext(
+ 'survey_accounts',
+ 'Instrument',
+ 'Instruments',
+ 1,
+ LC_MESSAGES
+ ),
array_merge(
['' => ''],
\NDB_BVL_Instrument::getDirectEntryInstrumentNamesList($this->loris)
)
);
- $this->addBasicText("Email", "Email address");
+ $this->addBasicText("Email", dgettext('survey_accounts', 'Email address'));
$this->addBasicText(
"Email2",
dgettext('survey_accounts', 'Confirm Email address')
diff --git a/modules/survey_accounts/php/survey_accounts.class.inc b/modules/survey_accounts/php/survey_accounts.class.inc
index 42423878b6..d16bde3538 100644
--- a/modules/survey_accounts/php/survey_accounts.class.inc
+++ b/modules/survey_accounts/php/survey_accounts.class.inc
@@ -77,10 +77,10 @@ class Survey_Accounts extends \DataFrameworkMenu
public function getFieldOptions() : array
{
$statusOptions = [
- 'Created' => dgettext("loris", "Created"),
- 'Sent' => dgettext("loris", "Sent"),
- 'In Progress' => dgettext("loris", "In Progress"),
- 'Complete' => dgettext("loris", "Complete"),
+ 'Created' => dgettext("survey_accounts", "Created"),
+ 'Sent' => dgettext("survey_accounts", "Sent"),
+ 'In Progress' => dgettext("survey_accounts", "In Progress"),
+ 'Complete' => dgettext("survey_accounts", "Complete"),
];
$instruments
diff --git a/modules/survey_accounts/templates/form_addSurvey.tpl b/modules/survey_accounts/templates/form_addSurvey.tpl
index d24e0ff5e8..214783e656 100644
--- a/modules/survey_accounts/templates/form_addSurvey.tpl
+++ b/modules/survey_accounts/templates/form_addSurvey.tpl
@@ -31,7 +31,7 @@
{/foreach}
-
+
@@ -60,8 +60,8 @@
-
-
+
+
{/if}
From 3182174e1721da671e657d00414336f248945fa1 Mon Sep 17 00:00:00 2001
From: George Murad
Date: Fri, 26 Jun 2026 10:08:06 -0400
Subject: [PATCH 5/7] add the json file
---
.../fr_CA/LC_MESSAGES/survey_accounts.json | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.json
diff --git a/modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.json b/modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.json
new file mode 100644
index 0000000000..7155095871
--- /dev/null
+++ b/modules/survey_accounts/locale/fr_CA/LC_MESSAGES/survey_accounts.json
@@ -0,0 +1,39 @@
+{
+ "Survey Accounts": "Comptes de sondage",
+ "Survey was added successfully.": "Le sondage a été ajouté avec succès.",
+ "Click here to go back to view the list of survey's created": "Cliquez ici pour revenir à la liste des sondages créés",
+ "Survey List": "Liste des sondages",
+ "Usage": "Utilisation",
+ "Use this form to create a link for a study participant to use in order to directly enter a form/data into Loris.": "Utilisez ce formulaire pour créer un lien permettant à un participant à l'étude de saisir directement un formulaire/des données dans LORIS.",
+ "Add Survey": "Ajouter un sondage",
+ "Create survey": "Créer un sondage",
+ "Email survey": "Envoyer le sondage par courriel",
+ "Close": "Fermer",
+ "Email to Study Participant": "Courriel au participant à l'étude",
+ "Optionally enter a customized message here. A default email will be sent if left blank.": "Saisissez éventuellement un message personnalisé ici. Un courriel par défaut sera envoyé si ce champ est laissé vide.",
+ "This is where your message goes.": "Votre message s'inscrit ici.",
+ "Email": "Adresse courriel",
+ "DCCID": "DCCID",
+ "PSCID": "PSCID",
+ "Visit": "Visite",
+ "Visit Label": "Libellé de visite",
+ "Email address": "Adresse courriel",
+ "Instrument": "Instrument",
+ "Instrument_plural": "Instruments",
+ "URL": "URL",
+ "Status": "Statut",
+ "Created": "Créé",
+ "Sent": "Envoyé",
+ "In Progress": "En cours",
+ "Complete": "Complété",
+ "PSCID and DCCID do not match or candidate does not exist.": "Le PSCID et le DCCID ne correspondent pas ou le candidat n'existe pas.",
+ "does not exist for given candidate": "n'existe pas pour le candidat donné",
+ "Please choose an instrument.": "Veuillez choisir un instrument.",
+ "already exists for given candidate for visit": "existe déjà pour le candidat donné pour la visite",
+ "The email address is not valid.": "L'adresse courriel n'est pas valide.",
+ "Please confirm the email address.": "Veuillez confirmer l'adresse courriel.",
+ "The email addresses do not match.": "Les adresses courriel ne correspondent pas.",
+ "You must specify a valid Visit Label.": "Vous devez préciser un libellé de visite valide.",
+ "You are not affiliated with this session's project": "Vous n'êtes pas affilié au projet de cette session",
+ "Confirm Email address": "Confirmer l'adresse courriel"
+}
\ No newline at end of file
From f313af362ce5614a0c73f884143ad03178e55b99 Mon Sep 17 00:00:00 2001
From: George Murad
Date: Fri, 26 Jun 2026 10:22:57 -0400
Subject: [PATCH 6/7] lint error
---
modules/survey_accounts/ajax/ValidateEmailSubmitInput.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php b/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php
index 4d9ac8b390..1ca9fe79d8 100644
--- a/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php
+++ b/modules/survey_accounts/ajax/ValidateEmailSubmitInput.php
@@ -69,7 +69,8 @@
if ($numSessions != 1) {
echo json_encode(
[
- 'error_msg' => dgettext('survey_accounts', 'Visit').' '. $_REQUEST['VL'].' '.
+ 'error_msg' => dgettext('survey_accounts', 'Visit').' '.
+ $_REQUEST['VL'].' '.
dgettext('survey_accounts', 'does not exist for given candidate'),
]
);
From fe6eedbed5c7f76fcc316c0495522fb080a4fda4 Mon Sep 17 00:00:00 2001
From: George Murad
Date: Fri, 26 Jun 2026 10:34:58 -0400
Subject: [PATCH 7/7] eslint error
---
modules/survey_accounts/jsx/surveyAccountsIndex.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/modules/survey_accounts/jsx/surveyAccountsIndex.js b/modules/survey_accounts/jsx/surveyAccountsIndex.js
index 01cc2f4acb..f776b2efa9 100644
--- a/modules/survey_accounts/jsx/surveyAccountsIndex.js
+++ b/modules/survey_accounts/jsx/surveyAccountsIndex.js
@@ -126,7 +126,9 @@ class SurveyAccountsIndex extends Component {
},
},
{
- label: t('Instrument', {ns: 'survey_accounts', count: 1}), show: true, filter: {
+ label: t('Instrument', {ns: 'survey_accounts', count: 1}),
+ show: true,
+ filter: {
name: 'instrument',
type: 'select',
options: options.instruments,