/**
  * Get the list of parameters for this form.
  * @return array List of parameters that this form requires.
  */
 public static function get_parameters()
 {
     $retVal = array_merge(parent::get_parameters(), array(array('name' => 'observer_name', 'caption' => 'Observer Name', 'description' => 'Indicia ID for the sample attribute that is the name of the observer.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'observer_email', 'caption' => 'Observer Email', 'description' => 'Indicia ID for the sample attribute that is the email of the observer.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'observer_phone_number', 'caption' => 'Observer Phone Number', 'description' => 'Indicia ID for the sample attribute that is the phone number of the observer.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'start_time', 'caption' => 'Start time', 'description' => 'Indicia ID for the sample attribute that records the start time of the watch.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'end_time', 'caption' => 'End time', 'description' => 'Indicia ID for the sample attribute that records the end time of the watch.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'cetaceans_seen', 'caption' => 'Cetaceans seen?', 'description' => 'Indicia ID for the sample attribute that records whether Cetaceans have been seen.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'cetaceans_seen_yes', 'caption' => 'Cetaceans Seen Yes Answer', 'description' => 'Indicia ID for the termlists_term that stores the Yes answer for Cetaceans Seen?.', 'type' => 'string', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'sea_state', 'caption' => 'Sea state?', 'description' => 'Indicia ID for the sample attribute that records sea state.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'visibility', 'caption' => 'Visibility?', 'description' => 'Indicia ID for the sample attribute that records visibility.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'non_cetacean_marine_animals_seen', 'caption' => 'Non cetacean marine animals seen?', 'description' => 'Indicia ID for the sample attribute that records whether non-cetacean marine animals have been seen.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'feeding_birds_seen', 'caption' => 'Feeding birds seen?', 'description' => 'Indicia ID for the sample attribute that records whether feeding birds have been seen.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'number_of_people_spoken_to_during_watch', 'caption' => 'Number of people spoken to during watch?', 'description' => 'Indicia ID for the sample attribute that records the number of people spoken to during the watch.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'wdcs_newsletter', 'caption' => 'WDCS newsletter opt-in', 'description' => 'Indicia ID for the sample attribute that records whether a guest has chosen to receive
         the WDCS newsletter.', 'type' => 'select', 'table' => 'sample_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Sample Attributes'), array('name' => 'bearing_to_sighting', 'caption' => 'Bearing to sighting', 'description' => 'Indicia ID for the occurrence attribute that stores the bearing to the sighting.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'reticules', 'caption' => 'Reticules', 'description' => 'Indicia ID for the occurrence attribute that holds the number of reticules.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'reticules_from', 'caption' => 'Reticules from', 'description' => 'Indicia ID for the occurrence attribute that stores whether the Reticules value
         is from the land or sky.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'distance_estimate', 'caption' => 'Distance Estimate', 'description' => 'Indicia ID for the occurrence attribute that stores the distance estimate to the sighting.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'adults', 'caption' => 'Adults', 'description' => 'Indicia ID for the occurrence attribute that stores the number of adults associated with the sighting.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'calves', 'caption' => 'Calves', 'description' => 'Indicia ID for the occurrence attribute that stores the number of Calves associated with the sighting.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'activity', 'caption' => 'Activity', 'description' => 'Indicia ID for the occurrence attribute that stores whether a sighting is travelling or staying.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Occurrence Attributes'), array('name' => 'behaviour', 'caption' => 'Behaviour', 'description' => 'Indicia ID for the occurrence attribute that stores whether a sighting is calm or active.', 'type' => 'select', 'table' => 'occurrence_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Occurrence Attributes'), array('name' => 'platform_height', 'caption' => 'Platform Height', 'description' => 'Indicia ID for the location attribute that stores the platform height.', 'type' => 'select', 'table' => 'location_attribute', 'valueField' => 'id', 'captionField' => 'caption', 'group' => 'Shorewatch Location Attributes'), array('name' => 'effort', 'caption' => 'Effort', 'description' => 'Indicia ID for the termlists_term that stores the effort sample method id.', 'type' => 'select', 'table' => 'termlists_term', 'valueField' => 'id', 'captionField' => 'term', 'group' => 'Shorewatch Sample Methods'), array('name' => 'reticule_sighting', 'caption' => 'Reticule Sighting', 'description' => 'Indicia ID for the termlists_term that stores the reticule sighting sample method id.', 'type' => 'select', 'table' => 'termlists_term', 'valueField' => 'id', 'captionField' => 'term', 'group' => 'Shorewatch Sample Methods'), array('name' => 'efforts_survey_id', 'caption' => 'Efforts And Online Recording Survey ID', 'description' => 'Indicia ID for the efforts and online recording survey (not adhoc).', 'type' => 'string', 'group' => 'Survey Ids'), array('name' => 'efforts_and_online_recording_page_path', 'caption' => 'Efforts And Online Recording Page Path', 'description' => 'Path to the Efforts And Online Recording page.', 'type' => 'string', 'group' => 'Recording Page Paths'), array('name' => 'ahoc_online_recording_page_path', 'caption' => 'Adhoc Online Recording Page Path', 'description' => 'Path to the Adhoc Online Recording page.', 'type' => 'string', 'group' => 'Recording Page Paths'), array('name' => 'keep_going_after_error', 'caption' => 'Continue import after issues detected?', 'description' => 'The import is processed on a line-by-line basis. Does the import stop or try importing the rest of the data if problems with the data are detected? ' . 'Note that data may have already been entered into the database before the issue occurred. ' . 'Leaving this option on may result in inconsistent data being entered into the database depending on when the problem occurred during processing. ' . 'Note also that errors that occur on the Warehouse rather than in the importer\'s submission builder during processing are unaffected by this option.', 'type' => 'boolean', 'default' => false, 'group' => 'Import Mode'), array('name' => 'presetSettings', 'caption' => 'Preset Settings', 'description' => 'Provide a list of predetermined settings which the user does not need to specify, one on each line in the form name=value. ' . 'The preset settings available are those which are available for input on the first page of the import wizard, depending on the table you ' . 'are inputting data for. You can use the following replacement tokens in the values: {user_id}, {username}, {email} or {profile_*} (i.e. any ' . 'field in the user profile data).', 'type' => 'textarea', 'required' => false)));
     return $retVal;
 }
 /**
  * Override the get_form_html function.
  * getForm in dynamic.php will now call this.
  * Vary the display of the page based on the interface type
  * 
  * @package    Client
  * @subpackage PrebuiltForms
  */
 protected static function get_form_html($args, $auth, $attributes)
 {
     if (empty($_GET['occurrence_id'])) {
         return 'This form requires an occurrence_id parameter in the URL.';
     } else {
         data_entry_helper::$javascript .= 'indiciaData.username = "******"\";\n";
         data_entry_helper::$javascript .= 'indiciaData.user_id = "' . hostsite_get_user_field('indicia_user_id') . "\";\n";
         data_entry_helper::$javascript .= 'indiciaData.website_id = ' . $args['website_id'] . ";\n";
         data_entry_helper::$javascript .= 'indiciaData.ajaxFormPostUrl="' . iform_ajaxproxy_url(null, 'occurrence') . "&sharing=reporting\";\n";
         return parent::get_form_html($args, $auth, $attributes);
     }
 }
 /**
  * Override get_form_html so we can store the remembered argument in a global, to make
  * it available to a hook function which exists outside the form.
  */
 protected static function get_form_html($args, $auth, $attributes)
 {
     // We always want an autocomplete formatter function for species lookups..
     call_user_func(array(self::$called_class, 'build_grid_autocomplete_function'), $args);
     global $remembered;
     $remembered = isset($args['remembered']) ? $args['remembered'] : '';
     return parent::get_form_html($args, $auth, $attributes);
 }
 protected static function get_control_spatialreference($auth, $args, $tabalias, $options)
 {
     $options = array_merge($options, array('fieldname' => 'location:centroid_sref'));
     return parent::get_control_spatialreference($auth, $args, $tabalias, $options);
 }
Example #5
0
 /**
  * Return the generated form output.
  * @return Form HTML.
  */
 public static function get_form($args, $node)
 {
     data_entry_helper::$website_id = $args['website_id'];
     if (!empty($args['high_volume']) && $args['high_volume']) {
         // node level caching for most page hits
         $cached = data_entry_helper::cache_get(array('node' => $node->nid), HIGH_VOLUME_CACHE_TIMEOUT);
         if ($cached !== false) {
             $cached = explode('|!|', $cached);
             data_entry_helper::$javascript = $cached[1];
             data_entry_helper::$late_javascript = $cached[2];
             data_entry_helper::$onload_javascript = $cached[3];
             data_entry_helper::$required_resources = json_decode($cached[4], true);
             return $cached[0];
         }
     }
     self::$node = $node;
     self::$called_class = 'iform_' . $node->iform;
     // Convert parameter, defaults, into structured array
     self::parse_defaults($args);
     // Supply parameters that may be missing after form upgrade
     if (method_exists(self::$called_class, 'getArgDefaults')) {
         $args = call_user_func(array(self::$called_class, 'getArgDefaults'), $args);
     }
     // Get authorisation tokens to update and read from the Warehouse. We allow child classes to generate this first if subclassed.
     if (self::$auth) {
         $auth = self::$auth;
     } else {
         $auth = data_entry_helper::get_read_write_auth($args['website_id'], $args['password']);
         self::$auth = $auth;
     }
     // Determine how the form was requested and therefore what to output
     $mode = method_exists(self::$called_class, 'getMode') ? call_user_func(array(self::$called_class, 'getMode'), $args, $node) : '';
     self::$mode = $mode;
     if ($mode === self::MODE_GRID) {
         // Output a grid of existing records
         $r = call_user_func(array(self::$called_class, 'getGrid'), $args, $node, $auth);
     } else {
         if (($mode === self::MODE_EXISTING || $mode === self::MODE_EXISTING_RO || $mode === self::MODE_CLONE) && is_null(data_entry_helper::$entity_to_load)) {
             // only load if not in error situation.
             call_user_func_array(array(self::$called_class, 'getEntity'), array(&$args, $auth));
             // when editing, no need to step through all the pages to save a change.
             if ($mode === self::MODE_EXISTING) {
                 $args['save_button_below_all_pages'] = true;
             }
         }
         // attributes must be fetched after the entity to load is filled in - this is because the id gets filled in then!
         $attributes = method_exists(self::$called_class, 'getAttributes') ? call_user_func(array(self::$called_class, 'getAttributes'), $args, $auth) : array();
         $r = call_user_func(array(self::$called_class, 'get_form_html'), $args, $auth, $attributes);
     }
     if (!empty($args['high_volume']) && $args['high_volume']) {
         $c = $r . '|!|' . data_entry_helper::$javascript . '|!|' . data_entry_helper::$late_javascript . '|!|' . data_entry_helper::$onload_javascript . '|!|' . json_encode(data_entry_helper::$required_resources);
         data_entry_helper::cache_set(array('node' => $node->nid), $c, HIGH_VOLUME_CACHE_TIMEOUT);
     }
     return $r;
 }
 /**
  * Override get_form_html so we can store the remembered argument in a global, to make
  * it available to a hook function which exists outside the form.
  */
 protected static function get_form_html($args, $auth, $attributes)
 {
     group_authorise_form($args, $auth['read']);
     // We always want an autocomplete formatter function for species lookups. The form implementation can
     // specify its own if required
     if (method_exists(self::$called_class, 'build_grid_autocomplete_function')) {
         call_user_func(array(self::$called_class, 'build_grid_autocomplete_function'), $args);
     } else {
         $opts = array('cacheLookup' => $args['cache_lookup'], 'speciesIncludeBothNames' => $args['species_include_both_names'], 'speciesIncludeTaxonGroup' => $args['species_include_taxon_group'], 'speciesIncludeIdDiff' => $args['species_include_id_diff']);
         data_entry_helper::build_species_autocomplete_item_function($opts);
     }
     global $remembered;
     $remembered = isset($args['remembered']) ? $args['remembered'] : '';
     if (empty(data_entry_helper::$entity_to_load['sample:group_id']) && !empty($_GET['group_id'])) {
         data_entry_helper::$entity_to_load['sample:group_id'] = $_GET['group_id'];
     }
     if (!empty(data_entry_helper::$entity_to_load['sample:group_id'])) {
         self::$group = data_entry_helper::get_population_data(array('table' => 'group', 'extraParams' => $auth['read'] + array('view' => 'detail', 'id' => data_entry_helper::$entity_to_load['sample:group_id'])));
         self::$group = self::$group[0];
         $filterdef = json_decode(self::$group['filter_definition']);
         // does the group filter define a site or boundary for the recording? If so we need to show it and limit the map extent
         $locationIDToLoad = empty($filterdef->location_id) ? empty($filterdef->indexed_location_id) ? false : $filterdef->indexed_location_id : $filterdef->location_id;
         if ($locationIDToLoad) {
             $response = data_entry_helper::get_population_data(array('table' => 'location', 'extraParams' => $auth['read'] + array('id' => $locationIDToLoad, 'view' => 'detail')));
             $geom = $response[0]['boundary_geom'] ? $response[0]['boundary_geom'] : $response[0]['centroid_geom'];
             iform_map_zoom_to_geom($geom, lang::get('Boundary of {1} for the {2} group', $response[0]['name'], self::$group['title']), true);
             self::hide_other_boundaries($args);
         } elseif (!empty($filterdef->searchArea)) {
             iform_map_zoom_to_geom($filterdef->searchArea, lang::get('Recording area for the {1} group', self::$group['title']), true);
             self::hide_other_boundaries($args);
         }
         if (!empty($filterDef->taxon_group_names)) {
             $args['taxon_filter'] = implode("\n", array_values((array) $filterdef->taxon_group_names));
             $args['taxon_filter_field'] = 'taxon_group';
         }
         // @todo Consider other types of species filter, e.g. family or species list?
     }
     return parent::get_form_html($args, $auth, $attributes);
 }
 /**
  * Get the list of parameters for this form.
  * @return array List of parameters that this form requires.
  */
 public static function get_parameters()
 {
     $retVal = array_merge(parent::get_parameters(), array(array('name' => 'structure', 'caption' => 'Form Structure', 'description' => 'Define the structure of the form. Each component goes on a new line and is nested inside the previous component where appropriate. The following types of ' . "component can be specified. <br/>" . "<strong>=tab/page name=</strong> is used to specify the name of a tab or wizard page. (Alpha-numeric characters only)<br/>" . "<strong>=*=</strong> indicates a placeholder for putting any custom attribute tabs not defined in this form structure. <br/>" . "<strong>[control name]</strong> indicates a predefined control is to be added to the form with the following predefined controls available: <br/>" . "&nbsp;&nbsp;<strong>[first name]</strong> - a text box to enter the person's first name.<br/>" . "&nbsp;&nbsp;<strong>[surname]</strong> - a text box to enter the person's surname.<br/>" . "&nbsp;&nbsp;<strong>[email address]</strong> - a text box to enter the person's email address.<br/>" . "<strong>@option=value</strong> on the line(s) following any control allows you to override one of the options passed to the control. The options " . "available depend on the control. For example @label=Last Name would set the untranslated label of a control to Last Name. Where the " . "option value is an array, use valid JSON to encode the value. For example an array of strings could be passed as @extraParams=[\"value1\",\"value2\"] " . "or a keyed array as @extraParams={\"preferred\":\"true\",\"orderby\":\"term\"}. " . "Other common options include helpText (set to a piece of additional text to display alongside the control) and class (to add css " . "classes to the control such as control-width-3). <br/>" . "<strong>[*]</strong> is used to make a placeholder for putting any custom attributes that should be inserted into the current tab. When this option is " . "used, you can change any of the control options for an individual custom attribute control by putting @control|option=value on the subsequent line(s). " . "For example, if a control is for psnAttr:4 then you can update it's label by specifying @psnAttr:4|label=New Label on the line after the [*].<br/>" . "<strong>[psnAttr:<i>n</i>]</strong> is used to insert a particular custom attribute identified by its ID number<br/>" . "<strong>@helpText</strong> is used to define help text to add to the tab, e.g. @helpText=Enter the surname of the person. <br/>" . "<strong>all else</strong> is copied to the output html so you can add structure for styling.", 'type' => 'textarea', 'default' => "=Information=\r\n" . "[first name]\r\n" . "[surname]\r\n" . "[email address]\r\n" . "[*]\r\n" . "=*=", 'group' => 'User Interface'), array('name' => 'defaults', 'caption' => 'Default Values', 'description' => 'Supply default values for each field as required. On each line, enter fieldname=value. For custom attributes, ' . 'the fieldname is the untranslated caption. For other fields, it is the model and fieldname, e.g. person.first_name. ', 'type' => 'textarea', 'required' => false)));
     return $retVal;
 }
 /**
  * Override the get_form to fetch our own auth tokens. This skips the write auth as it is unnecessary, 
  * which makes the tokens cachable therefore faster. It does mean that $auth['write'] will not be available.
  */
 public static function get_form($args, $node)
 {
     $conn = iform_get_connection_details($node);
     self::$auth = array('read' => data_entry_helper::get_read_auth($conn['website_id'], $conn['password']));
     return parent::get_form($args, $node);
 }
 /**
  * Override the get_form_html function.
  * getForm in dynamic.php will now call this.
  * Vary the display of the page based on the interface type
  * 
  * @package    Client
  * @subpackage PrebuiltForms
  */
 protected static function get_form_html($args, $auth, $attributes)
 {
     if (isset($_POST['enable'])) {
         module_enable(array('iform_ajaxproxy'));
         drupal_set_message(lang::get('The Indicia AJAX Proxy module has been enabled.', 'info'));
     }
     if (!defined('IFORM_AJAXPROXY_PATH')) {
         $r = '<p>' . lang::get('The Indicia AJAX Proxy module must be enabled to use this form. This lets the form save verifications to the ' . 'Indicia Warehouse without having to reload the page.') . '</p>';
         $r .= '<form method="post">';
         $r .= '<input type="hidden" name="enable" value="t"/>';
         $r .= '<input type="submit" value="' . lang::get('Enable Indicia AJAX Proxy') . '"/>';
         $r .= '</form>';
         return $r;
     }
     if (empty($_GET['taxa_taxon_list_id']) && empty($_GET['taxon_meaning_id'])) {
         return 'This form requires a taxa_taxon_list_id or taxon_meaning_id parameter in the URL.';
     }
     self::get_names($auth);
     hostsite_set_page_title(lang::get('Summary details for {1}', self::$preferred));
     return parent::get_form_html($args, $auth, $attributes);
 }
 /**
  * Return the generated form output.
  * @param array $args List of parameter values passed through to the form depending on how the form has been configured.
  * This array always contains a value for language.
  * @param object $node The Drupal node object.
  * @param array $response When this form is reloading after saving a submission, contains the response from the service call.
  * Note this does not apply when redirecting (in this case the details of the saved object are in the $_GET data).
  * @return Form HTML.
  * @todo: Implement this method 
  */
 public static function get_form($args, $node, $response = null)
 {
     if (empty($args['group_id'])) {
         drupal_set_message('Please specify a group_id in the page configuration.');
     }
     if (empty($args['instructions_configuration'])) {
         drupal_set_message('Please provide a page configuration in the User Interface options.');
     }
     //Only perform if the user has specified an instruction to appear under each page like.
     if (!empty($args['instructions_configuration'])) {
         $configuration = data_entry_helper::explode_lines($args['instructions_configuration']);
         $key = '';
         $description = '';
         //Keep track of the ordering of the titles
         $titleNumber = 0;
         //For each configured line we need to find all the descriptions and store them against the page titles in an array
         foreach ($configuration as $configLineNum => $configurationLine) {
             //If line is a link title (specified inside square brackets)
             if (preg_match('/^\\[.+\\]$/', $configurationLine)) {
                 //If this isn't the first title, then we need to store the description for the previous title into an
                 //array. The key is a number representing the order of the titles in the configuration, the sub array key is the name of the page link.
                 if (!empty($key)) {
                     $titleDescriptions[$titleNumber] = array($key => $description);
                     $description = '';
                 }
                 //Get the next array key we will use from the specified page link title. Chop the square brackets off the ends.
                 $key = substr($configurationLine, 1, -1);
                 $titleNumber++;
             } else {
                 //If the line does not use square brackets then we know it is part of the description/instruction. We do an
                 //append as the instruction might span several lines.
                 $description .= $configurationLine;
             }
         }
         //For the last description we still need to save it to the array.
         $titleDescriptions[$titleNumber] = array($key => $description);
         $description = '';
     }
     $r = '';
     global $user;
     $auth = data_entry_helper::get_read_write_auth($args['website_id'], $args['password']);
     //Get all the links to display
     $reportOptions = array('dataSource' => 'library/groups/groups_list', 'readAuth' => $auth['read'], 'mode' => 'report', 'extraParams' => array('currentUser' => hostsite_get_user_field('indicia_user_id'), 'id' => $args['group_id'], 'pending_path' => '{rootFolder}?q=groups/pending&group_id=', 'userFilterMode' => 'member'));
     // automatic handling for Drupal clean urls.
     $pathParam = function_exists('variable_get') && variable_get('clean_url', 0) == '0' ? 'q' : '';
     $rootFolder = helper_base::getRootFolder() . (empty($pathParam) ? '' : "?{$pathParam}=");
     $groupsData = data_entry_helper::get_report_data($reportOptions);
     if (empty($groupsData)) {
         if (!empty($args['no_group_found_message'])) {
             $r = '<div>' . $args['no_group_found_message'] . '</div>';
         } else {
             $r = '<div>' . 'Sorry, you do not appear to be a member of this group so there are no links to display.' . '</div>';
         }
         return $r;
     }
     foreach ($groupsData as $groupDataItem) {
         $pageLinks = $groupDataItem['pages'];
         $groupTitle = $groupDataItem['title'];
     }
     //All the page links come out of the database in one cluster. Explode these so we have each link separately
     $explodedPageLinks = explode('</a>', $pageLinks);
     // reinsert the closing </a> used in the explode above
     foreach ($explodedPageLinks as &$pageLink) {
         $pageLink .= '</a>';
     }
     $pageLinkHtml = '';
     //Go through all the page links to display
     foreach ($titleDescriptions as $titleDescArr) {
         foreach ($explodedPageLinks as &$pageLink) {
             //Each page link is a html link, we just want the plain name
             $plainPageLink = trim(strip_tags($pageLink));
             //If the user has specified an instruction/description for the page link, then display the instruction in the lines following the link
             //using italics.
             if (array_key_exists($plainPageLink, $titleDescArr)) {
                 if (!empty($titleDescArr[$plainPageLink])) {
                     $pageLinkHtml .= "<h3>{$pageLink}</a></h3><p>{$titleDescArr[$plainPageLink]}</p>\n";
                 } else {
                     $pageLinkHtml .= "<h3>{$pageLink}</a></h3>";
                 }
             }
         }
     }
     $r = "<div><h2>{$groupTitle} Links</h2>";
     $r .= str_replace(array('{rootFolder}', '{sep}'), array($rootFolder, strpos($rootFolder, '?') === FALSE ? '?' : '&'), $pageLinkHtml);
     $r .= '</div><br>';
     $r .= parent::get_form($args, $node, $response = null);
     return $r;
 }