/**
  * Ajax method to proxy requests for bulk verification on to the warehouse, attaching write auth
  * as it goes.
  */
 public static function ajax_bulk_verify($website_id, $password)
 {
     iform_load_helpers(array('data_entry_helper'));
     $auth = data_entry_helper::get_read_write_auth($website_id, $password);
     $url = data_entry_helper::$base_url . "index.php/services/data_utils/bulk_verify";
     $params = array_merge($_POST, $auth['write_tokens']);
     $response = data_entry_helper::http_post($url, $params);
     echo $response['output'];
 }
Esempio n. 2
0
 /**
  * Takes a file that has been uploaded to the client website upload folder, and moves it to the warehouse upload folder using the
  * data services.
  *
  * @param string $path Path to the file to upload, relative to the interim image path folder (normally the
  * client_helpers/upload folder.
  * @param boolean $persist_auth Allows the write nonce to be preserved after sending the file, useful when several files
  * are being uploaded.
  * @return string Error message, or true if successful.
  */
 private static function send_file_to_warehouse($path, $persist_auth = false)
 {
     $interim_image_folder = isset(parent::$interim_image_folder) ? parent::$interim_image_folder : 'upload/';
     $uploadpath = data_entry_helper::relative_client_helper_path() . $interim_image_folder;
     $serviceUrl = parent::$base_url . "/index.php/services/data/handle_media";
     // This is used by the file box control which renames uploaded files using a guid system, so disable renaming on the server.
     $postargs = array('name_is_guid' => 'true');
     // attach authentication details
     if (array_key_exists('auth_token', $_POST)) {
         $postargs['auth_token'] = $_POST['auth_token'];
     }
     if (array_key_exists('nonce', $_POST)) {
         $postargs['nonce'] = $_POST['nonce'];
     }
     if ($persist_auth) {
         $postargs['persist_auth'] = 'true';
     }
     $file_to_upload = array('media_upload' => '@' . realpath($uploadpath . $path));
     $response = data_entry_helper::http_post($serviceUrl, $file_to_upload + $postargs);
     $output = json_decode($response['output'], true);
     $r = true;
     // default is success
     if (is_array($output)) {
         //an array signals an error
         if (array_key_exists('error', $output)) {
             // return the most detailed bit of error information
             if (isset($output['errors']['media_upload'])) {
                 $r = $output['errors']['media_upload'];
             } else {
                 $r = $output['error'];
             }
         }
     }
     //remove local copy
     //unlink(realpath($uploadpath.$path));
     watchdog('file exists', realpath($uploadpath . $path));
     return $r;
 }
 /**
  * Method called when posting the form. Saves the subscription details to the warehouse.
  * @param $args
  * @param $auth
  * @throws \exception
  */
 private static function subscribe($args, $auth)
 {
     $url = data_entry_helper::$base_url . 'index.php/services/species_alerts/register?';
     $params = array('auth_token' => $auth['write_tokens']['auth_token'], 'nonce' => $auth['write_tokens']['nonce'], 'first_name' => $_POST['first_name'], 'surname' => $_POST['surname'], 'email' => $_POST['email'], 'website_id' => $args['website_id'], 'alert_on_entry' => $_POST['species_alert:alert_on_entry'] ? 't' : 'f', 'alert_on_verify' => $_POST['species_alert:alert_on_verify'] ? 't' : 'f');
     if (!empty($_POST['species_alert:id'])) {
         $params['id'] = $_POST['species_alert:id'];
     }
     if (!empty($_POST['species_alert:taxon_list_id'])) {
         $params['taxon_list_id'] = $_POST['species_alert:taxon_list_id'];
     }
     if (!empty($_POST['species_alert:location_id'])) {
         $params['location_id'] = $_POST['species_alert:location_id'];
     }
     if (!empty($_POST['user_id'])) {
         $params['user_id'] = $_POST['user_id'];
     }
     if (!empty($_POST['taxa_taxon_list_id'])) {
         // We've got a taxa_taxon_list_id in the post data. But, it is better to subscribe via a taxon
         // meaning ID, or even better, the external key.
         $taxon = data_entry_helper::get_population_data(array('table' => 'taxa_taxon_list', 'extraParams' => $auth['read'] + array('id' => $_POST['taxa_taxon_list_id'], 'view' => 'cache')));
         if (count($taxon) !== 1) {
             throw new exception('Unable to find unique taxon when attempting to subscribe');
         }
         $taxon = $taxon[0];
         if (!empty($taxon['external_key'])) {
             $params['external_key'] = $taxon['external_key'];
         } else {
             $params['taxon_meaning_id'] = $taxon['taxon_meaning_id'];
         }
     } elseif (!empty($_POST['species_alert:external_key'])) {
         $params['external_key'] = $_POST['species_alert:external_key'];
     } elseif (!empty($_POST['species_alert:taxon_meaning_id'])) {
         $params['taxon_meaning_id'] = $_POST['species_alert:taxon_meaning_id'];
     }
     $url .= data_entry_helper::array_to_query_string($params, true);
     $result = data_entry_helper::http_post($url);
     if ($result['result']) {
         hostsite_show_message(lang::get('Your subscription has been saved.'));
     } else {
         hostsite_show_message(lang::get('There was a problem saving your subscription.'));
         if (function_exists('watchdog')) {
             watchdog('iform', 'Species alert error on save: ' . print_r($result, true));
         }
     }
 }
 /**
  * Private helper function to call the get_user_id service.
  */
 private function callGetUserIdService($auth, $identifiers, $cmsUserId, $firstName, $surname, $extras = '')
 {
     $url = data_entry_helper::$base_url . 'index.php/services/user_identifier/get_user_id';
     $url .= '?nonce=' . $auth['write_tokens']['nonce'] . '&auth_token=' . $auth['write_tokens']['auth_token'];
     $identifiers = urlencode(json_encode($identifiers));
     $params = "cms_user_id={$cmsUserId}&first_name={$firstName}&surname={$surname}&identifiers={$identifiers}";
     if (!empty($extras)) {
         $params .= "&{$extras}";
     }
     return data_entry_helper::http_post($url, $params);
 }
   /**
    * Return the generated form output.
    * @return Form HTML.
    */
   public static function get_form($args, $node, $response = null)
   {
       global $user;
       global $custom_terms;
       $logged_in = $user->uid > 0;
       $r = '';
       // Get authorisation tokens to update and read from the Warehouse.
       $auth = data_entry_helper::get_read_write_auth($args['website_id'], $args['password']);
       $readAuth = $auth['read'];
       $svcUrl = data_entry_helper::$base_url . '/index.php/services';
       drupal_add_js(drupal_get_path('module', 'iform') . '/media/js/jquery.form.js', 'module');
       data_entry_helper::link_default_stylesheet();
       data_entry_helper::add_resource('jquery_ui');
       $language = iform_lang_iso_639_2($args['language']);
       if ($args['language'] != 'en') {
           data_entry_helper::add_resource('jquery_ui_' . $args['language']);
       }
       // If not logged in: Display an information message.
       // This form should only be called in POST mode when setting the location allocation.
       //  All other posting is now done via AJAX.
       // When invoked by GET there are the following modes:
       // No additional arguments: mode 0.
       // Additional argument - new : mode 1.
       // Additional argument - sample_id=<id> : mode 2.
       // Additional argument - occurrence_id=<id> : mode 3.
       // Additional arguments - merge_sample_id1=<id>&merge_sample_id2=<id> : mode 2.1
       $mode = 0;
       // default mode : output survey selector
       // mode 1: output the main Data Entry page: occurrence list or add/edit occurrence tabs hidden. "Survey" tab active
       // mode 2: output the main Data Entry page, display existing sample. Active tab determined by iform params. No occurence details filled in.
       // mode 2.1: sample 2 has all its occurrences merged into sample 1. sample 2 is then flagged as deleted. sample 1 is then viewed as in normal mode 2.
       // mode 3: output the main Data Entry page, display existing occurrence. "Edit Occurrence" tab active. Occurence details filled in.
       $surveyReadOnly = false;
       // On top of this, things can be flagged as readonly. RO mode 2+4 means no Add Occurrence tab.
       if (!$logged_in) {
           return lang::get('LANG_not_logged_in');
       }
       $parentSample = array();
       $parentLoadID = null;
       $childSample = array();
       $childLoadID = null;
       $thisOccID = -1;
       // IDs have to be >0, so this is outside the valid range
       // Load up attribute details
       $sample_walk_direction_id = self::getAttrID($auth, $args, 'sample', self::ATTR_WALK);
       $sample_reliability_id = self::getAttrID($auth, $args, 'sample', self::ATTR_RELIABILITY);
       $sample_visit_number_id = self::getAttrID($auth, $args, 'sample', self::ATTR_VISIT);
       $sample_wind_id = self::getAttrID($auth, $args, 'sample', self::ATTR_WIND);
       $sample_precipitation_id = self::getAttrID($auth, $args, 'sample', self::ATTR_RAIN);
       $sample_temperature_id = self::getAttrID($auth, $args, 'sample', self::ATTR_TEMP);
       $sample_cloud_id = self::getAttrID($auth, $args, 'sample', self::ATTR_CLOUD);
       $sample_start_time_id = self::getAttrID($auth, $args, 'sample', self::ATTR_START_TIME);
       $sample_end_time_id = self::getAttrID($auth, $args, 'sample', self::ATTR_END_TIME);
       $sample_closure_id = self::getAttrID($auth, $args, 'sample', self::ATTR_CLOSED);
       $uid_attr_id = self::getAttrID($auth, $args, 'sample', self::ATTR_UID);
       $email_attr_id = self::getAttrID($auth, $args, 'sample', self::ATTR_EMAIL);
       $username_attr_id = self::getAttrID($auth, $args, 'sample', self::ATTR_USERNAME);
       $occurrence_confidence_id = self::getAttrID($auth, $args, 'occurrence', self::ATTR_CONFIDENCE);
       $occurrence_count_id = self::getAttrID($auth, $args, 'occurrence', self::ATTR_COUNT);
       $occurrence_approximation_id = self::getAttrID($auth, $args, 'occurrence', self::ATTR_APPROXIMATION);
       $occurrence_territorial_id = self::getAttrID($auth, $args, 'occurrence', self::ATTR_TERRITORIAL);
       $occurrence_atlas_code_id = self::getAttrID($auth, $args, 'occurrence', self::ATTR_ATLAS_CODE);
       $occurrence_overflying_id = self::getAttrID($auth, $args, 'occurrence', self::ATTR_OVERFLYING);
       if (!$sample_closure_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_CLOSED . '" sample attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$uid_attr_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_UID . '" sample attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$email_attr_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_EMAIL . '" sample attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$username_attr_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_USERNAME . '" sample attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$sample_walk_direction_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_WALK . '" sample attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$sample_visit_number_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_VISIT . '" sample attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$occurrence_count_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_COUNT . '" occurrence attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$occurrence_territorial_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_TERRITORIAL . '" occurrence attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if (!$occurrence_atlas_code_id) {
           return '<p>This form must be used with a survey which has the "' . self::ATTR_ATLAS_CODE . '" occurrence attribute allocated to it. Survey_id = ' . $args['survey_id'];
       }
       if ($_POST) {
           if (!array_key_exists('website_id', $_POST)) {
               // non Indicia POST, in this case must be the location allocations. add check to ensure we don't corrept the data by accident
               if (iform_loctools_checkaccess($node, 'admin') && array_key_exists('mnhnlbtw', $_POST)) {
                   iform_loctools_deletelocations($node);
                   foreach ($_POST as $key => $value) {
                       $parts = explode(':', $key);
                       if ($parts[0] == 'location' && $value) {
                           iform_loctools_insertlocation($node, $value, $parts[1]);
                       }
                   }
               }
           }
       } else {
           if (array_key_exists('merge_sample_id1', $_GET) && array_key_exists('merge_sample_id2', $_GET) && user_access($args['edit_permission'])) {
               $mode = 2;
               // first check can access the 2 samples given
               $parentLoadID = $_GET['merge_sample_id1'];
               $url = $svcUrl . '/data/sample/' . $parentLoadID . "?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"];
               $session = curl_init($url);
               curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
               $entity = json_decode(curl_exec($session), true);
               if (count($entity) == 0 || $entity[0]["parent_id"]) {
                   return '<p>' . lang::get('LANG_No_Access_To_Sample') . ' ' . $parentLoadID . '</p>';
               }
               // The check for id2 is slightly different: there is the possiblity that someone will F5/refresh their browser, after the transfer and delete have taken place.
               // In this case we carry on, but do not do the transfer and delete.
               $url = $svcUrl . '/data/sample/' . $_GET['merge_sample_id2'] . "?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"];
               $session = curl_init($url);
               curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
               $entity = json_decode(curl_exec($session), true);
               if (count($entity) > 0 && !$entity[0]["parent_id"]) {
                   // now get child samples and point to new parent.
                   $url = $svcUrl . '/data/sample?mode=json&view=detail&auth_token=' . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"] . '&parent_id=' . $_GET['merge_sample_id2'];
                   $session = curl_init($url);
                   curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
                   $entities = json_decode(curl_exec($session), true);
                   if (count($entities) > 0) {
                       foreach ($entities as $entity) {
                           $Model = data_entry_helper::wrap(array('id' => $entity['id'], 'parent_id' => $_GET['merge_sample_id1']), 'sample');
                           $request = data_entry_helper::$base_url . "/index.php/services/data/save";
                           $postargs = 'submission=' . json_encode($Model) . '&auth_token=' . $auth['write_tokens']['auth_token'] . '&nonce=' . $auth['write_tokens']['nonce'] . '&persist_auth=true';
                           $postresponse = data_entry_helper::http_post($request, $postargs, false);
                           // the response array will always feature an output, which is the actual response or error message. if it is not json format, assume error text, and json encode that.
                           $response = $postresponse['output'];
                           if (!json_decode($response, true)) {
                               return "<p>" . lang::get('LANG_Error_When_Moving_Sample') . ": id " . $entity['id'] . " : " . $response;
                           }
                       }
                   }
                   // finally delete the no longer used sample
                   $Model = data_entry_helper::wrap(array('id' => $_GET['merge_sample_id2'], 'deleted' => 'true'), 'sample');
                   $request = data_entry_helper::$base_url . "/index.php/services/data/save";
                   $postargs = 'submission=' . json_encode($Model) . '&auth_token=' . $auth['write_tokens']['auth_token'] . '&nonce=' . $auth['write_tokens']['nonce'] . '&persist_auth=true';
                   $postresponse = data_entry_helper::http_post($request, $postargs, false);
                   // the response array will always feature an output, which is the actual response or error message. if it is not json format, assume error text, and json encode that.
                   $response = $postresponse['output'];
                   if (!json_decode($response, true)) {
                       return "<p>" . lang::get('LANG_Error_When_Deleting_Sample') . ": id " . $entity['id'] . " : " . $response;
                   }
               }
           } else {
               if (array_key_exists('sample_id', $_GET)) {
                   $mode = 2;
                   $parentLoadID = $_GET['sample_id'];
               } else {
                   if (array_key_exists('occurrence_id', $_GET)) {
                       $mode = 3;
                       $childLoadID = $_GET['occurrence_id'];
                       $thisOccID = $childLoadID;
                   } else {
                       if (array_key_exists('new', $_GET)) {
                           $mode = 1;
                       }
                   }
               }
           }
           // else default to mode 0
       }
       // define language strings so they can be used for validation translation.
       data_entry_helper::$javascript .= "var translations = [\n";
       foreach ($custom_terms as $key => $value) {
           if (substr($key, 0, 4) != "LANG") {
               data_entry_helper::$javascript .= "  {key: \"" . $key . "\", translated: \"" . $value . "\"},\n";
           }
       }
       data_entry_helper::$javascript .= "];\n";
       // define layers for all maps.
       // each argument is a comma separated list eg:
       // "Name:Lux Outline,URL:http://localhost/geoserver/wms,LAYERS:indicia:nation2,SRS:EPSG:2169,FORMAT:image/png,minScale:0,maxScale:1000000,units:m";
       $optionsArray_Location = array();
       $options = explode(',', $args['locationLayer']);
       foreach ($options as $option) {
           $parts = explode(':', $option);
           $optionName = $parts[0];
           unset($parts[0]);
           $optionsArray_Location[$optionName] = implode(':', $parts);
       }
       // Work out list of locations this user can see.
       $locations = iform_loctools_listlocations($node);
       if ($locations != 'all') {
           data_entry_helper::$javascript .= "var locationList = [" . implode(',', $locations) . "];\n";
       }
       drupal_add_js(drupal_get_path('module', 'iform') . '/media/js/hasharray.js', 'module');
       drupal_add_js(drupal_get_path('module', 'iform') . '/media/js/jquery.datagrid.js', 'module');
       if (method_exists(get_called_class(), 'getHeaderHTML')) {
           $r .= call_user_func(array(get_called_class(), 'getHeaderHTML'), $args);
       }
       // Work out list of locations this user can see.
       // $locations = iform_loctools_listlocations($node);
       ///////////////////////////////////////////////////////////////////
       // default mode 0 : display a page with tabs for survey selector,
       // locations allocator and reports (last two require permissions)
       ///////////////////////////////////////////////////////////////////
       if ($mode == 0) {
           // If the user has permissions, add tabs so can choose to see
           // locations allocator
           $tabs = array('#surveyList' => lang::get('LANG_Surveys'));
           if (iform_loctools_checkaccess($node, 'admin')) {
               $tabs['#setLocations'] = lang::get('LANG_Allocate_Locations');
           }
           if (iform_loctools_checkaccess($node, 'superuser')) {
               $tabs['#downloads'] = lang::get('LANG_Download');
           }
           if (count($tabs) > 1) {
               $r .= "<div id=\"controls\">" . data_entry_helper::enable_tabs(array('divId' => 'controls', 'active' => '#surveyList')) . "<div id=\"temp\"></div>";
               $r .= data_entry_helper::tab_header(array('tabs' => $tabs));
           }
           if ($locations == 'all') {
               $useloclist = 'NO';
               $loclist = '-1';
           } else {
               // an empty list will cause an sql error, lids must be > 0, so push a -1 to prevent the error.
               if (empty($locations)) {
                   $locations[] = -1;
               }
               $useloclist = 'YES';
               $loclist = implode(',', $locations);
           }
           // Create the Survey list datagrid for this user.
           drupal_add_js("jQuery(document).ready(function(){\n  \$('div#smp_grid').indiciaDataGrid('rpt:reports_for_prebuilt_forms/MNHNL/mnhnl_btw_list_samples', {\n    indiciaSvc: '" . $svcUrl . "',\n    dataColumns: ['location_name', 'date', 'num_visit', 'num_occurrences', 'num_taxa'],\n    reportColumnTitles: {location_name : '" . lang::get('LANG_Transect') . "', date : '" . lang::get('LANG_Date') . "', num_visit : '" . lang::get('LANG_Visit_No') . "', num_occurrences : '" . lang::get('LANG_Num_Occurrences') . "', num_taxa : '" . lang::get('LANG_Num_Species') . "'},\n    actionColumns: {" . lang::get('LANG_Show') . " : \"" . url('node/' . $node->nid, array('query' => 'sample_id=£id£')) . "\"},\n    auth : { nonce : '" . $readAuth['nonce'] . "', auth_token : '" . $readAuth['auth_token'] . "'},\n    parameters : { survey_id : '" . $args['survey_id'] . "', visit_attr_id : '" . $sample_visit_number_id . "', closed_attr_id : '" . $sample_closure_id . "', use_location_list : '" . $useloclist . "', locations : '" . $loclist . "'},\n    itemsPerPage : 12,\n    condCss : {field : 'closed', value : '0', css: 'mnhnl-btw-highlight'},\n    cssOdd : ''\n  });\n});\n      ", 'inline');
           data_entry_helper::$javascript .= "\n// Create Layers.\nvar locationListLayer;\nmapInitialisationHooks.push(function (div) {\n      \"use strict\";";
           if ($locations == 'all' || $loclist != '-1') {
               data_entry_helper::$javascript .= "\n  var WMSoptions = {SERVICE: 'WMS', VERSION: '1.1.0', STYLES: '',\n        SRS: div.map.projection.proj.srsCode, /* Now takes it from map */\n        FORMAT: '" . $optionsArray_Location['FORMAT'] . "',\n        TRANSPARENT: 'true', ";
               if ($locations != 'all') {
                   // when given a restricted feature list we have to use the feature id to filter in order to not go over 2000 char limit on the URL
                   // Can only generate the feature id if we access a table directly, not through a view. Go direct to the locations table.
                   // don't need to worry about parent_id in this case as we know exactly which features we want.
                   // need to use btw_transects view for unrestricted so we can filter by parent_id=NULL.
                   $locFeatures = array();
                   foreach ($locations as $location) {
                       $locFeatures[] = "locations." . $location;
                   }
                   data_entry_helper::$javascript .= "\n        LAYERS: 'indicia:locations',\n        FEATUREID: '" . implode(',', $locFeatures) . "'";
               } else {
                   data_entry_helper::$javascript .= " LAYERS: '" . $optionsArray_Location['LAYERS'] . "', CQL_FILTER: 'website_id=" . $args['website_id'] . "'";
               }
               data_entry_helper::$javascript .= "\n  };\n  locationListLayer = new OpenLayers.Layer.WMS('" . $optionsArray_Location['Name'] . "',\n        '" . (function_exists(iform_proxy_url) ? iform_proxy_url($optionsArray_Location['URL']) : $optionsArray_Location['URL']) . "',\n        WMSoptions, {\n        minScale: " . $optionsArray_Location['minScale'] . ",\n        maxScale: " . $optionsArray_Location['maxScale'] . ",\n        units: '" . $optionsArray_Location['units'] . "',\n        isBaseLayer: false,\n        singleTile: true\n  });\n  div.map.addLayer(locationListLayer);\n});\n";
           }
           $r .= '
 <div id="surveyList" class="mnhnl-btw-datapanel"><div id="smp_grid"></div>
   <form><input type="button" value="' . lang::get('LANG_Add_Survey') . '" onclick="window.location.href=\'' . url('node/' . $node->nid, array('query' => 'new')) . '\'"></form></div>';
           // Add the locations allocator if user has admin rights.
           if (iform_loctools_checkaccess($node, 'admin')) {
               $r .= '
 <div id="setLocations" class="mnhnl-btw-datapanel">
   <form method="post">
     <input type="hidden" id="mnhnlbtw" name="mnhnlbtw" value="mnhnlbtw" />';
               $url = $svcUrl . '/data/location?mode=json&view=detail&auth_token=' . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"] . "&parent_id=NULL&orderby=name&columns=id,name,parent_id";
               $session = curl_init($url);
               curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
               $entities = json_decode(curl_exec($session), true);
               $userlist = iform_loctools_listusers($node);
               if (!empty($entities)) {
                   foreach ($entities as $entity) {
                       if (!$entity["parent_id"]) {
                           // only assign parent locations.
                           $r .= "\n<label for=\"location:" . $entity["id"] . "\">" . $entity["name"] . ":</label><select id=\"location:" . $entity["id"] . "\" name=\"location:" . $entity["id"] . "\"><option value=\"\" >&lt;" . lang::get('LANG_Not_Allocated') . "&gt;</option>";
                           $defaultuserid = iform_loctools_getuser($node, $entity["id"]);
                           foreach ($userlist as $uid => $a_user) {
                               $r .= "<option value=\"" . $uid . "\" " . ($uid == $defaultuserid ? 'selected="selected" ' : '') . ">" . $a_user->name . "</option>";
                           }
                           $r .= "</select>";
                       }
                   }
               }
               $r .= "\n      <input type=\"submit\" class=\"ui-state-default ui-corner-all\" value=\"" . lang::get('Save Location Allocations') . "\" />\n    </form>\n  </div>";
           }
           // Add the downloader if user has manager (superuser) rights.
           if (iform_loctools_checkaccess($node, 'superuser')) {
               $r .= '
 <div id="downloads" class="mnhnl-btw-datapanel">
   <form method="post" action="' . data_entry_helper::$base_url . '/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_transect_direction_report.xml&reportSource=local&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth['nonce'] . '&mode=csv">
     <p>' . lang::get('LANG_Direction_Report') . '</p>
     <input type="hidden" name="params" value=\'{"survey_id":' . $args['survey_id'] . ', "direction_attr_id":' . $sample_walk_direction_id . ', "closed_attr_id":' . $sample_closure_id . '}\' />
     <input type="submit" class="ui-state-default ui-corner-all" value="' . lang::get('LANG_Direction_Report_Button') . '">
   </form>
   <form method="post" action="' . data_entry_helper::$base_url . '/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_verified_data_report.xml&reportSource=local&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth['nonce'] . '&mode=csv">
     <p>' . lang::get('LANG_Verified_Data_Report') . '</p>
     <input type="hidden" name="params" value=\'{"survey_id":' . $args['survey_id'] . '}\' />
     <input type="submit" class="ui-state-default ui-corner-all" value="' . lang::get('LANG_Verified_Data_Report_Button') . '">
   </form>
   <form method="post" action="' . data_entry_helper::$base_url . '/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_download_report_2.xml&reportSource=local&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth['nonce'] . '&mode=csv">
     <p>' . lang::get('LANG_Initial_Download') . '</p>
     <input type="hidden" name="params" value=\'{"survey_id":' . $args['survey_id'] . ', "closed_attr_id":' . $sample_closure_id . ', "download": "INITIAL", "quality": "!R", "occattrs": "' . $occurrence_confidence_id . ',' . $occurrence_count_id . ',' . $occurrence_approximation_id . ',' . $occurrence_territorial_id . ',' . $occurrence_atlas_code_id . ',' . $occurrence_overflying_id . '", "smpattrs" : "' . $sample_walk_direction_id . ',' . $sample_reliability_id . ',' . $sample_visit_number_id . ',' . $sample_wind_id . ',' . $sample_precipitation_id . ',' . $sample_temperature_id . ',' . $sample_cloud_id . ',' . $sample_start_time_id . ',' . $sample_end_time_id . ',' . $sample_closure_id . ',' . $uid_attr_id . ',' . $email_attr_id . ',' . $username_attr_id . '"}\' />
     <input type="submit" class="ui-state-default ui-corner-all" value="' . lang::get('LANG_Initial_Download_Button') . '">
   </form>
   <form method="post" action="' . data_entry_helper::$base_url . '/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_download_report_2.xml&reportSource=local&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth['nonce'] . '&mode=csv">
     <p>' . lang::get('LANG_Confirm_Download') . '</p>
     <input type="hidden" name="params" value=\'{"survey_id":' . $args['survey_id'] . ', "closed_attr_id":' . $sample_closure_id . ', "download": "CONFIRM", "quality": "V", "occattrs": "' . $occurrence_confidence_id . ',' . $occurrence_count_id . ',' . $occurrence_approximation_id . ',' . $occurrence_territorial_id . ',' . $occurrence_atlas_code_id . ',' . $occurrence_overflying_id . '", "smpattrs" : "' . $sample_walk_direction_id . ',' . $sample_reliability_id . ',' . $sample_visit_number_id . ',' . $sample_wind_id . ',' . $sample_precipitation_id . ',' . $sample_temperature_id . ',' . $sample_cloud_id . ',' . $sample_start_time_id . ',' . $sample_end_time_id . ',' . $sample_closure_id . ',' . $uid_attr_id . ',' . $email_attr_id . ',' . $username_attr_id . '"}\' />
     <input type="submit" class="ui-state-default ui-corner-all" value="' . lang::get('LANG_Confirm_Download_Button') . '">
   </form>
   <form method="post" action="' . data_entry_helper::$base_url . '/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_download_report_2.xml&reportSource=local&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth['nonce'] . '&mode=csv">
     <p>' . lang::get('LANG_Final_Download') . '</p>
     <input type="hidden" name="params" value=\'{"survey_id":' . $args['survey_id'] . ', "closed_attr_id":' . $sample_closure_id . ', "download": "FINAL", "quality": "V", "occattrs": "' . $occurrence_confidence_id . ',' . $occurrence_count_id . ',' . $occurrence_approximation_id . ',' . $occurrence_territorial_id . ',' . $occurrence_atlas_code_id . ',' . $occurrence_overflying_id . '", "smpattrs" : "' . $sample_walk_direction_id . ',' . $sample_reliability_id . ',' . $sample_visit_number_id . ',' . $sample_wind_id . ',' . $sample_precipitation_id . ',' . $sample_temperature_id . ',' . $sample_cloud_id . ',' . $sample_start_time_id . ',' . $sample_end_time_id . ',' . $sample_closure_id . ',' . $uid_attr_id . ',' . $email_attr_id . ',' . $username_attr_id . '"}\' />
     <input type="submit" class="ui-state-default ui-corner-all" value="' . lang::get('LANG_Final_Download_Button') . '">
   </form>
   <form method="post" action="' . data_entry_helper::$base_url . '/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_download_report_2.xml&reportSource=local&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth['nonce'] . '&mode=csv">
     <p>' . lang::get('LANG_Complete_Final_Download') . '</p>
     <input type="hidden" name="params" value=\'{"survey_id":' . $args['survey_id'] . ', "closed_attr_id":' . $sample_closure_id . ', "download": "OFF", "quality": "NA", "occattrs": "' . $occurrence_confidence_id . ',' . $occurrence_count_id . ',' . $occurrence_approximation_id . ',' . $occurrence_territorial_id . ',' . $occurrence_atlas_code_id . ',' . $occurrence_overflying_id . '", "smpattrs" : "' . $sample_walk_direction_id . ',' . $sample_reliability_id . ',' . $sample_visit_number_id . ',' . $sample_wind_id . ',' . $sample_precipitation_id . ',' . $sample_temperature_id . ',' . $sample_cloud_id . ',' . $sample_start_time_id . ',' . $sample_end_time_id . ',' . $sample_closure_id . ',' . $uid_attr_id . ',' . $email_attr_id . ',' . $username_attr_id . '"}\' />
     <input type="submit" class="ui-state-default ui-corner-all" value="' . lang::get('LANG_Complete_Final_Download_Button') . '">
   </form>
 </div>';
           }
           // Create Map
           $options = iform_map_get_map_options($args, $readAuth);
           $olOptions = iform_map_get_ol_options($args);
           //      if($locations == 'all' || $loclist != '-1')
           //        $options['layers'] = array('locationListLayer');
           $options['searchLayer'] = 'false';
           $options['editLayer'] = 'false';
           $options['initialFeatureWkt'] = null;
           $options['proxy'] = '';
           $options['scroll_wheel_zoom'] = false;
           $options['width'] = 'auto';
           // TBD remove from arglist
           $r .= "<div class=\"mnhnl-btw-mappanel\">\n" . data_entry_helper::map_panel($options, $olOptions) . "</div>\n";
           data_entry_helper::$javascript .= "\n\$('#controls').bind('tabsshow', function(event, ui) {\n  var y = \$('.mnhnl-btw-datapanel:visible').outerHeight(true) + \$('.mnhnl-btw-datapanel:visible').position().top;\n  if(y < \$('.mnhnl-btw-mappanel').outerHeight(true)+ \$('.mnhnl-btw-mappanel').position().top){\n    y = \$('.mnhnl-btw-mappanel').outerHeight(true)+ \$('.mnhnl-btw-mappanel').position().top;\n  }\n  \$('#controls').height(y - \$('#controls').position().top);\n});\n";
           if (count($tabs) > 1) {
               // close tabs div if present
               $r .= "</div>";
           }
           if (method_exists(get_called_class(), 'getTrailerHTML')) {
               $r .= call_user_func(array(get_called_class(), 'getTrailerHTML'), $args);
           }
           return $r;
       }
       ///////////////////////////////////////////////////////////////////
       // At this point there are 3 modes:
       // Adding a new survey
       // editing/showing an existing survey
       // editing/showing an existing occurrence
       // First load the occurrence (and its position sample) if provided
       // Then load the parent sample if provided, or derived from occurrence.
       // $occReadOnly is set if the occurrence has been downloaded. Not even an admin user can modify it in this case.
       data_entry_helper::$javascript .= "\n// Create Layers.\nvar locationLayer, occListLayer, control;\nmapInitialisationHooks.push(function (div) {\n  \"use strict\";\n  // Create vector layers: one to display the location onto, and another for the occurrence list\n  // the default edit layer is used for the occurrences themselves\n  var locStyleMap = new OpenLayers.StyleMap({\n      'default': new OpenLayers.Style({\n        fillColor: 'Green',\n        strokeColor: 'Black',\n        fillOpacity: 0.2,\n        strokeWidth: 1\n  })});\n  locationLayer = new OpenLayers.Layer.Vector('" . lang::get("LANG_Location_Layer") . "', {styleMap: locStyleMap});\n  var occStyleMap = new OpenLayers.StyleMap({\n      'default': new OpenLayers.Style({\n        pointRadius: 3,\n        fillColor: 'Red',\n        fillOpacity: 0.3,\n        strokeColor: 'Red',\n        strokeWidth: 1\n  })});\n  occListLayer = new OpenLayers.Layer.Vector(\"" . lang::get("LANG_Occurrence_List_Layer") . "\", {styleMap: occStyleMap});\n  div.map.addLayer(locationLayer);\n  div.map.addLayer(occListLayer);\n  var control = new OpenLayers.Control.SelectFeature(occListLayer);\n  occListLayer.map.addControl(control);\n  function onPopupClose(evt) {\n    // 'this' is the popup.\n    control.unselect(this.feature);\n  }\n  function onFeatureSelect(evt) {\n    feature = evt.feature;\n    popup = new OpenLayers.Popup.FramedCloud(\"featurePopup\",\n               feature.geometry.getBounds().getCenterLonLat(),\n                             new OpenLayers.Size(100,100),\n                             feature.attributes.taxon + \" (\" + feature.attributes.count + \")\",\n                             null, true, onPopupClose);\n    feature.popup = popup;\n    popup.feature = feature;\n    feature.layer.map.addPopup(popup);\n  }\n  function onFeatureUnselect(evt) {\n    feature = evt.feature;\n    if (feature.popup) {\n        popup.feature = null;\n        feature.layer.map.removePopup(feature.popup);\n        feature.popup.destroy();\n        feature.popup = null;\n    }\n  }\n  occListLayer.events.on({\n    'featureselected': onFeatureSelect,\n    'featureunselected': onFeatureUnselect\n  });\n  control.activate();\n});\n";
       $occReadOnly = false;
       $childSample = array();
       if ($childLoadID) {
           // load the occurrence and its associated sample (which holds the position)
           $url = $svcUrl . '/data/occurrence/' . $childLoadID;
           $url .= "?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"];
           $session = curl_init($url);
           curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
           $entity = json_decode(curl_exec($session), true);
           if (count($entity) == 0) {
               return '<p>' . lang::get('LANG_No_Access_To_Occurrence') . '</p>';
           }
           foreach ($entity[0] as $key => $value) {
               $childSample['occurrence:' . $key] = $value;
           }
           if ($entity[0]['downloaded_flag'] == 'F') {
               // Final download complete, now readonly
               $occReadOnly = true;
           }
           $url = $svcUrl . '/data/sample/' . $childSample['occurrence:sample_id'];
           $url .= "?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"];
           $session = curl_init($url);
           curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
           $entity = json_decode(curl_exec($session), true);
           if (count($entity) == 0) {
               return '<p>' . lang::get('LANG_No_Access_To_Occurrence') . '</p>';
           }
           foreach ($entity[0] as $key => $value) {
               $childSample['sample:' . $key] = $value;
           }
           $childSample['sample:geom'] = '';
           // value received from db is not WKT, which is assumed by all the code.
           $childSample['taxon'] = $childSample['occurrence:taxon'];
           $parentLoadID = $childSample['sample:parent_id'];
       }
       $parentSample = array();
       if ($parentLoadID) {
           // load the container master sample
           $url = $svcUrl . '/data/sample/' . $parentLoadID;
           $url .= "?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"];
           $session = curl_init($url);
           curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
           $entity = json_decode(curl_exec($session), true);
           if (count($entity) == 0) {
               return '<p>' . lang::get('LANG_No_Access_To_Sample') . '</p>';
           }
           foreach ($entity[0] as $key => $value) {
               $parentSample['sample:' . $key] = $value;
           }
           if (is_array($locations) && !in_array($entity[0]["location_id"], $locations)) {
               return '<p>' . lang::get('LANG_No_Access_To_Location') . '</p>';
           }
           if ($entity[0]["parent_id"]) {
               return '<p>' . lang::get('LANG_No_Access_To_Sample') . '</p>';
           }
           $parentSample['sample:date'] = $parentSample['sample:date_start'];
           // bit of a bodge
           $childSample['sample:date'] = $parentSample['sample:date'];
           // enforce a match between child and parent sample dates
           // default values for attributes from DB are picked up automatically.
       }
       data_entry_helper::$entity_to_load = $parentSample;
       $attributes = data_entry_helper::getAttributes(array('id' => data_entry_helper::$entity_to_load['sample:id'], 'valuetable' => 'sample_attribute_value', 'attrtable' => 'sample_attribute', 'key' => 'sample_id', 'fieldprefix' => 'smpAttr', 'extraParams' => $readAuth));
       $closedFieldName = $attributes[$sample_closure_id]['fieldname'];
       $closedFieldValue = data_entry_helper::check_default_value($closedFieldName, array_key_exists('default', $attributes[$sample_closure_id]) ? $attributes[$sample_closure_id]['default'] : '0');
       // default is not closed
       if ($closedFieldValue == '') {
           $closedFieldValue = '0';
       }
       if ($closedFieldValue == '1' && !user_access($args['edit_permission'])) {
           // sample has been closed, no admin perms. Everything now set to read only.
           $surveyReadOnly = true;
           $disabledText = "disabled=\"disabled\"";
           $defAttrOptions = array('extraParams' => $readAuth, 'disabled' => $disabledText);
       } else {
           // sample editable. Admin users can modify closed samples.
           $disabledText = "";
           $defAttrOptions = array('extraParams' => $readAuth);
       }
       // with the AJAX code, we deal with the validation semi manually: Form name is meant be invalid as we only want code included.
       data_entry_helper::enable_validation(null);
       $r .= "<div id=\"controls\">\n";
       $activeTab = 'survey';
       // mode 1 = new Sample, display sample.
       if ($mode == 2) {
           // have specified a sample ID
           if ($args["on_edit_survey_nav"] == "survey") {
               $activeTab = 'survey';
           } else {
               if ($surveyReadOnly || $args["on_edit_survey_nav"] == "list") {
                   $activeTab = 'occurrenceList';
               } else {
                   $activeTab = 'occurrence';
               }
           }
           if ($surveyReadOnly) {
               data_entry_helper::$javascript .= "jQuery('#occ-form').hide();";
           }
       } else {
           if ($mode == 3) {
               // have specified an occurrence ID
               $activeTab = 'occurrence';
           }
       }
       // Set Up form tabs.
       $r .= data_entry_helper::enable_tabs(array('divId' => 'controls', 'active' => $activeTab));
       $r .= "<div id=\"temp\"></div>";
       $r .= data_entry_helper::tab_header(array('tabs' => array('#survey' => lang::get('LANG_Survey'), '#occurrence' => lang::get($surveyReadOnly || $occReadOnly ? 'LANG_Show_Occurrence' : (isset($childSample['sample:id']) ? 'LANG_Edit_Occurrence' : 'LANG_Add_Occurrence')), '#occurrenceList' => lang::get('LANG_Occurrence_List'))));
       // Set up main Survey Form.
       $r .= "<div id=\"survey\" class=\"mnhnl-btw-datapanel\">\n  <p id=\"read-only-survey\"><strong>" . lang::get('LANG_Read_Only_Survey') . "</strong></p>";
       if (user_access($args['edit_permission']) && array_key_exists('sample:id', data_entry_helper::$entity_to_load)) {
           // check for other surveys of same date/transect: only if admin user.
           $url = $svcUrl . '/data/sample?mode=json&view=detail&auth_token=' . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"] . "&date_start=" . $parentSample['sample:date_start'] . "&location_id=" . $parentSample['sample:location_id'];
           $session = curl_init($url);
           curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
           $entity = json_decode(curl_exec($session), true);
           if (count($entity) > 1) {
               // ignore ourselves!
               $r .= "<div id=\"mergeSurveys\"><p><strong>" . lang::get('LANG_Found_Mergable_Surveys') . "</strong></p>";
               foreach ($entity as $survey) {
                   if ($survey['id'] != $parentSample['sample:id']) {
                       $r .= "<form action=\"" . url('node/' . $node->nid, array()) . "\" method=\"get\"><input type=\"submit\" value=\"" . lang::get('LANG_Merge_With_ID') . " " . $survey['id'] . "\"><input type=\"hidden\" name=\"merge_sample_id1\" value=\"" . $parentSample['sample:id'] . "\" /><input type=\"hidden\" name=\"merge_sample_id2\" value=\"" . $survey['id'] . "\" /></form>";
                   }
               }
               $r .= "</div>";
           }
       }
       $r .= "<form id=\"SurveyForm\" action=\"" . iform_ajaxproxy_url($node, 'sample') . "\" method=\"post\">\n    <input type=\"hidden\" id=\"website_id\" name=\"website_id\" value=\"" . $args['website_id'] . "\" />\n    <input type=\"hidden\" id=\"sample:survey_id\" name=\"sample:survey_id\" value=\"" . $args['survey_id'] . "\" />";
       if (array_key_exists('sample:id', data_entry_helper::$entity_to_load)) {
           $r .= "<input type=\"hidden\" id=\"sample:id\" name=\"sample:id\" value=\"" . data_entry_helper::$entity_to_load['sample:id'] . "\" />\n";
       } else {
           $r .= "<input type=\"hidden\" id=\"sample:id\" name=\"sample:id\" value=\"\" disabled=\"disabled\" />\n";
           // GvB 19/Nov/2012 : change to user detail defaults
           // logic is now much simpler, and they are only included/created if the sample is new.
           $fieldName = $attributes[$uid_attr_id]['fieldname'];
           $fieldValue = data_entry_helper::check_default_value($fieldName, $user->uid);
           $r .= "<input type=\"hidden\" name=\"" . $fieldName . "\" value=\"" . $fieldValue . "\" />\n";
           $fieldName = $attributes[$email_attr_id]['fieldname'];
           $fieldValue = data_entry_helper::check_default_value($fieldName, $user->mail);
           $r .= "<input type=\"hidden\" name=\"" . $fieldName . "\" value=\"" . $fieldValue . "\" />\n";
           $fieldName = $attributes[$username_attr_id]['fieldname'];
           $fieldValue = data_entry_helper::check_default_value($fieldName, $user->name);
           $r .= "<input type=\"hidden\" name=\"" . $fieldName . "\" value=\"" . $fieldValue . "\" />\n";
       }
       $defAttrOptions['validation'] = array('required');
       if ($locations == 'all') {
           $locOptions = array_merge(array('label' => lang::get('LANG_Transect')), $defAttrOptions);
           $locOptions['extraParams'] = array_merge(array('parent_id' => 'NULL', 'view' => 'detail', 'orderby' => 'name'), $locOptions['extraParams']);
           $r .= data_entry_helper::location_select($locOptions);
       } else {
           // can't use location select due to location filtering.
           $r .= "<label for=\"imp-location\">" . lang::get('LANG_Transect') . ":</label>\n<select id=\"imp-location\" name=\"sample:location_id\" " . $disabled_text . " class=\" \"  >";
           $url = $svcUrl . '/data/location?mode=json&view=detail&parent_id=NULL&orderby=name&auth_token=' . $readAuth['auth_token'] . '&nonce=' . $readAuth["nonce"];
           $session = curl_init($url);
           curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
           $entities = json_decode(curl_exec($session), true);
           if (!empty($entities)) {
               foreach ($entities as $entity) {
                   if (in_array($entity["id"], $locations)) {
                       if ($entity["id"] == data_entry_helper::$entity_to_load['sample:location_id']) {
                           $selected = 'selected="selected"';
                       } else {
                           $selected = '';
                       }
                       $r .= "<option value=\"" . $entity["id"] . "\" " . $selected . ">" . $entity["name"] . "</option>";
                   }
               }
           }
           $r .= "</select><span class=\"deh-required\">*</span><br />";
       }
       $languageFilteredAttrOptions = $defAttrOptions + array('language' => iform_lang_iso_639_2($args['language']));
       $r .= data_entry_helper::outputAttribute($attributes[$sample_walk_direction_id], $languageFilteredAttrOptions) . ($sample_reliability_id ? data_entry_helper::outputAttribute($attributes[$sample_reliability_id], $languageFilteredAttrOptions) : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_RELIABILITY . "' not assigned to this survey</span>") . data_entry_helper::outputAttribute($attributes[$sample_visit_number_id], array_merge($languageFilteredAttrOptions, array('default' => 1, 'noBlankText' => true)));
       if (isset(data_entry_helper::$entity_to_load['sample:date']) && preg_match('/^(\\d{4})/', data_entry_helper::$entity_to_load['sample:date'])) {
           // Date has 4 digit year first (ISO style) - convert date to expected output format
           $d = new DateTime(data_entry_helper::$entity_to_load['sample:date']);
           data_entry_helper::$entity_to_load['sample:date'] = $d->format('d/m/Y');
       }
       if ($args['language'] != 'en') {
           data_entry_helper::add_resource('jquery_ui_' . $args['language']);
       }
       // this will autoload the jquery_ui resource. The date_picker does not have access to the args.
       if ($surveyReadOnly) {
           $r .= data_entry_helper::text_input(array_merge($defAttrOptions, array('label' => lang::get('LANG_Date'), 'fieldname' => 'sample:date', 'disabled' => $disabledText)));
       } else {
           $r .= data_entry_helper::date_picker(array('label' => lang::get('LANG_Date'), 'fieldname' => 'sample:date', 'class' => 'vague-date-picker'));
       }
       $r .= ($sample_wind_id ? data_entry_helper::outputAttribute($attributes[$sample_wind_id], $languageFilteredAttrOptions) : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_WIND . "' not assigned to this survey</span>") . ($sample_precipitation_id ? data_entry_helper::outputAttribute($attributes[$sample_precipitation_id], $languageFilteredAttrOptions) : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_RAIN . "' not assigned to this survey</span>") . ($sample_temperature_id ? data_entry_helper::outputAttribute($attributes[$sample_temperature_id], array_merge($defAttrOptions, array('suffixTemplate' => 'nosuffix'))) . "<span class=\"attr-trailer\"> &deg;C</span><br />" : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_TEMP . "' not assigned to this survey</span>") . ($sample_cloud_id ? data_entry_helper::outputAttribute($attributes[$sample_cloud_id], $defAttrOptions) : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_CLOUD . "' not assigned to this survey</span>") . ($sample_start_time_id ? data_entry_helper::outputAttribute($attributes[$sample_start_time_id], array_merge($defAttrOptions, array('suffixTemplate' => 'nosuffix'))) . "<span class=\"attr-trailer\"> hh:mm</span><br />" : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_START_TIME . "' not assigned to this survey</span>") . ($sample_end_time_id ? data_entry_helper::outputAttribute($attributes[$sample_end_time_id], array_merge($defAttrOptions, array('suffixTemplate' => 'nosuffix'))) . "<span class=\"attr-trailer\"> hh:mm</span><br />" : "<span style=\"display: none;\">Sample attribute '" . self::ATTR_END_TIME . "' not assigned to this survey</span>");
       data_entry_helper::$javascript .= "\njQuery('.attr-trailer').prev('br').remove();\n";
       unset($defAttrOptions['suffixTemplate']);
       unset($defAttrOptions['validation']);
       if (user_access($args['edit_permission'])) {
           //  users with admin permissions can override the closing of the
           // sample by unchecking the checkbox.
           // Because this is attached to the sample, we have to include the sample required fields in the
           // the post. This means they can't be disabled, so we enable all fields in this case.
           // Normal users can only set this to closed, and they do this using a button/hidden field.
           $r .= data_entry_helper::outputAttribute($attributes[$sample_closure_id], $defAttrOptions);
           // In addition admin users can delete a survey/sample.
           $r .= data_entry_helper::checkbox(array('label' => lang::get('Deleted'), 'fieldname' => 'sample:deleted', 'id' => 'main-sample-deleted'));
       } else {
           // hidden closed
           $r .= "<input type=\"hidden\" id=\"main-sample-closed\" name=\"" . $closedFieldName . "\" value=\"" . $closedFieldValue . "\" />\n";
       }
       data_entry_helper::$javascript .= "\n\$.validator.messages.required = \"" . lang::get('validation_required') . "\";\n\$.validator.defaults.onsubmit = false; // override default - so that we handle all submission validation.\n";
       if (!$surveyReadOnly) {
           // NB that we don't even include the buttons when readonly.
           data_entry_helper::$javascript .= "\njQuery('#read-only-survey').hide();\njQuery('#ro-sur-occ-warn').hide();\n";
           $r .= "<input type=button id=\"close1\" class=\"ui-state-default ui-corner-all \" value=\"" . lang::get('LANG_Save_Survey_Details') . "\";\n        onclick=\"var result = \$('#SurveyForm input').valid();\n          var result2 = \$('#SurveyForm select').valid();\n          if (!result || !result2) {\n              return;\n            }\n            jQuery('#close1').addClass('loading-button');\n            jQuery('#SurveyForm').submit();\">\n";
           if (!user_access($args['edit_permission'])) {
               if ($mode == 1) {
                   data_entry_helper::$javascript .= "jQuery('#close2').hide();\n";
               }
               $r .= "<input type=button id=\"close2\" class=\"ui-state-default ui-corner-all \" value=\"" . lang::get('LANG_Save_Survey_And_Close') . "\"\n        onclick=\"if(confirm('" . lang::get('LANG_Close_Survey_Confirm') . "')){\n          var result = \$('#SurveyForm input').valid();\n          var result2 = \$('#SurveyForm select').valid();\n          if (!result || !result2) {\n              return;\n            }\n            jQuery('#main-sample-closed').val('1');\n            jQuery('#close2').addClass('loading-button');\n            jQuery('#SurveyForm').submit();\n          };\">\n";
           }
       }
       $r .= "</form></div>\n";
       data_entry_helper::$javascript .= "\nalertIndiciaError = function(data){\n\tvar errorString = \"" . lang::get('LANG_Indicia_Warehouse_Error') . "\";\n\tif(data.error){\terrorString = errorString + ' : ' + data.error;\t}\n\tif(data.errors){\n\t\tfor (var i in data.errors){\n\t\t\terrorString = errorString + ' : ' + data.errors[i];\n\t\t}\n\t}\n\talert(errorString);\n\t// the most likely cause is authentication failure - eg the read authentication has timed out.\n\t// prevent further use of the form:\n\t\$('.loading-panel').remove();\n\t\$('.loading-hide').removeClass('loading-hide');\n};\nerrorPos = null;\nclearErrors = function(formSel) {\n\tjQuery(formSel).find('.inline-error').remove();\n\terrorPos = null;\n};\nmyScrollTo = function(selector){\n\tjQuery(selector).filter(':visible').each(function(){\n\t\tif(errorPos == null || jQuery(this).offset().top < errorPos){\n\t\t\terrorPos = jQuery(this).offset().top;\n\t\t\twindow.scroll(0,errorPos);\n\t\t}\n\t});\n};\nmyScrollToError = function(){\n\tjQuery('.inline-error,.error').filter(':visible').prev().each(function(){\n\t\tif(errorPos == null || jQuery(this).offset().top < errorPos){\n\t\t\terrorPos = jQuery(this).offset().top;\n\t\t\twindow.scroll(0,errorPos);\n\t\t}\n\t});\n};\njQuery('#SurveyForm').ajaxForm({\n\tasync: false,\n\tdataType:  'json',\n    beforeSubmit:   function(data, obj, options){\n    \tvar valid = true;\n    \tclearErrors('form#SurveyForm');\n    \tif (!jQuery('form#SurveyForm > input').valid()) {\n\t\t\tmyScrollToError();\n  \t\t\tjQuery('.loading-button').removeClass('loading-button');\n\t\t\treturn false;\n  \t\t};\n  \t\tSurveyFormRetVal = true;\n  \t\tif(jQuery('#main-sample-deleted:checked').length == 0){ // only do check if not deleting\n          jQuery.ajax({ // now check if there are any other samples with this combination of date and location\n            type: 'GET',\n            url: \"" . $svcUrl . "/data/sample?mode=json&view=detail\" +\n                \"&nonce=" . $readAuth['nonce'] . "&auth_token=" . $readAuth['auth_token'] . "\" +\n                \"&orderby=id&callback=?&location_id=\"+jQuery('#imp-location').val()+\"&date_start=\"+jQuery('#SurveyForm [name=sample\\:date]').val(),\n            data: {},\n            success: function(detData) {\n              for(i=0, j=0; i< detData.length; i++){\n                if(detData[i].id != jQuery('#SurveyForm [name=sample\\:id]').val()) j++;\n              }\n              if(j) {\n              \tSurveyFormRetVal = confirm(\"" . lang::get('LANG_Survey_Already_Exists') . "\");\n              }\n            },\n            dataType: 'json',\n            async: false\n          });\n        }\n\t\treturn SurveyFormRetVal;\n\t},\n    success:   function(data){\n       // this will leave all the fields populated.\n       \tif(data.success == 'multiple records' && data.outer_table == 'sample'){\n          jQuery('#occ-form').show();\n          jQuery('#na-occ-warn,#mergeSurveys').hide();";
       if (!user_access($args['edit_permission'])) {
           // don't need to worry about record_status value for non admins as they can't modify when closed.
           data_entry_helper::$javascript .= "\n          if(jQuery('#main-sample-closed').val() == '1'){\n            jQuery('#read-only-survey,#ro-sur-occ-warn').show();\n            jQuery('#close1,#close2,#occ-form').hide(); //can't enter any more occurrences\n            jQuery('#SurveyForm').children().attr('disabled','disabled');\n          };\n";
       } else {
           data_entry_helper::$javascript .= "\n          jQuery('#occurrence\\\\:record_status').val(jQuery('#smpAttr\\\\:" . $attributes[$sample_closure_id]['attributeId'] . ":checked').length > 0 ? 'C' : 'I');\n          if(jQuery('#main-sample-deleted:checked').length > 0){\n            jQuery('#return-to-main').click();\n            return;\n          };\n";
       }
       data_entry_helper::$javascript .= "// If sample_id filled in -> we have a previously saved collection, so possibly have subsamples.\nif(jQuery('#SurveyForm > input[name=sample\\:id]').val() != ''){\n    // Put up warning dialogue that we are checking the subsamples: include a progress bar: set to zero%.\n    var dialog = \$('<span id=\"subsample-progress-span\"><p>'+\"" . lang::get('Please wait whilst some data integrity checks are carried out.') . "\"+'</p><div id=\"subsample-progress\"></div></span>').dialog({ title: \"" . lang::get('Checks') . "\", zIndex: 4000 });\n    jQuery('#subsample-progress').progressbar({value: 0});\n    jQuery.ajax({ // get all subsamples/occurrences to check if the dates match\n            type: 'GET',\n            url: \"" . $svcUrl . "/report/requestReport?report=library/occurrences/occurrences_list_for_parent_sample.xml&reportSource=local&mode=json&nonce=" . $readAuth['nonce'] . "&auth_token=" . $readAuth['auth_token'] . "\" +\n                \"&callback=?&sample_id=\"+data.outer_id+\"&survey_id=&date_from=&date_to=&taxon_group_id=&smpattrs=&occattrs=\",\n            data: {},\n            success: function(subData) {\n              jQuery('#subsample-progress').data('max',subData.length+1);\n              var mainDate = \$.datepicker.formatDate('yy-mm-dd', jQuery('#SurveyForm > input[name=sample\\:date]').datepicker(\"getDate\"));\n              for(i=0; i< subData.length; i++){ // loop through all subsamples\n                jQuery('#subsample-progress').progressbar('option','value',(i+1)*100/jQuery('#subsample-progress').data('max'));\n                var values = {};\n                var url = '';\n                // Check if date on subsamples matches supersample date: if not set up a post array for the sample, with correct date.\n                if(subData[i].date_start != mainDate){\n                  values['sample:id']=subData[i].sample_id;\n                  values['sample:date']=mainDate;\n                  url=\"" . iform_ajaxproxy_url($node, 'sample') . "\";\n                }\n";
       // Send AJAX request to set occurrence to 'C' if closed : use sync
       if (!user_access($args['edit_permission'])) {
           data_entry_helper::$javascript .= "                if(jQuery('#main-sample-closed').val() == '1'){\n";
       } else {
           data_entry_helper::$javascript .= "                if(jQuery('#smpAttr\\\\:" . $attributes[$sample_closure_id]['attributeId'] . ":checked').length > 0){\n";
       }
       // If records are already verified, they are left verified, as if the records themselves are saved
       // they will flagged as no longer verified: But have to force a re verification if date is changed.
       data_entry_helper::$javascript .= "\n                  if(subData[i].record_status == 'I' || typeof values['sample:id'] != 'undefined'){\n                    values['occurrence:id']=subData[i].occurrence_id;\n                    values['occurrence:record_status']='C';\n                    url=(url == '' ? \"" . iform_ajaxproxy_url($node, 'occurrence') . "\" : \"" . iform_ajaxproxy_url($node, 'smp-occ') . "\");\n                  }\n                } else { // any occurrences on unclosed collections must be flagged as 'I' - reopening unverifies.\n                  if(subData[i].record_status != 'I'){\n                    values['occurrence:id']=subData[i].occurrence_id;\n                    values['occurrence:record_status']='I';\n                    url=(url == '' ? \"" . iform_ajaxproxy_url($node, 'occurrence') . "\" : \"" . iform_ajaxproxy_url($node, 'smp-occ') . "\");\n                  }\n                }\n                if(url!=''){\n                  values['website_id']=" . $args['website_id'] . ";\n                  jQuery.ajax({ type: 'POST', url: url, data: values, dataType: 'json', async: false});\n                }\n              }\n            },\n            dataType: 'json',\n            async: false\n    });\n    dialog.dialog('close');\n    dialog.dialog('destroy');\n    jQuery('#subsample-progress-span').remove();\n}\n\n\t\t\twindow.scroll(0,0);\n            jQuery('#SurveyForm > input[name=sample\\:id]').removeAttr('disabled').val(data.outer_id);\n            jQuery('#occ-form > input[name=sample\\:parent_id]').val(data.outer_id);\n            jQuery('#occ-form > input[name=sample\\:date]').val(jQuery('#SurveyForm > input[name=sample\\:date]').val());\n            loadAttributes('sample_attribute_value', 'sample_attribute_id', 'sample_id', data.outer_id, 'smpAttr');\n            switch(\"" . $args["on_save_survey_nav"] . "\"){\n\t\t\t\tcase \"list\":\n\t\t\t\t\tvar a = \$('ul.ui-tabs-nav a')[2];\n\t\t\t\t\t\$(a).click();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"survey\":\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:";
       if (!user_access($args['edit_permission'])) {
           data_entry_helper::$javascript .= "\n\t\t\t\t\tif(jQuery('#main-sample-closed').val() == 0){\n\t\t\t\t\t\tvar a = \$('ul.ui-tabs-nav a')[1];\n\t\t\t\t\t\t\$(a).click();\n\t\t\t\t\t};";
       } else {
           data_entry_helper::$javascript .= "\n\t\t\t\t\tvar a = \$('ul.ui-tabs-nav a')[1];\n\t\t\t\t\t\$(a).click();";
       }
       data_entry_helper::$javascript .= "\n\t\t\t\t\tbreak;\n\t\t\t}\n        } else {\n\t\t\tif(data.error){\n\t\t\t\tvar lastIndex = data.error.lastIndexOf('Validation error');\n    \t\t\tif (lastIndex != -1 && lastIndex  == (data.error.length - 16)){\n\t\t\t\t\tif(data.errors){\n\t\t\t\t\t\t// TODO translation\n\t\t\t\t\t\tfor (i in data.errors){\n\t\t\t\t\t\t\tvar label = \$('<p/>').addClass('inline-error').html(data.errors[i]);\n\t\t\t\t\t\t\tlabel.insertAfter('[name='+i+']');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmyScrollToError();\n\t\t\t\t\t\treturn;\n  \t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\talertIndiciaError(data);\n        }\n\t},\n    complete: function (){\n  \t\tjQuery('.loading-button').removeClass('loading-button');\n  \t}\n});\n// In this case, all the samples attributes are on the survey tab, and all the occurrence attributes are on the occurrence tab. No need to worry about getting the correct form.\nloadAttributes = function(attributeTable, attributeKey, key, keyValue, prefix){\n    jQuery.ajax({\n        type: \"GET\",\n        url: \"" . $svcUrl . "/data/\" + attributeTable + \"?mode=json&view=list\" +\n        \t\"&reset_timeout=true&nonce=" . $readAuth['nonce'] . "&auth_token=" . $readAuth['auth_token'] . "\" +\n   \t\t\t\"&\" + key + \"=\" + keyValue + \"&callback=?\",\n        data: {},\n        success: (function(attrPrefix, attrKey) {\n          var retVal = function(attrdata) {\n            if(!(attrdata instanceof Array)){\n              alertIndiciaError(attrdata);\n            } else if (attrdata.length>0) {\n              for (var i=0;i<attrdata.length;i++){\n                // in all cases if the attribute already has the <prefix>:<X>:<Y> format name we leave. Other wise we update <prefix>:<X> to <prefix>:<X>:<Y>\n                // We leave all values unchanged.\n                // need to be careful about Cloud: this is a drop down, but it is not language specific: the termlist is\n                // always in english, so the iso won't match.\n                if (attrdata[i].id){\n                  if (attrdata[i].iso == null || attrdata[i].iso == '') // no iso - not a look up.\n                    jQuery('[name='+attrPrefix+'\\:'+attrdata[i][attrKey]+']').attr('name', attrPrefix+':'+attrdata[i][attrKey]+':'+attrdata[i].id);\n                  else {\n                    if (attrdata[i].iso == '" . $language . "') // this is our actual language so OK\n                      jQuery('[name='+attrPrefix+'\\:'+attrdata[i][attrKey]+']').attr('name', attrPrefix+':'+attrdata[i][attrKey]+':'+attrdata[i].id);\n                    else {// not our language: look up all the other attrs, and if we don't find one of this id for our language, use this one.\n                      var found = false;\n                      for (var j=0;j<attrdata.length;j++)\n                        found = found || (i!=j && attrdata[i][attrKey] == attrdata[j][attrKey] && attrdata[j].iso == '" . $language . "');\n                      if(!found)\n                        jQuery('[name='+attrPrefix+'\\:'+attrdata[i][attrKey]+']').attr('name', attrPrefix+':'+attrdata[i][attrKey]+':'+attrdata[i].id);\n                    }\n                  }\n                }\n              }\n            }};\n          return retVal;\n          })(prefix, attributeKey),\n\t\tdataType: 'json',\n\t    async: false\n\t});\n}";
       // Set up Occurrence List tab: don't include when creating a new sample as it will have no occurrences
       // Grid populated at a later point
       $r .= "<div id=\"occurrenceList\" class=\"mnhnl-btw-datapanel\"><div id=\"occ_grid\"></div>\n  <form method=\"post\" action=\"" . data_entry_helper::$base_url . "/index.php/services/report/requestReport?report=reports_for_prebuilt_forms/MNHNL/mnhnl_btw_occurrences_report.xml&reportSource=local&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth['nonce'] . "&mode=csv\">\n    <input type=\"hidden\" id=\"params\" name=\"params\" value='{\"survey_id\":" . $args['survey_id'] . ", \"sample_id\":" . data_entry_helper::$entity_to_load['sample:id'] . "}' />\n    <input type=\"submit\" class=\"ui-state-default ui-corner-all\" value=\"" . lang::get('LANG_Download_Occurrences') . "\">\n  </form></div>";
       if ($occReadOnly) {
           // NB that we don't even include the buttons when readonly.
           data_entry_helper::$javascript .= "\njQuery('#ro-occ-occ-warn').show();\njQuery('#ro-sur-occ-warn').hide();\n";
       } else {
           data_entry_helper::$javascript .= "\njQuery('#ro-occ-occ-warn').hide();\n";
       }
       if ($mode == 1) {
           data_entry_helper::$javascript .= "jQuery('#occ-form').hide();";
       } else {
           data_entry_helper::$javascript .= "jQuery('#na-occ-warn').hide();";
       }
       // Set up Occurrence tab: don't allow entry of a new occurrence until after top level sample is saved.
       data_entry_helper::$entity_to_load = $childSample;
       $attributes = data_entry_helper::getAttributes(array('id' => data_entry_helper::$entity_to_load['occurrence:id'], 'valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => 'occAttr', 'extraParams' => $readAuth));
       $extraParams = $readAuth + array('taxon_list_id' => $args['list_id'], 'view' => 'detail', 'query' => urlencode(json_encode(array('in' => array('language_iso', array('lat', iform_lang_iso_639_2($args['language'])))))));
       if ($occReadOnly) {
           // if the occurrence has been downloaded, no one can modify it.
           $disabledText = "disabled=\"disabled\"";
           $defAttrOptions['disabled'] = $disabledText;
       }
       $species_ctrl_args = array('label' => lang::get('LANG_Species'), 'fieldname' => 'occurrence:taxa_taxon_list_id', 'table' => 'taxa_taxon_list', 'captionField' => 'taxon', 'valueField' => 'id', 'columns' => 2, 'extraParams' => $extraParams, 'disabled' => $disabledText, 'defaultCaption' => data_entry_helper::$entity_to_load['occurrence:taxon']);
       $r .= "  <div id=\"occurrence\" class=\"mnhnl-btw-datapanel\">\n    <p id=\"ro-occ-occ-warn\"><strong>" . lang::get('LANG_Read_Only_Occurrence') . "</strong></p>\n    <p id=\"ro-sur-occ-warn\"><strong>" . lang::get('LANG_Read_Only_Survey') . "</strong></p>\n    <p id=\"na-occ-warn\"><strong>" . lang::get('LANG_Page_Not_Available') . "</strong></p>\n    <form method=\"post\" id=\"occ-form\" action=\"" . iform_ajaxproxy_url($node, 'smp-occ') . "\" >\n    <input type=\"hidden\" id=\"website_id\" name=\"website_id\" value=\"" . $args['website_id'] . "\" />\n    <input type=\"hidden\" id=\"sample:survey_id\" name=\"sample:survey_id\" value=\"" . $args['survey_id'] . "\" />\n    <input type=\"hidden\" id=\"sample:parent_id\" name=\"sample:parent_id\" value=\"" . $parentSample['sample:id'] . "\" />\n    <input type=\"hidden\" id=\"sample:date\" name=\"sample:date\" value=\"" . data_entry_helper::$entity_to_load['sample:date'] . "\" />\n    <input type=\"hidden\" id=\"sample:id\" name=\"sample:id\" value=\"" . data_entry_helper::$entity_to_load['sample:id'] . "\" />\n    <input type=\"hidden\" id=\"occurrence:id\" name=\"occurrence:id\" value=\"" . data_entry_helper::$entity_to_load['occurrence:id'] . "\" />\n    <input type=\"hidden\" id=\"occurrence:record_status\" name=\"occurrence:record_status\" value=\"" . ($closedFieldValue == '0' ? 'I' : 'C') . "\" />\n    <input type=\"hidden\" id=\"occurrence:downloaded_flag\" name=\"occurrence:downloaded_flag\" value=\"N\" />\n    " . data_entry_helper::autocomplete($species_ctrl_args) . "\n    " . ($occurrence_confidence_id ? data_entry_helper::outputAttribute($attributes[$occurrence_confidence_id], array_merge($languageFilteredAttrOptions, array('noBlankText' => ''))) : "<span style=\"display: none;\">Occurrence attribute '" . self::ATTR_CONFIDENCE . "' not assigned to this survey</span>") . "\n    " . data_entry_helper::sref_and_system(array('label' => lang::get('LANG_Spatial_ref'), 'systems' => array('2169' => 'Luref (Gauss Luxembourg)'))) . "\n    <p>" . lang::get('LANG_Click_on_map') . "</p>\n    " . data_entry_helper::outputAttribute($attributes[$occurrence_count_id], array_merge($defAttrOptions, array('default' => 1))) . "\n    " . ($occurrence_approximation_id ? data_entry_helper::outputAttribute($attributes[$occurrence_approximation_id], $defAttrOptions) : "<span style=\"display: none;\">Occurrence attribute '" . self::ATTR_APPROXIMATION . "' not assigned to this survey</span>") . "\n    " . data_entry_helper::outputAttribute($attributes[$occurrence_territorial_id], array_merge($defAttrOptions, array('default' => 1, 'id' => 'occ-territorial'))) . "\n    " . data_entry_helper::outputAttribute($attributes[$occurrence_atlas_code_id], $languageFilteredAttrOptions) . "\n    " . ($occurrence_overflying_id ? data_entry_helper::outputAttribute($attributes[$occurrence_overflying_id], $defAttrOptions) : "<span style=\"display: none;\">Occurrence attribute '" . self::ATTR_OVERFLYING . "' not assigned to this survey</span>") . "\n    " . data_entry_helper::textarea(array('label' => lang::get('LANG_Comment'), 'fieldname' => 'occurrence:comment', 'disabled' => $disabledText));
       if (!$surveyReadOnly && !$occReadOnly) {
           if ($mode == 3) {
               $r .= data_entry_helper::checkbox(array('label' => lang::get('Delete'), 'fieldname' => 'sample:deleted', 'id' => 'occ-sample-deleted'));
           }
           $r .= "<input type=\"submit\" id=\"occ-submit\" class=\"ui-state-default ui-corner-all\" value=\"" . lang::get('LANG_Save_Occurrence_Details') . "\" />";
       }
       $r .= "  </form>\n";
       data_entry_helper::$javascript .= "\n// because of ID tracking it is easier to rebuild entire list etc.\nretriggerGrid = function(){\n  \$('div#occ_grid').empty();\n  occListLayer.destroyFeatures();\n  activateAddList = 1;\n  thisOccID = -1;\n  \$('div#occ_grid').indiciaDataGrid('rpt:reports_for_prebuilt_forms/MNHNL/mnhnl_btw_list_occurrences', {\n    indiciaSvc: '" . $svcUrl . "',\n    dataColumns: ['taxon', 'territorial', 'count'],\n    reportColumnTitles: {taxon : '" . lang::get('LANG_Species') . "', territorial : '" . lang::get('LANG_Territorial') . "', count : '" . lang::get('LANG_Count') . "'},\n    actionColumns: {'" . lang::get('LANG_Show') . "' : \"" . url('node/' . $node->nid, array('query' => 'occurrence_id=£id£')) . "\",\n            '" . lang::get('LANG_Highlight') . "' : \"script:highlight(£id£);\"},\n    auth : { nonce : '" . $readAuth['nonce'] . "', auth_token : '" . $readAuth['auth_token'] . "'},\n    parameters : { survey_id : '" . $args['survey_id'] . "',\n            parent_id : jQuery('#SurveyForm [name=sample\\:id]').val(),\n            territorial_attr_id : '" . $occurrence_territorial_id . "',\n            count_attr_id : '" . $occurrence_count_id . "'},\n    itemsPerPage : 12,\n    callback : addListFeature ,\n    cssOdd : ''\n  });\n}\n\njQuery('#occ-form').ajaxForm({\n\tasync: false,\n\tdataType:  'json',\n    beforeSubmit:   function(data, obj, options){\n    \tvar valid = true;\n    \tclearErrors('form#occ-form');\n    \tif (!jQuery('form#occ-form > input').valid()) { valid = false; }\n    \tif (!jQuery('form#occ-form > select').valid()) { valid = false; }\n    \tif(!valid) {\n\t\t\tmyScrollToError();\n\t\t\treturn false;\n\t\t};\n\t\tjQuery('#occ-submit').addClass('loading-button');\n\t\treturn true;\n\t},\n    success:   function(data){\n       // this will leave all the fields populated.\n       \tif(data.success == 'multiple records' && data.outer_table == 'sample'){\n\t\t\twindow.scroll(0,0);\n\t\t\t// cant use reset form, as returns it to original values: if this was called with occurrence_id =<x> then it would repopulate with original occurrence's values\n\t\t\t// website_id, survey_id, record_status, downloaded_flag, sample:entered_sref_system are constants and are left alone. parent_id, date are only set referring to parent sample.\n\t\t\tjQuery('form#occ-form').find('[name^=occAttr\\:]').each(function(){\n\t\t\t\tvar name = jQuery(this).attr('name').split(':');\n\t\t\t\tjQuery(this).attr('name', name[0]+':'+name[1]);\n\t\t\t});\n\t\t\tjQuery('form#occ-form').find('[name=occurrence\\:id],[name=sample\\:id]').val('').attr('disabled', 'disabled');\n\t\t\tjQuery('form#occ-form').find('[name=occurrence\\:taxa_taxon_list_id],[name=occurrence\\:taxa_taxon_list_id\\:taxon],[name=sample\\:entered_sref],[name=sample\\:geom],[name=occurrence\\:comment]').val('');\n\t\t\tjQuery('form#occ-form').find('[name=occAttr\\:" . $occurrence_confidence_id . "]').find('option').removeAttr('selected');\n\t\t\tjQuery('form#occ-form').find('[name=occAttr\\:" . $occurrence_count_id . "]').val('1');\n\t\t\tjQuery('form#occ-form').find('input[name=occAttr\\:" . $occurrence_approximation_id . "],input[name=occAttr\\:" . $occurrence_overflying_id . "]').removeAttr('checked','checked');\n\t\t\tjQuery('form#occ-form').find('#occ-territorial').attr('checked','checked');\n\t\t\tjQuery('label[for=occ-sample-deleted]').remove(); // sample deleted only applicable when editing an existing occurrence. After saving reverts to Add Occurreence: no delete. Remove label then actual checkbox\n\t\t\tjQuery('form#occ-form').find('[name=sample\\:deleted]').remove(); // This removes both parts of the checkbox.\n\t\t\tsetAtlasStatus();\n\t\t\tretriggerGrid();\n\t\t\tlocationLayer.map.editLayer.destroyFeatures();\n\t\t\tvar a = \$('ul.ui-tabs-nav a')[1];\n\t\t\t\$(a).empty().html('<span>" . lang::get('LANG_Add_Occurrence') . "</span>');\n\t\t\tswitch(\"" . $args["on_save_occurrence_nav"] . "\"){\n\t\t\t\tcase \"list\":\n\t\t\t\t\ta = \$('ul.ui-tabs-nav a')[2];\n\t\t\t\t\t\$(a).click();\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"survey\":\n\t\t\t\t\ta = \$('ul.ui-tabs-nav a')[0];\n\t\t\t\t\t\$(a).click();\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n        } else {\n\t\t\tif(data.error){\n\t\t\t\tvar lastIndex = data.error.lastIndexOf('Validation error');\n    \t\t\tif (lastIndex != -1 && lastIndex  == (data.error.length - 16)){\n\t\t\t\t\tif(data.errors){\n\t\t\t\t\t\t// TODO translation\n\t\t\t\t\t\tfor (i in data.errors){\n\t\t\t\t\t\t\tvar label = \$('<p/>').addClass('inline-error').html(data.errors[i]);\n\t\t\t\t\t\t\tlabel.insertAfter('[name='+i+']');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmyScrollToError();\n\t\t\t\t\t\treturn;\n  \t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\talertIndiciaError(data);\n        }\n\t},\n    complete: function (){\n  \t\tjQuery('.loading-button').removeClass('loading-button');\n  \t}\n});\nsetAtlasStatus = function() {\n  if (jQuery(\"#occ-territorial:checked\").length == 0) {\n      jQuery(\"select[name=occAttr\\:" . $occurrence_atlas_code_id . "],select[name^=occAttr\\:" . $occurrence_atlas_code_id . "\\:]\").val('');\n  } else {\n      if(jQuery(\"select[name=occAttr\\:" . $occurrence_atlas_code_id . "],select[name^=occAttr\\:" . $occurrence_atlas_code_id . "\\:]\").val() == '') {\n        // Find the BB02 option (depends on the language what val it has)\n        var bb02;\n        jQuery.each(jQuery(\"select[name=occAttr\\:" . $occurrence_atlas_code_id . "],select[name^=occAttr\\:" . $occurrence_atlas_code_id . "\\:]\").find('option'), function(index, option) {\n          if (option.text.substr(0,4)=='BB02') {\n            bb02 = option.value;\n            return; // just from the each loop\n          }\n        });\n        jQuery(\"select[name=occAttr\\:" . $occurrence_atlas_code_id . "],select[name^=occAttr\\:" . $occurrence_atlas_code_id . "\\:]\").val(bb02);\n      }\n  }\n};\njQuery(\"#occ-territorial\").change(setAtlasStatus);\nif(\$.browser.msie) {\n    jQuery(\"#occ-territorial\").click(function() {\n        \$(this).change();\n    });\n}\n\n";
       if ($mode != 3) {
           data_entry_helper::$javascript .= "setAtlasStatus();\n";
       }
       // reset the atlas when not looking at a old occurrence.
       $r .= '</div>';
       // add map panel.
       $options = iform_map_get_map_options($args, $readAuth);
       $olOptions = iform_map_get_ol_options($args);
       // $options['layers'] = array('locationLayer', 'occListLayer');
       $options['searchLayer'] = 'false';
       $options['initialFeatureWkt'] = null;
       $options['proxy'] = '';
       $options['scroll_wheel_zoom'] = false;
       $options['width'] = 'auto';
       // TBD remove from arglist
       $r .= "<div class=\"mnhnl-btw-mappanel\">\n";
       $r .= data_entry_helper::map_panel($options, $olOptions);
       // for timing reasons, all the following has to be done after the map is loaded.
       // 1) feature selector for occurrence list must have the map present to attach the control
       // 2) location placer must have the location layer populated and the map present in
       //    order to zoom the map into the location.
       // 3) occurrence list feature adder must have map present in order to zoom into any
       //    current selection.
       data_entry_helper::$onload_javascript .= "\n\n\nlocationChange = function(obj){\n  locationLayer.destroyFeatures();\n  if(obj.value != ''){\n    jQuery.getJSON(\"" . $svcUrl . "\" + \"/data/location/\"+obj.value +\n      \"?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"] . "\" +\n      \"&callback=?\", function(data) {\n            if (data.length>0) {\n              var parser = new OpenLayers.Format.WKT();\n              for (var i=0;i<data.length;i++)\n        {\n          if(data[i].centroid_geom){\n            " . self::readBoundaryJs('data[i].centroid_geom', $args['map_projection']) . "\n            feature.style = {label: data[i].name,\n\t\t\t\t\t\t     strokeColor: \"Green\",\n                             strokeWidth: 2,\n                             fillOpacity: 0};\n            centre = feature.geometry.getCentroid();\n            centrefeature = new OpenLayers.Feature.Vector(centre, {}, {label: data[i].name});\n            locationLayer.addFeatures([feature, centrefeature]);\n          }\n          if(data[i].boundary_geom){\n            " . self::readBoundaryJs('data[i].boundary_geom', $args['map_projection']) . "\n            feature.style = {strokeColor: \"Blue\", strokeWidth: 2};\n            locationLayer.addFeatures([feature]);\n          }\n          locationLayer.map.zoomToExtent(locationLayer.getDataExtent());\n        }\n      }\n    });\n     jQuery.getJSON(\"" . $svcUrl . "\" + \"/data/location\" +\n      \"?mode=json&view=detail&auth_token=" . $readAuth['auth_token'] . "&nonce=" . $readAuth["nonce"] . "&callback=?&parent_id=\"+obj.value, function(data) {\n            if (data.length>0) {\n              var parser = new OpenLayers.Format.WKT();\n              for (var i=0;i<data.length;i++)\n        {\n          if(data[i].centroid_geom){\n            " . self::readBoundaryJs('data[i].centroid_geom', $args['map_projection']) . "\n            locationLayer.addFeatures([feature]);\n          }\n          if(data[i].boundary_geom){\n            " . self::readBoundaryJs('data[i].boundary_geom', $args['map_projection']) . "\n            feature.style = {label: data[i].name,\n              labelAlign: \"cb\",\n              strokeColor: \"Blue\",\n                        strokeWidth: 2};\n            locationLayer.addFeatures([feature]);\n           }\n         }\n      }\n        });\n  }\n};\n// upload location initial value into map.\njQuery('#imp-location').each(function(){\n  locationChange(this);\n});\njQuery('#imp-location').unbind('change');\njQuery('#imp-location').change(function(){\n  locationChange(this);\n});\nvar selected = \$('#controls').tabs('option', 'selected');\n\n// Only leave the click control activated for edit/add occurrence tab.\nif(selected != 1){\n    locationLayer.map.editLayer.clickControl.deactivate();\n}\n\$('#controls').bind('tabsshow', function(event, ui) {\n        if(ui.index == 1)\n        {\n         locationLayer.map.editLayer.clickControl.activate();\n        }\n        else\n        {\n         locationLayer.map.editLayer.clickControl.deactivate();\n        }\n    }\n);\nactivateAddList = 1;\nthisOccID = " . $thisOccID . ";\naddListFeature = function(div, r, record, count) {\n  if(activateAddList == 0)\n    return;\n  if(r == count)\n    activateAddList = 0;\n    var parser = new OpenLayers.Format.WKT();\n    " . self::readBoundaryJs('record.geom', $args['map_projection']) . "\n    if(record.id != thisOccID || 1==" . ($surveyReadOnly ? 1 : 0) . " || 1==" . ($occReadOnly ? 1 : 0) . "){\n      feature.attributes.id = record.id;\n      feature.attributes.taxon = record.taxon;\n      feature.attributes.count = record.count;\n      occListLayer.addFeatures([feature]);\n      if(record.id == " . $thisOccID . "){\n        var bounds=feature.geometry.getBounds();\n        locationLayer.map.setCenter(bounds.getCenterLonLat());\n      }\n    } else {\n      locationLayer.map.editLayer.destroyFeatures();\n      locationLayer.map.editLayer.addFeatures([feature]);\n      var bounds=feature.geometry.getBounds()\n      var centre=bounds.getCenterLonLat();\n      locationLayer.map.setCenter(centre);\n    }\n};\nhighlight = function(id){\n  if(id == " . $thisOccID . "){\n    if(occListLayer.map.editLayer.features.length > 0){\n      var bounds=occListLayer.map.editLayer.features[0].geometry.getBounds()\n      var centre=bounds.getCenterLonLat();\n      occListLayer.map.setCenter(centre);\n      return;\n    }\n  }\n  for(var i = 0; i < occListLayer.features.length; i++){\n    if(occListLayer.features[i].attributes.id == id){\n      control.unselectAll();\n      var bounds=occListLayer.features[i].geometry.getBounds()\n      var centre=bounds.getCenterLonLat();\n      occListLayer.map.setCenter(centre);\n      control.select(occListLayer.features[i]);\n      return;\n    }\n  }\n}\n";
       if ($mode != 1) {
           data_entry_helper::$onload_javascript .= "\n\$('div#occ_grid').indiciaDataGrid('rpt:reports_for_prebuilt_forms/MNHNL/mnhnl_btw_list_occurrences', {\n    indiciaSvc: '" . $svcUrl . "',\n    dataColumns: ['taxon', 'territorial', 'count'],\n    reportColumnTitles: {taxon : '" . lang::get('LANG_Species') . "', territorial : '" . lang::get('LANG_Territorial') . "', count : '" . lang::get('LANG_Count') . "'},\n    actionColumns: {'" . lang::get('LANG_Show') . "' : \"" . url('node/' . $node->nid, array('query' => 'occurrence_id=£id£')) . "\",\n            '" . lang::get('LANG_Highlight') . "' : \"script:highlight(£id£);\"},\n    auth : { nonce : '" . $readAuth['nonce'] . "', auth_token : '" . $readAuth['auth_token'] . "'},\n    parameters : { survey_id : '" . $args['survey_id'] . "',\n            parent_id : '" . $parentSample['sample:id'] . "',\n            territorial_attr_id : '" . $occurrence_territorial_id . "',\n            count_attr_id : '" . $occurrence_count_id . "'},\n    itemsPerPage : 12,\n    callback : addListFeature ,\n    cssOdd : ''\n  });\n\n// activateAddList = 0;\n\n";
       }
       $r .= "</div><div><form><input id=\"return-to-main\" type=\"button\" value=\"" . lang::get('LANG_Return') . "\" onclick=\"window.location.href='" . url('node/' . $node->nid, array('query' => 'Main')) . "'\"></form></div></div>\n";
       if (method_exists(get_called_class(), 'getTrailerHTML')) {
           $r .= call_user_func(array(get_called_class(), 'getTrailerHTML'), $args);
       }
       return $r;
   }
 public static function ajax_save($website_id, $password, $node)
 {
     iform_load_helpers(array('data_entry_helper'));
     //Build submission
     $Model = self::build_three_level_sample_with_occ_submission($_POST, $website_id, $password, $node->params['gpx_data_attr_id'], $node->params['dive_start_time_attr_id'], $node->params['exif_date_time_attr_id']);
     $node = node_load($nid);
     $conn = iform_get_connection_details($node);
     $postargs = "website_id=" . $conn['website_id'];
     $response = data_entry_helper::http_post(data_entry_helper::$base_url . '/index.php/services/security/get_nonce', $postargs, false);
     $nonce = $response['output'];
     $writeTokens = array('nonce' => $nonce, 'auth_token' => sha1($nonce . ":" . $conn['password']));
     //When the first page is saved we create a sample but we don't have a spatial reference. An attempt is made to read
     //a position from the first photo exif (elsewhere in code), however if GPS data can't be found on photo, then just fall back on a point on the Isle of Wight (as it is on land it won't get confused with a real position.
     if (empty($Model['fields']['entered_sref']['value'])) {
         drupal_set_message('Unable to find any GPS information, please correct this manually using the GPX upload or map tools');
         $Model['fields']['entered_sref_system']['value'] = '4277';
         $Model['fields']['entered_sref']['value'] = '50:41.0994N, 1:17.1864W';
     }
     //Save submission
     $response = data_entry_helper::forward_post_to('save', $Model, $writeTokens);
     echo json_encode($response);
 }
 /** 
  * Test that a basic date out of range test works.
  */
 public function testDateOutOfRange()
 {
     // Need a test rule we can use to check it works
     $ruleArr = array('verification_rule:title' => 'test', 'verification_rule:test_type' => 'PeriodWithinYear', 'verification_rule:error_message' => 'test error', 'metaFields:metadata' => "Tvk=TESTKEY\nStartDate=0801\nEndDate=0831", 'metaFields:data' => "");
     $rule = ORM::Factory('verification_rule');
     $rule->set_submission_data($ruleArr, false);
     if (!$rule->submit()) {
         echo kohana::debug($rule->getAllErrors());
         throw new exception('Failed to create test rule');
     }
     try {
         $response = data_entry_helper::http_post($this->request, array('sample' => json_encode(array('sample:survey_id' => 1, 'sample:date' => '12/09/2012', 'sample:entered_sref' => 'SU1234', 'sample:entered_sref_system' => 'osgb')), 'occurrences' => json_encode(array(array('occurrence:taxa_taxon_list_id' => $this->ttl->id))), 'rule_types' => json_encode(array('PeriodWithinYear'))));
         $errors = json_decode($response['output'], true);
         $this->assertTrue(is_array($errors), 'Errors list not returned');
         $this->assertTrue(isset($errors[0]['taxa_taxon_list_id']) && $errors[0]['taxa_taxon_list_id'] === $this->ttl->id, 'Incorrect taxa_taxon_list_id returned');
         $this->assertTrue(isset($errors[0]['message']) && $errors[0]['message'] === 'test error', 'Incorrect message returned');
         foreach ($rule->verification_rule_metadata as $m) {
             $m->delete();
         }
         $rule->delete();
     } catch (Exception $e) {
         foreach ($rule->verification_rule_metadata as $m) {
             $m->delete();
         }
         $rule->delete();
     }
 }
 /**
  * Takes uploaded files from the form submission and saves them on the server. Optionally sends it
  * to the warehouse via the data services handle_media method, in which case the local copy is
  * deleted.
  *
  * @param String $media_id Base name of the file entry in the $_FILES array e.g. occurrence:image.
  * Multiple files can be uploaded if they have a suffix 0f :0, :1 ... :n
  * @param Boolean $submit If true (default), then the file is uploaded first into the local
  * server's directory structure, then sent to the Indicia Warehouse using the data services.
  * If false, then it is uploaded to the local server only.
  * @return Array Returns with key success, set to true if files were uploaded, false if nothing was uploaded or there
  * was an error. If an error, then there can be a single error key or an array caller errors with details of errors encountered 
  * when uploading. On success, the return array also has a key 'files' populated with final filenames of the uploaded files 
  * so that they can be inserted in a field which stores the file path.
  */
 public static function handle_media($media_id, $submit = true)
 {
     $files = self::get_uploaded_files($media_id);
     $return = array();
     $uploadpath = parent::$upload_path;
     $target_url = parent::$base_url . "/index.php/services/data/handle_media";
     $i = 0;
     foreach ($files as $file) {
         $name = $file['name'];
         $fname = $file['tmp_name'];
         $parts = explode(".", $name);
         $fext = array_pop($parts);
         // Generate a file id to store the image as
         $destination = time() . rand(0, 1000) . "." . $fext;
         if (move_uploaded_file($fname, $uploadpath . $destination)) {
             //successfully stored locally
             if ($submit) {
                 //send to the warehouse
                 $postargs = array();
                 if (array_key_exists('auth_token', $_POST)) {
                     $postargs['auth_token'] = $_POST['auth_token'];
                 }
                 if (array_key_exists('nonce', $_POST)) {
                     $postargs['nonce'] = $_POST['nonce'];
                 }
                 $file_to_upload = array('media_upload' => '@' . realpath($uploadpath . $destination));
                 $response = data_entry_helper::http_post($target_url, $file_to_upload + $postargs);
                 $output = json_decode($response['output'], true);
                 if (is_array($output)) {
                     //an array signals an error
                     if (array_key_exists('error', $output)) {
                         $return['error'] = $output['error'];
                         //attach the errors to the control that caused them
                         //PROBLEM assumes numeric suffix
                         if (array_key_exists('errors', $output)) {
                             $return['errors']["{$media_id}:{$i}"] = $output['errors']['media_upload'];
                         }
                     }
                 } else {
                     //filenames are returned without structure
                     //the output of json_decode may not be valid.
                     $return['files'][] = $response['output'];
                 }
                 //remove local copy
                 unlink($uploadpath . $destination);
             } else {
                 // store locally so create the thumbnails or other image sizes.
                 Image::create_image_files($uploadpath, $destination);
                 $return['files'][] = $destination;
             }
         } else {
             $return['error'] = 'Upload error';
             //attach the errors to the control that caused them
             //PROBLEM assumes numeric suffix
             $return['errors']["{$media_id}:{$i}"] = 'Sorry, there was a problem uploading this file.';
         }
         $i++;
     }
     //finished looping through files. If no errors in the response array, all went well.
     $return['success'] = !(array_key_exists('error', $return) || array_key_exists('errors', $return)) && count($files) > 0;
     return $return;
 }
 /**
  * Outputs the form for mapping columns to the import fields.
  * @param array $options Options array passed to the import control.
  */
 private static function upload_mappings_form($options)
 {
     //The Shorewatch importer only supports 4326 as this is required for the occurrence sample grid reference
     //calculations to work. This can be hardcoded.
     $_POST['sample:entered_sref_system'] = 4326;
     $_SESSION['importSettingsToCarryForward'] = $_POST;
     if (!file_exists($_SESSION['uploaded_file'])) {
         return lang::get('upload_not_available');
     }
     data_entry_helper::add_resource('jquery_ui');
     $filename = basename($_SESSION['uploaded_file']);
     // If the last step was skipped because the user did not have any settings to supply, presetSettings contains the presets.
     // Otherwise we'll use the settings form content which already in $_POST so will overwrite presetSettings.
     if (isset($options['presetSettings'])) {
         $settings = array_merge($options['presetSettings'], $_POST);
     } else {
         $settings = $_POST;
     }
     // only want defaults that actually have a value - others can be set on a per-row basis by mapping to a column
     foreach ($settings as $key => $value) {
         if (empty($value)) {
             unset($settings[$key]);
         }
     }
     //The Shorewatch importer only supports 4326 as this is required for the occurrence sample grid reference
     //calculations to work. This can be hardcoded.
     $settings['sample:entered_sref_system'] = 4326;
     // cache the mappings
     $metadata = array('settings' => json_encode($settings));
     $post = array_merge($options['auth']['write_tokens'], $metadata);
     $request = data_entry_helper::$base_url . "index.php/services/import/cache_upload_metadata?uploaded_csv={$filename}";
     $response = data_entry_helper::http_post($request, $post);
     if (!isset($response['output']) || $response['output'] != 'OK') {
         return "Could not upload the settings metadata. <br/>" . print_r($response, true);
     }
     $request = data_entry_helper::$base_url . "index.php/services/import/get_import_fields/" . $options['model'];
     $request .= '?' . data_entry_helper::array_to_query_string($options['auth']['read']);
     // include survey and website information in the request if available, as this limits the availability of custom attributes
     if (!empty($settings['website_id'])) {
         $request .= '&website_id=' . trim($settings['website_id']);
     }
     if (!empty($settings['survey_id'])) {
         $request .= '&survey_id=' . trim($settings['survey_id']);
     }
     $response = data_entry_helper::http_post($request, array());
     $fields = json_decode($response['output'], true);
     if (!is_array($fields)) {
         return "curl request to {$request} failed. Response " . print_r($response, true);
     }
     $request = str_replace('get_import_fields', 'get_required_fields', $request);
     $response = data_entry_helper::http_post($request);
     $responseIds = json_decode($response['output'], true);
     if (!is_array($responseIds)) {
         return "curl request to {$request} failed. Response " . print_r($response, true);
     }
     $model_required_fields = self::expand_ids_to_fks($responseIds);
     if (!empty($settings)) {
         $preset_fields = self::expand_ids_to_fks(array_keys($settings));
     } else {
         $preset_fields = array();
     }
     if (!empty($preset_fields)) {
         $unlinked_fields = array_diff_key($fields, array_combine($preset_fields, $preset_fields));
     } else {
         $unlinked_fields = $fields;
     }
     // only use the required fields that are available for selection - the rest are handled somehow else
     $unlinked_required_fields = array_intersect($model_required_fields, array_keys($unlinked_fields));
     ini_set('auto_detect_line_endings', 1);
     $handle = fopen($_SESSION['uploaded_file'], "r");
     $columns = fgetcsv($handle, 1000, ",");
     $reload = data_entry_helper::get_reload_link_parts();
     $reloadpath = $reload['path'] . '?' . data_entry_helper::array_to_query_string($reload['params']);
     self::clear_website_survey_fields($unlinked_fields, $settings);
     self::clear_website_survey_fields($unlinked_required_fields, $settings);
     $savedFieldMappings = array();
     // Note the Shorewatch importer doesn't currently support remembered fields, so set this to false (we are reusing a lot of the import_helper code, so leave the variable in the code as it already has proven reliability).
     self::$rememberingMappings = false;
     //  if the user checked the Remember All checkbox, save it in a variable
     if (isset($savedFieldMappings['RememberAll'])) {
         $checked['RememberAll'] = 'checked';
     }
     $r = "<form method=\"post\" id=\"entry_form\" action=\"{$reloadpath}\" class=\"iform\">\n" . '<p>' . lang::get('column_mapping_instructions') . '</p>' . '<div class="ui-helper-clearfix import-mappings-table"><table class="ui-widget ui-widget-content">' . '<thead class="ui-widget-header">' . "<tr><th>Column in CSV File</th><th>Maps to attribute</th>";
     if (self::$rememberingMappings) {
         $r .= "<th id='remember-all-header' name='remember-all-header'>" . lang::get('Remember choice?') . "<br/><input type='checkbox' name='RememberAll' id='RememberAll' value='1' title='Tick all boxes to remember every column mapping next time you import.' {$checked['RememberAll']} onclick='\n           if (this.checked) {\n             \$(\".rememberField\").attr(\"checked\",\"checked\")\n           } else {\n             \$(\".rememberField\").removeAttr(\"checked\")\n           }'/></th>";
     }
     $r .= '</tr></thead><tbody>';
     foreach ($columns as $column) {
         $colFieldName = preg_replace('/[^A-Za-z0-9]/', '_', $column);
         $r .= "<tr><td>{$column}</td><td><select name=\"{$colFieldName}\" id=\"{$colFieldName}\">";
         $r .= self::get_column_options($options['model'], $unlinked_fields, $column, ' ', $savedFieldMappings);
         $r .= "</select></td></tr>\n";
     }
     $r .= '</tbody>';
     $r .= '</table>';
     $r .= '<div id="required-instructions" class="import-mappings-instructions"><h2>' . lang::get('Tasks') . '</h2><span>' . lang::get('The following database attributes must be matched to a column in your import file before you can continue') . ':</span><ul></ul><br/></div>';
     $r .= '<div id="duplicate-instructions" class="import-mappings-instructions"><span id="duplicate-instruct">' . lang::get('There are currently two or more drop-downs allocated to the same value.') . '</span><ul></ul><br/></div></div>';
     $r .= '<input type="hidden" name="import_step" value="2" />';
     $r .= '<input type="submit" name="submit" id="submit" value="' . lang::get('Upload') . '" class="ui-corner-all ui-state-default button" />';
     $r .= '</form>';
     data_entry_helper::$javascript .= "function detect_duplicate_fields() {\n      var valueStore = [];\n      var duplicateStore = [];\n      var valueStoreIndex = 0;\n      var duplicateStoreIndex = 0;\n      \$.each(\$('#entry_form select'), function(i, select) {\n        if (valueStoreIndex==0) {\n          valueStore[valueStoreIndex] = select.value;\n          valueStoreIndex++;\n        } else {\n          for(i=0; i<valueStoreIndex; i++) {\n            if (select.value==valueStore[i] && select.value != '<" . lang::get('Not imported') . ">') {\n              duplicateStore[duplicateStoreIndex] = select.value;\n              duplicateStoreIndex++;\n            }\n             \n          }\n          valueStore[valueStoreIndex] = select.value;\n          valueStoreIndex++;\n        }      \n      })\n      if (duplicateStore.length==0) {\n        DuplicateAllowsUpload = 1;\n        \$('#duplicate-instruct').css('display', 'none');\n      } else {\n        DuplicateAllowsUpload = 0;\n        \$('#duplicate-instruct').css('display', 'inline');\n      }\n    }\n";
     data_entry_helper::$javascript .= "function update_required_fields() {\n      // copy the list of required fields\n      var fields = \$.extend(true, {}, required_fields);\n      \$('#required-instructions li').remove();\n      var sampleVagueDates = [];\n      // strip out the ones we have already allocated\n      \$.each(\$('#entry_form select'), function(i, select) {\n        delete fields[select.value];\n        // special case for vague dates - if we have a complete sample vague date, then can strike out the sample:date required field\n        if (select.value.substr(0,12)=='sample:date_') {\n          sampleVagueDates.push(select.value);\n        }\n      });\n      if (sampleVagueDates.length==3) {\n        // got a full vague date, so can remove the required date field\n        delete fields['sample:date'];\n      }\n      var output = '';\n      \$.each(fields, function(field, caption) {\n        output += '<li>'+caption+'</li>';\n      });\n      if (output==='') {\n        \$('#required-instructions').css('display', 'none');\n        RequiredAllowsUpload = 1;\n      } else {\n        \$('#required-instructions').css('display', 'inline');\n        RequiredAllowsUpload = 0;\n      }\n      if (RequiredAllowsUpload == 1 && DuplicateAllowsUpload == 1) {\n        \$('#submit').attr('disabled', false);\n      } else {\n        \$('#submit').attr('disabled', true);\n      }\n      \$('#required-instructions ul').html(output);\n    }\n";
     data_entry_helper::$javascript .= "required_fields={};\n";
     foreach ($unlinked_required_fields as $field) {
         $caption = $unlinked_fields[$field];
         if (empty($caption)) {
             $tokens = explode(':', $field);
             $fieldname = $tokens[count($tokens) - 1];
             $caption = lang::get(self::leadingCaps(preg_replace(array('/^fk_/', '/_id$/'), array('', ''), $fieldname)));
         }
         $caption = self::translate_field($field, $caption);
         data_entry_helper::$javascript .= "required_fields['{$field}']='{$caption}';\n";
     }
     data_entry_helper::$javascript .= "detect_duplicate_fields();\n";
     data_entry_helper::$javascript .= "update_required_fields();\n";
     data_entry_helper::$javascript .= "\$('#entry_form select').change(function() {detect_duplicate_fields(); update_required_fields();});\n";
     return $r;
 }
Esempio n. 10
0
 /**
  * Creates a site using the form submission data and attaches the location_id to the
  * sample information in the submission.
  * @param array Form submission data. 
  */
 private static function create_personal_site(&$array)
 {
     // Check we don't already have a location ID, and have the other stuff we require
     if (!empty($array['sample:location_id']) || !array_key_exists('imp-location:name', $array) || !array_key_exists('sample:entered_sref', $array) || !array_key_exists('sample:entered_sref_system', $array)) {
         return;
     }
     $loc = array('location:name' => $array['imp-location:name'], 'location:centroid_sref' => $array['sample:entered_sref'], 'location:centroid_sref_system' => $array['sample:entered_sref_system'], 'locations_website:website_id' => $array['website_id']);
     if (!empty($array['sample:geom'])) {
         $loc['location:centroid_geom'] = $array['sample:geom'];
     }
     $submission = self::build_submission($loc, array('model' => 'location', 'subModels' => array('locations_website' => array('fk' => 'location_id'))));
     $request = parent::$base_url . "index.php/services/data/save";
     $postargs = 'submission=' . urlencode(json_encode($submission));
     // Setting persist_auth allows the write tokens to be re-used
     $postargs .= '&persist_auth=true&auth_token=' . $array['auth_token'];
     $postargs .= '&nonce=' . $array['nonce'];
     if (function_exists('hostsite_get_user_field')) {
         $postargs .= '&user_id=' . hostsite_get_user_field('indicia_user_id');
     }
     $response = data_entry_helper::http_post($request, $postargs);
     // The response should be in JSON if it worked
     if (isset($response['output'])) {
         $output = json_decode($response['output'], true);
         if (!$output) {
             throw new exception(print_r($response, true));
         } elseif (isset($output['success']) && $output['success'] === 'multiple records') {
             $array['sample:location_id'] = $output['outer_id'];
         } elseif (isset($output['success'])) {
             $array['sample:location_id'] = $output['success'];
         } else {
             throw new exception(print_r($response, true));
         }
     }
 }