Skip to content

Commit

Permalink
Bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
lsgs committed Jul 26, 2022
1 parent c5461a7 commit efd3bcd
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 55 deletions.
8 changes: 5 additions & 3 deletions AutonumberGenerators/AbstractAutonumberGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ abstract class AbstractAutonumberGenerator {
protected static $RequireDAG = false;
protected static $RequiredFields = array();
protected $config;
protected $module;
private $retryDelay = 1;
private $totalRetryTime = 0;

public function __construct($config) {
public function __construct($config, $module) {
try {
$this->config = $config;
$this->validateConfiguration();
$this->config = $config;
$this->module = $module;
$this->validateConfiguration();
} catch (RecordAutonumberException $ex) {
throw new AutonumberConfigException('Module configuration error for AutonumberGenerator option '.static::getClassNameWithoutNamespace().': "'.$ex->getMessage().'"', 0, $ex);
}
Expand Down
4 changes: 2 additions & 2 deletions AutonumberGenerators/AutonumberGeneratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
* @author luke.stevens
*/
class AutonumberGeneratorFactory {
public static function make($className, $config) {
public static function make($className, $config, $module) {

$dir = dirname(__FILE__);
$classFile = $dir.'/'.$className.'.php';
if (file_exists($classFile)) {
require_once $className.'.php';
$classWithNS = __NAMESPACE__.'\\'.$className;//'MCRI\\RecordAutonumber\\'.$className;
$ang = new $classWithNS($config);
$ang = new $classWithNS($config, $module);
} else {
$msg = "ERROR in ".__CLASS__.": Class '$className' not found";
REDCap::logEvent($msg);
Expand Down
7 changes: 7 additions & 0 deletions AutonumberGenerators/DAGIncrement.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ public function getNextRecordId($params=null) {

// ensure dag is for project
$dagId = $_POST['__GROUPID__'];
if (empty($dagId)) {
$sql = "select group_id from redcap_user_rights where project_id=? and username=?"; // group id not posted e.g. on schedulin page, try to detect from user
$q = $this->module->query($sql, [PROJECT_ID,USERID]);
$result = db_fetch_assoc($q);
$dagId = $result["group_id"];
if (empty($dagId)) throw new AutonumberMissingInputException('could not detect group_id');
}
$dagUniqueName = REDCap::getGroupNames(false, $dagId);
if ($dagUniqueName===false) {
throw new AutonumberMissingInputException('bad group_id '.$dagId);
Expand Down
11 changes: 7 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
All notable changes to the Record Autonumbering module will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [1.0.5] - 2022-07-26
- Update to framework version 8, min REDCap version 11.1.1
- Remove use of built-in constants in constructor for PHP8 compatibility
- Prevent lock/esig for unsaved records because record id does can not necessarily be determined
- Handle record auto-numbering when creating a record via the Scheduling page
- Attempt to utilise REDCap::reserveNewRecordId() but backed out - adds to cache but returns false
- Fix Project Setup page default dialog text and auto-edit module settings on Setup button click

## [0.0.503] - 2021-04-20
### Added
- Add Taryn Stoff's user guide to README (Kyle Chesney)


## [0.0.502] - 2021-03-22
### Added
- Revise module description in config.json to align with README (Philip Chase)
Expand All @@ -16,17 +22,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Fix crash of module when using "padded integer increment with prefix" caused by abstract method getRequiredDataEntryFields needing declaration (Kyle Chesney)
- Ensure module is enabled before initializing to prevent REDCap core's autonumbering enabling in *every* project (Kyle Chesney)


## [0.0.501] - 2020-12-09
### Added
- Redirect with js (Luke)


## [0.0.301] - 2019-05-23
### Added
- Update our repository with luke's 0.0.3 changes, namely, record_autonumber_v0.0.3 (Luke)


## [0.0.101] - 2019-04-19
### Summary
- This is the first release.
Expand Down
104 changes: 74 additions & 30 deletions RecordAutonumber.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* REDCap External Module: Record Autonumber
* REDCap External Module: Custom Record Autonumbering
* Specify record auto-numbering rules e.g. including part of DAG name, date format
* Does not work for records created via survey response or randomisation.
* @author Luke Stevens, Murdoch Children's Research Institute
Expand Down Expand Up @@ -32,6 +32,8 @@ class RecordAutonumber extends AbstractExternalModule
const MODULE_VARNAME = 'MCRI_Record_Autonumber';
const DAG_ELEMENT_NAME = '__GROUPID__';
const RAND_BUTTON_REPLACEMENT_TEXT = 'The project configuration for auto-numbering records requires that records be saved prior to randomization.';
const PROJECT_SETUP_DIALOG_TEXT = 'REDCap\'s built-in record auto-numbering is being overridden by the "Custom Record Auto-numbering" External Module.';
const UNSAVED_RECORD_LOCK_TEXT = 'Record must be saved prior to locking when using the \"Custom Record Auto-numbering\" External Module.';

private $autonumberGenerator;

Expand All @@ -44,11 +46,12 @@ class RecordAutonumber extends AbstractExternalModule

public function __construct() {
parent::__construct();
if (!(defined('PAGE') && defined('PROJECT_ID') && defined('USERID'))) return;
global $Proj, $lang, $user_rights;
$this->page = PAGE;
$this->project_id = intval(PROJECT_ID);
$this->super_user = SUPER_USER;
$this->user = strtolower(USERID);
$this->page = defined('PAGE') ? PAGE : '';
$this->project_id = $this->getProjectId();
$this->super_user = defined('SUPER_USER') ? SUPER_USER : false;
$this->user = defined('USERID') ? strtolower(USERID) : '';
$this->Proj = $Proj;
$this->lang = &$lang;
$this->user_rights = &$user_rights;
Expand Down Expand Up @@ -80,7 +83,8 @@ public function __construct() {
if ($autonumberClassName!=='') { // ... and is configured
$this->autonumberGenerator = AutonumberGeneratorFactory::make(
$autonumberClassName,
$autonumberSettings
$autonumberSettings,
$this // give the autonumber generator a reference to the module so can utilise module methods e.g. query()
);
}
} catch (AutonumberConfigException $e) {
Expand All @@ -91,11 +95,13 @@ public function __construct() {

function redcap_module_project_enable($version, $project_id) {
// Turn record autonumbering on if it is not already on
global $Proj;
if (!$Proj->project['auto_inc_set']) {
$sql = "update redcap_projects set auto_inc_set=1 where auto_inc_set=0 and project_id=".db_escape($project_id);
$q = db_query($sql);
$nrows = db_affected_rows();
if ($nrows==1) {
$sql = "update redcap_projects set auto_inc_set=1 where auto_inc_set=0 and project_id=? limit 1";
$query = $this->createQuery();
$query->add($sql, [$project_id]);
$query->execute();
if ($query->affected_rows==1) {
\Logging::logEvent($sql,"redcap_projects","MANAGE",$project_id,"project_id = $project_id","Modify project settings");
}
}
Expand All @@ -104,23 +110,22 @@ function redcap_module_project_enable($version, $project_id) {
/**
* redcap_every_page_top
* Perform three functions:
* 1. ProjectSetup page: alter "auto-numbering" button to indicate
* usage of this module.
* 2. ExternalModules/manager/project auto-open config when selected
* "Configure" from ProjectSetup page button
* 3. Detect auto-number generation error and display dialog box to
* inform user
* 1. ProjectSetup page: alter "auto-numbering" button to indicate usage of this module.
* 2. ExternalModules/manager/project auto-open config when selected "Configure" from ProjectSetup page button
* 3. Detect auto-number generation error and display dialog box to inform user
* 4. If scheduling page, add JS to populate new record id in text box
* @param int project_id
*/
public function redcap_every_page_top($project_id) {

if (defined('USERID') && isset($this->project_id) && $this->project_id > 0) {
if (isset($this->user) && isset($this->project_id) && $this->project_id > 0) {
if (strpos($this->page, 'ProjectSetup/index.php')!==false) {
$this->includeProjectSetupPageContent();
} else if (strpos($this->page, 'ExternalModules/manager/project.php')>0) {
} else if (strpos('ExternalModules/manager/project.php', $this->page)!==false) {
$this->includeModuleManagerPageContent();
}
else if ($this->page==='DataEntry/record_home.php' && isset($_GET['id']) && isset($_GET['auto'])) {
} else if (strpos('Calendar/scheduling.php', $this->page)!==false) {
$this->includeSchedulingPageContent();
} else if ($this->page==='DataEntry/record_home.php' && isset($_GET['id']) && isset($_GET['auto'])) {

if (isset($_GET['arm']) && array_key_exists($_GET['arm'], $this->Proj->events)) {
$armFirstEventId = key($this->Proj->events[$_GET['arm']]['events']);
Expand Down Expand Up @@ -153,13 +158,26 @@ public function redcap_every_page_top($project_id) {
* @param int project_id
*/
public function redcap_every_page_before_render($project_id) {
// is this is a new data entry record (not survey) that is not yet saved?
// is this is a new data entry record (not survey) that is not yet saved
//, or new record via Generate Schedule?
$newType = false;
$pkField = REDCap::getRecordIdField();
if ($this->page==='DataEntry/index.php' && isset($_POST['submit-action']) && $_POST['submit-action']!=='submit-btn-cancel') {
$pkField = REDCap::getRecordIdField();
$postRec = $_POST[$pkField];
$newType = 'DE';
$postRec = $_POST[$pkField];
} else if ($this->page==='Calendar/scheduling_ajax.php' && isset($_GET['action']) && $_GET['action']==='adddates' && isset($_GET['newid']) && $_GET['newid']=='1') {
$newType = 'GS';
$postRec = $_GET['idnumber'];
}

if ($newType!==false) {
if (isset($postRec) && !$this->recordExists($postRec)) {
try {
$_POST[$pkField] = $this->getNextRecordId();
$newRecordId = $this->getNextRecordId();
if ($newType==='DE') $_POST[$pkField] = $newRecordId;
if ($newType==='GS') $_GET['idnumber'] = $newRecordId;
global $auto_inc_set;
$auto_inc_set = 0;
} catch (AutonumberGenerateFailedException $e) {
$msg = 'Could not generate record number in the expected format: '.$e->getMessage();
$this->handleAutonumberException($postRec, $msg);
Expand All @@ -173,7 +191,6 @@ public function redcap_every_page_before_render($project_id) {
unset($_GET['auto']);
// continue and save record (with default, temp id if could not generate)...
}

}
}

Expand Down Expand Up @@ -236,7 +253,7 @@ protected function getNextRecordId() {
throw new AutonumberGenerateFailedException('Invalid record auto-numbering module configuration.');
}
do {
$nextId = $this->autonumberGenerator->getNextRecordId();
$nextId = $this->autonumberGenerator->getNextRecordId(); // $nextId = REDCap::reserveNewRecordId($this->project_id, $this->autonumberGenerator->getNextRecordId()); // v1.1.0: using like this does not work
if ($this->autonumberGenerator->idMatchesExpectedPattern($nextId) &&
!$this->recordExists($nextId)) {
return $nextId;
Expand All @@ -260,7 +277,8 @@ protected function includeMessagePopup($message) {
protected function includeProjectSetupPageContent() {
// Change the "Record autonumbering" enable/disble button to point to the module config page
$moduleConfigUrl = APP_PATH_WEBROOT.'ExternalModules/manager/project.php?pid='.$this->project_id.'&autonumber_config=1';
$disabled = (SUPER_USER || $this->user_rights['design']==='1') ? '' : 'disabled=""';
$disabled = ($this->super_user || $this->user_rights['design']==='1') ? '' : 'disabled=""';
$dialogText = REDCap::escapeHtml($this->getSystemSetting('project-setup-dialog-text') ?? self::PROJECT_SETUP_DIALOG_TEXT);
?>
<div id="emAutoNumConfigDiv" style="text-indent:-75px;margin-left:75px;margin-bottom:2px;color:green;display:none;">
<button class="btn btn-defaultrc btn-xs fs11" style="min-width:49px;" id="emAutoNumConfigBtn" <?=$disabled?>><?=$this->lang['rights_142']?></button>
Expand All @@ -276,7 +294,7 @@ protected function includeProjectSetupPageContent() {
});
$('#emAutoNumQuestionDialog').click(function() {
simpleDialog(
'<div title="<?php echo REDCap::escapeHtml($this->getModuleName());?>"><?php echo REDCap::escapeHtml($this->getProjectSetting('project-setup-dialog-text'));?></div>',
'<div title="<?php echo REDCap::escapeHtml($this->getModuleName());?>"><?=$dialogText?></div>',
'<i class="fas fa-cube mr-1"></i><?php echo REDCap::escapeHtml($this->getModuleName());?>'
);
});
Expand All @@ -296,27 +314,53 @@ protected function includeModuleManagerPageContent() {
if (isset($_GET['autonumber_config'])) {
?>
<script type="text/javascript">
/*Custom Record Autonumbering auto-config*/
$(window).on('load', function() {
history.pushState({}, null, location.href.split("&autonumber_config")[0]);
$('tr[data-module="record_autonumber"] button.external-modules-configure-button').trigger('click');
setTimeout(function() {
$('tr[data-module="record_autonumber"] button.external-modules-configure-button').trigger('click');
}, 1000);
});
</script>
<?php
}
}

/**
* Scheduling page content
* Get next record id and replace value in 'new record id' text box
*/
protected function includeSchedulingPageContent() {
try {
$nextId = $this->getNextRecordId();
} catch (RecordAutonumberException $e) {
return;
}
?>
<script type="text/javascript">
/*Custom Record Autonumbering*/
$(document).ready(function() {
$('#idnumber2').val('<?=$nextId?>');
});
</script>
<?php
}

/**
* Tweaks to data entry form
* 1. Hide display of temporary record id
* 2. Prevent submit when essential data (e.g. DAG) is missing
* 3. Hide lock/esig buttons and show message
*/
protected function includeDataEntryPageContent() {
global $Proj;
$pkField = REDCap::getRecordIdField();
$unsavedRecLockText = REDCap::escapeHtml($this->getSystemSetting('unsaved-record-lock-text') ?? self::UNSAVED_RECORD_LOCK_TEXT);
?>
<style type="text/css">
/* Hide the "Save changes and leave" button in the unsaved changes dialog */
.dataEntrySaveLeavePageBtn { display:none; }
#__LOCKRECORD__-tr input { display:none; }
</style>
<script type="text/javascript">
$(document).ready(function() {
Expand All @@ -325,7 +369,7 @@ protected function includeDataEntryPageContent() {
$('#<?php echo $pkField;?>-tr').hide();
$('div.menuboxsub').hide();
$('div.formMenuList').hide();

$('#__LOCKRECORD__-tr td:nth-child(2)').html('<div class="yellow"><?=$unsavedRecLockText?></div>');
});
</script>
<?php
Expand Down
26 changes: 10 additions & 16 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
{
"name": "Custom Record Auto-numbering",

"namespace": "MCRI\\RecordAutonumber",

"authors": [
{
"name": "Luke Stevens",
"email": "luke.stevens@mcri.edu.au",
"institution": "Murdoch Children's Research Institute"
}
],

"description": "Allows users to create a custom record auto-numbering schema for their REDCap project. This works during data entry but not in public surveys or API/CSV data import.",

"framework-version": 6,

"framework-version": 8,
"compatibility": {
"redcap-version-min": "10.4.1"
"redcap-version-min": "11.1.1"
},

"permissions": [
"redcap_add_edit_records_page",
"redcap_data_entry_form_top",
Expand All @@ -27,21 +21,21 @@
"redcap_save_record",
"redcap_module_project_enable"
],

"enable-every-page-hooks-on-system-pages": false,

"links": {
},

"system-settings": [
{
"key": "project-setup-dialog-text",
"name": "Project Setup page dialog box text",
"required": true,
"name": "Project Setup page dialog box text<p class=\"text-muted\">Default:<br>\"REDCap's built-in record auto-numbering is being overridden by the \"Custom Record Auto-numbering\" External Module.\"</p>",
"required": false,
"type": "text"
},
{
"key": "unsaved-record-lock-text",
"name": "Text for Lock option of data entry form for unsaved record<p class=\"text-muted\">Default:<br>\"Record must be saved prior to locking when using the \"Custom Record Auto-numbering\" External Module.\"</p>",
"required": false,
"type": "text"
}
],

"project-settings": [
{
"key": "descriptive-text-1",
Expand Down

0 comments on commit efd3bcd

Please sign in to comment.