diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e1fbc6..8b6bcc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/ExternalModule.php b/ExternalModule.php index 3ead3a8..8c0998e 100644 --- a/ExternalModule.php +++ b/ExternalModule.php @@ -13,6 +13,7 @@ use Project; use Records; use Survey; +use RCView; use REDCap; /** @@ -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'); @@ -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; } @@ -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']) { @@ -405,6 +419,32 @@ protected function setJsSettings($settings) { echo ''; } + /** + * Gets Piping helper buttons. + */ + protected function getPipingHelperButtons() { + global $lang; + + $this->includeCss('css/piping-helper.css'); + $buttons = array( + 'green' => array( + 'callback' => 'smartVariableExplainPopup', + 'contents' => '[] ' . $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(). */ diff --git a/README.md b/README.md index b72f5c6..9a95597 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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: diff --git a/config.json b/config.json index 0efcf20..43fd061 100644 --- a/config.json +++ b/config.json @@ -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", @@ -62,6 +77,11 @@ "name": "Field", "type": "field-list" }, + { + "key": "control_piping", + "name": "Piping", + "type": "text" + }, { "key": "control_default_value", "name": "Default/fallback value", diff --git a/css/config.css b/css/config.css index 897374d..f91af39 100644 --- a/css/config.css +++ b/css/config.css @@ -17,3 +17,7 @@ #external-modules-configure-modal.frsl tr > td:first-child { width: 40%; } + +.frsl-piping-helper { + margin-left: 10px; +} diff --git a/css/piping-helper.css b/css/piping-helper.css new file mode 100644 index 0000000..e4de992 --- /dev/null +++ b/css/piping-helper.css @@ -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; +} diff --git a/js/config.js b/js/config.js index 4fbe71d..154a933 100644 --- a/js/config.js +++ b/js/config.js @@ -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(); @@ -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" }); }); diff --git a/samples/Animal_ID_FRSL_config_page_1.png b/samples/Animal_ID_FRSL_config_page_1.png new file mode 100644 index 0000000..e3416b1 Binary files /dev/null and b/samples/Animal_ID_FRSL_config_page_1.png differ diff --git a/samples/Animal_ID_FRSL_config_page_2.png b/samples/Animal_ID_FRSL_config_page_2.png new file mode 100644 index 0000000..96e8e16 Binary files /dev/null and b/samples/Animal_ID_FRSL_config_page_2.png differ diff --git a/samples/Animal_ID_record_status_dashboard.png b/samples/Animal_ID_record_status_dashboard.png new file mode 100644 index 0000000..7211e69 Binary files /dev/null and b/samples/Animal_ID_record_status_dashboard.png differ diff --git a/samples/Animal_Identification.md b/samples/Animal_Identification.md new file mode 100644 index 0000000..d24131b --- /dev/null +++ b/samples/Animal_Identification.md @@ -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) diff --git a/samples/Animal_Identification.xml b/samples/Animal_Identification.xml new file mode 100644 index 0000000..8e946b5 --- /dev/null +++ b/samples/Animal_Identification.xml @@ -0,0 +1,1593 @@ + + + + + Animal Identification + This file contains the metadata, events, and data for REDCap project "Animal Identification". + Animal Identification + 1 + [event_1_arm_1][sex] - [event_1_arm_1][weight] + + 0 + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Record ID + + + Date of collection + + + Animal species + + + + Animal classification + + + + Animal order + + + + Animal weight + + + Animal's sex + + + + Complete? + + + + Average life span (in years) + + + Average litter size + + 1 + The value you provided is outside the suggested range. (1 - no limit). This value is admissible, but you may wish to verify. + + + + Form of reproduction + + + + Type of asexual reproduction + + + + Type of sexual reproduction + + + + Reproductive strategy + + + + Mode of reproduction + + + + Main diet type + + + + Lifestyle + + + + Complete? + + + + Cat breed + + + + Cat color + + + + Cat length + + + Cat height + + + Fur length + + + + Main diet + + + + Average litter size + + + + Complete? + + + + Length (inches) + + + Jellyfish taxonomy + + + + Jellyfish class + + + + Habitat + + + + Number of tentacles + + 1 + The value you provided is outside the suggested range. (1 - no limit). This value is admissible, but you may wish to verify. + + + + Toxicity + + + + Complete? + + + + Frog length (inches) + + + Frog type + + + + Frog color + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Type of reproduction + + + + Locomotion type(s) + + + + Locomotion type(s) + + + + Locomotion type(s) + + + + Locomotion type(s) + + + + Locomotion type(s) + + + + Locomotion type(s) + + + + Locomotion type(s) + + + + Complete? + + + + Genera + + + + Sailfish species + + + + Makaira species + + + + Tetrapturus species + + + + Length (feet) + + + Average swimming speed (mph) + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main diet + + + + Main habitat location + + + + Main habitat location + + + + Main habitat location + + + + Main habitat location + + + + Main habitat location + + + + Complete? + + + + Length (cm) + + + Circumference + + + Snake is: + + + + Main color + + + + Common name + + + + Main diet + + + + Main habitat + + + + Complete? + + + + Wing span (cm) + + + Migration range (miles) + + + Family + + + + Type of butterfly + + + + Main diet + + + + Main colors + + + + Main colors + + + + Main colors + + + + Main colors + + + + Main colors + + + + Main colors + + + + Main colors + + + + Main colors + + + + Main colors + + + + Complete? + + + + Height (cm) + + + Wing span (cm) + + + Type of parrot + + + + Main color(s) + + + + Main color(s) + + + + Main color(s) + + + + Main color(s) + + + + Main color(s) + + + + Main color(s) + + + + Main color(s) + + + + Main color(s) + + + + Complete? + + + + In rut? + + + + Complete? + + + + Pregnant or Gravid + + + + In Estrus? + + + + Complete? + + + + A form for big critters + + + Complete? + + + + A form for small critters + + + Complete? + + + + Cat + Jellyfish + Frog + Marlin + Snake + Butterfly + Parrot + + + Vertebrate + Invertibrate + + + Reptile + Fish + Amphibian + Bird + Mammal + Insect + Coelenterate + + + Male + Female + N/A + + + Incomplete + Unverified + Complete + + + Sexual + Asexual + Both + + + Cloning + Binary fission + Budding + Vegetative reproduction + Conjugation + + + Allogamy + Autogamy + Mitosis + Meiosis + + + K-selection (few offspring) + R-selection (many offspring) + + + Polycyclic - reproduce intermittently throughout their lives. + Semelparous - reproduce only once in their lifetime. + Iteroparous - produce offspring in successive (e.g. annual or seasonal) cycles. + + + Carnivore + Omnivore + Herbivore + + + Solitary + Social + + + Incomplete + Unverified + Complete + + + Abyssinian + American Bobtail + American Curl + American Shorthair + American Wirehair + Balinese + Bengal + Birman + Bombay + British Shorthair + Burmese + California Spangled Cat + Chartreux + Colorpoint Shorthair + Cornish Rex + Cymric + Devon Rex + Egyptian Mau + European Burmese + Exotic Shorthair + Havana Brown + Himalayan + Japanese Bobtail + Javanese + Korat + Maine Coon + Manx + Munchkin + Nebelung + Norwegian Forest Cat + Ocicat + Oriental + Persian + Ragdoll + Randombred Cat + Russian Blue + Scottish Fold + Selkirk Rex + Siamese + Siberian + Singapura + Snowshoe + Somali + Sphynx + Tiffany/Chantilly + Tonkinese + Turkish Angora + Turkish Van + York Chocolate + + + White + Black + Gray + Orange + Tabby + Tuxedo + Multi + Other + + + Short + Medium + Long + + + Wet food + Dry food + Combination + + + 1 + 2 + 3 + 4 + 5 + 6 + More than 6 + + + Incomplete + Unverified + Complete + + + Cotylorhiza tuberculata + Marivagia stellata + Aequorea forskalea + Aurelia aurita + Carybdea marsupialis + Cassiopea andromeda + Catostylus tagi + Chrysaora hysoscella + Drymonema dalmatinum + Mnemiopsis leidyi + Olindias phosphorica + Pelagia noctiluca + Phyllorhiza punctata + Physalia physalis + Porpita porpita + Rhizostoma pulmo + Rhopilema nomadica + Thalia democratica + Velella velella + + + Scyphozoa + Cubozoa + Hydrozoa + Staurozoa + + + Freshwater + Saltwater + Both + + + Poisonous + Non poisonous + + + Incomplete + Unverified + Complete + + + Centralian Burrowing-Frog + Boreal Chorus Frog + Bridled Frog + Broad-palmed Frog + Broad-palmed Rocket Frog + Bumpy Rocket Frog + Cascade Tree Frog + Centralian Tree Frog + Common Mist Frog + Cope's Gray Treefrog + Creek Frog + Dahl's Aquatic Frog + Dainty Tree Frog + Desert Tree Frog + Eastern Cricket Frog + Eastern Dwarf Tree Frog + Eastern Sedge Frog + Emerald-spotted Tree Frog + Floodplain Frog + Graceful Tree Frog + Great Barred Frog + Green Frog + Green Tree Frog + Green-eyed Tree Frog + Kuranda Tree Frog + Laughing Tree Frog + Mottled Barred Frog + Northern Barred Frog + Northern Green Frog + Northern Laughing Tree Frog + Northern Stony-creek Frog + Ornate Burrowing-Frog + Pearson's Tree Frog + Peron's Tree Frog + Red-eyed Tree Frog + Rocket Frog + Roth's Tree Frog + Southern Cricket Frog + Southern Orange-eyed Tree Frog + Stony Creek Frog + Striped Rocket Frog + Tarahumara Frog + Tawny Rocket Frog + Torrent Tree Frog + Tusked Frog + Waterfall Frog + White's Tree Frog + Wood Frog + + + Green + Brown + Spotted + Red + Orange + Blue + Yellow + Other + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Prolonged breeding + Explosive breeding + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Incomplete + Unverified + Complete + + + Swordfish + Sailfish + Istiompax + Makaria + Tetrapturus + + + Atlantic salifish + Indo-Pacific sailfish + + + Indo-Pacific blue marlin + Atlantic blue marlin + + + White marlin + Shortbill spearfish + Striped marlin + Roundscale spearfish + Mediterranean spearfish + Longbill spearfish + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Incomplete + Unverified + Complete + + + Not poisonous + Poisonous + + + Blue + Green + Black + Brown + Red + Yellow + Grey + White + Banded + + + Adder + Aesculapian sname + Anaconda + Arafura file snake + Asp + Ball python + Bird snake + Blind snake + Boa + Boiga + Boomslang + Brown snake + Bull snake + Bushmaster + Dwarf beaked snake + Rufous beaked snake + Canebreak + Cantil + Cascabel + Cascavel + Cat-eyed snake + Cat snake + Chicken snake + Coachwhip snake + Cobra + Collett's snake + Congo snake + Copperhead + Coral snake + Corn snake + Cottonmpouth + Crowned snake + Cuban wood snake + Eastern hognose snake + Egg-eater + Eyelash viper + Eastern coral snake + Fer-de-lance + Fierce snake + Fishing snake + Flying snake + Fox snake + Forest flame snake + Garter snake + Glossy snake + Gopher snake + Grass snake + Ground snake + Habu + Harlequin snake + Herald snake + Hognose snake + Hoop snake + Hundred pacer + Ikaheka snake + Indigo snake + Jamaican snake + Jararacussu + Keelback + King brown + King cobra + King snake + Krait + Large shield snake + Lancehead + Lora + Lyre snake + Machete savane + Mamba + Mamushi + Mangrove snake + Milk snake + Mangrove snake + Milk snake + Mocassin snake + Montpellier snake + Mud snake + Mussurana + Night snake + Parrot snake + Patchnose snake + Perrotet's shieldtail snake + Pine snake + Pipe snake + Python + Queen snake + Racer + Raddysnake + Rat snake + Rattlesnake + Ribbon snake + Rinkhals + River jack + Sea snake + Scott Shields Barrows Deadliest snake + Shield-tailed snake + Sidewinder + Small-eyed snake + Smooth snake + Sonoran + Stiletto snake + Striped snake + Sunbeam snake + Taipan + Tentacled snake + Tic polonga + Tiger snake + Tigre snake + Tree snake + Trinket snake + Twig snake + Twin headed King snake + Titanboa + Urutu + Vine snake + Viper + Wart snake + Water moccasin + Water snake + Whip snake + Wolf snake + Worm snake + Wutu + Xanlder + Yarara + Zebra snake + + + Insects + Rodents + Birds + Frogs + Mammals + Reptiles + Eggs + + + Water + Forest + Desert + Prarie + + + Incomplete + Unverified + Complete + + + Hedylidae + Hesperildae + Lycaenidae + Nymphalidae + Papilionidae + Pieridae + Riodinidae + + + American Snout + Blue Morpho + California Dogface + Garden Tiger + Goliath Birdwing + Julia + Karner Blue + Milbert's Tortoiseshell + Monarch + Mourning Cloak + Painted Lady + Peacock + Postman Butterfly + Queen Alexandra's Birdwing + Red Admiral + Saturn + Southern Dogface + Summer Azure + Tiger Swallowtail + Ulysses + Viceroy + Zebra Swallowtail + + + Flower nectar + Rotting fruit + Rotting animal flesh + Animal fluids + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Incomplete + Unverified + Complete + + + Cockatiels + Lovebirds + Small parakeets + Parrotlets + Caiques + Small conures + Lories + Lorikeets + Large parakeets + Pionus parrots + Poicephalus + African greys + Amazons + Small cockatoos + Large conures + Eclectus + Hawk headed parrots + Mini-macaws + Large cockatoos + Macaws + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Checked + Unchecked + + + Incomplete + Unverified + Complete + + + Yes + No + + + Incomplete + Unverified + Complete + + + Yes + No + + + Yes + No + + + Incomplete + Unverified + Complete + + + Incomplete + Unverified + Complete + + + Incomplete + Unverified + Complete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file