From 5910bfb5088d665f9cf7f35bfaf54107814fea33 Mon Sep 17 00:00:00 2001 From: Sai Pavan Kamma Date: Fri, 20 Sep 2024 15:49:52 -0400 Subject: [PATCH 1/5] Fix the bug introduced by adding null coalescing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change undo’s the lines that introduced a bug in the module. --- ExternalModule.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ExternalModule.php b/ExternalModule.php index 2898b57..328b5ff 100644 --- a/ExternalModule.php +++ b/ExternalModule.php @@ -51,8 +51,6 @@ function redcap_survey_page_top($project_id) { global $elements; // set the action_tag_class as it would be in the DataEntry context foreach( $elements as &$element) { - $element['name'] ??=[]; - $this->survey_APF_fields ??=[]; $i = array_search($element['name'], $this->survey_APF_fields); if ( $i !== false ) { // append to preserve existing tags. This can result in duplicate @DEFAULT action tags but there appear to be no affects from this @@ -82,7 +80,6 @@ function setDefaultValues() { // Temporarily overriding project metadata. foreach ($Proj->metadata as $field_name => $field_info) { // Overriding choice selection fields - checkboxes, radios and dropdowns. - $field_info['element_type'] ??= ''; if (!in_array($field_info['element_type'], ['checkbox', 'radio', 'select'])) { continue; } @@ -90,7 +87,7 @@ function setDefaultValues() { if (!$options = parseEnum($Proj->metadata[$field_name]['element_enum'])) { continue; } - $options ??= []; + foreach (array_keys($options) as $key) { // Replacing selection choices labels with keys. $options[$key] = $key . ',' . $key; @@ -141,7 +138,7 @@ function setDefaultValues() { $fields = empty($_GET['page']) ? $Proj->metadata : $Proj->forms[$_GET['page']]['fields']; $entry_num = ($double_data_entry && $user_rights['double_data'] != 0) ? '--' . $user_rights['double_data'] : ''; - $fields ??= []; + foreach (array_keys($fields) as $field_name) { $field_info = $Proj->metadata[$field_name]; $misc = $field_info['misc']; From 0a26c21305d6fc02c5058f7580e1d00d80799b71 Mon Sep 17 00:00:00 2001 From: Sai Pavan Kamma Date: Mon, 23 Sep 2024 09:20:09 -0400 Subject: [PATCH 2/5] Fix #66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error is raised when the Page parameter of the redcap url is bogus or isn’t design when the control reaches that part of the code. As the page doesn’t actually exist there will no data fields in it. This will set the $fields variable to null(and array_keys() doesn’t allow null types. To resolve it I have added a isset() conditional which checks if a variable is null. --- ExternalModule.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ExternalModule.php b/ExternalModule.php index 328b5ff..1e6a429 100644 --- a/ExternalModule.php +++ b/ExternalModule.php @@ -131,7 +131,6 @@ function setDefaultValues() { } else { $arm = $Proj->eventInfo[$_GET['event_id']]['arm_num']; - $Proj->events[$arm]['events'] ??= []; $events = array_keys($Proj->events[$arm]['events']); } } @@ -139,6 +138,8 @@ function setDefaultValues() { $fields = empty($_GET['page']) ? $Proj->metadata : $Proj->forms[$_GET['page']]['fields']; $entry_num = ($double_data_entry && $user_rights['double_data'] != 0) ? '--' . $user_rights['double_data'] : ''; + if (isset($fields)) + { foreach (array_keys($fields) as $field_name) { $field_info = $Proj->metadata[$field_name]; $misc = $field_info['misc']; @@ -261,6 +262,7 @@ function setDefaultValues() { break; } } + } // Now that pipings are done, let's restore original project metadata. $Proj->metadata = $aux_metadata; From 4c93b23e5214f4fa3b5dd978f8508c03cb1f4355 Mon Sep 17 00:00:00 2001 From: Sai Pavan Kamma Date: Mon, 23 Sep 2024 09:21:33 -0400 Subject: [PATCH 3/5] Fix typo in the module --- ExternalModule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExternalModule.php b/ExternalModule.php index 1e6a429..1973c42 100644 --- a/ExternalModule.php +++ b/ExternalModule.php @@ -306,7 +306,7 @@ function setDefaultWhenVisible() { * TRUE if the current form contains data, FALSE otherwise. */ function currentFormHasData() { - global $double_data_entry, $user_rights, $quesion_by_section, $pageFields; + global $double_data_entry, $user_rights, $question_by_section, $pageFields; $record = $_GET['id']; if ($double_data_entry && $user_rights['double_data'] != 0) { From 7b3f17aa886d68c3013fb382f5a5f01d6bc70e5d Mon Sep 17 00:00:00 2001 From: Philip Chase Date: Mon, 23 Sep 2024 11:54:09 -0400 Subject: [PATCH 4/5] Run code formatter on ExternalModule.php --- ExternalModule.php | 272 +++++++++++++++++++++++---------------------- 1 file changed, 140 insertions(+), 132 deletions(-) diff --git a/ExternalModule.php b/ExternalModule.php index 1973c42..d71c367 100644 --- a/ExternalModule.php +++ b/ExternalModule.php @@ -1,8 +1,10 @@ initializeJsObject(); if (PAGE == 'Design/online_designer.php') { $this->includeJs('js/helper.js'); - } - elseif ( (PAGE == 'DataEntry/index.php' || PAGE == 'surveys/index.php') && !empty($_GET['id']) ) { + } elseif ((PAGE == 'DataEntry/index.php' || PAGE == 'surveys/index.php') && !empty($_GET['id'])) { if (!$this->currentFormHasData()) { $this->setDefaultValues(); } - if (isset($_GET['page']) && (function_exists('getBranchingFields') || method_exists('\DataEntry', 'getBranchingFields')) ) { + if (isset($_GET['page']) && (function_exists('getBranchingFields') || method_exists('\DataEntry', 'getBranchingFields'))) { $this->setDefaultWhenVisible(); } } @@ -45,18 +48,19 @@ function redcap_every_page_top($project_id) { // Because REDCap does not recognize custom action tags this block appends // the REDCap action tag @DEFAULT in the case that any custom actions tags (@DEFAULT-*) // have been applied to a field. - function redcap_survey_page_top($project_id) { + function redcap_survey_page_top($project_id) + { $project_settings = $this->getProjectSettings(); if (!$project_settings['use_in_survey']) return; global $elements; // set the action_tag_class as it would be in the DataEntry context - foreach( $elements as &$element) { + foreach ($elements as &$element) { $i = array_search($element['name'], $this->survey_APF_fields); - if ( $i !== false ) { + if ($i !== false) { // append to preserve existing tags. This can result in duplicate @DEFAULT action tags but there appear to be no affects from this $element['action_tag_class'] .= " @DEFAULT"; unset($this->survey_APF_fields[$i]); - if ( empty($this->survey_APF_fields) ) break; + if (empty($this->survey_APF_fields)) break; } } } @@ -71,7 +75,8 @@ function redcap_survey_page_top($project_id) { * - Piping on choice selection fields now returns the option key instead * of the option label. */ - function setDefaultValues() { + function setDefaultValues() + { global $Proj, $user_rights, $double_data_entry; // Storing old metadata. @@ -119,7 +124,7 @@ function setDefaultValues() { GROUP BY event_id ORDER BY log_event_id "; - + $params = [$_GET['id'], $Proj->project_id]; $result = $this->query($sql, $params); @@ -128,8 +133,7 @@ function setDefaultValues() { $events[] = $row['event_id']; } } - } - else { + } else { $arm = $Proj->eventInfo[$_GET['event_id']]['arm_num']; $events = array_keys($Proj->events[$arm]['events']); } @@ -138,131 +142,128 @@ function setDefaultValues() { $fields = empty($_GET['page']) ? $Proj->metadata : $Proj->forms[$_GET['page']]['fields']; $entry_num = ($double_data_entry && $user_rights['double_data'] != 0) ? '--' . $user_rights['double_data'] : ''; - if (isset($fields)) - { - foreach (array_keys($fields) as $field_name) { - $field_info = $Proj->metadata[$field_name]; - $misc = $field_info['misc']; + if (isset($fields)) { + foreach (array_keys($fields) as $field_name) { + $field_info = $Proj->metadata[$field_name]; + $misc = $field_info['misc']; - // Getting available action tags. - $action_tags = $this->getMultipleActionTagsQueue($action_tags_to_look, $misc); - if (empty($action_tags)) { - continue; - } + // Getting available action tags. + $action_tags = $this->getMultipleActionTagsQueue($action_tags_to_look, $misc); + if (empty($action_tags)) { + continue; + } - $default_value = ''; + $default_value = ''; + + // Looping over @DEFAULT_ and @DEFAULT-FROM-PREVIOUS-EVENT_ + // action tags. + foreach ($action_tags as $action_tag) { + if (strpos($action_tag, '@DEFAULT-FROM-PREVIOUS-EVENT') === 0) { + if (!$source_field = self::getValueInActionTag($misc, $action_tag)) { + // If no value is provided on the action tag, set the same + // field as source by default. + $source_field = $field_name; + } elseif (!isset($Proj->metadata[$source_field])) { + // Invalid field. + continue; + } - // Looping over @DEFAULT_ and @DEFAULT-FROM-PREVIOUS-EVENT_ - // action tags. - foreach ($action_tags as $action_tag) { - if (strpos($action_tag, '@DEFAULT-FROM-PREVIOUS-EVENT') === 0) { - if (!$source_field = self::getValueInActionTag($misc, $action_tag)) { - // If no value is provided on the action tag, set the same - // field as source by default. - $source_field = $field_name; - } - elseif (!isset($Proj->metadata[$source_field])) { - // Invalid field. - continue; - } + $prev_event = false; + $source_form = $Proj->metadata[$source_field]['form_name']; - $prev_event = false; - $source_form = $Proj->metadata[$source_field]['form_name']; + // Getting previous event ID. + foreach ($events as $event) { + if ($event == $_GET['event_id']) { + break; + } + // assign a data type to prevent PHP warning using null coalescing operator + $Proj->eventsForms[$event] ??= []; - // Getting previous event ID. - foreach ($events as $event) { - if ($event == $_GET['event_id']) { - break; + if (in_array($source_form, $Proj->eventsForms[$event])) { + $prev_event = $event; + } } - // assign a data type to prevent PHP warning using null coalescing operator - $Proj->eventsForms[$event] ??= []; - - if (in_array($source_form, $Proj->eventsForms[$event])) { - $prev_event = $event; + + if (!$prev_event) { + continue; } - } - if (!$prev_event) { - continue; - } + // Getting previous event value. + if (isset($data[$prev_event][$source_field])) { + $default_value = $data[$prev_event][$source_field]; + } elseif (isset($data['repeat_instances'][$prev_event][""])) { + // Handling repeat events by using the most recent instance of the previous event to source values + $most_recent_instance = array_slice($data['repeat_instances'][$prev_event][""], -1)[0]; + $default_value = $most_recent_instance[$source_field]; + } - // Getting previous event value. - if (isset($data[$prev_event][$source_field])) { - $default_value = $data[$prev_event][$source_field]; - } elseif (isset($data['repeat_instances'][$prev_event][""])) { - // Handling repeat events by using the most recent instance of the previous event to source values - $most_recent_instance = array_slice($data['repeat_instances'][$prev_event][""], -1)[0]; - $default_value = $most_recent_instance[$source_field]; + // Handling checkboxes case. + if (is_array($default_value)) { + $default_value = implode(',', array_keys(array_filter($default_value))); + } + } else { + if ($default_value = Form::getValueInQuotesActionTag($misc, $action_tag)) { + // Steping ahead REDCap and piping strings in our way. + $default_value = Piping::replaceVariablesInLabel($default_value, $_GET['id'] . $entry_num, $_GET['event_id'], $_GET['instance'], array(), false, null, false); + } } - // Handling checkboxes case. - if (is_array($default_value)) { - $default_value = implode(',', array_keys(array_filter($default_value))); - } - } - else { - if ($default_value = Form::getValueInQuotesActionTag($misc, $action_tag)) { - // Steping ahead REDCap and piping strings in our way. - $default_value = Piping::replaceVariablesInLabel($default_value, $_GET['id'] . $entry_num, $_GET['event_id'], $_GET['instance'], array(), false, null, false); + if (empty($default_value) && !is_numeric($default_value)) { + continue; } - } - if (empty($default_value) && !is_numeric($default_value)) { - continue; - } - - // Date data must follow Y-M-D regardless of how it is validated - // Piping support makes pulling the data stored in the source field difficult - $field_validation = $aux_metadata[$field_name]['element_validation_type']; - // if starts with 'date' - if (substr($field_validation, 0, 4) == 'date') { - switch($field_validation){ - case 'date_mdy': - $out_format = 'Y-m-d'; - $in_format = '!m-d-Y'; - break; - case 'date_dmy': - $out_format = 'Y-m-d'; - $in_format = '!d-m-Y'; - break; - case 'datetime_mdy': - $out_format = 'Y-m-d H:i'; - $in_format = 'm-d-Y H:i'; - break; - case 'datetime_dmy': - $out_format = 'Y-m-d H:i'; - $in_format = 'd-m-Y H:i'; - break; - case 'datetime_seconds_mdy': - $out_format = 'Y-m-d H:i:s'; - $in_format = 'm-d-Y H:i:s'; - break; - case 'datetime_seconds_dmy': - $out_format = 'Y-m-d H:i:s'; - $in_format = 'd-m-Y H:i:s'; - break; - default: - // break 2 or continue 2 do not continue after parent if - $in_format = 'ymd'; - break; - } - if ($in_format !== 'ymd') { - $date = \DateTime::createFromFormat($in_format, $default_value); - // This ternary prevents crashing for mixed source and target formats (e.g. YMD -> DMY) - // users will get validation errors - $default_value = $date ? $date->format($out_format) : $default_value; + // Date data must follow Y-M-D regardless of how it is validated + // Piping support makes pulling the data stored in the source field difficult + $field_validation = $aux_metadata[$field_name]['element_validation_type']; + // if starts with 'date' + if (substr($field_validation, 0, 4) == 'date') { + switch ($field_validation) { + case 'date_mdy': + $out_format = 'Y-m-d'; + $in_format = '!m-d-Y'; + break; + case 'date_dmy': + $out_format = 'Y-m-d'; + $in_format = '!d-m-Y'; + break; + case 'datetime_mdy': + $out_format = 'Y-m-d H:i'; + $in_format = 'm-d-Y H:i'; + break; + case 'datetime_dmy': + $out_format = 'Y-m-d H:i'; + $in_format = 'd-m-Y H:i'; + break; + case 'datetime_seconds_mdy': + $out_format = 'Y-m-d H:i:s'; + $in_format = 'm-d-Y H:i:s'; + break; + case 'datetime_seconds_dmy': + $out_format = 'Y-m-d H:i:s'; + $in_format = 'd-m-Y H:i:s'; + break; + default: + // break 2 or continue 2 do not continue after parent if + $in_format = 'ymd'; + break; + } + if ($in_format !== 'ymd') { + $date = \DateTime::createFromFormat($in_format, $default_value); + // This ternary prevents crashing for mixed source and target formats (e.g. YMD -> DMY) + // users will get validation errors + $default_value = $date ? $date->format($out_format) : $default_value; + } } - } - // The first non empty default value wins! - $misc = $this->overrideActionTag('@DEFAULT', $default_value, $misc); - $aux_metadata[$field_name]['misc'] = $misc; - array_push($this->survey_APF_fields, $field_name); + // The first non empty default value wins! + $misc = $this->overrideActionTag('@DEFAULT', $default_value, $misc); + $aux_metadata[$field_name]['misc'] = $misc; + array_push($this->survey_APF_fields, $field_name); - break; + break; + } } } - } // Now that pipings are done, let's restore original project metadata. $Proj->metadata = $aux_metadata; @@ -282,14 +283,15 @@ function setDefaultValues() { * * @see js/default_when_visible.js */ - function setDefaultWhenVisible() { + function setDefaultWhenVisible() + { $equations = array(); - list($branching_fields, ) = (function_exists('getBranchingFields')) ? + list($branching_fields,) = (function_exists('getBranchingFields')) ? getBranchingFields($_GET['page']) : \DataEntry::getBranchingFields($_GET['page']); foreach ($branching_fields as $field => $equation) { - list($equations[$field], ) = LogicTester::formatLogicToJS($equation, false, $_GET['event_id'], true); + list($equations[$field],) = LogicTester::formatLogicToJS($equation, false, $_GET['event_id'], true); } // More current versions of REDCap do not have all js libraries loaded in time @@ -305,7 +307,8 @@ function setDefaultWhenVisible() { * @return bool * TRUE if the current form contains data, FALSE otherwise. */ - function currentFormHasData() { + function currentFormHasData() + { global $double_data_entry, $user_rights, $question_by_section, $pageFields; $record = $_GET['id']; @@ -339,7 +342,8 @@ function currentFormHasData() { * @return array * The sorted list of the fetched action tags. */ - function getMultipleActionTagsQueue($action_tags, $subject) { + function getMultipleActionTagsQueue($action_tags, $subject) + { $results = []; if (is_string($action_tags)) { @@ -404,13 +408,13 @@ function getMultipleActionTagsQueue($action_tags, $subject) { * @return string * The overriden subject string. */ - function overrideActionTag($key, $value, $subject, $append_if_not_exists = true) { + function overrideActionTag($key, $value, $subject, $append_if_not_exists = true) + { if (strpos($subject, $key . '=') !== false) { // Override action tag if exists. $regex = "/(' . $key . '\s*=\s*)((\"[^\"]+\")|(\'[^\']+\'))/"; $subject = preg_replace($regex, $key . '="' . $value . '"', $subject); - } - elseif ($append_if_not_exists) { + } elseif ($append_if_not_exists) { $subject .= ' ' . $key . '="' . $value . '"'; } @@ -423,7 +427,8 @@ function overrideActionTag($key, $value, $subject, $append_if_not_exists = true) * @param string $path * The relative path to the js file. */ - protected function includeJs($path) { + protected function includeJs($path) + { echo ''; } @@ -435,12 +440,14 @@ protected function includeJs($path) { * @param mixed $value * The setting value. */ - protected function setJsSetting($key, $value) { + protected function setJsSetting($key, $value) + { // initializeJsObject MUST be run once before this function echo ''; } - protected function initializeJsObject() { + protected function initializeJsObject() + { echo ''; } @@ -451,7 +458,8 @@ protected function initializeJsObject() { * * @see Form::getValueInActionTags() */ - public static function getValueInActionTag($subject, $action_tag) { + public static function getValueInActionTag($subject, $action_tag) + { if ($value = Form::getValueInQuotesActionTag($subject, $action_tag)) { return $value; } From 80d74ce8dd722236cf017eb9a0dc3a1216910b4f Mon Sep 17 00:00:00 2001 From: Philip Chase Date: Mon, 23 Sep 2024 12:01:10 -0400 Subject: [PATCH 5/5] Bump VERSION and update CHANGELOG.md for release 2.6.5 --- CHANGELOG.md | 5 ++++- VERSION | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e14ac63..c377f29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ -# auto_populate_fields 2.6.4 (released 2024-09-23) +# auto_populate_fields 2.6.5 (released 2024-09-23) - Run code formatter on ExternalModule.php (@pbchase, #70) - Fix typo in the module (@saipavan10-git, #70) - Fix #66 (@saipavan10-git, #70) - Fix the bug introduced by adding null coalescing (@saipavan10-git, #70) +# auto_populate_fields 2.6.4 (released 2024-09-23) +- null release + # auto_populate_fields 2.6.3 (released 2024-09-16) - Add zenodo.json and update the authors block in config (@saipavan10-git, #67, #68) - Fix #66 (@saipavan10-git, #66, #68) diff --git a/VERSION b/VERSION index 2714f53..57cf282 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.4 +2.6.5