예제 #1
0
 private static function do_data_services_download($args, $format)
 {
     iform_load_helpers(array('report_helper'));
     $conn = iform_get_connection_details($node);
     $readAuth = data_entry_helper::get_read_auth($conn['website_id'], $conn['password']);
     if (preg_match('/^library\\/occurrences\\/filterable/', $args["report_{$format}"])) {
         $filter = self::build_filter($args, $readAuth, $format, true);
     } else {
         $filter = self::build_filter($args, $readAuth, $format, false);
     }
     global $indicia_templates;
     // let's just get the URL, not the whole anchor element
     $indicia_templates['report_download_link'] = '{link}';
     $limit = $args['limit'] == 0 ? '' : $args['limit'];
     // unlimited or limited
     $sharing = preg_match('/^expert/', $_POST['user-filter']) ? 'verification' : 'data_flow';
     $url = report_helper::report_download_link(array('readAuth' => $readAuth, 'dataSource' => $args["report_{$format}"], 'extraParams' => $filter, 'format' => $format, 'sharing' => $sharing, 'itemsPerPage' => $limit));
     header("Location: {$url}");
 }
 /**
  * Performs the download.
  * @global array $indicia_templates
  * @param type $args
  * @param type $node
  */
 private static function do_data_services_download($args, $node)
 {
     iform_load_helpers(array('report_helper'));
     $format = $_POST['format'];
     $isCustom = preg_match('/^custom-(\\d+)$/', $_POST['format'], $matches);
     if ($isCustom) {
         $customFormats = json_decode($args['custom_formats'], true);
         $customFormat = $customFormats[$matches[1]];
         $report = $customFormat['report'];
         // strip unnecessary .xml from end of report name if provided
         $report = preg_replace('/\\.xml$/', '', $report);
         $format = $customFormat['format'];
         $additionalParamText = $customFormat['params'];
     } else {
         $report = $args["report_{$format}"];
         $additionalParamText = $args["report_params_{$format}"];
     }
     $params = self::build_params($args);
     $params = array_merge($params, get_options_array_with_user_data($additionalParamText));
     $conn = iform_get_connection_details($node);
     global $indicia_templates;
     // let's just get the URL, not the whole anchor element
     $indicia_templates['report_download_link'] = '{link}';
     $limit = $args['limit'] == 0 ? '' : $args['limit'];
     // unlimited or limited
     $sharing = substr($_POST['download-type'], 0, 1);
     $url = report_helper::report_download_link(array('readAuth' => data_entry_helper::$js_read_tokens, 'dataSource' => $report, 'extraParams' => $params, 'format' => $format, 'sharing' => self::expand_sharing_mode($sharing), 'itemsPerPage' => $limit));
     if ($args['reverse_proxy'] == true) {
         // Rewrite the url to pass through proxy.php
         $relative_proxy_path = iform_client_helpers_path() . 'proxy.php?url=' . report_helper::$base_url;
         global $base_url;
         $proxy_path = $base_url . substr($relative_proxy_path, 1);
         // remove report_helper::$base_url from $url
         $url = substr($url, strlen(report_helper::$base_url));
         // add $proxy_path to $url
         $url = $proxy_path . $url;
     }
     header("Location: {$url}");
 }
 /**
  * Returns a simple HTML link to download the contents of a report defined by the options. The options arguments supported are the same as for the
  * report_grid method. Pagination information will be ignored (e.g. itemsPerPage).
  * @param array $options Refer to report_helper::report_download_link documentation.
  * @deprecated Use report_helper::report_download_link.
  */
 public static function report_download_link($options)
 {
     require_once 'report_helper.php';
     return report_helper::report_download_link($options);
 }
 /**
  * Return the Indicia form code
  * @param array $args Input parameters.
  * @param array $node Drupal node object
  * @param array $response Response from Indicia services after posting a verification.
  * @return HTML string
  */
 public static function get_form($args, $node, $response)
 {
     global $user;
     $logged_in = $user->uid > 0;
     if (!$logged_in) {
         return '<p>' . lang::get('Please log in before attempting to use this form.') . '</p>';
     }
     // can't really do this automatically: better to give warning
     if (isset($args['locationTypeFilter'])) {
         return '<p>' . lang::get('Please contact the site administrator. This version of the form uses a different method of specifying the location types.') . '</p>';
     }
     iform_load_helpers(array('report_helper'));
     $auth = report_helper::get_read_auth($args['website_id'], $args['password']);
     if (!self::set_up_survey($args, $auth)) {
         return lang::get('set_up_survey returned false: survey_id missing from presets or location_type definition.');
     }
     $reportOptions = self::get_report_calendar_options($args, $auth);
     $reportOptions['id'] = 'calendar-summary-' . $node->nid;
     if (!empty($args['removable_params'])) {
         self::$removableParams = get_options_array_with_user_data($args['removable_params']);
     }
     self::copy_args($args, $reportOptions, array('weekstart', 'weekOneContains', 'weekNumberFilter', 'outputTable', 'outputChart', 'simultaneousOutput', 'tableHeaders', 'chartLabels', 'disableableSeries', 'chartType', 'rowGroupColumn', 'rowGroupID', 'width', 'height', 'includeTableTotalRow', 'includeTableTotalColumn', 'includeChartTotalSeries', 'includeChartItemSeries', 'includeRawData', 'includeSummaryData', 'includeEstimatesData', 'summaryDataCombining', 'dataRound', 'zeroPointAnchor', 'interpolation', 'firstValue', 'lastValue', 'linkURL', 'includeRawGridDownload', 'includeRawListDownload', 'includeSummaryGridDownload', 'includeEstimatesGridDownload', 'includeListDownload', 'avgFields'));
     if (isset($_GET['outputSource'])) {
         $reportOptions['outputSource'] = $_GET['outputSource'];
     }
     if (isset($_GET['outputFormat'])) {
         $reportOptions['outputFormat'] = $_GET['outputFormat'];
     } else {
         $reportOptions['outputFormat'] = $args['defaultOutput'];
     }
     if (isset($_GET['outputSeries'])) {
         $reportOptions['outputSeries'] = $_GET['outputSeries'];
     }
     // default is all
     // Advanced Chart options
     $rendererOptions = trim($args['renderer_options']);
     if (!empty($rendererOptions)) {
         $reportOptions['rendererOptions'] = json_decode($rendererOptions, true);
     }
     $legendOptions = trim($args['legend_options']);
     if (!empty($legendOptions)) {
         $reportOptions['legendOptions'] = json_decode($legendOptions, true);
     }
     $axesOptions = trim($args['axes_options']);
     if (!empty($axesOptions)) {
         $reportOptions['axesOptions'] = json_decode($axesOptions, true);
     }
     if (isset($args['countColumn']) && $args['countColumn'] != '') {
         $reportOptions['countColumn'] = 'attr_occurrence_' . str_replace(' ', '_', strtolower($args['countColumn']));
         // assume that this is an occurrence attribute.
         $reportOptions['extraParams']['occattrs'] = $args['countColumn'];
     }
     // for a normal user, we can only link to those samples we have created
     $reportOptions['location_list'] = array();
     // for a branch user, we have an allowed list of locations for which we can link to the sample.
     self::$branchLocationList = array();
     if (isset($args['branchManagerPermission']) && $args['branchManagerPermission'] != "" && user_access($args['branchManagerPermission'])) {
         // Get list of locations attached to this user via the branch cms user id attribute
         // first need to scan param_presets for survey_id..
         $attrArgs = array('valuetable' => 'location_attribute_value', 'attrtable' => 'location_attribute', 'key' => 'location_id', 'fieldprefix' => 'locAttr', 'extraParams' => $auth, 'survey_id' => self::$siteUrlParams[self::$SurveyKey]);
         if (isset($args['locationTypesFilter']) && $args['locationTypesFilter'] != "") {
             $attrArgs['location_type_id'] = self::$siteUrlParams[self::$locationTypeKey]['value'];
         }
         $locationAttributes = data_entry_helper::getAttributes($attrArgs, false);
         $cmsAttr = self::extract_attr($locationAttributes, $args['branchFilterAttribute']);
         if (!$cmsAttr) {
             return lang::get('Branch Manager location list lookup: missing Branch allocation attribute') . ' {' . print_r($attrArgs, true) . '} : ' . $args['branchFilterAttribute'];
         }
         $attrListArgs = array('extraParams' => array_merge(array('view' => 'list', 'website_id' => $args['website_id'], 'location_attribute_id' => $cmsAttr['attributeId'], 'raw_value' => $user->uid), $auth), 'table' => 'location_attribute_value');
         $attrList = data_entry_helper::get_population_data($attrListArgs);
         if (isset($attrList['error'])) {
             return $attrList['error'];
         }
         if (count($attrList) > 0) {
             foreach ($attrList as $attr) {
                 self::$branchLocationList[] = $attr['location_id'];
             }
         }
         $reportOptions['location_list'] = self::$branchLocationList;
     }
     // for an admin, we can link to all samples.
     if (isset($args['managerPermission']) && $args['managerPermission'] != "" && user_access($args['managerPermission'])) {
         $reportOptions['location_list'] = 'all';
     }
     if (function_exists('module_exists') && module_exists('easy_login') && function_exists('hostsite_get_user_field')) {
         $reportOptions['my_user_id'] = hostsite_get_user_field('indicia_user_id');
     } else {
         $reportOptions['my_user_id'] = $user->uid;
     }
     $retVal = '';
     // Add controls first: set up a control bar
     $retVal .= "\n<table id=\"controls-table\" class=\"ui-widget ui-widget-content ui-corner-all controls-table\"><thead class=\"ui-widget-header\"><tr>";
     $retVal .= self::date_control($args, $auth, $node, $reportOptions);
     $retVal .= '<th>' . self::user_control($args, $auth, $node, $reportOptions) . '</th>';
     $retVal .= '<th>' . self::location_control($args, $auth, $node, $reportOptions) . '</th>';
     // note this includes the location_type control if needed
     $siteUrlParams = self::get_site_url_params();
     if (!empty($args['removable_params'])) {
         foreach (self::$removableParams as $param => $caption) {
             $checked = isset($_GET[$param]) && $_GET[$param] === 'true' ? ' checked="checked"' : '';
             $retVal .= '<th><input type="checkbox" name="removeParam-' . $param . '" id="removeParam-' . $param . '" class="removableParam"' . $checked . '/>' . '<label for="removeParam-' . $param . '" >' . lang::get($caption) . '</label></th>';
         }
         self::set_up_control_change('removeParam-' . $param, $param, array(), true);
     }
     // are there any params that should be set to blank using one of the removable params tickboxes?
     foreach (self::$removableParams as $param => $caption) {
         if (isset($_GET[$param]) && $_GET[$param] === 'true') {
             $reportOptions['extraParams'][$param] = '';
         }
     }
     if (self::$siteUrlParams[self::$userKey]['value'] == '' && self::$siteUrlParams[self::$locationKey]['value'] == '') {
         $checked = self::$siteUrlParams[self::$cacheKey]['value'] === 'true' ? ' checked="checked"' : '';
         $retVal .= '<th><input type="checkbox" name="cachingParam" id="cachingParam" class="cachingParam"' . $checked . '/>' . '<label for="cachingParam" title="' . lang::get("When fetching the full data set, selcting this improves performance by not going to the warehouse to get the data. Occassionally, even when selected, the data will be refreshed, which will appear to slow down the response.") . '" >' . lang::get("Use cached data") . '</label></th>';
         $reportOptions['caching'] = self::$siteUrlParams[self::$cacheKey]['value'] === 'true' ? true : 'store';
         self::set_up_control_change('cachingParam', self::$cacheKey, array(), true);
         $checked = self::$siteUrlParams[self::$downloadKey]['value'] === 'true' ? ' checked="checked"' : '';
         if (self::$siteUrlParams[self::$downloadKey]['value'] !== 'true' && isset($reportOptions['includeListDownload']) && $reportOptions['includeListDownload'] == true) {
             $reportOptions['includeRawGridDownload'] = false;
             $reportOptions['includeRawListDownload'] = false;
             $reportOptions['includeSummaryGridDownload'] = false;
             $reportOptions['includeEstimatesGridDownload'] = false;
             $reportOptions['includeListDownload'] = false;
             for ($i = 1; $i <= 4; $i++) {
                 unset($args['Download' . $i . 'Caption']);
                 unset($args['download_report_' . $i]);
             }
             data_entry_helper::$javascript .= "jQuery('.downloads-table-label').remove();\n" . "jQuery('#downloads-table thead tr').prepend('<th>" . "<label for=\"downloadParam\" title=\"" . lang::get("When fetching the full data set, unselecting this improves performance by not including the download data in the page. This extra data can lead to a significantly increase in download time.") . "\" >" . lang::get("Include Downloads") . "</label>" . "<input type=\"checkbox\" name=\"downloadParam\" id=\"downloadParam\" class=\"downloadParam\"" . $checked . "/>" . "</th>');\n";
             self::set_up_control_change('downloadParam', self::$downloadKey, array(), true);
         }
     }
     if (self::$siteUrlParams[self::$locationTypeKey]['value'] == '') {
         if (isset($args['locationTypesFilter']) && $args['locationTypesFilter'] != "") {
             $types = explode(',', $args['locationTypesFilter']);
             $terms = self::get_sorted_termlist_terms(array('read' => $auth), 'indicia:location_types', array($types[0]));
             $reportOptions['extraParams']['location_type_id'] = $terms[0]['id'];
         }
     } else {
         $reportOptions['extraParams']['location_type_id'] = self::$siteUrlParams[self::$locationTypeKey]['value'];
     }
     $reportOptions['includeReportTimeStamp'] = isset($args['includeFilenameTimestamps']) && $args['includeFilenameTimestamps'];
     $retVal .= '</tr></thead></table>';
     $reportOptions['highlightEstimates'] = true;
     $retVal .= report_helper::report_calendar_summary($reportOptions);
     // upto this point the report options holds the cms user id as user_id: the report_helper converts this to the indicia
     // user_id if relevant to this installation. We now need to do the same for the report links.
     if (isset($reportOptions['extraParams']['user_id'])) {
         $reportOptions['extraParams']['cms_user_id'] = $reportOptions['extraParams']['user_id'];
         if (function_exists('module_exists') && module_exists('easy_login')) {
             $account = user_load($reportOptions['extraParams']['user_id']);
             if (function_exists('profile_load_profile')) {
                 profile_load_profile($account);
             }
             /* will not be invoked for Drupal7 where the fields are already in the account object */
             if (isset($account->profile_indicia_user_id)) {
                 $reportOptions['extraParams']['user_id'] = $account->profile_indicia_user_id;
             }
         }
     }
     if (isset($args['managerPermission']) && $args['managerPermission'] != "" && user_access($args['managerPermission']) || $reportOptions['extraParams']['location_list'] != '' || $reportOptions['extraParams']['user_id'] != '') {
         // if user specified - either me in normal or branch mode, or a manager
         global $indicia_templates;
         $indicia_templates['report_download_link'] = '<th><a href="{link}"><button type="button">{caption}</button></a></th>';
         // format is assumed to be CSV
         $downloadOptions = array('readAuth' => $auth, 'extraParams' => array_merge($reportOptions['extraParams'], array('date_from' => $reportOptions['date_start'], 'date_to' => $reportOptions['date_end'])), 'itemsPerPage' => false);
         // there are problems dealing with location_list as an array if empty, so connvert
         if ($downloadOptions['extraParams']['location_list'] == "") {
             $downloadOptions['extraParams']['location_list'] = "(-1)";
         } else {
             $downloadOptions['extraParams']['location_list'] = '(' . $downloadOptions['extraParams']['location_list'] . ')';
         }
         for ($i = 1; $i <= 4; $i++) {
             if (isset($args['Download' . $i . 'Caption']) && $args['Download' . $i . 'Caption'] != "" && isset($args['download_report_' . $i]) && $args['download_report_' . $i] != "") {
                 $downloadOptions['caption'] = $args['Download' . $i . 'Caption'];
                 $downloadOptions['dataSource'] = $args['download_report_' . $i];
                 $downloadOptions['filename'] = $reportOptions['downloadFilePrefix'] . preg_replace('/[^A-Za-z0-9]/i', '', $args['Download' . $i . 'Caption']);
                 $downloadOptions['filename'] .= isset($reportOptions['includeReportTimeStamp']) && $reportOptions['includeReportTimeStamp'] ? '_' . date('YmdHis') : '';
                 if (isset($args['download_report_' . $i . '_format'])) {
                     $downloadOptions['format'] = $args['download_report_' . $i . '_format'];
                 }
                 data_entry_helper::$javascript .= "\nif(jQuery('#downloads-table th').length==0)\n  jQuery('#downloads-table thead tr').append('<th class=\"downloads-table-label\">" . lang::get("Downloads") . "</th>');\njQuery('#downloads-table thead tr').append('" . report_helper::report_download_link($downloadOptions) . "');\n";
             }
         }
     }
     return $retVal;
 }
예제 #5
0
 /**
  * Performs the download.
  * URL arguments date_from, date_to, survey_list, format, download-type
  * @global array $indicia_templates
  * @param type $args
  * @param type $node
  */
 private static function do_data_services_download($args, $node)
 {
     iform_load_helpers(array('report_helper'));
     // default data format JSON
     if (!array_key_exists('format', $_GET)) {
         $_GET['format'] = 'json';
     }
     $format = $_GET['format'];
     // download type set to Reporting by default
     if (!array_key_exists('download-type', $_GET)) {
         $_GET['download-type'] = 'R';
     }
     $isCustom = preg_match('/^custom-(\\d+)$/', $_GET['format'], $matches);
     if ($isCustom) {
         $customFormats = json_decode($args['custom_formats'], true);
         $customFormat = $customFormats[$matches[1]];
         $report = $customFormat['report'];
         // strip unnecessary .xml from end of report name if provided
         $report = preg_replace('/\\.xml$/', '', $report);
         $format = $customFormat['format'];
         $additionalParamText = $customFormat['params'];
     } else {
         if ($format == 'json') {
             $report = $args["report_csv"];
             $additionalParamText = $args["report_params_csv"];
         } else {
             $report = $args["report_{$format}"];
             $additionalParamText = $args["report_params_{$format}"];
         }
     }
     //Getting the form params first and then allowing the user to
     //overwrite then through $args
     require_once 'includes/user.php';
     $params = get_options_array_with_user_data($additionalParamText);
     $params = array_merge($params, self::build_params($args));
     $conn = iform_get_connection_details($node);
     global $indicia_templates;
     // let's just get the URL, not the whole anchor element
     $indicia_templates['report_download_link'] = '{link}';
     $limit = $args['limit'] == 0 ? '' : $args['limit'];
     // unlimited or limited
     $sharing = substr($_GET['download-type'], 0, 1);
     $url = report_helper::report_download_link(array('readAuth' => data_entry_helper::$js_read_tokens, 'dataSource' => $report, 'extraParams' => $params, 'format' => $format, 'sharing' => self::expand_sharing_mode($sharing), 'itemsPerPage' => $limit));
     header("Location: {$url}");
 }
예제 #6
0
 /**
  * <p>Outputs a calendar based summary grid that loads the results of the summary builder module.</p>
  * <p>If you need 2 grids on one page, then you must define a different id in the options for each grid.</p>
  * <p>The grid operation has NOT been AJAXified. There is no download option.</p>
  *
  * @param array $options Options array with the following possibilities:<ul>
  * <li><b>id</b><br/>
  * Optional unique identifier for the grid's container div. This is required if there is more than
  * one grid on a single web page to allow separation of the page and sort $_GET parameters in the URLs
  * generated.</li>
  * <li><b>mode</b><br/>
  * Pass report for a report, or direct for an Indicia table or view. Default is report.</li>
  * <li><b>readAuth</b><br/>
  * Read authorisation tokens.</li>
  * <li><b>dataSource</b><br/>
  * Name of the report file or table/view. when used, any user_id must refer to the CMS user ID, not the Indicia
  * User.</li>
  * <li><b>view</b>
  * When loading from a view, specify list, gv or detail to determine which view variant is loaded. Default is list.
  * </li>
  * <li><b>extraParams</b><br/>
  * Array of additional key value pairs to attach to the request. This should include fixed values which cannot be changed by the
  * user and therefore are not needed in the parameters form.
  * </li>
  * <li><b>paramDefaults</b>
  * Optional associative array of parameter default values. Default values appear in the parameter form and can be overridden.</li>
  * <li><b>tableHeaders</b>
  * Defines which week column headers should be included: date, number or both
  * <li><b>weekstart</b>
  * Defines the first day of the week. There are 2 options.<br/>'.
  *  weekday=<n> where <n> is a number between 1 (for Monday) and 7 (for Sunday). Default is 'weekday=7'
  *  date=MMM-DD where MMM-DD is a month/day combination: e.g. choosing Apr-1 will start each week on the day of the week on which the 1st of April occurs.</li>
  * <li><b>weekOneContains</b>
  * Defines week one as the week which contains this date. Format should be MMM-DD, which is a month/day combination: e.g. choosing Apr-1 will define
  * week one as being the week containing the 1st of April. Defaults to the 1st of January.</li>
  * <li><b>weekNumberFilter</b>
  * Restrict displayed weeks to between 2 weeks defined by their week numbers. Colon separated.
  * Leaving an empty value means the end of the year.
  * Examples: "1:30" - Weeks one to thirty inclusive.
  * "4:" - Week four onwards.
  * ":5" - Upto and including week five.</li>
  * <li><b>rowGroupColumn</b>
  * The column in the report which is used as the label for the vertical axis on the grid.</li>
  * <li><b>rowGroupID</b>
  * The column in the report which is used as the id for the vertical axis on the grid.</li>
  * <li><b>countColumn</b>
  * OPTIONAL: The column in the report which contains the count for this occurrence. If omitted then the default
  * is to assume one occurrence = count of 1</li>
  * <li><b>includeChartItemSeries</b>
  * Defaults to true. Include a series for each item in the report output.
  * </li>
  * <li><b>includeChartTotalSeries</b>
  * Defaults to true. Include a series for the total of each item in the report output.
  * </li>
  * </ul>
  * @todo: Future Enhancements? Allow restriction to month.
  */
 public static function report_calendar_summary2($options)
 {
     $r = "";
     // I know that there are better ways to approach some of the date manipulation, but they are PHP 5.3+.
     // We support back to PHP 5.2
     // TODO put following JS into a control JS file.
     $warnings = '<span style="display:none;">Starting report_calendar_summary2 : ' . date(DATE_ATOM) . '</span>' . "\n";
     data_entry_helper::add_resource('jquery_ui');
     $options = self::get_report_calendar_summary_options($options);
     // don't use all of these now, eg. extraParams: this is used later for raw data
     $extras = '';
     $extraParams = $options['readAuth'] + array('year' => $options['year'], 'survey_id' => $options['survey_id']);
     // at the moment the summary_builder module indexes the user_id on the created_by_id field on the parent sample.
     // this effectively means that it assumes easy_login.
     // Also means we have to use the converted Indicia user_id, stored by options function above in the extraParams.
     $extraParams['user_id'] = !isset($options['extraParams']['user_id']) || $options['extraParams']['user_id'] == "" ? 'NULL' : $options['extraParams']['user_id'];
     if (isset($options['taxon_list_id']) && $options['taxon_list_id'] != "") {
         $extraParams['taxon_list_id'] = $options['taxon_list_id'];
     }
     if (isset($options['location_id']) && $options['location_id'] != "") {
         $extraParams['location_id'] = $options['location_id'];
     } else {
         if (isset($options['branch_location_list'])) {
             $extraParams['query'] = urlencode(json_encode(array('in' => array('location_id', $options['branch_location_list']))));
         } else {
             $extraParams['location_id'] = 'NULL';
         }
     }
     $records = data_entry_helper::get_population_data(array('table' => 'summary_occurrence', 'extraParams' => $extraParams));
     if (isset($records['error'])) {
         return $records['error'];
     }
     data_entry_helper::$javascript .= "\r\nvar pageURI = \"" . $_SERVER['REQUEST_URI'] . "\";\r\nfunction rebuild_page_url(oldURL, overrideparam, overridevalue) {\r\n  var parts = oldURL.split('?');\r\n  var params = [];\r\n  if(overridevalue!=='') params.push(overrideparam+'='+overridevalue);\r\n  if(parts.length > 1) {\r\n    var oldparams = parts[1].split('&');\r\n    for(var i = 0; i < oldparams.length; i++){\r\n      var bits = oldparams[i].split('=');\r\n      if(bits[0] != overrideparam) params.push(oldparams[i]);\r\n    }\r\n  }\r\n  return parts[0]+(params.length > 0 ? '?'+params.join('&') : '');\r\n};\r\nfunction update_controls(){\r\n  \$('#year-control-previous').attr('href',rebuild_page_url(pageURI,'year'," . $options['year'] . "-1));\r\n  \$('#year-control-next').attr('href',rebuild_page_url(pageURI,'year'," . $options['year'] . "+1));\r\n  // user and location ids are dealt with in the main form. their change functions look a pageURI\r\n}\r\nupdate_controls();\r\n";
     // ISO Date - Mon=1, Sun=7
     // Week 1 = the week with date_from in
     if (!isset($options['weekstart']) || $options['weekstart'] == "") {
         $options['weekstart'] = "weekday=7";
         // Default Sunday
     }
     if (!isset($options['weekNumberFilter']) || $options['weekNumberFilter'] == "") {
         $options['weekNumberFilter'] = ":";
     }
     $weeknumberfilter = explode(':', $options['weekNumberFilter']);
     if (count($weeknumberfilter) != 2) {
         $warnings .= "Week number filter unrecognised {" . $options['weekNumberFilter'] . "} defaulting to all<br />";
         $weeknumberfilter = array('', '');
     } else {
         if ($weeknumberfilter[0] != '' && (intval($weeknumberfilter[0]) != $weeknumberfilter[0] || $weeknumberfilter[0] > 52)) {
             $warnings .= "Week number filter start unrecognised or out of range {" . $weeknumberfilter[0] . "} defaulting to year start<br />";
             $weeknumberfilter[0] = '';
         }
         if ($weeknumberfilter[1] != '' && (intval($weeknumberfilter[1]) != $weeknumberfilter[1] || $weeknumberfilter[1] < $weeknumberfilter[0] || $weeknumberfilter[1] > 52)) {
             $warnings .= "Week number filter end unrecognised or out of range {" . $weeknumberfilter[1] . "} defaulting to year end<br />";
             $weeknumberfilter[1] = '';
         }
     }
     $weekstart = explode('=', $options['weekstart']);
     if ($weekstart[0] == 'date') {
         $weekstart_date = date_create($options['year'] . "-" . $weekstart[1]);
         if (!$weekstart_date) {
             $warnings .= "Weekstart month-day combination unrecognised {" . $weekstart[1] . "} defaulting to weekday=7 - Sunday<br />";
             $weekstart[1] = 7;
         } else {
             $weekstart[1] = $weekstart_date->format('N');
         }
     }
     if (intval($weekstart[1]) != $weekstart[1] || $weekstart[1] < 1 || $weekstart[1] > 7) {
         $warnings .= "Weekstart unrecognised or out of range {" . $weekstart[1] . "} defaulting to 7 - Sunday<br />";
         $weekstart[1] = 7;
     }
     if (isset($options['weekOneContains']) && $options['weekOneContains'] != "") {
         $weekOne_date = date_create($options['year'] . '-' . $options['weekOneContains']);
         if (!$weekOne_date) {
             $warnings .= "Week one month-day combination unrecognised {" . $options['weekOneContains'] . "} defaulting to Jan-01<br />";
             $weekOne_date = date_create($options['year'] . '-Jan-01');
         }
     } else {
         $weekOne_date = date_create($options['year'] . '-Jan-01');
     }
     $weekOne_date_weekday = $weekOne_date->format('N');
     if ($weekOne_date_weekday > $weekstart[1]) {
         // scan back to start of week
         $weekOne_date->modify('-' . ($weekOne_date_weekday - $weekstart[1]) . ' day');
     } else {
         if ($weekOne_date_weekday < $weekstart[1]) {
             $weekOne_date->modify('-' . (7 + $weekOne_date_weekday - $weekstart[1]) . ' day');
         }
     }
     $firstWeek_date = clone $weekOne_date;
     // date we start providing data for
     $weekOne_date_yearday = $weekOne_date->format('z');
     // day within year note year_start_yearDay is by definition 0
     $weekOne_date_weekday = $weekOne_date->format('N');
     // day within week
     $minWeekNo = $weeknumberfilter[0] != '' ? $weeknumberfilter[0] : 1;
     $numWeeks = ceil($weekOne_date_yearday / 7);
     // number of weeks in year prior to $weekOne_date - 1st Jan gives zero, 2nd-8th Jan gives 1, etc
     if ($minWeekNo - 1 < -1 * $numWeeks) {
         $minWeekNo = -1 * $numWeeks + 1;
     }
     // have to allow for week zero
     if ($minWeekNo < 1) {
         $firstWeek_date->modify(($minWeekNo - 1) * 7 . ' days');
     } else {
         if ($minWeekNo > 1) {
             $firstWeek_date->modify('+' . ($minWeekNo - 1) * 7 . ' days');
         }
     }
     if ($weeknumberfilter[1] != '') {
         $maxWeekNo = $weeknumberfilter[1];
     } else {
         $year_end = date_create($options['year'] . '-Dec-25');
         // don't want to go beyond the end of year: this is 1st Jan minus 1 week: it is the start of the last full week
         $year_end_yearDay = $year_end->format('z');
         // day within year
         $maxWeekNo = 1 + ceil(($year_end_yearDay - $weekOne_date_yearday) / 7);
     }
     $warnings .= '<span style="display:none;">Initial date processing complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     $tableNumberHeaderRow = "";
     $tableDateHeaderRow = "";
     $downloadNumberHeaderRow = "";
     $downloadDateHeaderRow = "";
     $chartNumberLabels = array();
     $chartDateLabels = array();
     $fullDates = array();
     for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
         $tableNumberHeaderRow .= '<th class="week">' . $i . '</th>';
         $tableDateHeaderRow .= '<th class="week">' . $firstWeek_date->format('M') . '<br/>' . $firstWeek_date->format('d') . '</th>';
         $downloadNumberHeaderRow .= ',' . $i;
         $downloadDateHeaderRow .= ',' . $firstWeek_date->format('d/m/Y');
         $chartNumberLabels[] = "" . $i;
         $chartDateLabels[] = $firstWeek_date->format('M-d');
         $fullDates[$i] = $firstWeek_date->format('d/m/Y');
         $firstWeek_date->modify('+7 days');
     }
     $summaryArray = array();
     // this is used for the table output format
     $rawArray = array();
     // this is used for the table output format
     // In order to apply the data combination and estmation processing, we assume that the the records are in taxon, location_id, sample_id order.
     $locationArray = array();
     // this is for a single species at a time.
     $lastLocation = false;
     $seriesLabels = array();
     $lastTaxonID = false;
     $lastSample = false;
     $locationSamples = array();
     $weekList = array();
     $sampleFieldList = !empty($options['sampleFields']) ? explode(',', $options['sampleFields']) : false;
     if (!$sampleFieldList || count($sampleFieldList) == 0) {
         $sampleFields = false;
     } else {
         $sampleFields = array();
         foreach ($sampleFieldList as $sampleField) {
             $parts = explode(':', $sampleField);
             $field = array('caption' => $parts[0], 'field' => $parts[1], 'attr' => false);
             if (count($parts) == 3 && ($parts[1] = 'smpattr')) {
                 $smpAttribute = data_entry_helper::get_population_data(array('table' => 'sample_attribute', 'extraParams' => $options['readAuth'] + array('view' => 'list', 'id' => $parts[2])));
                 if (count($smpAttribute) >= 1) {
                     // may be assigned to more than one survey on this website. This is not relevant to info we want.
                     $field['id'] = $parts[2];
                     $field['attr'] = $smpAttribute[0];
                 }
             }
             $sampleFields[] = $field;
         }
     }
     if ($options['location_list'] != 'all' && count($options['location_list']) == 0) {
         $options['location_list'] = 'none';
     }
     foreach ($records as $recid => $record) {
         // If the taxon has changed
         $this_date = date_create(str_replace('/', '-', $record['date']));
         // prevents day/month ordering issues
         $this_index = $this_date->format('z');
         $this_weekday = $this_date->format('N');
         if ($this_weekday > $weekstart[1]) {
             // scan back to start of week
             $this_date->modify('-' . ($this_weekday - $weekstart[1]) . ' day');
         } else {
             if ($this_weekday < $weekstart[1]) {
                 $this_date->modify('-' . (7 + $this_weekday - $weekstart[1]) . ' day');
             }
         }
         // this_date now points to the start of the week. Next work out the week number.
         $this_yearday = $this_date->format('z');
         $weekno = $record['period_number'];
         if (isset($weekList[$weekno])) {
             if (!in_array($record['location_name'], $weekList[$weekno])) {
                 $weekList[$weekno][] = $record['location_name'];
             }
         } else {
             $weekList[$weekno] = array($record['location_name']);
         }
     }
     $warnings .= '<span style="display:none;">Records date pre-processing complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     $count = count($records);
     $warnings .= '<span style="display:none;">Number of records processed : ' . $count . ' : ' . date(DATE_ATOM) . '</span>' . "\n";
     $summaryArray = array();
     $sortData = array();
     foreach ($records as $idex => $record) {
         $taxonID = $record['taxon_meaning_id'];
         // TODO ??
         if (!empty($record['default_common_name'])) {
             $seriesLabels[$taxonID] = array('label' => $record['default_common_name']);
         }
         // TODO user to be able to choose from taxon or common
         if (empty($seriesLabels[$taxonID])) {
             $seriesLabels[$taxonID] = array('label' => $record['taxon']);
         }
         // various fall backs.
         if (empty($seriesLabels[$taxonID])) {
             $seriesLabels[$taxonID] = array('label' => '[' . $record['taxa_taxon_list_id'] . ']');
         }
         if (!empty($record['preferred_taxon'])) {
             $seriesLabels[$taxonID]['preferred'] = $record['preferred_taxon'];
         }
         $weekno = $record['period_number'];
         $count = $record['count'];
         if (!isset($summaryArray[$taxonID])) {
             $summaryArray[$taxonID] = array();
         }
         $sortData[$taxonID] = array($record['taxonomic_sort_order'], $taxonID);
         if ($weekno >= $minWeekNo && $weekno <= $maxWeekNo) {
             if (!isset($summaryArray[$taxonID][$weekno])) {
                 $summaryArray[$taxonID][$weekno] = array('total' => null, 'estimate' => 0);
             }
             if ($count !== null) {
                 $summaryArray[$taxonID][$weekno]['total'] = ($summaryArray[$taxonID][$weekno]['total'] == null ? 0 : $summaryArray[$taxonID][$weekno]['total']) + $count;
             }
             $summaryArray[$taxonID][$weekno]['estimate'] += $record['estimate'];
         }
     }
     usort($sortData, array('report_helper', 'report_calendar_summary_sort1'));
     $warnings .= '<span style="display:none;">Estimate processing finished : ' . date(DATE_ATOM) . '</span>' . "\n";
     // will storedata in an array[Y][X]
     data_entry_helper::add_resource('jqplot');
     switch ($options['chartType']) {
         case 'bar':
             self::add_resource('jqplot_bar');
             $renderer = '$.jqplot.BarRenderer';
             break;
         case 'pie':
             self::add_resource('jqplot_pie');
             $renderer = '$.jqplot.PieRenderer';
             break;
         default:
             // default is line
             $renderer = '$.jqplot.LineRenderer';
             break;
     }
     self::add_resource('jqplot_category_axis_renderer');
     $opts = array();
     $opts[] = "seriesDefaults:{\n" . (isset($renderer) ? "  renderer:{$renderer},\n" : '') . "  rendererOptions:" . json_encode($options['rendererOptions']) . "}";
     $warnings .= '<span style="display:none;">Controls complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     $seriesToDisplay = isset($options['outputSeries']) ? explode(',', $options['outputSeries']) : 'all';
     $thClass = $options['thClass'];
     $summaryTab = '<div class="results-grid-wrapper-outer"><div class="results-grid-wrapper-inner"><table id="' . $options['tableID'] . '-summary" class="' . $options['tableClass'] . '"><thead class="' . $thClass . '">';
     $estimateTab = '<div class="results-grid-wrapper-outer"><div class="results-grid-wrapper-inner"><table id="' . $options['tableID'] . '-estimate" class="' . $options['tableClass'] . '"><thead class="' . $thClass . '">';
     $summaryDataDownloadGrid = '';
     $estimateDataDownloadGrid = '';
     $rawDataDownloadGrid = '';
     $summaryTab .= '<tr><th class="freeze-first-col">' . lang::get('Week') . '</th>' . $tableNumberHeaderRow . '<th>Total</th></tr>' . '<tr><th class="freeze-first-col">' . lang::get('Date') . '</th>' . $tableDateHeaderRow . '<th></th></tr></thead><tbody>';
     $estimateTab .= '<tr><th class="freeze-first-col">' . lang::get('Week') . '</th>' . $tableNumberHeaderRow . '<th>Total with<br/>estimates</th></tr>' . '<tr><th class="freeze-first-col">' . lang::get('Date') . '</th>' . $tableDateHeaderRow . '<th></th></tr></thead><tbody>';
     $summaryDataDownloadGrid .= 'Week,' . $downloadNumberHeaderRow . ',Total' . "\n" . lang::get('Date') . ',' . $downloadDateHeaderRow . ",\n";
     $estimateDataDownloadGrid .= 'Week,' . $downloadNumberHeaderRow . ',Estimates Total' . "\n" . lang::get('Date') . ',' . $downloadDateHeaderRow . ",\n";
     $altRow = false;
     $grandTotal = 0;
     $totalRow = array();
     $estimatesGrandTotal = 0;
     $totalEstimatesRow = array();
     $seriesIDs = array();
     $summarySeriesData = array();
     $estimatesSeriesData = array();
     $seriesOptions = array();
     for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
         $totalRow[$i] = 0;
         $totalEstimatesRow[$i] = 0;
     }
     foreach ($sortData as $sortedTaxon) {
         $seriesID = $sortedTaxon[1];
         $summaryRow = $summaryArray[$seriesID];
         $summaryValues = array();
         $estimatesValues = array();
         if (!empty($seriesLabels[$seriesID])) {
             $total = 0;
             // row total
             $estimatesTotal = 0;
             // row total
             $summaryTab .= '<tr class="datarow ' . ($altRow ? $options['altRowClass'] : '') . '"><td class="freeze-first-col"' . (isset($seriesLabels[$seriesID]['preferred']) ? ' title="' . $seriesLabels[$seriesID]['preferred'] . '"' : '') . '>' . $seriesLabels[$seriesID]['label'] . '</td>';
             $estimateTab .= '<tr class="datarow ' . ($altRow ? $options['altRowClass'] : '') . '"><td class="freeze-first-col"' . (isset($seriesLabels[$seriesID]['preferred']) ? ' title="' . $seriesLabels[$seriesID]['preferred'] . '"' : '') . '>' . $seriesLabels[$seriesID]['label'] . '</td>';
             $summaryDataDownloadGrid .= '"' . $seriesLabels[$seriesID]['label'] . '","' . (isset($seriesLabels[$seriesID]['preferred']) ? $seriesLabels[$seriesID]['preferred'] : '') . '"';
             $estimateDataDownloadGrid .= '"' . $seriesLabels[$seriesID]['label'] . '","' . (isset($seriesLabels[$seriesID]['preferred']) ? $seriesLabels[$seriesID]['preferred'] : '') . '"';
             for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
                 $summaryDataDownloadGrid .= ',';
                 $estimateDataDownloadGrid .= ',';
                 if (isset($summaryRow[$i])) {
                     $summaryValue = $summaryRow[$i]['total'];
                     $estimateValue = $summaryRow[$i]['estimate'];
                     $class = $summaryValue !== null && $summaryValue === 0 ? 'forcedZero' : '';
                     $estimatesClass = $summaryValue === null || $summaryValue != $estimateValue ? 'highlight-estimates' : '';
                     if ($summaryValue !== null && $summaryValue === 0 && $estimateValue === 0) {
                         $estimatesClass = 'forcedZero';
                     }
                     $summaryDataDownloadGrid .= $summaryValue;
                     $estimateDataDownloadGrid .= $estimateValue;
                     $summaryTab .= '<td class="' . $class . '">' . ($summaryValue !== '' ? $summaryValue : '') . '</td>';
                     $estimateTab .= '<td class="' . $estimatesClass . '">' . $estimateValue . '</td>';
                     if ($summaryValue !== null && $summaryValue !== 0) {
                         $total += $summaryValue;
                         $totalRow[$i] += $summaryValue;
                         // = $summaryTotalRow
                         $grandTotal += $summaryValue;
                         $summaryValues[] = $summaryValue;
                     } else {
                         $summaryValues[] = 0;
                     }
                     $estimatesValues[] = $estimateValue;
                     $estimatesTotal += $estimateValue;
                     $totalEstimatesRow[$i] += $estimateValue;
                     // = $estimatesTotalRow
                     $estimatesGrandTotal += $estimateValue;
                 } else {
                     $summaryTab .= '<td></td>';
                     $estimateTab .= '<td></td>';
                     $summaryValues[] = 0;
                     $estimatesValues[] = 0;
                 }
             }
             if ($options['includeChartItemSeries']) {
                 $seriesIDs[] = $seriesID;
                 $summarySeriesData[] = '[' . implode(',', $summaryValues) . ']';
                 $estimatesSeriesData[] = '[' . implode(',', $estimatesValues) . ']';
                 $seriesOptions[] = '{"show":' . ($seriesToDisplay == 'all' || in_array($seriesID, $seriesToDisplay) ? 'true' : 'false') . ',"label":"' . $seriesLabels[$seriesID]['label'] . '","showlabel":true}';
             }
             $summaryTab .= '<td class="total-column">' . $total . '</td></tr>';
             $summaryDataDownloadGrid .= ',' . $total . "\n";
             $estimateTab .= '<td class="total-column estimates">' . $estimatesTotal . '</td></tr>';
             $estimateDataDownloadGrid .= ',' . $estimatesTotal . "\n";
             $altRow = !$altRow;
         }
     }
     if (isset($options['includeChartTotalSeries']) && $options['includeChartTotalSeries']) {
         // totals are put at the start
         array_unshift($seriesIDs, 0);
         // Total has ID 0
         array_unshift($summarySeriesData, '[' . implode(',', $totalRow) . ']');
         array_unshift($estimatesSeriesData, '[' . implode(',', $totalEstimatesRow) . ']');
         array_unshift($seriesOptions, '{"show":' . ($seriesToDisplay == 'all' || in_array(0, $seriesToDisplay) ? 'true' : 'false') . ',"label":"' . lang::get('Total') . '","showlabel":true}');
     }
     $opts[] = 'series:[' . implode(',', $seriesOptions) . ']';
     $options['axesOptions']['xaxis']['renderer'] = '$.jqplot.CategoryAxisRenderer';
     if (isset($options['chartLabels']) && $options['chartLabels'] == 'number') {
         $options['axesOptions']['xaxis']['ticks'] = $chartNumberLabels;
     } else {
         $options['axesOptions']['xaxis']['ticks'] = $chartDateLabels;
     }
     // We need to fudge the json so the renderer class is not a string
     $axesOpts = str_replace('"$.jqplot.CategoryAxisRenderer"', '$.jqplot.CategoryAxisRenderer', 'axes:' . json_encode($options['axesOptions']));
     $opts[] = $axesOpts;
     $summaryTab .= "<tr class=\"totalrow\"><td class=\"freeze-first-col\">" . lang::get('Total (Summary)') . '</td>';
     $estimateTab .= "<tr class=\"totalrow estimates\"><td class=\"freeze-first-col\">" . lang::get('Total inc Estimates') . '</td>';
     $summaryDataDownloadGrid .= '"' . lang::get('Total (Summary)') . '",';
     $estimateDataDownloadGrid .= '"' . lang::get('Total') . '",';
     for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
         $summaryTab .= '<td>' . $totalRow[$i] . '</td>';
         $estimateTab .= '<td>' . $totalEstimatesRow[$i] . '</td>';
         $estimateDataDownloadGrid .= ',' . $totalEstimatesRow[$i];
         $summaryDataDownloadGrid .= ',' . $totalRow[$i];
     }
     $summaryTab .= '<td class="total-column grand-total">' . $grandTotal . '</td></tr>';
     $summaryDataDownloadGrid .= ',' . $grandTotal . "\n";
     $estimateTab .= '<td class="total-column grand-total estimates">' . $estimatesGrandTotal . '</td></tr>';
     $estimateDataDownloadGrid .= ',' . $estimatesGrandTotal . "\n";
     $summaryTab .= "</tbody></table></div></div>\n";
     $estimateTab .= "</tbody></table></div></div>\n";
     data_entry_helper::$javascript .= "var seriesData = {ids: [" . implode(',', $seriesIDs) . "], summary: [" . implode(',', $summarySeriesData) . "], estimates: [" . implode(',', $estimatesSeriesData) . "]};\n";
     data_entry_helper::$javascript .= "\r\nfunction replot(type){\r\n  // there are problems with the coloring of series when added to a plot: easiest just to completely redraw.\r\n  var max=0;\r\n  \$('#" . $options['chartID'] . "-'+type).empty();\n" . (!isset($options['width']) || $options['width'] == '' ? "  jQuery('#" . $options['chartID'] . "-'+type).width(jQuery('#" . $options['chartID'] . "-'+type).width());\n" : '') . "  var opts = {" . implode(",\n", $opts) . "};\r\n  // copy series from checkboxes.\r\n  \$('#" . $options['chartID'] . "-'+type).parent().find('[name=" . $options['chartID'] . "-series]').each(function(idx, elem){\r\n      opts.series[idx].show = (jQuery(elem).filter('[checked]').length > 0);\r\n  });\r\n  for(var i=0; i<seriesData[type].length; i++)\r\n    if(opts.series[i].show)\r\n      for(var j=0; j<seriesData[type][i].length; j++)\r\n          max=(max>seriesData[type][i][j]?max:seriesData[type][i][j]);\r\n  opts.axes.yaxis.max=max+1;\r\n  opts.axes.yaxis.tickInterval = Math.floor(max/15); // number of ticks - too many takes too long to display\r\n  if(!opts.axes.yaxis.tickInterval) opts.axes.yaxis.tickInterval=1;\r\n  \$('.legend-colours').remove();\r\n  if(\$('#" . $options['chartID'] . "-'+type).parent().find('[name=" . $options['chartID'] . "-series]').filter('[checked]').length == 0) return;\r\n  var plot = \$.jqplot('" . $options['chartID'] . "-'+type, seriesData[type], opts);\r\n  for(var i=0; i<plot.series.length; i++){\r\n  \tif(plot.series[i].show==true) {\r\n\t    var elem = \$('#" . $options['chartID'] . "-'+type).parent().find('[name=" . $options['chartID'] . "-series]').eq(i);\r\n    \telem.after('<div class=\"legend-colours\"><div class=\"legend-colours-inner\" style=\"background:'+plot.series[i].color+';\">&nbsp;</div></div>');\r\n\t}\r\n  }\r\n};\r\nindiciaFns.bindTabsActivate(\$('#controls'), function(event, ui) {\r\n  panel = typeof ui.newPanel==='undefined' ? ui.panel : ui.newPanel[0];\r\n  if (panel.id==='summaryChart') { replot('summary'); }\r\n  if (panel.id==='estimateChart') { replot('estimates'); }\r\n  if (panel.id==='summaryData' || panel.id==='estimateData' || panel.id==='rawData') {\r\n  \tvar max=0;\r\n  \tvar extMax=0;\r\n  \t\$('#'+panel.id+' .freeze-first-col').each(function(idx, elem){\r\n  \t  \$(elem).css('width',''); \r\n  \t  if(max < \$(elem).width()) max= \$(elem).width();\r\n  \t  if(extMax < \$(elem).outerWidth(true)) extMax= \$(elem).outerWidth(true);});\r\n  \t\$('#'+panel.id+' .freeze-first-col').width(max+1);\r\n  \t\$('#'+panel.id+' .results-grid-wrapper-inner').css('margin-left',extMax+1);\r\n  \t\$('#'+panel.id+' table').hide();\r\n  \t\$('#'+panel.id+' > div.results-grid-wrapper-outer').each(function(idx, elem){ \$(elem).css('width',''); \$(elem).width(\$(elem).width());});\r\n  \t\$('#'+panel.id+' table').show();\r\n  }\r\n});\r\n";
     $summarySeriesPanel = "";
     if (isset($options['disableableSeries']) && $options['disableableSeries'] && count($summaryArray) > (isset($options['includeChartTotalSeries']) && $options['includeChartTotalSeries'] ? 0 : 1) && isset($options['includeChartItemSeries']) && $options['includeChartItemSeries']) {
         $class = 'series-fieldset';
         if (function_exists('hostsite_add_library') && (!defined('DRUPAL_CORE_COMPATIBILITY') || DRUPAL_CORE_COMPATIBILITY !== '7.x')) {
             hostsite_add_library('collapse');
             $class .= ' collapsible collapsed';
         }
         $summarySeriesPanel .= '<fieldset id="' . $options['chartID'] . '-series" class="' . $class . '"><legend>' . lang::get('Display Series') . "</legend><span>\n";
         $summarySeriesPanel .= '<input type="button" class="disable-button" value="' . lang::get('Hide all ') . $options['rowGroupColumn'] . "\"/>\n";
         $idx = 0;
         if (isset($options['includeChartTotalSeries']) && $options['includeChartTotalSeries']) {
             // use series ID = 0 for Total
             $summarySeriesPanel .= '<span class="chart-series-span"><input type="checkbox" checked="checked" id="' . $options['chartID'] . '-series-' . $idx . '" name="' . $options['chartID'] . '-series" value="' . $idx . '"/><label for="' . $options['chartID'] . '-series-' . $idx . '">' . lang::get('Total') . "</label></span>\n";
             $idx++;
             data_entry_helper::$javascript .= "\njQuery('[name=" . $options['chartID'] . "-series]').filter('[value=0]')." . ($seriesToDisplay == 'all' || in_array(0, $seriesToDisplay) ? 'attr("checked","checked");' : 'removeAttr("checked");');
         }
         foreach ($sortData as $sortedTaxon) {
             $seriesID = $sortedTaxon[1];
             $summaryRow = $summaryArray[$seriesID];
             $summarySeriesPanel .= '<span class="chart-series-span"><input type="checkbox" checked="checked" id="' . $options['chartID'] . '-series-' . $idx . '" name="' . $options['chartID'] . '-series" value="' . $seriesID . '"/><label for="' . $options['chartID'] . '-series-' . $idx . '"' . (isset($seriesLabels[$seriesID]['preferred']) ? ' title="' . $seriesLabels[$seriesID]['preferred'] . '"' : '') . '>' . $seriesLabels[$seriesID]['label'] . "</label></span>\n";
             $idx++;
             data_entry_helper::$javascript .= "\njQuery('[name=" . $options['chartID'] . "-series]').filter('[value=" . $seriesID . "]')." . ($seriesToDisplay == 'all' || in_array($seriesID, $seriesToDisplay) ? 'attr("checked","checked");' : 'removeAttr("checked");');
         }
         $summarySeriesPanel .= "</span></fieldset>\n";
         // Known issue: jqplot considers the min and max of all series when drawing on the screen, even those which are not displayed
         // so replotting doesn't scale to the displayed series!
         // Note we are keeping the 2 charts in sync.
         data_entry_helper::$javascript .= "\r\njQuery('#summaryChart [name=" . $options['chartID'] . "-series]').change(function(){\r\n  \$('#estimateChart [name=" . $options['chartID'] . "-series]').filter('[value='+\$(this).val()+']').attr('checked',\$(this).attr('checked'));\r\n  replot('summary');\r\n});\r\njQuery('#estimateChart [name=" . $options['chartID'] . "-series]').change(function(){\r\n  \$('#summaryChart [name=" . $options['chartID'] . "-series]').filter('[value='+\$(this).val()+']').attr('checked',\$(this).attr('checked'));\r\n  replot('estimates');\r\n});\r\njQuery('#summaryChart .disable-button').click(function(){\r\n  if(jQuery(this).is('.cleared')){ // button is to show all\r\n    jQuery('[name=" . $options['chartID'] . "-series]').not('[value=0]').attr('checked','checked');\r\n    jQuery('.disable-button').removeClass('cleared').val(\"" . lang::get('Hide all ') . $options['rowGroupColumn'] . "\");\r\n  } else {\r\n    jQuery('[name=" . $options['chartID'] . "-series]').not('[value=0]').removeAttr('checked');\r\n    jQuery('.disable-button').addClass('cleared').val(\"" . lang::get('Show all ') . $options['rowGroupColumn'] . "\");\r\n  }\r\n  replot('summary');\r\n});\r\njQuery('#estimateChart .disable-button').click(function(){\r\n  if(jQuery(this).is('.cleared')){ // button is to show all\r\n    jQuery('[name=" . $options['chartID'] . "-series]').not('[value=0]').attr('checked','checked');\r\n    jQuery('.disable-button').removeClass('cleared').val(\"" . lang::get('Hide all ') . $options['rowGroupColumn'] . "\");\r\n  } else {\r\n    jQuery('[name=" . $options['chartID'] . "-series]').not('[value=0]').removeAttr('checked');\r\n    jQuery('.disable-button').addClass('cleared').val(\"" . lang::get('Show all ') . $options['rowGroupColumn'] . "\");\r\n  }\r\n  replot('estimates');\r\n});\r\n";
     }
     $hasRawData = false;
     if (isset($options['location_id']) && $options['location_id'] != "") {
         // get the raw data for a single location.
         $options['extraParams']['orderby'] = 'date';
         self::request_report($response, $options, $currentParamValues, false, '');
         if (isset($response['error'])) {
             $rawTab = "ERROR RETURNED FROM request_report:<br />" . print_r($response, true);
         } else {
             if (isset($response['parameterRequest'])) {
                 // We're not even going to bother with asking the user to populate a partially filled in report parameter set.
                 $rawTab = '<p>Internal Error: Report request parameters not set up correctly.<br />' . print_r($response, true) . '<p>';
             } else {
                 // convert records to a date based array so it can be used when generating the grid.
                 $altRow = false;
                 $records = $response['records'];
                 $rawTab = isset($options['linkMessage']) ? $options['linkMessage'] : '';
                 $rawDataDownloadGrid = lang::get('Date') . ',';
                 $rawArray = array();
                 $sampleList = array();
                 $sampleDateList = array();
                 $smpIdx = 0;
                 $hasRawData = count($records) > 0;
                 if (!$hasRawData) {
                     $rawTab .= '<p>' . lang::get('No raw data available for this period with these filter values.') . '</p>';
                 } else {
                     $rawTab = '<div class="results-grid-wrapper-outer"><div class="results-grid-wrapper-inner"><table class="' . $options['tableClass'] . '"><thead class="' . $thClass . '"><tr><th class="freeze-first-col">' . lang::get('Date') . '</th>';
                     foreach ($records as $occurrence) {
                         if (!in_array($occurrence['sample_id'], $sampleList)) {
                             $sampleList[] = $occurrence['sample_id'];
                             $sampleData = array('id' => $occurrence['sample_id'], 'date' => $occurrence['date'], 'location' => $occurrence['locaton_name']);
                             $rawArray[$occurrence['sample_id']] = array();
                             if ($sampleFields) {
                                 foreach ($sampleFields as $sampleField) {
                                     if ($sampleField['attr'] === false) {
                                         $sampleData[$sampleField['caption']] = $occurrence[$sampleField['field']];
                                     } else {
                                         if ($sampleField['attr']['data_type'] == 'L') {
                                             $sampleData[$sampleField['caption']] = $occurrence['attr_sample_term_' . $sampleField['id']];
                                         } else {
                                             $sampleData[$sampleField['caption']] = $occurrence['attr_sample_' . $sampleField['id']];
                                         }
                                     }
                                 }
                             }
                             $sampleDateList[] = $sampleData;
                         }
                         if ($occurrence['taxon_meaning_id'] !== null && $occurrence['taxon_meaning_id'] != '') {
                             $count = isset($options['countColumn']) && $options['countColumn'] != '' ? isset($occurrence[$options['countColumn']]) ? $occurrence[$options['countColumn']] : 0 : 1;
                             if (!isset($rawArray[$occurrence['sample_id']][$occurrence['taxon_meaning_id']])) {
                                 $rawArray[$occurrence['sample_id']][$occurrence['taxon_meaning_id']] = $count;
                             } else {
                                 $rawArray[$occurrence['sample_id']][$occurrence['taxon_meaning_id']] += $count;
                             }
                         }
                     }
                     foreach ($sampleDateList as $sample) {
                         $sample_date = date_create($sample['date']);
                         $rawTab .= '<th>' . (isset($options['linkURL']) && $options['linkURL'] != '' ? '<a href="' . $options['linkURL'] . $sample['id'] . '" target="_blank" title="Link to data entry form for ' . $sample['location'] . ' on ' . $sample['date'] . ' (Sample ID ' . $sample['id'] . ')">' : '') . $sample_date->format('M') . '<br/>' . $sample_date->format('d') . (isset($options['linkURL']) && $options['linkURL'] != '' ? '</a>' : '') . '</th>';
                         $rawDataDownloadGrid .= ',' . $sample['date'];
                     }
                     $rawDataDownloadGrid .= "\n";
                     $rawTab .= '</tr></thead><tbody>';
                     if ($sampleFields) {
                         foreach ($sampleFields as $sampleField) {
                             // last-sample-datarow
                             $rawTab .= '<tr class="sample-datarow ' . ($altRow ? $options['altRowClass'] : '') . '"><td class="freeze-first-col">' . $sampleField['caption'] . '</td>';
                             $rawDataDownloadGrid .= '"' . $sampleField['caption'] . '",';
                             foreach ($sampleDateList as $sample) {
                                 $rawTab .= '<td>' . ($sample[$sampleField['caption']] === null || $sample[$sampleField['caption']] == '' ? '&nbsp;' : $sample[$sampleField['caption']]) . '</td>';
                                 $rawDataDownloadGrid .= ',' . $sample[$sampleField['caption']];
                             }
                             $rawTab .= '</tr>';
                             $rawDataDownloadGrid .= "\n";
                             $altRow = !$altRow;
                         }
                         data_entry_helper::$javascript .= "var sampleDatarows = \$('#rawData .sample-datarow').length;\n\$('#rawData .sample-datarow').eq(sampleDatarows-1).addClass('last-sample-datarow');\n";
                     }
                     foreach ($sortData as $sortedTaxon) {
                         $seriesID = $sortedTaxon[1];
                         // this is the meaning id
                         if (!empty($seriesLabels[$seriesID])) {
                             $rawTab .= '<tr class="datarow ' . ($altRow ? $options['altRowClass'] : '') . '"><td class="freeze-first-col"' . (isset($seriesLabels[$seriesID]['preferred']) ? ' title="' . $seriesLabels[$seriesID]['preferred'] . '"' : '') . '>' . $seriesLabels[$seriesID]['label'] . '</td>';
                             $rawDataDownloadGrid .= '"' . $seriesLabels[$seriesID]['label'] . '","' . (isset($seriesLabels[$seriesID]['preferred']) ? $seriesLabels[$seriesID]['preferred'] : '') . '",';
                             foreach ($sampleList as $sampleID) {
                                 $rawTab .= '<td>' . (isset($rawArray[$sampleID][$seriesID]) ? $rawArray[$sampleID][$seriesID] : '&nbsp;') . '</td>';
                                 $rawDataDownloadGrid .= ',' . (isset($rawArray[$sampleID][$seriesID]) ? $rawArray[$sampleID][$seriesID] : '');
                             }
                             $rawTab .= '</tr>';
                             $rawDataDownloadGrid .= "\n";
                             $altRow = !$altRow;
                         }
                     }
                     $rawTab .= '</tbody></table></div></div>';
                 }
             }
         }
     } else {
         $rawTab = "<p>Raw Data is only available when a location is specified.</p>";
     }
     $hasData = count($summaryArray) > 0;
     $tabs = array('#summaryData' => lang::get('Summary Table'));
     if ($hasData) {
         $tabs = array_merge($tabs, array('#summaryChart' => lang::get('Summary Chart'), '#estimateData' => lang::get('Estimate Table'), '#estimateChart' => lang::get('Estimate Chart')));
     }
     $tabs['#rawData'] = lang::get('Raw Data');
     $downloadTab = "";
     $timestamp = isset($options['includeReportTimeStamp']) && $options['includeReportTimeStamp'] ? '_' . date('YmdHis') : '';
     unset($options['extraParams']['orderby']);
     // may have been set for raw data
     // No need for saved reports to be atomic events. Will be purged automatically.
     global $base_url;
     $cacheFolder = data_entry_helper::$cache_folder ? data_entry_helper::$cache_folder : data_entry_helper::relative_client_helper_path() . 'cache/';
     if ($hasData && $options['includeSummaryGridDownload']) {
         $cacheFile = $options['downloadFilePrefix'] . 'summaryDataGrid' . $timestamp . '.csv';
         $handle = fopen($cacheFolder . $cacheFile, 'wb');
         fwrite($handle, $summaryDataDownloadGrid);
         fclose($handle);
         $downloadTab .= '<tr><td>' . lang::get('Download Summary Grid (CSV Format)') . ' : </td><td><a target="_blank" href="' . $base_url . '/' . drupal_get_path('module', 'iform') . '/client_helpers/cache/' . $cacheFile . '" download type="text/csv"><button type="button">' . lang::get('Download') . '</button></a></td></tr>' . "\n";
     }
     if ($hasData && $options['includeEstimatesGridDownload']) {
         $cacheFile = $options['downloadFilePrefix'] . 'estimateDataGrid' . $timestamp . '.csv';
         $handle = fopen($cacheFolder . $cacheFile, 'wb');
         fwrite($handle, $estimateDataDownloadGrid);
         fclose($handle);
         $downloadTab .= '<tr><td>' . lang::get('Download Estimates Grid (CSV Format)') . ' : </td><td><a target="_blank" href="' . $base_url . '/' . drupal_get_path('module', 'iform') . '/client_helpers/cache/' . $cacheFile . '" download type="text/csv"><button type="button">' . lang::get('Download') . '</button></a></td></tr>' . "\n";
     }
     if ($hasRawData && $options['includeRawGridDownload']) {
         $cacheFile = $options['downloadFilePrefix'] . 'rawDataGrid' . $timestamp . '.csv';
         $handle = fopen($cacheFolder . $cacheFile, 'wb');
         fwrite($handle, $rawDataDownloadGrid);
         fclose($handle);
         $downloadTab .= '<tr><td>' . lang::get('Download Raw Data Grid (CSV Format)') . ' : </td><td><a target="_blank" href="' . $base_url . '/' . drupal_get_path('module', 'iform') . '/client_helpers/cache/' . $cacheFile . '" download type="text/csv"><button type="button">' . lang::get('Download') . '</button></a></td></tr>' . "\n";
     }
     if ($hasData && count($options['downloads']) > 0) {
         // format is assumed to be CSV
         global $indicia_templates;
         $indicia_templates['report_download_link'] = '<a target="_blank" href="{link}" download ><button type="button">' . lang::get('Download') . '</button></a>';
         $downloadOptions = array('readAuth' => $options['readAuth'], 'extraParams' => array_merge($options['extraParams'], array('date_from' => $options['date_start'], 'date_to' => $options['date_end'])), 'itemsPerPage' => false);
         // there are problems dealing with location_list as an array if empty, so connvert
         if ($downloadOptions['extraParams']['location_list'] == "") {
             $downloadOptions['extraParams']['location_list'] = "(-1)";
         } else {
             $downloadOptions['extraParams']['location_list'] = '(' . $downloadOptions['extraParams']['location_list'] . ')';
         }
         foreach ($options['downloads'] as $download) {
             $downloadOptions['dataSource'] = $download['dataSource'];
             $downloadOptions['filename'] = $download['filename'];
             $downloadTab .= '<tr><td>' . $download['caption'] . ' : </td><td>' . report_helper::report_download_link($downloadOptions) . '</td></tr>';
         }
     }
     if ($downloadTab != "") {
         $tabs['#dataDownloads'] = lang::get('Downloads');
     }
     $r .= '<div id="controls">' . data_entry_helper::enable_tabs(array('divId' => 'controls')) . data_entry_helper::tab_header(array('tabs' => $tabs)) . ($hasData ? '<div id="summaryData">' . $summaryTab . '</div>' . '<div id="summaryChart"><div id="' . $options['chartID'] . '-summary" style="height:' . $options['height'] . 'px;' . (isset($options['width']) && $options['width'] != '' ? 'width:' . $options['width'] . 'px;' : '') . '"></div>' . $summarySeriesPanel . '</div>' . '<div id="estimateData">' . $estimateTab . '</div>' . '<div id="estimateChart"><div id="' . $options['chartID'] . '-estimates" style="height:' . $options['height'] . 'px;' . (isset($options['width']) && $options['width'] != '' ? 'width:' . $options['width'] . 'px;' : '') . '"></div>' . $summarySeriesPanel . '</div>' : '<div id="summaryData"><p>' . lang::get('No data available for this period with these filter values.') . '</p></div>') . '<div id="rawData">' . $rawTab . '</div>' . ($downloadTab != "" ? '<div id="dataDownloads"><table><tbody style="border:none;">' . $downloadTab . '</tbody></table></div>' : '') . '</div>';
     $warnings .= '<span style="display:none;">Finish report_calendar_summary : ' . date(DATE_ATOM) . '</span>' . "\n";
     return $warnings . $r;
 }