Skip to content

Commit

Permalink
Merge branch 'release/3.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
pbchase committed Jun 3, 2018
2 parents c1cab06 + 36089df commit a554015
Show file tree
Hide file tree
Showing 12 changed files with 1,752 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
All notable changes to the Form Render Skip Logic module will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [3.1.0] - 2018-06-02
### Added
- Add support for data piping and smart variables in place of a control field. (Tiago Bember Simeao & Philip Chase)


## [3.0.0] - 2018-05-31
### Added
- Add support for multiple control fields. (Tiago Bember Simeao & Philip Chase)
Expand Down
50 changes: 45 additions & 5 deletions ExternalModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Project;
use Records;
use Survey;
use RCView;
use REDCap;

/**
Expand All @@ -24,7 +25,7 @@ class ExternalModule extends AbstractExternalModule {
*/
function redcap_every_page_top($project_id) {
if (strpos(PAGE, 'ExternalModules/manager/project.php') !== false) {
$this->setJsSettings(array('modulePrefix' => $this->PREFIX));
$this->setJsSettings(array('modulePrefix' => $this->PREFIX, 'helperButtons' => $this->getPipingHelperButtons()));
$this->includeJs('js/config.js');
$this->includeCss('css/config.css');

Expand Down Expand Up @@ -111,8 +112,13 @@ function getFormsAccessMatrix($arm, $record = null) {

$i = 0;
foreach ($settings['control_fields'] as $cf) {
if (!$cf['control_event_id'] || !$cf['control_field_key']) {
// Checking for required fields.
if ($cf['control_mode'] == 'default' && (!$cf['control_event_id'] || !$cf['control_field_key'])) {
// Checking for required fields in default mode.
continue;
}

if ($cf['control_mode'] == 'advanced' && !$cf['control_piping']) {
// Checking for required fields in advanced mode.
continue;
}

Expand Down Expand Up @@ -167,8 +173,16 @@ function getFormsAccessMatrix($arm, $record = null) {
$a = $cf['control_default_value'];
$b = $cf['condition_value'];

if (isset($data[$ev][$fd]) && Records::formHasData($id, $Proj->metadata[$fd]['form_name'], $ev)) {
$a = $data[$ev][$fd];
if ($cf['control_mode'] == 'advanced') {
$piped = Piping::replaceVariablesInLabel($cf['control_piping'], $id, $event_id, 1, array(), true, null, false);
if ($piped !== '') {
$a = $piped;
}
}
else {
if (isset($data[$ev][$fd]) && Records::formHasData($id, $Proj->metadata[$fd]['form_name'], $ev)) {
$a = $data[$ev][$fd];
}
}

switch ($cf['condition_operator']) {
Expand Down Expand Up @@ -405,6 +419,32 @@ protected function setJsSettings($settings) {
echo '<script>formRenderSkipLogic = ' . json_encode($settings) . ';</script>';
}

/**
* Gets Piping helper buttons.
*/
protected function getPipingHelperButtons() {
global $lang;

$this->includeCss('css/piping-helper.css');
$buttons = array(
'green' => array(
'callback' => 'smartVariableExplainPopup',
'contents' => '[<i class="fas fa-bolt fa-xs"></i>] ' . $lang['global_146'],
),
'purple' => array(
'callback' => 'pipingExplanation',
'contents' => RCView::img(array('src' => APP_PATH_IMAGES . 'pipe.png')) . $lang['info_41'],
),
);

$output = '';
foreach ($buttons as $color => $btn) {
$output .= RCView::button(array('class' => 'btn btn-xs btn-rc' . $color . ' btn-rc' . $color . '-light', 'onclick' => $btn['callback'] . '(); return false;'), $btn['contents']);
}

return RCView::span(array('class' => 'frsl-piping-helper'), $output);
}

/**
* Auxiliary function for getFormattedSettings().
*/
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ See the original functional specification at [https://docs.google.com/document/d
## Configuration
Access **Manage External Modules** section of your project, click on Form Render Skip Logic's configure button, and save settings in order to show or hide instruments according to your needs.

The top level entry in the configuration is a Control Field. A control field is described by an event name and a field name. Each control field can govern the display of a set of forms. You can define multiple control fields as long as each controls a separate set of forms.
The top level entry in the configuration is a Control Field. A control field is described by either an event name and a field name _or_ a smart variable _or_ piped data. Each control field can govern the display of a set of forms. You can define multiple control fields as long as each controls a separate set of forms.

Each control field can have multiple conditions. Each condition compares the control field to a string or number. If the condition evaluates as true, the forms listed under the condition will be displayed. If the condition is false and no other true condition displays them, the forms will be hidden. Be careful that the values in the conditions of a control field are mutually exclusive or the results could be unexpected.

Expand All @@ -36,6 +36,8 @@ The image below shows a sample configuration where the control field is named `r

![module configuration screen](img/configuration_form.png)

See [Animal Identification Example](samples/Animal_Identification.md) for a working example of a project that uses FRSL.

## Upgrading From Version 2.x - 3.x

Note that version 3.0.0 introduced a breaking change in the configuration. To execute the upgrade you will need to follow these steps:
Expand Down
20 changes: 20 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@
"type": "sub_settings",
"repeatable": true,
"sub_settings": [
{
"key": "control_mode",
"name": "Control mode",
"type": "radio",
"choices": [
{
"value": "default",
"name": "Default (Control field)"
},
{
"value": "advanced",
"name": "Advanced (Piping)"
}
]
},
{
"key": "control_event_id",
"name": "Field",
Expand All @@ -62,6 +77,11 @@
"name": "Field",
"type": "field-list"
},
{
"key": "control_piping",
"name": "Piping",
"type": "text"
},
{
"key": "control_default_value",
"name": "Default/fallback value",
Expand Down
4 changes: 4 additions & 0 deletions css/config.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
#external-modules-configure-modal.frsl tr > td:first-child {
width: 40%;
}

.frsl-piping-helper {
margin-left: 10px;
}
17 changes: 17 additions & 0 deletions css/piping-helper.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.frsl-piping-helper button {
margin-right: 6px;
font-size: 11px;
padding: 0px 3px 1px;
line-height: 14px;
}

.frsl-piping-helper .btn-rcgreen i {
margin: 0 1px;
}

.frsl-piping-helper .btn-rcpurple img {
width: 12px;
position: relative;
top: -1px;
margin-right: 2px;
}
57 changes: 52 additions & 5 deletions js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ $(document).ready(function() {

$modal.addClass('frsl');

var branchingLogic = function($checkbox) {
var branchingLogicCheckboxes = function($checkbox) {
var prefix = $checkbox.attr('name').replace('_select', '');
$target = $modal.find('select[name^="' + prefix + '"]').parent().parent();

Expand All @@ -31,20 +31,67 @@ $(document).ready(function() {
}
};

var branchingLogicRadios = function($radio) {
$radio.prop('checked', true);

var suffix = '____' + $radio.attr('name').slice(-1);
var selectorShow = '[name="control_event_id' + suffix + '"], [name="control_field_key' + suffix + '"]';
var selectorHide = '[name="control_piping' + suffix + '"]';

if ($radio.val() === 'advanced') {
var aux = selectorShow;
selectorShow = selectorHide;
selectorHide = aux;
}

$(selectorShow).parent().parent().show();
$(selectorHide).parent().parent().hide();
};

var $checkboxes = $modal.find('tr[field="target_events_select"] .external-modules-input-element');
$checkboxes.each(function() {
branchingLogic($(this));
branchingLogicCheckboxes($(this));
});

$checkboxes.change(function() {
branchingLogic($(this));
branchingLogicCheckboxes($(this));
});

$modal.find('tr[field="control_mode"]').each(function() {
var defaultValue = 'default';
$(this).find('.external-modules-input-element').each(function() {
if (typeof this.attributes.checked !== 'undefined') {
defaultValue = $(this).val();
return false;
}
});

branchingLogicRadios($(this).find('.external-modules-input-element[value="' + defaultValue + '"]'));
});

$modal.find('tr[field="control_mode"] .external-modules-input-element').change(function() {
branchingLogicRadios($(this));
});

$modal.find('tr[field="control_piping"] td:first-child').each(function() {
if ($(this).find('.frsl-piping-helper').length === 0) {
$(this).append(formRenderSkipLogic.helperButtons);
}
});

$modal.find('tr[field="control_field_key"], tr[field="condition_value"]').each(function() {
$(this).find('.external-modules-input-element').position({
if (!$(this).is(':visible')) {
return;
}

var $element = $(this).find('.external-modules-input-element');
var $target = $(this).prev().find('.external-modules-input-element');

$element.css('width', ($target.parent().width() - $target.outerWidth(true) - 10) + 'px');
$element.position({
my: 'left+10 top',
at: 'right top',
of: $(this).prev().find('.external-modules-input-element')[0],
of: $target[0],
collision: "none"
});
});
Expand Down
Binary file added samples/Animal_ID_FRSL_config_page_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/Animal_ID_FRSL_config_page_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/Animal_ID_record_status_dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions samples/Animal_Identification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Animal Identification sample project

To build an example project that uses Form Render Skip Logic to control form display, create a REDCap project using the [Animal Identification Sample Project](Animal_Identification.xml).

Enable Form Render Skip logic on your new project and configure it as shown in these images:

![](Animal_ID_FRSL_config_page_1.png)

![](Animal_ID_FRSL_config_page_2.png)

The record status dashboard should look like this:

![](Animal_ID_record_status_dashboard.png)
Loading

0 comments on commit a554015

Please sign in to comment.