/** * 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; }
/** * 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+';\"> </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']] == '' ? ' ' : $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] : ' ') . '</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; }
/** * <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; }