1<?php 2/** 3 * Copyright since 2007 PrestaShop SA and Contributors 4 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA 5 * 6 * NOTICE OF LICENSE 7 * 8 * This source file is subject to the Open Software License (OSL 3.0) 9 * that is bundled with this package in the file LICENSE.md. 10 * It is also available through the world-wide-web at this URL: 11 * https://opensource.org/licenses/OSL-3.0 12 * If you did not receive a copy of the license and are unable to 13 * obtain it through the world-wide-web, please send an email 14 * to license@prestashop.com so we can send you a copy immediately. 15 * 16 * DISCLAIMER 17 * 18 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer 19 * versions in the future. If you wish to customize PrestaShop for your 20 * needs please refer to https://devdocs.prestashop.com/ for more information. 21 * 22 * @author PrestaShop SA and Contributors <contact@prestashop.com> 23 * @copyright Since 2007 PrestaShop SA and Contributors 24 * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) 25 */ 26use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder; 27 28class AdminDashboardControllerCore extends AdminController 29{ 30 public function __construct() 31 { 32 $this->bootstrap = true; 33 $this->display = 'view'; 34 35 parent::__construct(); 36 37 if (Tools::isSubmit('profitability_conf') || Tools::isSubmit('submitOptionsconfiguration')) { 38 $this->fields_options = $this->getOptionFields(); 39 } 40 } 41 42 public function setMedia($isNewTheme = false) 43 { 44 parent::setMedia($isNewTheme); 45 46 $this->addJqueryUI('ui.datepicker'); 47 $this->addJS([ 48 _PS_JS_DIR_ . 'vendor/d3.v3.min.js', 49 __PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $this->bo_theme . '/js/vendor/nv.d3.min.js', 50 _PS_JS_DIR_ . '/admin/dashboard.js', 51 ]); 52 $this->addCSS(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $this->bo_theme . '/css/vendor/nv.d3.css'); 53 } 54 55 public function initPageHeaderToolbar() 56 { 57 $this->page_header_toolbar_title = $this->trans('Dashboard', [], 'Admin.Dashboard.Feature'); 58 $this->page_header_toolbar_btn['switch_demo'] = [ 59 'desc' => $this->trans('Demo mode', [], 'Admin.Dashboard.Feature'), 60 'icon' => 'process-icon-toggle-' . (Configuration::get('PS_DASHBOARD_SIMULATION') ? 'on' : 'off'), 61 'help' => $this->trans('This mode displays sample data so you can try your dashboard without real numbers.', [], 'Admin.Dashboard.Help'), 62 ]; 63 64 parent::initPageHeaderToolbar(); 65 66 // Remove the last element on this controller to match the title with the rule of the others 67 array_pop($this->meta_title); 68 } 69 70 protected function getOptionFields() 71 { 72 $forms = []; 73 $currency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT')); 74 $carriers = Carrier::getCarriers((int) $this->context->language->id, true, false, false, null, Carrier::ALL_CARRIERS); 75 $modules = Module::getModulesOnDisk(true); 76 77 $forms = [ 78 'payment' => ['title' => $this->trans('Average bank fees per payment method', [], 'Admin.Dashboard.Feature'), 'id' => 'payment'], 79 'carriers' => ['title' => $this->trans('Average shipping fees per shipping method', [], 'Admin.Dashboard.Feature'), 'id' => 'carriers'], 80 'other' => ['title' => $this->trans('Other settings', [], 'Admin.Dashboard.Feature'), 'id' => 'other'], 81 ]; 82 foreach ($forms as &$form) { 83 $form['icon'] = 'tab-preferences'; 84 $form['fields'] = []; 85 $form['submit'] = ['title' => $this->trans('Save', [], 'Admin.Actions')]; 86 } 87 88 foreach ($modules as $module) { 89 if (isset($module->tab) && $module->tab == 'payments_gateways' && $module->id) { 90 $moduleClass = Module::getInstanceByName($module->name); 91 if (!$moduleClass->isEnabledForShopContext()) { 92 continue; 93 } 94 95 $forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_FIXED'] = [ 96 'title' => $module->displayName, 97 'desc' => $this->trans( 98 'Choose a fixed fee for each order placed in %currency% with %module%.', 99 [ 100 '%currency' => $currency->iso_code, 101 '%module%' => $module->displayName, 102 ], 103 'Admin.Dashboard.Help' 104 ), 105 'validation' => 'isPrice', 106 'cast' => 'floatval', 107 'type' => 'text', 108 'defaultValue' => '0', 109 'suffix' => $currency->iso_code, 110 ]; 111 $forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_VAR'] = [ 112 'title' => $module->displayName, 113 'desc' => $this->trans( 114 'Choose a variable fee for each order placed in %currency% with %module%. It will be applied on the total paid with taxes.', 115 [ 116 '%currency' => $currency->iso_code, 117 '%module%' => $module->displayName, 118 ], 119 'Admin.Dashboard.Help' 120 ), 121 'validation' => 'isPercentage', 122 'cast' => 'floatval', 123 'type' => 'text', 124 'defaultValue' => '0', 125 'suffix' => '%', 126 ]; 127 128 if (Currency::isMultiCurrencyActivated()) { 129 $forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_FIXED_FOREIGN'] = [ 130 'title' => $module->displayName, 131 'desc' => $this->trans( 132 'Choose a fixed fee for each order placed with a foreign currency with %module%.', 133 [ 134 '%module%' => $module->displayName, 135 ], 136 'Admin.Dashboard.Help' 137 ), 138 'validation' => 'isPrice', 139 'cast' => 'floatval', 140 'type' => 'text', 141 'defaultValue' => '0', 142 'suffix' => $currency->iso_code, 143 ]; 144 $forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_VAR_FOREIGN'] = [ 145 'title' => $module->displayName, 146 'desc' => $this->trans( 147 'Choose a variable fee for each order placed with a foreign currency with %module%. It will be applied on the total paid with taxes.', 148 ['%module%' => $module->displayName], 149 'Admin.Dashboard.Help' 150 ), 151 'validation' => 'isPercentage', 152 'cast' => 'floatval', 153 'type' => 'text', 154 'defaultValue' => '0', 155 'suffix' => '%', 156 ]; 157 } 158 } 159 } 160 161 foreach ($carriers as $carrier) { 162 $forms['carriers']['fields']['CONF_' . strtoupper($carrier['id_reference']) . '_SHIP'] = [ 163 'title' => $carrier['name'], 164 'desc' => $this->trans( 165 'For the carrier named %s, indicate the domestic delivery costs in percentage of the price charged to customers.', 166 [ 167 '%s' => $carrier['name'], 168 ], 169 'Admin.Dashboard.Help' 170 ), 171 'validation' => 'isPercentage', 172 'cast' => 'floatval', 173 'type' => 'text', 174 'defaultValue' => '0', 175 'suffix' => '%', 176 ]; 177 $forms['carriers']['fields']['CONF_' . strtoupper($carrier['id_reference']) . '_SHIP_OVERSEAS'] = [ 178 'title' => $carrier['name'], 179 'desc' => $this->trans( 180 'For the carrier named %s, indicate the overseas delivery costs in percentage of the price charged to customers.', 181 [ 182 '%s' => $carrier['name'], 183 ], 184 'Admin.Dashboard.Help' 185 ), 186 'validation' => 'isPercentage', 187 'cast' => 'floatval', 188 'type' => 'text', 189 'defaultValue' => '0', 190 'suffix' => '%', 191 ]; 192 } 193 194 $forms['carriers']['description'] = $this->trans('Method: Indicate the percentage of your carrier margin. For example, if you charge $10 of shipping fees to your customer for each shipment, but you really pay $4 to this carrier, then you should indicate "40" in the percentage field.', [], 'Admin.Dashboard.Help'); 195 196 $forms['other']['fields']['CONF_AVERAGE_PRODUCT_MARGIN'] = [ 197 'title' => $this->trans('Average gross margin percentage', [], 'Admin.Dashboard.Feature'), 198 'desc' => $this->trans('You should calculate this percentage as follows: ((total sales revenue) - (cost of goods sold)) / (total sales revenue) * 100. This value is only used to calculate the Dashboard approximate gross margin, if you do not specify the wholesale price for each product.', [], 'Admin.Dashboard.Help'), 199 'validation' => 'isPercentage', 200 'cast' => 'intval', 201 'type' => 'text', 202 'defaultValue' => '0', 203 'suffix' => '%', 204 ]; 205 206 $forms['other']['fields']['CONF_ORDER_FIXED'] = [ 207 'title' => $this->trans('Other fees per order', [], 'Admin.Dashboard.Feature'), 208 'desc' => $this->trans('You should calculate this value by making the sum of all of your additional costs per order.', [], 'Admin.Dashboard.Help'), 209 'validation' => 'isPrice', 210 'cast' => 'floatval', 211 'type' => 'text', 212 'defaultValue' => '0', 213 'suffix' => $currency->iso_code, 214 ]; 215 216 Media::addJsDef([ 217 'dashboard_ajax_url' => $this->context->link->getAdminLink('AdminDashboard'), 218 'read_more' => '', 219 ]); 220 221 return $forms; 222 } 223 224 public function renderView() 225 { 226 if (Tools::isSubmit('profitability_conf')) { 227 return parent::renderOptions(); 228 } 229 230 $testStatsDateUpdate = $this->context->cookie->__get('stats_date_update'); 231 if (!empty($testStatsDateUpdate) && $this->context->cookie->__get('stats_date_update') < strtotime(date('Y-m-d'))) { 232 switch ($this->context->employee->preselect_date_range) { 233 case 'day': 234 $date_from = date('Y-m-d'); 235 $date_to = date('Y-m-d'); 236 237 break; 238 case 'prev-day': 239 $date_from = date('Y-m-d', strtotime('-1 day')); 240 $date_to = date('Y-m-d', strtotime('-1 day')); 241 242 break; 243 case 'month': 244 default: 245 $date_from = date('Y-m-01'); 246 $date_to = date('Y-m-d'); 247 248 break; 249 case 'prev-month': 250 $date_from = date('Y-m-01', strtotime('-1 month')); 251 $date_to = date('Y-m-t', strtotime('-1 month')); 252 253 break; 254 case 'year': 255 $date_from = date('Y-01-01'); 256 $date_to = date('Y-m-d'); 257 258 break; 259 case 'prev-year': 260 $date_from = date('Y-m-01', strtotime('-1 year')); 261 $date_to = date('Y-12-t', strtotime('-1 year')); 262 263 break; 264 } 265 $this->context->employee->stats_date_from = $date_from; 266 $this->context->employee->stats_date_to = $date_to; 267 $this->context->employee->update(); 268 $this->context->cookie->__set('stats_date_update', strtotime(date('Y-m-d'))); 269 $this->context->cookie->write(); 270 } 271 272 $calendar_helper = new HelperCalendar(); 273 274 $calendar_helper->setDateFrom(Tools::getValue('date_from', $this->context->employee->stats_date_from)); 275 $calendar_helper->setDateTo(Tools::getValue('date_to', $this->context->employee->stats_date_to)); 276 277 $stats_compare_from = $this->context->employee->stats_compare_from; 278 $stats_compare_to = $this->context->employee->stats_compare_to; 279 280 if (null === $stats_compare_from || $stats_compare_from == '0000-00-00') { 281 $stats_compare_from = null; 282 } 283 284 if (null === $stats_compare_to || $stats_compare_to == '0000-00-00') { 285 $stats_compare_to = null; 286 } 287 288 $calendar_helper->setCompareDateFrom($stats_compare_from); 289 $calendar_helper->setCompareDateTo($stats_compare_to); 290 $calendar_helper->setCompareOption(Tools::getValue('compare_date_option', $this->context->employee->stats_compare_option)); 291 292 $params = [ 293 'date_from' => $this->context->employee->stats_date_from, 294 'date_to' => $this->context->employee->stats_date_to, 295 ]; 296 297 $moduleManagerBuilder = ModuleManagerBuilder::getInstance(); 298 $moduleManager = $moduleManagerBuilder->build(); 299 300 $this->tpl_view_vars = [ 301 'date_from' => $this->context->employee->stats_date_from, 302 'date_to' => $this->context->employee->stats_date_to, 303 'hookDashboardZoneOne' => Hook::exec('dashboardZoneOne', $params), 304 'hookDashboardZoneTwo' => Hook::exec('dashboardZoneTwo', $params), 305 'action' => '#', 306 'warning' => $this->getWarningDomainName(), 307 'new_version_url' => Tools::getCurrentUrlProtocolPrefix() . _PS_API_DOMAIN_ . '/version/check_version.php?v=' . _PS_VERSION_ . '&lang=' . $this->context->language->iso_code . '&autoupgrade=' . (int) ($moduleManager->isInstalled('autoupgrade') && $moduleManager->isEnabled('autoupgrade')) . '&hosted_mode=' . (int) defined('_PS_HOST_MODE_'), 308 'dashboard_use_push' => Configuration::get('PS_DASHBOARD_USE_PUSH'), 309 'calendar' => $calendar_helper->generate(), 310 'PS_DASHBOARD_SIMULATION' => Configuration::get('PS_DASHBOARD_SIMULATION'), 311 'datepickerFrom' => Tools::getValue('datepickerFrom', $this->context->employee->stats_date_from), 312 'datepickerTo' => Tools::getValue('datepickerTo', $this->context->employee->stats_date_to), 313 'preselect_date_range' => Tools::getValue('preselectDateRange', $this->context->employee->preselect_date_range), 314 'help_center_link' => $this->getHelpCenterLink($this->context->language->iso_code), 315 ]; 316 317 return parent::renderView(); 318 } 319 320 public function postProcess() 321 { 322 if (Tools::isSubmit('submitDateRealTime')) { 323 if ($use_realtime = (int) Tools::getValue('submitDateRealTime')) { 324 $this->context->employee->stats_date_from = date('Y-m-d'); 325 $this->context->employee->stats_date_to = date('Y-m-d'); 326 $this->context->employee->stats_compare_option = HelperCalendar::DEFAULT_COMPARE_OPTION; 327 $this->context->employee->stats_compare_from = null; 328 $this->context->employee->stats_compare_to = null; 329 $this->context->employee->update(); 330 } 331 Configuration::updateValue('PS_DASHBOARD_USE_PUSH', $use_realtime); 332 } 333 334 if (Tools::isSubmit('submitDateRange')) { 335 if (!Validate::isDate(Tools::getValue('date_from')) 336 || !Validate::isDate(Tools::getValue('date_to'))) { 337 $this->errors[] = $this->trans('The selected date range is not valid.', [], 'Admin.Notifications.Error'); 338 } 339 340 if (Tools::getValue('datepicker_compare')) { 341 if (!Validate::isDate(Tools::getValue('compare_date_from')) 342 || !Validate::isDate(Tools::getValue('compare_date_to'))) { 343 $this->errors[] = $this->trans('The selected date range is not valid.', [], 'Admin.Notifications.Error'); 344 } 345 } 346 347 if (!count($this->errors)) { 348 $this->context->employee->stats_date_from = Tools::getValue('date_from'); 349 $this->context->employee->stats_date_to = Tools::getValue('date_to'); 350 $this->context->employee->preselect_date_range = Tools::getValue('preselectDateRange'); 351 352 if (Tools::getValue('datepicker_compare')) { 353 $this->context->employee->stats_compare_from = Tools::getValue('compare_date_from'); 354 $this->context->employee->stats_compare_to = Tools::getValue('compare_date_to'); 355 $this->context->employee->stats_compare_option = Tools::getValue('compare_date_option'); 356 } else { 357 $this->context->employee->stats_compare_from = null; 358 $this->context->employee->stats_compare_to = null; 359 $this->context->employee->stats_compare_option = HelperCalendar::DEFAULT_COMPARE_OPTION; 360 } 361 362 $this->context->employee->update(); 363 } 364 } 365 366 parent::postProcess(); 367 } 368 369 protected function getWarningDomainName() 370 { 371 $warning = false; 372 if (Shop::isFeatureActive()) { 373 return; 374 } 375 376 $shop = Context::getContext()->shop; 377 if ($_SERVER['HTTP_HOST'] != $shop->domain && $_SERVER['HTTP_HOST'] != $shop->domain_ssl && Tools::getValue('ajax') == false && !defined('_PS_HOST_MODE_')) { 378 $warning = $this->trans('You are currently connected under the following domain name:', [], 'Admin.Dashboard.Notification') . ' <span style="color: #CC0000;">' . $_SERVER['HTTP_HOST'] . '</span><br />'; 379 if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE')) { 380 $warning .= $this->trans( 381 'This is different from the shop domain name set in the Multistore settings: "%s".', 382 [ 383 '%s' => $shop->domain, 384 ], 385 'Admin.Dashboard.Notification' 386 ) . $this->trans( 387 'If this is your main domain, please {link}change it now{/link}.', 388 [ 389 '{link}' => '<a href="index.php?controller=AdminShopUrl&id_shop_url=' . (int) $shop->id . '&updateshop_url&token=' . Tools::getAdminTokenLite('AdminShopUrl') . '">', 390 '{/link}' => '</a>', 391 ], 392 'Admin.Dashboard.Notification' 393 ); 394 } else { 395 $warning .= $this->trans('This is different from the domain name set in the "SEO & URLs" tab.', [], 'Admin.Dashboard.Notification') . ' 396 ' . $this->trans( 397 'If this is your main domain, please {link}change it now{/link}.', 398 [ 399 '{link}' => '<a href="index.php?controller=AdminMeta&token=' . Tools::getAdminTokenLite('AdminMeta') . '#meta_fieldset_shop_url">', 400 '{/link}' => '</a>', 401 ], 402 'Admin.Dashboard.Notification' 403 ); 404 } 405 } 406 407 return $warning; 408 } 409 410 public function ajaxProcessRefreshDashboard() 411 { 412 $id_module = null; 413 if ($module = Tools::getValue('module')) { 414 $module_obj = Module::getInstanceByName($module); 415 if (Validate::isLoadedObject($module_obj)) { 416 $id_module = $module_obj->id; 417 } 418 } 419 420 $params = [ 421 'date_from' => $this->context->employee->stats_date_from, 422 'date_to' => $this->context->employee->stats_date_to, 423 'compare_from' => $this->context->employee->stats_compare_from, 424 'compare_to' => $this->context->employee->stats_compare_to, 425 'dashboard_use_push' => (int) Tools::getValue('dashboard_use_push'), 426 'extra' => (int) Tools::getValue('extra'), 427 ]; 428 429 die(json_encode(Hook::exec('dashboardData', $params, $id_module, true, true, (int) Tools::getValue('dashboard_use_push')))); 430 } 431 432 public function ajaxProcessSetSimulationMode() 433 { 434 Configuration::updateValue('PS_DASHBOARD_SIMULATION', (int) Tools::getValue('PS_DASHBOARD_SIMULATION')); 435 die('k' . Configuration::get('PS_DASHBOARD_SIMULATION') . 'k'); 436 } 437 438 /** 439 * Returns last news from the blog 440 * 441 * @throws PrestaShopException 442 */ 443 public function displayAjaxGetBlogRss() 444 { 445 $newsFetcher = $this->get('prestashop.adapter.news.provider'); 446 $return = $newsFetcher->getData($this->context->language->iso_code); 447 448 // Response 449 header('Content-Type: application/json'); 450 $this->ajaxRender(json_encode($return)); 451 } 452 453 public function ajaxProcessSaveDashConfig() 454 { 455 $return = ['has_errors' => false, 'errors' => []]; 456 $module = Tools::getValue('module'); 457 $hook = Tools::getValue('hook'); 458 $configs = Tools::getValue('configs'); 459 460 $params = [ 461 'date_from' => $this->context->employee->stats_date_from, 462 'date_to' => $this->context->employee->stats_date_to, 463 ]; 464 465 if (Validate::isModuleName($module) && $module_obj = Module::getInstanceByName($module)) { 466 $return['errors'] = $module_obj->validateDashConfig($configs); 467 if (count($return['errors'])) { 468 $return['has_errors'] = true; 469 } else { 470 $return['has_errors'] = $module_obj->saveDashConfig($configs); 471 } 472 } 473 474 if (Validate::isHookName($hook) && method_exists($module_obj, $hook)) { 475 $return['widget_html'] = $module_obj->$hook($params); 476 } 477 478 die(json_encode($return)); 479 } 480 481 /** 482 * Returns the Help center link for the provided locale 483 * 484 * @param string $languageCode 2-letter locale code 485 * 486 * @return string 487 */ 488 private function getHelpCenterLink($languageCode) 489 { 490 $links = [ 491 'fr' => 'https://www.prestashop.com/fr/contact?utm_source=back-office&utm_medium=links&utm_campaign=help-center-fr&utm_content=download17', 492 'en' => 'https://www.prestashop.com/en/contact?utm_source=back-office&utm_medium=links&utm_campaign=help-center-en&utm_content=download17', 493 'es' => 'https://www.prestashop.com/es/contacto?utm_source=back-office&utm_medium=links&utm_campaign=help-center-es&utm_content=download17', 494 'de' => 'https://www.prestashop.com/de/kontakt?utm_source=back-office&utm_medium=links&utm_campaign=help-center-de&utm_content=download17', 495 'it' => 'https://www.prestashop.com/it/contatti?utm_source=back-office&utm_medium=links&utm_campaign=help-center-it&utm_content=download17', 496 'nl' => 'https://www.prestashop.com/nl/contacteer-ons?utm_source=back-office&utm_medium=links&utm_campaign=help-center-nl&utm_content=download17', 497 'pt' => 'https://www.prestashop.com/pt/contato?utm_source=back-office&utm_medium=links&utm_campaign=help-center-pt&utm_content=download17', 498 'pl' => 'https://www.prestashop.com/pl/kontakt?utm_source=back-office&utm_medium=links&utm_campaign=help-center-pl&utm_content=download17', 499 ]; 500 501 return isset($links[$languageCode]) ? $links[$languageCode] : $links['en']; 502 } 503} 504