<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id: wikiplugin_pivottable.php 67287 2018-08-27 23:16:23Z jyhem $

function wikiplugin_pivottable_info()
{
	return [
		'name' => tr('Pivot table'),
		'description' => tr('Create and display data in pivot table for reporting'),
		'prefs' => ['wikiplugin_pivottable'],
		'body' => tra('Leave one space in the box below to allow easier editing of current values with the plugin popup helper later on'),
		'validate' => 'all',
		'format' => 'html',
		'iconname' => 'table',
		'introduced' => '16.1',
		'params' => [
			'data' => [
				'name' => tr('Data source'),
				'description' => tr("For example 'tracker:1' or 'activitystream'"),
				'required' => true,
				'default' => 0,
				'filter' => 'text',
				'profile_reference' => 'tracker',
				'separator' => ':',
			],
			'overridePermissions' => [
				'name' => tra('Override item permissions'),
				'description' => tra('Return all tracker items ignoring permissions to view the corresponding items.'),
				'since' => '18.1',
				'required' => false,
				'filter' => 'alpha',
				'default' => 'n',
				'options' => [
					['text' => '', 'value' => ''],
					['text' => tra('Yes'), 'value' => 'y'],
					['text' => tra('No'), 'value' => 'n']
				]
			],
			'width' => [
				'required' => false,
				'name' => tra('Width'),
				'description' => tr('Width of pivot table. Units: % or px.'),
				'since' => '',
				'filter' => 'word',
				'default' => '100%',
			],
			'height' => [
				'required' => false,
				'name' => tra('Height'),
				'description' => tr('Height of pivot table. Units: px'),
				'since' => '',
				'filter' => 'word',
				'default' => '400px',
			],
			'rows' => [
				'required' => false,
				'name' => tra('Pivot table Rows'),
				'description' => tr('Which field or fields to use as table rows. Leaving blank will remove grouping by table rows. ') . ' ' . tr('Use permanentNames in case of tracker fields.') . ' ' . tr('Separated by colon (:) if more than one.'),
				'since' => '',
				'filter' => 'text',
				'default' => '',
				'profile_reference' => 'tracker_field',
				'separator' => ':',
			],
			'cols' => [
				'required' => false,
				'name' => tra('Pivot table Columns'),
				'description' => tr('Which field or fields to use as table columns. Leaving blank will use the first available field.') . ' ' . tr('Use permanentNames in case of tracker fields.') . ' ' . tr('Separated by colon (:) if more than one.'),
				'since' => '',
				'filter' => 'text',
				'default' => '',
				'profile_reference' => 'tracker_field',
				'separator' => ':',
			],
			'heatmapDomain' => [
				'required' => false,
				'name' => tra('Values used to decide what heatmapColors to use.'),
				'description' => tr(''),
				'since' => '17',
				'filter' => 'text',
				'default' => '',
				'separator' => ':',
			],
			'heatmapColors' => [
				'required' => false,
				'name' => tra('Color for each heatmapDomain value.'),
				'description' => tr(''),
				'since' => '17',
				'filter' => 'text',
				'default' => '',
				'separator' => ':',
			],
			'rendererName' => [
				'name' => tr('Renderer Name'),
				'description' => tr('Display format of data'),
				'since' => '',
				'required' => false,
				'filter' => 'text',
				'default' => 'Table',
				'options' => [
					['text' => 'Table', 'value' => 'Table'],
					['text' => tra('Table Barchart'), 'value' => 'Table Barchart'],
					['text' => tra('Heatmap'), 'value' => 'Heatmap'],
					['text' => tra('Row Heatmap'), 'value' => 'Row Heatmap'],
					['text' => tra('Col Heatmap'), 'value' => 'Col Heatmap'],
					['text' => tra('Line Chart'), 'value' => 'Line Chart'],
					['text' => tra('Bar Chart'), 'value' => 'Bar Chart'],
					['text' => tra('Stacked Bar Chart'), 'value' => 'Stacked Bar Chart'],
					['text' => tra('Area Chart'), 'value' => 'Area Chart'],
					['text' => tra('Scatter Chart'), 'value' => 'Scatter Chart'],
					['text' => tra('Treemap'), 'value' => 'Treemap']
				]
			],
			'aggregatorName' => [
				'name' => tr('Aggregator Name'),
				'description' => tr('Function to apply on the numeric values from the variables selected.'),
				'since' => '',
				'required' => false,
				'filter' => 'text',
				'default' => 'Count',
				'options' => [
					['text' => 'Count', 'value' => 'Count'],
					['text' => tra('Count Unique Values'), 'value' => 'Count Unique Values'],
					['text' => tra('List Unique Values'), 'value' => 'List Unique Values'],
					['text' => tra('Sum'), 'value' => 'Sum'],
					['text' => tra('Integer Sum'), 'value' => 'Integer Sum'],
					['text' => tra('Average'), 'value' => 'Average'],
					['text' => tra('Minimum'), 'value' => 'Minimum'],
					['text' => tra('Maximum'), 'value' => 'Maximum'],
					['text' => tra('Sum over Sum'), 'value' => 'Sum over Sum'],
					['text' => tra('80% Upper Bound'), 'value' => '80% Upper Bound'],
					['text' => tra('80% Lower Bound'), 'value' => '80% Lower Bound'],
					['text' => tra('Sum as Fraction of Total'), 'value' => 'Sum as Fraction of Total'],
					['text' => tra('Sum as Fraction of Rows'), 'value' => 'Sum as Fraction of Rows'],
					['text' => tra('Sum as Fraction of Columns'), 'value' => 'Sum as Fraction of Columns'],
					['text' => tra('Count as Fraction of Total'), 'value' => 'Count as Fraction of Total'],
					['text' => tra('Count as Fraction of Rows'), 'value' => 'Count as Fraction of Rows'],
					['text' => tra('Count as Fraction of Columns'), 'value' => 'Count as Fraction of Columns']
				]
			],
			'vals' => [
				'name' => tr('Values'),
				'description' => tr('Variable with numeric values or tracker field permNames, on which the formula from the aggregator is applied. It can be left empty if aggregator is related to Counts.') . ' ' . tr('Use permanentNames in case of tracker fields, separated by : in case of multiple fields function.'),
				'since' => '',
				'required' => false,
				'filter' => 'text',
				'profile_reference' => 'tracker_field',
				'separator' => ':',
			],
			'inclusions' => [
				'name' => tr('Inclusions'),
				'description' => tr('Filter values for fields in rows or columns. Contains JSON encoded object of arrays of strings.'),
				'since' => '',
				'required' => false,
				'filter' => 'text',
			],
			'menuLimit' => [
				'name' => tr('Filter list limit'),
				'description' => tr('Pivottable menuLimit option override - number of entries to consider the menu list too big when filtering on a particular column or row.'),
				'since' => '16.2',
				'required' => false,
				'filter' => 'digits',
			],
			'aggregateDetails' => [
				'name' => tr('Aggregate details'),
				'description' => tr('When enabled, clicking a table cell will popup all items that were aggregated into that cell. Specify the name of the field or fields to use to display the details separated by colon. Enabled by default. To disable, set contents to an empty string.'),
				'since' => '16.2',
				'required' => false,
				'filter' => 'text',
				'profile_reference' => 'tracker_field',
				'separator' => ':',
			],
			'highlightMine' => [
				'name' => tra('Highlight my items'),
				'description' => tra('Highlight owned items\' values in Charts.'),
				'since' => '16.3',
				'required' => false,
				'filter' => 'alpha',
				'default' => 'n',
				'options' => [
					['text' => '', 'value' => ''],
					['text' => tra('Yes'), 'value' => 'y'],
					['text' => tra('No'), 'value' => 'n']
				]
			],
			'highlightGroup' => [
				'name' => tra('Highlight my group items'),
				'description' => tra('Highlight items\' values belonging to one of my groups in Charts.'),
				'since' => '16.3',
				'required' => false,
				'filter' => 'alpha',
				'default' => 'n',
				'options' => [
					['text' => '', 'value' => ''],
					['text' => tra('Yes'), 'value' => 'y'],
					['text' => tra('No'), 'value' => 'n']
				]
			],
			'highlightGroupColors' => [
				'required' => false,
				'name' => tra('Color for each highlighted group items.'),
				'description' => tr(''),
				'since' => '18.1',
				'filter' => 'text',
				'default' => '',
				'separator' => ':',
			],
			'xAxisLabel' => [
				'name' => tr('xAxis label'),
				'description' => tr('Override label of horizontal axis when using Chart renderers.'),
				'since' => '16.3',
				'required' => false,
				'filter' => 'text',
			],
			'yAxisLabel' => [
				'name' => tr('yAxis label'),
				'description' => tr('Override label of vertical axis when using Chart renderers.'),
				'since' => '16.3',
				'required' => false,
				'filter' => 'text',
			],
			'chartTitle' => [
				'name' => tr('Chart title'),
				'description' => tr('Override title when using Chart renderers.'),
				'since' => '16.3',
				'required' => false,
				'filter' => 'text',
			],
			'chartHoverBar' => [
				'name' => tr('Chart hover bar'),
				'description' => tr('Display the Chart hover bar or not.'),
				'since' => '16.3',
				'required' => false,
				'filter' => 'alpha',
				'default' => 'y',
				'options' => [
					['text' => tra('Yes'), 'value' => 'y'],
					['text' => tra('No'), 'value' => 'n']
				]
			],
			'translate' => [
				'name' => tr('Translate displayed data'),
				'description' => tr('Use translated data values for calculations and display.') . ' ' . tr('Default value: No'),
				'since' => '18.3',
				'required' => false,
				'filter' => 'alpha',
				'default' => 'n',
				'options' => [
					['text' => '', 'value' => ''],
					['text' => tra('No'), 'value' => 'n'],
					['text' => tra('Yes'), 'value' => 'y']
				]
			]
		],
	];
}

function wikiplugin_pivottable($data, $params)
{

	//included globals for permission check
	global $prefs, $page, $wikiplugin_included_page, $user;

	//checking if vendor files are present
	if (! file_exists('vendor_bundled/vendor/nicolaskruchten/pivottable/')) {
		return WikiParser_PluginOutput::internalError(tr('Missing required files, please make sure plugin files are installed at vendor_bundled/vendor/nicolaskruchten/pivottable. <br/><br /> To install, please run composer or download from following url:<a href="https://github.com/nicolaskruchten/pivottable/archive/master.zip" target="_blank">https://github.com/nicolaskruchten/pivottable/archive/master.zip</a>'));
	}

	static $id = 0;
	$id++;

	$headerlib = TikiLib::lib('header');
	$headerlib->add_cssfile('vendor_bundled/vendor/nicolaskruchten/pivottable/dist/pivot.css');
	$headerlib->add_jsfile('vendor_bundled/vendor/nicolaskruchten/pivottable/dist/pivot.js', true);
	$headerlib->add_jsfile('vendor_bundled/vendor/plotly/plotly.js/dist/plotly-cartesian.min.js', true);
	$headerlib->add_jsfile('vendor_bundled/vendor/nagarajanchinnasamy/subtotal/dist/subtotal.min.js', true);
	$headerlib->add_jsfile('lib/jquery_tiki/wikiplugin-pivottable.js', true);

	$lang = substr($prefs['site_language'], 0, 2);
	if (file_exists('vendor_bundled/vendor/nicolaskruchten/pivottable/dist/pivot.' . $lang . '.js')) {
		$headerlib->add_jsfile('vendor_bundled/vendor/nicolaskruchten/pivottable/dist/pivot.' . $lang . '.js', true);
	}

	$translate = (! empty($params['translate']) && $params['translate'] == 'y') ? true : false;

	$smarty = TikiLib::lib('smarty');
	$smarty->assign('lang', $lang);


	//checking data type
	if (empty($params['data']) || ! is_array($params['data'])) {
		return WikiParser_PluginOutput::internalError(tr('Missing data parameter with format: source:ID, e.g. tracker:1'));
	}
	$dataType = $params['data'][0];
	if ($dataType !== 'activitystream' && $dataType !== 'tracker') {
		return WikiParser_PluginOutput::internalError(tr('Error data parameter'));
	}

	if (! empty($params['rendererName'])) {
		$rendererName = $params['rendererName'];
	} else {
		$rendererName = "Table";
	}

	if (! empty($params['aggregatorName'])) {
		$aggregatorName = $params['aggregatorName'];
	} else {
		$aggregatorName = "Count";
	}

	if (! empty($params['width'])) {
		$width = $params['width'];
	} else {
		$width = "100%";
	}

	if (! empty($params['height'])) {
		$height = $params['height'];
	} else {
		$height = "1000px";
	}

	if ($dataType === "tracker") {
		$trackerId = $params['data'][1];
		$definition = Tracker_Definition::get($trackerId);
		if (! $definition) {
			return WikiParser_PluginOutput::userError(tr('Tracker data source not found.'));
		}

		$perms = Perms::get(['type' => 'tracker', 'object' => $trackerId]);

		$fields = $definition->getFields();

		if (! $perms->admin_trackers && $params['overridePermissions'] !== 'y') {
			$hasFieldPermissions = false;
			foreach ($fields as $key => $field) {
				$isHidden = $field['isHidden'];
				$visibleBy = $field['visibleBy'];

				if ($isHidden != 'n' || ! empty($visibleBy)) {
					$hasFieldPermissions = true;
				}

				if ($isHidden == 'c') {
					// creators can see their own items coming from the search index
				} elseif ($isHidden == 'y') {
					// Visible by administrator only
					unset($fields[$key]);
				} elseif (! empty($visibleBy)) {
					// Permission based on visibleBy apply
					$commonGroups = array_intersect($visibleBy, $perms->getGroups());
					if (count($commonGroups) == 0) {
						unset($fields[$key]);
					}
				}
			}
			if (! $hasFieldPermissions && ! $perms->view_trackers && ! $definition->isEnabled('userCanSeeOwn') && ! $definition->isEnabled('writerCanModify')) {
				return WikiParser_PluginOutput::userError(tr('You do not have rights to view tracker data.'));
			}
		}

		$fields[] = [
			'name' => 'object_id',
			'permName' => 'object_id',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'object_type',
			'permName' => 'object_type',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'creation_date',
			'permName' => 'creation_date',
			'type' => 'f'
		];
		$fields[] = [
			'name' => 'modification_date',
			'permName' => 'modification_date',
			'type' => 'f'
		];
		$fields[] = [
			'name' => 'tracker_status',
			'permName' => 'tracker_status',
			'type' => 't'
		];

		$heatmapParams = [];
		if ($rendererName === 'Heatmap') {
			$validConfig = ! (empty($params['heatmapDomain']) && empty($params['heatmapColors']))
				&& is_array($params['heatmapDomain'])
				&& is_array($params['heatmapColors'])
				&& count($params['heatmapDomain']) === count($params['heatmapColors']);

			if ($validConfig) {
				$heatmapParams = [
					'domain' => array_map(floatval, $params['heatmapDomain']),
					'colors' => $params['heatmapColors']
				];
			}

			unset($validConfig);
		}

		$query = new Search_Query;
		$query->filterType('trackeritem');
		$query->filterContent($trackerId, 'tracker_id');

		$unifiedsearchlib = TikiLib::lib('unifiedsearch');
		if (!empty($params['overridePermissions']) && $params['overridePermissions'] === 'y') {
			$unifiedsearchlib->initQueryBase($query);
			$unifiedsearchlib->initQueryPresentation($query);
		} else {
			$unifiedsearchlib->initQuery($query);
		}

		$matches = WikiParser_PluginMatcher::match($data);

		$builder = new Search_Query_WikiBuilder($query);
		$builder->apply($matches);

		if (! $index = $unifiedsearchlib->getIndex()) {
			return WikiParser_PluginOutput::userError(tr('Unified search index not found.'));
		}

		$result = [];
		foreach ($query->scroll($index) as $row) {
			$result[] = $row;
		}
		$result = Search_ResultSet::create($result);
		$result->setId('wppivottable-' . $id);

		$resultBuilder = new Search_ResultSet_WikiBuilder($result);
		$resultBuilder->apply($matches);

		$columnsListed = false;
		$derivedAttributes = [];

		foreach ($matches as $match) {
			if ($match->getName() == 'display' || $match->getName() == 'column') {
				$columnsListed = true;
			} elseif ($match->getName() == 'derivedattribute') {
				if (preg_match('/name="([^"]+)"/', $match->getArguments(), $match_name)
					&& preg_match('/function="([^"]+)"/', $match->getArguments(), $match_function)
					&& preg_match('/parameters="([^"]*)"/', $match->getArguments(), $match_parameters)) {
					$derivedattr_name = $match_name[1];
					$function_name = $match_function[1];
					$function_params = explode(':', $match_parameters[1]);

					if (empty($function_params)) {
						$function_params = '';
					} else {
						$function_params = '"' . implode('","', $function_params) . '"';
					}

					$derivedAttributes[] = sprintf('"%s": %s(%s)', $derivedattr_name, $function_name, $function_params);
				}
			}
		}

		if ($columnsListed) {
			$data .= '{display name="object_id"}{display name="object_type"}';
			$plugin = new Search_Formatter_Plugin_ArrayTemplate($data);
			$usedFields = array_keys($plugin->getFields());
			foreach ($fields as $key => $field) {
				if (! in_array('tracker_field_' . $field['permName'], $usedFields)
					&& ! in_array($field['permName'], $usedFields) ) {
					unset($fields[$key]);
				}
			}
			$fields = array_values($fields);
			$plugin->setFieldPermNames($fields);
		} else {
			$plugin = new Search_Formatter_Plugin_ArrayTemplate(implode("", array_map(
				function ($f) {
					if (in_array($f['permName'], ['object_id', 'object_type', 'creation_date', 'modification_date', 'tracker_status'])) {
						return '{display name="' . $f['permName'] . '" default=" "}';
					} else {
						return '{display name="tracker_field_' . $f['permName'] . '" default=" "}';
					}
				},
				$fields
			)));
			$plugin->setFieldPermNames($fields);
		}
	}

	if ($dataType === "activitystream") {
		$unifiedsearchlib = TikiLib::lib('unifiedsearch');
		$query = new Search_Query;
		$unifiedsearchlib->initQuery($query);
		$query->filterType('activity');

		if ($params['overridePermissions'] === 'y') {
			$unifiedsearchlib->initQueryBase($query);
			$unifiedsearchlib->initQueryPresentation($query);
		} else {
			$unifiedsearchlib->initQuery($query);
		}

		$matches = WikiParser_PluginMatcher::match($data);

		$builder = new Search_Query_WikiBuilder($query);
		$builder->enableAggregate();
		$builder->apply($matches);

		$query->setOrder('modification_date_desc');

		if (! $index = $unifiedsearchlib->getIndex()) {
			throw new Services_Exception_NotAvailable(tr('Activity stream currently unavailable.'));
		}

		$result = [];
		foreach ($query->scroll($index) as $row) {
			$result[] = $row;
		}
		$result = Search_ResultSet::create($result);
		$result->setId('wppivottable-' . $id);

		$paginationArguments = $builder->getPaginationArguments();

		$resultBuilder = new Search_ResultSet_WikiBuilder($result);
		$resultBuilder->setPaginationArguments($paginationArguments);
		$resultBuilder->apply($matches);

		$fields = [];
		$fields[] = [
			'name' => 'object',
			'permName' => 'object',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'object_id',
			'permName' => 'object_id',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'object_type',
			'permName' => 'object_type',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'modification_date',
			'permName' => 'modification_date',
			'type' => 'f'
		];

		$fields[] = [
			'name' => 'user',
			'permName' => 'user',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'event_type',
			'permName' => 'event_type',
			'type' => 't'
		];

		$fields[] = [
			'name' => 'type',
			'permName' => 'type',
			'type' => 't'
		];

		try {
			$plugin = new Search_Formatter_Plugin_ArrayTemplate(implode("", array_map(
				function ($f) {
					$activityArray = [
						'object',
						'object_id',
						'object_type',
						'modification_date',
						'user',
						'event_type',
						'type'
					];
					if (in_array($f['permName'], $activityArray)) {
						return '{display name="' . $f['permName'] . '" default=" "}';
					}
				},
				$fields
			)));
			$plugin->setFieldPermNames($fields);
		} catch (SmartyException $e) {
			throw new Services_Exception_NotAvailable($e->getMessage());
		}
	}

	$builder = new Search_Formatter_Builder;
	$builder->setId('wppivottable-' . $id);
	$builder->setCount($result->count());
	$builder->apply($matches);
	$builder->setFormatterPlugin($plugin);

	$formatter = $builder->getFormatter();
	$entries = $formatter->getPopulatedList($result, false);
	$entries = $plugin->renderEntries($entries);

	$pivotData = [];
	foreach ($entries as $entry) {
		$row = [];
		foreach ($entry as $fieldName => $value) {
			if ($entry['object_type'] != 'activity' && $field = $definition->getFieldFromPermName($fieldName)) {
				// Actual data values
				if ($translate) {
					$row[$field['name']] = tra($value);
				} else {
					$row[$field['name']] = $value;
				}
			} else {
				// predefined fields (created date, lastmod, etc.)
				$row[$fieldName] = $value;
			}
		}
		$pivotData[] = $row;
	}

	//translating permName to field name for columns and rows
	$cols = [];
	if (! empty($params['cols'])) {
		foreach ($params['cols'] as $colName) {
			if ($params['data'][0] !== 'activitystream' && $field = $definition->getFieldFromPermName(trim($colName))) {
				$cols[] = $field['name'];
			} else {
				$cols[] = $colName;
			}
		}
	} elseif (! empty($fields) && empty($params['rows'])) {
		$cols[] = $fields[0]['name'];
	}

	$rows = [];
	if (! empty($params['rows'])) {
		foreach ($params['rows'] as $rowName) {
			if ($params['data'][0] !== 'activitystream' && $field = $definition->getFieldFromPermName(trim($rowName))) {
				$rows[] = $field['name'];
			} else {
				$rows[] = $rowName;
			}
		}
	}

	$vals = [];
	if (! empty($params['vals'])) {
		foreach ($params['vals'] as $valName) {
			if ($params['data'][0] !== 'activitystream' && $field = $definition->getFieldFromPermName(trim($valName))) {
				$vals[] = $field['name'];
			} else {
				$vals[] = $valName;
			}
		}
	}

	$inclusions = ! empty($params['inclusions']) ? $params['inclusions'] : '{}';

	// parsing array to hold permNames mapped with field names for save button
	// and list of date fields for custom sorting
	$fieldsArr = [];
	$dateFields = [];
	foreach ($fields as $field) {
		$fieldsArr[$field['name']] = $field['permName'];
		if ($field['type'] == 'f') {
			$dateFields[] = $field['name'];
		}
	}

	$smarty->loadPlugin('smarty_function_object_link');

	if (! isset($params['aggregateDetails'])) {
		if (isset($fields[2])) {
			$params['aggregateDetails'][] = $fields[2]['permName'];
		} elseif (isset($fields[0])) {
			$params['aggregateDetails'][] = $fields[0]['permName'];
		}
	}

	if (! empty($params['aggregateDetails']) && ! empty($params['aggregateDetails'][0])) {
		$aggregateDetails = [];
		foreach ($params['aggregateDetails'] as $fieldName) {
			if ($params['data'][0] != 'activitystream' && $field = $definition->getFieldFromPermName(trim($fieldName))) {
				$aggregateDetails[] = $field['name'];
			} else {
				$aggregateDetails[] = trim($fieldName);
			}
		}
		foreach ($pivotData as &$row) {
			$title = implode(' ', array_map(function ($field) use ($row) {
				return $row[$field];
			}, $aggregateDetails));
			$pivotLinkParams = [
				'type' => $row['object_type'],
				'id' => $row['object_id'],
				'title' => $title,    
			];
			if ($row['object_type'] === 'activity') {
				$pivotLinkParams = [
					'type' => $row['type'],
					'id' => $row['object'],
					'title' => $row['type'],
				];
			}
			$row['pivotLink'] = smarty_function_object_link($pivotLinkParams, $smarty->getEmptyInternalTemplate());
		}
	} else {
		$params['aggregateDetails'] = [];
	}

	$highlight = [];
	if (! empty($params['highlightMine']) && $params['highlightMine'] === 'y' && $params['data'][0] !== 'activitystream') {
		$ownerFields = array_map(function ($fieldId) use ($definition) {
			return $definition->getField($fieldId);
		}, $definition->getItemOwnerFields());
		foreach ($pivotData as $item) {
			foreach ($ownerFields as $ownerField) {
				$itemUsers = TikiLib::lib('trk')->parse_user_field(@$item[$ownerField['name']]);
				if (in_array($user, $itemUsers)) {
					$highlight[] = ['item' => $item];
					break;
				}
			}
		}
	}
	if (! empty($params['highlightGroup']) && $params['highlightGroup'] === 'y') {
		$groupField = null;
		foreach ($fields as $field) {
			if ($field['type'] == 'g') {
				$groupField = $field;
				break;
			}
		}
		if ($groupField) {
			$myGroups = TikiLib::lib('tiki')->get_user_groups($user);
			foreach ($pivotData as $item) {
				$group = @$item[$groupField['name']];
				if (in_array($group, $myGroups)) {
					$highlight[] = ['item' => $item, 'group' => $group];
				}
			}
			$groupColors = [];
			if ($prefs['feature_conditional_formatting'] === 'y') {
				$groupsInfo = TikiLib::lib('user')->get_group_info($myGroups);
				foreach ($groupsInfo as $groupInfo) {
					$groupColors[$groupInfo['groupName']] = $groupInfo['groupColor'];
				}
			}
			if (! empty($params['highlightGroupColors'])) {
				$index = 0;
				foreach ($myGroups as $group) {
					if (empty($params['highlightGroupColors'][$index])) {
						break;
					}
					$groupColors[$group] = $params['highlightGroupColors'][$index];
					$index++;
				}
			}
			if ($groupColors) {
				foreach ($highlight as &$row) {
					if (! empty($row['group'])) {
						$group = $row['group'];
						if ($group && ! empty($groupColors[$group])) {
							$row['color'] = $groupColors[$group];
						}
					}
				}
			}
		}
	}

	//checking if user can see edit button
	if (! empty($wikiplugin_included_page)) {
		$sourcepage = $wikiplugin_included_page;
	} else {
		$sourcepage = $page;
	}
	//checking if user has edit permissions on the wiki page using the current permission library to obey global/categ/object perms
	$objectperms = Perms::get([ 'type' => 'wiki page', 'object' => $sourcepage ]);
	if ($objectperms->edit) {
		$showControls = true;
	} else {
		$showControls = false;
	}

	$out = str_replace(['~np~', '~/np~'], '', $formatter->renderFilters());

	$smarty->assign('pivottable', [
		'id' => 'pivottable' . $id,
		'trows' => $rows,
		'tcolumns' => $cols,
		'dataSource' => $dataType == 'activitystream' ? $dataType : $dataType . ':' . $trackerId,
		'data' => $pivotData,
		'derivedAttributes' => $derivedAttributes,
		'rendererName' => $rendererName,
		'aggregatorName' => $aggregatorName,
		'vals' => $vals,
		'width' => $width,
		'height' => $height,
		'heatmapParams' => $heatmapParams,
		'showControls' => $showControls,
		'page' => $sourcepage,
		'fieldsArr' => $fieldsArr,
		'dateFields' => $dateFields,
		'inclusions' => $inclusions,
		'menuLimit' => empty($params['menuLimit']) ? null : $params['menuLimit'],
		'aggregateDetails' => implode(':', $params['aggregateDetails']),
		'highlight' => $highlight,
		'highlightMine' => empty($params['highlightMine']) ? null : $params['highlightMine'],
		'highlightGroup' => empty($params['highlightGroup']) ? null : $params['highlightGroup'],
		'xAxisLabel' => empty($params['xAxisLabel']) ? null : $params['xAxisLabel'],
		'yAxisLabel' => empty($params['yAxisLabel']) ? null : $params['yAxisLabel'],
		'chartTitle' => empty($params['chartTitle']) ? null : $params['chartTitle'],
		'chartHoverBar' => empty($params['chartHoverBar']) ? null : $params['chartHoverBar'],
		'translate' => empty($params['translate']) ? null : $params['translate'],
		'index' => $id
	]);

	$out .= $smarty->fetch('wiki-plugins/wikiplugin_pivottable.tpl');

	return $out;
}
