1<?php 2/** 3 * Matomo - free/libre analytics platform 4 * 5 * @link https://matomo.org 6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later 7 * 8 */ 9namespace Piwik; 10 11use Exception; 12use Piwik\API\Request; 13use Piwik\Container\StaticContainer; 14use Piwik\DataTable\Row; 15use Piwik\DataTable\Simple; 16use Piwik\Plugins\ImageGraph\API; 17 18/** 19 * A Report Renderer produces user friendly renderings of any given Piwik report. 20 * All new Renderers must be copied in ReportRenderer and added to the $availableReportRenderers. 21 */ 22abstract class ReportRenderer extends BaseFactory 23{ 24 const DEFAULT_REPORT_FONT_FAMILY = 'dejavusans'; 25 const REPORT_TEXT_COLOR = "13,13,13"; 26 const REPORT_TITLE_TEXT_COLOR = "13,13,13"; 27 const TABLE_HEADER_BG_COLOR = "255,255,255"; 28 const TABLE_HEADER_TEXT_COLOR = "13,13,13"; 29 const TABLE_HEADER_TEXT_TRANSFORM = "uppercase"; 30 const TABLE_HEADER_TEXT_WEIGHT = "normal"; 31 const TABLE_CELL_BORDER_COLOR = "217,217,217"; 32 const TABLE_BG_COLOR = "242,242,242"; 33 34 const HTML_FORMAT = 'html'; 35 const PDF_FORMAT = 'pdf'; 36 const CSV_FORMAT = 'csv'; 37 const TSV_FORMAT = 'tsv'; 38 39 protected $idSite = 'all'; 40 41 protected $report; 42 43 private static $availableReportRenderers = array( 44 self::PDF_FORMAT, 45 self::HTML_FORMAT, 46 self::CSV_FORMAT, 47 self::TSV_FORMAT, 48 ); 49 50 /** 51 * Sets the site id 52 * 53 * @param int $idSite 54 */ 55 public function setIdSite($idSite) 56 { 57 $this->idSite = $idSite; 58 } 59 60 public function setReport($report) 61 { 62 $this->report = $report; 63 } 64 65 protected static function getClassNameFromClassId($rendererType) 66 { 67 return 'Piwik\ReportRenderer\\' . self::normalizeRendererType($rendererType); 68 } 69 70 protected static function getInvalidClassIdExceptionMessage($rendererType) 71 { 72 return Piwik::translate( 73 'General_ExceptionInvalidReportRendererFormat', 74 array(self::normalizeRendererType($rendererType), implode(', ', self::$availableReportRenderers)) 75 ); 76 } 77 78 protected static function normalizeRendererType($rendererType) 79 { 80 return ucfirst(strtolower($rendererType)); 81 } 82 83 /** 84 * Initialize locale settings. 85 * If not called, locale settings defaults to 'en' 86 * 87 * @param string $locale 88 */ 89 abstract public function setLocale($locale); 90 91 /** 92 * Save rendering to disk 93 * 94 * @param string $filename without path & without format extension 95 * @return string path of file 96 */ 97 abstract public function sendToDisk($filename); 98 99 /** 100 * Send rendering to browser with a 'download file' prompt 101 * 102 * @param string $filename without path & without format extension 103 */ 104 abstract public function sendToBrowserDownload($filename); 105 106 /** 107 * Output rendering to browser 108 * 109 * @param string $filename without path & without format extension 110 */ 111 abstract public function sendToBrowserInline($filename); 112 113 /** 114 * Get rendered report 115 */ 116 abstract public function getRenderedReport(); 117 118 /** 119 * Generate the first page. 120 * 121 * @param string $reportTitle 122 * @param string $prettyDate formatted date 123 * @param string $description 124 * @param array $reportMetadata metadata for all reports 125 * @param array $segment segment applied to all reports 126 */ 127 abstract public function renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata, $segment); 128 129 /** 130 * Render the provided report. 131 * Multiple calls to this method before calling outputRendering appends each report content. 132 * 133 * @param array $processedReport @see API::getProcessedReport() 134 */ 135 abstract public function renderReport($processedReport); 136 137 /** 138 * Get report attachments, ex. graph images 139 * 140 * @param $report 141 * @param $processedReports 142 * @param $prettyDate 143 * @return array 144 */ 145 abstract public function getAttachments($report, $processedReports, $prettyDate); 146 147 /** 148 * Append $extension to $filename 149 * 150 * @static 151 * @param string $filename 152 * @param string $extension 153 * @return string filename with extension 154 */ 155 protected static function makeFilenameWithExtension($filename, $extension) 156 { 157 // the filename can be used in HTTP headers, remove new lines to prevent HTTP header injection 158 $filename = str_replace(array("\n", "\t"), " ", $filename); 159 160 return $filename . "." . $extension; 161 } 162 163 /** 164 * Return $filename with temp directory and delete file 165 * 166 * @static 167 * @param $filename 168 * @return string path of file in temp directory 169 */ 170 protected static function getOutputPath($filename) 171 { 172 $outputFilename = StaticContainer::get('path.tmp') . '/assets/' . $filename; 173 174 @chmod($outputFilename, 0600); 175 176 if(file_exists($outputFilename)){ 177 @unlink($outputFilename); 178 } 179 180 return $outputFilename; 181 } 182 183 protected static function writeFile($filename, $extension, $content) 184 { 185 $filename = self::makeFilenameWithExtension($filename, $extension); 186 $outputFilename = self::getOutputPath($filename); 187 188 $bytesWritten = file_put_contents($outputFilename, $content); 189 if ($bytesWritten === false) { 190 throw new Exception("ReportRenderer: Could not write to file '" . $outputFilename . "'."); 191 } 192 193 return $outputFilename; 194 } 195 196 protected static function sendToBrowser($filename, $extension, $contentType, $content) 197 { 198 $filename = ReportRenderer::makeFilenameWithExtension($filename, $extension); 199 200 ProxyHttp::overrideCacheControlHeaders(); 201 Common::sendHeader('Content-Description: File Transfer'); 202 Common::sendHeader('Content-Type: ' . $contentType); 203 Common::sendHeader('Content-Disposition: attachment; filename="' . str_replace('"', '\'', basename($filename)) . '";'); 204 Common::sendHeader('Content-Length: ' . strlen($content)); 205 206 echo $content; 207 } 208 209 protected static function inlineToBrowser($contentType, $content) 210 { 211 Common::sendHeader('Content-Type: ' . $contentType); 212 echo $content; 213 } 214 215 /** 216 * Convert a dimension-less report to a multi-row two-column data table 217 * 218 * @static 219 * @param $reportMetadata array 220 * @param $report DataTable 221 * @param $reportColumns array 222 * @return array DataTable $report & array $columns 223 */ 224 protected static function processTableFormat($reportMetadata, $report, $reportColumns) 225 { 226 $finalReport = $report; 227 if (empty($reportMetadata['dimension'])) { 228 $simpleReportMetrics = $report->getFirstRow(); 229 if ($simpleReportMetrics) { 230 $finalReport = new Simple(); 231 foreach ($simpleReportMetrics->getColumns() as $metricId => $metric) { 232 $newRow = new Row(); 233 $newRow->addColumn("label", $reportColumns[$metricId]); 234 $newRow->addColumn("value", $metric); 235 $finalReport->addRow($newRow); 236 } 237 } 238 239 $reportColumns = array( 240 'label' => Piwik::translate('General_Name'), 241 'value' => Piwik::translate('General_Value'), 242 ); 243 } 244 245 return array( 246 $finalReport, 247 $reportColumns, 248 ); 249 } 250 251 public static function getStaticGraph($reportMetadata, $width, $height, $evolution, $segment) 252 { 253 $imageGraphUrl = $reportMetadata['imageGraphUrl']; 254 255 if ($evolution && !empty($reportMetadata['imageGraphEvolutionUrl'])) { 256 $imageGraphUrl = $reportMetadata['imageGraphEvolutionUrl']; 257 } 258 259 $requestGraph = $imageGraphUrl . 260 '&outputType=' . API::GRAPH_OUTPUT_PHP . 261 '&format=original&serialize=0' . 262 '&filter_truncate=' . 263 '&width=' . $width . 264 '&height=' . $height . 265 ($segment != null ? '&segment=' . urlencode($segment['definition']) : ''); 266 267 $request = new Request($requestGraph); 268 269 try { 270 $imageGraph = $request->process(); 271 272 // Get image data as string 273 ob_start(); 274 imagepng($imageGraph); 275 $imageGraphData = ob_get_contents(); 276 ob_end_clean(); 277 imagedestroy($imageGraph); 278 279 return $imageGraphData; 280 } catch (Exception $e) { 281 throw new Exception("ImageGraph API returned an error: " . $e->getMessage() . "\n"); 282 } 283 } 284} 285