Example #1
0
/**
 * Retreives the options array required to set up a report according to the default
 * report parameters.
 * @global <type> $indicia_templates
 * @param string $args
 * @param <type> $readAuth
 * @return string
 */
function iform_report_get_report_options($args, $readAuth)
{
    // handle auto_params_form for backwards compatibility
    if (empty($args['output']) && !empty($args['auto_params_form'])) {
        if (!$args['auto_params_form']) {
            $args['output'] = 'output';
        }
    }
    if (isset($args['map_toolbar_pos']) && $args['map_toolbar_pos'] == 'map') {
        // report params cannot go in the map toolbar if displayed as overlay on map
        $args['params_in_map_toolbar'] = false;
    }
    $r = '';
    require_once 'user.php';
    $presets = get_options_array_with_user_data($args['param_presets']);
    $defaults = get_options_array_with_user_data($args['param_defaults']);
    $ignores = isset($args['param_ignores']) ? helper_base::explode_lines($args['param_ignores']) : array();
    // default columns behaviour is to just include anything returned by the report
    $columns = array();
    // this can be overridden
    if (isset($args['columns_config']) && !empty($args['columns_config'])) {
        $columns = json_decode($args['columns_config'], true);
    }
    $reportOptions = array('id' => 'report-grid', 'reportGroup' => isset($args['report_group']) ? $args['report_group'] : '', 'rememberParamsReportGroup' => isset($args['remember_params_report_group']) ? $args['remember_params_report_group'] : '', 'dataSource' => $args['report_name'], 'mode' => 'report', 'readAuth' => $readAuth, 'columns' => $columns, 'itemsPerPage' => empty($args['items_per_page']) ? 20 : $args['items_per_page'], 'extraParams' => $presets, 'paramDefaults' => $defaults, 'ignoreParams' => $ignores, 'galleryColCount' => isset($args['gallery_col_count']) ? $args['gallery_col_count'] : 1, 'headers' => isset($args['gallery_col_count']) && $args['gallery_col_count'] > 1 ? false : true, 'paramsInMapToolbar' => isset($args['params_in_map_toolbar']) ? $args['params_in_map_toolbar'] : false);
    // put each param control in a div, which makes it easier to layout with CSS
    if (!isset($args['params_in_map_toolbar']) || !$args['params_in_map_toolbar']) {
        $reportOptions['paramPrefix'] = '<div id="container-{fieldname}" class="param-container">';
        $reportOptions['paramSuffix'] = '</div>';
    }
    // If in Drupal, allow the params panel to collapse.
    if (function_exists('drupal_add_js')) {
        if (function_exists('hostsite_add_library') && (!defined('DRUPAL_CORE_COMPATIBILITY') || DRUPAL_CORE_COMPATIBILITY !== '7.x')) {
            hostsite_add_library('collapse');
            $reportOptions['fieldsetClass'] = 'collapsible';
        }
    }
    if (empty($args['output']) || $args['output'] == 'default') {
        $reportOptions['autoParamsForm'] = true;
    } elseif ($args['output'] == 'form') {
        $reportOptions['autoParamsForm'] = true;
        $reportOptions['paramsOnly'] = true;
    } else {
        $reportOptions['autoParamsForm'] = false;
    }
    if (!empty($args['row_class'])) {
        $reportOptions['rowClass'] = $args['row_class'];
    }
    // Set up a page refresh for dynamic update of the report at set intervals
    if (isset($args['refresh_timer']) && $args['refresh_timer'] !== 0 && is_numeric($args['refresh_timer'])) {
        // is_numeric prevents injection
        if (isset($args['load_on_refresh']) && !empty($args['load_on_refresh'])) {
            report_helper::$javascript .= "setTimeout('window.location=\"" . $args['load_on_refresh'] . "\";', " . $args['refresh_timer'] . "*1000 );\n";
        } else {
            report_helper::$javascript .= "setTimeout('window.location.reload( false );', " . $args['refresh_timer'] . "*1000 );\n";
        }
    }
    return $reportOptions;
}
 /**
  * Generates the parameters form required for configuring a prebuilt form.
  * Fieldsets are given classes which define that they are collapsible and normally initially
  * collapsed, though the css for handling this must be defined elsewhere. For Drupal usage this
  * css is normally handled by default in the template.
  * @param array $options Options array with the following possibilities:<ul>
  * <li><b>form</b>
  * Name of the form file without the .php extension, e.g. mnhnl_dynamic_1.</li>
  * <li><b>currentSettings</b>
  * Associative array of default values to load into the form controls.</li>
  * <li><b>expandFirst</b>
  * Optional. If set to true, then the first fieldset on the form is initially expanded.</li>
  * <li><b>siteSpecific</b>
  * Optional. Defaults to false. If true then only parameters marked as specific to a site
  * are loaded. Used to provide a reduced version of the params form after migrating a
  * form between sites (e.g. when installing a Drupal feature).</li>
  * </ul>
  */
 public static function prebuilt_form_params_form($options)
 {
     if (function_exists('hostsite_add_library') && (!defined('DRUPAL_CORE_COMPATIBILITY') || DRUPAL_CORE_COMPATIBILITY !== '7.x')) {
         hostsite_add_library('collapse');
     }
     require_once 'data_entry_helper.php';
     // temporarily disable caching because performance is not as important as reflecting
     // the latest available parameters, surveys etc. in the drop downs
     $oldnocache = self::$nocache;
     if (!isset($options['siteSpecific'])) {
         $options['siteSpecific'] = false;
     }
     self::$nocache = true;
     $formparams = self::get_form_parameters($options['form']);
     $fieldsets = array();
     $r = '';
     foreach ($formparams as $control) {
         // skip hidden controls or non-site specific controls when displaying the reduced site specific
         // version of the form
         if (isset($control['visible']) && !$control['visible'] || $options['siteSpecific'] && !(isset($control['siteSpecific']) && $control['siteSpecific'])) {
             continue;
         }
         $fieldset = isset($control['group']) ? $control['group'] : 'Other IForm Parameters';
         // apply default options to the control
         $ctrlOptions = array_merge(array('id' => $control['fieldname'], 'sep' => '<br/>', 'class' => '', 'blankText' => '<' . lang::get('please select') . '>', 'extraParams' => array(), 'readAuth' => $options['readAuth']), $control);
         $type = self::map_type($control);
         // current form settings will overwrite the default
         if (isset($options['currentSettings']) && isset($options['currentSettings'][$control['fieldname']])) {
             $ctrlOptions['default'] = $options['currentSettings'][$control['fieldname']];
         }
         $ctrlOptions['extraParams'] = array_merge($ctrlOptions['extraParams'], $options['readAuth']);
         // standardise the control width unless specified already in the control options
         if (strpos($ctrlOptions['class'], 'control-width') == false && $type != 'checkbox' && $type != 'report_helper::report_picker') {
             $ctrlOptions['class'] .= ' control-width-6';
         }
         if (!isset($fieldsets[$fieldset])) {
             $fieldsets[$fieldset] = '';
         }
         // form controls can specify the report helper class
         if (substr($type, 0, 15) == 'report_helper::') {
             $type = substr($type, 15);
             require_once 'report_helper.php';
             $fieldsets[$fieldset] .= report_helper::$type($ctrlOptions);
         } else {
             $fieldsets[$fieldset] .= data_entry_helper::$type($ctrlOptions);
         }
     }
     $class = isset($options['expandFirst']) && $options['expandFirst'] ? 'collapsible' : 'collapsible collapsed';
     foreach ($fieldsets as $fieldset => $content) {
         // Drupal 7 collapsible fieldsets broken, see http://drupal.org/node/1607822
         // so we remove the class
         if (defined('DRUPAL_CORE_COMPATIBILITY') && DRUPAL_CORE_COMPATIBILITY === '7.x') {
             $class = '';
         }
         $r .= "<fieldset class=\"{$class}\"><legend>{$fieldset}</legend>\n";
         $r .= $fieldsets[$fieldset];
         $r .= "\n</fieldset>\n";
         // any subsequent fieldset should be collapsed
         if (isset($options['expandFirst']) && $options['expandFirst']) {
             $class .= ' collapsed';
         }
     }
     self::$nocache = $oldnocache;
     return $r;
 }
Example #3
0
/**
 * Retreives the options array required to set up a report according to the default
 * report parameters.
 * @global <type> $indicia_templates
 * @param string $args
 * @param <type> $readAuth
 * @return string
 */
function iform_report_get_report_options($args, $readAuth)
{
    // handle auto_params_form for backwards compatibility
    if (empty($args['output']) && !empty($args['auto_params_form'])) {
        if (!$args['auto_params_form']) {
            $args['output'] = 'output';
        }
    }
    if (isset($args['map_toolbar_pos']) && $args['map_toolbar_pos'] == 'map') {
        // report params cannot go in the map toolbar if displayed as overlay on map
        $args['params_in_map_toolbar'] = false;
    }
    $r = '';
    require_once 'user.php';
    $presets = get_options_array_with_user_data($args['param_presets']);
    $defaults = get_options_array_with_user_data($args['param_defaults']);
    $ignores = isset($args['param_ignores']) ? helper_base::explode_lines($args['param_ignores']) : array();
    $param_lookup_extras = array();
    if (isset($args['param_lookup_extras'])) {
        $paramlx = helper_base::explode_lines($args['param_lookup_extras']);
        foreach ($paramlx as $param) {
            if (!empty($param)) {
                $tokens = explode(':', $param, 2);
                if (count($tokens) == 2) {
                    $tokens2 = explode('=', $tokens[1], 2);
                    if (count($tokens2) == 2) {
                        if (!isset($param_lookup_extras[$tokens[0]])) {
                            $param_lookup_extras[$tokens[0]] = array();
                        }
                        $param_lookup_extras[$tokens[0]][$tokens2[0]] = explode(',', $tokens2[1]);
                    } else {
                        throw new Exception('One of the param_lookup_extras defined for this page are not of the form key:param=value[,value...] : ' . $param . '. (No equals)');
                    }
                } else {
                    throw new Exception('One of the param_lookup_extras defined for this page are not of the form key:param=value[,value...] : ' . $param . '. (No colon)');
                }
            }
        }
    } else {
        $param_lookup_extras = array();
    }
    // default columns behaviour is to just include anything returned by the report
    $columns = array();
    // this can be overridden
    if (isset($args['columns_config']) && !empty($args['columns_config'])) {
        $columns = json_decode($args['columns_config'], true);
    }
    // do the form arguments request that certain columns are globally skipped?
    if (!empty($args['skipped_report_columns'])) {
        // look for configured columns that should be skipped
        foreach ($columns as &$column) {
            if (array_key_exists($column['fieldname'], $args['skipped_report_columns'])) {
                $column['visible'] = false;
                unset($args['skipped_report_columns']);
            }
        }
        // add configurations to hide any remaining columns that should be skipped
        foreach ($args['skipped_report_columns'] as $fieldname) {
            $columns[] = array('fieldname' => $fieldname, 'visible' => false);
        }
    }
    $reportOptions = array('id' => 'report-grid', 'reportGroup' => isset($args['report_group']) ? $args['report_group'] : '', 'rememberParamsReportGroup' => isset($args['remember_params_report_group']) ? $args['remember_params_report_group'] : '', 'dataSource' => isset($args['report_name']) ? $args['report_name'] : '', 'mode' => 'report', 'readAuth' => $readAuth, 'columns' => $columns, 'itemsPerPage' => empty($args['items_per_page']) ? 20 : $args['items_per_page'], 'extraParams' => $presets, 'paramDefaults' => $defaults, 'ignoreParams' => $ignores, 'param_lookup_extras' => $param_lookup_extras, 'galleryColCount' => isset($args['gallery_col_count']) ? $args['gallery_col_count'] : 1, 'headers' => isset($args['gallery_col_count']) && $args['gallery_col_count'] > 1 ? false : true, 'paramsInMapToolbar' => isset($args['params_in_map_toolbar']) ? $args['params_in_map_toolbar'] : false);
    // put each param control in a div, which makes it easier to layout with CSS
    if (!isset($args['params_in_map_toolbar']) || !$args['params_in_map_toolbar']) {
        $reportOptions['paramPrefix'] = '<div id="container-{fieldname}" class="param-container">';
        $reportOptions['paramSuffix'] = '</div>';
    }
    // If in Drupal, allow the params panel to collapse.
    if (function_exists('drupal_add_js')) {
        if (function_exists('hostsite_add_library') && (!defined('DRUPAL_CORE_COMPATIBILITY') || DRUPAL_CORE_COMPATIBILITY !== '7.x')) {
            hostsite_add_library('collapse');
            $reportOptions['fieldsetClass'] = 'collapsible';
        }
    }
    if (empty($args['output']) || $args['output'] == 'default') {
        $reportOptions['autoParamsForm'] = true;
    } elseif ($args['output'] == 'form') {
        $reportOptions['autoParamsForm'] = true;
        $reportOptions['paramsOnly'] = true;
    } else {
        $reportOptions['autoParamsForm'] = false;
    }
    if (!empty($args['row_class'])) {
        $reportOptions['rowClass'] = $args['row_class'];
    }
    // Set up a page refresh for dynamic update of the report at set intervals
    if (isset($args['refresh_timer']) && $args['refresh_timer'] !== 0 && is_numeric($args['refresh_timer'])) {
        // is_numeric prevents injection
        if (isset($args['load_on_refresh']) && !empty($args['load_on_refresh'])) {
            report_helper::$javascript .= "setTimeout('window.location=\"" . $args['load_on_refresh'] . "\";', " . $args['refresh_timer'] . "*1000 );\n";
        } else {
            report_helper::$javascript .= "setTimeout('window.location.reload( false );', " . $args['refresh_timer'] . "*1000 );\n";
        }
    }
    return $reportOptions;
}
 /**
  * <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;
 }
/**
 * Code to output a standardised report filtering panel.
 *
 * Filters can be saved and loaded by each user. Additionally, filters can define permissions to a certain task, e.g. they can be used to define the
 * context within which someone can verify. In this case they provide the "outer limit" of the available records.
 * Requires a [map] control on the page. If you don't want a map, the current option is to include one anyway and use css to hide the #map-container div.
 *
 * @param array $readAuth Pass read authorisation tokens.
 * @param array $options Options array with the following possibilities:
 *   sharing - define the record sharing task that is being filtered against. Options are reporting (default), peer_review, verification, moderation, data_flow.
 *   context_id - can also be passed as URL parameter. Force the initial selection of a particular context (a record which has defines_permissions=true in the
 *   filters table. Set to "default" to select their profile verification settings when sharing=verification.
 *   filter_id - can also be passed as URL parameter. Force the initial selection of a particular filter record in the filters table.
 *   filterTypes - allows control of the list of filter panels available, e.g. to turn one off. Associative array keyed by category
 *   so that the filter panels can be grouped (use a blank key if not required). The array values are an array of or strings with a comma separated list
 *   of the filter types to included in the category - options are what, where, when, who, quality, source.
 *   filter-#name# - set the initial value of a report filter parameter #name#.
 *   allowLoad - set to false to disable the load bar at the top of the panel.
 *   allowSave - set to false to disable the save bar at the foot of the panel.
 *   presets - provide an array of preset filters to provide in the filters drop down. Choose from my-records, my-groups (uses
 *     your list of taxon groups in the user account), my-locality (uses your recording locality from the user account),
 *     my-groups-locality (uses taxon groups and recording locality from the user account), my-queried-records, queried-records,
 *     answered-records, accepted-records, not-accepted-records.
 * @param integer $website_id The current website's warehouse ID.
 * @param string $hiddenStuff Output parameter which will contain the hidden popup HTML that will be shown
 * using fancybox during filter editing. Should be appended AFTER any form element on the page as nested forms are not allowed.
 * @return string HTML for the report filter panel
 */
function report_filter_panel($readAuth, $options, $website_id, &$hiddenStuff)
{
    if (function_exists('iform_load_helpers')) {
        iform_load_helpers(array('report_helper'));
    } else {
        //When running on warehouse we don't have iform_load_helpers
        require_once DOCROOT . 'client_helpers/report_helper.php';
    }
    if (!empty($_POST['filter:sharing'])) {
        $options['sharing'] = $_POST['filter:sharing'];
    }
    $options = array_merge(array('sharing' => 'reporting', 'admin' => false, 'adminCanSetSharingTo' => array('R' => 'reporting', 'V' => 'verification'), 'allowLoad' => true, 'allowSave' => true, 'redirect_on_success' => '', 'presets' => array('my-records', 'my-queried-records', 'my-queried-or-not-accepted-records', 'my-not-reviewed-records', 'my-accepted-records', 'my-groups', 'my-locality', 'my-groups-locality'), 'entity' => 'occurrence'), $options);
    // Introduce some extra quick filters useful for verifiers.
    if ($options['sharing'] === 'verification') {
        $options['presets'] = array_merge(array('queried-records', 'answered-records', 'accepted-records', 'not-accepted-records'), $options['presets']);
    }
    if ($options['entity'] === 'sample') {
        unset($options['presets']['my-groups']);
        unset($options['presets']['my-groups-locality']);
    }
    //If in the warehouse we don't need to worry about the iform master list.
    if (function_exists('variable_get')) {
        $options = array_merge(array('taxon_list_id' => variable_get('iform_master_checklist_id', 0)), $options);
    }
    $options['sharing'] = report_filters_sharing_code_to_full_term($options['sharing']);
    $options['sharingCode'] = report_filters_full_term_to_sharing_code($options['sharing']);
    if (!preg_match('/^(reporting|peer_review|verification|data_flow|moderation)$/', $options['sharing'])) {
        return 'The @sharing option must be one of reporting, peer_review, verification, data_flow or moderation (currently ' . $options['sharing'] . ').';
    }
    report_helper::add_resource('reportfilters');
    report_helper::add_resource('validation');
    report_helper::add_resource('fancybox');
    if (function_exists('hostsite_add_library')) {
        hostsite_add_library('collapse');
    }
    $filterData = report_filters_load_existing($readAuth, $options['sharingCode']);
    $existing = '';
    $contexts = '';
    // add some preset filters in
    //If in the warehouse we don't need to worry about user specific preferences when setting up milestones.
    if (function_exists('hostsite_get_user_field')) {
        foreach ($options['presets'] as $preset) {
            $title = false;
            switch ($preset) {
                case 'my-records':
                    if (hostsite_get_user_field('id')) {
                        $title = lang::get('My records');
                    }
                    break;
                case 'my-queried-records':
                    if (hostsite_get_user_field('id')) {
                        $title = lang::get('My queried records');
                    }
                    break;
                case 'my-queried-or-not-accepted-records':
                    if (hostsite_get_user_field('id')) {
                        $title = lang::get('My not accepted or queried records');
                    }
                    break;
                case 'my-not-reviewed-records':
                    if (hostsite_get_user_field('id')) {
                        $title = lang::get('My not reviewed records');
                    }
                    break;
                case 'my-accepted-records':
                    if (hostsite_get_user_field('id')) {
                        $title = lang::get('My accepted records');
                    }
                    break;
                case 'my-groups':
                    if (hostsite_get_user_field('taxon_groups', false, true)) {
                        $title = lang::get('Records in species groups I like to record');
                    }
                    break;
                case 'my-locality':
                    if (hostsite_get_user_field('location')) {
                        $title = lang::get('Records in the locality I generally record in');
                    }
                    break;
                case 'my-groups-locality':
                    if (hostsite_get_user_field('taxon_groups', false, true) && hostsite_get_user_field('location')) {
                        $title = lang::get('Records of my species groups in my locality');
                    }
                    break;
                case 'queried-records':
                    $title = lang::get('Queried records');
                    break;
                case 'answered-records':
                    $title = lang::get('Records with answers');
                    break;
                case 'accepted-records':
                    $title = lang::get('Accepted records');
                    break;
                case 'not-accepted-records':
                    $title = lang::get('Not accepted records');
                    break;
                default:
                    throw new exception("Unsupported preset {$preset} for the filter panel");
            }
            if ($title) {
                $presetFilter = array('id' => $preset, 'title' => $title, 'defines_permissions' => 'f');
                $filterData[] = $presetFilter;
            }
        }
        if (count($options['presets'])) {
            if ($groups = hostsite_get_user_field('taxon_groups', false, true)) {
                data_entry_helper::$javascript .= "indiciaData.userPrefsTaxonGroups='" . implode(',', $groups) . "';\n";
            }
            if ($location = hostsite_get_user_field('location')) {
                data_entry_helper::$javascript .= "indiciaData.userPrefsLocation=" . $location . ";\n";
            }
        }
        $contextDefs = array();
        if ($options['sharing'] === 'verification') {
            // apply legacy verification settings from their profile
            $location_id = hostsite_get_user_field('location_expertise');
            $taxon_group_ids = hostsite_get_user_field('taxon_groups_expertise', false, true);
            $survey_ids = hostsite_get_user_field('surveys_expertise', false, true);
            if ($location_id || $taxon_group_ids || $survey_ids) {
                $selected = !empty($options['context_id']) && $options['context_id'] === 'default' ? 'selected="selected" ' : '';
                $contexts .= "<option value=\"default\" {$selected}>" . lang::get('My verification records') . "</option>";
                $def = array();
                if ($location_id) {
                    // user profile geographic limits should always be based on an indexed location.
                    $def['indexed_location_id'] = $location_id;
                }
                if ($taxon_group_ids) {
                    $def['taxon_group_list'] = implode(',', $taxon_group_ids);
                    $def['taxon_group_names'] = array();
                    $groups = data_entry_helper::get_population_data(array('table' => 'taxon_group', 'extraParams' => $readAuth + array('id' => $taxon_group_ids)));
                    foreach ($groups as $group) {
                        $def['taxon_group_names'][$group['id']] = $group['title'];
                    }
                }
                if ($survey_ids) {
                    $def['survey_list'] = implode(',', array_filter($survey_ids));
                }
                $contextDefs['default'] = $def;
            }
        }
    }
    if (!empty($_GET['context_id'])) {
        $options['context_id'] = $_GET['context_id'];
    }
    if (!empty($_GET['filter_id'])) {
        $options['filter_id'] = $_GET['filter_id'];
    }
    if (!empty($_GET['filters_user_id'])) {
        $options['filters_user_id'] = $_GET['filters_user_id'];
    }
    foreach ($filterData as $filter) {
        if ($filter['defines_permissions'] === 't') {
            $selected = !empty($options['context_id']) && $options['context_id'] == $filter['id'] ? 'selected="selected" ' : '';
            $contexts .= "<option value=\"{$filter['id']}\" {$selected}>{$filter['title']}</option>";
            $contextDefs[$filter['id']] = json_decode($filter['definition']);
        } else {
            $selected = !empty($options['filter_id']) && $options['filter_id'] == $filter['id'] ? 'selected="selected" ' : '';
            $existing .= "<option value=\"{$filter['id']}\" {$selected}>{$filter['title']}</option>";
        }
    }
    $r = '<div id="standard-params" class="ui-widget">';
    if ($options['allowSave'] && $options['admin']) {
        if (empty($_GET['filters_user_id'])) {
            // new filter to create, so sharing type can be edited
            $reload = data_entry_helper::get_reload_link_parts();
            $reloadPath = $reload['path'];
            if (count($reload['params'])) {
                $reloadPath .= '?' . data_entry_helper::array_to_query_string($reload['params']);
            }
            $r .= "<form action=\"{$reloadPath}\" method=\"post\" >";
            $r .= data_entry_helper::select(array('label' => lang::get('Select filter type'), 'fieldname' => 'filter:sharing', 'lookupValues' => $options['adminCanSetSharingTo'], 'afterControl' => '<input type="submit" value="Go"/>', 'default' => $options['sharingCode']));
            $r .= '</form>';
        } else {
            // existing filter to edit, type is therefore fixed. JS will fill these values in.
            $r .= '<p>' . lang::get('This filter is for <span id="sharing-type-label"></span>.') . '</p>';
            $r .= data_entry_helper::hidden_text(array('fieldname' => 'filter:sharing'));
        }
    }
    if ($options['allowLoad']) {
        $r .= '<div class="header ui-toolbar ui-widget-header ui-helper-clearfix"><div><span id="active-filter-label">' . lang::get('New report') . '</span></div><span class="changed" style="display:none" title="This filter has been changed">*</span>';
        $r .= '<div>';
        if ($contexts) {
            data_entry_helper::$javascript .= "indiciaData.filterContextDefs = " . json_encode($contextDefs) . ";\n";
            if (count($contextDefs) > 1) {
                $r .= '<label for="context-filter">' . lang::get('Context:') . "</label><select id=\"context-filter\">{$contexts}</select>";
            } else {
                $keys = array_keys($contextDefs);
                $r .= '<input type="hidden" id="context-filter" value="' . $keys[0] . '" />';
            }
        }
        $r .= '<label for="select-filter">' . lang::get('Filter:') . '</label><select id="select-filter"><option value="" selected="selected">' . lang::get('Select filter') . "...</option>{$existing}</select>";
        $r .= '<button type="button" id="filter-apply">' . lang::get('Apply') . '</button>';
        $r .= '<button type="button" id="filter-reset" class="disabled">' . lang::get('Reset') . '</button>';
        $r .= '<button type="button" id="filter-build">' . lang::get('Create a filter') . '</button></div>';
        $r .= '</div>';
        $r .= '<div id="filter-details" style="display: none">';
        $r .= '<img src="' . data_entry_helper::$images_path . 'nuvola/close-22px.png" width="22" height="22" alt="Close filter builder" title="Close filter builder" class="button" id="filter-done"/>' . "\n";
    } else {
        $r .= '<div id="filter-details">';
        if (!empty($options['filter_id'])) {
            $r .= "<input type=\"hidden\" id=\"select-filter\" value=\"{$options['filter_id']}\"/>";
        } elseif (!empty($options['filters_user_id'])) {
            $r .= "<input type=\"hidden\" id=\"select-filters-user\" value=\"{$options['filters_user_id']}\"/>";
        }
    }
    $r .= '<div id="filter-panes">';
    if ($options['entity'] === 'occurrence') {
        $filters = array('filter_what' => new filter_what(), 'filter_where' => new filter_where(), 'filter_when' => new filter_when(), 'filter_who' => new filter_who(), 'filter_occurrence_id' => new filter_occurrence_id(), 'filter_quality' => new filter_quality(), 'filter_source' => new filter_source());
    } elseif ($options['entity'] === 'sample') {
        $filters = array('filter_where' => new filter_where(), 'filter_when' => new filter_when(), 'filter_who' => new filter_who(), 'filter_sample_id' => new filter_sample_id(), 'filter_quality' => new filter_quality_sample(), 'filter_source' => new filter_source());
    }
    if (!empty($options['filterTypes'])) {
        $filterModules = array();
        foreach ($options['filterTypes'] as $category => $list) {
            // $list can be an array or comma separated list
            if (is_array($list)) {
                $list = implode(',', $list);
            }
            $paneNames = 'filter_' . str_replace(',', ',filter_', $list);
            $paneList = explode(',', $paneNames);
            $filterModules[$category] = array_intersect_key($filters, array_fill_keys($paneList, 1));
        }
    } else {
        $filterModules = array('' => $filters);
    }
    foreach ($filterModules as $category => $list) {
        if ($category) {
            $r .= '<fieldset class="collapsible collapsed">' . '<legend>' . '<span class="fieldset-legend">' . $category . '</span>' . '</legend>' . '<div class="fieldset-wrapper">';
        }
        foreach ($list as $moduleName => $module) {
            $r .= "<div class=\"pane\" id=\"pane-{$moduleName}\"><a class=\"fb-filter-link\" href=\"#controls-{$moduleName}\"><span class=\"pane-title\">" . $module->get_title() . '</span>';
            $r .= '<span class="filter-desc"></span></a>';
            $r .= "</div>";
        }
        if ($category) {
            $r .= '</div></fieldset>';
        }
    }
    $r .= '</div>';
    // filter panes
    $r .= '<div class="toolbar">';
    if ($options['allowSave']) {
        $r .= '<label for="filter:title">' . lang::get('Save filter as') . ':</label> <input id="filter:title" class="control-width-5"/>';
        if ($options['admin']) {
            $r .= '<br/>';
            if (empty($options['adminCanSetSharingTo'])) {
                throw new exception('Report standard params panel in admin mode so adminCanSetSharingTo option must be populated.');
            }
            $r .= data_entry_helper::autocomplete(array('label' => 'For who?', 'fieldname' => 'filters_user:user_id', 'table' => 'user', 'valueField' => 'id', 'captionField' => 'person_name', 'formatFunction' => "function(item) { return item.person_name + ' (' + item.email_address + ')'; }", 'extraParams' => $readAuth + array('view' => 'detail'), 'class' => 'control-width-5'));
            $r .= data_entry_helper::textarea(array('label' => 'Description', 'fieldname' => 'filter:description'));
        }
        $r .= '<img src="' . data_entry_helper::$images_path . 'nuvola/save-22px.png" width="22" height="22" alt="Save filter" title="Save filter" class="button" id="filter-save"/>';
        $r .= '<img src="' . data_entry_helper::$images_path . 'trash-22px.png" width="22" height="22" alt="Bin this filter" title="Bin this filter" class="button disabled" id="filter-delete"/>';
    }
    $r .= '</div></div>';
    // toolbar + clearfix
    if (!empty($options['filters_user_id'])) {
        // if we are preloading based on a filter user ID, we need to get the information now so that the sharing mode can be known
        // when loading controls
        $fu = data_entry_helper::get_population_data(array('table' => 'filters_user', 'extraParams' => $readAuth + array('id' => $options['filters_user_id']), 'caching' => false));
        if (count($fu) !== 1) {
            throw new exception('Could not find filter user record');
        }
        $options['sharing'] = report_filters_sharing_code_to_full_term($fu[0]['filter_sharing']);
    }
    report_helper::$javascript .= "indiciaData.lang={pleaseSelect:\"" . lang::get('Please select') . "\"};\n";
    // create the hidden panels required to populate the popups for setting each type of filter up.
    $hiddenStuff = '';
    foreach ($filterModules as $category => $list) {
        foreach ($list as $moduleName => $module) {
            $hiddenStuff .= "<div style=\"display: none\"><div class=\"filter-popup\" id=\"controls-{$moduleName}\"><form action=\"#\" class=\"filter-controls\"><fieldset>" . $module->get_controls($readAuth, $options) . '<button class="fb-close" type="button">Cancel</button>' . '<button class="fb-apply" type="submit">Apply</button></fieldset></form></div></div>';
            $shortName = str_replace('filter_', '', $moduleName);
            report_helper::$javascript .= "indiciaData.lang.NoDescription{$shortName}='" . lang::get('Click to Filter ' . ucfirst($shortName)) . "';\n";
        }
    }
    $r .= '</div>';
    report_helper::$js_read_tokens = $readAuth;
    report_helper::$javascript .= "indiciaData.lang.CreateAFilter='" . lang::get('Create a filter') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.ModifyFilter='" . lang::get('Modify filter') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.FilterReport='" . lang::get('New report') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.FilterSaved='" . lang::get('The filter has been saved') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.FilterDeleted='" . lang::get('The filter has been deleted') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.ConfirmFilterChangedLoad='" . lang::get('Do you want to load the selected filter and lose your current changes?') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.FilterExistsOverwrite='" . lang::get('A filter with that name already exists. Would you like to overwrite it?') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.AutochecksFailed='" . lang::get('Automated checks failed') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.AutochecksPassed='" . lang::get('Automated checks passed') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.HasPhotos='" . lang::get('Records which have photos') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.ConfirmFilterDelete='" . lang::get('Are you sure you want to permanently delete the {title} filter?') . "';\n";
    report_helper::$javascript .= "indiciaData.lang.MyRecords='" . lang::get('My records only') . "';\n";
    if (function_exists('iform_ajaxproxy_url')) {
        report_helper::$javascript .= "indiciaData.filterPostUrl='" . iform_ajaxproxy_url(null, 'filter') . "';\n";
        report_helper::$javascript .= "indiciaData.filterAndUserPostUrl='" . iform_ajaxproxy_url(null, 'filter_and_user') . "';\n";
    }
    report_helper::$javascript .= "indiciaData.filterSharing='" . strtoupper(substr($options['sharing'], 0, 1)) . "';\n";
    if (function_exists('hostsite_get_user_field')) {
        report_helper::$javascript .= "indiciaData.user_id='" . hostsite_get_user_field('indicia_user_id') . "';\n";
    } else {
        report_helper::$javascript .= "indiciaData.user_id='" . $_SESSION['auth_user']->id . "';\n";
    }
    if (!empty($website_id)) {
        report_helper::$javascript .= "indiciaData.website_id=" . $website_id . ";\n";
    }
    report_helper::$javascript .= "indiciaData.redirectOnSuccess='{$options['redirect_on_success']}';\n";
    // load up the filter, BEFORE any AJAX load of the grid code. First fetch any URL param overrides.
    $getParams = array();
    $optionParams = array();
    foreach ($_GET as $key => $value) {
        if (substr($key, 0, 7) === 'filter-') {
            $getParams[substr($key, 7)] = $value;
        }
    }
    foreach ($options as $key => $value) {
        if (substr($key, 0, 7) === 'filter-') {
            $optionParams[substr($key, 7)] = $value;
        }
    }
    $allParams = array_merge($optionParams, $getParams);
    if (!empty($allParams)) {
        $allParams = json_encode($allParams);
        report_helper::$onload_javascript .= "var params = {$allParams};\n";
        report_helper::$onload_javascript .= "indiciaData.filter.def=\$.extend(indiciaData.filter.def, params);\n";
        report_helper::$onload_javascript .= "indiciaData.filter.orig=\$.extend({}, params);\n";
    }
    $getParams = empty($getParams) ? '{}' : json_encode($getParams);
    if (!empty($options['filters_user_id'])) {
        report_helper::$onload_javascript .= "loadFilterUser(" . json_encode($fu[0]) . ", {$getParams});\n";
    } else {
        report_helper::$onload_javascript .= "if (\$('#select-filter').val()) {\n" . "  loadFilter(\$('#select-filter').val(), {$getParams});\n" . "} else {\n" . "  applyFilterToReports(false);\n" . "}\n";
    }
    return $r;
}
Example #6
0
 /**
  * <p>Outputs a calendar based summary grid that loads the content of a report.</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_summary($options)
 {
     // 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 : i8n
     // TODO invariant IDs and names prevents more than one on a page.
     // TODO convert to tabs when switching between chart and table.
     $warnings = '<span style="display:none;">Starting report_calendar_summary : ' . date(DATE_ATOM) . '</span>' . "\n";
     data_entry_helper::add_resource('jquery_ui');
     // there are some report parameters that we can assume for a calendar based request...
     // the report must have a date field, a user_id field if set in the configuration, and a location_id.
     // default is samples_list_for_cms_user.xml
     $options = self::get_report_calendar_summary_options($options);
     $extras = '';
     self::request_report($response, $options, $currentParamValues, false, $extras);
     if (isset($response['error'])) {
         return "ERROR RETURNED FROM request_report:<br />" . print_r($response, true);
     }
     // We're not even going to bother with asking the user to populate a partially filled in report parameter set.
     if (isset($response['parameterRequest'])) {
         return '<p>Internal Error: Report request parameters not set up correctly.<br />' . print_r($response, true) . '<p>';
     }
     // convert records to a date based array so it can be used when generating the grid.
     $warnings .= '<span style="display:none;">Report request finish : ' . date(DATE_ATOM) . '</span>' . "\n";
     $records = $response['records'];
     $pageUrlParams = self::get_report_calendar_grid_page_url_params($options);
     $pageUrl = self::report_calendar_grid_get_reload_url($pageUrlParams);
     $pageUrl .= strpos($pageUrl, '?') === false ? '?' : '&';
     data_entry_helper::$javascript .= "\nvar pageURI = \"" . $_SERVER['REQUEST_URI'] . "\";\nfunction rebuild_page_url(oldURL, overrideparam, overridevalue) {\n  var parts = oldURL.split('?');\n  var params = [];\n  if(overridevalue!=='') params.push(overrideparam+'='+overridevalue);\n  if(parts.length > 1) {\n    var oldparams = parts[1].split('&');\n    for(var i = 0; i < oldparams.length; i++){\n      var bits = oldparams[i].split('=');\n      if(bits[0] != overrideparam) params.push(oldparams[i]);\n    }\n  }\n  return parts[0]+(params.length > 0 ? '?'+params.join('&') : '');\n};\nvar pageURI = \"" . $_SERVER['REQUEST_URI'] . "\";\nfunction update_controls(){\n  \$('#year-control-previous').attr('href',rebuild_page_url(pageURI,'year'," . substr($options['date_start'], 0, 4) . "-1));\n  \$('#year-control-next').attr('href',rebuild_page_url(pageURI,'year'," . substr($options['date_start'], 0, 4) . "+1));\n  // user and location ids are dealt with in the main form. their change functions look a pageURI\n}\nupdate_controls();\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(substr($options['date_start'], 0, 4) . "-" . $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(substr($options['date_start'], 0, 4) . '-' . $options['weekOneContains']);
         if (!$weekOne_date) {
             $warnings .= "Week one month-day combination unrecognised {" . $options['weekOneContains'] . "} defaulting to Jan-01<br />";
             $weekOne_date = date_create(substr($options['date_start'], 0, 4) . '-Jan-01');
         }
     } else {
         $weekOne_date = date_create(substr($options['date_start'], 0, 4) . '-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');
         }
     }
     // don't do anything if equal.
     $year_start = date_create(substr($options['date_start'], 0, 4) . '-Jan-01');
     $year_end = date_create(substr($options['date_start'], 0, 4) . '-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
     $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(substr($options['date_start'], 0, 4) . '-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 .= '<td class="week">' . $i . '</td>';
         $tableDateHeaderRow .= '<td class="week">' . $firstWeek_date->format('M') . '<br/>' . $firstWeek_date->format('d') . '</td>';
         $downloadNumberHeaderRow .= '%2C' . $i;
         $downloadDateHeaderRow .= '%2C' . $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();
     $dateList = array();
     $weekList = array();
     $avgFieldList = !empty($options['avgFields']) ? explode(',', $options['avgFields']) : false;
     $smpAttrList = array();
     if (!$avgFieldList || count($avgFieldList) == 0) {
         $avgFields = false;
     } else {
         $avgFields = array();
         foreach ($avgFieldList as $avgField) {
             $avgFields[$avgField] = array('caption' => $avgField, 'attr' => false);
             $parts = explode(':', $avgField);
             if (count($parts) == 2 && ($parts[0] = 'smpattr')) {
                 $smpAttribute = data_entry_helper::get_population_data(array('table' => 'sample_attribute', 'extraParams' => $options['readAuth'] + array('view' => 'list', 'id' => $parts[1])));
                 if (count($smpAttribute) >= 1) {
                     // may be assigned to more than one survey on this website. This is not relevant to info we want.
                     $avgFields[$avgField]['id'] = $parts[1];
                     $avgFields[$avgField]['attr'] = $smpAttribute[0];
                     $avgFields[$avgField]['caption'] = $smpAttribute[0]['caption'];
                     if ($smpAttribute[0]['data_type'] == 'L') {
                         $avgFields[$avgField]['attr']['termList'] = data_entry_helper::get_population_data(array('table' => 'termlists_term', 'extraParams' => $options['readAuth'] + array('view' => 'detail', 'termlist_id' => $avgFields[$avgField]['attr']['termlist_id'])));
                     }
                 }
             }
         }
     }
     // we are assuming that there can be more than one occurrence of a given taxon per sample.
     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 = (int) floor(($this_yearday - $weekOne_date_yearday) / 7) + 1;
         if (isset($weekList[$weekno])) {
             if (!in_array($record['location_name'], $weekList[$weekno])) {
                 $weekList[$weekno][] = $record['location_name'];
             }
         } else {
             $weekList[$weekno] = array($record['location_name']);
         }
         if (!isset($rawArray[$this_index])) {
             $rawArray[$this_index] = array('weekno' => $weekno, 'counts' => array(), 'date' => $record['date'], 'total' => 0, 'samples' => array(), 'avgFields' => array());
         }
         // we assume that the report is configured to return the user_id which matches the method used to generate my_user_id
         if (($options['my_user_id'] == $record['user_id'] || $options['location_list'] == 'all' || $options['location_list'] != 'none' && in_array($record['location_id'], $options['location_list'])) && !isset($rawArray[$this_index]['samples'][$record['sample_id']])) {
             $rawArray[$this_index]['samples'][$record['sample_id']] = array('id' => $record['sample_id'], 'location_name' => $record['location_name'], 'avgFields' => array());
             if ($avgFields) {
                 foreach ($avgFields as $field => $avgField) {
                     if (!$avgField['attr']) {
                         $rawArray[$this_index]['samples'][$record['sample_id']]['avgFields'][$field] = $record[$field];
                     } else {
                         if ($avgField['attr']['data_type'] == 'L') {
                             $term = trim($record['attr_sample_term_' . $avgField['id']], "% \t\n\r\v");
                             $rawArray[$this_index]['samples'][$record['sample_id']]['avgFields'][$field] = is_numeric($term) ? $term : null;
                         } else {
                             $rawArray[$this_index]['samples'][$record['sample_id']]['avgFields'][$field] = $record['attr_sample_' . $avgField['id']];
                         }
                     }
                 }
             }
         }
         $records[$recid]['weekno'] = $weekno;
         $records[$recid]['date_index'] = $this_index;
         if (isset($locationSamples[$record['location_id']])) {
             if (isset($locationSamples[$record['location_id']][$weekno])) {
                 if (!in_array($record['sample_id'], $locationSamples[$record['location_id']][$weekno])) {
                     $locationSamples[$record['location_id']][$weekno][] = $record['sample_id'];
                 }
             } else {
                 $locationSamples[$record['location_id']][$weekno] = array($record['sample_id']);
             }
         } else {
             $locationSamples[$record['location_id']] = array($weekno => array($record['sample_id']));
         }
     }
     $warnings .= '<span style="display:none;">Records date pre-processing complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     if ($avgFields) {
         foreach ($rawArray as $dateIndex => $rawData) {
             foreach ($avgFields as $field => $avgField) {
                 $total = 0;
                 $count = 0;
                 foreach ($rawArray[$dateIndex]['samples'] as $sample) {
                     if ($sample['avgFields'][$field] != null) {
                         $total += $sample['avgFields'][$field];
                         $count++;
                     }
                 }
                 $rawArray[$dateIndex]['avgFields'][$field] = $count ? $total / $count : "";
                 if ($options['avgFieldRound'] == 'nearest' && $rawArray[$dateIndex]['avgFields'][$field] != "") {
                     $rawArray[$dateIndex]['avgFields'][$field] = (int) round($rawArray[$dateIndex]['avgFields'][$field]);
                 }
             }
         }
     }
     $warnings .= '<span style="display:none;">Sample Attribute processing complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     $count = count($records);
     self::report_calendar_summary_initLoc1($minWeekNo, $maxWeekNo, $weekList);
     if ($count > 0) {
         $locationArray = self::report_calendar_summary_initLoc2($minWeekNo, $maxWeekNo, $locationSamples[$records[0]['location_id']]);
     }
     $warnings .= '<span style="display:none;">Number of records processed : ' . $count . ' : ' . date(DATE_ATOM) . '</span>' . "\n";
     $downloadList = 'Location%2C' . ($options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number' ? lang::get('Week Number') . '%2C' : '') . lang::get('Week Commencing') . '%2C' . lang::get('Species') . '%2C' . lang::get('Type') . '%2C' . lang::get('Value') . '%0A';
     foreach ($records as $idex => $record) {
         // If the taxon has changed
         if ($lastTaxonID && $lastTaxonID != $record[$options['rowGroupID']] || $lastLocation && $lastLocation != $record['location_id']) {
             self::report_calendar_summary_processEstimates($summaryArray, $locationArray, $locationSamples[$lastLocation], $minWeekNo, $maxWeekNo, $fullDates, $lastTaxonID, $seriesLabels[$lastTaxonID], $options, $downloadList);
             $locationArray = self::report_calendar_summary_initLoc2($minWeekNo, $maxWeekNo, $locationSamples[$record['location_id']]);
         }
         $lastTaxonID = $record[$options['rowGroupID']];
         $seriesLabels[$lastTaxonID] = $record[$options['rowGroupColumn']];
         $lastLocation = $record['location_id'];
         $lastSample = $record['sample_id'];
         $weekno = $record['weekno'];
         if ($lastTaxonID === null) {
             $count = 0;
         } else {
             if (isset($options['countColumn']) && $options['countColumn'] != '') {
                 $count = isset($record[$options['countColumn']]) ? $record[$options['countColumn']] : 0;
             } else {
                 $count = 1;
             }
         }
         // default to single row = single occurrence
         // leave this conditional in - not sure what may happen in future, and it works.
         if ($weekno >= $minWeekNo && $weekno <= $maxWeekNo) {
             if ($locationArray[$weekno]['this_sample'] != $lastSample) {
                 $locationArray[$weekno]['max'] = max($locationArray[$weekno]['max'], $locationArray[$weekno]['sampleTotal']);
                 $locationArray[$weekno]['this_sample'] = $lastSample;
                 $locationArray[$weekno]['numSamples']++;
                 $locationArray[$weekno]['sampleTotal'] = $count;
             } else {
                 $locationArray[$weekno]['sampleTotal'] += $count;
             }
             $locationArray[$weekno]['total'] += $count;
             $locationArray[$weekno]['forcedZero'] = false;
             $locationArray[$weekno]['location'] = $record['location_name'];
         }
         $this_index = $record['date_index'];
         if ($lastTaxonID != null) {
             if (isset($rawArray[$this_index]['counts'][$lastTaxonID])) {
                 $rawArray[$this_index]['counts'][$lastTaxonID] += $count;
             } else {
                 $rawArray[$this_index]['counts'][$lastTaxonID] = $count;
             }
             $rawArray[$this_index]['total'] += $count;
         }
     }
     if ($lastTaxonID || $lastLocation) {
         self::report_calendar_summary_processEstimates($summaryArray, $locationArray, $locationSamples[$lastLocation], $minWeekNo, $maxWeekNo, $fullDates, $lastTaxonID, $seriesLabels[$lastTaxonID], $options, $downloadList);
     }
     $warnings .= '<span style="display:none;">Estimate processing finished : ' . date(DATE_ATOM) . '</span>' . "\n";
     if (count($summaryArray) == 0) {
         return $warnings . '<p>' . lang::get('No data returned for this period.') . '</p>';
     }
     $r = "";
     // will storedata in an array[Y][X]
     $format = array();
     if (isset($options['outputTable']) && $options['outputTable']) {
         $format['table'] = array('include' => true, 'display' => isset($options['simultaneousOutput']) && $options['simultaneousOutput'] || isset($options['outputFormat']) && $options['outputFormat'] == 'table' || !isset($options['outputFormat']));
     }
     if (isset($options['outputChart']) && $options['outputChart']) {
         $format['chart'] = array('include' => true, 'display' => isset($options['simultaneousOutput']) && $options['simultaneousOutput'] || isset($options['outputFormat']) && $options['outputFormat'] == 'chart');
         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:
                 $renderer = '$.jqplot.LineRenderer';
                 break;
                 // default is line
         }
         self::add_resource('jqplot_category_axis_renderer');
         $opts = array();
         $options['legendOptions']["show"] = true;
         $opts[] = "seriesDefaults:{\n" . (isset($renderer) ? "  renderer:{$renderer},\n" : '') . "  rendererOptions:" . json_encode($options['rendererOptions']) . "}";
         $opts[] = 'legend:' . json_encode($options['legendOptions']);
     }
     if (count($format) == 0) {
         $format['table'] = array('include' => true);
     }
     $defaultSet = false;
     foreach ($format as $type => $info) {
         $defaultSet = $defaultSet || $info['display'];
     }
     if (!$defaultSet) {
         if (isset($format['table'])) {
             $format['table']['display'] = true;
         } else {
             if (isset($format['chart'])) {
                 $format['chart']['display'] = true;
             }
         }
     }
     $seriesData = array();
     $r .= "\n<div class=\"inline-control report-summary-controls\">";
     $userPicksFormat = count($format) > 1 && !(isset($options['simultaneousOutput']) && $options['simultaneousOutput']);
     $userPicksSource = ($options['includeRawData'] ? 1 : 0) + ($options['includeSummaryData'] ? 1 : 0) + ($options['includeEstimatesData'] ? 1 : 0) > 1;
     if (!$userPicksFormat && !$userPicksSource) {
         $r .= '<input type="hidden" id="outputSource" name="outputSource" value="' . ($options['includeRawData'] ? "raw" : ($options['includeSummaryData'] ? "summary" : "estimates")) . '"/>';
         if (isset($options['simultaneousOutput']) && $options['simultaneousOutput']) {
             // for combined format its fairly obvious what it is, so no need to add text.
             $r .= '<input type="hidden" id="outputFormat" name="outputFormat" value="both"/>';
         } else {
             // for single format its fairly obvious what it is, so no need to add text.
             foreach ($format as $type => $details) {
                 $r .= '<input type="hidden" id="outputFormat" name="outputFormat" value="' . $type . '"/>';
             }
         }
         // don't need to set URI as only 1 option.
     } else {
         $r .= lang::get('View ');
         if ($userPicksSource) {
             $r .= '<select id="outputSource" name="outputSource">' . ($options['includeRawData'] ? '<option id="viewRawData" value="raw"/>' . lang::get('raw data') . '</option>' : '') . ($options['includeSummaryData'] ? '<option id="viewSummaryData" value="summary"/>' . lang::get('summary data') . '</option>' : '') . ($options['includeEstimatesData'] ? '<option id="viewDataEstimates" value="estimates"/>' . lang::get('summary data with estimates') . '</option>' : '') . '</select>';
             data_entry_helper::$javascript .= "jQuery('#outputSource').change(function(){\n  pageURI = rebuild_page_url(pageURI, \"outputSource\", jQuery(this).val());\n  update_controls();\n  switch(jQuery(this).val()){\n    case 'raw':\n        jQuery('#" . $options['tableID'] . "-raw,#" . $options['chartID'] . "-raw').show();\n        jQuery('#" . $options['tableID'] . ",#" . $options['chartID'] . "-summary,#" . $options['chartID'] . "-estimates').hide();\n        break;\n    case 'summary':\n        jQuery('#" . $options['tableID'] . ",.summary,#" . $options['chartID'] . "-summary').show();\n        jQuery('#" . $options['tableID'] . "-raw,#" . $options['chartID'] . "-raw,.estimates,#" . $options['chartID'] . "-estimates').hide();\n        break;\n    case 'estimates':\n        jQuery('#" . $options['tableID'] . ",.estimates,#" . $options['chartID'] . "-estimates').show();\n        jQuery('#" . $options['tableID'] . "-raw,#" . $options['chartID'] . "-raw,.summary,#" . $options['chartID'] . "-summary').hide();\n        break;\n   }\n   if(jQuery('#outputFormat').val() != 'table')\n     replot();\n});\n" . (isset($options['outputSource']) ? "\$('#outputSource').val('" . $options['outputSource'] . "').change();\n" : "if(\$('#viewDataEstimates').length > 0){\n    \$('#outputSource').val('estimates').change();\n} else if(\$('#viewSummaryData').length > 0){\n    \$('#outputSource').val('summary').change();\n} else {\n    \$('#outputSource').val('raw').change();\n}\n");
         } else {
             $r .= '<input type="hidden" id="outputSource" name="outputSource" value="' . ($options['includeRawData'] ? "raw" : ($options['includeSummaryData'] ? "summary" : "estimates")) . '"/>';
         }
         if ($userPicksFormat) {
             $defaultTable = !isset($options['outputFormat']) || $options['outputFormat'] == '' || $options['outputFormat'] == 'table';
             $r .= lang::get(' as a ') . '<select id="outputFormat" name="outputFormat">' . '<option ' . ($defaultTable ? 'selected="selected"' : '') . ' value="table"/>' . lang::get('table') . '</option>' . '<option ' . (!$defaultTable ? 'selected="selected"' : '') . ' value="chart"/>' . lang::get('chart') . '</option>' . '</select>';
             // not providing option for both at moment
             data_entry_helper::$javascript .= "jQuery('[name=outputFormat]').change(function(){\n  pageURI = rebuild_page_url(pageURI, \"outputFormat\", jQuery(this).val());\n  update_controls();\n  switch(\$(this).val()) {\n    case 'table' :\n        jQuery('#" . $options['tableContainerID'] . "').show();\n        jQuery('#" . $options['chartContainerID'] . "').hide();\n        break;\n    default : // chart\n        jQuery('#" . $options['tableContainerID'] . "').hide();\n        jQuery('#" . $options['chartContainerID'] . "').show();\n        replot();\n        break;\n  }\n});\njQuery('[name=outputFormat]').change();\n";
         } else {
             if (isset($options['simultaneousOutput']) && $options['simultaneousOutput']) {
                 // for combined format its fairly obvious what it is, so no need to add text.
                 $r .= '<input type="hidden" id="outputFormat" name="outputFormat" value="both"/>';
             } else {
                 // for single format its fairly obvious what it is, so no need to add text.
                 foreach ($format as $type => $details) {
                     $r .= '<input type="hidden" id="outputFormat" name="outputFormat" value="' . $type . '"/>';
                 }
             }
         }
     }
     $r .= "</div>\n";
     $warnings .= '<span style="display:none;">Controls complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     ksort($rawArray);
     $warnings .= '<span style="display:none;">Raw data sort : ' . date(DATE_ATOM) . '</span>' . "\n";
     if (isset($format['chart'])) {
         $seriesToDisplay = isset($options['outputSeries']) ? explode(',', $options['outputSeries']) : 'all';
         $seriesIDs = array();
         $rawSeriesData = array();
         $rawTicks = array();
         $summarySeriesData = array();
         $estimatesSeriesData = array();
         $seriesOptions = array();
         // Series options are not configurable as we need to setup for ourselves...
         // we need show, label and show label filled in. rest are left to defaults
         $rawTotalRow = array();
         $summaryTotalRow = array();
         $estimatesTotalRow = array();
         for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
             $summaryTotalRow[$i] = 0;
             $estimatesTotalRow[$i] = 0;
         }
         foreach ($rawArray as $dateIndex => $rawData) {
             $rawTotalRow[] = 0;
             $rawTicks[] = "\"" . $rawData['date'] . "\"";
         }
         foreach ($summaryArray as $seriesID => $summaryRow) {
             if (empty($seriesLabels[$seriesID])) {
                 continue;
             }
             $rawValues = array();
             $summaryValues = array();
             $estimatesValues = array();
             for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
                 if (isset($summaryRow[$i])) {
                     $estimatesValues[] = $summaryRow[$i]['estimates'];
                     $estimatesTotalRow[$i] += $summaryRow[$i]['estimates'];
                     if ($summaryRow[$i]['summary'] !== false) {
                         $summaryValues[] = $summaryRow[$i]['summary'];
                         $summaryTotalRow[$i] += $summaryRow[$i]['summary'];
                     } else {
                         $summaryValues[] = 0;
                         $summaryTotalRow[$i] += 0;
                     }
                 } else {
                     $summaryValues[] = 0;
                     $estimatesValues[] = 0;
                 }
             }
             // we want to ensure that series match between summary and raw data. raw data is indexed by date.
             foreach ($rawArray as $dateIndex => $rawData) {
                 $rawValues[] = isset($rawData['counts'][$seriesID]) ? $rawData['counts'][$seriesID] : 0;
             }
             foreach ($rawValues as $idx => $rawValue) {
                 $rawTotalRow[$idx] += $rawValue;
             }
             // each series will occupy an entry in $seriesData
             if ($options['includeChartItemSeries']) {
                 $seriesIDs[] = $seriesID;
                 $rawSeriesData[] = '[' . implode(',', $rawValues) . ']';
                 $summarySeriesData[] = '[' . implode(',', $summaryValues) . ']';
                 $estimatesSeriesData[] = '[' . implode(',', $estimatesValues) . ']';
                 $seriesOptions[] = '{"show":' . ($seriesToDisplay == 'all' || in_array($seriesID, $seriesToDisplay) ? 'true' : 'false') . ',"label":"' . $seriesLabels[$seriesID] . '","showlabel":true}';
             }
         }
         if (isset($options['includeChartTotalSeries']) && $options['includeChartTotalSeries']) {
             // totals are put at the start
             array_unshift($seriesIDs, 0);
             // Total has ID 0
             array_unshift($rawSeriesData, '[' . implode(',', $rawTotalRow) . ']');
             array_unshift($summarySeriesData, '[' . implode(',', $summaryTotalRow) . ']');
             array_unshift($estimatesSeriesData, '[' . implode(',', $estimatesTotalRow) . ']');
             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;
         data_entry_helper::$javascript .= "var seriesData = {ids: [" . implode(',', $seriesIDs) . "], raw: [" . implode(',', $rawSeriesData) . "], summary: [" . implode(',', $summarySeriesData) . "], estimates: [" . implode(',', $estimatesSeriesData) . "]};\n";
         // Finally, dump out the Javascript with our constructed parameters.
         // width stuff is a bit weird, but jqplot requires a fixed width, so this just stretches it to fill the space.
         data_entry_helper::$javascript .= "\nvar plots = [];\nfunction replot(){\n  // there are problems with the coloring of series when added to a plot: easiest just to completely redraw.\n  var max=0;\n  var type = jQuery('#outputSource').val();\n  jQuery('#" . $options['chartID'] . "-'+type).empty();";
         if (!isset($options['width']) || $options['width'] == '') {
             data_entry_helper::$javascript .= "\n  jQuery('#" . $options['chartID'] . "-'+type).width(jQuery('#" . $options['chartID'] . "-'+type).width());";
         }
         data_entry_helper::$javascript .= "\n  var opts = {" . implode(",\n", $opts) . "};\n  if(type == 'raw') opts.axes.xaxis.ticks = [" . implode(',', $rawTicks) . "];\n  // copy series from checkboxes.\n  jQuery('[name=" . $options['chartID'] . "-series]').each(function(idx, elem){\n      opts.series[idx].show = (jQuery(elem).filter('[checked]').length > 0);\n  });\n  for(var i=0; i<seriesData[type].length; i++)\n    if(opts.series[i].show)\n      for(var j=0; j<seriesData[type][i].length; j++)\n          max=(max>seriesData[type][i][j]?max:seriesData[type][i][j]);\n  opts.axes.yaxis.max=max+1;\n  opts.axes.yaxis.tickInterval = Math.floor(max/15); // number of ticks - too many takes too long to display\n  if(!opts.axes.yaxis.tickInterval) opts.axes.yaxis.tickInterval=1;\n  plots[type] = \$.jqplot('" . $options['chartID'] . "-'+type,  seriesData[type], opts);\n};\n";
         // div are full width.
         $r .= '<div id="' . $options['chartContainerID'] . '" class="' . $options['chartClass'] . '" style="' . (isset($options['width']) && $options['width'] != '' ? 'width:' . $options['width'] . 'px;' : '') . ($format['chart']['display'] ? '' : 'display:none;') . '">';
         if (isset($options['title'])) {
             $r .= '<div class="' . $options['headerClass'] . '">' . $options['title'] . '</div>';
         }
         if ($options['includeRawData']) {
             $r .= '<div id="' . $options['chartID'] . '-raw" style="height:' . $options['height'] . 'px;' . (isset($options['width']) && $options['width'] != '' ? 'width:' . $options['width'] . 'px;' : '') . ($options['includeSummaryData'] || $options['includeEstimatesData'] ? ' display:none;' : '') . '"></div>' . "\n";
         }
         if ($options['includeSummaryData']) {
             $r .= '<div id="' . $options['chartID'] . '-summary" style="height:' . $options['height'] . 'px;' . (isset($options['width']) && $options['width'] != '' ? 'width:' . $options['width'] . 'px;' : '') . ($options['includeEstimatesData'] ? ' display:none;' : '') . '"></div>' . "\n";
         }
         if ($options['includeEstimatesData']) {
             $r .= '<div id="' . $options['chartID'] . '-estimates" style="height:' . $options['height'] . 'px;' . (isset($options['width']) && $options['width'] != '' ? 'width:' . $options['width'] . 'px;' : '') . '"></div>' . "\n";
         }
         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';
             }
             $r .= '<fieldset id="' . $options['chartID'] . '-series" class="' . $class . '"><legend>' . lang::get('Display Series') . "</legend><span>\n";
             $idx = 0;
             if (isset($options['includeChartTotalSeries']) && $options['includeChartTotalSeries']) {
                 // use value = 0 for Total
                 $r .= '<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");');
             }
             $r .= '<input type="button" class="disable-button" id="' . $options['chartID'] . '-series-disable" value="' . lang::get('Hide all ') . $options['rowGroupColumn'] . "\"/>\n";
             foreach ($summaryArray as $seriesID => $summaryRow) {
                 if (empty($seriesLabels[$seriesID])) {
                     continue;
                 }
                 $r .= '<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 . '">' . $seriesLabels[$seriesID] . "</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");');
             }
             $r .= "</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!
             if ($format['chart']['display']) {
                 data_entry_helper::$javascript .= "replot();\n";
             }
             data_entry_helper::$javascript .= "\n// above done due to need to ensure get around field caching on browser refresh.\nsetSeriesURLParam = function(){\n  var activeSeries = [],\n    active = jQuery('[name=" . $options['chartID'] . "-series]').filter('[checked]'),\n    total = jQuery('[name=" . $options['chartID'] . "-series]');\n  if(active.length == total.length) {\n    pageURI = rebuild_page_url(pageURI, 'outputSeries', '');\n  } else {\n    active.each(function(idx,elem){ activeSeries.push(\$(elem).val()); });\n    pageURI = rebuild_page_url(pageURI, 'outputSeries', activeSeries.join(','));\n  }\n  update_controls();\n}\njQuery('[name=" . $options['chartID'] . "-series]').change(function(){\n  var seriesID = jQuery(this).val(), index;\n  \$.each(seriesData.ids, function(idx, elem){\n    if(seriesID == elem) index = idx;\n  });\n  if(jQuery(this).filter('[checked]').length){\n    if(typeof plots['raw'] != 'undefined') plots['raw'].series[index].show = true;\n    if(typeof plots['summary'] != 'undefined') plots['summary'].series[index].show = true;\n    if(typeof plots['estimates'] != 'undefined') plots['estimates'].series[index].show = true;\n  } else {\n    if(typeof plots['raw'] != 'undefined') plots['raw'].series[index].show = false;\n    if(typeof plots['summary'] != 'undefined') plots['summary'].series[index].show = false;\n    if(typeof plots['estimates'] != 'undefined') plots['estimates'].series[index].show = false;\n  }\n  setSeriesURLParam();\n  replot();\n});\njQuery('#" . $options['chartID'] . "-series-disable').click(function(){\n  if(jQuery(this).is('.cleared')){ // button is to show all\n    jQuery('[name=" . $options['chartID'] . "-series]').not('[value=0]').attr('checked','checked');\n    \$.each(seriesData.ids, function(idx, elem){\n      if(elem == 0) return; // ignore total series\n      if(typeof plots['raw'] != 'undefined') plots['raw'].series[idx].show = true;\n      if(typeof plots['summary'] != 'undefined') plots['summary'].series[idx].show = true;\n      if(typeof plots['estimates'] != 'undefined') plots['estimates'].series[idx].show = true;\n    });\n    jQuery(this).removeClass('cleared').val(\"" . lang::get('Hide all ') . $options['rowGroupColumn'] . "\");\n  } else {\n    jQuery('[name=" . $options['chartID'] . "-series]').not('[value=0]').removeAttr('checked');\n    \$.each(seriesData.ids, function(idx, elem){\n      if(elem == 0) return; // ignore total series\n      if(typeof plots['raw'] != 'undefined') plots['raw'].series[idx].show = false;\n      if(typeof plots['summary'] != 'undefined') plots['summary'].series[idx].show = false;\n      if(typeof plots['estimates'] != 'undefined') plots['estimates'].series[idx].show = false;\n    });\n    jQuery(this).addClass('cleared').val(\"" . lang::get('Show all ') . $options['rowGroupColumn'] . "\");\n  }\n  setSeriesURLParam();\n  replot();\n});\n";
         }
         $r .= "</div>\n";
         $warnings .= '<span style="display:none;">Output chart complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     }
     if (isset($format['table'])) {
         $r .= '<div id="' . $options['tableContainerID'] . '">';
         if ($options['includeRawData']) {
             $thClass = $options['thClass'];
             $rawDataDownloadGrid = "";
             $rawDataDownloadList = 'Location%2C' . ($options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number' ? 'Week%20Number%2C' : '') . 'Date%2CSpecies%2CCount%0A';
             $r .= "\n<table id=\"" . $options['tableID'] . "-raw\" class=\"" . $options['tableClass'] . "\" style=\"" . ($format['table']['display'] ? '' : 'display:none;') . "\">";
             $r .= "\n<thead class=\"{$thClass}\">";
             // raw data headers: %Sun, mean temp, Date, Week Number, Location?
             // the Total column is driven as per summary
             // no total row
             if ($options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number') {
                 $r .= '<tr><td>Week</td>';
                 $rawDataDownloadGrid .= "Week";
                 foreach ($rawArray as $idx => $rawColumn) {
                     $r .= '<td class="week">' . $rawColumn['weekno'] . '</td>';
                     $rawDataDownloadGrid .= '%2C' . $rawColumn['weekno'];
                 }
                 if ($options['includeTableTotalColumn']) {
                     $r .= '<td class="total-column"></td>';
                     $rawDataDownloadGrid .= '%2C';
                 }
             }
             $r .= '</tr><tr><td>Date</td>';
             $rawDataDownloadGrid .= '%0ADate';
             $rawTotalRow = "";
             $rawDataDownloadGridTotalRow = "";
             $rawGrandTotal = 0;
             foreach ($rawArray as $idx => $rawColumn) {
                 $this_date = date_create(str_replace('/', '-', $rawColumn['date']));
                 // prevents day/month ordering issues
                 $r .= '<td class="week">' . $this_date->format('M') . '<br/>' . $this_date->format('d') . '</td>';
                 $rawDataDownloadGrid .= '%2C' . $this_date->format('d/m/Y');
                 $rawTotalRow .= '<td>' . $rawColumn['total'] . '</td>';
                 $rawDataDownloadGridTotalRow .= '%2C' . $rawColumn['total'];
                 $rawGrandTotal += $rawColumn['total'];
             }
             if ($options['includeTableTotalColumn']) {
                 $r .= '<td class="total-column">Total</td>';
                 $rawDataDownloadGrid .= '%2CTotal';
             }
             $r .= "</tr>";
             $rawDataDownloadGrid .= '%0A';
             // don't include links in download
             if (isset($options['linkURL']) && $options['linkURL'] != '') {
                 $r .= '<tr><td>Sample Links</td>';
                 foreach ($rawArray as $idx => $rawColumn) {
                     $links = array();
                     if (count($rawColumn['samples']) > 0) {
                         foreach ($rawColumn['samples'] as $sample) {
                             $links[] = '<a href="' . $options['linkURL'] . $sample['id'] . '" target="_blank" title="' . $sample['location_name'] . '">(' . $sample['id'] . ')</a>';
                         }
                     }
                     $r .= '<td class="links">' . implode('<br/>', $links) . '</td>';
                 }
                 $r .= ($options['includeTableTotalColumn'] ? '<td class="total-column"></td>' : '') . "</tr>";
             }
             $r .= "</thead>\n<tbody>\n";
             $altRow = false;
             if ($avgFields) {
                 foreach ($avgFieldList as $i => $field) {
                     $r .= "<tr class=\"sample-datarow " . ($altRow ? $options['altRowClass'] : '') . " " . ($i == count($avgFields) - 1 ? 'last-sample-datarow' : '') . "\">";
                     $caption = t('Mean ' . ucwords($avgFields[$field]['caption']));
                     $r .= '<td>' . $caption . '</td>';
                     $rawDataDownloadGrid .= '"' . $caption . '"';
                     foreach ($rawArray as $dateIndex => $rawData) {
                         $r .= '<td>' . $rawData['avgFields'][$field] . '</td>';
                         $rawDataDownloadGrid .= '%2C' . $rawData['avgFields'][$field];
                     }
                     if ($options['includeTableTotalColumn']) {
                         $r .= '<td class="total-column"></td>';
                         $rawDataDownloadGrid .= '%2C';
                     }
                     $r .= "</tr>";
                     $rawDataDownloadGrid .= '%0A';
                     $altRow = !$altRow;
                 }
             }
             foreach ($summaryArray as $seriesID => $summaryRow) {
                 // use the same row headings as the summary table.
                 if (!empty($seriesLabels[$seriesID])) {
                     $total = 0;
                     // row total
                     $r .= "<tr class=\"datarow " . ($altRow ? $options['altRowClass'] : '') . "\">";
                     $r .= '<td>' . $seriesLabels[$seriesID] . '</td>';
                     $rawDataDownloadGrid .= '"' . $seriesLabels[$seriesID] . '"';
                     foreach ($rawArray as $date => $rawColumn) {
                         if (isset($rawColumn['counts'][$seriesID])) {
                             $r .= '<td>' . $rawColumn['counts'][$seriesID] . '</td>';
                             $total += $rawColumn['counts'][$seriesID];
                             $rawDataDownloadGrid .= '%2C' . $rawColumn['counts'][$seriesID];
                             $locations = array();
                             if (count($rawColumn['samples']) > 0) {
                                 foreach ($rawColumn['samples'] as $sample) {
                                     $locations[$sample['location_name']] = true;
                                 }
                             }
                             $this_date = date_create(str_replace('/', '-', $rawColumn['date']));
                             // prevents day/month ordering issues
                             $rawDataDownloadList .= '"' . implode(': ', array_keys($locations)) . '"' . ($options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number' ? '%2C' . $rawColumn['weekno'] : '') . '%2C' . $this_date->format('d/m/Y') . '%2C"' . $seriesLabels[$seriesID] . '"%2C' . $rawColumn['counts'][$seriesID] . '%0A';
                         } else {
                             $r .= '<td></td>';
                             $rawDataDownloadGrid .= '%2C';
                         }
                     }
                     if ($options['includeTableTotalColumn']) {
                         $r .= '<td class="total-column">' . $total . '</td>';
                         $rawDataDownloadGrid .= '%2C' . $total;
                     }
                     $r .= "</tr>";
                     $rawDataDownloadGrid .= '%0A';
                     $altRow = !$altRow;
                 }
             }
             if ($options['includeTableTotalRow']) {
                 $r .= '<tr class="totalrow"><td>Total</td>' . $rawTotalRow . ($options['includeTableTotalColumn'] ? '<td>' . $rawGrandTotal . '</td>' : '') . '</tr>';
                 $rawDataDownloadGrid .= 'Total' . $rawDataDownloadGridTotalRow . ($options['includeTableTotalColumn'] ? '%2C' . $rawGrandTotal : '') . '%0A';
             }
             $r .= "</tbody></table>\n";
         }
         $summaryDataDownloadGrid = "";
         $estimateDataDownloadGrid = "";
         $r .= "\n<table id=\"" . $options['tableID'] . "\" class=\"" . $options['tableClass'] . "\" style=\"" . ($format['table']['display'] ? '' : 'display:none;') . "\">";
         $r .= "\n<thead class=\"{$thClass}\">";
         if ($options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number') {
             $r .= '<tr><td>Week</td>' . $tableNumberHeaderRow . ($options['includeTableTotalColumn'] ? ($options['includeSummaryData'] ? '<td>Total</td>' : '') . ($options['includeEstimatesData'] ? '<td class="estimates">Total with<br />estimates</td>' : '') : '') . '</tr>';
             $summaryDataDownloadGrid .= 'Week' . $downloadNumberHeaderRow . ($options['includeTableTotalColumn'] ? $options['includeSummaryData'] ? '%2CTotal' : '' : '') . '%0A';
         }
         if ($options['tableHeaders'] != 'number') {
             $r .= '<tr><td>' . lang::get('Date') . '</td>' . $tableDateHeaderRow . ($options['includeTableTotalColumn'] ? $options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number' ? $options['includeSummaryData'] && $options['includeEstimatesData'] ? '<td></td><td class="estimates"></td>' : '<td ' . ($options['includeEstimatesData'] ? 'class="estimates"' : '') . '></td>' : ($options['includeSummaryData'] ? '<td>Total</td>' : '') . ($options['includeEstimatesData'] ? '<td>Total with<br />estimates</td>' : '') : '') . '</tr>';
             $summaryDataDownloadGrid .= lang::get('Date') . $downloadDateHeaderRow . ($options['includeTableTotalColumn'] ? $options['tableHeaders'] == 'both' || $options['tableHeaders'] == 'number' ? '%2C' : '%2CTotal' : '') . '%0A';
         }
         $estimateDataDownloadGrid = $summaryDataDownloadGrid;
         $r .= "</thead>\n";
         $r .= "<tbody>\n";
         $altRow = false;
         $grandTotal = 0;
         $totalRow = array();
         $estimatesGrandTotal = 0;
         $totalEstimatesRow = array();
         for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
             $totalRow[$i] = 0;
             $totalEstimatesRow[$i] = 0;
         }
         foreach ($summaryArray as $seriesID => $summaryRow) {
             // skip rows with no labels, caused by report left joins to fill in all date columns even if no records
             if (!empty($seriesLabels[$seriesID])) {
                 $total = 0;
                 // row total
                 $estimatesTotal = 0;
                 // row total
                 $r .= "<tr class=\"datarow " . ($altRow ? $options['altRowClass'] : '') . "\">";
                 $r .= '<td>' . $seriesLabels[$seriesID] . '</td>';
                 $summaryDataDownloadGrid .= '"' . $seriesLabels[$seriesID] . '"';
                 $estimateDataDownloadGrid .= '"' . $seriesLabels[$seriesID] . '"';
                 for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
                     $r .= '<td>';
                     $summaryDataDownloadGrid .= '%2C';
                     $estimateDataDownloadGrid .= '%2C';
                     if (isset($summaryRow[$i])) {
                         $summaryValue = $summaryRow[$i]['forcedZero'] ? 0 : ($summaryRow[$i]['hasData'] ? $summaryRow[$i]['summary'] : '');
                         $class = '';
                         $estimatesClass = '';
                         if ($summaryValue !== '' && $options['includeSummaryData']) {
                             $class = ($options['includeEstimatesData'] && $summaryRow[$i]['hasEstimates'] && $summaryRow[$i]['estimates'] !== $summaryValue ? 'summary' : '') . ($summaryRow[$i]['forcedZero'] && $options['highlightEstimates'] ? ' forcedZero' : '');
                         }
                         if ($options['includeEstimatesData']) {
                             $estimatesClass = ($options['includeSummaryData'] ? 'estimates' : '') . ($options['highlightEstimates'] ? ' highlight-estimates' : '');
                         }
                         $summaryDataDownloadGrid .= $summaryValue;
                         if ($summaryRow[$i]['hasEstimates'] || $summaryRow[$i]['forcedZero']) {
                             $estimateDataDownloadGrid .= $summaryRow[$i]['estimates'];
                         }
                         if ($options['includeSummaryData'] && $summaryValue !== '') {
                             if ($class == '') {
                                 $r .= $summaryValue;
                             } else {
                                 $r .= '<span class="' . $class . '">' . $summaryValue . '</span>';
                             }
                         }
                         if (!$options['includeSummaryData'] || $options['includeEstimatesData'] && $summaryRow[$i]['hasEstimates'] && $summaryRow[$i]['estimates'] !== $summaryValue) {
                             $r .= '<span class="' . $estimatesClass . '">' . $summaryRow[$i]['estimates'] . '</span>';
                         }
                         if ($summaryValue !== '' && $summaryValue !== 0) {
                             $total += $summaryValue;
                             $totalRow[$i] += $summaryValue;
                             $grandTotal += $summaryValue;
                         }
                         $estimatesTotal += $summaryRow[$i]['estimates'];
                         $totalEstimatesRow[$i] += $summaryRow[$i]['estimates'];
                         $estimatesGrandTotal += $summaryRow[$i]['estimates'];
                     }
                     // else absolutely nothing - so leave blank.
                     $r .= '</td>';
                 }
                 if ($options['includeTableTotalColumn']) {
                     if ($options['includeSummaryData']) {
                         $r .= '<td class="total-column">' . $total . '</td>';
                         $summaryDataDownloadGrid .= '%2C' . $total;
                     }
                     if ($options['includeEstimatesData']) {
                         $r .= '<td class="total-column estimates">' . $estimatesTotal . '</td>';
                         $estimateDataDownloadGrid .= '%2C' . $estimatesTotal;
                     }
                 }
                 $r .= "</tr>";
                 $summaryDataDownloadGrid .= '%0A';
                 $estimateDataDownloadGrid .= '%0A';
                 $altRow = !$altRow;
             }
         }
         if ($options['includeTableTotalRow']) {
             if ($options['includeSummaryData']) {
                 $r .= "<tr class=\"totalrow\"><td>" . lang::get('Total (Summary)') . '</td>';
                 $summaryDataDownloadGrid .= '"' . lang::get('Total (Summary)') . '"';
                 for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
                     $r .= '<td>' . $totalRow[$i] . '</td>';
                     $summaryDataDownloadGrid .= '%2C' . $totalRow[$i];
                 }
                 if ($options['includeTableTotalColumn']) {
                     $r .= '<td class="total-column grand-total">' . $grandTotal . '</td>' . ($options['includeEstimatesData'] ? '<td class="estimates"></td>' : '');
                     $summaryDataDownloadGrid .= '%2C' . $grandTotal;
                 }
                 $r .= "</tr>";
                 $summaryDataDownloadGrid .= '%0A';
             }
             if ($options['includeEstimatesData']) {
                 $r .= "<tr class=\"totalrow estimates\"><td>" . lang::get('Total inc Estimates') . '</td>';
                 $estimateDataDownloadGrid .= '"' . lang::get('Total') . '"';
                 for ($i = $minWeekNo; $i <= $maxWeekNo; $i++) {
                     $r .= '<td>' . $totalEstimatesRow[$i] . '</td>';
                     $estimateDataDownloadGrid .= '%2C' . $totalEstimatesRow[$i];
                 }
                 if ($options['includeTableTotalColumn']) {
                     $r .= ($options['includeSummaryData'] ? '<td></td>' : '') . '<td class="total-column grand-total estimates">' . $estimatesGrandTotal . '</td>';
                     $estimateDataDownloadGrid .= '%2C' . $estimatesGrandTotal;
                 }
                 $r .= "</tr>";
                 $estimateDataDownloadGrid .= '%0A';
             }
         }
         $r .= "</tbody></table>\n";
         $r .= "</div>";
         $downloads = "";
         if ($options['includeRawData']) {
             if ($options['includeRawGridDownload']) {
                 $downloads .= '<th><a download="' . $options['downloadFilePrefix'] . 'rawDataGrid.csv" href="data:application/csv;charset=utf-8,' . str_replace(array(' ', '"'), array('%20', '%22'), $rawDataDownloadGrid) . '"><button type="button">Raw Grid Data</button></a></th>' . "\n";
             }
             if ($options['includeRawListDownload']) {
                 $downloads .= '<th><a download="' . $options['downloadFilePrefix'] . 'rawDataList.csv" href="data:application/csv;charset=utf-8,' . str_replace(array(' ', '"'), array('%20', '%22'), $rawDataDownloadList) . '"><button type="button">Raw List Data</button></a></th>' . "\n";
             }
         }
         if ($options['includeSummaryData'] && $options['includeSummaryGridDownload']) {
             $downloads .= '<th><a download="' . $options['downloadFilePrefix'] . 'summaryDataGrid.csv" href="data:application/csv;charset=utf-8,' . str_replace(array(' ', '"'), array('%20', '%22'), $summaryDataDownloadGrid) . '"><button type="button">Summary Grid Data</button></a></th>' . "\n";
         }
         if ($options['includeEstimatesData'] && $options['includeEstimatesGridDownload']) {
             $downloads .= '<th><a download="' . $options['downloadFilePrefix'] . 'estimateDataGrid.csv" href="data:application/csv;charset=utf-8,' . str_replace(array(' ', '"'), array('%20', '%22'), $estimateDataDownloadGrid) . '"><button type="button">Estimate Grid Data</button></a></th>' . "\n";
         }
         if (($options['includeSummaryData'] || $options['includeEstimatesData']) && $options['includeListDownload']) {
             $downloads .= '<th><a download="' . $options['downloadFilePrefix'] . 'dataList.csv" href="data:application/csv;charset=utf-8,' . str_replace(array(' ', '"'), array('%20', '%22'), $downloadList) . '"><button type="button">List Data</button></a></th>' . "\n";
         }
         //      $r .= '<br/><table id="downloads-table" class="ui-widget ui-widget-content ui-corner-all downloads-table" '.($downloads == '' ? 'style="display:none"' : '').'><thead class="ui-widget-header"><tr>'.
         $r .= '<br/><table id="downloads-table" class="ui-widget ui-widget-content ui-corner-all downloads-table" ><thead class="ui-widget-header"><tr>' . ($downloads == '' ? '' : '<th class="downloads-table-label">Downloads</th>' . $downloads) . "</tr></thead></table>\n";
         $warnings .= '<span style="display:none;">Output table complete : ' . date(DATE_ATOM) . '</span>' . "\n";
     }
     if (count($summaryArray) == 0) {
         $r .= '<p>' . lang::get('No data returned for this period.') . '</p>';
     }
     $warnings .= '<span style="display:none;">Finish report_calendar_summary : ' . date(DATE_ATOM) . '</span>' . "\n";
     return $warnings . $r;
 }