dolibarr 24.0.0-beta
context.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2023-2024 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2025 Schaffhauser sébastien <sebastien@webmaster67.fr>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
26require_once __DIR__ . '/controller.class.php';
27require_once __DIR__ . '/webPortalTheme.class.php';
28
33{
39 private static $_instance = null;
40
44 public $db;
45
49 public $title;
50
54 public $desc;
55
59 public $meta_title;
60
64 public $meta_desc;
65
70 public $appliName;
71
75 public $controller;
76
80 public $controller_found = false;
81
85 private $controllers = array();
86
90 public $controllerInstance;
91
96 public $error;
97
101 public $errors = array();
102
106 public $action;
107
111 public $tplDir;
112
116 public $tplPath;
117
121 public $topMenu;
122
126 public $rootUrl;
127
131 public $cdnUrl;
132
136 public $menu_active = array();
137
141 public $eventMessages = array();
142
146 public $tokenKey = 'token';
147
152 public $object;
153
157 public $logged_user = null;
158
162 public $logged_thirdparty = null;
163
167 public $logged_member = null;
168
172 public $logged_partnership = null;
173
177 public $theme;
178
179
185 private function __construct()
186 {
187 global $db;
188
189 $this->db = $db;
190
191 $this->tplDir = __DIR__ . '/../';
192
193 $this->getControllerUrl();
194
195 $this->topMenu = new stdClass();
196
197 $this->tplPath = realpath(__DIR__ . '/../../public/webportal/tpl');
198
199 $this->controller = GETPOST('controller', 'aZ09'); // for security, limited to 'aZ09'
200 $this->action = GETPOST('action', 'aZ09');// for security, limited to 'aZ09'
201
202 if (empty($this->controller)) {
203 $this->controller = 'default';
204 }
205
206 $this->appliName = getDolGlobalString('WEBPORTAL_TITLE', getDolGlobalString('MAIN_INFO_SOCIETE_NOM'));
207
208 //$this->generateNewToken();
209
210 $this->initController(false);
211
212 // Init of base URL. Must be the public URL.
213 $this->rootUrl = self::getRootConfigUrl();
214
215 // Init of CDN URL. Must be the public URL.
216 // BECAUSE IN SOME CASES IT COULD BE IMPORTANT TO HIDE PUBLIC URL BUT YOU CAN SET A CDN URL IN HIDDEN CONF
217 $this->cdnUrl = getDolGlobalString('WEBPORTAL_CDN_URL', dol_buildpath('/public/includes/', 3));
218 $this->cdnUrl = rtrim(trim($this->cdnUrl), '/');
219
220 $this->theme = new WebPortalTheme(false);
221 }
222
228 public static function getInstance()
229 {
230 if (is_null(self::$_instance)) {
231 self::$_instance = new Context();
232 }
233
234 return self::$_instance;
235 }
236
243 public function initController($init_theme = true)
244 {
245 global $hookmanager;
246
247 $defaultControllersPath = __DIR__ . '/../controllers/';
248
249 // define controllers definition
250 $this->addControllerDefinition('login', $defaultControllersPath . 'login.controller.class.php', 'LoginController');
251 $this->addControllerDefinition('default', $defaultControllersPath . 'default.controller.class.php', 'DefaultController');
252 $this->addControllerDefinition('document', $defaultControllersPath . 'document.controller.class.php', 'DocumentController');
253 $this->addControllerDefinition('propallist', $defaultControllersPath . 'propallist.controller.class.php', 'PropalListController');
254 $this->addControllerDefinition('orderlist', $defaultControllersPath . 'orderlist.controller.class.php', 'OrderListController');
255 $this->addControllerDefinition('invoicelist', $defaultControllersPath . 'invoicelist.controller.class.php', 'InvoiceListController');
256 $this->addControllerDefinition('ficheinterlist', $defaultControllersPath . 'ficheinterlist.controller.class.php', 'FicheinterListController');
257 $this->addControllerDefinition('ticketlist', $defaultControllersPath . 'ticketlist.controller.class.php', 'TicketListController');
258 $this->addControllerDefinition('membercard', $defaultControllersPath . 'membercard.controller.class.php', 'MemberCardController');
259 $this->addControllerDefinition('partnershipcard', $defaultControllersPath . 'partnershipcard.controller.class.php', 'PartnershipCardController');
260 //** below the addition of DocumentListController adding files by third party attached documents
261 $this->addControllerDefinition('documentlist', $defaultControllersPath . 'documentlist.controller.class.php', 'DocumentListController');
262 //** Below is the addition to the menu of the DocumentUtileController.class.php controller in order to share via the GED (documents) "Documentscomptes"
263 $this->addControllerDefinition('documentutile', $defaultControllersPath . 'documentutile.controller.class.php', 'DocumentUtileController');
264 $this->addControllerDefinition('viewimage', $defaultControllersPath . 'viewimage.controller.class.php', 'ViewImageController');
265
266 // Hooks for init controller
267 $hookmanager->initHooks(array('webportaldao'));
268 $parameters = array();
269 $reshook = $hookmanager->executeHooks('initController', $parameters, $this);
270
271 // search for controller
272 $this->controllerInstance = new Controller();
273 if (isset($this->controllers[$this->controller]) && file_exists($this->controllers[$this->controller]->path)) {
274 require_once $this->controllers[$this->controller]->path;
275
276 if (class_exists($this->controllers[$this->controller]->class)) {
277 $this->controllerInstance = new $this->controllers[$this->controller]->class();
278 $this->setControllerFound();
279 }
280 }
281
282 if ($init_theme) {
283 $this->theme->init();
284 }
285 }
286
295 public function addControllerDefinition($controller, $path, $className)
296 {
297 $fileName = basename($path);
298 $needle = '.controller.class.php';
299 $length = strlen($needle);
300 $isControllerFile = $length > 0 ? substr($fileName, -$length) === $needle : true;
301 if (!$isControllerFile) {
302 $this->setError('Error: controller definition ' . $fileName);
303 return false;
304 }
305
306 $this->controllers[$controller] = new stdClass();
307 $this->controllers[$controller]->path = $path;
308 $this->controllers[$controller]->class = $className;
309
310 return true;
311 }
312
318 public function setControllerFound()
319 {
320 $this->controller_found = true;
321 }
322
328 public static function getRootConfigUrl()
329 {
330 // Init of base URL
331 if (getDolGlobalString('WEBPORTAL_ROOT_URL')) {
332 $rootUrl = getDolGlobalString('WEBPORTAL_ROOT_URL');
333 if (substr($rootUrl, -1) !== '/') {
334 $rootUrl .= '/';
335 }
336 } else {
337 $rootUrl = dol_buildpath('/public/webportal/', 3); // Return the public URL for external access
338 }
339
340 return $rootUrl;
341 }
342
352 public function getRootUrl($controller = '', $moreParams = '', $addToken = true)
353 {
354 return self::getControllerUrl($controller, $moreParams, $addToken);
355 }
356
365 public function getControllerUrl($controller = '', $moreParams = '', $addToken = true)
366 {
367 // TODO : addToken parameter on auto to detect (create or edit) action and add token on url
368 $url = $this->rootUrl;
369
370 if (empty($controller)) {
371 // because can be called without params to get only rootUrl
372 return $url;
373 }
374
375 $Tparams = array();
376
377 $Tparams['controller'] = $controller;
378
379 if (!empty($addToken)) {
380 $Tparams[$this->tokenKey] = $this->newToken();
381 }
382
383 return self::getPublicControllerUrl($controller, $moreParams, $Tparams);
384 }
385
396 public static function getPublicControllerUrl($controller = '', $moreParams = '', $Tparams = array())
397 {
398 $url = self::getRootConfigUrl();
399
400 if (empty($controller)) {
401 // because can be called without params to get only rootUrl
402 return $url;
403 }
404
405 $Tparams['controller'] = $controller;
406
407 // if $moreParams is an array
408 if (!empty($moreParams) && is_array($moreParams)) {
409 if (isset($moreParams['controller'])) {
410 unset($moreParams['controller']);
411 }
412 if (!empty($moreParams)) {
413 foreach ($moreParams as $paramKey => $paramVal) {
414 $Tparams[$paramKey] = $paramVal;
415 }
416 }
417 }
418
419 if (!empty($Tparams)) {
420 $TCompiledAttr = array();
421 foreach ($Tparams as $key => $value) {
422 $TCompiledAttr[] = $key . '=' . $value;
423 }
424 $url .= '?' . implode("&", $TCompiledAttr);
425 }
426
427 // if $moreParams is a string
428 if (!empty($moreParams) && !is_array($moreParams)) {
429 if (empty($Tparams)) {
430 if ($moreParams[0] !== '?') {
431 $url .= '?';
432 }
433 if ($moreParams[0] === '&') {
434 $moreParams = substr($moreParams, 1);
435 }
436 }
437 $url .= $moreParams;
438 }
439
440 return $url;
441 }
442
450 public static function urlOrigin($withRequestUri = true, $use_forwarded_host = false)
451 {
452 $s = $_SERVER;
453
454 $ssl = (!empty($s['HTTPS']) && $s['HTTPS'] == 'on');
455 $sp = strtolower($s['SERVER_PROTOCOL']);
456 $protocol = substr($sp, 0, strpos($sp, '/')) . (($ssl) ? 's' : '');
457 $port = $s['SERVER_PORT'];
458 $port = ((!$ssl && $port == '80') || ($ssl && $port == '443')) ? '' : ':' . $port;
459 $host = ($use_forwarded_host && isset($s['HTTP_X_FORWARDED_HOST'])) ? $s['HTTP_X_FORWARDED_HOST'] : (isset($s['HTTP_HOST']) ? $s['HTTP_HOST'] : null);
460 $host = isset($host) ? $host : $s['SERVER_NAME'] . $port;
461
462 $url = $protocol . '://' . $host;
463
464 if ($withRequestUri) {
465 $url .= $s['REQUEST_URI'];
466 }
467
468 return $url;
469 }
470
476 public function userIsLog()
477 {
478 global $hookmanager;
479
480 // Hooks for security access
481 $hookmanager->initHooks(array('webportaldao'));
482 $parameters = array();
483 $reshook = $hookmanager->executeHooks('userIsLog', $parameters, $this);
484 if ($reshook > 0) {
485 return !empty($hookmanager->resArray['userIsLog']);
486 }
487
488 if (!empty($_SESSION["webportal_logged_thirdparty_account_id"])) {
489 return true;
490 } elseif (!empty($_SESSION["webportal_logged_member_account_id"])) {
491 return true;
492 } else {
493 return false;
494 }
495 }
496
503 public function menuIsActive($menuName)
504 {
505 return in_array($menuName, $this->menu_active);
506 }
507
514 public function setError($errors)
515 {
516 if (!is_array($errors)) {
517 $errors = array($errors);
518 }
519 if (!isset($_SESSION['webportal_errors'])) {
520 $_SESSION['webportal_errors'] = array();
521 }
522 foreach ($errors as $msg) {
523 if (!in_array($msg, $_SESSION['webportal_errors'])) {
524 $_SESSION['webportal_errors'][] = $msg;
525 }
526 }
527 }
528
534 public function getErrors()
535 {
536 if (!empty($_SESSION['webportal_errors'])) {
537 $this->errors = array_values($_SESSION['webportal_errors']);
538 return count($this->errors);
539 }
540
541 return 0;
542 }
543
549 public function clearErrors()
550 {
551 unset($_SESSION['webportal_errors']);
552 $this->errors = array();
553 }
554
563 public function setEventMessage($mesgs, $style = 'mesgs')
564 {
565 $TAcceptedStyle = array('mesgs', 'warnings', 'errors');
566
567 if (!in_array($style, $TAcceptedStyle)) {
568 $style = 'mesgs';
569 }
570
571 if (!is_array($mesgs)) {
572 $mesgs = array($mesgs);
573 }
574 if (!isset($_SESSION['webportal_events'])) {
575 $_SESSION['webportal_events'] = array(
576 'mesgs' => array(), 'warnings' => array(), 'errors' => array()
577 );
578 }
579
580 foreach ($mesgs as $msg) {
581 if (!in_array($msg, $_SESSION['webportal_events'][$style])) {
582 $_SESSION['webportal_events'][$style][] = $msg;
583 }
584 }
585 }
586
596 public function setEventMessages($mesg, $mesgs, $style = 'mesgs')
597 {
598 if (empty($mesg) && empty($mesgs)) {
599 dol_syslog(__METHOD__ . ' Try to add a message in stack, but value to add is empty message', LOG_WARNING);
600 } else {
601 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
602 dol_print_error(null, 'Bad parameter style=' . $style . ' for setEventMessages');
603 }
604 if (empty($mesgs)) {
605 $this->setEventMessage($mesg, $style);
606 } else {
607 if (!empty($mesg) && !in_array($mesg, $mesgs)) {
608 $this->setEventMessage($mesg, $style); // Add message string if not already into array
609 }
610 $this->setEventMessage($mesgs, $style);
611 }
612 }
613 }
614
620 public function loadEventMessages()
621 {
622 if (!empty($_SESSION['webportal_events'])) {
623 $this->eventMessages = $_SESSION['webportal_events'];
624 return 1;
625 }
626
627 return 0;
628 }
629
635 public function clearEventMessages()
636 {
637 unset($_SESSION['webportal_events']);
638 $this->eventMessages = array();
639 }
640
648 public function newToken()
649 {
650 return newToken();
651 }
652
658 protected function generateNewToken()
659 {
660 $currentToken = $this->newToken();
661 // Creation of a token against CSRF vulnerabilities
662 if (!defined('NOTOKENRENEWAL') || empty($currentToken)) {
663 // Rolling token at each call ($_SESSION['token'] contains token of previous page)
664 if (isset($_SESSION['newtoken'])) {
665 $_SESSION['token'] = $_SESSION['newtoken'];
666 }
667
668 // Save what will be next token. Into forms, we will add param $context->newToken();
669
670 $token = bin2hex(random_bytes(32));
671 $_SESSION['newtoken'] = $token;
672
673 return $token;
674 } else {
675 return $this->newToken();
676 }
677 }
678
684 public function getUrlToken()
685 {
686 $token = $this->newToken();
687 if ($token) {
688 return '&' . $this->tokenKey . '=' . $this->newToken();
689 }
690
691 return null;
692 }
693
699 public function getFormToken()
700 {
701 $token = $this->newToken();
702 if ($token) {
703 return '<input type="hidden" name="' . $this->tokenKey . '" value="' . $this->newToken() . '" />';
704 }
705
706 return null;
707 }
708
716 public function getThirdPartyAccountFromLogin($login, $pass)
717 {
718 $id = 0;
719
720 $sql = "SELECT sa.rowid as id, sa.pass_crypted";
721 $sql .= " FROM " . $this->db->prefix() . "societe_account as sa";
722 $sql .= " WHERE sa.login = '" . $this->db->escape($login) . "'";
723 //$sql .= " AND BINARY sa.pass_crypted = '" . $this->db->escape($pass) . "'"; // case sensitive
724 $sql .= " AND sa.site = 'dolibarr_portal'";
725 $sql .= " AND sa.status = 1";
726 $sql .= " AND sa.entity IN (" . getEntity('societe') . ")";
727
728 dol_syslog(__METHOD__ . ' Try to find the third-party account id for login"' . $login . '" and site="dolibarr_portal"', LOG_DEBUG);
729 $result = $this->db->query($sql);
730 if ($result) {
731 if ($this->db->num_rows($result) == 1) {
732 $passok = false;
733 $obj = $this->db->fetch_object($result);
734 if ($obj) {
735 $passcrypted = $obj->pass_crypted;
736
737 // Check crypted password according to crypt algorithm
738 if ($passcrypted && dol_verifyHash($pass, $passcrypted, '0')) {
739 $passok = true;
740 }
741
742 // Password ok ?
743 if ($passok) {
744 $id = $obj->id;
745 } else {
746 dol_syslog(__METHOD__ .' Authentication KO bad password for ' . $login . ', cryptType=auto', LOG_NOTICE);
747 sleep(1); // Brut force protection. Must be same delay when login is not valid
748 return -3;
749 }
750 }
751 } else {
752 dol_syslog(__METHOD__ . ' Many third-party account found for login"' . $login . '" and site="dolibarr_portal"', LOG_ERR);
753 return -2;
754 }
755 } else {
756 $this->error = $this->db->lasterror();
757 return -1;
758 }
759
760 return $id;
761 }
762
770 public function getMemberAccountFromLogin($login, $pass)
771 {
772 $id = 0;
773
774 $sql = "SELECT a.rowid as id, a.pass_crypted";
775 $sql .= " FROM " . $this->db->prefix() . "adherent as a";
776 $sql .= " WHERE a.login = '" . $this->db->escape($login) . "'";
777 $sql .= " AND a.statut = 1";
778 $sql .= " AND a.entity IN (" . getEntity('member') . ")";
779
780 dol_syslog(__METHOD__ . ' Try to find the member account id for login"' . $login . '"', LOG_DEBUG);
781 $result = $this->db->query($sql);
782 if ($result) {
783 if ($this->db->num_rows($result) == 1) {
784 $passok = false;
785 $obj = $this->db->fetch_object($result);
786 if ($obj) {
787 $passcrypted = $obj->pass_crypted;
788
789 // Check crypted password according to crypt algorithm
790 if ($passcrypted && dol_verifyHash($pass, $passcrypted, '0')) {
791 $passok = true;
792 }
793
794 // Password ok ?
795 if ($passok) {
796 $id = $obj->id;
797 } else {
798 dol_syslog(__METHOD__ .' Authentication KO bad password for ' . $login . ', cryptType=auto', LOG_NOTICE);
799 sleep(1); // Brut force protection. Must be same delay when login is not valid
800 return -3;
801 }
802 }
803 } else {
804 dol_syslog(__METHOD__ . ' Many member account found for login"' . $login . '"', LOG_ERR);
805 return -2;
806 }
807 } else {
808 $this->error = $this->db->lasterror();
809 return -1;
810 }
811
812 return $id;
813 }
814}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
Class Context.
static urlOrigin($withRequestUri=true, $use_forwarded_host=false)
Url origin.
generateNewToken()
Generate new token.
menuIsActive($menuName)
Is menu enabled ?
getErrors()
Get errors.
static getInstance()
Singleton method to create one instance of this object.
$object
Current object of page.
clearEventMessages()
Clear event messages.
getFormToken()
Get token input for form.
newToken()
Return the value of token currently saved into session with name 'newToken'.
setError($errors)
Set errors.
userIsLog()
Check if user is logged.
getUrlToken()
Get token url.
addControllerDefinition($controller, $path, $className)
Add controller definition.
getRootUrl($controller='', $moreParams='', $addToken=true)
Get root url.
static getPublicControllerUrl($controller='', $moreParams='', $Tparams=array())
Generate public controller URL Used for external link (like email or web page) so remove token and co...
setEventMessages($mesg, $mesgs, $style='mesgs')
Set event messages in dol_events session object.
getThirdPartyAccountFromLogin($login, $pass)
Try to find the third-party account id from.
setControllerFound()
Set controller found.
getControllerUrl($controller='', $moreParams='', $addToken=true)
Get controller url according to context.
static getRootConfigUrl()
Get WebPortal root url.
initController($init_theme=true)
Init controller.
$appliName
The application name.
setEventMessage($mesgs, $style='mesgs')
Set event messages in dol_events session object.
__construct()
Constructor.
loadEventMessages()
Load event messages.
clearErrors()
Clear errors.
getMemberAccountFromLogin($login, $pass)
Try to find the member account id from.
Class to manage pages.
Class WebPortalTheme.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_verifyHash($chain, $hash, $type='0')
Compute a hash and compare it to the given one For backward compatibility reasons,...