1<?php 2/** 3 * 2007-2016 PrestaShop 4 * 5 * thirty bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA 6 * Copyright (C) 2017-2018 thirty bees 7 * 8 * NOTICE OF LICENSE 9 * 10 * This source file is subject to the Open Software License (OSL 3.0) 11 * that is bundled with this package in the file LICENSE.txt. 12 * It is also available through the world-wide-web at this URL: 13 * http://opensource.org/licenses/osl-3.0.php 14 * If you did not receive a copy of the license and are unable to 15 * obtain it through the world-wide-web, please send an email 16 * to license@thirtybees.com so we can send you a copy immediately. 17 * 18 * DISCLAIMER 19 * 20 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer 21 * versions in the future. If you wish to customize PrestaShop for your 22 * needs please refer to https://www.thirtybees.com for more information. 23 * 24 * @author thirty bees <contact@thirtybees.com> 25 * @author PrestaShop SA <contact@prestashop.com> 26 * @copyright 2017-2018 thirty bees 27 * @copyright 2007-2016 PrestaShop SA 28 * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) 29 * PrestaShop is an internationally registered trademark & property of PrestaShop SA 30 */ 31 32/** 33 * Class ModuleGraphCore 34 * 35 * @since 1.0.0 36 */ 37abstract class ModuleGraphCore extends Module 38{ 39 // @codingStandardsIgnoreStart 40 /** @var Employee $_employee */ 41 protected $_employee; 42 /** @var int[] graph data */ 43 protected $_values = []; 44 /** @var string[] graph legends (X axis) */ 45 protected $_legend = []; 46 /**@var string[] graph titles */ 47 protected $_titles = ['main' => null, 'x' => null, 'y' => null]; 48 /** @var ModuleGraphEngine graph engine */ 49 protected $_render; 50 // @codingStandardsIgnoreEnd 51 52 /** 53 * @param int $idEmployee 54 * 55 * @since 1.0.0 56 * @version 1.0.0 Initial version 57 */ 58 public function setEmployee($idEmployee) 59 { 60 $this->_employee = new Employee($idEmployee); 61 } 62 63 /** 64 * @param int $idLang 65 * 66 * @since 1.0.0 67 * @version 1.0.0 Initial version 68 */ 69 public function setLang($idLang) 70 { 71 $this->_id_lang = $idLang; 72 } 73 74 /** 75 * @param $layers 76 * @param bool $legend 77 * 78 * @since 1.0.0 79 * @version 1.0.0 Initial version 80 */ 81 protected function setDateGraph($layers, $legend = false) 82 { 83 // Get dates in a manageable format 84 $fromArray = getdate(strtotime($this->_employee->stats_date_from)); 85 $toArray = getdate(strtotime($this->_employee->stats_date_to)); 86 87 // If the granularity is inferior to 1 day 88 if ($this->_employee->stats_date_from == $this->_employee->stats_date_to) { 89 if ($legend) { 90 for ($i = 0; $i < 24; $i++) { 91 if ($layers == 1) { 92 $this->_values[$i] = 0; 93 } else { 94 for ($j = 0; $j < $layers; $j++) { 95 $this->_values[$j][$i] = 0; 96 } 97 } 98 $this->_legend[$i] = ($i % 2) ? '' : sprintf('%02dh', $i); 99 } 100 } 101 if (is_callable([$this, 'setDayValues'])) { 102 $this->setDayValues($layers); 103 } 104 } elseif (strtotime($this->_employee->stats_date_to) - strtotime($this->_employee->stats_date_from) <= 2678400) { 105 // If the granularity is inferior to 1 month 106 // @TODO : change to manage 28 to 31 days 107 if ($legend) { 108 $days = []; 109 if ($fromArray['mon'] == $toArray['mon']) { 110 for ($i = $fromArray['mday']; $i <= $toArray['mday']; ++$i) { 111 $days[] = $i; 112 } 113 } else { 114 $imax = date('t', mktime(0, 0, 0, $fromArray['mon'], 1, $fromArray['year'])); 115 for ($i = $fromArray['mday']; $i <= $imax; ++$i) { 116 $days[] = $i; 117 } 118 for ($i = 1; $i <= $toArray['mday']; ++$i) { 119 $days[] = $i; 120 } 121 } 122 foreach ($days as $i) { 123 if ($layers == 1) { 124 $this->_values[$i] = 0; 125 } else { 126 for ($j = 0; $j < $layers; $j++) { 127 $this->_values[$j][$i] = 0; 128 } 129 } 130 $this->_legend[$i] = ($i % 2) ? '' : sprintf('%02d', $i); 131 } 132 } 133 if (is_callable([$this, 'setMonthValues'])) { 134 $this->setMonthValues($layers); 135 } 136 } elseif (strtotime('-1 year', strtotime($this->_employee->stats_date_to)) < strtotime($this->_employee->stats_date_from)) { 137 // If the granularity is less than 1 year 138 if ($legend) { 139 $months = []; 140 if ($fromArray['year'] == $toArray['year']) { 141 for ($i = $fromArray['mon']; $i <= $toArray['mon']; ++$i) { 142 $months[] = $i; 143 } 144 } else { 145 for ($i = $fromArray['mon']; $i <= 12; ++$i) { 146 $months[] = $i; 147 } 148 for ($i = 1; $i <= $toArray['mon']; ++$i) { 149 $months[] = $i; 150 } 151 } 152 foreach ($months as $i) { 153 if ($layers == 1) { 154 $this->_values[$i] = 0; 155 } else { 156 for ($j = 0; $j < $layers; $j++) { 157 $this->_values[$j][$i] = 0; 158 } 159 } 160 $this->_legend[$i] = sprintf('%02d', $i); 161 } 162 } 163 if (is_callable([$this, 'setYearValues'])) { 164 $this->setYearValues($layers); 165 } 166 } else { 167 // If the granularity is greater than 1 year 168 if ($legend) { 169 $years = []; 170 for ($i = $fromArray['year']; $i <= $toArray['year']; ++$i) { 171 $years[] = $i; 172 } 173 foreach ($years as $i) { 174 if ($layers == 1) { 175 $this->_values[$i] = 0; 176 } else { 177 for ($j = 0; $j < $layers; $j++) { 178 $this->_values[$j][$i] = 0; 179 } 180 } 181 $this->_legend[$i] = sprintf('%04d', $i); 182 } 183 } 184 if (is_callable([$this, 'setAllTimeValues'])) { 185 $this->setAllTimeValues($layers); 186 } 187 } 188 } 189 190 /** 191 * @param $datas 192 * 193 * @since 1.0.0 194 * @version 1.0.0 Initial version 195 */ 196 protected function csvExport($datas) 197 { 198 $context = Context::getContext(); 199 200 $this->setEmployee($context->employee->id); 201 $this->setLang($context->language->id); 202 203 $layers = isset($datas['layers']) ? $datas['layers'] : 1; 204 if (isset($datas['option'])) { 205 $this->setOption($datas['option'], $layers); 206 } 207 $this->getData($layers); 208 209 // @todo use native CSV PHP functions ? 210 // Generate first line (column titles) 211 if (is_array($this->_titles['main'])) { 212 for ($i = 0, $totalMain = count($this->_titles['main']); $i <= $totalMain; $i++) { 213 if ($i > 0) { 214 $this->_csv .= ';'; 215 } 216 if (isset($this->_titles['main'][$i])) { 217 $this->_csv .= $this->_titles['main'][$i]; 218 } 219 } 220 } else { // If there is only one column title, there is in fast two column (the first without title) 221 $this->_csv .= ';'.$this->_titles['main']; 222 } 223 $this->_csv .= "\n"; 224 if (count($this->_legend)) { 225 $total = 0; 226 if ($datas['type'] == 'pie') { 227 foreach ($this->_legend as $key => $legend) { 228 for ($i = 0, $totalMain = (is_array($this->_titles['main']) ? count($this->_values) : 1); $i < $totalMain; ++$i) { 229 $total += (is_array($this->_values[$i]) ? $this->_values[$i][$key] : $this->_values[$key]); 230 } 231 } 232 } 233 foreach ($this->_legend as $key => $legend) { 234 $this->_csv .= $legend.';'; 235 for ($i = 0, $totalMain = (is_array($this->_titles['main']) ? count($this->_values) : 1); $i < $totalMain; ++$i) { 236 if (!isset($this->_values[$i]) || !is_array($this->_values[$i])) { 237 if (isset($this->_values[$key])) { 238 // We don't want strings to be divided. Example: product name 239 if (is_numeric($this->_values[$key])) { 240 $this->_csv .= $this->_values[$key] / (($datas['type'] == 'pie') ? $total : 1); 241 } else { 242 $this->_csv .= $this->_values[$key]; 243 } 244 } else { 245 $this->_csv .= '0'; 246 } 247 } else { 248 // We don't want strings to be divided. Example: product name 249 if (is_numeric($this->_values[$i][$key])) { 250 $this->_csv .= $this->_values[$i][$key] / (($datas['type'] == 'pie') ? $total : 1); 251 } else { 252 $this->_csv .= $this->_values[$i][$key]; 253 } 254 } 255 $this->_csv .= ';'; 256 } 257 $this->_csv .= "\n"; 258 } 259 } 260 $this->_displayCsv(); 261 } 262 263 /** 264 * @since 1.0.0 265 * @version 1.0.0 Initial version 266 */ 267 protected function _displayCsv() 268 { 269 if (ob_get_level() && ob_get_length() > 0) { 270 ob_end_clean(); 271 } 272 header('Content-Type: application/octet-stream'); 273 header('Content-Disposition: attachment; filename="'.$this->displayName.' - '.time().'.csv"'); 274 echo $this->_csv; 275 exit; 276 } 277 278 /** 279 * @param mixed $render 280 * @param mixed $type 281 * @param mixed $width 282 * @param mixed $height 283 * @param mixed $layers 284 * 285 * @since 1.0.0 286 * @version 1.0.0 Initial version 287 */ 288 public function create($render, $type, $width, $height, $layers) 289 { 290 if (!Validate::isModuleName($render)) { 291 die(Tools::displayError()); 292 } 293 if (!file_exists($file = _PS_ROOT_DIR_.'/modules/'.$render.'/'.$render.'.php')) { 294 die(Tools::displayError()); 295 } 296 require_once($file); 297 $this->_render = new $render($type); 298 299 $this->getData($layers); 300 $this->_render->createValues($this->_values); 301 $this->_render->setSize($width, $height); 302 $this->_render->setLegend($this->_legend); 303 $this->_render->setTitles($this->_titles); 304 } 305 306 /** 307 * @since 1.0.0 308 * @version 1.0.0 Initial version 309 */ 310 public function draw() 311 { 312 $this->_render->draw(); 313 } 314 315 /** 316 * @param mixed $option 317 * @param int $layers 318 * 319 * @since 1.0.0 320 * @version 1.0.0 Initial version 321 */ 322 public function setOption($option, $layers = 1) 323 { 324 } 325 326 /** 327 * @param array $params 328 * 329 * @return array|mixed|string 330 * 331 * @since 1.0.0 332 * @version 1.0.0 Initial version 333 */ 334 public function engine($params) 335 { 336 $context = Context::getContext(); 337 $render = Configuration::get('PS_STATS_RENDER'); 338 $idEmployee = (int) $context->employee->id; 339 $idLang = (int) $context->language->id; 340 341 if (!isset($params['layers'])) { 342 $params['layers'] = 1; 343 } 344 if (!isset($params['type'])) { 345 $params['type'] = 'column'; 346 } 347 if (!isset($params['width'])) { 348 $params['width'] = '100%'; 349 } 350 if (!isset($params['height'])) { 351 $params['height'] = 270; 352 } 353 354 $urlParams = $params; 355 $urlParams['render'] = $render; 356 $urlParams['module'] = Tools::getValue('module'); 357 $urlParams['id_employee'] = $idEmployee; 358 $urlParams['id_lang'] = $idLang; 359 $drawer = 'drawer.php?'.http_build_query(array_map('Tools::safeOutput', $urlParams), '', '&'); 360 361 if (file_exists(_PS_ROOT_DIR_.'/modules/'.$render.'/'.$render.'.php')) { 362 require_once(_PS_ROOT_DIR_.'/modules/'.$render.'/'.$render.'.php'); 363 364 return call_user_func([$render, 'hookGraphEngine'], $params, $drawer); 365 } else { 366 return call_user_func(['ModuleGraphEngine', 'hookGraphEngine'], $params, $drawer); 367 } 368 } 369 370 /** 371 * @param null $employee 372 * @param Context|null $context 373 * 374 * @return bool|Employee|null 375 * 376 * @since 1.0.0 377 * @version 1.0.0 Initial version 378 */ 379 public static function getEmployee($employee = null, Context $context = null) 380 { 381 if (!Validate::isLoadedObject($employee)) { 382 if (!$context) { 383 $context = Context::getContext(); 384 } 385 if (!Validate::isLoadedObject($context->employee)) { 386 return false; 387 } 388 $employee = $context->employee; 389 } 390 391 if (empty($employee->stats_date_from) || empty($employee->stats_date_to) 392 || $employee->stats_date_from == '0000-00-00' || $employee->stats_date_to == '0000-00-00') { 393 if (empty($employee->stats_date_from) || $employee->stats_date_from == '0000-00-00') { 394 $employee->stats_date_from = date('Y').'-01-01'; 395 } 396 if (empty($employee->stats_date_to) || $employee->stats_date_to == '0000-00-00') { 397 $employee->stats_date_to = date('Y').'-12-31'; 398 } 399 $employee->update(); 400 } 401 402 return $employee; 403 } 404 405 /** 406 * @return string 407 * 408 * @since 1.0.0 409 * @version 1.0.0 Initial version 410 */ 411 public function getDate() 412 { 413 return ModuleGraph::getDateBetween($this->_employee); 414 } 415 416 /** 417 * @param null $employee 418 * 419 * @return string 420 * 421 * @since 1.0.0 422 * @version 1.0.0 Initial version 423 */ 424 public static function getDateBetween($employee = null) 425 { 426 if ($employee = ModuleGraph::getEmployee($employee)) { 427 return ' \''.$employee->stats_date_from.' 00:00:00\' AND \''.$employee->stats_date_to.' 23:59:59\' '; 428 } 429 430 return ' \''.date('Y-m').'-01 00:00:00\' AND \''.date('Y-m-t').' 23:59:59\' '; 431 } 432 433 /** 434 * @return mixed 435 * 436 * @since 1.0.0 437 * @version 1.0.0 Initial version 438 */ 439 public function getLang() 440 { 441 return $this->_id_lang; 442 } 443 444 /** 445 * @param $layers 446 * 447 * @return mixed 448 * 449 * @since 1.0.0 450 * @version 1.0.0 Initial version 451 */ 452 abstract protected function getData($layers); 453} 454