/**
  * Return the generated output for the media grid tab.
  * @param array $args List of parameter values passed through to the form depending on how the form has been configured.
  *                    This array always contains a value for language.
  * @param integer $nid The Drupal node object's ID.
  * @param object $auth The full read-write authorisation.
  * @return HTML.
  */
 private static function get_media_tab($args, $nid, $auth)
 {
     global $user;
     data_entry_helper::add_resource('fancybox');
     data_entry_helper::add_resource('plupload');
     data_entry_helper::add_resource('jquery_ui');
     $limit1 = $args['sample_attribute_id_1_limit'];
     $limit2 = $args['sample_attribute_id_2_limit'];
     if (!isset($args['sample_attribute_id_2']) || $args['sample_attribute_id_2'] == "") {
         $limit2 = 1;
         $caption2 = "";
     } else {
         $caption2 = $attributesIdx[$args['sample_attribute_id_2']]['caption'];
     }
     $attributesIdx = data_entry_helper::getAttributes(array('valuetable' => 'sample_attribute_value', 'attrtable' => 'sample_attribute', 'key' => 'sample_id', 'fieldprefix' => 'smpAttr', 'extraParams' => $auth['read'], 'survey_id' => $args['survey_id'], 'sample_method_id' => $args['quadrat_level_sample_method_id'], 'multiValue' => false));
     $scd = isset($args['site_caption_differentiator']) && $args['site_caption_differentiator'] != "" ? explode(',', $args['site_caption_differentiator']) : array('1');
     $qcd = isset($args['quadrat_caption_differentiator']) && $args['quadrat_caption_differentiator'] != "" ? explode(',', $args['quadrat_caption_differentiator']) : array('1');
     $bumpf = isset($args['media_tab_bumpf']) && $args['media_tab_bumpf'] != "" ? $args['media_tab_bumpf'] : lang::get('Please use the caption field to indicate whether the image is of a particular ' . (count($qcd) > 1 ? 'aspect of a ' : '') . $attributesIdx[$args['sample_attribute_id_1']]['caption'] . ', or the shore/site.') . ' ' . lang::get('When you start typing you should see a set of appropriate options from which you can choose: this will guide you to enter the correct value and format. Alternative just pressing the down arrow key should give the full list.');
     //    'description'=>'Format is <x>:<n>=<txt>:<n>=<txt>[,<x>:<n>=<txt>:<n>=<txt>] where <x> is the attribute id, <n> is the attribute value, and <txt> is the replacement value to be used in the template.',
     $mappings = isset($args['quadrat_caption_attribute_mapping']) ? explode(',', $args['quadrat_caption_attribute_mapping']) : array();
     $mappingsList = array();
     foreach ($mappings as $mapping) {
         $parts = explode(':', $mapping);
         $map = array();
         for ($i = 1; $i < count($parts); $i++) {
             // skip first
             $map[] = explode('=', $parts[$i]);
         }
         $mappingsList['attr' . $parts[0]] = $map;
     }
     data_entry_helper::$javascript .= "indiciaData.limit1={$limit1};\r\nindiciaData.site_caption_template='" . (isset($args['site_caption_template']) ? $args['site_caption_template'] : 'Site') . "';\r\nindiciaData.site_caption_differentiator=" . json_encode($scd) . ";\r\nindiciaData.limit2={$limit2};\r\nindiciaData.quadrat_caption_template='" . str_replace(array('{caption1}', '{caption2}'), array($attributesIdx[$args['sample_attribute_id_1']]['caption'], $caption2), isset($args['quadrat_caption_template']) ? $args['quadrat_caption_template'] : '{caption1} {N1}') . "';\r\nindiciaData.quadrat_caption_differentiator=" . json_encode($qcd) . ";\r\nindiciaData.quadrat_caption_attribute_mapping=" . json_encode($mappingsList) . ";\r\n";
     $r = '<div id="media">' . '<p class="ui-state-highlight page-notice ui-corner-all">' . $bumpf . '<span style="display:none;">' . 'MEM:' . ini_get('memory_limit') . ' FILE:' . ini_get('upload_max_filesize') . ' POST:' . ini_get('post_max_size') . '</span>' . '<br/>' . lang::get('The maximum number of files you can upload is ') . (count($scd) + count($qcd) * $limit1 * $limit2) . '. The maximum filesize you can upload is 4M - the images will be resized to a maximum of 1500 pixels high or wide, to ensure this is the case.' . '.<p>' . data_entry_helper::file_box(array('table' => 'sample_medium', 'caption' => lang::get('Photos'), 'codeGenerated' => 'all', 'maxFileCount' => count($scd) + count($qcd) * $limit1 * $limit2, 'file_box_uploaded_extra_fieldsTemplate' => '<input type="hidden" name="{idField}" id="{idField}" value="{idValue}" />' . '<input type="hidden" name="{pathField}" id="{pathField}" value="{pathValue}" />' . '<input type="hidden" name="{typeField}" id="{typeField}" value="{typeValue}" />' . '<input type="hidden" name="{typeNameField}" id="{typeNameField}" value="{typeNameValue}" />' . '<input type="hidden" name="{deletedField}" id="{deletedField}" value="{deletedValue}" class="deleted-value" />' . '<input type="hidden" id="{isNewField}" value="{isNewValue}" />' . '<label for="{captionField}">Caption:</label><br/><input type="text" list="captions" maxlength="100" style="width: {imagewidth}px" name="{captionField}" id="{captionField}" value="{captionValue}"/>', 'resizeWidth' => '1500', 'resizeHeight' => '1500')) . (self::$readOnly ? '' : '<br/><input type="submit" value="' . lang::get('Save') . '" title="' . lang::get('Saves any data entered across all the tabs.') . '" />') . '<datalist id="captions"></datalist>' . '</div>';
     $typeTermData = data_entry_helper::get_population_data(array('table' => 'termlists_term', 'extraParams' => $auth['read'] + array('view' => 'cache', 'termlist_title' => 'Media types', 'columns' => 'id,term')));
     $typeTermIdLookup = array();
     foreach ($typeTermData as $record) {
         $typeTermIdLookup[$record['term']] = $record['id'];
     }
     data_entry_helper::$javascript .= "indiciaData.mediaTypeTermIdLookup=" . json_encode($typeTermIdLookup) . ";\n";
     return $r;
 }
 public static function get_sample_form($args, $node, $response)
 {
     global $user;
     if (!module_exists('iform_ajaxproxy')) {
         return 'This form must be used in Drupal with the Indicia AJAX Proxy module enabled.';
     }
     iform_load_helpers(array('map_helper'));
     $auth = data_entry_helper::get_read_write_auth($args['website_id'], $args['password']);
     $sampleId = isset($_GET['sample_id']) ? $_GET['sample_id'] : null;
     $locationId = null;
     if ($sampleId) {
         data_entry_helper::load_existing_record($auth['read'], 'sample', $sampleId, 'detail', false, true);
         $locationId = data_entry_helper::$entity_to_load['sample:location_id'];
     } else {
         // location ID also might be in the $_POST data after a validation save of a new record
         if (isset($_POST['sample:location_id'])) {
             $locationId = $_POST['sample:location_id'];
         }
     }
     $url = explode('?', $args['my_obs_page'], 2);
     $params = NULL;
     $fragment = NULL;
     // fragment is always at the end.
     if (count($url) > 1) {
         $params = explode('#', $url[1], 2);
         if (count($params) > 1) {
             $fragment = $params[1];
         }
         $params = $params[0];
     } else {
         $url = explode('#', $url[0], 2);
         if (count($url) > 1) {
             $fragment = $url[1];
         }
     }
     $args['my_obs_page'] = url($url[0], array('query' => $params, 'fragment' => $fragment, 'absolute' => TRUE));
     $r = '<form method="post" id="sample">';
     $r .= $auth['write'];
     $r .= '<input type="hidden" name="page" value="mainSample"/>';
     $r .= '<input type="hidden" name="website_id" value="' . $args['website_id'] . '"/>';
     if (isset(data_entry_helper::$entity_to_load['sample:id'])) {
         $r .= '<input type="hidden" name="sample:id" value="' . data_entry_helper::$entity_to_load['sample:id'] . '"/>';
     }
     $r .= '<input type="hidden" name="sample:survey_id" value="' . $args['survey_id'] . '"/>';
     $r .= '<div id="cols" class="ui-helper-clearfix"><div class="left" style="width: ' . (98 - (isset($args['percent_width']) ? $args['percent_width'] : 50)) . '%">';
     // Output only the locations for this website and location type.
     $availableSites = data_entry_helper::get_population_data(array('report' => 'library/locations/locations_list', 'extraParams' => $auth['read'] + array('website_id' => $args['website_id'], 'location_type_id' => $args['locationType'], 'locattrs' => 'CMS User ID', 'attr_location_cms_user_id' => $user->uid), 'nocache' => true));
     // convert the report data to an array for the lookup, plus one to pass to the JS so it can keep the map updated
     $sitesLookup = array();
     $sitesIds = array();
     $sitesJs = array();
     foreach ($availableSites as $site) {
         $sitesLookup[$site['location_id']] = $site['name'];
         $sitesIds[] = $site['location_id'];
     }
     $sites = data_entry_helper::get_population_data(array('table' => 'location', 'extraParams' => $auth['read'] + array('website_id' => $args['website_id'], 'id' => $sitesIds, 'view' => 'detail')));
     foreach ($sites as $site) {
         $sitesJs[$site['id']] = $site;
     }
     data_entry_helper::$javascript .= "indiciaData.sites = " . json_encode($sitesJs) . ";\n";
     if ($locationId) {
         $r .= '<input type="hidden" name="sample:location_id" id="sample_location_id" value="' . $locationId . '"/>';
         // for reload of existing, don't let the user switch the square as that could mess everything up.
         $r .= '<label>' . lang::get('1km square') . ':</label><span>' . $sitesJs[$locationId]['name'] . '</span><br/>' . lang::get('<p class="ui-state-highlight page-notice ui-corner-all">Please use the map to select a more precise location for your timed observation.</p>');
     } else {
         $options = array('label' => lang::get('Select 1km square'), 'validation' => array('required'), 'blankText' => lang::get('Please select'), 'lookupValues' => $sitesLookup, 'id' => "sample_location_id");
         // if ($locationId) $options['default'] = $locationId;
         $r .= data_entry_helper::location_select($options) . lang::get('<p class="ui-state-highlight page-notice ui-corner-all">After selecting the 1km square, use the map to select a more precise location for your timed observation.</p>');
     }
     // [spatial reference]
     $systems = array();
     foreach (explode(',', str_replace(' ', '', $args['spatial_systems'])) as $system) {
         $systems[$system] = lang::get("sref:{$system}");
     }
     $r .= data_entry_helper::sref_and_system(array('label' => lang::get('Grid Ref'), 'systems' => $systems));
     $r .= data_entry_helper::file_box(array('table' => 'sample_image', 'readAuth' => $auth['read'], 'caption' => lang::get('Upload photo(s) of timed search area')));
     $sampleMethods = helper_base::get_termlist_terms($auth, 'indicia:sample_methods', array('Field Observation'));
     $attributes = data_entry_helper::getAttributes(array('id' => $sampleId, 'valuetable' => 'sample_attribute_value', 'attrtable' => 'sample_attribute', 'key' => 'sample_id', 'fieldprefix' => 'smpAttr', 'extraParams' => $auth['read'], 'survey_id' => $args['survey_id'], 'sample_method_id' => $sampleMethods[0]['id']));
     $r .= get_user_profile_hidden_inputs($attributes, $args, '', $auth['read']);
     if (isset($_GET['date'])) {
         $r .= '<input type="hidden" name="sample:date" value="' . $_GET['date'] . '"/>';
         $r .= '<label>' . lang::get('Date') . ':</label> <span class="value-label">' . $_GET['date'] . '</span><br/>';
     } else {
         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
             // @todo The date format should be a global configurable option. It should also be applied to reloading of custom date attributes.
             $d = new DateTime(data_entry_helper::$entity_to_load['sample:date']);
             data_entry_helper::$entity_to_load['sample:date'] = $d->format('d/m/Y');
         }
         $r .= data_entry_helper::date_picker(array('label' => lang::get('Date'), 'fieldname' => 'sample:date'));
     }
     // are there any option overrides for the custom attributes?
     if (isset($args['custom_attribute_options']) && $args['custom_attribute_options']) {
         $blockOptions = get_attr_options_array_with_user_data($args['custom_attribute_options']);
     } else {
         $blockOptions = array();
     }
     $r .= get_attribute_html($attributes, $args, array('extraParams' => $auth['read']), null, $blockOptions);
     $r .= '<input type="hidden" name="sample:sample_method_id" value="' . $sampleMethods[0]['id'] . '" />';
     $r .= '<input type="submit" value="' . lang::get('Next') . '" />';
     $r .= '<a href="' . $args['my_obs_page'] . '" class="button">' . lang::get('Cancel') . '</a>';
     if (isset(data_entry_helper::$entity_to_load['sample:id'])) {
         $r .= '<button id="delete-button" type="button" class="ui-state-default ui-corner-all" />' . lang::get('Delete') . '</button>';
     }
     $r .= "</div>";
     // left
     $r .= '<div class="right" style="width: ' . (isset($args['percent_width']) ? $args['percent_width'] : 50) . '%">';
     // [place search]
     $georefOpts = iform_map_get_georef_options($args, $auth['read']);
     $georefOpts['label'] = lang::get('Search for Place on Map');
     // can't use place search without the driver API key
     if ($georefOpts['driver'] == 'geoplanet' && empty(helper_config::$geoplanet_api_key)) {
         $r .= '<span style="display: none;">The form structure includes a place search but needs a geoplanet api key.</span>';
     } else {
         $r .= data_entry_helper::georeference_lookup($georefOpts);
     }
     // [map]
     $options = iform_map_get_map_options($args, $auth['read']);
     if (!empty(data_entry_helper::$entity_to_load['sample:wkt'])) {
         $options['initialFeatureWkt'] = data_entry_helper::$entity_to_load['sample:wkt'];
     }
     $olOptions = iform_map_get_ol_options($args);
     if (!isset($options['standardControls'])) {
         $options['standardControls'] = array('layerSwitcher', 'panZoomBar');
     }
     $r .= map_helper::map_panel($options, $olOptions);
     data_entry_helper::$javascript .= "\nmapInitialisationHooks.push(function(mapdiv) {\n  var defaultStyle = new OpenLayers.Style({pointRadius: 6,fillOpacity: 0,strokeColor: \"Red\",strokeWidth: 1});\n  var SiteStyleMap = new OpenLayers.StyleMap({\"default\": defaultStyle});\n  indiciaData.SiteLayer = new OpenLayers.Layer.Vector('1km square',{styleMap: SiteStyleMap, displayInLayerSwitcher: true});\n  mapdiv.map.addLayer(indiciaData.SiteLayer);\n  if(jQuery('#sample_location_id').length > 0) {\n    if(jQuery('#sample_location_id').val() != ''){\n      var parser = new OpenLayers.Format.WKT();\n      var feature = parser.read(indiciaData.sites[jQuery('#sample_location_id').val()].geom);\n      indiciaData.SiteLayer.addFeatures([feature]);\n      // for existing data we zoom on the site, not this parent location\n    } \n    jQuery('#sample_location_id').change(function(){\n      indiciaData.SiteLayer.destroyFeatures();\n      if(jQuery('#sample_location_id').val() != ''){\n        var parser = new OpenLayers.Format.WKT();\n        var feature = parser.read(indiciaData.sites[jQuery('#sample_location_id').val()].geom);\n        indiciaData.SiteLayer.addFeatures([feature]);\n        var layerBounds = indiciaData.SiteLayer.getDataExtent().clone(); // use a clone\n        indiciaData.SiteLayer.map.zoomToExtent(layerBounds);\n      }\n    });\n  }\n});\n";
     $r .= "</div>";
     // right
     $r .= '</form>';
     // Recorder Name - assume Easy Login uid
     if (function_exists('module_exists') && module_exists('easy_login')) {
         $userId = hostsite_get_user_field('indicia_user_id');
         // For non easy login test only     $userId = 1;
         foreach ($attributes as $attrID => $attr) {
             if (strcasecmp('Recorder Name', $attr["untranslatedCaption"]) == 0 && !empty($userId)) {
                 // determining which you have used is difficult from a services based autocomplete, esp when the created_by_id is not available on the data.
                 data_entry_helper::add_resource('autocomplete');
                 data_entry_helper::$javascript .= "bindRecorderNameAutocomplete(" . $attrID . ", '" . $userId . "', '" . data_entry_helper::$base_url . "', '" . $args['survey_id'] . "', '" . $auth['read']['auth_token'] . "', '" . $auth['read']['nonce'] . "');\n";
             }
         }
     }
     if (isset(data_entry_helper::$entity_to_load['sample:id'])) {
         // allow deletes if sample id is present.
         data_entry_helper::$javascript .= "jQuery('#delete-button').click(function(){\n  if(confirm(\"" . lang::get('Are you sure you want to delete this walk?') . "\")){\n    jQuery('#delete-form').submit();\n  } // else do nothing.\n});\n";
         // note we only require bare minimum in order to flag a sample as deleted.
         $r .= '<form method="post" id="delete-form" style="display: none;">';
         $r .= $auth['write'];
         $r .= '<input type="hidden" name="page" value="delete"/>';
         $r .= '<input type="hidden" name="website_id" value="' . $args['website_id'] . '"/>';
         $r .= '<input type="hidden" name="sample:id" value="' . data_entry_helper::$entity_to_load['sample:id'] . '"/>';
         $r .= '<input type="hidden" name="sample:deleted" value="t"/>';
         $r .= '</form>';
     }
     data_entry_helper::enable_validation('sample');
     return $r;
 }
Esempio n. 3
0
 /**
  * Get the block of custom attributes at the species (occurrence) level
  */
 private static function get_control_speciesattributes($auth, $args, $tabalias, $options)
 {
     if (!self::getGridMode($args)) {
         // Add any dynamically generated controls
         $attrArgs = array('valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => 'occAttr', 'extraParams' => $auth['read'], 'survey_id' => $args['survey_id']);
         if (count(self::$occurrenceIds) == 1) {
             // if we have a single occurrence Id to load, use it to get attribute values
             $attrArgs['id'] = self::$occurrenceIds[0];
         }
         $attributes = data_entry_helper::getAttributes($attrArgs);
         $defAttrOptions = array('extraParams' => $auth['read']);
         $r = self::get_attribute_html($attributes, $args, $defAttrOptions);
         if ($args['occurrence_comment']) {
             $r .= data_entry_helper::textarea(array('fieldname' => 'occurrence:comment', 'label' => lang::get('Record Comment')));
         }
         if ($args['occurrence_images']) {
             $opt = array('table' => 'occurrence_image', 'label' => lang::get('Upload your photos'));
         }
         if ($args['interface'] !== 'one_page') {
             $opts['tabDiv'] = $tabalias;
         }
         $r .= data_entry_helper::file_box($opts);
         return $r;
     } else {
         // in grid mode the attributes are embedded in the grid.
         return '';
     }
 }
 /**
  * Provides a control for inputting photos against the record, when in single record mode.
  *
  * @param array $readAuth Read authorisation tokens
  * @param array $options Options array for the control.
  * @param string $tabAlias ID of the tab's div if this is being loaded onto a div.
  */
 protected static function occurrence_photo_input($readAuth, $options, $tabAlias, $args)
 {
     $opts = array('table' => 'occurrence_image', 'readAuth' => $readAuth, 'label' => lang::get('Upload your photos'), 'caption' => lang::get('Photos'), 'resizeWidth' => 1600, 'resizeHeight' => 1600, 'readAuth' => $readAuth);
     if ($args['interface'] !== 'one_page') {
         $opts['tabDiv'] = $tabAlias;
     }
     foreach ($options as $key => $value) {
         // skip attribute specific options as they break the JavaScript.
         if (strpos($key, ':') === false) {
             $opts[$key] = $value;
         }
     }
     return data_entry_helper::file_box($opts);
 }
 private static function get_control_speciesidentifier($auth, $args, $tabalias, $options)
 {
     static $taxIdx = 0;
     // we need to control which items are lockable if locking requested
     if (!empty($options['lockable']) && $options['lockable'] == true) {
         $options['identifiers_lockable'] = $options['lockable'];
     } else {
         $options['identifiers_lockable'] = '';
     }
     unset($options['lockable']);
     // get the identifier type data
     $filter = array('termlist_external_key' => 'indicia:assoc:identifier_type');
     $dataOpts = array('table' => 'termlists_term', 'extraParams' => $auth['read'] + $filter);
     $options['identifierTypes'] = data_entry_helper::get_population_data($dataOpts);
     // get the identifier attribute type data
     $dataOpts = array('table' => 'identifier_attribute', 'extraParams' => $auth['read']);
     $options['idnAttributeTypes'] = data_entry_helper::get_population_data($dataOpts);
     // set up the known system types for identifier attributes
     $options['baseColourId'] = -1;
     $options['textColourId'] = -1;
     $options['sequenceId'] = -1;
     $options['positionId'] = -1;
     foreach ($options['idnAttributeTypes'] as $idnAttributeType) {
         if (!empty($idnAttributeType['system_function'])) {
             switch ($idnAttributeType['system_function']) {
                 case 'base_colour':
                     $options['baseColourId'] = $idnAttributeType['id'];
                     break;
                 case 'text_colour':
                     $options['textColourId'] = $idnAttributeType['id'];
                     break;
                 case 'sequence':
                     $options['sequenceId'] = $idnAttributeType['id'];
                     break;
                 case 'position':
                     $options['positionId'] = $idnAttributeType['id'];
                     break;
             }
         }
     }
     // get the subject observation attribute type data
     $dataOpts = array('table' => 'subject_observation_attribute', 'extraParams' => $auth['read']);
     $options['sjoAttributeTypes'] = data_entry_helper::get_population_data($dataOpts);
     // set up the known system types for subject_observation attributes
     $options['attachmentId'] = -1;
     $options['genderId'] = -1;
     $options['stageId'] = -1;
     $options['lifeStatusId'] = -1;
     foreach ($options['sjoAttributeTypes'] as $sjoAttributeType) {
         if (!empty($sjoAttributeType['system_function'])) {
             switch ($sjoAttributeType['system_function']) {
                 case 'attachment':
                     $options['attachmentId'] = $sjoAttributeType['id'];
                     break;
                 case 'gender':
                     $options['genderId'] = $sjoAttributeType['id'];
                     break;
                 case 'stage':
                     $options['stageId'] = $sjoAttributeType['id'];
                     break;
                 case 'life_status':
                     $options['lifeStatusId'] = $sjoAttributeType['id'];
                     break;
             }
         }
     }
     // get the identifiers subject observation attribute type data
     $dataOpts = array('table' => 'identifiers_subject_observation_attribute', 'extraParams' => $auth['read']);
     $options['isoAttributeTypes'] = data_entry_helper::get_population_data($dataOpts);
     // set up the known system types for subject_observation attributes
     $options['conditionsId'] = -1;
     foreach ($options['isoAttributeTypes'] as $isoAttributeType) {
         if (!empty($isoAttributeType['system_function'])) {
             switch ($isoAttributeType['system_function']) {
                 case 'identifier_condition':
                     $options['conditionsId'] = $isoAttributeType['id'];
                     break;
             }
         }
     }
     // throw an exception if any of the required custom attributes is missing
     $errorMessages = array();
     foreach (array('baseColourId', 'textColourId', 'sequenceId', 'positionId', 'attachmentId', 'genderId', 'stageId', 'lifeStatusId', 'conditionsId') as $attrId) {
         if ($options[$attrId] === -1) {
             $errorMessages[] = lang::get('Required custom attribute for ' . $attrId . ' has not been found. ' . 'Please check this has been created on the warehouse and is associated with the correct system function.');
         }
     }
     if (count($errorMessages) > 0) {
         $errorMessage = implode('<br />', $errorMessages);
         throw new exception($errorMessage);
     }
     // configure the identifiers javascript
     // write it late so it happens after any locked values are applied
     if (!$options['inNewIndividual']) {
         data_entry_helper::$late_javascript .= "indicia.wwt.initForm (\n        '" . $options['baseColourId'] . "',\n        '" . $options['textColourId'] . "',\n        '" . $options['sequenceId'] . "',\n        '" . $options['positionId'] . "',\n        '" . $args['default_leg_vertical'] . "',\n        '" . (!empty($args['neck_collar_regex']) ? $args['neck_collar_regex'] : '') . "',\n        '" . (!empty($args['enscribed_colour_ring_regex']) ? $args['enscribed_colour_ring_regex'] : '') . "',\n        '" . (!empty($args['metal_ring_regex']) ? $args['metal_ring_regex'] : '') . "',\n        '" . ($args['clientSideValidation'] ? 'true' : 'false') . "',\n        '" . ($args['subjectAccordion'] ? 'true' : 'false') . "'\n" . ");\n";
     }
     $r = '';
     $options['fieldprefix'] = 'idn:' . $taxIdx . ':';
     if (!$options['inNewIndividual']) {
         $r .= '<div id="idn:subject:accordion" class="idn-subject-accordion">';
     }
     if ($args['subjectAccordion']) {
         $r .= '<h3 id="' . $options['fieldprefix'] . 'individual:header" class="individual_header"><a href="" data-heading="' . lang::get('Colour-marked Individual') . ' ' . ($taxIdx + 1) . '">' . lang::get('Colour-marked Individual') . ' ' . ($taxIdx + 1) . '</a></h3>';
     } else {
         $r .= '<h3 id="' . $options['fieldprefix'] . 'individual:header" class="individual_header" data-heading="' . lang::get('Colour-marked Individual') . ' ' . ($taxIdx + 1) . '">' . lang::get('Colour-marked Individual') . ' ' . ($taxIdx + 1) . '</h3>';
     }
     $r .= '<div id="' . $options['fieldprefix'] . 'individual:panel" class="individual_panel ui-corner-all">';
     $r .= '<div class="ui-helper-clearfix">';
     $r .= '<fieldset id="' . $options['fieldprefix'] . 'individual:fieldset" class="taxon_individual ui-corner-all">';
     $r .= '<legend id="' . $options['fieldprefix'] . 'individual:legend">Individual details</legend>';
     // output the hiddens
     if (isset(data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'subject_observation:id'])) {
         $r .= '<input type="hidden" id="' . $options['fieldprefix'] . 'subject_observation:id" name="' . $options['fieldprefix'] . 'subject_observation:id" ' . 'value="' . data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'subject_observation:id'] . '" />' . "\n";
     }
     if (isset(data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'occurrences_subject_observation:id'])) {
         $r .= '<input type="hidden" id="' . $options['fieldprefix'] . 'occurrences_subject_observation:id" name="' . $options['fieldprefix'] . 'occurrences_subject_observation:id" ' . 'value="' . data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'occurrences_subject_observation:id'] . '" />' . "\n";
     }
     if (isset(data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'occurrence:id'])) {
         $r .= '<input type="hidden" id="' . $options['fieldprefix'] . 'occurrence:id" name="' . $options['fieldprefix'] . 'occurrence:id" ' . 'value="' . data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'occurrence:id'] . '" />' . "\n";
     }
     // Check if Record Status is included as a control. If not, then add it as a hidden.
     $arr = helper_base::explode_lines($args['structure']);
     if (!in_array('[record status]', $arr)) {
         if (isset(data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'occurrence:record_status'])) {
             $value = data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'occurrence:record_status'];
         } else {
             $value = isset($args['defaults']['occurrence:record_status']) ? $args['defaults']['occurrence:record_status'] : 'C';
         }
         $r .= '<input type="hidden" id="' . $options['fieldprefix'] . 'occurrence:record_status" ' . 'name="' . $options['fieldprefix'] . 'occurrence:record_status" value="' . $value . '" />' . "\n";
     }
     // add subject type and count as a hidden
     $value = '';
     if (isset(data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'subject_observation:subject_type_id'])) {
         $value = data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'subject_observation:subject_type_id'];
     } else {
         if (isset($args['subject_type_id'])) {
             $value = $args['subject_type_id'];
         }
     }
     if ($value !== '') {
         $r .= '<input type="hidden" id="' . $options['fieldprefix'] . 'subject_observation:subject_type_id" ' . 'name="' . $options['fieldprefix'] . 'subject_observation:subject_type_id" value="' . $value . '" />' . "\n";
     }
     if (isset(data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'subject_observation:count'])) {
         $value = data_entry_helper::$entity_to_load[$options['fieldprefix'] . 'subject_observation:count'];
     } else {
         $value = '1';
     }
     if ($value !== '') {
         $r .= '<input type="hidden" id="' . $options['fieldprefix'] . 'subject_observation:count" ' . 'name="' . $options['fieldprefix'] . 'subject_observation:count" value="' . $value . '" />' . "\n";
     }
     // output the species selection control
     $options['blankText'] = '<Please select>';
     $options['lockable'] = $options['identifiers_lockable'];
     if ($args['species_ctrl'] == 'autocomplete') {
         $temp = data_entry_helper::$javascript;
     }
     $r .= self::get_control_species($auth, $args, $tabalias, $options + array('validation' => array('required'), 'class' => 'select_taxon'));
     if ($args['species_ctrl'] == 'autocomplete') {
         if (!$options['inNewIndividual']) {
             $autoJavascript = substr(data_entry_helper::$javascript, strlen($temp));
         } else {
             data_entry_helper::$javascript = $temp;
         }
         unset($temp);
     } else {
         $autoJavascript = '';
     }
     unset($options['lockable']);
     // gender
     if ($options['genderId'] > 0 && !empty($args['request_gender_values']) && count($args['request_gender_values']) > 0) {
         // filter the genders available
         $query = array('in' => array('id', $args['request_gender_values']));
         $filter = array('query' => json_encode($query), 'orderby' => 'sort_order');
         $extraParams = array_merge($filter, $auth['read']);
         $options['lockable'] = $options['identifiers_lockable'];
         $fieldname = $options['fieldprefix'] . 'sjoAttr:' . $options['genderId'];
         $idname = $fieldname;
         // if this attribute exists on DB, we need to append id to fieldname
         if (is_array(data_entry_helper::$entity_to_load)) {
             $stored_keys = preg_grep('/^' . $fieldname . ':[0-9]+$/', array_keys(data_entry_helper::$entity_to_load));
             if (count($stored_keys) === 1) {
                 foreach ($stored_keys as $stored_key) {
                     $fieldname = $stored_key;
                 }
             }
         }
         $r .= data_entry_helper::select(array_merge(array('label' => lang::get('Sex of the bird'), 'fieldname' => $fieldname, 'id' => $idname, 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'default' => $args['default_gender'], 'extraParams' => $extraParams), $options));
         unset($options['lockable']);
     }
     // age
     if ($options['stageId'] > 0 && !empty($args['request_stage_values']) && count($args['request_stage_values']) > 0) {
         // filter the stages available
         $query = array('in' => array('id', $args['request_stage_values']));
         $filter = array('query' => json_encode($query), 'orderby' => 'sort_order');
         $extraParams = array_merge($filter, $auth['read']);
         $options['lockable'] = $options['identifiers_lockable'];
         $fieldname = $options['fieldprefix'] . 'sjoAttr:' . $options['stageId'];
         $idname = $fieldname;
         // if this attribute exists on DB, we need to append id to fieldname
         if (is_array(data_entry_helper::$entity_to_load)) {
             $stored_keys = preg_grep('/^' . $fieldname . ':[0-9]+$/', array_keys(data_entry_helper::$entity_to_load));
             if (count($stored_keys) === 1) {
                 foreach ($stored_keys as $stored_key) {
                     $fieldname = $stored_key;
                 }
             }
         }
         $r .= data_entry_helper::select(array_merge(array('label' => lang::get('Age of the bird'), 'fieldname' => $fieldname, 'id' => $idname, 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'default' => $args['default_stage'], 'extraParams' => $extraParams), $options));
         unset($options['lockable']);
     }
     // subject status
     if ($options['lifeStatusId'] > 0 && !empty($args['request_life_status_values']) && count($args['request_life_status_values']) > 0) {
         // filter the life status's available
         $query = array('in' => array('id', $args['request_life_status_values']));
         $filter = array('query' => json_encode($query), 'orderby' => 'sort_order');
         $extraParams = array_merge($filter, $auth['read']);
         $options['lockable'] = $options['identifiers_lockable'];
         $fieldname = $options['fieldprefix'] . 'sjoAttr:' . $options['lifeStatusId'];
         $idname = $fieldname;
         // if this attribute exists on DB, we need to append id to fieldname
         if (is_array(data_entry_helper::$entity_to_load)) {
             $stored_keys = preg_grep('/^' . $fieldname . ':[0-9]+$/', array_keys(data_entry_helper::$entity_to_load));
             if (count($stored_keys) === 1) {
                 foreach ($stored_keys as $stored_key) {
                     $fieldname = $stored_key;
                 }
             }
         }
         $r .= data_entry_helper::select(array_merge(array('label' => lang::get('This bird was'), 'fieldname' => $fieldname, 'id' => $idname, 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'default' => $args['default_life_status'], 'extraParams' => $extraParams), $options));
         unset($options['lockable']);
     }
     // output each required identifier
     $r .= '<div id="' . $options['fieldprefix'] . 'accordion" class="idn-accordion">';
     // setup and call function for neck collar
     $options['identifierName'] = '';
     $options['identifierTypeId'] = '';
     foreach ($options['identifierTypes'] as $identifier_type) {
         if ($identifier_type['id'] == $args['neck_collar_type']) {
             $options['identifierName'] = $identifier_type['term'];
             $options['identifierTypeId'] = $identifier_type['id'];
             break;
         }
     }
     $options['attrList'] = array(array('attrType' => 'idn', 'typeId' => $options['baseColourId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['textColourId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['sequenceId'], 'lockable' => false, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['positionId'], 'lockable' => false, 'hidden' => true, 'hiddenValue' => $args['neck_collar_position']), array('attrType' => 'iso', 'typeId' => $options['conditionsId'], 'lockable' => false, 'hidden' => false));
     $options['fieldprefix'] = 'idn:' . $taxIdx . ':neck-collar:';
     $options['classprefix'] = 'idn-neck-collar-';
     $options['seq_maxlength'] = !empty($args['neck_collar_max_length']) ? $args['neck_collar_max_length'] : '';
     if (!empty($args['neck_collar_regex'])) {
         $options['seq_format_class'] = 'collarFormat';
     }
     $r .= self::get_control_identifier($auth, $args, $tabalias, $options);
     if (!empty($args['neck_collar_regex'])) {
         unset($options['seq_format_class']);
     }
     // setup and call function for left enscribed colour ring
     $options['identifierName'] = '';
     $options['identifierTypeId'] = '';
     foreach ($options['identifierTypes'] as $identifier_type) {
         if ($identifier_type['id'] == $args['enscribed_colour_ring_type']) {
             $options['identifierName'] = $identifier_type['term'] . lang::get(' (Left leg)');
             $options['identifierTypeId'] = $identifier_type['id'];
             break;
         }
     }
     $options['attrList'] = array(array('attrType' => 'idn', 'typeId' => $options['baseColourId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['textColourId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['sequenceId'], 'lockable' => false, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['positionId'], 'lockable' => false, 'hidden' => true, 'hiddenValue' => $args['left_enscribed_colour_ring_position']), array('attrType' => 'iso', 'typeId' => $options['conditionsId'], 'lockable' => false, 'hidden' => false));
     $options['fieldprefix'] = 'idn:' . $taxIdx . ':colour-left:';
     $options['classprefix'] = 'idn-colour-left-';
     $options['seq_maxlength'] = !empty($args['enscribed_colour_ring_max_length']) ? $args['enscribed_colour_ring_max_length'] : '';
     if (!empty($args['enscribed_colour_ring_regex'])) {
         $options['seq_format_class'] = 'colourRingFormat';
     }
     $r .= self::get_control_identifier($auth, $args, $tabalias, $options);
     if (!empty($args['enscribed_colour_ring_regex'])) {
         unset($options['seq_format_class']);
     }
     // setup and call function for right enscribed colour ring
     $options['identifierName'] = '';
     $options['identifierTypeId'] = '';
     foreach ($options['identifierTypes'] as $identifier_type) {
         if ($identifier_type['id'] == $args['enscribed_colour_ring_type']) {
             $options['identifierName'] = $identifier_type['term'] . lang::get(' (Right leg)');
             $options['identifierTypeId'] = $identifier_type['id'];
             break;
         }
     }
     $options['attrList'] = array(array('attrType' => 'idn', 'typeId' => $options['baseColourId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['textColourId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['sequenceId'], 'lockable' => false, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['positionId'], 'lockable' => false, 'hidden' => true, 'hiddenValue' => $args['right_enscribed_colour_ring_position']), array('attrType' => 'iso', 'typeId' => $options['conditionsId'], 'lockable' => false, 'hidden' => false));
     $options['fieldprefix'] = 'idn:' . $taxIdx . ':colour-right:';
     $options['classprefix'] = 'idn-colour-right-';
     $options['seq_maxlength'] = !empty($args['enscribed_colour_ring_max_length']) ? $args['enscribed_colour_ring_max_length'] : '';
     if (!empty($args['enscribed_colour_ring_regex'])) {
         $options['seq_format_class'] = 'colourRingFormat';
     }
     $r .= self::get_control_identifier($auth, $args, $tabalias, $options);
     if (!empty($args['enscribed_colour_ring_regex'])) {
         unset($options['seq_format_class']);
     }
     // setup and call function for metal ring
     $options['identifierName'] = '';
     $options['identifierTypeId'] = '';
     foreach ($options['identifierTypes'] as $identifier_type) {
         if ($identifier_type['id'] == $args['metal_ring_type']) {
             $options['identifierName'] = $identifier_type['term'];
             $options['identifierTypeId'] = $identifier_type['id'];
             break;
         }
     }
     $options['attrList'] = array(array('attrType' => 'idn', 'typeId' => $options['positionId'], 'lockable' => true, 'hidden' => false), array('attrType' => 'idn', 'typeId' => $options['sequenceId'], 'lockable' => false, 'hidden' => false), array('attrType' => 'iso', 'typeId' => $options['conditionsId'], 'lockable' => false, 'hidden' => false));
     $options['fieldprefix'] = 'idn:' . $taxIdx . ':metal:';
     $options['classprefix'] = 'idn-metal-';
     $options['seq_maxlength'] = !empty($args['metal_ring_max_length']) ? $args['metal_ring_max_length'] : '';
     $options['seq_maxlength'] = !empty($args['metal_ring_max_length']) ? $args['metal_ring_max_length'] : '';
     if (!empty($args['metal_ring_regex'])) {
         $options['seq_format_class'] = 'metalRingFormat';
     }
     $r .= self::get_control_identifier($auth, $args, $tabalias, $options);
     if (!empty($args['metal_ring_regex'])) {
         unset($options['seq_format_class']);
     }
     unset($options['seq_maxlength']);
     $r .= '</div>';
     // end of identifier accordion
     // other devices (trackers etc.)
     if ($options['attachmentId'] > 0 && !empty($args['other_devices']) && count($args['other_devices']) > 0) {
         // reset prefix
         $options['fieldprefix'] = 'idn:' . $taxIdx . ':';
         // filter the devices available
         $query = array('in' => array('id', $args['other_devices']));
         $filter = array('query' => json_encode($query), 'orderby' => 'sort_order');
         $extraParams = array_merge($filter, $auth['read']);
         $fieldname = $options['fieldprefix'] . 'sjoAttr:' . $options['attachmentId'];
         $default = array();
         // if this attribute exists on DB, we need to write a hidden with id appended to fieldname and set defaults for checkboxes
         if (is_array(data_entry_helper::$entity_to_load)) {
             $stored_keys = preg_grep('/^' . $fieldname . ':[0-9]+$/', array_keys(data_entry_helper::$entity_to_load));
             foreach ($stored_keys as $stored_key) {
                 $r .= '<input type="hidden" name="' . $stored_key . '" value="" />';
                 $default[] = array('fieldname' => $stored_key, 'default' => data_entry_helper::$entity_to_load[$stored_key]);
                 unset(data_entry_helper::$entity_to_load[$stored_key]);
             }
         }
         $r .= data_entry_helper::checkbox_group(array_merge(array('label' => lang::get('What other devices did you see on the bird'), 'fieldname' => $fieldname, 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'default' => $default, 'extraParams' => $extraParams), $options));
     }
     // subject_observation comment
     if ($args['observation_comment']) {
         $r .= self::get_control_observationcomment($auth, $args, $tabalias, $options);
     }
     $r .= '</fieldset>';
     // output identifier visualisations
     $r .= '<div id="idn:' . $taxIdx . ':neck-collar:colourbox" class="neck-collar-indentifier-colourbox ui-corner-all">&nbsp;</div>';
     $r .= '<div id="idn:' . $taxIdx . ':colour-left:colourbox" class="colour-left-indentifier-colourbox ui-corner-all">&nbsp;</div>';
     $r .= '<div id="idn:' . $taxIdx . ':colour-right:colourbox" class="colour-right-indentifier-colourbox ui-corner-all">&nbsp;</div>';
     $r .= '</div>';
     // close clearfix div
     // occurrence images
     $opts = array('table' => 'idn:' . $taxIdx . ':' . 'occurrence_image', 'label' => lang::get('Upload your photos'));
     if ($args['interface'] !== 'one_page') {
         $opts['tabDiv'] = $tabalias;
     }
     $opts['resizeWidth'] = isset($options['resizeWidth']) ? $options['resizeWidth'] : 1600;
     $opts['resizeHeight'] = isset($options['resizeHeight']) ? $options['resizeHeight'] : 1600;
     $opts['caption'] = lang::get('Photos');
     $opts['readAuth'] = $auth['read'];
     $opts['imageWidth'] = '168';
     // $opts['id'] = 'idn:0';
     if ($options['inNewIndividual']) {
         $opts['codeGenerated'] = 'php';
     }
     $r .= data_entry_helper::file_box($opts);
     // remove bird button - don't show if bird is being edited or only bird on the form
     $r .= '<input type="button" id="idn:0:remove-individual" class="idn-remove-individual" value="' . lang::get('Remove This Bird') . '" />';
     $r .= '</div>';
     // recursive call to get a template for the 'individual panel' markup for a new observation so we can add another bird
     if (!$options['inNewIndividual']) {
         $r .= '</div>';
         $temp = data_entry_helper::$entity_to_load;
         data_entry_helper::$entity_to_load = null;
         $options['inNewIndividual'] = true;
         $options['lockable'] = $options['identifiers_lockable'];
         $new_individual = self::get_control_speciesidentifier($auth, $args, $tabalias, $options);
         unset($options['lockable']);
         $opts['codeGenerated'] = 'js';
         $photoJavascript = data_entry_helper::file_box($opts);
         data_entry_helper::$entity_to_load = $temp;
         unset($options['inNewIndividual']);
         data_entry_helper::$javascript .= "window.indicia.wwt.newIndividual = '" . str_replace(array('\'', "\n"), array('\\\'', ' '), $new_individual) . "';\n";
         // save the javascript needed for an additional colour-marked individual
         // process it to sanitise the string and remove comments (works now but not 100% reliable)
         data_entry_helper::$javascript .= "window.indicia.wwt.newJavascript = '" . str_replace(array('\'', "\n"), array('\\\'', ' '), str_replace('\\', '\\\\', preg_replace('#^\\s*//.+$#m', '', $photoJavascript))) . str_replace(array('\'', "\n", "\r"), array('\\\'', ' ', ' '), str_replace('\\', '\\\\', preg_replace('#^\\s*//.+$#m', '', $autoJavascript))) . "';\n";
         $r .= '<input type="button" id="idn:add-another" class="ui-state-default ui-corner-all" ' . 'value="' . lang::get('Add Another Bird at the Same Date and Location') . '" /><br />';
     }
     return $r;
 }
 /**
  * Helper function to generate a species checklist from a given taxon list.
  *
  * <p>This function will generate a flexible grid control with one row for each species
  * in the specified list. For each row, the control will display the list preferred term
  * for that species, a checkbox to indicate its presence, and a series of cells for a set
  * of occurrence attributes passed to the control.</p>
  *
  * <p>Further, the control will incorporate the functionality to add extra terms to the
  * control from the parent list of the one given. This will take the form of an autocomplete
  * box against the parent list which will add an extra row to the control upon selection.</p>
  *
  * <p>To change the format of the label displayed for each taxon in the grid rows that are pre-loaded into the grid,
  * use the global $indicia_templates variable to set the value for the entry 'taxon_label'. The tags available in the template are {taxon}, {preferred_name},
  * {authority} and {common}. This can be a PHP snippet if PHPtaxonLabel is set to true.</p>
  *
  * <p>To change the format of the label displayed for each taxon in the autocomplete used for searching for species to add to the grid,
  * use the global $indicia_templates variable to set the value for the entry 'format_species_autocomplete_fn'. This must be a JavaScript function
  * which takes a single parameter. The parameter is the item returned from the database with attributes taxon, preferred ('t' or 'f'),
  * preferred_name, common, authority, taxon_group, language. The function must return the string to display in the autocomplete list.</p>
  *
  * <p>To perform an action on the event of a new row being added to the grid, write a JavaScript function taking arguments (data, row) and add to the array
  * hook_species_checklist_new_row, where data is an object containing the details of the taxon row as loaded from the data services.</p>
  *
  * @param array $options Options array with the following possibilities:<ul>
  * <li><b>listId</b><br/>
  * Optional. The ID of the taxon_lists record which is to be used to obtain the species or taxon list. This is
  * required unless lookupListId is provided.</li>
  * <li><b>occAttrs</b><br/>
  * Optional integer array, where each entry corresponds to the id of the desired attribute in the
  * occurrence_attributes table. If omitted, then all the occurrence attributes for this survey are loaded.</li>
  * <li><b>occAttrClasses</b><br/>
  * String array, where each entry corresponds to the css class(es) to apply to the corresponding
  * attribute control (i.e. there is a one to one match with occAttrs). If this array is shorter than
  * occAttrs then all remaining controls re-use the last class.</li>
  * <li><b>occAttrOptions</b><br/>
  * array, where the key to each item is the id of an attribute and the item is an array of options
  * to pass to the control for this atrtribute.</li>
  * <li><b>extraParams</b><br/>
  * Associative array of items to pass via the query string to the service calls used for taxon names lookup. This
  * should at least contain the read authorisation array.</li>
  * <li><b>lookupListId</b><br/>
  * Optional. The ID of the taxon_lists record which is to be used to select taxa from when adding
  * rows to the grid. If specified, then an autocomplete text box and Add Row button are generated
  * automatically allowing the user to pick a species to add as an extra row.</li>
  * <li><b>cacheLookup</b></li>
  * Optional. Set to true to select to use a cached version of the lookup list when
  * searching for species names using the autocomplete control, or set to false to use the
  * live version (default). The latter is slower and places more load on the warehouse so should only be
  * used during development or when there is a specific need to reflect taxa that have only 
  * just been added to the list.
  * <li><b>taxonFilterField</b><br/>
  * If the list of species to be made available for recording is to be limited (either by species or taxon group), allows selection of 
  * the field to filter against. Options are none (default), preferred_name, taxon_meaning_id, taxa_taxon_list_id, taxon_group. If filtering for a large list
  * of taxa then taxon_meaning_id or taxa_taxon_list_id is more efficient.
  * </li>
  * <li><b>taxonFilter</b><br/>
  * If taxonFilterField is not set to none, then pass an array of values to filter against, i.e. an array of
  * taxon preferred names, taxon meaning ids or taxon group titles.
  * </li>
  * <li><b>usersPreferredGroups</b><br/>
  * If the user has defined a list of taxon groups they like to record, then supply an array of the taxon group IDs in this parameter.
  * This lets the user easily opt to record against their chosen groups.
  * </li>
  * <li><b>userControlsTaxonFilter</b><br/>
  * If set to true, then a filter button in the title of the species input column allows the user to configure the filter applied to 
  * which taxa are available to select from, e.g. which taxon groups can be picked from. Only applies when lookupListId is set.
  * </li>
  * <li><b>speciesNameFilterMode</b><br/>
  * Optional. Method of filtering the available species names (both for initial population into the grid and additional rows). Options are
  *   preferred - only preferred names
  *   currentLanguage - only names in the language identified by the language option are included
  *   excludeSynonyms - all names except synonyms (non-preferred latin names) are included.
  * </li>
  * <li><b>header</b><br/>
  * Include a header row in the grid? Defaults to true.</li>
  * <li><b>columns</b><br/>
  * Number of repeating columns of output. For example, a simple grid of species checkboxes could be output in 2 or 3 columns.
  * Defaults to 1.</li>
  * <li><b>rowInclusionCheck</b><br/>
  * Defines how the system determines whether a row in the grid actually contains an occurrence or not. There are 4 options: <br/>
  * checkbox - a column is included in the grid containing a presence checkbox. If checked then an occurrence is created for the row. This is the default unless listId is not set.<br/>
  * alwaysFixed - occurrences are created for all rows in the grid. Rows cannot be removed from the grid apart from newly added rows.<br/>
  * alwaysRemovable - occurrences are created for all rows in the grid. Rows can always be removed from the grid. Best used with no listId so there are
  * no default taxa in the grid, otherwise editing an existing sample will re-add all the existing taxa. This is the default when listId is not set, but 
  * lookupListId is set.<br/>
  * hasData - occurrences are created for any row which has a data value specified in at least one of its columns. <br/>
  * This option supercedes the checkboxCol option which is still recognised for backwards compatibility.</li>
  * <li><b>hasDataIgnoreAttrs</b><br/>
  * Optional integer array, where each entry corresponds to the id of an attribute that should be ignored when doing
  * the hasData row inclusion check. If a column has a default value, especially a gridIdAttribute, you may not want
  * it to trigger creation of an occurrence so include it in this array.</li>
  * <li><b>class</b><br/>
  * Optional. CSS class names to add to the control.</li>
  * <li><b>cachetimeout</b><br/>
  * Optional. Specifies the number of seconds before the data cache times out - i.e. how long
  * after a request for data to the Indicia Warehouse before a new request will refetch the data,
  * rather than use a locally stored (cached) copy of the previous request. This speeds things up
  * and reduces the loading on the Indicia Warehouse. Defaults to the global website-wide value:
  * if this is not specified then 1 hour.</li>
  * <li><b>survey_id</b><br/>
  * Optional. Used to determine which attributes are valid for this website/survey combination</li>
  * <li><b>occurrenceComment</b><br/>
  * Optional. If set to true, then an occurrence comment input field is included on each row.</li>
  * <li><b>occurrenceSensitivity</b><br/>
  * Optional. If set to true, then an occurrence sensitivity selector is included on each row.</li>
  * <li><b>mediaTypes</b><br/>
  * Optional. Array of media types that can be uploaded. Choose from Audio:Local, Audio:SoundCloud, Image:Flickr,
  * Image:Instagram, Image:Local, Image:Twitpic, Social:Facebook, Social:Twitter, Video:Youtube,
  * Video:Vimeo.
  * Currently not supported for multi-column grids.</li>
  * <li><b>resizeWidth</b><br/>
  * If set, then the image files will be resized before upload using this as the maximum pixels width.
  * </li>
  * <li><b>resizeHeight</b><br/>
  * If set, then the image files will be resized before upload using this as the maximum pixels height.
  * </li>
  * <li><b>resizeQuality</b><br/>
  * Defines the quality of the resize operation (from 1 to 100). Has no effect unless either resizeWidth or resizeHeight are non-zero.
  * <li><b>colWidths</b><br/>
  * Optional. Array containing percentage values for each visible column's width, with blank entries for columns that are not specified. If the array is shorter
  * than the actual number of columns then the remaining columns use the default width determined by the browser.</li>
  * <li><b>attrCellTemplate</b><br/>
  * Optional. If specified, specifies the name of the template (in global $indicia_templates) to use
  * for each cell containing an attribute input control. Valid replacements are {label}, {class} and {content}.
  * Default is attribute_cell.</li>
  * <li><b>language</b><br/>
  * Language used to filter lookup list items in attributes. ISO 639:3 format. </li>
  * <li><b>PHPtaxonLabel</b></li>
  * If set to true, then the taxon_label template should contain a PHP statement that returns the HTML to display for each
  * taxon's label. Otherwise the template should be plain HTML. Defaults to false.</li>
  * <li><b>useLoadedExistingRecords</b></li>
  * Optional. Defaults to false. Set to true to prevent a grid from making a web service call to load existing occurrence
  * data when reloading a sample. This can be useful if there are more than one species checklist on the page such as when
  * species input is split across several tabs - the first can load all the data and subsequent grids just display 
  * the appropriate records depending on the species they are configured to show.</li>
  * <li><b>reloadExtraParams</b></li>
  * Set to an array of additional parameters such as filter criteria to pass to the service request used to load 
  * existing records into the grid when reloading a sample. Especially useful when there are more than one species checklist
  * on a single form, so that each grid can display the appropriate output.</li>
  * <li><b>subSpeciesColumn</b>
  * If true and doing grid based data entry with lookupListId set so allowing the recorder to add species they choose to 
  * the bottom of the grid, subspecies will be displayed in a separate column so the recorder picks the species 
  * first then the subspecies. The species checklist must be configured as a simple 2 level list so that species are 
  * parents of the subspecies. For performance reasons, this option forces the cacheLookup option to be set to true therefore it 
  * requires the cache_builder module to be running on the warehouse. Defaults to false.</li>
  * <li><b>subSpeciesRemoveSspRank</b>
  * Set to true to force the displayed subspecies names to remove the rank (var., forma, ssp) etc. Useful if all subspecies
  * are the same rank.
  * </li>
  * </ul>
  * The output of this control can be configured using the following templates: 
  * <ul>
  * <li><b>file_box</b></br>
  * When image upload for records is enabled, outputs the HTML container which will contain the 
  * upload button and images.
  * </li>
  * <li><b>file_box_initial_file_info</b></br>
  * When image upload for records is enabled, this template provides the HTML for the outer container 
  * for each displayed image, including the header and remove file button. Has an element with 
  * class set to media-wrapper into which images themselves will be inserted.
  * </li>
  * <li><b>file_box_uploaded_image</b></br>
  * Template for the HTML for each uploaded image, including the image, caption input
  * and hidden inputs to define the link to the database. Will be inserted into the
  * file_box_initial_file_info template's media-wrapper element.
  * </li>
  * <li><b>taxon_label_cell</b></br>
  * Generates the label shown for the taxon name for each added row.
  * </li>
  * <li><b>format_species_autocomplete_fn</b></br>
  * Can be set to an optional JavaScript function which formats the contents of the species
  * autocomplete's search list items.
  * </li>
  * <li><b>taxon_label</b></br>
  * If format_species_autocomplete_fn is not set, then this provides an HTML template for the
  * contents of the species autocomplete's search list items.
  * </li>
  * <li><b>attribute_cell</b></br>
  * HTML wrapper for cells containing attribute inputs.
  * </li>
  * <li><b>attributeIds</b><br/>
  * Provide an array of occurrence attribute IDs if you want to limit those shown in the grid. The default list of
  * attributes shown is the list associated with the survey on the warehouse, but this option allows you to ignore
  * some. An example use of this might be when you have multiple grids on the page each supporting a different
  * species group with different attributes. 
  * </li>
  * <li><b>gridIdAttributeId</b><br/>
  * If you have multiple grids on one input form, then you can create an occurrence attribute (text) for your
  * survey which will store the ID of the grid used to create the record. Provide the attribute's ID through this
  * parameter so that the grid can automatically save the value and use it when reloading records, so that the
  * records are reloaded into the correct grid. To do this, you would need to set a unique ID for each grid using the 
  * id parameter. You can combine this with the attributeIds parameter to show different columns for each grid.
  * </li>
  * <li><b>speciesControlToUseSubSamples</b>
  * Optional. Enables support for sub samples in the grid where input records can be allocated to different sub samples, e.g. 
  * when inputting a list of records at different places. Default false.
  * </li>
  * <li><b>subSamplePerRow</b>
  * Optional. Requires speciesControlToUseSubSamples to be set to true, then if this is also true it generates a sub-sample 
  * per row in the grid. It is then necessary to write code which processes the submission to at least a spatial reference
  * for each sub sample. This might be used when an occurrence attribute in the grid can be used to calculate the sub-sample's
  * spatial reference, such as when capturing the reticules and bearing for a cetacean sighting.
  * </li>
  * <li><b>subSampleSampleMethodID</b>
  * Optional. sample_method_id to use for the subsamples.
  * </li>
  * <li><b>copyDataFromPreviousRow</b>
  * Optional. When enabled, the system will copy data from the previous row into new rows on the species grid. The data is copied
  * automatically when the new row is created and also when edits are made to the previous row. The columns to copy are determined 
  * by the previousRowColumnsToInclude option. 
  * </li>
  * <li><b>previousRowColumnsToInclude</b>
  * Optional. Requires copyDataFromPreviousRow to be set to true. Allows the user to specify which columns of data from the previous 
  * row will be copied into a new row on the species grid. Comma separated list of column titles, non-case or white space sensitive.
  * Any unrecognised columns are ignored and the images column cannot be copied.
  * </li>
  * <li><b>sticky</b>
  * Optional, defaults to true. Enables sticky table headers if supported by the host site (e.g. Drupal). 
  * </li>
  * <li><b>numValues</b><br/>
  * Optional. Number of returned values in the species autocomplete drop down list. Defaults to 20.
  * </li>
  * <li><b>selectMode</b>
  * Should the species autocomplete used for adding new rows simulate a select drop down control by adding a drop down arrow after the input box which, when clicked,
  * populates the drop down list with all search results to a maximum of numValues. This is similar to typing * into the box. Default false.
  * </li>
  * <li><b>speciesColTitle</b>
  * Title for the species column which will be looked up in lang files. If not set, uses
  * species_checklist.species.
  * </li>
  * 
  * </ul>
  * @return string HTML for the species checklist input grid.
  */
 public static function species_checklist($options)
 {
     global $indicia_templates;
     $options = self::check_options($options);
     $options = self::get_species_checklist_options($options);
     $classlist = array('ui-widget', 'ui-widget-content', 'species-grid');
     if (!empty($options['class'])) {
         $classlist[] = $options['class'];
     }
     if ($options['sticky']) {
         $stickyHeaderClass = self::add_sticky_headers($options);
         if (!empty($stickyHeaderClass)) {
             $classlist[] = $stickyHeaderClass;
         }
     }
     if ($options['subSamplePerRow']) {
         // we'll track 1 sample per grid row.
         $smpIdx = 0;
     }
     if ($options['columns'] > 1 && count($options['mediaTypes']) > 1) {
         throw new Exception('The species_checklist control does not support having more than one occurrence per row (columns option > 0) ' . 'at the same time has having the mediaTypes option in use.');
     }
     self::add_resource('json');
     self::add_resource('autocomplete');
     $filterArray = self::get_species_names_filter($options);
     $filterNameTypes = array('all', 'currentLanguage', 'preferred', 'excludeSynonyms');
     //make a copy of the options so that we can maipulate it
     $overrideOptions = $options;
     //We are going to cycle through each of the name filter types
     //and save the parameters required for each type in an array so
     //that the Javascript can quickly access the required parameters
     foreach ($filterNameTypes as $filterType) {
         $overrideOptions['speciesNameFilterMode'] = $filterType;
         $nameFilter[$filterType] = self::get_species_names_filter($overrideOptions);
         $nameFilter[$filterType] = json_encode($nameFilter[$filterType]);
     }
     if (count($filterArray)) {
         $filterParam = json_encode($filterArray);
         self::$javascript .= "indiciaData['taxonExtraParams-" . $options['id'] . "'] = {$filterParam};\n";
         // Apply a filter to extraParams that can be used when loading the initial species list, to get just the correct names.
         if (isset($options['speciesNameFilterMode']) && !empty($options['listId'])) {
             $filterFields = array();
             $filterWheres = array();
             self::parse_species_name_filter_mode($options, $filterFields, $filterWheres);
             if (count($filterWheres)) {
                 $options['extraParams'] += array('query' => json_encode(array('where' => $filterWheres)));
             }
             $options['extraParams'] += $filterFields;
         }
     }
     self::$js_read_tokens = $options['readAuth'];
     self::$javascript .= "indiciaData['rowInclusionCheck-" . $options['id'] . "'] = '" . $options['rowInclusionCheck'] . "';\n";
     self::$javascript .= "indiciaData['copyDataFromPreviousRow-" . $options['id'] . "'] = '" . $options['copyDataFromPreviousRow'] . "';\n";
     self::$javascript .= "indiciaData['includeSpeciesGridLinkPage-" . $options['id'] . "'] = '" . $options['includeSpeciesGridLinkPage'] . "';\n";
     self::$javascript .= "indiciaData.speciesGridPageLinkUrl = '" . $options['speciesGridPageLinkUrl'] . "';\n";
     self::$javascript .= "indiciaData.speciesGridPageLinkParameter = '" . $options['speciesGridPageLinkParameter'] . "';\n";
     self::$javascript .= "indiciaData.speciesGridPageLinkTooltip = '" . $options['speciesGridPageLinkTooltip'] . "';\n";
     self::$javascript .= "indiciaData['editTaxaNames-" . $options['id'] . "'] = '" . $options['editTaxaNames'] . "';\n";
     self::$javascript .= "indiciaData['subSpeciesColumn-" . $options['id'] . "'] = '" . $options['subSpeciesColumn'] . "';\n";
     self::$javascript .= "indiciaData['subSamplePerRow-" . $options['id'] . "'] = " . ($options['subSamplePerRow'] ? 'true' : 'false') . ";\n";
     if ($options['copyDataFromPreviousRow']) {
         self::$javascript .= "indiciaData['previousRowColumnsToInclude-" . $options['id'] . "'] = '" . $options['previousRowColumnsToInclude'] . "';\n";
         self::$javascript .= "indiciaData.langAddAnother='" . lang::get('Add another') . "';\n";
     }
     if (count($options['mediaTypes'])) {
         self::add_resource('plupload');
         // store some globals that we need later when creating uploaders
         $relpath = self::getRootFolder() . self::client_helper_path();
         $interim_image_folder = isset(parent::$interim_image_folder) ? parent::$interim_image_folder : 'upload/';
         self::$javascript .= "indiciaData.uploadSettings = {\n";
         self::$javascript .= "  uploadScript: '" . $relpath . "upload.php',\n";
         self::$javascript .= "  destinationFolder: '" . $relpath . $interim_image_folder . "',\n";
         self::$javascript .= "  jsPath: '" . self::$js_path . "'";
         if (isset($options['resizeWidth'])) {
             self::$javascript .= ",\n  resizeWidth: " . $options['resizeWidth'];
         }
         if (isset($options['resizeHeight'])) {
             self::$javascript .= ",\n  resizeHeight: " . $options['resizeHeight'];
         }
         if (isset($options['resizeQuality'])) {
             self::$javascript .= ",\n  resizeQuality: " . $options['resizeQuality'];
         }
         self::$javascript .= "\n}\n";
         if ($indicia_templates['file_box'] != '') {
             self::$javascript .= "file_boxTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box']) . "';\n";
         }
         if ($indicia_templates['file_box_initial_file_info'] != '') {
             self::$javascript .= "file_box_initial_file_infoTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box_initial_file_info']) . "';\n";
         }
         if ($indicia_templates['file_box_uploaded_image'] != '') {
             self::$javascript .= "file_box_uploaded_imageTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box_uploaded_image']) . "';\n";
         }
     }
     $occAttrControls = array();
     $occAttrs = array();
     $occAttrControlsExisting = array();
     $taxonRows = array();
     $subSampleRows = array();
     // Load any existing sample's occurrence data into $entity_to_load
     if (isset(self::$entity_to_load['sample:id']) && $options['useLoadedExistingRecords'] === false) {
         self::preload_species_checklist_occurrences(self::$entity_to_load['sample:id'], $options['readAuth'], $options['mediaTypes'], $options['reloadExtraParams'], $subSampleRows, $options['speciesControlToUseSubSamples'], isset($options['subSampleSampleMethodID']) ? $options['subSampleSampleMethodID'] : '');
     }
     // load the full list of species for the grid, including the main checklist plus any additional species in the reloaded occurrences.
     $taxalist = self::get_species_checklist_taxa_list($options, $taxonRows);
     // If we managed to read the species list data we can proceed
     if (!array_key_exists('error', $taxalist)) {
         $attrOptions = array('id' => null, 'valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => "sc:-idx-::occAttr", 'extraParams' => $options['readAuth'], 'survey_id' => array_key_exists('survey_id', $options) ? $options['survey_id'] : null);
         if (!empty($options['attributeIds'])) {
             // make sure we load the grid ID attribute
             if (!empty($options['gridIdAttributeId']) && !in_array($options['gridIdAttributeId'], $options['attributeIds'])) {
                 $options['attributeIds'][] = $options['gridIdAttributeId'];
             }
             $attrOptions['extraParams'] += array('query' => json_encode(array('in' => array('id' => $options['attributeIds']))));
         }
         $attributes = self::getAttributes($attrOptions);
         // Merge in the attribute options passed into the control which can override the warehouse config
         if (isset($options['occAttrOptions'])) {
             foreach ($options['occAttrOptions'] as $attrId => $attr) {
                 if (isset($attributes[$attrId])) {
                     $attributes[$attrId] = array_merge($attributes[$attrId], $attr);
                 }
             }
         }
         // Get the attribute and control information required to build the custom occurrence attribute columns
         self::species_checklist_prepare_attributes($options, $attributes, $occAttrControls, $occAttrControlsExisting, $occAttrs);
         $beforegrid = '<span style="display: none;">Step 1</span>' . "\n";
         if (isset($options['lookupListId'])) {
             $beforegrid .= self::get_species_checklist_clonable_row($options, $occAttrControls, $attributes);
         }
         $onlyImages = true;
         if ($options['mediaTypes']) {
             foreach ($options['mediaTypes'] as $mediaType) {
                 if (substr($mediaType, 0, 6) !== 'Image:') {
                     $onlyImages = false;
                 }
             }
         }
         $grid = self::get_species_checklist_header($options, $occAttrs, $onlyImages);
         $rows = array();
         $imageRowIdxs = array();
         $taxonCounter = array();
         $rowIdx = 0;
         // tell the addTowToGrid javascript how many rows are already used, so it has a unique index for new rows
         self::$javascript .= "indiciaData['gridCounter-" . $options['id'] . "'] = " . count($taxonRows) . ";\n";
         self::$javascript .= "indiciaData['gridSampleCounter-" . $options['id'] . "'] = " . count($subSampleRows) . ";\n";
         // Loop through all the rows needed in the grid
         // Get the checkboxes (hidden or otherwise) that indicate a species is present
         if (is_array(self::$entity_to_load)) {
             $presenceValues = preg_grep("/^sc:[0-9]*:[0-9]*:present\$/", array_keys(self::$entity_to_load));
         }
         // if subspecies are stored, then need to load up the parent species info into the $taxonRows data
         if ($options['subSpeciesColumn']) {
             self::load_parent_species($taxalist, $options);
             if ($options['subSpeciesRemoveSspRank']) {
                 // remove subspecific rank information from the displayed subspecies names by passing a regex
                 self::$javascript .= "indiciaData.subspeciesRanksToStrip='" . lang::get('(form[a\\.]?|var\\.?|ssp\\.)') . "';\n";
             }
         }
         // track if there is a row we are editing in this grid
         $hasEditedRecord = false;
         if ($options['mediaTypes']) {
             $mediaBtnLabel = lang::get($onlyImages ? 'Add images' : 'Add media');
             $mediaBtnClass = 'sc' . $onlyImages ? 'Image' : 'Media' . 'Link';
         }
         foreach ($taxonRows as $txIdx => $rowIds) {
             $ttlId = $rowIds['ttlId'];
             $loadedTxIdx = isset($rowIds['loadedTxIdx']) ? $rowIds['loadedTxIdx'] : -1;
             $existing_record_id = isset($rowIds['occId']) ? $rowIds['occId'] : false;
             // Multi-column input does not work when image upload allowed
             $colIdx = count($options['mediaTypes']) ? 0 : (int) floor($rowIdx / (count($taxonRows) / $options['columns']));
             // Find the taxon in our preloaded list data that we want to output for this row
             $taxonIdx = 0;
             while ($taxonIdx < count($taxalist) && $taxalist[$taxonIdx]['id'] != $ttlId) {
                 $taxonIdx += 1;
             }
             if ($taxonIdx >= count($taxalist)) {
                 continue;
             }
             // next taxon, as this one was not found in the list
             $taxon = $taxalist[$taxonIdx];
             // If we are using the sub-species column then when the taxon has a parent (=species) this goes in the
             // first column and we put the subsp in the second column in a moment.
             if (isset($options['subSpeciesColumn']) && $options['subSpeciesColumn'] && !empty($taxon['parent'])) {
                 $firstColumnTaxon = $taxon['parent'];
             } else {
                 $firstColumnTaxon = $taxon;
             }
             // map field names if using a cached lookup
             if ($options['cacheLookup']) {
                 $firstColumnTaxon = $firstColumnTaxon + array('preferred_name' => $firstColumnTaxon['preferred_taxon'], 'common' => $firstColumnTaxon['default_common_name']);
             }
             // Get the cell content from the taxon_label template
             $firstCell = self::mergeParamsIntoTemplate($firstColumnTaxon, 'taxon_label');
             // If the taxon label template is PHP, evaluate it.
             if ($options['PHPtaxonLabel']) {
                 $firstCell = eval($firstCell);
             }
             // Now create the table cell to contain this.
             $colspan = isset($options['lookupListId']) && $options['rowInclusionCheck'] != 'alwaysRemovable' ? ' colspan="2"' : '';
             $row = '';
             // Add a delete button if the user can remove rows, add an edit button if the user has the edit option set, add a page link if user has that option set.
             if ($options['rowInclusionCheck'] == 'alwaysRemovable') {
                 $imgPath = empty(self::$images_path) ? self::relative_client_helper_path() . "../media/images/" : self::$images_path;
                 $speciesGridLinkPageIconSource = $imgPath . "nuvola/find-22px.png";
                 if ($options['editTaxaNames']) {
                     $row .= '<td class="row-buttons">
                  <img class="action-button remove-row" src=' . $imgPath . 'nuvola/cancel-16px.png>
                  <img class="action-button edit-taxon-name" src=' . $imgPath . 'nuvola/package_editors-16px.png>';
                     if ($options['includeSpeciesGridLinkPage']) {
                         $row .= '<img class="species-grid-link-page-icon" title="' . $options['speciesGridPageLinkTooltip'] . '" alt="Notes icon" src=' . $speciesGridLinkPageIconSource . '>';
                     }
                     $row .= '</td>';
                 } else {
                     $row .= '<td class="row-buttons"><img class="action-button remove-row" src=' . $imgPath . 'nuvola/cancel-16px.png>';
                     if ($options['includeSpeciesGridLinkPage']) {
                         $row .= '<img class="species-grid-link-page-icon" title="' . $options['speciesGridPageLinkTooltip'] . '" alt="Notes icon" src=' . $speciesGridLinkPageIconSource . '>';
                     }
                     $row .= '</td>';
                 }
             }
             // if editing a specific occurrence, mark it up
             $editedRecord = isset($_GET['occurrence_id']) && $_GET['occurrence_id'] == $existing_record_id;
             $editClass = $editedRecord ? ' edited-record ui-state-highlight' : '';
             $hasEditedRecord = $hasEditedRecord || $editedRecord;
             // Verified records can be flagged with an icon
             //Do an isset check as the npms_paths form for example uses the species checklist, but doesn't use an entity_to_load
             if (isset(self::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:record_status"])) {
                 $status = self::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:record_status"];
                 if (preg_match('/[VDR]/', $status)) {
                     $img = false;
                     switch ($status) {
                         case 'V':
                             $img = 'ok';
                             $statusLabel = 'verified';
                             break;
                         case 'D':
                             $img = 'dubious';
                             $statusLabel = 'queried';
                             break;
                         case 'R':
                             $img = 'cancel';
                             $statusLabel = 'rejected';
                             break;
                     }
                     if ($img) {
                         $label = lang::get($statusLabel);
                         $title = lang::get('This record has been {1}. Changing it will mean that it will need to be rechecked by an expert.', $label);
                         $firstCell .= "<img alt=\"{$label}\" title=\"{$title}\" src=\"{$imgPath}nuvola/{$img}-16px.png\">";
                     }
                 }
             }
             $row .= str_replace(array('{content}', '{colspan}', '{editClass}', '{tableId}', '{idx}'), array($firstCell, $colspan, $editClass, $options['id'], $colIdx), $indicia_templates['taxon_label_cell']);
             $row .= self::species_checklist_get_subsp_cell($taxon, $txIdx, $existing_record_id, $options);
             $hidden = $options['rowInclusionCheck'] == 'checkbox' ? '' : ' style="display:none"';
             // AlwaysFixed mode means all rows in the default checklist are included as occurrences. Same for
             // AlwayeRemovable except that the rows can be removed.
             // If we are reloading a record there will be an entity_to_load which will indicate whether present should be checked.
             // This has to be evaluated true or false if reloading a submission with errors.
             if ($options['rowInclusionCheck'] == 'alwaysFixed' || $options['rowInclusionCheck'] == 'alwaysRemovable' || self::$entity_to_load != null && array_key_exists("sc:{$loadedTxIdx}:{$existing_record_id}:present", self::$entity_to_load) && self::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:present"] == true) {
                 $checked = ' checked="checked"';
             } else {
                 $checked = '';
             }
             $row .= "\n<td class=\"scPresenceCell\" headers=\"{$options['id']}-present-{$colIdx}\"{$hidden}>";
             $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:present";
             if ($options['rowInclusionCheck'] === 'hasData') {
                 $row .= "<input type=\"hidden\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$taxon['id']}\"/>";
             } else {
                 // this includes a control to force out a 0 value when the checkbox is unchecked.
                 $row .= "<input type=\"hidden\" class=\"scPresence\" name=\"{$fieldname}\" value=\"0\"/>" . "<input type=\"checkbox\" class=\"scPresence\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$taxon['id']}\" {$checked} />";
             }
             // If we have a grid ID attribute, output a hidden
             if (!empty($options['gridIdAttributeId'])) {
                 $gridAttributeId = $options['gridIdAttributeId'];
                 if (empty($existing_record_id)) {
                     //If in add mode we don't need to include the occurrence attribute id
                     $fieldname = "sc:{$options['id']}-{$txIdx}::occAttr:{$gridAttributeId}";
                     $row .= "<input type=\"hidden\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$options['id']}\"/>";
                 } else {
                     $search = preg_grep("/^sc:[0-9]*:{$existing_record_id}:occAttr:{$gridAttributeId}:" . '[0-9]*$/', array_keys(self::$entity_to_load));
                     if (!empty($search)) {
                         $match = array_pop($search);
                         $parts = explode(':', $match);
                         //The id of the existing occurrence attribute value is at the end of the data
                         $idxOfOccValId = count($parts) - 1;
                         //$txIdx is row number in the grid. We cannot simply take the data from entity_to_load as it doesn't contain the row number.
                         $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occAttr:{$gridAttributeId}:{$parts[$idxOfOccValId]}";
                         $row .= "<input type=\"hidden\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$options['id']}\"/>";
                     }
                 }
             }
             $row .= "</td>";
             if ($options['speciesControlToUseSubSamples']) {
                 $row .= "\n<td class=\"scSampleCell\" style=\"display:none\">";
                 $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occurrence:sampleIDX";
                 $value = $options['subSamplePerRow'] ? $smpIdx : $rowIds['smpIdx'];
                 $row .= "<input type=\"hidden\" class=\"scSample\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"{$value}\" />";
                 $row .= "</td>";
                 // always increment the sample index if 1 per row.
                 if ($options['subSamplePerRow']) {
                     $smpIdx++;
                 }
             }
             $idx = 0;
             foreach ($occAttrControlsExisting as $attrId => $control) {
                 $existing_value = '';
                 $valId = false;
                 if (!empty(data_entry_helper::$entity_to_load)) {
                     // Search for the control in the data to load. It has a suffix containing the attr_value_id which we don't know, hence preg.
                     $search = preg_grep("/^sc:{$loadedTxIdx}:{$existing_record_id}:occAttr:{$attrId}:" . '[0-9]*$/', array_keys(self::$entity_to_load));
                     // Does the control post an array of values? If so, we need to ensure that the existing values are handled properly.
                     $isArrayControl = preg_match('/name="{?[a-z\\-_]*}?\\[\\]"/', $control);
                     if ($isArrayControl) {
                         foreach ($search as $subfieldname) {
                             // to link each value to existing records, we need to store the value ID in the value data.
                             $valueId = preg_match('/(\\d+)$/', $subfieldname, $matches);
                             $control = str_replace('value="' . self::$entity_to_load[$subfieldname] . '"', 'value="' . self::$entity_to_load[$subfieldname] . ':' . $matches[1] . '" selected="selected"', $control);
                         }
                         $ctrlId = str_replace('-idx-', "{$options['id']}-{$txIdx}", $attributes[$attrId]['fieldname']);
                         // remove [] from the end of the fieldname if present, as it is already in the row template
                         $ctrlId = preg_replace('/\\[\\]$/', '', $ctrlId);
                         $loadedCtrlFieldName = '-';
                     } elseif (count($search) > 0) {
                         // Got an existing value.
                         // Warning - if there are multi-values in play here then it will just load one, because this is NOT an array control.
                         // use our preg search result as the field name to load from the existing data array.
                         $loadedCtrlFieldName = array_pop($search);
                         // Convert loaded field name to our output row index
                         $ctrlId = str_replace("sc:{$loadedTxIdx}:", "sc:{$options['id']}-{$txIdx}:", $loadedCtrlFieldName);
                         // find out the loaded value record ID
                         preg_match("/occAttr:[0-9]+:(?P<valId>[0-9]+)\$/", $loadedCtrlFieldName, $matches);
                         if (!empty($matches['valId'])) {
                             $valId = $matches['valId'];
                         } else {
                             $valId = null;
                         }
                     } else {
                         // go for the default, which has no suffix.
                         $loadedCtrlFieldName = str_replace('-idx-:', $loadedTxIdx . ':' . $existing_record_id, $attributes[$attrId]['fieldname']);
                         $ctrlId = str_replace('-idx-:', "{$options['id']}-{$txIdx}:{$existing_record_id}", $attributes[$attrId]['fieldname']);
                     }
                     if (isset(self::$entity_to_load[$loadedCtrlFieldName])) {
                         $existing_value = self::$entity_to_load[$loadedCtrlFieldName];
                     }
                 } else {
                     // no existing record, so use a default control ID which excludes the existing record ID.
                     $ctrlId = str_replace('-idx-', "{$options['id']}-{$txIdx}", $attributes[$attrId]['fieldname']);
                     $loadedCtrlFieldName = '-';
                 }
                 if (!$existing_record_id && $existing_value === '' && array_key_exists('default', $attributes[$attrId])) {
                     // this case happens when reloading an existing record
                     $existing_value = $attributes[$attrId]['default'];
                 }
                 // inject the field name into the control HTML
                 $oc = str_replace('{fieldname}', $ctrlId, $control);
                 if ($existing_value != "") {
                     // For select controls, specify which option is selected from the existing value
                     if (substr($oc, 0, 7) === '<select') {
                         $oc = str_replace('value="' . $existing_value . '"', 'value="' . $existing_value . '" selected="selected"', $oc);
                     } else {
                         if (strpos($oc, 'type="checkbox"') !== false) {
                             if ($existing_value == "1") {
                                 $oc = str_replace('type="checkbox"', 'type="checkbox" checked="checked"', $oc);
                             }
                         } else {
                             if (is_array($existing_value)) {
                                 $existing_value = implode('', $existing_value);
                             }
                             $oc = str_replace('value=""', 'value="' . $existing_value . '"', $oc);
                         }
                     }
                 }
                 $errorField = "occAttr:{$attrId}" . ($valId ? ":{$valId}" : '');
                 $error = self::check_errors($errorField);
                 if ($error) {
                     $oc = str_replace("class='", "class='ui-state-error ", $oc);
                     $oc .= $error;
                 }
                 $headers = $options['id'] . "-attr{$attrId}-{$colIdx}";
                 $class = self::species_checklist_occ_attr_class($options, $idx, $attributes[$attrId]['untranslatedCaption']);
                 $class = $class . 'Cell';
                 $row .= str_replace(array('{label}', '{class}', '{content}', '{headers}'), array(lang::get($attributes[$attrId]['caption']), $class, $oc, $headers), $indicia_templates[$options['attrCellTemplate']]);
                 $idx++;
             }
             if ($options['occurrenceComment']) {
                 $row .= "\n<td class=\"ui-widget-content scCommentCell\" headers=\"{$options['id']}-comment-{$colIdx}\">";
                 $fieldname = "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occurrence:comment";
                 $row .= "<input class=\"scComment\" type=\"text\" name=\"{$fieldname}\" id=\"{$fieldname}\" value=\"" . self::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:occurrence:comment"] . "\" />";
                 $row .= "</td>";
             }
             if ($options['occurrenceSensitivity']) {
                 $row .= "\n<td class=\"ui-widget-content scSensitivityCell\" headers=\"" . $options['id'] . "-sensitivity-{$colIdx}\">";
                 $row .= self::select(array('fieldname' => "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occurrence:sensitivity_precision", 'default' => isset(self::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:occurrence:sensitivity_precision"]) ? self::$entity_to_load["sc:{$loadedTxIdx}:{$existing_record_id}:occurrence:sensitivity_precision"] : false, 'lookupValues' => array('100' => lang::get('Blur to 100m'), '1000' => lang::get('Blur to 1km'), '2000' => lang::get('Blur to 2km'), '10000' => lang::get('Blur to 10km'), '100000' => lang::get('Blur to 100km')), 'blankText' => 'Not sensitive'));
                 $row .= "</td>\n";
             }
             if ($options['mediaTypes']) {
                 $existingImages = is_array(self::$entity_to_load) ? preg_grep("/^sc:{$loadedTxIdx}:{$existing_record_id}:occurrence_medium:id:[a-z0-9]*\$/", array_keys(self::$entity_to_load)) : array();
                 $row .= "\n<td class=\"ui-widget-content scAddMediaCell\">";
                 $style = count($existingImages) > 0 ? ' style="display: none"' : '';
                 $fieldname = "add-media:{$options['id']}-{$txIdx}:{$existing_record_id}";
                 $row .= "<a href=\"\"{$style} class=\"add-media-link button {$mediaBtnClass}\" id=\"{$fieldname}\">" . "{$mediaBtnLabel}</a>";
                 $row .= "</td>";
             }
             // Are we in the first column of a multicolumn grid, or doing single column grid? If so start new row.
             if ($colIdx === 0) {
                 $rows[$rowIdx] = $row;
             } else {
                 $rows[$rowIdx % ceil(count($taxonRows) / $options['columns'])] .= $row;
             }
             $rowIdx++;
             if ($options['mediaTypes'] && count($existingImages) > 0) {
                 $totalCols = ($options['lookupListId'] ? 2 : 1) + 1 + count($occAttrControls) + ($options['occurrenceComment'] ? 1 : 0) + ($options['occurrenceSensitivity'] ? 1 : 0) + (count($options['mediaTypes']) ? 1 : 0);
                 $rows[$rowIdx] = '<td colspan="' . $totalCols . '">' . data_entry_helper::file_box(array('table' => "sc:{$options['id']}-{$txIdx}:{$existing_record_id}:occurrence_medium", 'loadExistingRecordKey' => "sc:{$loadedTxIdx}:{$existing_record_id}:occurrence_medium", 'mediaTypes' => $options['mediaTypes'], 'readAuth' => $options['readAuth'])) . '</td>';
                 $imageRowIdxs[] = $rowIdx;
                 $rowIdx++;
             }
         }
         $grid .= "\n<tbody>\n";
         if (count($rows) > 0) {
             $grid .= self::species_checklist_implode_rows($rows, $imageRowIdxs);
         }
         $grid .= "</tbody>\n";
         $grid = str_replace(array('{class}', '{id}', '{content}'), array(' class="' . implode(' ', $classlist) . '"', " id=\"{$options['id']}\"", $grid), $indicia_templates['data-input-table']);
         // in hasData mode, the wrap_species_checklist method must be notified of the different default
         // way of checking if a row is to be made into an occurrence. This may differ between grids when
         // there are multiple grids on a page.
         if ($options['rowInclusionCheck'] == 'hasData') {
             $grid .= '<input name="rowInclusionCheck-' . $options['id'] . '" value="hasData" type="hidden" />';
             if (!empty($options['hasDataIgnoreAttrs'])) {
                 $grid .= '<input name="hasDataIgnoreAttrs-' . $options['id'] . '" value="' . implode(',', $options['hasDataIgnoreAttrs']) . '" type="hidden" />';
             }
         }
         self::add_resource('addrowtogrid');
         // If the lookupListId parameter is specified then the user is able to add extra rows to the grid,
         // selecting the species from this list. Add the required controls for this.
         if (isset($options['lookupListId'])) {
             // Javascript to add further rows to the grid
             if (isset($indicia_templates['format_species_autocomplete_fn'])) {
                 self::$javascript .= 'formatter = ' . $indicia_templates['format_species_autocomplete_fn'];
             } else {
                 self::$javascript .= "formatter = '" . $indicia_templates['taxon_label'] . "';\n";
             }
             if (!empty(parent::$warehouse_proxy)) {
                 $url = parent::$warehouse_proxy . "index.php/services/data";
             } else {
                 $url = parent::$base_url . "index.php/services/data";
             }
             self::$javascript .= "if (typeof indiciaData.speciesGrid==='undefined') {indiciaData.speciesGrid={};}\n";
             self::$javascript .= "indiciaData.speciesGrid['{$options['id']}']={};\n";
             self::$javascript .= "indiciaData.speciesGrid['{$options['id']}'].cacheLookup=" . ($options['cacheLookup'] ? 'true' : 'false') . ";\n";
             self::$javascript .= "indiciaData.speciesGrid['{$options['id']}'].numValues=" . (!empty($options['numValues']) ? $options['numValues'] : 20) . ";\n";
             self::$javascript .= "indiciaData.speciesGrid['{$options['id']}'].selectMode=" . (!empty($options['selectMode']) && $options['selectMode'] ? 'true' : 'false') . ";\n";
             self::$javascript .= "addRowToGrid('{$url}', '" . $options['id'] . "', '" . $options['lookupListId'] . "', {'auth_token' : '" . $options['readAuth']['auth_token'] . "', 'nonce' : '" . $options['readAuth']['nonce'] . "'}," . " formatter);\r\n";
         }
         // If options contain a help text, output it at the end if that is the preferred position
         $options['helpTextClass'] = isset($options['helpTextClass']) ? $options['helpTextClass'] : 'helpTextLeft';
         $r = self::get_help_text($options, 'before');
         $r .= $beforegrid . $grid;
         $r .= self::get_help_text($options, 'after');
         self::$javascript .= "\$('#" . $options['id'] . "').find('input,select').keydown(keyHandler);\n";
         //nameFilter is an array containing all the parameters required to return data for each of the
         //"Choose species names available for selection" filter types
         self::species_checklist_filter_popup($options, $nameFilter);
         if ($options['subSamplePerRow']) {
             // output a hidden block to contain sub-sample hidden input values.
             $r .= '<div id="' . $options['id'] . '-blocks">' . self::get_subsample_per_row_hidden_inputs() . '</div>';
         }
         if ($hasEditedRecord) {
             self::$javascript .= "\$('#{$options['id']} tbody tr').hide();\n";
             self::$javascript .= "\$('#{$options['id']} tbody tr td.edited-record').parent().show();\n";
             self::$javascript .= "\$('#{$options['id']} tbody tr td.edited-record').parent().next('tr.supplementary-row').show();\n";
             $r .= '<p>' . lang::get('You are editing a single record that is part of a larger sample, so any changes to the sample\'s information such as edits to the date or map reference ' . 'will affect the whole sample.') . " <a id=\"species-grid-view-all-{$options['id']}\">" . lang::get('View all the records in this sample or add more records.') . '</a></p>';
             self::$javascript .= "\$('#species-grid-view-all-{$options['id']}').click(function(e) {\n  \$('#{$options['id']} tbody tr').show(); \n  \$(e.currentTarget).hide();\n});\n";
             self::$onload_javascript .= "\nif (\$('#{$options['id']}').parents('.ui-tabs-panel').length) {\n  indiciaFns.activeTab(\$('#controls'), \$('#{$options['id']}').parents('.ui-tabs-panel')[0].id);\n}\n";
         }
         if ($options['mediaTypes']) {
             $r .= self::add_link_popup($options);
             // make the media types setting available to the grid row add js which has to create file uploader controls
             self::$javascript .= "indiciaData.uploadSettings.mediaTypes=" . json_encode($options['mediaTypes']) . ";\n";
         }
         return $r;
     } else {
         return $taxalist['error'];
     }
 }
 /**
  * Get the location photo control
  */
 protected static function get_control_locationphoto($auth, $args, $tabalias, $options)
 {
     return data_entry_helper::file_box(array_merge(array('table' => 'location_medium', 'readAuth' => $auth['read'], 'caption' => lang::get('File upload'), 'readAuth' => $auth['read']), $options));
 }
Esempio n. 8
0
 /**
  * Return the generated form output.
  * @param array $args List of parameter values passed through to the form depending on how the form has been configured.
  * This array always contains a value for language.
  * @param object $node The Drupal node object.
  * @param array $response When this form is reloading after saving a submission, contains the response from the service call.
  * Note this does not apply when redirecting (in this case the details of the saved object are in the $_GET data).
  * @return Form HTML.
  * @todo: Implement this method 
  */
 public static function get_form($args, $node, $response = null)
 {
     global $indicia_templates, $user;
     data_entry_helper::enable_validation('entry_form');
     $url = !empty($_SERVER['HTTPS']) ? "https://" . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'] : "http://" . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
     $r = data_entry_helper::loading_block_start();
     $r .= "<form method=\"post\" id=\"entry_form\" action=\"{$url}\">\n";
     $readAuth = data_entry_helper::get_read_auth($args['website_id'], $args['password']);
     $r .= "<div id=\"controls\">\n";
     if ($args['interface'] != 'one_page') {
         $r .= "<ul>\n";
         if ($user->uid == 0) {
             $r .= '  <li><a href="#about_you"><span>' . lang::get('about you') . "</span></a></li>\n";
         }
         $r .= '  <li><a href="#species"><span>' . lang::get('what did you see') . "</span></a></li>\n";
         $r .= '  <li><a href="#place"><span>' . lang::get('where was it') . "</span></a></li>\n";
         $r .= '  <li><a href="#other"><span>' . lang::get('other information') . "</span></a></li>\n";
         $r .= "</ul>\n";
         data_entry_helper::enable_tabs(array('divId' => 'controls', 'style' => $args['interface']));
     }
     if ($user->uid == 0) {
         $r .= "<fieldset id=\"about_you\">\n";
         if ($args['interface'] == 'one_page') {
             $r .= '<legend>' . lang::get('about you') . '</legend>';
         }
         $r .= data_entry_helper::text_input(array('label' => lang::get('first name'), 'fieldname' => 'smpAttr:' . $args['first_name_attr_id'], 'class' => 'control-width-4', 'validation' => array('required')));
         $r .= data_entry_helper::text_input(array('label' => lang::get('surname'), 'fieldname' => 'smpAttr:' . $args['surname_attr_id'], 'class' => 'control-width-4', 'validation' => array('required')));
         $r .= data_entry_helper::text_input(array('label' => lang::get('phone number'), 'fieldname' => 'smpAttr:' . $args['phone_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('email'), 'fieldname' => 'smpAttr:' . $args['email_attr_id'], 'class' => 'control-width-4 optional', 'validation' => array('email')));
         if ($args['interface'] == 'wizard') {
             $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => 'first'));
         }
         $r .= "</fieldset>\n";
     }
     // Species tab
     $r .= "<fieldset id=\"species\">\n";
     if ($args['interface'] == 'one_page') {
         $r .= '<legend>' . lang::get('what did you see') . '</legend>';
     }
     $species_list_args = array('label' => lang::get('Species'), 'listId' => $args['species_list_id'], 'columns' => 1, 'checkboxCol' => false, 'occAttrs' => array($args['abundance_attr_id']), 'extraParams' => $readAuth + array('view' => 'detail', 'orderby' => 'taxonomic_sort_order'), 'survey_id' => $args['survey_id'], 'header' => false, 'view' => 'detail', 'PHPtaxonLabel' => true);
     // Build a nice template to show a picture of each species, with fancybox.
     data_entry_helper::add_resource('fancybox');
     data_entry_helper::$javascript .= "jQuery('a.fancybox').fancybox();\n";
     $indicia_templates['taxon_label'] = 'return \'<div class="taxon-cell">' . '<a href="' . data_entry_helper::$base_url . 'upload/{image_path}" class="fancybox" >' . '<img alt="{taxon}" src="' . data_entry_helper::$base_url . 'upload/med-{image_path}" width="250"/></a>' . '<div>{taxon}</div></div>' . '<div class="taxon-desc"><ul><li>\'.str_replace("\\n", "</li><li>","{description_in_list}").\'</li></ul>' . '<a href="http://www.marine-life.org.uk/northeastcetaceans/?q=\'.
     strtolower(str_replace(array(" ", "\\\'"), array("-", ""), "{taxon}")).
     \'" target="_blank" class="ui-state-default ui-corner-all indicia-button">' . lang::get('More Info') . '...</a></div>\';';
     // Template the taxon label cell
     $indicia_templates['taxon_label_cell'] = "\n<td class='scTaxonCell'>{content}</td>";
     // Also template the attribute controls to show the label in place.
     $indicia_templates['attribute_cell'] = "\n<td class='scOccAttrCell'><label>{label}:</label><br/>{content}</td>";
     $r .= data_entry_helper::species_checklist($species_list_args);
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => $user->uid == 0 ? 'middle' : 'first'));
     }
     $r .= "</fieldset>";
     // --Place tab--
     $r .= "<fieldset id=\"place\">\n";
     if ($args['interface'] == 'one_page') {
         $r .= '<legend>' . lang::get('where was it') . '</legend>';
     }
     $r .= data_entry_helper::radio_group(array('label' => 'Where were you when you made the sighting?', 'fieldname' => 'smpAttr:' . $args['platform_attr_id'], 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'extraParams' => $readAuth + array('termlist_id' => $args['platform_termlist_id']), 'sep' => '<br />', 'labelClass' => 'auto', 'class' => 'inline sighting-platform', 'validation' => array('required')));
     $r .= '<div id="place_wrapper" class="hidden">';
     // Some instructions only visible when entering data from a boat
     $r .= '<p class="boat_mode page-notice ui-state-highlight ui-corner-all">' . lang::get('Instructions for when on boat') . '</p>';
     // Some instructions only visible when entering data from the shore
     $r .= '<p class="shore_mode page-notice ui-state-highlight ui-corner-all">' . lang::get('Instructions for clicking on map') . '</p>';
     $r .= '<div class="boat_mode">';
     // Add help examples to the lat and long boxes
     $indicia_templates['sref_textbox_latlong'] = '<label for="{idLat}">{labelLat}:</label>' . '<input type="text" id="{idLat}" name="{fieldnameLat}" {class} {disabled} value="{default}" /> <p class="helpText">e.g. 55:12.345N</p>' . '<label for="{idLong}">{labelLong}:</label>' . '<input type="text" id="{idLong}" name="{fieldnameLong}" {class} {disabled} value="{default}" /> <p class="helpText">e.g. 0:45.678W</p>' . '<input type="hidden" id="imp-geom" name="{table}:geom" value="{defaultGeom}" />' . '<input type="text" id="{id}" name="{fieldname}" style="display:none" value="{default}" />';
     $r .= data_entry_helper::sref_and_system(array('systems' => array(4326 => lang::get('Latitude, Longitude')), 'splitLatLong' => true, 'helpText' => lang::get('Instructions for latlong')));
     $r .= '</div>';
     // Initially, we hide the map. Only show it when the user selects the sighting was from the shore,
     // as a click on the map for boat recordings will not be accurate.
     $r .= '<div class="shore_mode">';
     $options = iform_map_get_map_options($args, $readAuth);
     $olOptions = iform_map_get_ol_options($args);
     $options['maxZoom'] = 9;
     // Switch to degrees and decimal minutes for lat long.
     $options['latLongFormat'] = 'DM';
     $r .= data_entry_helper::map_panel($options, $olOptions);
     // Now, add some JavaScript to show or hide the map. Show it for when the sighting was from the shore.
     // Hide it for boat based sightings as we want a GPS coordinate in this case. The JavaScript looks for the
     // checked radio button to see the value
     data_entry_helper::$javascript .= 'jQuery(".sighting-platform input").click(
   function() {
     var platformId = jQuery("input[name=smpAttr\\\\:' . $args['platform_attr_id'] . ']:checked").val();
     if (platformId == ' . $args['platform_mapped_term_id'] . ') {
       jQuery("#place_wrapper").removeClass("hidden");
       jQuery(".shore_mode").removeClass("hidden");
       jQuery(".boat_mode").addClass("hidden");
     } else {          
       jQuery("#place_wrapper").removeClass("hidden");
       jQuery(".shore_mode").addClass("hidden");
       jQuery(".boat_mode").removeClass("hidden");
     }
   }
 );' . "\n";
     // Force existing setting of the radio buttons to reload when showign page after validation failure
     data_entry_helper::$onload_javascript .= '
 jQuery("input[name=smpAttr\\\\:' . $args['platform_attr_id'] . ']:checked").trigger("click");
 ';
     $r .= '</div></div>';
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls'));
     }
     $r .= '</fieldset>';
     // --Other information tab--
     $r .= "<fieldset id=\"other\">\n";
     // Get authorisation tokens to update and read from the Warehouse.
     $r .= data_entry_helper::get_auth($args['website_id'], $args['password']);
     $r .= "<input type=\"hidden\" name=\"website_id\" value=\"" . $args['website_id'] . "\" />\n";
     $r .= "<input type=\"hidden\" name=\"survey_id\" value=\"" . $args['survey_id'] . "\" />\n";
     $r .= "<input type=\"hidden\" name=\"occurrence:record_status\" value=\"C\" />\n";
     if ($args['interface'] == 'one_page') {
         $r .= '<legend>' . lang::get('other information') . '</legend>';
     }
     $r .= data_entry_helper::date_picker(array('label' => lang::get('Sighting Date'), 'fieldname' => 'sample:date'));
     $indicia_templates['timeFormat'] = '<label>hh:mm</label><br/>';
     $r .= data_entry_helper::text_input(array('label' => lang::get('Sighting Time'), 'fieldname' => 'smpAttr:' . $args['sample_time_attr_id'], 'class' => 'control-width-1', 'suffixTemplate' => 'timeFormat'));
     $r .= data_entry_helper::textarea(array('label' => lang::get('Any other information'), 'fieldname' => 'sample:comment', 'class' => 'control-width-6', 'helpText' => lang::get('Instructions for any other info')));
     $r .= data_entry_helper::file_box(array('caption' => 'Upload your photos', 'resizeWidth' => 1024, 'resizeHeight' => 768, 'table' => 'occurrence_image', 'tabDiv' => 'other'));
     $r .= '<div class="footer">' . data_entry_helper::checkbox(array('label' => lang::get('happy for contact'), 'labelClass' => 'auto', 'fieldname' => 'smpAttr:' . $args['contact_attr_id'])) . '</div>';
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => 'last'));
     } else {
         $r .= "<input type=\"submit\" class=\"ui-state-default ui-corner-all\" value=\"Save\" />\n";
     }
     $r .= "</fieldset></div>";
     $r .= "</form>";
     $r .= data_entry_helper::loading_block_end();
     return $r;
 }
Esempio n. 9
0
 /**
  * Helper function to generate a species checklist from a given taxon list.
  *
  * <p>This function will generate a flexible grid control with one row for each species
  * in the specified list. For each row, the control will display the list preferred term
  * for that species, a checkbox to indicate its presence, and a series of cells for a set
  * of occurrence attributes passed to the control.</p>
  *
  * <p>Further, the control will incorporate the functionality to add extra terms to the
  * control from the parent list of the one given. This will take the form of an autocomplete
  * box against the parent list which will add an extra row to the control upon selection.</p>
  *
  * <p>To change the format of the label displayed for each taxon, use the global $indicia_templates variable
  * to set the value for the entry 'taxon_label'. The tags available in the template are {taxon},
  * {authority} and {common}.</p>
  *
  * @param array $options Options array with the following possibilities:<ul>
  * <li><b>listId</b><br/>
  * Optional. The ID of the taxon_lists record which is to be used to obtain the species or taxon list. This is 
  * required unless lookupListId is provided.</li>
  * <li><b>occAttrs</b><br/>
  * Optional integer array, where each entry corresponds to the id of the desired attribute in the
  * occurrence_attributes table. If omitted, then all the occurrence attributes for this survey are loaded.</li>
  * <li><b>occAttrClasses</b><br/>
  * String array, where each entry corresponds to the css class(es) to apply to the corresponding
  * attribute control (i.e. there is a one to one match with occAttrs). If this array is shorter than
  * occAttrs then all remaining controls re-use the last class.</li>
  * <li><b>extraParams</b><br/>
  * Associative array of items to pass via the query string to the service. This
  * should at least contain the read authorisation array.</li>
  * <li><b>lookupListId</b><br/>
  * Optional. The ID of the taxon_lists record which is to be used to select taxa from when adding
  * rows to the grid. If specified, then an autocomplete text box and Add Row button are generated
  * automatically allowing the user to pick a species to add as an extra row.</li>
  * <li><b>header</b><br/>
  * Include a header row in the grid? Defaults to true.</li>
  * <li><b>columns</b><br/>
  * Number of repeating columns of output. For example, a simple grid of species checkboxes could be output in 2 or 3 columns.
  * Defaults to 1.</li>
  * <li><b>checkboxCol</b><br/>
  * Include a presence checkbox column in the grid. If present, then this contains a checkbox for each row which must be ticked for the
  * row to be saved. Otherwise any row containing data in an attribute gets saved.</li>
  * <li><b>class</b><br/>
  * Optional. CSS class names to add to the control.</li>
  * <li><b>cachetimeout</b><br/>
  * Optional. Specifies the number of seconds before the data cache times out - i.e. how long
  * after a request for data to the Indicia Warehouse before a new request will refetch the data,
  * rather than use a locally stored (cached) copy of the previous request. This speeds things up
  * and reduces the loading on the Indicia Warehouse. Defaults to the global website-wide value:
  * if this is not specified then 1 hour.</li>
  * <li><b>survey_id</b><br/>
  * Optional. Used to determine which attributes are valid for this website/survey combination</li>
  * <li><b>occurrenceComment</b><br/>
  * Optional. If set to true, then an occurrence comment input field is included on each row.</li>
  * <li><b>occurrenceImages</b><br/>
  * Optional. If set to true, then images can be uploaded for each occurrence row. Currently not supported for 
  * multi-column grids.</li>
  * <li><b>attrCellTemplate</b><br/>
  * Optional. If specified, specifies the name of the template (in global $indicia_templates) to use
  * for each cell containing an attribute input control. Valid replacements are {label} and {content}.
  * Default is attribute_cell.</li>
  * <li><b>PHPtaxonLabel</b></li>
  * If set to true, then the taxon_label template should contain a PHP statement that returns the HTML to display for each
  * taxon's label. Otherwise the template should be plain HTML. Defaults to false.
  * </ul>
  */
 public static function species_checklist()
 {
     global $indicia_templates;
     $options = self::check_arguments(func_get_args(), array('listId', 'occAttrs', 'readAuth', 'extraParams', 'lookupListId'));
     $options = self::get_species_checklist_options($options);
     if ($options['columns'] > 1 && $options['occurrenceImages']) {
         throw new Exception('The species_checklist control does not support having more than one occurrence per row (columns option > 0) ' . 'at the same time has having the occurrenceImages option enabled.');
     }
     self::add_resource('json');
     self::add_resource('autocomplete');
     if ($options['occurrenceImages']) {
         self::add_resource('plupload');
         // store some globals that we need later when creating uploaders
         $relpath = self::relative_client_helper_path();
         $interim_image_folder = isset(parent::$interim_image_folder) ? parent::$interim_image_folder : 'upload/';
         self::$javascript .= "uploadScript = '" . dirname($_SERVER['PHP_SELF']) . '/' . $relpath . "upload.php';\n";
         self::$javascript .= "destinationFolder = '" . dirname($_SERVER['PHP_SELF']) . '/' . $relpath . $interim_image_folder . "';\n";
         self::$javascript .= "swfAndXapFolder = '" . $relpath . "plupload/';\n";
         self::$javascript .= "jsPath = '" . self::$js_path . "';\n";
     }
     $occAttrControls = array();
     $occAttrs = array();
     $taxaThatExist = array();
     // Load any existing sample's occurrence data into $entity_to_load
     if (isset(self::$entity_to_load['sample:id'])) {
         self::preload_species_checklist_occurrences(self::$entity_to_load['sample:id'], $options['readAuth'], $options['occurrenceImages']);
     }
     // load the full list of species for the grid, including the main checklist plus any additional species in the reloaded occurrences.
     $taxalist = self::get_species_checklist_taxa_list($options, $taxaThatExist);
     // If we managed to read the species list data we can proceed
     if (!array_key_exists('error', $taxalist)) {
         $attributes = self::getAttributes(array('id' => null, 'valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => "sc:{ttlId}::occAttr", 'extraParams' => $options['readAuth'], 'survey_id' => array_key_exists('survey_id', $options) ? $options['survey_id'] : null));
         // Get the attribute and control information required to build the custom occurrence attribute columns
         self::species_checklist_prepare_attributes($options, $attributes, $occAttrControls, $occAttrs);
         $grid = '';
         if (isset($options['lookupListId'])) {
             $grid .= self::get_species_checklist_clonable_row($options, $occAttrControls);
         }
         $grid .= '<table class="ui-widget ui-widget-content ' . $options['class'] . '" id="' . $options['id'] . '">';
         $grid .= self::get_species_checklist_header($options, $occAttrs);
         $rows = array();
         $rowIdx = 0;
         foreach ($taxalist as $taxon) {
             $id = $taxon['id'];
             // Get the cell content from the taxon_label template
             $firstCell = self::mergeParamsIntoTemplate($taxon, 'taxon_label');
             // If the taxon label template is PHP, evaluate it.
             if ($options['PHPtaxonLabel']) {
                 $firstCell = eval($firstCell);
             }
             // Now create the table cell to contain this.
             $colspan = isset($options['lookupListId']) ? ' colspan="2"' : '';
             $row = str_replace('{content}', $firstCell, str_replace('{colspan}', $colspan, $indicia_templates['taxon_label_cell']));
             $existing_record_id = false;
             $search = preg_grep("/^sc:{$id}:[0-9]*:present\$/", array_keys(self::$entity_to_load));
             if (count($search) === 1) {
                 // we have to implode the search result as the key can be not zero, then strip out the stuff other than the occurrence Id.
                 $existing_record_id = str_replace(array("sc:{$id}:", ":present"), '', implode('', $search));
             }
             if ($options['checkboxCol'] == 'true') {
                 if (self::$entity_to_load != null && array_key_exists("sc:{$id}:{$existing_record_id}:present", self::$entity_to_load)) {
                     $checked = ' checked="checked"';
                 } else {
                     $checked = '';
                 }
                 $row .= "\n<td class=\"scPresenceCell\"><input type=\"checkbox\" name=\"sc:{$id}:{$existing_record_id}:present\" {$checked} /></td>";
             }
             foreach ($occAttrControls as $attrId => $control) {
                 if ($existing_record_id) {
                     $search = preg_grep("/^sc:{$id}:{$existing_record_id}:occAttr:{$attrId}" . '[:[0-9]*]?$/', array_keys(self::$entity_to_load));
                     $ctrlId = count($search) === 1 ? implode('', $search) : $attributes[$attrId]['fieldname'];
                 } else {
                     $ctrlId = str_replace('{ttlId}', $id, $attributes[$attrId]['fieldname']);
                 }
                 if (isset(self::$entity_to_load[$ctrlId])) {
                     $existing_value = self::$entity_to_load[$ctrlId];
                 } elseif (array_key_exists('default', $attributes[$attrId])) {
                     // this case happens when reloading an existing record
                     $existing_value = $attributes[$attrId]['default'];
                 } else {
                     $existing_value = '';
                 }
                 // inject the field name into the control HTML
                 $oc = str_replace('{fieldname}', $ctrlId, $control);
                 if (!empty($existing_value)) {
                     // For select controls, specify which option is selected from the existing value
                     if (substr($oc, 0, 7) == '<select') {
                         $oc = str_replace('value="' . $existing_value . '"', 'value="' . $existing_value . '" selected="selected"', $oc);
                     } else {
                         $oc = str_replace('value=""', 'value="' . $existing_value . '"', $oc);
                     }
                     $error = self::check_errors("occAttr:{$attrId}");
                     if ($error) {
                         $oc = str_replace("class='", "class='ui-state-error ", $oc);
                         $oc .= $error;
                     }
                 }
                 $row .= str_replace(array('{label}', '{content}'), array(lang::get($attributesForThisRow[$attrId]['caption']), $oc), $indicia_templates[$options['attrCellTemplate']]);
             }
             if ($options['occurrenceComment']) {
                 $row .= "\n<td class=\"ui-widget-content\"><input class=\"control-width-4\" type=\"text\" name=\"sc:{$id}:{$existing_record_id}:occurrence:comment\" " . "value=\"" . self::$entity_to_load["sc:{$id}:{$existing_record_id}:occurrence:comment"] . "\" /></td>";
             }
             if ($options['occurrenceImages']) {
                 $existingImages = preg_grep("/^sc:{$id}:{$existing_record_id}:occurrence_image:id:[0-9]*\$/", array_keys(self::$entity_to_load));
                 if (count($existingImages) === 0) {
                     $row .= "\n<td class=\"ui-widget-content\"><a href=\"\" class=\"add-image-link\" id=\"add-images:{$id}:{$existing_record_id}\">" . lang::get('add images') . '</a></td>';
                 } else {
                     $row .= "\n<td class=\"ui-widget-content\"><a href=\"\" class=\"hide-image-link\" id=\"hide-images:{$id}:{$existing_record_id}\">" . lang::get('hide images') . '</a></td>';
                 }
             }
             // Are we in the first column? Note this is disabled if using occurrenceImages as it adds extra rows and messes things up.
             if ($options['occurrenceImages'] || $rowIdx < count($taxalist) / $options['columns']) {
                 $rows[$rowIdx] = $row;
             } else {
                 $rows[$rowIdx % ceil(count($taxalist) / $options['columns'])] .= $row;
             }
             $rowIdx++;
             if ($options['occurrenceImages']) {
                 // If there are existing images for this row, display the image control
                 if (count($existingImages) > 0) {
                     $totalCols = ($options['lookupListId'] ? 2 : 1) + ($options['checkboxCol'] ? 1 : 0) + ($options['occurrenceImages'] ? 1 : 0) + count($occAttrControls);
                     $rows[$rowIdx] = '<td colspan="' . $totalCols . '">' . data_entry_helper::file_box(array('table' => "sc:{$id}:{$existing_record_id}:occurrence_image", 'label' => lang::get('Upload your photos'), 'maxFileCount' => 3)) . '</td>';
                     $rowIdx++;
                 }
             }
         }
         $grid .= "<tbody>\n<tr>" . implode("</tr>\n<tr>", $rows) . "</tr>\n";
         $grid .= '</tbody></table>';
         // If the lookupListId parameter is specified then the user is able to add extra rows to the grid,
         // selecting the species from this list. Add the required controls for this.
         if (isset($options['lookupListId'])) {
             // Javascript to add further rows to the grid
             self::add_resource('addrowtogrid');
             self::$javascript .= "addRowToGrid('" . parent::$base_url . "index.php/services/data" . "', '" . $options['id'] . "', '" . $options['lookupListId'] . "', {'auth_token' : '" . $options['readAuth']['auth_token'] . "', 'nonce' : '" . $options['readAuth']['nonce'] . "'}," . "'" . $indicia_templates['taxon_label'] . "');\r\n";
         }
         if ($options['checkboxCol'] == 'true') {
             // need to tag if checkboxes active so can delete entry if needed
             $grid .= "<input type='hidden' id='control:checkbox' name='control:checkbox' value='YES'/>";
         }
         return $grid;
     } else {
         return $taxalist['error'];
     }
 }
 private static function get_species_checklist_empty_row_with_image($options, $occAttrControls, $attributes, $rowIdx, $mediaId)
 {
     $rowClass = 'scOccImageRow';
     $rowId = $options['id'] . '-scOccImageRow-' . $mediaId;
     $r = '<table><tbody><tr class="' . $rowClass . '" id="' . $rowId . '">';
     $r .= self::get_species_checklist_empty_row($options, $occAttrControls, $attributes);
     if ($options['mediaTypes']) {
         $totalCols = ($options['lookupListId'] ? 2 : 1) + 1 + (count($options['mediaTypes']) ? 1 : 0) + count($occAttrControls);
         $gridId = $options['id'];
         $r .= '<td colspan="' . $totalCols . '">' . data_entry_helper::file_box(array('table' => "sc:{$gridId}-{$rowIdx}:{$mediaId}:sample_medium", 'loadExistingRecordKey' => "sc:{$gridId}:{$rowIdx}:{$mediaId}:sample_medium", 'mediaTypes' => $options['mediaTypes'], 'readAuth' => $options['readAuth'])) . '</td>';
     }
     $r .= "</tr></tbody></table>\n";
     return $r;
 }
 /**
  * Return the generated form output.
  * @return Form HTML.
  */
 public static function get_form($args)
 {
     global $user;
     $logged_in = $user->uid > 0;
     // 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'];
     // enable image viewing with FancyBox - only required if the file box is enabled
     //data_entry_helper::$javascript .= "jQuery(\"a.fancybox\").fancybox();\n";
     $r = "\n<form method=\"post\" id=\"entry_form\">\n";
     if (isset($_GET['taxa_taxon_list_id']) || isset($_GET['taxon_external_key'])) {
         if (isset($_GET['taxa_taxon_list_id'])) {
             $filter = array('id' => $_GET['taxa_taxon_list_id']);
         } else {
             $filter = array('external_key' => $_GET['taxon_external_key']);
         }
         $species = data_entry_helper::get_population_data(array('table' => 'taxa_taxon_list', 'extraParams' => $readAuth + $filter + array('taxon_list_id' => $args['list_id'], 'view' => 'detail', 'preferred' => 't')));
         // we need only one result, but there could be more than one picture, therefore multiple rows
         $uniqueMeaning = false;
         if (count($species) == 1) {
             $uniqueMeaning = $species[0]['taxon_meaning_id'];
         }
         if (count($species) > 1) {
             $uniqueMeaning = $species[0]['taxon_meaning_id'];
             foreach ($species as $item) {
                 if ($item['taxon_meaning_id'] != $uniqueMeaning) {
                     $uniqueMeaning = false;
                 }
             }
         }
         if ($uniqueMeaning) {
             // now we have the meaning_id, we need to fetch the actual species in the chosen common name
             $speciesCommon = data_entry_helper::get_population_data(array('table' => 'taxa_taxon_list', 'extraParams' => $readAuth + array('taxon_meaning_id' => $uniqueMeaning, 'language_iso' => iform_lang_iso_639_2($user->lang), 'view' => 'detail')));
             $r .= '<div class="ui-widget ui-widget-content ui-corner-all page-notice ui-helper-clearfix">';
             $nameString = ($species[0]['language_iso'] == 'lat' ? '<em>' : '') . $species[0]['taxon'] . ($species[0]['language_iso'] == 'lat' ? '</em>' : '');
             if (count($speciesCommon) >= 1) {
                 // use a common name if we have one
                 $nameString = $speciesCommon[0]['taxon'] . ' (' . $nameString . ')';
             }
             if (!empty($species[0]['description_in_list'])) {
                 $r .= '<div class="page-notice">' . lang::get('you are recording a', $nameString) . '</div>';
             }
             $taxa_taxon_list_id = $species[0]['id'];
             $images_path = data_entry_helper::$base_url . (isset(data_entry_helper::$indicia_upload_path) ? data_entry_helper::$indicia_upload_path : 'upload/');
             foreach ($species as $item) {
                 if (!empty($item['image_path'])) {
                     $r .= '<a class="fancybox left" href="' . $images_path . $item['image_path'] . '" style="margin: 0 1em 1em;">';
                     $r .= '<img width="100" src="' . $images_path . 'thumb-' . $item['image_path'] . '" />';
                     $r .= '</a>';
                 }
             }
             if (!empty($species[0]['description_in_list'])) {
                 $r .= '<p>' . $species[0]['description_in_list'] . "</p>";
             } else {
                 $r .= '<p>' . lang::get('you are recording a', $nameString) . '</p>';
             }
             $r .= "</div>\n";
         } else {
             $r .= "<p>The species not be identified uniquely from the URL parameters.</p>\n";
         }
     }
     // request automatic JS validation
     data_entry_helper::enable_validation('entry_form');
     $r .= "<div id=\"controls\">\n";
     if ($args['interface'] != 'one_page') {
         $r .= "<ul>\n";
         if (!$logged_in) {
             $r .= '  <li><a href="#about_you"><span>' . lang::get('about you') . "</span></a></li>\n";
         }
         if (!isset($taxa_taxon_list_id)) {
             $r .= '  <li><a href="#species"><span>' . lang::get('what did you see') . "</span></a></li>\n";
         }
         $r .= '  <li><a href="#place"><span>' . lang::get('where was it') . "</span></a></li>\n";
         $r .= '  <li><a href="#other"><span>' . lang::get('other information') . "</span></a></li>\n";
         $r .= "</ul>\n";
         data_entry_helper::enable_tabs(array('divId' => 'controls', 'style' => $args['interface']));
     }
     if ($user->uid == 0) {
         $r .= "<fieldset id=\"about_you\">\n";
         $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('about you tab instructions') . "</p>";
         $r .= data_entry_helper::text_input(array('label' => lang::get('first name'), 'fieldname' => 'smpAttr:' . $args['first_name_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('surname'), 'fieldname' => 'smpAttr:' . $args['surname_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('email'), 'fieldname' => 'smpAttr:' . $args['email_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('phone number'), 'fieldname' => 'smpAttr:' . $args['phone_attr_id'], 'class' => 'control-width-4'));
         if ($args['interface'] == 'wizard') {
             $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => 'first'));
         }
         $r .= "</fieldset>\n";
     }
     // the species tab is ommitted if the page is called with a taxon in the querystring parameters
     if (isset($taxa_taxon_list_id)) {
         $r .= "<input type=\"hidden\" name=\"occurrence:taxa_taxon_list_id\" value=\"{$taxa_taxon_list_id}\"/>\n";
     } else {
         $r .= "<fieldset id=\"species\">\n";
         $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('species tab instructions') . "</p>";
         $extraParams = $readAuth + array('taxon_list_id' => $args['list_id']);
         if ($args['preferred']) {
             $extraParams += array('preferred' => 't');
         }
         if ($args['restrict_species_to_users_lang']) {
             $extraParams += array('language_iso' => iform_lang_iso_639_2($user->lang));
         }
         $species_list_args = array('label' => lang::get('occurrence:taxa_taxon_list_id'), 'fieldname' => 'occurrence:taxa_taxon_list_id', 'table' => 'taxa_taxon_list', 'captionField' => 'taxon', 'valueField' => 'id', 'columns' => 2, 'view' => 'detail', 'parentField' => 'parent_id', 'extraParams' => $extraParams);
         if ($args['species_ctrl'] == 'tree_browser') {
             // change the node template to include images
             global $indicia_templates;
             $indicia_templates['tree_browser_node'] = '<div>' . '<img src="' . data_entry_helper::$base_url . '/upload/thumb-{image_path}" alt="Image of {caption}" width="80" /></div>' . '<span>{caption}</span>';
         }
         // Dynamically generate the species selection control required.
         $r .= call_user_func(array('data_entry_helper', $args['species_ctrl']), $species_list_args);
         if ($args['interface'] == 'wizard') {
             $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => $user->id == 0 ? 'first' : 'middle'));
         }
         $r .= "</fieldset>\n";
     }
     $r .= "<fieldset id=\"place\">\n";
     // Output all our hidden data here, because this tab is always present
     $r .= $auth['write'];
     if ($logged_in) {
         // If logged in, output some hidden data about the user
         $r .= iform_user_get_hidden_inputs($args);
     }
     // if the species being recorded is a fixed species defined in the URL, then output a hidden
     if (isset($taxa_taxon_list_id)) {
         $r .= "<input type=\"hidden\" name=\"occurrence:taxa_taxon_list_id'\" value=\"" . $taxa_taxon_list_id . "\" />\n";
     }
     $r .= "<input type=\"hidden\" name=\"website_id\" value=\"" . $args['website_id'] . "\" />\n";
     $r .= "<input type=\"hidden\" name=\"survey_id\" value=\"" . $args['survey_id'] . "\" />\n";
     $r .= "<input type=\"hidden\" name=\"record_status\" value=\"" . $args['record_status'] . "\" />\n";
     $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('place tab instructions') . "</p>";
     // Build the array of spatial reference systems into a format Indicia can use.
     $systems = array();
     $list = explode(',', str_replace(' ', '', $args['spatial_systems']));
     foreach ($list as $system) {
         $systems[$system] = lang::get($system);
     }
     $r .= data_entry_helper::georeference_lookup(iform_map_get_georef_options($args, $auth['read']));
     $r .= data_entry_helper::sref_and_system(array('label' => lang::get('sample:entered_sref'), 'systems' => $systems));
     // retrieve options for the IndiciaMapPanel, and optionally options for OpenLayers.
     $options = iform_map_get_map_options($args, $readAuth);
     $options['tabDiv'] = 'place';
     $olOptions = iform_map_get_ol_options($args);
     $options['scroll_wheel_zoom'] = false;
     $r .= data_entry_helper::map_panel($options, $olOptions);
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => $user->id == 0 && isset($taxa_taxon_list_id) ? 'first' : 'middle'));
     }
     $r .= "</fieldset>\n";
     $r .= "<fieldset id=\"other\">\n";
     $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('other tab instructions') . "</p>";
     $r .= data_entry_helper::date_picker(array('label' => lang::get('Date'), 'fieldname' => 'sample:date'));
     $r .= data_entry_helper::file_box(array('caption' => 'Upload your photos', 'readAuth' => $readAuth, 'resizeWidth' => 1024, 'resizeHeight' => 768, 'table' => 'occurrence_image', 'tabDiv' => 'other', 'runtimes' => array('html5', 'html4')));
     // Dynamically create a control for the abundance, unless overridden for this species
     if (isset($species) && count($species) > 0 && trim($args['abundance_overrides']) !== '') {
         $overrides = explode("\n", $args['abundance_overrides']);
         foreach ($overrides as $override) {
             $tokens = explode(':', $override);
             if ($tokens[0] == $species[0]['taxon']) {
                 // remove the default abundance attribute behaviour
                 $args['abundance_attr_id'] = '';
                 if (trim($tokens[1]) !== '') {
                     $attrIds = explode(',', $tokens[1]);
                     $attributes = data_entry_helper::getAttributes(array('id' => null, 'valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => "occAttr", 'extraParams' => $readAuth + array('query' => urlencode(json_encode(array('in' => array('id', $attrIds))))), 'survey_id' => $args['survey_id']));
                     foreach ($attributes as $attribute) {
                         $r .= data_entry_helper::outputAttribute($attribute, array('language' => iform_lang_iso_639_2($user->lang), 'booleanCtrl' => 'checkbox'));
                     }
                 }
             }
         }
     }
     if (!empty($args['abundance_attr_id'])) {
         $abundance_args = array('label' => lang::get('abundance'), 'fieldname' => 'occAttr:' . $args['abundance_attr_id'], 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'extraParams' => $readAuth + array('termlist_id' => $args['abundance_termlist_id']), 'size' => 6, 'sep' => '<br/>');
         $r .= call_user_func(array('data_entry_helper', $args['abundance_ctrl']), $abundance_args);
     }
     $r .= data_entry_helper::textarea(array('label' => lang::get('sample:comment'), 'fieldname' => 'sample:comment', 'class' => 'wide'));
     $r .= '<div class="footer">' . data_entry_helper::checkbox(array('label' => lang::get('happy for contact'), 'labelClass' => 'auto', 'fieldname' => 'smpAttr:' . $args['contact_attr_id'])) . '</div>';
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => 'last'));
     } else {
         $r .= "<input type=\"submit\" class=\"ui-state-default ui-corner-all\" value=\"Save\" />\n";
     }
     $r .= "</fieldset>\n";
     $r .= "</div>\n";
     $r .= "</form>";
     return $r;
 }
 protected static function get_control_habitatblocks($auth, $args, $tabAlias, $options)
 {
     $habitatName = self::$habitatAttrsByCaption['habitat name'];
     $biotopeCode = self::$habitatAttrsByCaption['biotope code'];
     $seabedType = self::$habitatAttrsByCaption['seabed type'];
     $seabedTypeOther = self::$habitatAttrsByCaption['other seabed type'];
     $communities = self::$habitatAttrsByCaption['communities'];
     $animalTurf = self::$habitatAttrsByCaption['animal turf'];
     $animalBed = self::$habitatAttrsByCaption['animal bed'];
     $sedimentTypes = self::$habitatAttrsByCaption['sediment types'];
     // build a template for the data entry controls for each habitat
     $template = '<legend title="' . lang::get('Each habitat is numbered. Make sure the description and quantitative data is ' . 'entered in the correct columns and that you number your sketch or plan in the same way. Each written description ' . 'should tally with the information entered on the columns and diagrams on the next page.') . '">Habitat habitatIdx</legend>';
     $template .= data_entry_helper::text_input(array('fieldname' => "smpAttr:{$habitatName['attributeId']}::habitatIdx", 'label' => lang::get('Habitat name'), 'class' => "control-width-4 habitat-name"));
     $template .= data_entry_helper::textarea(array('fieldname' => 'sample:comment:habitatIdx', 'label' => lang::get('DESCRIPTION (physical + community'), 'tooltip' => lang::get('This should be a brief \\\'sketch in words\\\' to describe the main characteristics ' . 'of each habitat and the dominant plant or animal communities. An example would be: "Gently shelving ' . 'seabed consisting of large boulders up to 1m x 1m with patches of coarse sand collecting between them. Kelp ' . 'forest on boulders with pink encrusting algae and red seaweeds beneath".')));
     if (user_access('biotope codes')) {
         $template .= data_entry_helper::text_input(array('fieldname' => "smpAttr:{$biotopeCode['attributeId']}::habitatIdx", 'label' => lang::get('Biotope code')));
     }
     $template .= data_entry_helper::checkbox_group(array('fieldname' => "smpAttr:{$seabedType['attributeId']}::habitatIdx", 'label' => lang::get('Seabed type'), 'table' => 'termlists_term', 'valueField' => 'id', 'captionField' => 'term', 'extraParams' => $auth['read'] + array('termlist_id' => $seabedType['termlist_id'], 'view' => 'cache'), 'afterControl' => data_entry_helper::text_input(array('label' => lang::get('other'), 'labelClass' => 'auto', 'fieldname' => "smpAttr:{$seabedTypeOther['attributeId']}::habitatIdx")), 'labelClass' => 'auto', 'tooltip' => lang::get('Each habitat should only contain a limited number of physical types. Rock and boulders or ' . 'cobble and pebbles are fine but avoid identifying habitats containing very different physical characteristics, ' . 'for instance rock and sand or wreckage and mud.')));
     $template .= data_entry_helper::checkbox_group(array('fieldname' => "smpAttr:{$communities['attributeId']}::habitatIdx", 'label' => lang::get('Communities'), 'table' => 'termlists_term', 'valueField' => 'id', 'captionField' => 'term', 'extraParams' => $auth['read'] + array('label' => lang::get('other'), 'termlist_id' => $communities['termlist_id'], 'view' => 'cache', 'orderby' => 'sort_order'), 'labelClass' => 'auto', 'tooltip' => lang::get('Each habitat described should rarely have more than one dominant community. For instance ' . 'if the main cover is kelp forest with pink encrusting algae and anemones on the rocks beneath only tick the ' . 'kelp forest box because this dominates.')));
     $template .= '<div style="display: inline-block;">';
     $template .= data_entry_helper::text_input(array('label' => lang::get('animal turf'), 'fieldname' => "smpAttr:{$animalTurf['attributeId']}::habitatIdx", 'labelClass' => 'auto', 'tooltip' => lang::get('Write the main component in the box. This may, for example, be hydroids, jewel anemones or ' . 'bryozoans but will not be mobile animals.')));
     $template .= '</div>&nbsp; <div style="display: inline-block;">';
     $template .= data_entry_helper::text_input(array('label' => lang::get('animal bed'), 'fieldname' => "smpAttr:{$animalBed['attributeId']}::habitatIdx", 'labelClass' => 'auto', 'tooltip' => lang::get('Animal beds are where large numbers of a particular animal changes the composition of the ' . 'seabed. Examples are the brittlestar beds, mussel beds and gravel sea cucumber beds.')));
     $template .= '</div>';
     $template .= data_entry_helper::checkbox_group(array('fieldname' => "smpAttr:{$sedimentTypes['attributeId']}::habitatIdx", 'table' => 'termlists_term', 'valueField' => 'id', 'captionField' => 'term', 'extraParams' => $auth['read'] + array('label' => lang::get('other'), 'termlist_id' => $sedimentTypes['termlist_id'], 'view' => 'cache')));
     $template .= data_entry_helper::file_box(array('table' => 'sample_mediumhabitatIdx', 'caption' => lang::get('Habitat photos'), 'codeGenerated' => 'php'));
     // create the control output
     // add the template, wrapped in a hidden div. JS will be used to clone it as many times as is required.
     $r = "<div style=\"display: none;\"><fieldset id=\"habitat-block-template\">\n{$template}\n</fieldset></div>\n";
     $r .= '<input type="hidden" id="habitat-count" name="habitat-count" />';
     $r .= "<div id=\"habitat-blocks\"></div>\n";
     return $r;
 }
Esempio n. 13
0
<form method="post" enctype="multipart/form-data">
<?php 
// Get authorisation tokens to update and read from the Warehouse.
$auth = data_entry_helper::get_read_write_auth($config['website_id'], $config['password']);
echo $auth['write'];
$readAuth = $auth['read'];
?>
<input type='hidden' id='website_id' name='website_id' value='<?php 
echo $config['website_id'];
?>
' />
<input type='hidden' id='record_status' name='record_status' value='C' />
<?php 
echo data_entry_helper::autocomplete(array('label' => 'Species', 'fieldname' => 'occurrence:taxa_taxon_list_id', 'table' => 'taxa_taxon_list', 'captionField' => 'taxon', 'valueField' => 'id', 'extraParams' => $readAuth + array('taxon_list_id' => $config['species_checklist_taxon_list'])));
echo data_entry_helper::date_picker(array('label' => 'Date', 'fieldname' => 'sample:date'));
echo data_entry_helper::file_box(array('caption' => 'Upload your photos', 'resizeWidth' => 1024, 'resizeHeight' => 768, 'table' => 'occurrence_image', 'runtimes' => array('html4')));
echo data_entry_helper::sref_and_system(array('label' => 'Grid ref'));
echo data_entry_helper::select(array('label' => 'Survey', 'fieldname' => 'sample:survey_id', 'table' => 'survey', 'captionField' => 'title', 'valueField' => 'id', 'extraParams' => $readAuth));
echo data_entry_helper::textarea(array('label' => 'Comment', 'fieldname' => 'sample:comment', 'class' => 'wide'));
?>

<input type="submit" class="ui-state-default ui-corner-all" value="Save" />
</form>
<?php 
echo data_entry_helper::loading_block_end();
echo data_entry_helper::dump_remaining_errors();
echo data_entry_helper::dump_javascript();
?>
</div>
</body>
</html>
Esempio n. 14
0
 private static function get_site_trees_tab($auth, $args, $settings)
 {
     global $indicia_templates;
     $r = '<div id="site-trees" class="ui-helper-clearfix">';
     $r .= '<form method="post" id="tree-form" action="' . self::$ajaxFormUrl . '">';
     $help = '<p>' . lang::get('To add a tree, click on the "Add Tree" button. You can then use the map\'s Location Tool to select the approximate location of your tree on the map. A new tree will then appear on the map.') . '</p>' . '<p>' . lang::get('To select a tree from the existing list of trees at this site you can either:') . '</p>' . '<ol><li>' . lang::get('Click on the button for the tree you wish to view, or') . '</li>' . '<li>' . lang::get('Use the map\'s Query Tool to click on the tree you wish to view on the map.') . '</li></ol>' . '<p>' . lang::get('To remove a tree, first select the tree you wish to remove, then click on the "Remove Tree" button. It will remove the current tree you are viewing completely.') . '</p>';
     $r .= '<div class="ui-state-highlight page-notice ui-corner-all">' . $help . '</div>';
     $r .= self::tree_selector($settings);
     $r .= '<input type="button" value="' . lang::get('Remove Tree') . '" class="remove-tree form-button right" title="' . lang::get('Completely remove the highlighted tree. The total number of tree will be reduced by one. The form will be reloaded after the tree is deleted.') . '">';
     $r .= '<input type="button" value="' . lang::get('Add Tree') . '" class="insert-tree form-button right" title="' . lang::get('This inserts an extra tree.') . '">';
     $r .= '<div id="cols" class="ui-helper-clearfix"><div class="left" style="width: ' . (98 - (isset($args['percent_width']) ? $args['percent_width'] : 50)) . '%">';
     $r .= '<fieldset><legend>' . lang::get('Tree Details') . '</legend>';
     $r .= '<input type="hidden" name="location:id" value="" id="tree-location-id" />';
     $r .= '<input type="hidden" name="locations_website:website_id" value="' . $args['website_id'] . '" id="locations-website-website-id" />';
     $r .= '<input type="hidden" name="location:parent_id" value="' . $settings['locationId'] . '" />';
     $r .= '<input type="hidden" name="location:location_type_id" value="' . $settings['TreeLocationType'][0]['id'] . '" />';
     $r .= '<input type="hidden" name="website_id" value="' . $args['website_id'] . "\" />\n";
     $r .= data_entry_helper::text_input(array('fieldname' => 'location:name', 'label' => lang::get('Tree ID'), 'class' => 'control-width-4 required'));
     $systems = array();
     $list = explode(',', str_replace(' ', '', $args['spatial_systems']));
     foreach ($list as $system) {
         $systems[$system] = lang::get($system);
     }
     $srefOptions = array('id' => 'imp-sref-tree', 'fieldname' => 'location:centroid_sref', 'geomid' => 'imp-geom-tree', 'geomFieldname' => 'location:centroid_geom', 'label' => 'Grid Ref', 'labelClass' => 'auto', 'class' => 'required', 'helpText' => lang::get('You can also click on the map to set the grid reference. If directly entering the coordinates from a GPS device, set the format to "Lat/Long" first. To enter an OS Grid square, choose the "OSGB" or "OSIE" formats.'));
     data_entry_helper::$javascript .= "\n\$('#imp-sref-tree').attr('title',\n    '" . lang::get("When directly entering coordinates as a GPS Lat/Long reading, there should be no spaces between the numbers and the letter. " . "The degrees, minutes and seconds must all be separated by a colon (:). The direction letter can be placed at the start or the end of the number (e.g. N56.532 or 56.532N). " . "The figures may be entered as decimal degrees (e.g. 56.532), degrees and decimal minutes (e.g. 56:31.92), or degrees, minutes and decimal seconds (e.g. 56:31:55.2). " . "You can mix the formats of the Latitude and Longitude, provided they each follow the previous guidelines and a space separates them (e.g. N56.532 2:30W).") . "  " . lang::get("When directly entering an OS map reference, there should be no spaces between any of the characters. " . "An OSGB reference should comprise of 2 letters followed by an even number of digits (e.g. NT274628). " . "An OSIE reference should comprise of of 1 letter followed by an even number of digits (e.g. J081880).") . "');\n";
     // Output the sref control
     $r .= data_entry_helper::sref_textbox($srefOptions);
     $srefOptions = array('id' => 'imp-sref-system-tree', 'fieldname' => 'location:centroid_sref_system', 'class' => 'required', 'systems' => $systems);
     // Output the system control
     if (count($systems) < 2) {
         // Hidden field for the system
         $keys = array_keys($options['systems']);
         $r .= "<input type=\"hidden\" id=\"imp-sref-system-tree\" name=\"" . $options['fieldname'] . "\" value=\"" . $keys[0] . "\" />\n";
         // TODO    	self::include_sref_handler_js($options['systems']);
     } else {
         $r .= data_entry_helper::sref_system_select($srefOptions);
     }
     $r .= '<input type="hidden" name="survey_id" value="' . $args['survey_id'] . '" />';
     $r .= '<input type="hidden" name="sample:survey_id" value="' . $args['survey_id'] . '" />';
     $r .= '<input type="hidden" name="sample:id" value="" />';
     // this sample will reference the location id.
     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
         // @todo The date format should be a global configurable option. It should also be applied to reloading of custom date attributes.
         $d = new DateTime(data_entry_helper::$entity_to_load['sample:date']);
         data_entry_helper::$entity_to_load['sample:date'] = $d->format('d/m/Y');
     }
     $r .= data_entry_helper::date_picker(array('label' => lang::get('Date Tree Selected'), 'fieldname' => 'sample:date', 'class' => 'control-width-2 required'));
     $r .= '<input type="hidden" id="sample:sample_method_id" value="' . $settings['treeSampleMethod']['id'] . '" name="sample:sample_method_id">';
     $r .= '<input type="hidden" id="sample:location_name" value="" name="sample:location_name">';
     $r .= '<input type="hidden" name="occurrence:id" value="" id="occurrence:id" />';
     $r .= '<input type="hidden" name="occurrence:record_status" value="C" id="occurrence:record_status" />';
     $extraParams = $auth['read'];
     $extraParams['taxon_list_id'] = $args['taxon_list_id'];
     $options = array('speciesNameFilterMode' => $args['speciesNameFilterMode']);
     $ctrl = $args['species_ctrl'];
     $species_ctrl_opts = array_merge(array('fieldname' => 'occurrence:taxa_taxon_list_id', 'label' => lang::get('Tree Species'), 'columns' => 1, 'parentField' => 'parent_id', 'blankText' => lang::get('Please select'), 'cacheLookup' => false), $options);
     if (isset($species_ctrl_opts['extraParams'])) {
         $species_ctrl_opts['extraParams'] = array_merge($extraParams, $species_ctrl_opts['extraParams']);
     } else {
         $species_ctrl_opts['extraParams'] = $extraParams;
     }
     if (!empty($args['taxon_filter'])) {
         $species_ctrl_opts['taxonFilterField'] = $args['taxon_filter_field'];
         // applies to autocompletes
         $species_ctrl_opts['taxonFilter'] = helper_base::explode_lines($args['taxon_filter']);
         // applies to autocompletes
     }
     // obtain table to query and hence fields to use
     $db = data_entry_helper::get_species_lookup_db_definition(false);
     // get local vars for the array
     extract($db);
     if ($ctrl !== 'species_autocomplete') {
         // The species autocomplete has built in support for the species name filter.
         // For other controls we need to apply the species name filter to the params used for population
         if (!empty($species_ctrl_opts['taxonFilter']) || $options['speciesNameFilterMode']) {
             $species_ctrl_opts['extraParams'] = array_merge($species_ctrl_opts['extraParams'], data_entry_helper::get_species_names_filter($species_ctrl_opts));
         }
         // for controls which don't know how to do the lookup, we need to tell them
         $species_ctrl_opts = array_merge(array('table' => $tblTaxon, 'captionField' => $colTaxon, 'valueField' => $colId), $species_ctrl_opts);
     }
     // if using something other than an autocomplete, then set the caption template to include the appropriate names. Autocompletes
     // use a JS function instead.
     if ($ctrl !== 'autocomplete' && isset($args['species_include_both_names']) && $args['species_include_both_names']) {
         if ($args['speciesNameFilterMode'] === 'all') {
             $indicia_templates['species_caption'] = "{{$colTaxon}}";
         } elseif ($args['speciesNameFilterMode'] === 'language') {
             $indicia_templates['species_caption'] = "{{$colTaxon}} - {{$colPreferred}}";
         } else {
             $indicia_templates['species_caption'] = "{{$colTaxon}} - {{$colCommon}}";
         }
         $species_ctrl_opts['captionTemplate'] = 'species_caption';
     }
     if ($ctrl == 'tree_browser') {
         // change the node template to include images
         $indicia_templates['tree_browser_node'] = '<div>' . '<img src="' . data_entry_helper::$base_url . '/upload/thumb-{image_path}" alt="Image of {caption}" width="80" /></div>' . '<span>{caption}</span>';
     }
     // Dynamically generate the species selection control required.
     $r .= call_user_func(array('data_entry_helper', $ctrl), $species_ctrl_opts);
     $ctrlOptions = array('extraParams' => $auth['read']);
     $attrSpecificOptions = array();
     $options = helper_base::explode_lines_key_value_pairs($args['attrOptions']);
     self::parseForAttrSpecificOptions($options, $ctrlOptions, $attrSpecificOptions);
     $r .= get_attribute_html($settings['tree_attributes'], $args, $ctrlOptions, '', $attrSpecificOptions);
     $r .= '</fieldset>';
     $r .= "</div>" . '<div class="right" style="width: ' . (isset($args['percent_width']) ? $args['percent_width'] : 50) . '%">';
     $olOptions = iform_map_get_ol_options($args);
     $options = iform_map_get_map_options($args, $auth['read']);
     $options['divId'] = 'trees-map';
     $options['toolbarDiv'] = 'top';
     $options['tabDiv'] = 'site-trees';
     $options['gridRefHint'] = true;
     $options['latLongFormat'] = 'DMS';
     // TODO drive from args or user.
     if (array_key_exists('standard_controls_trees', $args) && $args['standard_controls_trees']) {
         $standard_controls_trees = str_replace("\r\n", "\n", $args['standard_controls_trees']);
         $options['standardControls'] = explode("\n", $standard_controls_trees);
         // If drawing controls are enabled, then allow polygon recording.
         if (in_array('drawPolygon', $options['standardControls']) || in_array('drawLine', $options['standardControls'])) {
             $options['allowPolygonRecording'] = true;
         }
     }
     // also let the user click on a feature to select it. The highlighter just makes it easier to select one.
     // these controls are not present in read-only mode: all you can do is look at the map.
     $options['switchOffSrefRetrigger'] = true;
     $options['clickForSpatialRef'] = true;
     // override the opacity so the parent square does not appear filled in.
     $options['fillOpacity'] = 0;
     // override the map height and buffer size, which are specific to this map.
     $options['height'] = $args['tree_map_height'];
     $options['maxZoomBuffer'] = $args['tree_map_buffer'];
     $options['srefId'] = 'imp-sref-tree';
     $options['geomId'] = 'imp-geom-tree';
     $options['srefSystemId'] = 'imp-sref-system-tree';
     $help = '<p>' . lang::get('Add your trees using the appropriate tools in the top right of the map') . ':</p>' . '<ol><li>' . lang::get('Navigation Tool.') . '</li>' . '<li>' . lang::get('Query Tool. This tool allows you to click on a tree on the map to view its tree details.') . '</li>' . '<li>' . lang::get('Location Tool. This tool allows you to select the approximate location of a new tree on your site map. You can also reposition existing trees by first clicking on the tree and then clicking on its new location on the map.') . '</li></ol>';
     $r .= '<div class="ui-state-highlight page-notice ui-corner-all">' . $help . '</div>';
     $r .= map_helper::map_panel($options, $olOptions);
     $r .= data_entry_helper::file_box(array('table' => 'location_medium', 'readAuth' => $auth['read'], 'caption' => lang::get('Photos of Tree'), 'readAuth' => $auth['read']));
     $r .= "</div>";
     // right
     $r .= '<div class="follow_on_block" style="clear:both;">';
     $r .= get_attribute_html($settings['tree_attributes'], $args, $ctrlOptions, 'Lower Block', $attrSpecificOptions);
     data_entry_helper::$javascript .= "\n\$('#fieldset-optional-external-sc').prepend(\"" . lang::get('If you choose to record this tree for one of the citizen science projects below, please submit the tree ID used for that scheme.') . "\");\n";
     $r .= data_entry_helper::textarea(array('id' => 'location-comment', 'fieldname' => 'location:comment', 'label' => lang::get("Additional information"), 'labelClass' => 'autowidth')) . "<br />";
     $r .= '<input type="submit" value="' . lang::get('Save') . '" class="form-button right" id="submit-tree" />';
     $r .= '</div></form></div>';
     data_entry_helper::$onload_javascript .= "\$('#current-tree').change(selectTree);\n";
     return $r;
 }
Esempio n. 15
0
 /**
  * Helper function to generate a species checklist from a given taxon list.
  *
  * <p>This function will generate a flexible grid control with one row for each species
  * in the specified list. For each row, the control will display the list preferred term
  * for that species, a checkbox to indicate its presence, and a series of cells for a set
  * of occurrence attributes passed to the control.</p>
  *
  * <p>Further, the control will incorporate the functionality to add extra terms to the
  * control from the parent list of the one given. This will take the form of an autocomplete
  * box against the parent list which will add an extra row to the control upon selection.</p>
  *
  * <p>To change the format of the label displayed for each taxon in the grid rows that are pre-loaded into the grid,
  * use the global $indicia_templates variable to set the value for the entry 'taxon_label'. The tags available in the template are {taxon}, {preferred_name},
  * {authority} and {common}. This can be a PHP snippet if PHPtaxonLabel is set to true.</p>
  *
  * <p>To change the format of the label displayed for each taxon in the autocomplete used for searching for species to add to the grid,
  * use the global $indicia_templates variable to set the value for the entry 'format_species_autocomplete_fn'. This must be a JavaScript function
  * which takes a single parameter. The parameter is the item returned from the database with attributes taxon, preferred ('t' or 'f'),
  * preferred_name, common, authority, taxon_group, language. The function must return the string to display in the autocomplete list.</p>
  *
  * <p>To perform an action on the event of a new row being added to the grid, write a JavaScript function called hook_species_checklist_new_row(data), where data
  * is an object containing the details of the taxon row as loaded from the data services.</p>
  *
  * @param array $options Options array with the following possibilities:<ul>
  * <li><b>listId</b><br/>
  * Optional. The ID of the taxon_lists record which is to be used to obtain the species or taxon list. This is
  * required unless lookupListId is provided.</li>
  * <li><b>occAttrs</b><br/>
  * Optional integer array, where each entry corresponds to the id of the desired attribute in the
  * occurrence_attributes table. If omitted, then all the occurrence attributes for this survey are loaded.</li>
  * <li><b>occAttrClasses</b><br/>
  * String array, where each entry corresponds to the css class(es) to apply to the corresponding
  * attribute control (i.e. there is a one to one match with occAttrs). If this array is shorter than
  * occAttrs then all remaining controls re-use the last class.</li>
  * <li><b>extraParams</b><br/>
  * Associative array of items to pass via the query string to the service. This
  * should at least contain the read authorisation array.</li>
  * <li><b>lookupListId</b><br/>
  * Optional. The ID of the taxon_lists record which is to be used to select taxa from when adding
  * rows to the grid. If specified, then an autocomplete text box and Add Row button are generated
  * automatically allowing the user to pick a species to add as an extra row.</li>
  * <li><b>header</b><br/>
  * Include a header row in the grid? Defaults to true.</li>
  * <li><b>columns</b><br/>
  * Number of repeating columns of output. For example, a simple grid of species checkboxes could be output in 2 or 3 columns.
  * Defaults to 1.</li>
  * <li><b>rowInclusionCheck</b><br/>
  * Defines how the system determines whether a row in the grid actually contains an occurrence or not. There are 3 options: <br/>
  * checkbox - a column is included in the grid containing a presence checkbox. If checked then an occurrence is created for the row. This is the default.<br/>
  * alwaysFixed - occurrences are created for all rows in the grid. Rows cannot be removed from the grid apart from newly added rows.<br/>
  * alwaysRemovable - occurrences are created for all rows in the grid. Rows can always be removed from the grid. Best used with no listId so there are
  * no default taxa in the grid, otherwise editing an existing sample will re-add all the existing taxa.<br/>
  * hasData - occurrences are created for any row which has a data value specified in at least one of its columns. <br/>
  * This option supercedes the checkboxCol option which is still recognised for backwards compatibility.</li>
  * <li><b>class</b><br/>
  * Optional. CSS class names to add to the control.</li>
  * <li><b>cachetimeout</b><br/>
  * Optional. Specifies the number of seconds before the data cache times out - i.e. how long
  * after a request for data to the Indicia Warehouse before a new request will refetch the data,
  * rather than use a locally stored (cached) copy of the previous request. This speeds things up
  * and reduces the loading on the Indicia Warehouse. Defaults to the global website-wide value:
  * if this is not specified then 1 hour.</li>
  * <li><b>survey_id</b><br/>
  * Optional. Used to determine which attributes are valid for this website/survey combination</li>
  * <li><b>occurrenceComment</b><br/>
  * Optional. If set to true, then an occurrence comment input field is included on each row.</li>
  * <li><b>occurrenceConfidential</b><br/>
  * Optional. If set to true, then an occurrence confidential checkbox is included on each row.</li>
  * <li><b>occurrenceImages</b><br/>
  * Optional. If set to true, then images can be uploaded for each occurrence row. Currently not supported for
  * multi-column grids.</li>
  * <li><b>resizeWidth</b><br/>
  * If set, then the image files will be resized before upload using this as the maximum pixels width.
  * </li>
  * <li><b>resizeHeight</b><br/>
  * If set, then the image files will be resized before upload using this as the maximum pixels height.
  * </li>
  * <li><b>resizeQuality</b><br/>
  * Defines the quality of the resize operation (from 1 to 100). Has no effect unless either resizeWidth or resizeHeight are non-zero.
  * <li><b>colWidths</b><br/>
  * Optional. Array containing percentage values for each visible column's width, with blank entries for columns that are not specified. If the array is shorter
  * than the actual number of columns then the remaining columns use the default width determined by the browser.</li>
  * <li><b>attrCellTemplate</b><br/>
  * Optional. If specified, specifies the name of the template (in global $indicia_templates) to use
  * for each cell containing an attribute input control. Valid replacements are {label}, {class} and {content}.
  * Default is attribute_cell.</li>
  * <li><b>language</b><br/>
  * language used to filter lookup list items in attributes..</li>
  * <li><b>PHPtaxonLabel</b></li>
  * If set to true, then the taxon_label template should contain a PHP statement that returns the HTML to display for each
  * taxon's label. Otherwise the template should be plain HTML. Defaults to false.
  * </ul>
  */
 public static function species_checklist()
 {
     global $indicia_templates;
     $options = self::check_arguments(func_get_args(), array('listId', 'occAttrs', 'readAuth', 'extraParams', 'lookupListId'));
     $options = self::get_species_checklist_options($options);
     if ($options['columns'] > 1 && $options['occurrenceImages']) {
         throw new Exception('The species_checklist control does not support having more than one occurrence per row (columns option > 0) ' . 'at the same time has having the occurrenceImages option enabled.');
     }
     self::add_resource('json');
     self::add_resource('autocomplete');
     if ($options['occurrenceImages']) {
         self::add_resource('plupload');
         // store some globals that we need later when creating uploaders
         $relpath = self::getRootFolder() . self::relative_client_helper_path();
         $interim_image_folder = isset(parent::$interim_image_folder) ? parent::$interim_image_folder : 'upload/';
         self::$javascript .= "uploadSettings = {\n";
         self::$javascript .= "  uploadScript: '" . $relpath . "upload.php',\n";
         self::$javascript .= "  destinationFolder: '" . $relpath . $interim_image_folder . "',\n";
         self::$javascript .= "  swfAndXapFolder: '" . $relpath . "plupload/',\n";
         self::$javascript .= "  jsPath: '" . self::$js_path . "'";
         if (isset($options['resizeWidth'])) {
             self::$javascript .= ",\n  resizeWidth: " . $options['resizeWidth'];
         }
         if (isset($options['resizeHeight'])) {
             self::$javascript .= ",\n  resizeHeight: " . $options['resizeHeight'];
         }
         if (isset($options['resizeQuality'])) {
             self::$javascript .= ",\n  resizeQuality: " . $options['resizeQuality'];
         }
         self::$javascript .= "\n}\n";
         if ($indicia_templates['file_box'] != '') {
             self::$javascript .= "file_boxTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box']) . "';\n";
         }
         if ($indicia_templates['file_box_initial_file_info'] != '') {
             self::$javascript .= "file_box_initial_file_infoTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box_initial_file_info']) . "';\n";
         }
         if ($indicia_templates['file_box_uploaded_image'] != '') {
             self::$javascript .= "file_box_uploaded_imageTemplate = '" . str_replace('"', '\\"', $indicia_templates['file_box_uploaded_image']) . "';\n";
         }
     }
     $occAttrControls = array();
     $occAttrs = array();
     $taxaThatExist = array();
     // Load any existing sample's occurrence data into $entity_to_load
     if (isset(self::$entity_to_load['sample:id'])) {
         self::preload_species_checklist_occurrences(self::$entity_to_load['sample:id'], $options['readAuth'], $options['occurrenceImages']);
     }
     // load the full list of species for the grid, including the main checklist plus any additional species in the reloaded occurrences.
     $taxalist = self::get_species_checklist_taxa_list($options, $taxaThatExist);
     // If we managed to read the species list data we can proceed
     if (!array_key_exists('error', $taxalist)) {
         $attributes = self::getAttributes(array('id' => null, 'valuetable' => 'occurrence_attribute_value', 'attrtable' => 'occurrence_attribute', 'key' => 'occurrence_id', 'fieldprefix' => "sc:-ttlId-::occAttr", 'extraParams' => $options['readAuth'], 'survey_id' => array_key_exists('survey_id', $options) ? $options['survey_id'] : null));
         // Get the attribute and control information required to build the custom occurrence attribute columns
         self::species_checklist_prepare_attributes($options, $attributes, $occAttrControls, $occAttrs);
         $grid = "\n";
         if (isset($options['lookupListId'])) {
             $grid .= self::get_species_checklist_clonable_row($options, $occAttrControls, $attributes);
         }
         $grid .= '<table class="ui-widget ui-widget-content species-grid ' . $options['class'] . '" id="' . $options['id'] . '">';
         $grid .= self::get_species_checklist_header($options, $occAttrs);
         $rows = array();
         $rowIdx = 0;
         foreach ($taxalist as $taxon) {
             $id = $taxon['id'];
             // Get the cell content from the taxon_label template
             $firstCell = self::mergeParamsIntoTemplate($taxon, 'taxon_label');
             // If the taxon label template is PHP, evaluate it.
             if ($options['PHPtaxonLabel']) {
                 $firstCell = eval($firstCell);
             }
             // Now create the table cell to contain this.
             $colspan = isset($options['lookupListId']) && $options['rowInclusionCheck'] != 'alwaysRemovable' ? ' colspan="2"' : '';
             $row = '';
             // Add a X button if the user can remove rows
             if ($options['rowInclusionCheck'] == 'alwaysRemovable') {
                 $row .= '<td class="ui-state-default remove-row" style="width: 1%">X</td>';
             }
             $row .= str_replace('{content}', $firstCell, str_replace('{colspan}', $colspan, $indicia_templates['taxon_label_cell']));
             $existing_record_id = false;
             if (is_array(self::$entity_to_load)) {
                 $search = preg_grep("/^sc:{$id}:[0-9]*:present\$/", array_keys(self::$entity_to_load));
                 if (count($search) === 1) {
                     // we have to implode the search result as the key can be not zero, then strip out the stuff other than the occurrence Id.
                     $existing_record_id = str_replace(array("sc:{$id}:", ":present"), '', implode('', $search));
                 }
             }
             $hidden = $options['rowInclusionCheck'] == 'checkbox' ? '' : ' style="display:none"';
             // AlwaysFixed mode means all rows in the default checklist are included as occurrences. Same for
             // AlwayeRemovable except that the rows can be removed.
             if ($options['rowInclusionCheck'] == 'alwaysFixed' || $options['rowInclusionCheck'] == 'alwaysRemovable' || self::$entity_to_load != null && array_key_exists("sc:{$id}:{$existing_record_id}:present", self::$entity_to_load)) {
                 $checked = ' checked="checked"';
             } else {
                 $checked = '';
             }
             $row .= "\n<td class=\"scPresenceCell\"{$hidden}>";
             if ($options['rowInclusionCheck'] != 'hasData') {
                 // this includes a control to force out a 0 value when the checkbox is unchecked.
                 $row .= "<input type=\"hidden\" class=\"scPresence\" name=\"sc:{$id}:{$existing_record_id}:present\" value=\"0\"/><input type=\"checkbox\" class=\"scPresence\" name=\"sc:{$id}:{$existing_record_id}:present\" {$checked} />";
             }
             $row .= "</td>";
             foreach ($occAttrControls as $attrId => $control) {
                 if ($existing_record_id) {
                     $search = preg_grep("/^sc:{$id}:{$existing_record_id}:occAttr:{$attrId}" . '[:[0-9]*]?$/', array_keys(self::$entity_to_load));
                     $ctrlId = count($search) === 1 ? implode('', $search) : $attributes[$attrId]['fieldname'];
                 } else {
                     $ctrlId = str_replace('-ttlId-', $id, $attributes[$attrId]['fieldname']);
                 }
                 if (isset(self::$entity_to_load[$ctrlId])) {
                     $existing_value = self::$entity_to_load[$ctrlId];
                 } elseif (array_key_exists('default', $attributes[$attrId])) {
                     // this case happens when reloading an existing record
                     $existing_value = $attributes[$attrId]['default'];
                 } else {
                     $existing_value = '';
                 }
                 // inject the field name into the control HTML
                 $oc = str_replace('{fieldname}', $ctrlId, $control);
                 if (!empty($existing_value)) {
                     // For select controls, specify which option is selected from the existing value
                     if (substr($oc, 0, 7) == '<select') {
                         $oc = str_replace('value="' . $existing_value . '"', 'value="' . $existing_value . '" selected="selected"', $oc);
                     } else {
                         if (strpos($oc, 'checkbox') !== false) {
                             if ($existing_value == "1") {
                                 $oc = str_replace('type="checkbox"', 'type="checkbox" checked="checked"', $oc);
                             }
                         } else {
                             $oc = str_replace('value=""', 'value="' . $existing_value . '"', $oc);
                         }
                     }
                     $error = self::check_errors("sc:{$id}::occAttr:{$attrId}");
                     if (!$error) {
                         // double check in case there is an error against the whole column
                         $error = self::check_errors("occAttr:{$attrId}");
                     }
                     if ($error) {
                         $oc = str_replace("class='", "class='ui-state-error ", $oc);
                         $oc .= $error;
                     }
                 }
                 $row .= str_replace(array('{label}', '{content}'), array(lang::get($attributes[$attrId]['caption']), $oc), $indicia_templates[$options['attrCellTemplate']]);
             }
             if ($options['occurrenceComment']) {
                 $row .= "\n<td class=\"ui-widget-content scCommentCell\"><input class=\"scComment\" type=\"text\" name=\"sc:{$id}:{$existing_record_id}:occurrence:comment\" " . "id=\"sc:{$id}:{$existing_record_id}:occurrence:comment\" value=\"" . self::$entity_to_load["sc:{$id}:{$existing_record_id}:occurrence:comment"] . "\" /></td>";
             }
             if (isset($options['occurrenceConfidential']) && $options['occurrenceConfidential']) {
                 $row .= "\n<td class=\"ui-widget-content scConfidentialCell\">";
                 $row .= self::checkbox(array('fieldname' => "sc:{$id}:{$existing_record_id}:occurrence:confidential"));
                 $row .= "</td>\n";
             }
             if ($options['occurrenceImages']) {
                 $existingImages = is_array(self::$entity_to_load) ? preg_grep("/^sc:{$id}:{$existing_record_id}:occurrence_image:id:[0-9]*\$/", array_keys(self::$entity_to_load)) : array();
                 if (count($existingImages) === 0) {
                     $row .= "\n<td class=\"ui-widget-content scImageLinkCell\"><a href=\"\" class=\"add-image-link scImageLink\" id=\"add-images:{$id}:{$existing_record_id}\">" . str_replace(' ', '&nbsp;', lang::get('add images')) . '</a></td>';
                 } else {
                     $row .= "\n<td class=\"ui-widget-content scImageLinkCell\"><a href=\"\" class=\"hide-image-link scImageLink\" id=\"hide-images:{$id}:{$existing_record_id}\">" . str_replace(' ', '&nbsp;', lang::get('hide images')) . '</a></td>';
                 }
             }
             // Are we in the first column? Note multi-column grids are disabled if using occurrenceImages as it adds extra rows and messes things up.
             if ($options['occurrenceImages'] || $rowIdx < count($taxalist) / $options['columns']) {
                 $rows[$rowIdx] = $row;
             } else {
                 $rows[$rowIdx % ceil(count($taxalist) / $options['columns'])] .= $row;
             }
             $rowIdx++;
             if ($options['occurrenceImages']) {
                 // If there are existing images for this row, display the image control
                 if (count($existingImages) > 0) {
                     $totalCols = ($options['lookupListId'] ? 2 : 1) + 1 + ($options['occurrenceImages'] ? 1 : 0) + count($occAttrControls);
                     $rows[$rowIdx] = '<td colspan="' . $totalCols . '">' . data_entry_helper::file_box(array('table' => "sc:{$id}:{$existing_record_id}:occurrence_image", 'label' => lang::get('Upload your photos'))) . '</td>';
                     $rowIdx++;
                 }
             }
         }
         $grid .= "\n<tbody>\n";
         if (count($rows) > 0) {
             $grid .= "<tr>" . implode("</tr>\n<tr>", $rows) . "</tr>\n";
         } else {
             $grid .= "<tr style=\"display: none\"><td></td></tr>\n";
         }
         $grid .= "</tbody>\n</table>\n";
         // in hasData mode, the wrap_species_checklist method must be notified of the different default way of checking if a row is to be
         // made into an occurrence
         if ($options['rowInclusionCheck'] == 'hasData') {
             $grid .= '<input name="rowInclusionCheck" value="hasData" type="hidden" />';
         }
         if (isset($options['lookupListId']) || isset($options['occurrenceImages']) && $options['occurrenceImages']) {
             // include a js file that has code for handling grid rows, including adding image rows.
             self::add_resource('addrowtogrid');
         }
         // If the lookupListId parameter is specified then the user is able to add extra rows to the grid,
         // selecting the species from this list. Add the required controls for this.
         if (isset($options['lookupListId'])) {
             // Javascript to add further rows to the grid
             if (isset($indicia_templates['format_species_autocomplete_fn'])) {
                 self::$javascript .= 'var formatter = ' . $indicia_templates['format_species_autocomplete_fn'];
             } else {
                 self::$javascript .= "var formatter = '" . $indicia_templates['taxon_label'] . "';\n";
             }
             self::$javascript .= "addRowToGrid('" . parent::$base_url . "index.php/services/data" . "', '" . $options['id'] . "', '" . $options['lookupListId'] . "', {'auth_token' : '" . $options['readAuth']['auth_token'] . "', 'nonce' : '" . $options['readAuth']['nonce'] . "'}," . " formatter);\r\n";
         }
         // If options contain a help text, output it at the end if that is the preferred position
         $options['helpTextClass'] = 'helpTextLeft';
         $r = self::get_help_text($options, 'before');
         $r = $grid;
         $r .= self::get_help_text($options, 'after');
         return $r;
     } else {
         return $taxalist['error'];
     }
 }
 /**
  * Return the generated form output.
  * @return Form HTML.
  */
 public static function get_form($args)
 {
     global $user;
     $logged_in = $user->uid > 0;
     $r = "\n<form method=\"post\" id=\"entry_form\">\n";
     // 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'];
     // request automatic JS validation
     data_entry_helper::enable_validation('entry_form');
     $r .= "<div id=\"controls\">\n";
     if ($args['interface'] != 'one_page') {
         $r .= "<ul>\n";
         if (!$logged_in) {
             $r .= '  <li><a href="#about_you"><span>' . lang::get('about you') . "</span></a></li>\n";
         }
         $r .= '  <li><a href="#species"><span>' . lang::get('what did you see') . "</span></a></li>\n";
         $r .= '  <li><a href="#place"><span>' . lang::get('where was it') . "</span></a></li>\n";
         $r .= '  <li><a href="#other"><span>' . lang::get('other information') . "</span></a></li>\n";
         $r .= "</ul>\n";
         data_entry_helper::enable_tabs(array('divId' => 'controls', 'style' => $args['interface']));
     }
     if ($user->uid == 0) {
         $r .= "<div id=\"about_you\">\n";
         $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('about you tab instructions') . "</p>";
         $r .= data_entry_helper::text_input(array('label' => lang::get('first name'), 'fieldname' => 'smpAttr:' . $args['first_name_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('surname'), 'fieldname' => 'smpAttr:' . $args['surname_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('email'), 'fieldname' => 'smpAttr:' . $args['email_attr_id'], 'class' => 'control-width-4'));
         $r .= data_entry_helper::text_input(array('label' => lang::get('phone number'), 'fieldname' => 'smpAttr:' . $args['phone_attr_id'], 'class' => 'control-width-4'));
         if ($args['interface'] == 'wizard') {
             $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => 'first'));
         }
         $r .= "</div>\n";
     }
     $r .= "<div id=\"species\">\n";
     // Output all our hidden data here
     $r .= $auth['write'];
     if ($logged_in) {
         // If logged in, output some hidden data about the user
         $r .= iform_user_get_hidden_inputs($args);
     }
     $r .= "<input type=\"hidden\" id=\"website_id\" name=\"website_id\" value=\"" . $args['website_id'] . "\" />\n";
     $r .= "<input type=\"hidden\" id=\"survey_id\" name=\"survey_id\" value=\"" . $args['survey_id'] . "\" />\n";
     $r .= "<input type=\"hidden\" id=\"record_status\" name=\"record_status\" value=\"C\" />\n";
     $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('species tab instructions') . "</p>";
     $extraParams = $readAuth + array('taxon_list_id' => $args['list_id']);
     if ($args['preferred']) {
         $extraParams += array('preferred' => 't');
     }
     if ($args['restrict_species_to_users_lang']) {
         $extraParams += array('language_iso' => iform_lang_iso_639_2($user->lang));
     }
     $species_list_args = array('label' => lang::get('occurrence:taxa_taxon_list_id'), 'fieldname' => 'occurrence:taxa_taxon_list_id', 'table' => 'taxa_taxon_list', 'captionField' => 'taxon', 'valueField' => 'id', 'columns' => 2, 'view' => 'detail', 'parentField' => 'parent_id', 'extraParams' => $extraParams);
     if ($args['species_ctrl'] == 'tree_browser') {
         // change the node template to include images
         global $indicia_templates;
         $indicia_templates['tree_browser_node'] = '<div>' . '<img src="' . data_entry_helper::$base_url . '/upload/thumb-{image_path}" alt="Image of {caption}" width="80" /></div>' . '<span>{caption}</span>';
     }
     // Dynamically generate the species selection control required.
     $r .= call_user_func(array('data_entry_helper', $args['species_ctrl']), $species_list_args);
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => $user->id == 0 ? 'first' : 'middle'));
     }
     $r .= "</div>\n";
     $r .= "<div id=\"place\">\n";
     $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('place tab instructions') . "</p>";
     // Build the array of spatial reference systems into a format Indicia can use.
     $systems = array();
     $list = explode(',', str_replace(' ', '', $args['spatial_systems']));
     foreach ($list as $system) {
         $systems[$system] = lang::get($system);
     }
     $r .= data_entry_helper::sref_and_system(array('label' => lang::get('sample:entered_sref'), 'systems' => $systems));
     $r .= data_entry_helper::georeference_lookup(array('label' => lang::get('search for place on map'), 'georefPreferredArea' => $args['georefPreferredArea'], 'georefCountry' => $args['georefCountry'], 'georefLang' => $args['language']));
     // retrieve options for the IndiciaMapPanel, and optionally options for OpenLayers.
     $options = iform_map_get_map_options($args, $readAuth);
     $olOptions = iform_map_get_ol_options($args);
     $r .= data_entry_helper::map_panel($options, $olOptions);
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls'));
     }
     $r .= "</div>\n";
     $r .= "<div id=\"other\">\n";
     $r .= '<p class="page-notice ui-state-highlight ui-corner-all">' . lang::get('other tab instructions') . "</p>";
     $r .= data_entry_helper::date_picker(array('label' => lang::get('Date'), 'fieldname' => 'sample:date'));
     $r .= data_entry_helper::file_box(array('caption' => 'Upload your photos', 'resizeWidth' => 1024, 'resizeHeight' => 768, 'table' => 'occurrence_image'));
     // Dynamically create a control for the abundance
     $abundance_args = array('label' => lang::get('abundance'), 'fieldname' => 'occAttr:' + $args['abundance_attr_id'], 'table' => 'termlists_term', 'captionField' => 'term', 'valueField' => 'id', 'extraParams' => $readAuth + array('termlist_id' => $args['abundance_termlist_id']), 'size' => 6, 'sep' => '<br/>');
     $r .= call_user_func(array('data_entry_helper', $args['abundance_ctrl']), $abundance_args);
     $r .= data_entry_helper::textarea(array('label' => lang::get('sample:comment'), 'fieldname' => 'sample:comment', 'class' => 'wide'));
     $r .= '<div class="footer">' . data_entry_helper::checkbox(array('label' => lang::get('happy for contact'), 'labelClass' => 'auto', 'fieldname' => 'smpAttr:' . $args['contact_attr_id'])) . '</div>';
     if ($args['interface'] == 'wizard') {
         $r .= data_entry_helper::wizard_buttons(array('divId' => 'controls', 'page' => 'last'));
     } else {
         $r .= "<input type=\"submit\" class=\"ui-state-default ui-corner-all\" value=\"Save\" />\n";
     }
     $r .= "</div>\n";
     $r .= "</div>\n";
     $r .= "</form>";
     return $r;
 }