/** * 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; }
/** * 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"> </div>'; $r .= '<div id="idn:' . $taxIdx . ':colour-left:colourbox" class="colour-left-indentifier-colourbox ui-corner-all"> </div>'; $r .= '<div id="idn:' . $taxIdx . ':colour-right:colourbox" class="colour-right-indentifier-colourbox ui-corner-all"> </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)); }
/** * 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; }
/** * 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> <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; }
<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>
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; }
/** * 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(' ', ' ', 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(' ', ' ', 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; }