1+ // This file is part of Moodle - http://moodle.org/
2+ //
3+ // Moodle is free software: you can redistribute it and/or modify
4+ // it under the terms of the GNU General Public License as published by
5+ // the Free Software Foundation, either version 3 of the License, or
6+ // (at your option) any later version.
7+ //
8+ // Moodle is distributed in the hope that it will be useful,
9+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ // GNU General Public License for more details.
12+ //
13+ // You should have received a copy of the GNU General Public License
14+ // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15+
16+ /**
17+ * Module to enforce required and locked profile fields during user creation.
18+ *
19+ * @module theme_trema/profilefields
20+ * @author Rodrigo Mady
21+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22+ */
23+
24+ define ( [ 'jquery' , 'core/str' , 'core/notification' ] , function ( $ , Str , Notification ) {
25+
26+ /**
27+ * Initialize the module.
28+ *
29+ * @param {Object } profileFieldsData - Data containing required profile fields
30+ */
31+ var init = function ( profileFieldsData ) {
32+ setupUserCreationPage ( profileFieldsData || { } ) ;
33+ } ;
34+
35+ /**
36+ * Setup handlers for the user creation page.
37+ *
38+ * @param {Object } profileFieldsData - Data containing required profile fields
39+ */
40+ var setupUserCreationPage = function ( profileFieldsData ) {
41+ if ( profileFieldsData . length > 0 ) {
42+ addFormValidation ( profileFieldsData ) ;
43+ }
44+ } ;
45+
46+ /**
47+ * Add validation to the user creation form.
48+ *
49+ * @param {Array } fields - Array of fields that are required
50+ */
51+ var addFormValidation = function ( fields ) {
52+ // Get the form element
53+ var $form = $ ( '#id_mform1' ) ;
54+
55+ // Get the required string for validation messages
56+ var requiredString = 'Required' ;
57+ Str . get_string ( 'required' , 'core' ) . then ( function ( string ) {
58+ requiredString = string ;
59+ return true ;
60+ } ) . catch ( Notification . exception ) ;
61+
62+ // Process each profile field that is required.
63+ fields . forEach ( function ( field ) {
64+ var fieldId = 'id_profile_field_' + field ;
65+ var $field = $ ( '#' + fieldId ) ;
66+
67+ if ( $field . length > 0 ) {
68+ var $formGroup = $field . closest ( '.form-group' ) ;
69+
70+ $field . attr ( 'required' , 'required' ) ;
71+
72+ $field . attr ( 'aria-required' , 'true' ) ;
73+
74+ var $labelCol = $formGroup . find ( '.col-form-label' ) ;
75+ var $labelAddon = $labelCol . find ( '.form-label-addon' ) ;
76+
77+ if ( $labelAddon . length === 0 ) {
78+ $labelAddon = $ ( '<div class="form-label-addon d-flex align-items-center align-self-start"></div>' ) ;
79+ $labelCol . append ( $labelAddon ) ;
80+ }
81+
82+ // Add the required icon if not present
83+ if ( $labelAddon . find ( '.text-danger' ) . length === 0 ) {
84+ var $requiredIcon = $ (
85+ '<div class="text-danger" title="Required">' +
86+ '<i class="icon fa fa-exclamation-circle text-danger fa-fw " ' +
87+ 'title="Required" role="img" aria-label="Required"></i>' +
88+ '</div>'
89+ ) ;
90+ $labelAddon . append ( $requiredIcon ) ;
91+ }
92+
93+ var errorId = fieldId + '_error' ;
94+ var $felement = $formGroup . find ( '.felement' ) ;
95+ if ( $felement . length > 0 && ! $felement . find ( '#' + errorId ) . length ) {
96+ var $errorContainer = $ ( '<div class="form-control-feedback invalid-feedback" id="' + errorId + '"></div>' ) ;
97+ $felement . append ( $errorContainer ) ;
98+ }
99+
100+ $field . on ( 'blur' , function ( ) {
101+ validateField ( $field , requiredString ) ;
102+ } ) ;
103+
104+ if ( ! $form . data ( 'validation-added' ) ) {
105+ $form . on ( 'submit' , function ( e ) {
106+ var isValid = true ;
107+
108+ // Validate all required profile fields
109+ fields . forEach ( function ( fieldName ) {
110+ var fieldSelector = '#id_profile_field_' + fieldName ;
111+ var $fieldToValidate = $ ( fieldSelector ) ;
112+
113+ if ( $fieldToValidate . length > 0 && ! validateField ( $fieldToValidate , requiredString ) ) {
114+ isValid = false ;
115+ }
116+ } ) ;
117+
118+ if ( ! isValid ) {
119+ e . preventDefault ( ) ;
120+ // Scroll to the first invalid field
121+ var $firstInvalidField = $ ( '.form-group.has-danger' ) . first ( ) ;
122+ if ( $firstInvalidField . length > 0 ) {
123+ $ ( 'html, body' ) . animate ( {
124+ scrollTop : $firstInvalidField . offset ( ) . top - 100
125+ } , 200 ) ;
126+ }
127+ }
128+ } ) ;
129+
130+ $form . data ( 'validation-added' , true ) ;
131+ }
132+ }
133+ } ) ;
134+ } ;
135+
136+ /**
137+ * Validate a single field.
138+ *
139+ * @param {jQuery } $field - The field to validate
140+ * @param {String } requiredString - The text to display for required fields
141+ * @return {boolean } - Whether the field is valid
142+ */
143+ var validateField = function ( $field , requiredString ) {
144+ var fieldId = $field . attr ( 'id' ) ;
145+ var $formGroup = $field . closest ( '.form-group' ) ;
146+ var $errorContainer = $ ( '#' + fieldId + '_error' ) ;
147+
148+ // Get the field value, handling different input types
149+ var value = $field . val ( ) ;
150+ if ( $field . is ( 'input[type="checkbox"]' ) ) {
151+ value = $field . is ( ':checked' ) ;
152+ } else if ( $field . is ( 'input[type="radio"]' ) ) {
153+ value = $formGroup . find ( 'input[type="radio"]:checked' ) . length > 0 ;
154+ } else if ( $field . is ( 'select' ) ) {
155+ // For select elements, empty string or default "choose" option is invalid
156+ if ( value === '' || value === '0' || value === '-1' ) {
157+ value = '' ;
158+ }
159+ }
160+
161+ // Check if the field has a value
162+ var isValid = value !== null && value !== '' && value !== false ;
163+
164+ if ( isValid ) {
165+ // Field is valid - remove error styling
166+ $formGroup . removeClass ( 'has-danger' ) ;
167+ $field . removeClass ( 'is-invalid' ) ;
168+ if ( $errorContainer . length ) {
169+ $errorContainer . empty ( ) ;
170+ }
171+ } else {
172+ // Field is invalid - add error styling
173+ $formGroup . addClass ( 'has-danger' ) ;
174+ $field . addClass ( 'is-invalid' ) ;
175+ if ( $errorContainer . length ) {
176+ $errorContainer . text ( requiredString ) ;
177+ }
178+ }
179+
180+ return isValid ;
181+ } ;
182+
183+ return {
184+ init : init
185+ } ;
186+ } ) ;
0 commit comments