/**
  * 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.
  */
 public static function get_form($args, $node, $response = null)
 {
     $conn = iform_get_connection_details($node);
     data_entry_helper::$js_read_tokens = data_entry_helper::get_read_auth($conn['website_id'], $conn['password']);
     if (!empty($_GET)) {
         self::do_data_services_download($args, $node);
     }
 }
 /**
  * Return the Indicia form code.
  * Expects there to be a sample attribute with caption 'Email' containing the email
  * address.
  * @param array $args Input parameters.
  * @param array $node Drupal node object
  * @param array $response Response from Indicia services after posting a verification.
  * @return HTML string
  */
 public static function get_form($args, $node, $response)
 {
     iform_load_helpers(array('data_entry_helper', 'map_helper', 'report_helper'));
     $auth = data_entry_helper::get_read_write_auth($args['website_id'], $args['password']);
     //Clear Verifier Tasks automatically when they open the screen if the option is set.
     if ($args['clear_verification_task_notifications'] && hostsite_get_user_field('indicia_user_id')) {
         self::clear_verifier_task_notifications($auth);
     }
     // set some defaults, applied when upgrading from a form configured on a previous form version.
     if (empty($args['email_subject_send_to_recorder'])) {
         $args['email_subject_send_to_recorder'] = 'Record of %taxon% requires confirmation (ID:%id%)';
     }
     if (empty($args['email_body_send_to_recorder'])) {
         $args['email_body_send_to_recorder'] = 'The following record requires confirmation. Please could you reply to this email stating how confident you are that the record is correct ' . 'and any other information you have which may help to confirm this.' . "\n\n%record%";
     }
     if (isset($_POST['enable'])) {
         module_enable(array('iform_ajaxproxy'));
         drupal_set_message(lang::get('The Indicia AJAX Proxy module has been enabled.', 'info'));
     } elseif (!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 (function_exists('drupal_add_js')) {
         drupal_add_js('misc/collapse.js');
     }
     // fancybox for popup comment forms etc
     data_entry_helper::add_resource('fancybox');
     data_entry_helper::add_resource('validation');
     global $user, $indicia_templates;
     $indicia_user_id = self::get_indicia_user_id($args);
     data_entry_helper::$js_read_tokens = $auth['read'];
     // Find a list of websites we are allowed verify
     $websiteIds = iform_get_allowed_website_ids($auth['read'], 'verification');
     if (function_exists('module_exists') && module_exists('easy_login')) {
         if (strpos($args['param_presets'] . $args['param_defaults'], 'expertise_location') === false) {
             $args['param_presets'] .= "\nexpertise_location={profile_location_expertise}";
         }
         if (strpos($args['param_presets'] . $args['param_defaults'], 'expertise_taxon_groups') === false) {
             $args['param_presets'] .= "\nexpertise_taxon_groups={profile_taxon_groups_expertise}";
         }
         if (strpos($args['param_presets'] . $args['param_defaults'], 'expertise_surveys') === false) {
             $args['param_presets'] .= "\nexpertise_surveys={profile_surveys_expertise}";
         }
     }
     $args['sharing'] = 'verification';
     $opts = array_merge(iform_report_get_report_options($args, $auth['read']), array('id' => 'verification-grid', 'reportGroup' => 'verification', 'rowId' => 'occurrence_id', 'paramsFormButtonCaption' => lang::get('Filter'), 'paramPrefix' => '<div class="report-param">', 'paramSuffix' => '</div>', 'sharing' => 'verification', 'ajax' => TRUE, 'callback' => 'verificationGridLoaded', 'rowClass' => 'zero-{zero_abundance}'));
     $opts['columns'][] = array('display' => '', 'template' => '<div class="nowrap"><button class="default-button quick-verify tools-btn" type="button" id="quick-{occurrence_id}" title="Record tools">...</button>' . '<input type="hidden" class="row-input-form" value="{rootFolder}{input_form}"/><input type="hidden" class="row-belongs-to-site" value="{belongs_to_site}"/><ul class="verify-tools"><li><a href="#" class="quick-verify-tool">Bulk verify similar records</a></li>' . '<li><a href="#" class="trust-tool">Recorder\'s trust settings</a></li><li><a href="#" class="edit-record">Edit record</a></li></ul>' . '<input type="checkbox" class="check-row no-select" style="display: none" value="{occurrence_id}" /></div>');
     $params = self::report_filter_panel($args, $auth['read']);
     $opts['zoomMapToOutput'] = false;
     $grid = report_helper::report_grid($opts);
     $r = str_replace(array('{grid}', '{paramsForm}'), array($grid, $params), self::get_template_with_map($args, $auth['read'], $opts['extraParams'], $opts['paramDefaults']));
     $link = data_entry_helper::get_reload_link_parts();
     global $user;
     data_entry_helper::$js_read_tokens = $auth['read'];
     data_entry_helper::$javascript .= 'indiciaData.nid = "' . $node->nid . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.username = "******"\";\n";
     data_entry_helper::$javascript .= 'indiciaData.userId = "' . $indicia_user_id . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.rootUrl = "' . $link['path'] . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.website_id = ' . $args['website_id'] . ";\n";
     data_entry_helper::$javascript .= 'indiciaData.ajaxFormPostUrl="' . iform_ajaxproxy_url($node, 'occurrence') . "&user_id={$indicia_user_id}&sharing=verification\";\n";
     data_entry_helper::$javascript .= 'indiciaData.ajaxUrl="' . url('iform/ajax/verification_4') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.autoDiscard = ' . $args['auto_discard_rows'] . ";\n";
     if (!empty($args['indicia_species_layer_feature_type']) && !empty(data_entry_helper::$geoserver_url)) {
         data_entry_helper::$javascript .= "indiciaData.indiciaSpeciesLayer = {\n" . '  "title":"' . lang::get('Online recording data for this species') . "\",\n" . '  "featureType":"' . $args['indicia_species_layer_feature_type'] . "\",\n" . '  "wmsUrl":"' . data_entry_helper::$geoserver_url . "wms\",\n" . '  "cqlFilter":"website_id IN (' . implode(',', $websiteIds) . ') AND ' . $args['indicia_species_layer_filter_field'] . "='{filterValue}'\",\n" . '  "filterField":"' . $args['indicia_species_layer_ds_filter_field'] . "\",\n" . '  "sld":"' . (isset($args['indicia_species_layer_sld']) ? $args['indicia_species_layer_sld'] : '') . "\"\n" . "};\n";
     }
     if (!empty($args['additional_wms_species_layer_title'])) {
         data_entry_helper::$javascript .= 'indiciaData.wmsSpeciesLayers = [{"title":"' . $args['additional_wms_species_layer_title'] . '",' . '"url":"' . $args['additional_wms_species_layer_url'] . '",' . '"settings":' . $args['additional_wms_species_layer_settings'] . ',' . '"olSettings":' . $args['additional_wms_species_layer_ol_settings'] . "}];\n";
     }
     // output some translations for JS to use
     data_entry_helper::$javascript .= "indiciaData.popupTranslations = {};\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.title="' . lang::get('Add {1} comment') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.save="' . lang::get('Save and {1}') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.verbV="' . lang::get('verify') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.verbR="' . lang::get('reject') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.verbD="' . lang::get('query') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.V="' . lang::get('Verification') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.R="' . lang::get('Rejection') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.D="' . lang::get('Query') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.emailTitle="' . lang::get('Email record details for checking') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sendEmail="' . lang::get('Send Email') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.emailSent="' . lang::get('The email was sent successfully.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.requestManualEmail="' . lang::get('The webserver is not correctly configured to send emails. Please send the following email usual your email client:') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.multipleWarning="' . lang::get('You are about to verify multiple records. Please note that this comment will apply to all the ticked records. ' . 'If you did not intend to do this, please close this box and turn off the Select Records tool before proceeding.') . "\";\n";
     data_entry_helper::$javascript .= "indiciaData.statusTranslations = {};\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.V = "' . lang::get('Verified') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.R = "' . lang::get('Rejected') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.D = "' . lang::get('Query') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.I = "' . lang::get('In progress') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.T = "' . lang::get('Test record') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.S = "' . lang::get('Sent for verification') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.statusTranslations.C = "' . lang::get('Awaiting verification') . "\";\n";
     data_entry_helper::$javascript .= "indiciaData.commentTranslations = {};\n";
     data_entry_helper::$javascript .= 'indiciaData.commentTranslations.emailed = "' . lang::get('I emailed this record to {1} for checking.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.commentTranslations.recorder = "' . lang::get('the recorder') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.commentTranslations.expert = "' . lang::get('an expert') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.email_subject_send_to_verifier = "' . $args['email_subject_send_to_verifier'] . "\";\n";
     $body = str_replace(array("\r", "\n"), array('', '\\n'), $args['email_body_send_to_verifier']);
     data_entry_helper::$javascript .= 'indiciaData.email_body_send_to_verifier = "' . $body . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.email_subject_send_to_recorder = "' . $args['email_subject_send_to_recorder'] . "\";\n";
     $body = str_replace(array("\r", "\n"), array('', '\\n'), $args['email_body_send_to_recorder']);
     data_entry_helper::$javascript .= 'indiciaData.email_body_send_to_recorder = "' . $body . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.str_month = "' . lang::get('month') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.expertise_location = "' . $opts['extraParams']['expertise_location'] . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.expertise_surveys = "' . $opts['extraParams']['expertise_surveys'] . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.expertise_taxon_groups = "' . $opts['extraParams']['expertise_taxon_groups'] . "\";\n";
     data_entry_helper::add_resource('jqplot');
     data_entry_helper::add_resource('jqplot_bar');
     return $r;
 }
 /**
  * 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.
  */
 public static function get_form($args, $node, $response = null)
 {
     $conn = iform_get_connection_details($node);
     data_entry_helper::$js_read_tokens = data_entry_helper::get_read_auth($conn['website_id'], $conn['password']);
     if (!empty($_POST) && !empty($_POST['format'])) {
         self::do_data_services_download($args, $node);
     }
     $conn = iform_get_connection_details($node);
     data_entry_helper::$js_read_tokens = data_entry_helper::get_read_auth($conn['website_id'], $conn['password']);
     $types = self::get_download_types($args);
     $formats = self::get_download_formats($args);
     if (count($types) === 0) {
         return 'This download page is configured so that no download type options are available.';
     }
     if (count($formats) === 0) {
         return 'This download page is configured so that no download format options are available.';
     }
     $reload = data_entry_helper::get_reload_link_parts();
     $reloadPath = $reload['path'];
     if (count($reload['params'])) {
         $reloadPath .= '?' . helper_base::array_to_query_string($reload['params']);
     }
     $r = '<form method="POST" action="' . $reloadPath . '">';
     $r .= '<fieldset id="download-type-fieldset"><legend>' . lang::get('Records to download') . '</legend>';
     if (count($types) === 1) {
         $r .= '<input type="hidden" name="download-type" id="download-type" value="' . implode('', array_keys($types)) . '"/>';
         hostsite_set_page_title(lang::get('Download {1}', strtolower(implode('', $types))));
     } else {
         $r .= data_entry_helper::select(array('fieldname' => 'download-type', 'label' => lang::get('Download type'), 'lookupValues' => $types, 'class' => 'control-width-5', 'helpText' => 'Select the type of download you require, i.e. the purpose for the data. This defines which records are available to download.'));
     }
     $r .= data_entry_helper::select(array('fieldname' => 'download-subfilter', 'label' => lang::get('Filter to apply'), 'lookupValues' => array(), 'class' => 'control-width-5', 'helpText' => lang::get('Optionally select from the available filters. Filters you create on the Explore pages will be available here.')));
     $r .= "</fieldset>\n";
     $r .= '<fieldset><legend>' . lang::get('Limit the records') . '</legend>';
     if (empty($args['survey_id'])) {
         // put up an empty surveys drop down. AJAX will populate it.
         $r .= data_entry_helper::select(array('fieldname' => 'survey_id', 'label' => lang::get('Survey to include'), 'helpText' => 'Choose a survey, or <all> to not filter by survey.', 'lookupValues' => array(), 'class' => 'control-width-5'));
     } else {
         $r .= '<input type="hidden" name="survey_id" value="' . $args['survey_id'] . '"/>';
     }
     // Let the user pick the date range to download.
     $r .= data_entry_helper::select(array('label' => lang::get('Date field'), 'fieldname' => 'date_type', 'lookupValues' => array('recorded' => lang::get('Field record date'), 'input' => lang::get('Input date'), 'edited' => lang::get('Last changed date'), 'verified' => 'Verification status change date'), 'helpText' => 'If filtering on date, which date field would you like to filter on?'));
     $r .= data_entry_helper::date_picker(array('fieldname' => 'date_from', 'label' => lang::get('Start Date'), 'helpText' => 'Leave blank for no start date filter', 'class' => 'control-width-4'));
     $r .= data_entry_helper::date_picker(array('fieldname' => 'date_to', 'label' => lang::get('End Date'), 'helpText' => 'Leave blank for no end date filter', 'class' => 'control-width-4'));
     $r .= '</fieldset>';
     if (!empty($args['custom_formats'])) {
         $customFormats = json_decode($args['custom_formats'], true);
         foreach ($customFormats as $idx => $format) {
             if (empty($format['permission']) || user_access($format['permission'])) {
                 $formats["custom-{$idx}"] = lang::get(isset($format['title']) ? $format['title'] : 'Untitled format');
             }
         }
     }
     if (count($formats) > 1) {
         $r .= '<fieldset><legend>' . lang::get('Select a format to download') . '</legend>';
         $keys = array_keys($formats);
         $r .= data_entry_helper::radio_group(array('fieldname' => 'format', 'lookupValues' => $formats, 'default' => $keys[0]));
         $r .= '</fieldset>';
     } else {
         // only allowed 1 format, so no need for a selection control
         $keys = array_keys($formats);
         $r .= '<input type="hidden" name="format" value="' . array_pop($keys) . '"/>';
     }
     $r .= '<input type="submit" value="' . lang::get('Download') . '"/></form>';
     data_entry_helper::$javascript .= 'indiciaData.ajaxUrl="' . url('iform/ajax/easy_download_2') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.nid = "' . $node->nid . "\";\n";
     data_entry_helper::$javascript .= "setAvailableDownloadFilters();\n";
     return $r;
 }
 /**
  * Outputs a panel and "Precheck my records" button. When clicked, the contents of the
  * current form are sent to the warehouse and run through any data cleaner verification
  * rules. The results are then displayed in the panel allowing the user to provide more
  * details for records of interest before submitting the form. Requires the data_cleaner 
  * module to be enabled on the warehouse.
  * The output of this control can be configured using the following templates: 
  * <ul>
  * <li><b>verification_panel</b></br>
  * HTML template used to generate container element for the verification panel.
  * </li>
  * </ul>
  * @global type $indicia_templates
  * @param array $options Options array with the following possibilities:<ul>
  * <li><b>readAuth</b><br/>
  * Read authorisation tokens
  * </ul>
  * <li><b>panelOnly</b><br/>
  * Default false. If true, then the button is ommited and the button's functionality
  * must be included elsewhere on the page.
  * </li>
  * </ul>
  * @return type HTML to insert onto the page for the verification panel.
  */
 public function verification_panel($options)
 {
     global $indicia_templates;
     $options = array_merge(array('panelOnly' => false), $options);
     $button = $options['panelOnly'] ? '' : self::apply_replacements_to_template($indicia_templates['button'], array('href' => '#', 'id' => 'verify-btn', 'class' => 'class="indicia-button"', 'caption' => lang::get('Precheck my records'), 'title' => ''));
     $replacements = array('button' => $button);
     self::add_resource('verification');
     self::$js_read_tokens = $options['readAuth'];
     self::$javascript .= "indiciaData.verifyMessages=[];\n";
     self::$javascript .= "indiciaData.verifyMessages.nothingToCheck='" . lang::get('There are no records on this form to check.') . "';\n";
     self::$javascript .= "indiciaData.verifyMessages.completeRecordFirst='" . lang::get('Before checking, please complete at least the date and grid reference of the record.') . "';\n";
     self::$javascript .= "indiciaData.verifyMessages.noProblems='" . lang::get('Automated verification checks did not find anything of note.') . "';\n";
     self::$javascript .= "indiciaData.verifyMessages.problems='" . lang::get('Automated verification checks resulted in the following messages:') . "';\n";
     self::$javascript .= "indiciaData.verifyMessages.problemsFooter='" . lang::get('A message not mean that there is anything wrong with the record, but if you can provide as much information ' . 'as possible, including photos, then it will help with its confirmation.') . "';\n";
     return self::apply_replacements_to_template($indicia_templates['verification_panel'], $replacements);
 }
Beispiel #5
0
 public static function add_sites_to_any_user($auth, $args, $tabalias, $options, $path)
 {
     //Need to call this so we can use indiciaData.read
     data_entry_helper::$js_read_tokens = $auth['read'];
     if (!function_exists('iform_ajaxproxy_url')) {
         return 'An AJAX Proxy module must be enabled for user sites administration to work.';
     }
     $r = "<form><fieldset><legend>" . lang::get('Add locations to the sites lists for other users') . "</legend>";
     if (empty($options['locationTypes']) || !preg_match('/^([0-9]+,( )?)*[0-9]+$/', $options['locationTypes'])) {
         return 'The sites form is not correctly configured. Please provide the location type you can add.';
     }
     $locationTypes = explode(',', str_replace(' ', '', $options['locationTypes']));
     if (empty($options['mySitesPsnAttrId']) || !preg_match('/^[0-9]+$/', $options['mySitesPsnAttrId'])) {
         return 'The sites form is not correctly configured. Please provide the person attribute ID used to store My Sites.';
     }
     if (!empty($options['locationParamFromURL']) && !empty($_GET[$options['locationParamFromURL']])) {
         $locationIdFromURL = $_GET[$options['locationParamFromURL']];
     } else {
         $locationIdFromURL = 0;
     }
     //If we don't want to automatically get the location id from the URL, then display a drop-down of locations the user can select from
     if (empty($locationIdFromURL)) {
         $r .= '<label>' . lang::get('Location :') . '</label> ';
         //Get a list of all the locations that match the given location types (in this case my sites are returned first, although this isn't a requirement)
         $r .= data_entry_helper::location_select(array('id' => 'location-select', 'nocache' => true, 'report' => 'reports_for_prebuilt_forms/Shorewatch/locations_with_my_sites_first', 'extraParams' => $auth['read'] + array('location_type_ids' => $options['locationTypes'], 'user_id' => hostsite_get_user_field('indicia_user_id'), 'my_sites_person_attr_id' => $options['mySitesPsnAttrId']), 'blankText' => '<' . lang::get('please select') . '>'));
     }
     //Get the user select control
     $r .= self::user_select_for_add_sites_to_any_user_control($auth['read'], $args);
     $r .= '<input id="add-user-site-button" type="button" value="' . lang::get('Add to this User\'s Sites List') . '"/><br></form><br>';
     $postUrl = iform_ajaxproxy_url(null, 'person_attribute_value');
     //Firstly check both a uer and location have been selected.
     //Then get the current user/sites saved in the database and if the new combination doesn't already exist then call a function to add it.
     data_entry_helper::$javascript .= "\n    function duplicateCheck(locationId, userId) {\n      var userIdToAdd = \$('#user-select').val();\n      var locationIdToAdd = locationId;\n      var sitesReport = indiciaData.read.url +'/index.php/services/report/requestReport?report=library/locations/all_user_sites.xml&mode=json&mode=json&callback=?';\n        \n      var sitesReportParameters = {\n        'person_site_attr_id': '" . $options['mySitesPsnAttrId'] . "',\n        'auth_token': indiciaData.read.auth_token,\n        'nonce': indiciaData.read.nonce,\n        'reportSource':'local'\n      };\n        \n      if (!userIdToAdd||!locationIdToAdd) {\n        alert('Please select both a user and a location to add.');\n      } else {\n        \$.getJSON (\n          sitesReport,\n          sitesReportParameters,\n          function (data) {\n            var duplicateDetected=false;\n            \$.each(data, function(i, dataItem) {\n              if (userIdToAdd==dataItem.pav_user_id&&locationIdToAdd==dataItem.location_id) {\n                  duplicateDetected=true;\n              }\n            });\n            if (duplicateDetected===true) {\n              alert('The site/user combination you are adding already exists in the database.');\n            } else {\n              addUserSiteData(locationId, userIdToAdd);\n            }\n          }\n        );\n      }    \n    }\n    ";
     //After duplicate check is performed, add the user/site combination to the person_attribute_values database table
     data_entry_helper::$javascript .= "\n    function addUserSiteData(locationId, userIdToAdd) {\n      if (!isNaN(locationId) && locationId!=='') {\n        \$.post('{$postUrl}', \n          {\"website_id\":" . $args['website_id'] . ",\"person_attribute_id\":" . $options['mySitesPsnAttrId'] . ",\"user_id\":userIdToAdd,\"int_value\":locationId},\n          function (data) {\n            if (typeof data.error === 'undefined') {\n              alert('User site configuration saved successfully');\n              location.reload();\n            } else {\n              alert(data.error);\n            }              \n          },\n          'json'\n        );\n      }\n    }\n    ";
     //Call duplicate check when administrator elects to save a user/site combination
     data_entry_helper::$javascript .= "\n    \$('#add-user-site-button').click(function() {\n      //We can get the location id from the url or from the locations drop-down depending on the option the administrator has set.\n      var locationId;\n      if (" . $locationIdFromURL . ") {\n        locationId = " . $locationIdFromURL . ";\n      } else {\n        locationId = \$('#location-select').val()       \n      }\n      duplicateCheck(locationId,\$('#dynamic-the_user_id').val());\n    });";
     //Zoom map as user selects locations
     data_entry_helper::$javascript .= "\n    \$('#location-select, #location-search, #locality_id').change(function() {\n      if (typeof indiciaData.mapdiv!=='undefined') {\n        indiciaData.mapdiv.locationSelectedInInput(indiciaData.mapdiv, this.value);\n      }\n    });\n    ";
     //Function for when user elects to remove sites
     data_entry_helper::$javascript .= "\n    user_site_delete = function(pav_id) {\n      var userId=\$('#dynamic-the_user_id').val();\n      \$.post('{$postUrl}', \n        {\"website_id\":" . $args['website_id'] . ",\"id\":pav_id, \"deleted\":\"t\"},\n        function (data) {\n          if (typeof data.error === 'undefined') {\n            location.reload(); \n          } else {\n            alert(data.error);\n          }\n        },\n        'json'\n      );\n    }\n    ";
     return $r;
 }
 /**
  * Helper function to generate a species checklist from a given taxon list.
  *
  * Please not that although this is based on the data_entry_helper function, it has only been tested with the following
  * options for seasearch - @id,@useThirdLevelSamples,@lookupListId,@gridIdAttributeId,@speciesControlToUseSubSamples,@subSamplePerRow,@resizeWidth,@resizeHeight
  * If you intend to use any other options, they will require further testing or development.
  *
  */
 private static function species_checklist($options)
 {
     global $indicia_templates;
     data_entry_helper::add_resource('addrowtogrid');
     $options = data_entry_helper::get_species_checklist_options($options);
     $classlist = array('ui-widget', 'ui-widget-content', 'species-grid');
     if (!empty($options['class'])) {
         $classlist[] = $options['class'];
     }
     if ($options['subSamplePerRow']) {
         // we'll track 1 sample per grid row.
         $smpIdx = 0;
     }
     if ($options['columns'] > 1 && count($options['mediaTypes']) > 1) {
         throw new Exception('The species_checklist control does not support having more than one occurrence per row (columns option > 0) ' . 'at the same time has having the mediaTypes option in use.');
     }
     data_entry_helper::add_resource('json');
     data_entry_helper::add_resource('autocomplete');
     $filterArray = data_entry_helper::get_species_names_filter($options);
     $filterNameTypes = array('all', 'currentLanguage', 'preferred', 'excludeSynonyms');
     //make a copy of the options so that we can maipulate it
     $overrideOptions = $options;
     //We are going to cycle through each of the name filter types
     //and save the parameters required for each type in an array so
     //that the Javascript can quickly access the required parameters
     foreach ($filterNameTypes as $filterType) {
         $overrideOptions['speciesNameFilterMode'] = $filterType;
         $nameFilter[$filterType] = data_entry_helper::get_species_names_filter($overrideOptions);
         $nameFilter[$filterType] = json_encode($nameFilter[$filterType]);
     }
     if (count($filterArray)) {
         $filterParam = json_encode($filterArray);
         data_entry_helper::$javascript .= "indiciaData['taxonExtraParams-" . $options['id'] . "'] = {$filterParam};\n";
         // Apply a filter to extraParams that can be used when loading the initial species list, to get just the correct names.
         if (isset($options['speciesNameFilterMode']) && !empty($options['listId'])) {
             $filterFields = array();
             $filterWheres = array();
             self::parse_species_name_filter_mode($options, $filterFields, $filterWheres);
             if (count($filterWheres)) {
                 $options['extraParams'] += array('query' => json_encode(array('where' => $filterWheres)));
             }
             $options['extraParams'] += $filterFields;
         }
     }
     data_entry_helper::$js_read_tokens = $options['readAuth'];
     data_entry_helper::$javascript .= "indiciaData['rowInclusionCheck-" . $options['id'] . "'] = '" . $options['rowInclusionCheck'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData['copyDataFromPreviousRow-" . $options['id'] . "'] = '" . $options['copyDataFromPreviousRow'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData['includeSpeciesGridLinkPage-" . $options['id'] . "'] = '" . $options['includeSpeciesGridLinkPage'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData.speciesGridPageLinkUrl = '" . $options['speciesGridPageLinkUrl'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData.speciesGridPageLinkParameter = '" . $options['speciesGridPageLinkParameter'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData.speciesGridPageLinkTooltip = '" . $options['speciesGridPageLinkTooltip'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData['editTaxaNames-" . $options['id'] . "'] = '" . $options['editTaxaNames'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData['subSpeciesColumn-" . $options['id'] . "'] = '" . $options['subSpeciesColumn'] . "';\n";
     data_entry_helper::$javascript .= "indiciaData['subSamplePerRow-" . $options['id'] . "'] = " . ($options['subSamplePerRow'] ? 'true' : 'false') . ";\n";
     if ($options['copyDataFromPreviousRow']) {
         data_entry_helper::$javascript .= "indiciaData['previousRowColumnsToInclude-" . $options['id'] . "'] = '" . $options['previousRowColumnsToInclude'] . "';\n";
         data_entry_helper::$javascript .= "indiciaData.langAddAnother='" . lang::get('Add another') . "';\n";
     }
     if (count($options['mediaTypes'])) {
         data_entry_helper::add_resource('plupload');
         // store some globals that we need later when creating uploaders
         $relpath = data_entry_helper::getRootFolder() . data_entry_helper::client_helper_path();
         $interim_image_folder = isset(parent::$interim_image_folder) ? parent::$interim_image_folder : 'upload/';
         data_entry_helper::$javascript .= "indiciaData.uploadSettings = {\n";
         data_entry_helper::$javascript .= "  uploadScript: '" . $relpath . "upload.php',\n";
         data_entry_helper::$javascript .= "  destinationFolder: '" . $relpath . $interim_image_folder . "',\n";
         data_entry_helper::$javascript .= "  jsPath: '" . data_entry_helper::$js_path . "'";
         if (isset($options['resizeWidth'])) {
             data_entry_helper::$javascript .= ",\n  resizeWidth: " . $options['resizeWidth'];
         }
         if (isset($options['resizeHeight'])) {
             data_entry_helper::$javascript .= ",\n  resizeHeight: " . $options['resizeHeight'];
         }
         if (isset($options['resizeQuality'])) {
             data_entry_helper::$javascript .= ",\n  resizeQuality: " . $options['resizeQuality'];
         }
         data_entry_helper::$javascript .= "\n}\n";
         if ($indicia_templates['file_box'] != '') {
             data_entry_helper::$javascript .= "file_boxTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box']) . "';\n";
         }
         if ($indicia_templates['file_box_initial_file_info'] != '') {
             data_entry_helper::$javascript .= "file_box_initial_file_infoTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box_initial_file_info']) . "';\n";
         }
         if ($indicia_templates['file_box_uploaded_image'] != '') {
             data_entry_helper::$javascript .= "file_box_uploaded_imageTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box_uploaded_image']) . "';\n";
         }
     }
     $occAttrControls = array();
     $occAttrs = array();
     $occAttrControlsExisting = array();
     $taxonRows = array();
     $subSampleRows = array();
     if (!empty($options['useThirdLevelSamples']) && $options['useThirdLevelSamples'] == true) {
         $useThirdLevelSamples = true;
     } else {
         $useThirdLevelSamples = false;
     }
     // Load any existing sample's occurrence data into $entity_to_load
     if (isset(data_entry_helper::$entity_to_load['sample:id']) && $options['useLoadedExistingRecords'] === false) {
         self::preload_species_checklist_occurrences(data_entry_helper::$entity_to_load['sample:id'], $options['readAuth'], $options['mediaTypes'], $options['reloadExtraParams'], $subSampleRows, $options['speciesControlToUseSubSamples'], isset($options['subSampleSampleMethodID']) ? $options['subSampleSampleMethodID'] : '', $options['id'], $useThirdLevelSamples);
     }
     // load the full list of species for the grid, including the main checklist plus any additional species in the reloaded occurrences.
     $taxalist = data_entry_helper::get_species_checklist_taxa_list($options, $taxonRows);
     // If we managed to read the species list data we can proceed
     if (!array_key_exists('error', $taxalist)) {
         $attrOptions = array('id' => null, 'valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => "sc:-idx-::occAttr", 'extraParams' => $options['readAuth'], 'survey_id' => array_key_exists('survey_id', $options) ? $options['survey_id'] : null);
         if (isset($options['attributeIds'])) {
             // make sure we load the grid ID attribute
             if (!empty($options['gridIdAttributeId']) && !in_array($options['gridIdAttributeId'], $options['attributeIds'])) {
                 $options['attributeIds'][] = $options['gridIdAttributeId'];
             }
             $attrOptions['extraParams'] += array('query' => json_encode(array('in' => array('id' => $options['attributeIds']))));
         }
         $attributes = data_entry_helper::getAttributes($attrOptions);
         // Merge in the attribute options passed into the control which can override the warehouse config
         if (isset($options['occAttrOptions'])) {
             foreach ($options['occAttrOptions'] as $attrId => $attr) {
                 if (isset($attributes[$attrId])) {
                     $attributes[$attrId] = array_merge($attributes[$attrId], $attr);
                 }
             }
         }
         // Get the attribute and control information required to build the custom occurrence attribute columns
         data_entry_helper::species_checklist_prepare_attributes($options, $attributes, $occAttrControls, $occAttrControlsExisting, $occAttrs);
         $beforegrid = '<span style="display: none;">Step 1</span>' . "\n";
         if (isset($options['lookupListId'])) {
             $subSampleImagesToLoad = array();
             //Cycle through sub-samples of the main parent sample
             foreach ($subSampleRows as $subSampleIdx => $subSampleRow) {
                 foreach (data_entry_helper::$entity_to_load as $key => $value) {
                     $keyParts = explode(':', $key);
                     //Get an array of sample media to load onto the grid
                     if (strpos($key, 'third-level-smp-occ-grid') !== false && strpos($key, ':sample_medium:id') !== false) {
                         if (!in_array($keyParts[3], $subSampleImagesToLoad)) {
                             $subSampleImagesToLoad[] = $keyParts[3];
                         }
                     }
                 }
             }
             //For each sub-sample, add a row to the occurrences grid with the image loaded, this is then ready for the user.
             //To create occurrences with
             if (isset($subSampleImagesToLoad)) {
                 $mediaIdArray = array();
                 foreach ($subSampleImagesToLoad as $subSampleImageIdx => $subSampleImageToLoad) {
                     $mediaIdArray[] = $subSampleImageToLoad;
                     $beforegrid .= self::get_species_checklist_empty_row_with_image($options, $occAttrControls, $attributes, $subSampleImageIdx, $subSampleImageToLoad);
                 }
                 $encodedMediaArray = json_encode($mediaIdArray);
                 data_entry_helper::$javascript .= "indiciaData.encodedMediaArray=" . json_encode($encodedMediaArray) . ";\n";
             }
             $beforegrid .= self::get_species_checklist_clonable_row($options, $occAttrControls, $attributes);
         }
         $onlyImages = true;
         if ($options['mediaTypes']) {
             foreach ($options['mediaTypes'] as $mediaType) {
                 if (substr($mediaType, 0, 6) !== 'Image:') {
                     $onlyImages = false;
                 }
             }
         }
         $grid = data_entry_helper::get_species_checklist_header($options, $occAttrs, $onlyImages);
         $rows = array();
         $imageRowIdxs = array();
         $taxonCounter = array();
         $rowIdx = 0;
         // tell the addTowToGrid javascript how many rows are already used, so it has a unique index for new rows
         data_entry_helper::$javascript .= "indiciaData['gridCounter-" . $options['id'] . "'] = " . count($taxonRows) . ";\n";
         data_entry_helper::$javascript .= "indiciaData['gridSampleCounter-" . $options['id'] . "'] = " . count($subSampleRows) . ";\n";
         // Loop through all the rows needed in the grid
         // Get the checkboxes (hidden or otherwise) that indicate a species is present
         if (is_array(data_entry_helper::$entity_to_load)) {
             $presenceValues = preg_grep("/^sc:[0-9]*:[0-9]*:present\$/", array_keys(data_entry_helper::$entity_to_load));
         }
         // if subspecies are stored, then need to load up the parent species info into the $taxonRows data
         if ($options['subSpeciesColumn']) {
             self::load_parent_species($taxalist, $options);
             if ($options['subSpeciesRemoveSspRank']) {
                 // remove subspecific rank information from the displayed subspecies names by passing a regex
                 data_entry_helper::$javascript .= "indiciaData.subspeciesRanksToStrip='" . lang::get('(form[a\\.]?|var\\.?|ssp\\.)') . "';\n";
             }
         }
         // track if there is a row we are editing in this grid
         $hasEditedRecord = false;
         if ($options['mediaTypes']) {
             $mediaBtnLabel = lang::get($onlyImages ? 'Add images' : 'Add media');
             $mediaBtnClass = 'sc' . $onlyImages ? 'Image' : 'Media' . 'Link';
         }
         foreach ($taxonRows as $txIdx => $rowIds) {
             $ttlId = $rowIds['ttlId'];
             $loadedTxIdx = isset($rowIds['loadedTxIdx']) ? $rowIds['loadedTxIdx'] : -1;
             $existing_record_id = isset($rowIds['occId']) ? $rowIds['occId'] : false;
             // Multi-column input does not work when image upload allowed
             $colIdx = count($options['mediaTypes']) ? 0 : (int) floor($rowIdx / (count($taxonRows) / $options['columns']));
             // Find the taxon in our preloaded list data that we want to output for this row
             $taxonIdx = 0;
             while ($taxonIdx < count($taxalist) && $taxalist[$taxonIdx]['id'] != $ttlId) {
                 $taxonIdx += 1;
             }
             if ($taxonIdx >= count($taxalist)) {
                 continue;
             }
             // next taxon, as this one was not found in the list
             $taxon = $taxalist[$taxonIdx];
             // If we are using the sub-species column then when the taxon has a parent (=species) this goes in the
             // first column and we put the subsp in the second column in a moment.
             if (isset($options['subSpeciesColumn']) && $options['subSpeciesColumn'] && !empty($taxon['parent'])) {
                 $firstColumnTaxon = $taxon['parent'];
             } else {
                 $firstColumnTaxon = $taxon;
             }
             // map field names if using a cached lookup
             if ($options['cacheLookup']) {
                 $firstColumnTaxon = $firstColumnTaxon + array('preferred_name' => $firstColumnTaxon['preferred_taxon'], 'common' => $firstColumnTaxon['default_common_name']);
             }
             // Get the cell content from the taxon_label template
             $firstCell = helper_base::mergeParamsIntoTemplate($firstColumnTaxon, 'taxon_label');
             // If the taxon label template is PHP, evaluate it.
             if ($options['PHPtaxonLabel']) {
                 $firstCell = eval($firstCell);
             }
             // Now create the table cell to contain this.
             $colspan = isset($options['lookupListId']) && $options['rowInclusionCheck'] != 'alwaysRemovable' ? ' colspan="2"' : '';
             $row = '';
             // Add a delete button if the user can remove rows, add an edit button if the user has the edit option set, add a page link if user has that option set.
             if ($options['rowInclusionCheck'] == 'alwaysRemovable') {
                 $imgPath = empty(helper_base::$images_path) ? helper_base::relative_client_helper_path() . "../media/images/" : helper_base::$images_path;
                 $speciesGridLinkPageIconSource = $imgPath . "nuvola/find-22px.png";
                 if ($options['editTaxaNames']) {
                     $row .= '<td class="row-buttons">
                  <img class="action-button remove-row" src=' . $imgPath . 'nuvola/cancel-16px.png>
                  <img class="action-button edit-taxon-name" src=' . $imgPath . 'nuvola/package_editors-16px.png>';
                     if ($options['includeSpeciesGridLinkPage']) {
                         $row .= '<img class="species-grid-link-page-icon" title="' . $options['speciesGridPageLinkTooltip'] . '" alt="Notes icon" src=' . $speciesGridLinkPageIconSource . '>';
                     }
                     $row .= '</td>';
                 } else {
                     $row .= '<td class="row-buttons"><img class="action-button remove-row" src=' . $imgPath . 'nuvola/cancel-16px.png>';
                     if ($options['includeSpeciesGridLinkPage']) {
                         $row .= '<img class="species-grid-link-page-icon" title="' . $options['speciesGridPageLinkTooltip'] . '" alt="Notes icon" src=' . $speciesGridLinkPageIconSource . '>';
                     }
                     $row .= '</td>';
                 }
             }
             // if editing a specific occurrence, mark it up
             $editedRecord = isset($_GET['occurrence_id']) && $_GET['occurrence_id'] == $existing_record_id;
             $editClass = $editedRecord ? ' edited-record ui-state-highlight' : '';
             $hasEditedRecord = $hasEditedRecord || $editedRecord;
             // Verified records can be flagged with an icon
             //Do an isset check as the npms_paths form for example uses the species checklist, but doesn't use an entity_to_load
             if (isset(data_entry_helper::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:record_status"])) {
                 $status = data_entry_helper::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:record_status"];
                 if (preg_match('/[VDR]/', $status)) {
                     $img = false;
                     switch ($status) {
                         case 'V':
                             $img = 'ok';
                             $statusLabel = 'verified';
                             break;
                         case 'D':
                             $img = 'dubious';
                             $statusLabel = 'queried';
                             break;
                         case 'R':
                             $img = 'cancel';
                             $statusLabel = 'rejected';
                             break;
                     }
                     if ($img) {
                         $label = lang::get($statusLabel);
                         $title = lang::get('This record has been {1}. Changing it will mean that it will need to be rechecked by an expert.', $label);
                         $firstCell .= "<img alt=\"{$label}\" title=\"{$title}\" src=\"{$imgPath}nuvola/{$img}-16px.png\">";
                     }
                 }
             }
             $row .= str_replace(array('{content}', '{colspan}', '{editClass}', '{tableId}', '{idx}'), array($firstCell, $colspan, $editClass, $options['id'], $colIdx), $indicia_templates['taxon_label_cell']);
             $hidden = $options['rowInclusionCheck'] == 'checkbox' ? '' : ' style="display:none"';
             // AlwaysFixed mode means all rows in the default checklist are included as occurrences. Same for
             // AlwayeRemovable except that the rows can be removed.
             // If we are reloading a record there will be an entity_to_load which will indicate whether present should be checked.
             // This has to be evaluated true or false if reloading a submission with errors.
             if ($options['rowInclusionCheck'] == 'alwaysFixed' || $options['rowInclusionCheck'] == 'alwaysRemovable' || data_entry_helper::$entity_to_load != null && array_key_exists("sc:{$loadedTxIdx}:{$existing_record_id}:present", data_entry_helper::$entity_to_load) && data_entry_helper::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:present"] == true) {
                 $checked = ' checked="checked"';
             } else {
                 $checked = '';
             }
             $row .= "\n<td class=\"scPresenceCell\" headers=\"{$options['id']}-present-{$colIdx}\"{$hidden}>";
             $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:present";
             if ($options['rowInclusionCheck'] === 'hasData') {
                 $row .= "<input type=\"hidden\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$taxon['id']}\"/>";
             } else {
                 // this includes a control to force out a 0 value when the checkbox is unchecked.
                 $row .= "<input type=\"hidden\" class=\"scPresence\" name=\"{$fieldname}\" value=\"0\"/>" . "<input type=\"checkbox\" class=\"scPresence\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$taxon['id']}\" {$checked} />";
             }
             // If we have a grid ID attribute, output a hidden
             if (!empty($options['gridIdAttributeId'])) {
                 $gridAttributeId = $options['gridIdAttributeId'];
                 if (empty($existing_record_id)) {
                     //If in add mode we don't need to include the occurrence attribute id
                     $fieldname = "sc:{$options['id']}-{$txIdx}::occAttr:{$gridAttributeId}";
                     $row .= "<input type=\"hidden\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$options['id']}\"/>";
                 } else {
                     $search = preg_grep("/^sc:[0-9]*:{$existing_record_id}:occAttr:{$gridAttributeId}:" . '[0-9]*$/', array_keys(data_entry_helper::$entity_to_load));
                     if (!empty($search)) {
                         $match = array_pop($search);
                         $parts = explode(':', $match);
                         //The id of the existing occurrence attribute value is at the end of the data
                         $idxOfOccValId = count($parts) - 1;
                         //$txIdx is row number in the grid. We cannot simply take the data from entity_to_load as it doesn't contain the row number.
                         $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occAttr:{$gridAttributeId}:{$parts[$idxOfOccValId]}";
                         $row .= "<input type=\"hidden\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$options['id']}\"/>";
                     }
                 }
             }
             $row .= "</td>";
             if ($options['speciesControlToUseSubSamples']) {
                 $row .= "\n<td class=\"scSampleCell\" style=\"display:none\">";
                 $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occurrence:sampleIDX";
                 $value = $options['subSamplePerRow'] ? $smpIdx : $rowIds['smpIdx'];
                 $row .= "<input type=\"hidden\" class=\"scSample\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$value}\" />";
                 $row .= "</td>";
                 // always increment the sample index if 1 per row.
                 if ($options['subSamplePerRow']) {
                     $smpIdx++;
                 }
             }
             $idx = 0;
             if ($options['mediaTypes']) {
                 $existingImages = is_array(data_entry_helper::$entity_to_load) ? preg_grep("/^sc:{$loadedTxIdx}:{$existing_record_id}:occurrence_medium:id:[a-z0-9]*\$/", array_keys(data_entry_helper::$entity_to_load)) : array();
                 $row .= "\n<td class=\"ui-widget-content scAddMediaCell\">";
                 $style = count($existingImages) > 0 ? ' style="display: none"' : '';
                 $fieldname = "add-media:{$options['id']}-{$txIdx}:{$existing_record_id}";
                 $row .= "<a href=\"\"{$style} class=\"add-media-link button {$mediaBtnClass}\" id=\"{$fieldname}\">" . "{$mediaBtnLabel}</a>";
                 $row .= "</td>";
             }
             // Are we in the first column of a multicolumn grid, or doing single column grid? If so start new row.
             if ($colIdx === 0) {
                 $rows[$rowIdx] = $row;
             } else {
                 $rows[$rowIdx % ceil(count($taxonRows) / $options['columns'])] .= $row;
             }
             $rowIdx++;
             if ($options['mediaTypes'] && count($existingImages) > 0) {
                 $totalCols = ($options['lookupListId'] ? 2 : 1) + 1 + count($occAttrControls) + ($options['occurrenceComment'] ? 1 : 0) + ($options['occurrenceSensitivity'] ? 1 : 0) + (count($options['mediaTypes']) ? 1 : 0);
                 $rows[$rowIdx] = '<td colspan="' . $totalCols . '">' . data_entry_helper::file_box(array('table' => "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occurrence_medium", 'loadExistingRecordKey' => "sc:{$loadedTxIdx}:{$existing_record_id}:occurrence_medium", 'mediaTypes' => $options['mediaTypes'], 'readAuth' => $options['readAuth'])) . '</td>';
                 $imageRowIdxs[] = $rowIdx;
                 $rowIdx++;
             }
         }
         $grid .= "\n<tbody>\n";
         if (count($rows) > 0) {
             $grid .= data_entry_helper::species_checklist_implode_rows($rows, $imageRowIdxs);
         }
         $grid .= "</tbody>\n";
         $grid = str_replace(array('{class}', '{id}', '{content}'), array(' class="' . implode(' ', $classlist) . '"', " id=\"{$options['id']}\"", $grid), $indicia_templates['data-input-table']);
         // in hasData mode, the wrap_species_checklist method must be notified of the different default
         // way of checking if a row is to be made into an occurrence. This may differ between grids when
         // there are multiple grids on a page.
         if ($options['rowInclusionCheck'] == 'hasData') {
             $grid .= '<input name="rowInclusionCheck-' . $options['id'] . '" value="hasData" type="hidden" />';
             if (!empty($options['hasDataIgnoreAttrs'])) {
                 $grid .= '<input name="hasDataIgnoreAttrs-' . $options['id'] . '" value="' . implode(',', $options['hasDataIgnoreAttrs']) . '" type="hidden" />';
             }
         }
         // If the lookupListId parameter is specified then the user is able to add extra rows to the grid,
         // selecting the species from this list. Add the required controls for this.
         if (isset($options['lookupListId'])) {
             // Javascript to add further rows to the grid
             if (isset($indicia_templates['format_species_autocomplete_fn'])) {
                 data_entry_helper::$javascript .= 'formatter = ' . $indicia_templates['format_species_autocomplete_fn'];
             } else {
                 data_entry_helper::$javascript .= "formatter = '" . $indicia_templates['taxon_label'] . "';\n";
             }
             if (!empty(parent::$warehouse_proxy)) {
                 $url = parent::$warehouse_proxy . "index.php/services/data";
             } else {
                 $url = helper_base::$base_url . "index.php/services/data";
             }
             data_entry_helper::$javascript .= "if (typeof indiciaData.speciesGrid==='undefined') {indiciaData.speciesGrid={};}\n";
             data_entry_helper::$javascript .= "indiciaData.speciesGrid['{$options['id']}']={};\n";
             data_entry_helper::$javascript .= "indiciaData.speciesGrid['{$options['id']}'].cacheLookup=" . ($options['cacheLookup'] ? 'true' : 'false') . ";\n";
             data_entry_helper::$javascript .= "indiciaData.speciesGrid['{$options['id']}'].numValues=" . (!empty($options['numValues']) ? $options['numValues'] : 20) . ";\n";
             data_entry_helper::$javascript .= "indiciaData.speciesGrid['{$options['id']}'].selectMode=" . (!empty($options['selectMode']) && $options['selectMode'] ? 'true' : 'false') . ";\n";
             //encoded media array is just and array of media items that has been json_encoded.
             //Add a row to the occurrence grid for each media item.
             data_entry_helper::$javascript .= "\n        if (indiciaData.encodedMediaArray) {\n          var encodedMediaArray = eval(indiciaData.encodedMediaArray);\n          for (var i=0; i<encodedMediaArray.length; i++) {\n            makeImageRowOrSpareRow('" . $options['id'] . "', {'auth_token' : '" . $options['readAuth']['auth_token'] . "', 'nonce' : '" . $options['readAuth']['nonce'] . "'},'" . $options['lookupListId'] . "','{$url}', null, false, null, null, encodedMediaArray[i]);\n          }\n        }\n        \r\n";
             data_entry_helper::$javascript .= "makeImageRowOrSpareRow('" . $options['id'] . "', {'auth_token' : '" . $options['readAuth']['auth_token'] . "', 'nonce' : '" . $options['readAuth']['nonce'] . "'},'" . $options['lookupListId'] . "','{$url}', null, false, null, null);\r\n";
         }
         // If options contain a help text, output it at the end if that is the preferred position
         $options['helpTextClass'] = isset($options['helpTextClass']) ? $options['helpTextClass'] : 'helpTextLeft';
         $r = $beforegrid . $grid;
         data_entry_helper::$javascript .= "\$('#" . $options['id'] . "').find('input,select').keydown(keyHandler);\n";
         //nameFilter is an array containing all the parameters required to return data for each of the
         //"Choose species names available for selection" filter types
         data_entry_helper::species_checklist_filter_popup($options, $nameFilter);
         if ($options['subSamplePerRow']) {
             // output a hidden block to contain sub-sample hidden input values.
             $r .= '<div id="' . $options['id'] . '-blocks">' . data_entry_helper::get_subsample_per_row_hidden_inputs() . '</div>';
         }
         if ($hasEditedRecord) {
             data_entry_helper::$javascript .= "\$('#{$options['id']} tbody tr').hide();\n";
             data_entry_helper::$javascript .= "\$('#{$options['id']} tbody tr td.edited-record').parent().show();\n";
             data_entry_helper::$javascript .= "\$('#{$options['id']} tbody tr td.edited-record').parent().next('tr.supplementary-row').show();\n";
             $r .= '<p>' . lang::get('You are editing a single record that is part of a larger sample, so any changes to the sample\'s information such as edits to the date or map reference ' . 'will affect the whole sample.') . " <a id=\"species-grid-view-all-{$options['id']}\">" . lang::get('View all the records in this sample or add more records.') . '</a></p>';
             data_entry_helper::$javascript .= "\$('#species-grid-view-all-{$options['id']}').click(function(e) {\n  \$('#{$options['id']} tbody tr').show();\n  \$(e.currentTarget).hide();\n});\n";
             self::$onload_javascript .= "\nif (\$('#{$options['id']}').parents('.ui-tabs-panel').length) {\n  indiciaFns.activeTab(\$('#controls'), \$('#{$options['id']}').parents('.ui-tabs-panel')[0].id);\n}\n";
         }
         return $r;
     } else {
         return $taxalist['error'];
     }
 }
 /**
  * Return the Indicia form code.
  * Expects there to be a sample attribute with caption 'Email' containing the email
  * address.
  * @param array $args Input parameters.
  * @param array $node Drupal node object
  * @param array $response Response from Indicia services after posting a verification.
  * @return string HTML
  */
 public static function get_form($args, $node, $response)
 {
     if (!self::check_prerequisites()) {
         return '';
     }
     iform_load_helpers(array('data_entry_helper', 'map_helper', 'report_helper'));
     $auth = data_entry_helper::get_read_write_auth($args['website_id'], $args['password']);
     //Clear Verifier Tasks automatically when they open the screen if the option is set.
     if ($args['clear_verification_task_notifications'] && hostsite_get_user_field('indicia_user_id')) {
         self::clear_verifier_task_notifications($auth);
     }
     // set some defaults, applied when upgrading from a form configured on a previous form version.
     if (empty($args['email_subject_send_to_recorder'])) {
         $args['email_subject_send_to_recorder'] = 'Sample requires confirmation (ID:%id%)';
     }
     if (empty($args['email_body_send_to_recorder'])) {
         $args['email_body_send_to_recorder'] = 'The following record requires confirmation. Please could you reply to this email stating how confident you are that the record is correct ' . 'and any other information you have which may help to confirm this.' . "\n\n%record%";
     }
     if (isset($_POST['enable'])) {
         module_enable(array('iform_ajaxproxy'));
         drupal_set_message(lang::get('The Indicia AJAX Proxy module has been enabled.', 'info'));
     } elseif (!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 (function_exists('drupal_add_js')) {
         drupal_add_js('misc/collapse.js');
     }
     // fancybox for popup comment forms etc
     data_entry_helper::add_resource('fancybox');
     data_entry_helper::add_resource('validation');
     $indicia_user_id = self::get_indicia_user_id($args);
     data_entry_helper::$js_read_tokens = $auth['read'];
     // Find a list of websites we are allowed verify
     if (function_exists('module_exists') && module_exists('easy_login')) {
         if (strpos($args['param_presets'] . $args['param_defaults'], 'expertise_location') === false) {
             $args['param_presets'] .= "\nexpertise_location={profile_location_expertise}";
         }
         if (strpos($args['param_presets'] . $args['param_defaults'], 'expertise_taxon_groups') === false) {
             $args['param_presets'] .= "\nexpertise_taxon_groups={profile_taxon_groups_expertise}";
         }
         if (strpos($args['param_presets'] . $args['param_defaults'], 'expertise_surveys') === false) {
             $args['param_presets'] .= "\nexpertise_surveys={profile_surveys_expertise}";
         }
     }
     $args['sharing'] = 'verification';
     $opts = array_merge(iform_report_get_report_options($args, $auth['read']), array('id' => 'verification-grid', 'reportGroup' => 'verification', 'rowId' => 'sample_id', 'paramsFormButtonCaption' => lang::get('Filter'), 'paramPrefix' => '<div class="report-param">', 'paramSuffix' => '</div>', 'sharing' => 'verification', 'ajax' => TRUE, 'callback' => 'verificationGridLoaded'));
     $opts['columns'][] = array('display' => '', 'template' => '<div class="nowrap">' . '<input type="hidden" class="row-input-form" value="{rootFolder}{input_form}"/><input type="hidden" class="row-belongs-to-site" value="{belongs_to_site}"/>' . '<input type="checkbox" class="check-row no-select" style="display: none" value="{occurrence_id}" /></div>');
     $params = self::report_filter_panel($args, $auth['read']);
     $opts['zoomMapToOutput'] = false;
     $grid = report_helper::report_grid($opts);
     $r = str_replace(array('{grid}', '{paramsForm}'), array($grid, $params), self::get_template_with_map($args, $auth['read'], $opts['extraParams'], $opts['paramDefaults']));
     $link = data_entry_helper::get_reload_link_parts();
     global $user;
     data_entry_helper::$js_read_tokens = $auth['read'];
     data_entry_helper::$javascript .= 'indiciaData.nid = "' . $node->nid . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.username = "******"\";\n";
     data_entry_helper::$javascript .= 'indiciaData.userId = "' . $indicia_user_id . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.rootUrl = "' . $link['path'] . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.website_id = ' . $args['website_id'] . ";\n";
     data_entry_helper::$javascript .= 'indiciaData.ajaxFormPostUrl="' . iform_ajaxproxy_url($node, 'sample') . "&user_id={$indicia_user_id}&sharing=verification\";\n";
     data_entry_helper::$javascript .= 'indiciaData.ajaxUrl="' . url('iform/ajax/verification_samples') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.autoDiscard = ' . $args['auto_discard_rows'] . ";\n";
     $imgPath = empty(data_entry_helper::$images_path) ? data_entry_helper::relative_client_helper_path() . "../media/images/" : data_entry_helper::$images_path;
     data_entry_helper::$javascript .= 'indiciaData.imgPath = "' . $imgPath . "\";\n";
     // output some translations for JS to use
     // @todo: Check list for unused (e.g. query stuff)
     data_entry_helper::$javascript .= "indiciaData.popupTranslations = {};\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.title="' . lang::get('Add comment regarding setting status to {1}') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.save="' . lang::get('Save and {1}') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.verbV="' . lang::get('accept') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.verbR="' . lang::get('don\'t accept') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.verbC3="' . lang::get('mark as plausible') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.V="' . lang::get('accepted') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.R="' . lang::get('not accepted') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sub1="' . lang::get('correct') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sub2="' . lang::get('considered correct') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sub3="' . lang::get('plausible') . "\";\n";
     // @todo: Should this term be unable to accept
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sub4="' . lang::get('unable to verify') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sub5="' . lang::get('incorrect') . "\";\n";
     // IS THIS REQUIRED
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.D="' . lang::get('Query') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.tab_email="' . lang::get('Send query as email') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.tab_comment="' . lang::get('Save query to comments log') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.emailTitle="' . lang::get('Email record details') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.emailInstruction="' . lang::get('Use this form to send an email a copy of the record, for example when you would ' . 'like to get the opinion of another expert.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.sendEmail="' . lang::get('Send Email') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.emailSent="' . lang::get('The email was sent successfully.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.requestManualEmail="' . lang::get('The webserver is not correctly configured to send emails. Please send the following email usual your email client:') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.multipleWarning="' . lang::get('You are about to process multiple records. Please note that this comment will apply to all the ticked records. ' . 'If you did not intend to do this, please close this box and turn off the Select Records tool before proceeding.') . "\";\n";
     // translations for querying
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.queryProbablyCantContact="' . lang::get('The record does not have sufficient information for us to be able to contact the recorder. You can leave a query ' . 'in the box below but we cannot guarantee that they will see it.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.queryNeedsEmail="' . lang::get('The recorder can be contacted by email. If you prefer you can just leave the query as a comment on the ' . 'record but it is unlikely that they will see it as they haven\'t previously checked their notifications.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.queryProbablyNeedsEmailNo="' . lang::get('The recorder can be contacted by email. If you prefer you can just leave the query as a comment on the ' . 'record but it they are not known to check their notifications so may not spot the query.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.queryProbablyNeedsEmailUnknown="' . lang::get('The recorder can be contacted by email. If you prefer you can just leave the query as a comment on the ' . 'record though we don\'t have any information to confirm that they will receive the associated notification.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.popupTranslations.queryProbablyWillGetNotified="' . lang::get('The recorder normally checks their notifications so your query can be posted as a comment ' . 'against the record. If you prefer, you can send a direct email.') . "\";\n";
     self::translateStatusTerms();
     data_entry_helper::$javascript .= "indiciaData.statusTranslations = " . json_encode(self::$statusTerms) . ";\n";
     data_entry_helper::$javascript .= "indiciaData.commentTranslations = {};\n";
     data_entry_helper::$javascript .= 'indiciaData.commentTranslations.emailed = "' . lang::get('I emailed this sample to {1} for checking.') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.commentTranslations.recorder = "' . lang::get('the recorder') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.commentTranslations.expert = "' . lang::get('an expert') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.email_subject_send_to_verifier = "' . $args['email_subject_send_to_verifier'] . "\";\n";
     $body = str_replace(array("\r", "\n", '"'), array('', '\\n', '\\"'), $args['email_body_send_to_verifier']);
     data_entry_helper::$javascript .= 'indiciaData.email_body_send_to_verifier = "' . $body . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.email_subject_send_to_recorder = "' . $args['email_subject_send_to_recorder'] . "\";\n";
     $body = str_replace(array("\r", "\n"), array('', '\\n'), $args['email_body_send_to_recorder']);
     data_entry_helper::$javascript .= 'indiciaData.email_body_send_to_recorder = "' . $body . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.str_month = "' . lang::get('month') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.expertise_location = "' . $opts['extraParams']['expertise_location'] . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.expertise_surveys = "' . $opts['extraParams']['expertise_surveys'] . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.expertise_taxon_groups = "' . $opts['extraParams']['expertise_taxon_groups'] . "\";\n";
     data_entry_helper::add_resource('jqplot');
     data_entry_helper::add_resource('jqplot_bar');
     return $r;
 }
 public static function add_locations_to_user($auth, $args, $tabalias, $options, $path)
 {
     global $user;
     //Need to call this so we can use indiciaData.read
     data_entry_helper::$js_read_tokens = $auth['read'];
     if (!function_exists('iform_ajaxproxy_url')) {
         return 'An AJAX Proxy module must be enabled for user sites administration to work.';
     }
     if (!empty($options['locationDropDownLabel'])) {
         $locationDropDownLabel = $addButtonLabel = $options['locationDropDownLabel'] . ' :';
     } else {
         $locationDropDownLabel = lang::get('Location :');
     }
     if (!empty($options['addButtonLabel'])) {
         $addButtonLabel = $options['addButtonLabel'];
     } else {
         $addButtonLabel = lang::get('Add to this User\'s Sites List');
     }
     if (!empty($options['fieldSetLegend'])) {
         $fieldSetLegendText = $options['fieldSetLegend'];
     } else {
         $fieldSetLegendText = lang::get('Add locations to the sites lists for other users');
     }
     if (!empty($options['rolesExemptFromApproval'])) {
         $RolesExemptFromApproval = explode(',', $options['rolesExemptFromApproval']);
     } else {
         $RolesExemptFromApproval = array();
     }
     $r = "<form><fieldset><legend>" . $fieldSetLegendText . "</legend>";
     if (empty($options['locationTypes']) || !preg_match('/^([0-9]+,( )?)*[0-9]+$/', $options['locationTypes'])) {
         return 'The sites form is not correctly configured. Please provide the location type you can add.';
     }
     $locationTypes = explode(',', str_replace(' ', '', $options['locationTypes']));
     if (empty($options['mySitesPsnAttrId']) || !preg_match('/^[0-9]+$/', $options['mySitesPsnAttrId'])) {
         return 'The sites form is not correctly configured. Please provide the person attribute ID used to store My Sites.';
     }
     if (!empty($options['locationParamFromURL']) && !empty($_GET[$options['locationParamFromURL']])) {
         $locationIdFromURL = $_GET[$options['locationParamFromURL']];
     } else {
         $locationIdFromURL = 0;
     }
     //Get the user_id from the URL if we can, this would hide the user drop-down and make
     //the control applicable to a single user.
     if (!empty($options['userParamFromURL']) && !empty($_GET[$options['userParamFromURL']])) {
         $userIdFromURL = $_GET[$options['userParamFromURL']];
     } elseif (!empty($_GET['dynamic-the_user_id'])) {
         $userIdFromURL = $_GET['dynamic-the_user_id'];
     } else {
         $userIdFromURL = 0;
     }
     $extraParams = array('location_type_ids' => $options['locationTypes'], 'user_id' => hostsite_get_user_field('indicia_user_id'), 'my_sites_person_attr_id' => $options['mySitesPsnAttrId']);
     //Can limit results in location drop-down to certain distance of a post code
     if (!empty($options['postCodeGeomParamName']) && !empty($_GET[$options['postCodeGeomParamName']])) {
         $extraParams['post_code_geom'] = $_GET[$options['postCodeGeomParamName']];
     }
     if (!empty($options['distanceFromPostCodeParamName']) && !empty($_GET[$options['distanceFromPostCodeParamName']])) {
         $extraParams['distance_from_post_code'] = $_GET[$options['distanceFromPostCodeParamName']];
     }
     if (!empty($options['excludedSquareAttrId'])) {
         $extraParams['excluded_square_attr_id'] = $options['excludedSquareAttrId'];
     }
     if (!empty($options['dontReturnAllocatedLocations'])) {
         $extraParams['dont_return_allocated_locations'] = $options['dontReturnAllocatedLocations'];
     }
     if (!empty($options['maxAllocationForLocationAttrId'])) {
         $extraParams['max_allocation_for_location_attr_id'] = $options['maxAllocationForLocationAttrId'];
     }
     //If we don't want to automatically get the location id from the URL, then display a drop-down of locations the user can select from
     if (empty($locationIdFromURL)) {
         $r .= '<label>' . $locationDropDownLabel . '</label> ';
         //Get a list of all the locations that match the given location types (in this case my sites are returned first, although this isn't a requirement)
         $r .= data_entry_helper::location_select(array('id' => 'location-select', 'nocache' => true, 'report' => 'reports_for_prebuilt_forms/Splash/locations_for_add_location_drop_down', 'extraParams' => $auth['read'] + $extraParams, 'blankText' => '<' . lang::get('please select') . '>'));
     }
     //Get the user select control if the user id isn't in the url
     if (empty($userIdFromURL)) {
         $r .= self::user_select_for_add_sites_to_any_user_control($auth['read'], $args);
     }
     $r .= '<input id="add-user-site-button" type="button" value="' . $addButtonLabel . '"/><br></form><br>';
     $postUrl = iform_ajaxproxy_url(null, 'person_attribute_value');
     //Firstly check both a uer and location have been selected.
     //Then get the current user/sites saved in the database and if the new combination doesn't already exist then call a function to add it.
     data_entry_helper::$javascript .= "\n    function duplicateCheck(locationId, userId) {\n      var userIdToAdd = userId;\n      var locationIdToAdd = locationId;\n      var sitesReport = indiciaData.read.url +'/index.php/services/report/requestReport?report=library/locations/all_user_sites.xml&mode=json&mode=json&callback=?';\n        \n      var sitesReportParameters = {\n        'person_site_attr_id': '" . $options['mySitesPsnAttrId'] . "',\n        'auth_token': indiciaData.read.auth_token,\n        'nonce': indiciaData.read.nonce,\n        'reportSource':'local'\n      };\n      \n      if (!userIdToAdd||!locationIdToAdd) {\n        alert('Please select both a user and a location to add.');\n      } else {\n        \$.getJSON (\n          sitesReport,\n          sitesReportParameters,\n          function (data) {\n            var duplicateDetected=false;\n            \$.each(data, function(i, dataItem) {\n              if (userIdToAdd==dataItem.pav_user_id&&locationIdToAdd==dataItem.location_id) {\n                  duplicateDetected=true;\n              }\n            });\n            if (duplicateDetected===true) {\n              alert('The site/user combination you are adding already exists in the database.');\n            } else {\n              addUserSiteData(locationId, userIdToAdd);\n            }\n          }\n        );\n      }    \n    }\n    ";
     //This veriabe holds the updated_by_id=1 if the user is found to be exempt, if they aren't exempt then this is blank so that the
     //updated_by_id is set automatically by the system.
     $updatedBySystem = '';
     //See if any of the user's roles are in the exempt list.
     foreach ($RolesExemptFromApproval as $exemptRole) {
         foreach ($user->roles as $userRole) {
             if ($exemptRole === $userRole) {
                 $updatedBySystem = ',"updated_by_id":1';
             }
         }
     }
     //Add the user/site combination to the person_attribute_values database table.
     //This overrides the function in the my_sites.php file.
     data_entry_helper::$javascript .= "\n    var addUserSiteData = function (locationId, userIdToAdd) {\n      if (!isNaN(locationId) && locationId!=='') {\n        \$.post('{$postUrl}', \n          {\"website_id\":" . $args['website_id'] . ",\"person_attribute_id\":" . $options['mySitesPsnAttrId'] . ",\"user_id\":userIdToAdd,\"int_value\":locationId" . $updatedBySystem . "},\n          function (data) {\n            if (typeof data.error === 'undefined') {\n              alert('User site configuration saved successfully');\n              location.reload();\n            } else {\n              alert(data.error);\n            }              \n          },\n          'json'\n        );\n      }\n    }\n    ";
     //Call duplicate check when administrator elects to save a user/site combination
     data_entry_helper::$javascript .= "\n    \$('#add-user-site-button').click(function() {\n      //We can get the location id from the url or from the locations drop-down depending on the option the administrator has set.\n      var locationId;\n      var userId;\n      if (" . $locationIdFromURL . ") {\n        locationId = " . $locationIdFromURL . ";\n      } else {\n        locationId = \$('#location-select').val();       \n      }\n      if (" . $userIdFromURL . ") {\n        userId = " . $userIdFromURL . ";\n      } else {\n        userId = \$('#user-select').val();     \n      }\n      duplicateCheck(locationId,userId);\n    });";
     //Zoom map as user selects locations
     data_entry_helper::$javascript .= "\n    \$('#location-select, #location-search, #locality_id').change(function() {\n      if (typeof indiciaData.mapdiv!=='undefined') {\n        indiciaData.mapdiv.locationSelectedInInput(indiciaData.mapdiv, this.value);\n      }\n    });\n    ";
     self::user_site_delete($postUrl, $args);
     return $r;
 }
 /**
  * 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 $nid The Drupal node object's ID.
  * @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.
  */
 public static function get_form($args, $nid, $response = null)
 {
     drupal_add_js(iform_client_helpers_path() . "prebuilt_forms/js/easy_download_2.js");
     drupal_add_css(iform_client_helpers_path() . "prebuilt_forms/css/easy_download_2.css");
     $conn = iform_get_connection_details($nid);
     $args = array_merge(array('download_administered_groups' => 'indicia data admin', 'download_group_types' => ''), $args);
     $readAuth = data_entry_helper::get_read_auth($conn['website_id'], $conn['password']);
     if (data_entry_helper::$js_read_tokens === null) {
         data_entry_helper::$js_read_tokens = $readAuth;
     }
     if (!empty($_POST) && !empty($_POST['format'])) {
         self::do_data_services_download($args, $nid);
     }
     $types = self::get_download_types($args);
     $formats = self::get_download_formats($args);
     if (count($types) === 0) {
         return 'This download page is configured so that no download type options are available.';
     }
     if (count($formats) === 0) {
         return 'This download page is configured so that no download format options are available.';
     }
     $reload = data_entry_helper::get_reload_link_parts();
     $reloadPath = $reload['path'];
     if (count($reload['params'])) {
         $reloadPath .= '?' . helper_base::array_to_query_string($reload['params']);
     }
     $r = '<form method="POST" action="' . $reloadPath . '">';
     $r .= '<fieldset id="download-type-fieldset"><legend>' . lang::get('Records to download') . '</legend>';
     if (count($types) === 1) {
         $r .= '<input type="hidden" name="download-type" id="download-type" value="' . implode('', array_keys($types)) . '"/>';
         hostsite_set_page_title(lang::get('Download {1}', strtolower(implode('', $types))));
     } else {
         $r .= data_entry_helper::select(array('fieldname' => 'download-type', 'label' => lang::get('Download type'), 'lookupValues' => $types, 'class' => 'control-width-5', 'helpText' => 'Select the type of download you require, i.e. the purpose for the data. This defines which records are available to download.'));
     }
     $r .= data_entry_helper::select(array('fieldname' => 'download-subfilter', 'label' => lang::get('Filter to apply'), 'lookupValues' => array(), 'class' => 'control-width-5', 'helpText' => lang::get('Optionally select from the available filters. Filters you create on the Explore pages will be available here.')));
     $r .= "</fieldset>\n";
     $r .= '<fieldset><legend>' . lang::get('Limit the records') . '</legend>';
     // Hub
     // TODO may run into URL size limits
     $vocabulary = taxonomy_vocabulary_machine_name_load('hubs');
     $terms = entity_load('taxonomy_term', FALSE, array('vid' => $vocabulary->vid));
     // the hub is driven by a user field, stored as tid.
     $hubList = array('' => lang::get('<All>'));
     foreach ($terms as $term) {
         // TODO Cache
         $query = new EntityFieldQuery();
         $query->entityCondition('entity_type', 'user')->fieldCondition('field_preferred_training_hub', 'tid', $term->tid);
         $result = $query->execute();
         // This gives us the list of users which are in the hub: CMS user ID: now convert to indicia user id
         $userIDList = array();
         if (count($result) > 0) {
             $cmsUserIDs = array_keys($result['user']);
             foreach ($cmsUserIDs as $cmsUserID) {
                 $user_data = user_load($cmsUserID);
                 // TODO Making assumption about language
                 if (!empty($user_data->field_indicia_user_id['und'][0]['value'])) {
                     $userIDList[] = $user_data->field_indicia_user_id['und'][0]['value'];
                 }
             }
             if (count($userIDList) > 0) {
                 $hubList[implode(',', $userIDList)] = $term->tid . ' ' . $term->name;
             }
         }
     }
     $r .= data_entry_helper::select(array('fieldname' => 'user_id_list', 'label' => lang::get('Hub to include'), 'helpText' => 'Choose a Hub, or &lt;All&gt; to not filter by Hub. This is driven off the users currently allocated to the hub. This currently does not apply to the NBN download.', 'lookupValues' => $hubList, 'class' => 'control-width-5'));
     // End Cocoast Hub
     if (empty($args['survey_id'])) {
         // put up an empty surveys drop down. AJAX will populate it.
         $r .= data_entry_helper::select(array('fieldname' => 'survey_id', 'label' => lang::get('Survey to include'), 'helpText' => 'Choose a survey, or &lt;All&gt; to not filter by survey.', 'lookupValues' => array(), 'class' => 'control-width-5'));
     } else {
         $r .= '<input type="hidden" name="survey_id" value="' . $args['survey_id'] . '"/>';
     }
     // Let the user pick the date range to download.
     $r .= data_entry_helper::select(array('label' => lang::get('Date field'), 'fieldname' => 'date_type', 'lookupValues' => array('recorded' => lang::get('Field record date'), 'input' => lang::get('Input date'), 'edited' => lang::get('Last changed date'), 'verified' => 'Verification status change date'), 'helpText' => 'If filtering on date, which date field would you like to filter on?'));
     $r .= data_entry_helper::date_picker(array('fieldname' => 'date_from', 'label' => lang::get('Start Date'), 'helpText' => 'Leave blank for no start date filter', 'class' => 'control-width-4'));
     $r .= data_entry_helper::date_picker(array('fieldname' => 'date_to', 'label' => lang::get('End Date'), 'helpText' => 'Leave blank for no end date filter', 'class' => 'control-width-4'));
     $r .= '</fieldset>';
     if (!empty($args['custom_formats'])) {
         $customFormats = json_decode($args['custom_formats'], true);
         foreach ($customFormats as $idx => $format) {
             if (empty($format['permission']) || hostsite_user_has_permission($format['permission'])) {
                 $formats["custom-{$idx}"] = lang::get(isset($format['title']) ? $format['title'] : 'Untitled format');
             }
         }
     }
     if (count($formats) > 1) {
         $r .= '<fieldset><legend>' . lang::get('Select a format to download') . '</legend>';
         $keys = array_keys($formats);
         $r .= data_entry_helper::radio_group(array('fieldname' => 'format', 'lookupValues' => $formats, 'default' => $keys[0]));
         $r .= '</fieldset>';
     } else {
         // only allowed 1 format, so no need for a selection control
         $keys = array_keys($formats);
         $r .= '<input type="hidden" name="format" value="' . array_pop($keys) . '"/>';
     }
     $r .= '<input type="submit" value="' . lang::get('Download') . '"/></form>';
     data_entry_helper::$javascript .= 'indiciaData.ajaxUrl="' . url('iform/ajax/easy_download_2') . "\";\n";
     data_entry_helper::$javascript .= 'indiciaData.nid = "' . $nid . "\";\n";
     data_entry_helper::$javascript .= "setAvailableDownloadFilters();\n";
     return $r;
 }