dolibarr 24.0.1
website2.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
32function dolSaveMasterFile($filemaster)
33{
34 // Now generate the master.inc.php page
35 dol_syslog("We regenerate the master.inc.php file");
36
37 dol_delete_file($filemaster);
38
39 $mastercontent = '<?php'."\n";
40 $mastercontent .= '// File generated to link to the master file - DO NOT MODIFY - It is just an include'."\n";
41 $mastercontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) {\n";
42 $mastercontent .= " if (! defined('USEEXTERNALSERVER')) define('USEEXTERNALSERVER', 1);\n";
43 $mastercontent .= " require_once '".DOL_DOCUMENT_ROOT."/master.inc.php';\n";
44 $mastercontent .= "}\n";
45 $mastercontent .= '?>'."\n";
46 $result = file_put_contents($filemaster, $mastercontent);
47 dolChmod($filemaster);
48
49 return $result;
50}
51
62function dolSavePageAlias($filealias, $object, $objectpage)
63{
64 // Now create the .tpl file
65 dol_syslog("dolSavePageAlias We regenerate the alias page filealias=".$filealias." and a wrapper into all language subdirectories");
66
67 $aliascontent = '<?php'."\n";
68 $aliascontent .= "// File generated to wrap the alias page - DO NOT MODIFY - It is just a wrapper to real page\n";
69 $aliascontent .= 'global $dolibarr_main_data_root;'."\n";
70 $aliascontent .= 'if (empty($dolibarr_main_data_root)) $res=include \'./page'.$objectpage->id.'.tpl.php\'; ';
71 $aliascontent .= 'else $res=include $dolibarr_main_data_root.\'/website/\'.$website->ref.\'/page'.$objectpage->id.'.tpl.php\';'."\n";
72 $aliascontent .= 'if ($res === false) { http_response_code(500); print \'Failed to make include\'; }'."\n";
73 $aliascontent .= '?>'."\n";
74 $result = file_put_contents($filealias, $aliascontent);
75 if ($result === false) {
76 dol_syslog("Failed to write file ".$filealias, LOG_WARNING);
77 }
78 dolChmod($filealias);
79
80 // Save also alias into language subdirectory if it is not a main language
81 if ($objectpage->lang && in_array($objectpage->lang, explode(',', $object->otherlang))) {
82 $dirname = dirname($filealias);
83 $filename = basename($filealias);
84 $filealiassub = $dirname.'/'.$objectpage->lang.'/'.$filename;
85
86 dol_mkdir($dirname.'/'.$objectpage->lang, DOL_DATA_ROOT);
87
88 $aliascontent = '<?php'."\n";
89 $aliascontent .= "// File generated to wrap the alias page - DO NOT MODIFY - It is just a wrapper to real page\n";
90 $aliascontent .= 'global $dolibarr_main_data_root;'."\n";
91 $aliascontent .= 'if (empty($dolibarr_main_data_root)) $res=include \'../page'.$objectpage->id.'.tpl.php\'; ';
92 $aliascontent .= 'else $res=include $dolibarr_main_data_root.\'/website/\'.$website->ref.\'/page'.$objectpage->id.'.tpl.php\';'."\n";
93 $aliascontent .= 'if ($res === false) { http_response_code(500); print \'Failed to make include\'; }'."\n";
94 $aliascontent .= '?>'."\n";
95 $result = file_put_contents($filealiassub, $aliascontent);
96 if ($result === false) {
97 dol_syslog("Failed to write file ".$filealiassub, LOG_WARNING);
98 }
99 dolChmod($filealiassub);
100 } elseif (empty($objectpage->lang) || !in_array($objectpage->lang, explode(',', $object->otherlang))) {
101 // Save also alias into all language subdirectories if it is a main language
102 if (!getDolGlobalString('WEBSITE_DISABLE_MAIN_LANGUAGE_INTO_LANGSUBDIR') && !empty($object->otherlang)) {
103 $dirname = dirname($filealias);
104 $filename = basename($filealias);
105 foreach (explode(',', $object->otherlang) as $sublang) {
106 // Avoid to erase main alias file if $sublang is empty string
107 if (empty(trim($sublang))) {
108 continue;
109 }
110 $filealiassub = $dirname.'/'.$sublang.'/'.$filename;
111
112 $aliascontent = '<?php'."\n";
113 $aliascontent .= "// File generated to wrap the alias page - DO NOT MODIFY - It is just a wrapper to real page\n";
114 $aliascontent .= 'global $dolibarr_main_data_root;'."\n";
115 $aliascontent .= 'if (empty($dolibarr_main_data_root)) $res=include \'../page'.$objectpage->id.'.tpl.php\'; ';
116 $aliascontent .= 'else $res=include $dolibarr_main_data_root.\'/website/\'.$website->ref.\'/page'.$objectpage->id.'.tpl.php\';'."\n";
117 $aliascontent .= 'if ($res === false) { http_response_code(500); print \'Failed to make include\'; }'."\n";
118 $aliascontent .= '?>'."\n";
119
120 dol_mkdir($dirname.'/'.$sublang);
121 $result = file_put_contents($filealiassub, $aliascontent);
122 if ($result === false) {
123 dol_syslog("Failed to write file ".$filealiassub, LOG_WARNING);
124 }
125 dolChmod($filealiassub);
126 }
127 }
128 }
129
130 return ($result ? true : false);
131}
132
133
145function dolSavePageContent($filetpl, Website $object, WebsitePage $objectpage, $backupold = 0)
146{
147 global $conf, $db;
148
149 // Now create the .tpl file (duplicate code with actions updatesource or updatecontent but we need this to save new header)
150 dol_syslog("dolSavePageContent We regenerate the tpl page filetpl=".$filetpl);
151
152 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
153
154 if (dol_is_file($filetpl)) {
155 if ($backupold) {
156 $result = archiveOrBackupFile($filetpl);
157 if (! $result) {
158 return false;
159 }
160 } else {
161 dol_delete_file($filetpl);
162 }
163 }
164
165 $shortlangcode = '';
166 if ($objectpage->lang) {
167 $shortlangcode = substr($objectpage->lang, 0, 2); // en_US or en-US -> en
168 }
169 if (empty($shortlangcode)) {
170 // Take the language of website
171 $shortlangcode = substr($object->lang, 0, 2); // en_US or en-US -> en
172 }
173
174 if (!empty($objectpage->type_container) && in_array($objectpage->type_container, array('library', 'service'))) {
175 $originalcontentonly = 1;
176 }
177
178 $tplcontent = '';
179 if (!isset($originalcontentonly)) {
180 // If we want to generate a page with some code to manage PHP content
181 $tplcontent .= "<?php // BEGIN PHP\n";
182 $tplcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
183 $tplcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) {\n";
184 $tplcontent .= ' $pathdepth = count(explode(\'/\', $_SERVER[\'SCRIPT_NAME\'])) - 2;'."\n";
185 $tplcontent .= ' require_once ($pathdepth ? str_repeat(\'../\', $pathdepth) : \'./\').\'master.inc.php\';'."\n";
186 if ($objectpage->disable_waf != 'all') {
187 if (strpos($objectpage->disable_waf, 'NOSCANAUDIOFORINJECTION') !== false) {
188 $tplcontent .= ' define(\'NOSCANAUDIOFORINJECTION\', 1);'."\n";
189 }
190 if (strpos($objectpage->disable_waf, 'NOSCANIFRAMEFORINJECTION') !== false) {
191 $tplcontent .= ' define(\'NOSCANIFRAMEFORINJECTION\', 1);'."\n";
192 }
193 if (strpos($objectpage->disable_waf, 'NOSCANOBJECTFORINJECTION') !== false) {
194 $tplcontent .= ' define(\'NOSCANOBJECTFORINJECTION\', 1);'."\n";
195 }
196 $tplcontent .= ' require_once DOL_DOCUMENT_ROOT.\'/waf.inc.php\';'."\n";
197 }
198 $tplcontent .= "}\n";
199 $tplcontent .= "require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
200 $tplcontent .= "require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
201 if (in_array($objectpage->type_container, array('page', 'blogpost', 'service'))) {
202 $tplcontent .= 'dol_syslog("--- Prepare content of page '.((int) $objectpage->id).' - '.$objectpage->pageurl.'");'."\n";
203 }
204 $tplcontent .= "ob_start();\n";
205 $tplcontent .= "try {\n";
206 $tplcontent .= "// END PHP ?>\n";
207 if (getDolGlobalString('WEBSITE_FORCE_DOCTYPE_HTML5')) {
208 $tplcontent .= "<!DOCTYPE html>\n";
209 }
210 // If a language was forced on page, we use it, else we use the lang of visitor else the lang of web site
211 $tplcontent .= '<html'.($objectpage->lang ? ' lang="'.substr($objectpage->lang, 0, 2).'"' : '<?php echo $weblangs->shortlang ? \' lang="\'.$weblangs->shortlang.\'"\' : \'\' ?>').'>'."\n";
212 $tplcontent .= '<head>'."\n";
213 $tplcontent .= '<title>'.dol_string_nohtmltag($objectpage->title, 1, 'UTF-8').'</title>'."\n";
214 $tplcontent .= '<meta charset="utf-8">'."\n";
215 $tplcontent .= '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'."\n";
216 $tplcontent .= '<meta name="robots" content="'.($objectpage->index ? 'index' : 'noindex').', '.($objectpage->follow ? 'follow' : 'nofollow').'" />'."\n";
217 $tplcontent .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n";
218 $tplcontent .= '<meta name="keywords" content="'.dol_string_nohtmltag($objectpage->keywords, 1, 'UTF-8').'" />'."\n";
219 $tplcontent .= '<meta name="title" content="'.dol_string_nohtmltag($objectpage->title, 1, 'UTF-8').'" />'."\n";
220 $tplcontent .= '<meta name="description" content="'.dol_string_nohtmltag($objectpage->description, 1, 'UTF-8').'" />'."\n";
221 $tplcontent .= '<meta name="generator" content="'.DOL_APPLICATION_TITLE.' '.DOL_VERSION.' (https://www.dolibarr.org)" />'."\n";
222 $tplcontent .= '<meta name="dolibarr:pageid" content="'.((int) $objectpage->id).'" />'."\n";
223
224 // Add favicon if not already done in htmlheader
225 $htmldeaderindestdir = dol_sanitizePathName($conf->website->dir_temp.'/'.$object->ref.'/containers/htmlheader.html');
226 $htmlheader = file_get_contents($htmldeaderindestdir);
227 if (in_array($objectpage->type_container, array('page', 'blogpost')) && !preg_match('/'.preg_quote('rel="icon"', '/').'/', $htmlheader)) {
228 $tplcontent .= '<link rel="icon" type="image/png" href="/favicon.png" />'."\n";
229 }
230
231 $listofaltlang = $object->otherlang;
232
233 // Note: $object is website, $objectpage is website page
234 if ($object->virtualhost) {
235 // Add the link of the canonical reference
236 $canonicalurladdidlang = '';
237 if ($objectpage->lang) { // A language is forced on the page, it means we may have other language files with hard links into properties of page
238 $canonicalurl = (($objectpage->id == $object->fk_default_home) ? '/' : (($shortlangcode != substr($object->lang, 0, 2) ? '/'.$shortlangcode : '').'/'.$objectpage->pageurl.'.php'));
239 } else { // No language forced, it means the canonical is the one with params making url unique
240 $canonicalurl = '/'.$objectpage->pageurl.'.php';
241
242 if ($object->lang && $listofaltlang) {
243 // Add parameter ID required to be unique/canonical
244 $canonicalurladdidlang = '?__SEO_CANONICAL_URL_PARAMS__';
245 $canonicalurladdidlang .= '&l=__SEO_CANONICAL_LANG__';
246 } else {
247 // Add parameter ID required to be unique/canonical
248 $canonicalurladdidlang = '?__SEO_CANONICAL_URL_PARAMS__';
249 }
250 }
251
252 $tplcontent .= '<link rel="canonical" href="<?php echo $website->virtualhost; ?>'.$canonicalurl.$canonicalurladdidlang.'" />'."\n";
253
254 // Add the link of alternate translation reference
255 if ($listofaltlang) { // If website has other languages to support
256 if ($objectpage->lang) { // A language is forced on the page, it means we may have other language files with hard links into properties of page
257 // Add page "translation of"
258 $translationof = $objectpage->fk_page;
259 if ($translationof) {
260 $tmppage = new WebsitePage($db);
261 $tmppage->fetch($translationof);
262 if ($tmppage->id > 0) {
263 $tmpshortlangcode = '';
264 if ($tmppage->lang) {
265 $tmpshortlangcode = preg_replace('/[_-].*$/', '', $tmppage->lang); // en_US or en-US -> en
266 }
267 if (empty($tmpshortlangcode)) {
268 $tmpshortlangcode = preg_replace('/[_-].*$/', '', $object->lang); // en_US or en-US -> en
269 }
270 if ($tmpshortlangcode != $shortlangcode) {
271 $tplcontent .= '<link rel="alternate" hreflang="'.$tmpshortlangcode.'" href="<?php echo $website->virtualhost; ?>'.($object->fk_default_home == $tmppage->id ? '/' : (($tmpshortlangcode != substr($object->lang, 0, 2)) ? '/'.$tmpshortlangcode : '').'/'.$tmppage->pageurl.'.php').'" />'."\n";
272 }
273 }
274 }
275
276 // Add "has translation pages"
277 $sql = "SELECT rowid as id, lang, pageurl from ".MAIN_DB_PREFIX.'website_page where fk_page IN ('.$db->sanitize($objectpage->id.($translationof ? ", ".$translationof : '')).")";
278 $resql = $db->query($sql);
279 if ($resql) {
280 $num_rows = $db->num_rows($resql);
281 if ($num_rows > 0) {
282 while ($obj = $db->fetch_object($resql)) {
283 $tmpshortlangcode = '';
284 if ($obj->lang) {
285 $tmpshortlangcode = preg_replace('/[_-].*$/', '', $obj->lang); // en_US or en-US -> en
286 }
287 if ($tmpshortlangcode != $shortlangcode) {
288 $tplcontent .= '<link rel="alternate" hreflang="'.$tmpshortlangcode.'" href="<?php echo $website->virtualhost; ?>'.($object->fk_default_home == $obj->id ? '/' : (($tmpshortlangcode != substr($object->lang, 0, 2) ? '/'.$tmpshortlangcode : '')).'/'.$obj->pageurl.'.php').'" />'."\n";
289 }
290 }
291 }
292 } else {
294 }
295
296 // Add myself
297 $tplcontent .= '<?php if ($_SERVER["PHP_SELF"] == "'.(($object->fk_default_home == $objectpage->id) ? '/' : (($shortlangcode != substr($object->lang, 0, 2)) ? '/'.$shortlangcode : '')).'/'.$objectpage->pageurl.'.php") { ?>'."\n";
298 $tplcontent .= '<link rel="alternate" hreflang="'.$shortlangcode.'" href="<?php echo $website->virtualhost; ?>'.(($object->fk_default_home == $objectpage->id) ? '/' : (($shortlangcode != substr($object->lang, 0, 2)) ? '/'.$shortlangcode : '').'/'.$objectpage->pageurl.'.php').'" />'."\n";
299
300 $tplcontent .= '<?php } ?>'."\n";
301 } else { // No language forced, it means the canonical is the one withparams making url unique
302 $canonicalurl = '/'.$objectpage->pageurl.'.php';
303 $arrayofaltlang = explode(',', $listofaltlang);
304
305 foreach ($arrayofaltlang as $altlang) {
306 // Add parameter ID required to be unique/canonical
307 $canonicalurladdidlang = '?__SEO_CANONICAL_URL_PARAMS__';
308 $canonicalurladdidlang .= '&l='.$altlang;
309 $tplcontent .= '<link rel="alternate" hreflang="'.$altlang.'" href="<?php echo $website->virtualhost; ?>'.$canonicalurl.$canonicalurladdidlang.'" />'."\n";
310 }
311
312 $tmpshortlangcode = preg_replace('/[_-].*$/', '', $object->lang); // en_US or en-US -> en
313 $canonicalurladdidlang = '?__SEO_CANONICAL_URL_PARAMS__';
314 $canonicalurladdidlang .= '&l='.$tmpshortlangcode;
315 $tplcontent .= '<link rel="alternate" hreflang="'.$tmpshortlangcode.'" href="<?php echo $website->virtualhost; ?>'.$canonicalurl.$canonicalurladdidlang.'" />'."\n";
316 }
317 }
318 }
319
320 // Add manifest.json. Do we have to add it only on home page ?
321 $tplcontent .= '<?php if ($website->use_manifest) { print \'<link rel="manifest" href="/manifest.json.php" />\'."\n"; } ?>'."\n";
322
323 // Add HTML headers (must be before the Add of the common CSS and js). The common js may content javascript using jquery or a framework loaded by the HTML header.
324 $tplcontent .= '<!-- Include HTML header from common file -->'."\n";
325 $tplcontent .= '<?php if (file_exists(DOL_DATA_ROOT."/website/".$websitekey."/htmlheader.html")) include DOL_DATA_ROOT."/website/".$websitekey."/htmlheader.html"; ?>'."\n";
326 $tplcontent .= '<!-- Include HTML header from page header block -->'."\n";
327 $tplcontent .= preg_replace('/<\/?html>/ims', '', $objectpage->htmlheader)."\n";
328
329 // Add css
330 $tplcontent .= '<!-- Include link to common CSS file -->'."\n";
331 $tplcontent .= '<link rel="stylesheet" href="/styles.css.php?website=<?php echo $websitekey; ?>" type="text/css" />'."\n";
332
333 // Add js
334 $tplcontent .= '<!-- Include link to common JS file -->'."\n";
335 $tplcontent .= '<script nonce="'.getNonce().'" async src="/javascript.js.php?website=<?php echo $websitekey; ?>"></script>'."\n";
336 $tplcontent .= '</head>'."\n";
337 $tplcontent .= "\n";
338
339 // Page content
340 $tplcontent .= '<!-- File content defined in Dolibarr website module editor -->'."\n";
341 $tplcontent .= '<body id="bodywebsite" class="bodywebsite bodywebpage-'.$objectpage->ref.'">'."\n";
342
343 // Import necessary environment for the config page
344 if ($objectpage->type_container == 'setup') {
345 $content = '';
346 $content .= '<?php'."\n";
347 $content .= 'require_once DOL_DOCUMENT_ROOT.\'/core/class/html.formsetup.class.php\';'."\n";
348 $content .= '$formSetup = new FormSetup($db);'."\n";
349 $content .= '?>'."\n";
350 $tplcontent .= $content."\n";
351 }
352
353 $tplcontent .= $objectpage->content."\n";
354
355 // Add logic to handle view and actions for managing parameters in the special config page
356 if ($objectpage->type_container == 'setup') {
357 $content = '<div id="websitetemplateconfigpage">'."\n";
358 $content .= '<?php'."\n";
359 $content .= '/*' . "\n";
360 $content .= ' * Actions' . "\n";
361 $content .= ' */' . "\n";
362 $content .= '$websitetemplateconf = GETPOSTINT(\'websitetemplateconf\');' . "\n";
363 $content .= 'include DOL_DOCUMENT_ROOT.\'/core/actions_setmoduleoptions.inc.php\';' . "\n";
364 $content .= '' . "\n";
365 $content .= '/*' . "\n";
366 $content .= ' * View' . "\n";
367 $content .= ' */' . "\n";
368 $content .= 'print load_fiche_titre($langs->trans(\'SetupAndProperties\'), \'\', \'title_setup\');' . "\n";
369 $content .= '' . "\n";
370 $content .= 'if (!empty($message)) {' . "\n";
371 $content .= ' print $message;' . "\n";
372 $content .= '}' . "\n";
373 $content .= '' . "\n";
374 $content .= 'if (!empty($formSetup->items)) {' . "\n";
375 $content .= ' $html = \'\';' . "\n";
376 $content .= '' . "\n";
377 $content .= ' $html .= \'<form action="config.php" method="POST">\';' . "\n";
378 $content .= ' // Generate hidden values from $formSetup->formHiddenInputs' . "\n";
379 $content .= ' if (!empty($formSetup->formHiddenInputs) && is_array($formSetup->formHiddenInputs)) {' . "\n";
380 $content .= ' foreach ($formSetup->formHiddenInputs as $hiddenKey => $hiddenValue) {' . "\n";
381 $content .= ' $html .= \'<input type="hidden" name="\' . dol_escape_htmltag($hiddenKey) . \'" value="\' . dol_escape_htmltag($hiddenValue) . \'">\';' . "\n";
382 $content .= ' }' . "\n";
383 $content .= ' }' . "\n";
384 $content .= '' . "\n";
385 $content .= ' // Generate output table' . "\n";
386 $content .= ' $html .= $formSetup->generateTableOutput(true);' . "\n";
387 $content .= '' . "\n";
388 $content .= ' // Submit button' . "\n";
389 $content .= ' $html .= \'<input type="hidden" name="action" value="preview">\';' . "\n";
390 $content .= ' $html .= \'<input type="hidden" name="websitetemplateconf" value="1">\';' . "\n";
391 $content .= ' $html .= \'<br>\';' . "\n";
392 $content .= ' $html .= \'<div class="form-setup-button-container center">\';' . "\n";
393 $content .= ' $html .= \'<input class="button button-submit" type="submit" value="\' . $langs->trans("Save") . \'">\';' . "\n";
394 $content .= ' $html .= \'</div>\';' . "\n";
395 $content .= ' $html .= \'</form>\';' . "\n";
396 $content .= '' . "\n";
397 $content .= ' print $html;' . "\n";
398 $content .= '}' . "\n";
399 $content .= '?>' . "\n";
400 $content .= '</div>' . "\n";
401 $tplcontent .= $content."\n";
402 }
403
404
405 $tplcontent .= '</body>'."\n";
406 $tplcontent .= '</html>'."\n";
407
408 $tplcontent .= '<?php // BEGIN PHP'."\n";
409 $tplcontent .= '} catch(Exception $e) { print $e->getMessage(); }'."\n";
410 $tplcontent .= '$tmp = ob_get_contents(); ob_end_clean();'."\n"; // replace with ob_get_clean ?
411
412 $tplcontent .= "// Now fix the content for SEO or multilanguage\n";
413 // Old method for custom SEO
414 if (strpos($objectpage->content, '$__PAGE__KEYWORDS__') !== false) {
415 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"keywords\" content=\".*?\" \/>/ms", "<meta name=\"keywords\" content=\"" . dolPrintHTMLForAttribute($__PAGE__KEYWORDS__ ?? "", 1) . "\" />", $tmp);'."\n";
416 }
417 if (strpos($objectpage->content, '$__PAGE__TITLE__') !== false) {
418 $tplcontent .= '$tmp = preg_replace("/^<title>.*?<\/title>/ms", "<title>" . dolPrintHTMLForAttribute($__PAGE__TITLE__ ?? "", 1) . "</title>", $tmp);'."\n";
419 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"title\" content=\".*?\" \/>/ms", "<meta name=\"title\" content=\"" . dolPrintHTMLForAttribute($__PAGE__TITLE__ ?? "", 1) . "\" />", $tmp);'."\n";
420 }
421 if (strpos($objectpage->content, '$__PAGE__DESC__') !== false) {
422 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"description\" content=\".*?\" \/>/ms", "<meta name=\"description\" content=\"" . dolPrintHTMLForAttribute($__PAGE__DESC__ ?? "", 1) . "\" />", $tmp);'."\n";
423 }
424 // New method for custom SEO
425 if (strpos($objectpage->content, 'define("__SEO_PAGE_LANG__"') !== false) {
426 $tmpshortlangcode = preg_replace('/[_-].*$/', '', $object->lang); // en_US or en-US -> en
427 $tplcontent .= '$tmp = preg_replace("/^<html lang=\"[a-z]+\"/ms", "<html lang=\"" . dolPrintHTMLForAttribute(defined("__SEO_PAGE_LANG__") ? preg_replace(\'/\[_-\].*$/\', "", constant("__SEO_PAGE_LANG__")) : (empty($weblangs->shortlang) ? "'.$tmpshortlangcode.'" : $weblangs->shortlang), 1) . "\"", $tmp);'."\n";
428 }
429 if (strpos($objectpage->content, 'define("__SEO_PAGE_KEYWORDS__"') !== false) {
430 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"keywords\" content=\".*?\" \/>/ms", "<meta name=\"keywords\" content=\"" . dolPrintHTMLForAttribute(constant("__SEO_PAGE_KEYWORDS__"), 1) . "\" />", $tmp);'."\n";
431 }
432 if (strpos($objectpage->content, 'define("__SEO_PAGE_TITLE__"') !== false) {
433 $tplcontent .= '$tmp = preg_replace("/^<title>.*?<\/title>/ms", "<title>" . dolPrintHTMLForAttribute(defined("__SEO_PAGE_TITLE__") ? constant("__SEO_PAGE_TITLE__") : "", 1) . "</title>", $tmp);'."\n";
434 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"title\" content=\".*?\" \/>/ms", "<meta name=\"title\" content=\"" . dolPrintHTMLForAttribute(defined("__SEO_PAGE_TITLE__") ? constant("__SEO_PAGE_TITLE__") : "", 1) . "\" />", $tmp);'."\n";
435 }
436 if (strpos($objectpage->content, 'define("__SEO_PAGE_DESC__"') !== false) {
437 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"description\" content=\".*?\" \/>/ms", "<meta name=\"description\" content=\"" . dolPrintHTMLForAttribute(constant("__SEO_PAGE_DESC__"), 1) . "\" />", $tmp);'."\n";
438 }
439 if (empty($objectpage->lang)) { // We may need to use param into the canonical url
440 $tplcontent .= 'defined("__SEO_CANONICAL_URL_PARAMS__") ? ($tmp = preg_replace("/__SEO_CANONICAL_URL_PARAMS__/", dolPrintHTMLForAttributeUrl(constant("__SEO_CANONICAL_URL_PARAMS__")), $tmp)) : ($tmp = preg_replace("/\?__SEO_CANONICAL_URL_PARAMS__\"/", "\"", preg_replace("/\?__SEO_CANONICAL_URL_PARAMS__&/", "?", $tmp)));'."\n";
441
442 $tmpshortlangcode = preg_replace('/[_-].*$/', '', $object->lang); // en_US or en-US -> en
443 $tplcontent .= '$tmp = preg_replace("/__SEO_CANONICAL_LANG__/", (defined("__SEO_PAGE_LANG__") ? preg_replace(\'/\[_-\].*$/\', "", constant("__SEO_PAGE_LANG__")) : (empty($weblangs->shortlang) ? "'.$tmpshortlangcode.'" : $weblangs->shortlang)), $tmp);'."\n";
444 }
445
446 $tplcontent .= "// Now output the generated page content\n";
447 $tplcontent .= 'dolWebsiteOutput($tmp, "html", '.((int) $objectpage->id).'); dolWebsiteIncrementCounter('.((int) $object->id).', "'.$objectpage->type_container.'", '.((int) $objectpage->id).');'."\n";
448 $tplcontent .= "// END PHP ?>\n";
449 } else {
450 $tplcontent .= "<?php // BEGIN PHP\n";
451 $tplcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
452 $tplcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) {\n";
453 $tplcontent .= ' $pathdepth = count(explode(\'/\', $_SERVER[\'SCRIPT_NAME\'])) - 2;'."\n";
454 $tplcontent .= ' require_once ($pathdepth ? str_repeat(\'../\', $pathdepth) : \'./\').\'master.inc.php\';'."\n";
455 if ($objectpage->disable_waf != 'all') {
456 if (strpos($objectpage->disable_waf, 'NOSCANAUDIOFORINJECTION') !== false) {
457 $tplcontent .= ' define(\'NOSCANAUDIOFORINJECTION\', 1);'."\n";
458 }
459 if (strpos($objectpage->disable_waf, 'NOSCANIFRAMEFORINJECTION') !== false) {
460 $tplcontent .= ' define(\'NOSCANIFRAMEFORINJECTION\', 1);'."\n";
461 }
462 if (strpos($objectpage->disable_waf, 'NOSCANOBJECTFORINJECTION') !== false) {
463 $tplcontent .= ' define(\'NOSCANOBJECTFORINJECTION\', 1);'."\n";
464 }
465 $tplcontent .= ' require_once DOL_DOCUMENT_ROOT.\'/waf.inc.php\';'."\n";
466 }
467 $tplcontent .= "}\n";
468 $tplcontent .= "require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
469 $tplcontent .= "require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
470 if (in_array($objectpage->type_container, array('page', 'blogpost', 'service'))) {
471 $tplcontent .= 'dol_syslog("--- Prepare content of page '.((int) $objectpage->id).' - '.$objectpage->pageurl.'");'."\n";
472 }
473 $tplcontent .= "// END PHP ?>\n";
474
475 $tplcontent .= $objectpage->content;
476 }
477
478 //var_dump($filetpl);exit;
479 $result = file_put_contents($filetpl, $tplcontent);
480
481 dolChmod($filetpl);
482
483 return $result;
484}
485
486
497function dolSaveIndexPage($pathofwebsite, $fileindex, $filetpl, $filewrapper, $object = null)
498{
499 global $db;
500
501 $result1 = false;
502 $result2 = false;
503
504 dol_mkdir($pathofwebsite);
505
506 if ($fileindex) {
507 dol_delete_file($fileindex);
508 $indexcontent = '<?php'."\n";
509 $indexcontent .= "// BEGIN PHP File generated to provide an index.php as Home Page or alias redirector - DO NOT MODIFY - It is just a generated wrapper.\n";
510 $indexcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
511 $indexcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) { require_once './master.inc.php'; } // Load master if not already loaded\n";
512 $indexcontent .= 'if (!empty($_GET[\'pageref\']) || !empty($_GET[\'pagealiasalt\']) || !empty($_GET[\'pageid\'])) {'."\n";
513 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
514 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
515 $indexcontent .= ' redirectToContainer($_GET[\'pageref\'], $_GET[\'pagealiasalt\'], $_GET[\'pageid\']);'."\n";
516 $indexcontent .= "}\n";
517 $indexcontent .= "include_once './".basename($filetpl)."'\n";
518 $indexcontent .= '// END PHP ?>'."\n";
519
520 $result1 = file_put_contents($fileindex, $indexcontent);
521
522 dolChmod($fileindex);
523
524 if (is_object($object) && $object->fk_default_home > 0) {
525 $objectpage = new WebsitePage($db);
526 $objectpage->fetch($object->fk_default_home);
527
528 // Create a version for sublanguages
529 if (empty($objectpage->lang) || !in_array($objectpage->lang, explode(',', $object->otherlang))) {
530 if (!getDolGlobalString('WEBSITE_DISABLE_MAIN_LANGUAGE_INTO_LANGSUBDIR') && is_object($object) && !empty($object->otherlang)) {
531 $dirname = dirname($fileindex);
532 foreach (explode(',', $object->otherlang) as $sublang) {
533 // Avoid to erase main alias file if $sublang is empty string
534 if (empty(trim($sublang))) {
535 continue;
536 }
537 $fileindexsub = $dirname.'/'.$sublang.'/index.php';
538
539 // Same indexcontent than previously but with ../ instead of ./ for master and tpl file include/require_once.
540 $relpath = '..';
541 $indexcontent = '<?php'."\n";
542 $indexcontent .= "// BEGIN PHP File generated to provide an index.php as Home Page or alias redirector - DO NOT MODIFY - It is just a generated wrapper.\n";
543 $indexcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
544 $indexcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) { require_once '".$relpath."/master.inc.php'; } // Load master if not already loaded\n";
545 $indexcontent .= 'if (!empty($_GET[\'pageref\']) || !empty($_GET[\'pagealiasalt\']) || !empty($_GET[\'pageid\'])) {'."\n";
546 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
547 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
548 $indexcontent .= ' redirectToContainer($_GET[\'pageref\'], $_GET[\'pagealiasalt\'], $_GET[\'pageid\']);'."\n";
549 $indexcontent .= "}\n";
550 $indexcontent .= "include_once '".$relpath."/".basename($filetpl)."'\n"; // use .. instead of .
551 $indexcontent .= '// END PHP ?>'."\n";
552 $result = file_put_contents($fileindexsub, $indexcontent);
553 if ($result === false) {
554 dol_syslog("Failed to write file ".$fileindexsub, LOG_WARNING);
555 }
556 dolChmod($fileindexsub);
557 }
558 }
559 }
560 }
561 } else {
562 $result1 = true;
563 }
564
565 if ($filewrapper) {
566 dol_delete_file($filewrapper);
567 $wrappercontent = file_get_contents(DOL_DOCUMENT_ROOT.'/website/samples/wrapper.php');
568
569 $result2 = file_put_contents($filewrapper, $wrappercontent);
570 dolChmod($filewrapper);
571 } else {
572 $result2 = true;
573 }
574
575 return ($result1 && $result2);
576}
577
578
586function dolSaveHtmlHeader($filehtmlheader, $htmlheadercontent)
587{
588 global $pathofwebsite;
589
590 dol_syslog("Save html header into ".$filehtmlheader);
591
592 dol_mkdir($pathofwebsite);
593 $result = file_put_contents($filehtmlheader, $htmlheadercontent);
594 dolChmod($filehtmlheader);
595
596 return $result;
597}
598
606function dolSaveCssFile($filecss, $csscontent)
607{
608 global $pathofwebsite;
609
610 dol_syslog("Save css file into ".$filecss);
611
612 dol_mkdir($pathofwebsite);
613 $result = file_put_contents($filecss, $csscontent);
614 dolChmod($filecss);
615
616 return $result;
617}
618
626function dolSaveJsFile($filejs, $jscontent)
627{
628 global $pathofwebsite;
629
630 dol_syslog("Save js file into ".$filejs);
631
632 dol_mkdir($pathofwebsite);
633 $result = file_put_contents($filejs, $jscontent);
634 dolChmod($filejs);
635
636 return $result;
637}
638
646function dolSaveRobotFile($filerobot, $robotcontent)
647{
648 global $pathofwebsite;
649
650 dol_syslog("Save robot file into ".$filerobot);
651
652 dol_mkdir($pathofwebsite);
653 $result = file_put_contents($filerobot, $robotcontent);
654 dolChmod($filerobot);
655
656 return $result;
657}
658
666function dolSaveHtaccessFile($filehtaccess, $htaccess)
667{
668 global $pathofwebsite;
669
670 dol_syslog("Save htaccess file into ".$filehtaccess);
671
672 dol_mkdir($pathofwebsite);
673 $result = file_put_contents($filehtaccess, $htaccess);
674 dolChmod($filehtaccess);
675
676 return $result;
677}
678
686function dolSaveManifestJson($file, $content)
687{
688 global $pathofwebsite;
689
690 dol_syslog("Save manifest.js.php file into ".$file);
691
692 dol_mkdir($pathofwebsite);
693 $result = file_put_contents($file, $content);
694 dolChmod($file);
695
696 return $result;
697}
698
706function dolSaveReadme($file, $content)
707{
708 global $pathofwebsite;
709
710 dol_syslog("Save README.md file into ".$file);
711
712 dol_mkdir($pathofwebsite);
713 $result = file_put_contents($file, $content);
714 dolChmod($file);
715
716 return $result;
717}
718
726function dolSaveLicense($file, $content)
727{
728 global $pathofwebsite;
729
730 dol_syslog("Save LICENSE file into ".$file);
731
732 dol_mkdir($pathofwebsite);
733 $result = file_put_contents($file, $content);
734 dolChmod($file);
735
736 return $result;
737}
738
746function showWebsiteTemplates(Website $website, int $refresh)
747{
748 global $conf, $langs, $form, $user;
749
750 // We want only one directory for dir of website templates. If an external module need to provide a template, the template must be copied into this directory
751 // when module is enabled.
752 $dirthemes = array('/doctemplates/websites');
753
754 $warningtoshow = '';
755 $arrayoftemplatesfound = array();
756
757 if (count($dirthemes)) {
758 $i = 0;
759 // Scan dir to get all deployed qualified templates
760 foreach ($dirthemes as $dir) {
761 $dirtheme = DOL_DATA_ROOT.$dir;
762
763 if (is_dir($dirtheme)) {
764 $handle = opendir($dirtheme);
765 if (is_resource($handle)) {
766 while (($subdir = readdir($handle)) !== false) { // Scan files of directory
767 //var_dump($dirtheme.'/'.$subdir);
768 if (dol_is_file($dirtheme."/".$subdir) && substr($subdir, 0, 1) != '.' && substr($subdir, 0, 3) != 'CVS' && preg_match('/\.zip$/i', $subdir)) {
769 $subdirwithoutzip = preg_replace('/\.zip$/i', '', $subdir);
770 $subdirwithoutzipwithoutver = preg_replace('/(_exp|_dev)$/i', '', $subdirwithoutzip);
771
772 // Disable not stable themes (dir ends with _exp or _dev)
773 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && preg_match('/_dev$/i', $subdirwithoutzip)) {
774 continue;
775 }
776 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && preg_match('/_exp$/i', $subdirwithoutzip)) {
777 continue;
778 }
779
780 $arrayoftemplatesfound[$subdirwithoutzip] = array('id' => $subdirwithoutzip);
781 $i++;
782 }
783 }
784 }
785 }
786 }
787
788 // Now test if we found template available into source not copied into documents
789 $arrayofsourcetemplates = dol_dir_list(DOL_DOCUMENT_ROOT.'/install/doctemplates/websites', 'directories', 0, 'website_.*$');
790 $arrayofsourcetemplatesnotdeployed = array();
791 foreach ($arrayofsourcetemplates as $val) {
792 // Disable not stable themes (dir ends with _exp or _dev)
793 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && preg_match('/_dev$/i', $val['relativename'])) {
794 continue;
795 }
796 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && preg_match('/_exp$/i', $val['relativename'])) {
797 continue;
798 }
799
800 if (empty($arrayoftemplatesfound[$val['relativename']])) {
801 // We found a template into sources that is not into documents
802 if ($refresh) { // We copy it
803 $src = DOL_DOCUMENT_ROOT.'/install/doctemplates/websites/'.$val['name'];
804 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$val['name'];
805
806 dol_delete_file($dest.'.zip');
807
808 // Compress it
809 global $errormsg; // Used by dol_compress_dir
810 $errormsg = '';
811 $result = dol_compress_dir($src, $dest.'.zip', 'zip');
812 if ($result < 0) {
813 dol_syslog("Error in compress of dir ".$src, LOG_ERR);
814 }
815
816 $srcfile = DOL_DOCUMENT_ROOT.'/install/doctemplates/websites/'.preg_replace('/(_exp|_dev)$/', '', $val['name']).'.jpg';
817 $destfile = DOL_DATA_ROOT.'/doctemplates/websites/'.preg_replace('/(_exp|_dev)$/', '', $val['name']).'.jpg';
818
819 dol_copy($srcfile, $destfile);
820 } else {
821 $arrayofsourcetemplatesnotdeployed[$val['relativename']] = $val;
822 }
823 }
824 }
825
826 if (count($arrayofsourcetemplatesnotdeployed)) {
827 $warningtoshow = img_picto($langs->trans("WarningTemplatesFoundNotDeployedClickRefresh").': '.implode(', ', array_keys($arrayofsourcetemplatesnotdeployed)).'. '.$langs->trans("WarningTemplatesFoundNotDeployedClickRefresh2"), 'warning', 'class="valignmiddle paddingright"');
828 }
829 }
830
831 $colspan = 2;
832
833 $importButtonIsDisabled = 0;
834
835 global $dolibarr_website_allow_custom_php;
836 if (!empty($dolibarr_website_allow_custom_php) && $dolibarr_website_allow_custom_php == 1) {
837 $notdisabledsystemfunction = '';
838 $systemfunctions = array("exec", "passthru", "shell_exec", "system", "popen", "proc_open");
839 foreach ($systemfunctions as $systemfunction) {
840 // @phpstan-ignore-next-line
841 if (function_exists($systemfunction)) {
842 $notdisabledsystemfunction .= ($notdisabledsystemfunction ? ', ' : '').$systemfunction;
843 }
844 }
845 if ($notdisabledsystemfunction) {
846 print '<div class="warning">';
847 print $langs->trans("ImportOfWebsiteTemplateIncludingPHPIsAllowedIf", 'warning');
848 print '</div>';
849
850 $importButtonIsDisabled = 1;
851 }
852 }
853 if (empty($dolibarr_website_allow_custom_php)) {
854 print '<div class="warning">';
855 print $langs->trans("ImportOfWebsiteTemplateIncludingPHPIsDisabled", 'warning');
856 print '</div>';
857
858 $importButtonIsDisabled = 1;
859 }
860
861 print '<!-- For website template import -->'."\n";
862 print '<table class="noborder centpercent">';
863
864 // Title
865 print '<tr class="liste_titre"><th class="titlefield">';
866 print $form->textwithpicto($langs->trans("Templates"), $langs->trans("ThemeDir").' : '.implode(", ", $dirthemes));
867 print ' ';
868 print '<a class="valignmiddle" href="'.$_SERVER["PHP_SELF"].'?website='.urlencode($website->ref).'&importsite=2" rel="noopener noreferrer external">';
869 print $warningtoshow;
870 print img_picto($langs->trans("Refresh"), 'refresh', 'class="valignmiddle"');
871 print '</a>';
872 print '</th>';
873 print '<th class="right">';
874 $url = 'https://www.dolistore.com/index.php?cat=84';
875 print '<a href="'.$url.'" target="_blank" rel="noopener noreferrer external">';
876 print img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans('DownloadMoreSkins');
877 print '</a>';
878 print '</th></tr>';
879
880 print '<tr><td colspan="'.$colspan.'">';
881
882 print '<table class="nobordernopadding centpercent"><tr><td><div class="display-flex">';
883
884 if (count($dirthemes)) {
885 $i = 0;
886 foreach ($dirthemes as $dir) {
887 $dirtheme = DOL_DATA_ROOT.$dir;
888
889 if (is_dir($dirtheme)) {
890 $handle = opendir($dirtheme);
891 if (is_resource($handle)) {
892 while (($subdir = readdir($handle)) !== false) { // Scan files of directory
893 //var_dump($dirtheme.'/'.$subdir);
894 if (dol_is_file($dirtheme."/".$subdir) && substr($subdir, 0, 1) != '.' && substr($subdir, 0, 3) != 'CVS' && preg_match('/\.zip$/i', $subdir)) {
895 $subdirwithoutzip = preg_replace('/\.zip$/i', '', $subdir);
896 $subdirwithoutzipwithoutver = preg_replace('/(_exp|_dev)$/i', '', $subdirwithoutzip);
897
898 // Disable not stable themes (dir ends with _exp or _dev)
899 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && preg_match('/_dev$/i', $subdirwithoutzip)) {
900 continue;
901 }
902 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && preg_match('/_exp$/i', $subdirwithoutzip)) {
903 continue;
904 }
905
906 $arrayoftemplatesfound[$subdirwithoutzip] = array('id' => $subdirwithoutzip);
907
908 print '<div class="inline-block center flex-item" style="min-width: 250px; max-width: 400px; margin-top: 10px; margin-bottom: 10px; margin-right: 20px; margin-left: 20px;">';
909
910 $templatedir = $dirtheme."/".$subdir;
911 $file = $dirtheme."/".$subdirwithoutzipwithoutver.".jpg";
912 $url = DOL_URL_ROOT.'/viewimage.php?modulepart=doctemplateswebsite&file='.$subdirwithoutzipwithoutver.".jpg";
913
914 if (!file_exists($file)) {
915 $url = DOL_URL_ROOT.'/public/theme/common/nophoto.png';
916 }
917
918 $originalimgfile = basename($file);
919 $entity = $conf->entity;
920 $modulepart = 'doctemplateswebsite';
921 $cache = '';
922 $title = $file;
923
924 $ret = '';
925 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalimgfile, 1, '&entity='.$entity);
926 if (!empty($urladvanced)) {
927 $ret .= '<a class="'.$urladvanced['css'].'" target="'.$urladvanced['target'].'" mime="'.$urladvanced['mime'].'" href="'.$urladvanced['url'].'">';
928 } else {
929 $ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.urlencode($modulepart).'&entity='.((int) $entity).'&file='.urlencode($originalimgfile).'&cache='.((int) $cache).'">';
930 }
931 print $ret;
932 print '<img class="img-skinthumb shadow" src="'.$url.'" border="0" alt="'.$title.'" title="'.$title.'" style="margin-bottom: 5px;">';
933 print '</a>';
934
935 print '<br>';
936 print $subdir;
937 print '<br>';
938 print '<span class="opacitymedium">'.dol_print_size(dol_filesize($dirtheme."/".$subdir), 1, 1).' - '.dol_print_date(dol_filemtime($templatedir), 'dayhour', 'tzuserrel').'</span>';
939 if ($user->hasRight('website', 'delete')) {
940 print ' <a href="'.$_SERVER["PHP_SELF"].'?action=deletetemplate&token='.newToken().'&website='.urlencode($website->ref).'&templateuserfile='.urlencode($subdir).'">'.img_picto('', 'delete').'</a>';
941 }
942 print '<br><a href="'.$_SERVER["PHP_SELF"].'?action=importsiteconfirm&token='.newToken().'&website='.urlencode($website->ref).'&templateuserfile='.urlencode($subdir).'" class="button'.($importButtonIsDisabled ? ' disabled' : '').'">'.$langs->trans("Load").'</a>';
943 print '</div>';
944
945 $i++;
946 }
947 }
948 print '<div class="inline-block center flex-item" style="min-width: 250px; max-width: 400px;margin-top: 10px; margin-bottom: 10px; margin-right: 20px; margin-left: 20px;"></div>';
949 print '<div class="inline-block center flex-item" style="min-width: 250px; max-width: 400px;margin-top: 10px; margin-bottom: 10px; margin-right: 20px; margin-left: 20px;"></div>';
950 print '<div class="inline-block center flex-item" style="min-width: 250px; max-width: 400px;margin-top: 10px; margin-bottom: 10px; margin-right: 20px; margin-left: 20px;"></div>';
951 print '<div class="inline-block center flex-item" style="min-width: 250px; max-width: 400px;margin-top: 10px; margin-bottom: 10px; margin-right: 20px; margin-left: 20px;"></div>';
952 print '<div class="inline-block center flex-item" style="min-width: 250px; max-width: 400px;margin-top: 10px; margin-bottom: 10px; margin-right: 20px; margin-left: 20px;"></div>';
953 }
954 }
955 }
956 } else {
957 print '<span class="opacitymedium">'.$langs->trans("None").'</span>';
958 }
959
960 print '</div></td></tr></table>';
961
962 print '</td></tr>';
963 print '</table>';
964}
965
966
982function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
983{
984 global $langs, $user;
985
986 $error = 0;
987
988 if (empty($phpfullcodestringold) && empty($phpfullcodestring)) {
989 return 0;
990 }
991
992 // First check permission
993 if ($phpfullcodestringold != $phpfullcodestring) {
994 global $dolibarr_website_allow_custom_php;
995 if (empty($dolibarr_website_allow_custom_php)) { // Case of $dolibarr_website_allow_custom_php = 0
996 $error++;
997 setEventMessages($langs->trans("NotAllowedToAddDynamicContentDisabledGlobaly", 'dolibarr_website_allow_custom_php'), null, 'errors');
998 } elseif ($dolibarr_website_allow_custom_php == 1) { // Case of $dolibarr_website_allow_custom_php = 1
999 $notdisabledsystemfunction = '';
1000 $systemfunctions = array("exec", "passthru", "shell_exec", "system", "popen", "proc_open");
1001 foreach ($systemfunctions as $systemfunction) {
1002 // @phpstan-ignore-next-line
1003 if (function_exists($systemfunction)) {
1004 $notdisabledsystemfunction .= ($notdisabledsystemfunction ? ', ' : '').$systemfunction;
1005 }
1006 }
1007 if ($notdisabledsystemfunction) {
1008 $error++;
1009 $langs->load("errors");
1010 setEventMessages($langs->trans("ErrorDynamicPHPContentNotAllowed", 'dolibarr_website_allow_custom_php'), null, 'errors');
1011 }
1012 }
1013
1014 if (!$error && !$user->hasRight('website', 'writephp')) {
1015 $error++;
1016 setEventMessages($langs->trans("NotAllowedToAddDynamicContent"), null, 'errors');
1017 }
1018 }
1019
1020 $phpfullcodestringnew = $phpfullcodestring;
1021
1022 // Then check forbidden commands
1023 if (!$error) {
1024 if (getDolGlobalString("WEBSITE_DISALLOW_DOLLAR_UNDERSCORE")) {
1025 $phpfullcodestring = preg_replace('/\$_COOKIE\[/', '__DOLLARCOOKIE__', $phpfullcodestring);
1026 $phpfullcodestring = preg_replace('/\$_FILES\[/', '__DOLLARFILES__', $phpfullcodestring);
1027 $phpfullcodestring = preg_replace('/\$_SESSION\[/', '__DOLLARSESSION__', $phpfullcodestring);
1028 $forbiddenphpstrings = array('$$', '$_', '}[');
1029 } else {
1030 $forbiddenphpstrings = array('$$', '}[');
1031 }
1032 //$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_FILES', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
1033 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', 'ReflectionFunction'));
1034
1035 $forbiddenphpfunctions = array();
1036 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("base64"."_"."decode", "rawurl"."decode", "url"."decode", "str"."_rot13", "hex"."2bin")); // name of forbidden functions are split to avoid false positive
1037 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
1038 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
1039 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
1040 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
1041
1042 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_EXEC')) { // If experimental option is not on, we TRY to disallow functions to execute commands
1043 // WARNING: This is not reliable. If you don't want user to run code on the server, just NEVER give him permission to execute dynamic PHP in website module (or never enable website module).
1044 // The following list shares a lot of content with the whitelist guard in dol_eval() function.
1045
1046 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
1047
1048 // Functions with callable parameters
1049 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
1050 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("array_all", "array_any", "array_diff_ukey", "array_filter", "array_find", "array_find_key", "array_map", "array_reduce", "array_intersect_uassoc", "array_intersect_ukey", "array_walk", "array_walk_recursive"));
1051 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort"));
1052 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
1053 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("error_log", "set_error_handler", "set_exception_handler", "libxml_set_external_entity_loader", "register_shutdown_function", "register_tick_function", "unregister_tick_function"));
1054 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
1055 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
1056 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
1057
1058 // Exec functions
1059 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
1060 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("pcntl_alarm", "pcntl_exec", "pcntl_fork", "pcntl_waitpid", "pcntl_wait", "pcntl_wifexited", "pcntl_wifstopped", "pcntl_wifsignaled", "pcntl_wifcontinued", "pcntl_wexitstatus", "pcntl_wtermsig", "pcntl_wstopsig", "pcntl_signal"));
1061 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("pcntl_signal_get_handler", "pcntl_signal_dispatch", "pcntl_get_last_error", "pcntl_strerror", "pcntl_sigprocmask", "pcntl_sigwaitinfo", "pcntl_sigtimedwait", "pcntl_getpriority", "pcntl_async_signals", "pcntl_unshare", ));
1062 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
1063 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
1064 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
1065
1066 // Include functions
1067 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
1068 }
1069 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_WRITE')) { // If option is not on, we disallow functions to write files
1070 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_decode", "dol_dir_list", "dol_dir_list_in_database", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
1071 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("chdir", "dir", "fopen", "file", "file_exists", "file_get_contents", "file_put_contents", "fget", "fgetc", "fgetcsv", "flock", "fputs", "fputscsv", "fpassthru", "fscanf", "fseek", "fwrite", "is_file", "is_dir", "is_link", "mkdir", "opendir", "rmdir", "scandir", "symlink", "touch", "unlink", "umask"));
1072 }
1073 if (getDolGlobalString('WEBSITE_PHP_DISALLOW_READ')) { // If option is not on, we disallow functions to read files
1074 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_decode")); // more dolibarr functions
1075 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("file", "fopen", "file_get_contents", "fgets", "fgetscsv", "fgetss", "fread"));
1076 }
1077
1078 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
1079
1080 foreach ($forbiddenphpstrings as $forbiddenphpstring) {
1081 if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ims', $phpfullcodestring)) {
1082 $error++;
1083 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpstring), null, 'errors');
1084 break;
1085 }
1086 }
1087 /* replaced with next block
1088 foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function(" but also "'function'(" and "function ("
1089 if (preg_match('/'.$forbiddenphpfunction.'[\'\s]*\‍(/ims', $phpfullcodestring)) {
1090 $error++;
1091 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
1092 break;
1093 }
1094 }*/
1095 foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function" whatever is "function(" or "function'(" or "function (" or "function"
1096 $reg = array();
1097 if (preg_match('/\b'.$forbiddenphpfunction.'(=| class=|\b)/ims', $phpfullcodestring, $reg)) {
1098 if ($reg[1] != '=' && $reg[1] != ' class=') { // So we may accept string ...&file=... even if 'file' is in forbiddenphpfunction. And we accept content with '<dl class=...'
1099 $error++;
1100 //setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction)." :<br>\n".$phpfullcodestring, null, 'errors');
1101 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
1102 break;
1103 }
1104 }
1105 }
1106
1107 foreach ($forbiddenphpmethods as $forbiddenphpmethod) {
1108 if (preg_match('/->'.$forbiddenphpmethod.'/ims', $phpfullcodestring)) {
1109 $error++;
1110 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpmethod), null, 'errors');
1111 break;
1112 }
1113 }
1114 }
1115
1116 // This char can be used to execute RCE for example by using echo `ls`
1117 if (!$error) {
1118 $forbiddenphpchars = array();
1119 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_DANGEROUS_CHARS')) { // If option is not on, we disallow functions to execute commands
1120 $forbiddenphpchars = array("`");
1121 }
1122 foreach ($forbiddenphpchars as $forbiddenphpchar) {
1123 if (preg_match('/'.$forbiddenphpchar.'/ims', $phpfullcodestring)) {
1124 $error++;
1125 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors');
1126 break;
1127 }
1128 }
1129 }
1130
1131 // Deny code to call a function obfuscated with comment, like "exec/*...*/ ('ls')";
1132 if (!$error) {
1133 if (preg_match('/\*\/\s*\‍(/ims', $phpfullcodestring)) {
1134 $error++;
1135 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", "exec/*...*/ ('ls')"), null, 'errors');
1136 }
1137 }
1138
1139 // Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
1140 if (!$error) {
1141 if (preg_match('/[}\]]\s*\‍(/ims', $phpfullcodestring)) {
1142 $error++;
1143 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", ']('), null, 'errors');
1144 }
1145 }
1146
1147 // Deny dynamic functions '$xxx(' or '$xxx (' or '$xxx" ('
1148 if (!$error) {
1149 if (preg_match('/\$[a-z0-9_\-\/\*\"]+\s*\‍(/ims', $phpfullcodestring)) {
1150 $error++;
1151 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", '$...('), null, 'errors');
1152 }
1153 }
1154
1155 // No need to block $conf->global->aaa() because PHP try to run the method aaa of $conf->global and not the function into $conf->global->aaa.
1156
1157 // Then check if installmodules.lock does not block dynamic PHP code change.
1158 if ($phpfullcodestringold != $phpfullcodestringnew) {
1159 if (!$error) {
1160 $dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT);
1161 $allowimportsite = true;
1162 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1163 if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) {
1164 $allowimportsite = false;
1165 }
1166
1167 if (!$allowimportsite) {
1168 $error++;
1169 // Blocked by installmodules.lock
1170 if (getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')) {
1171 // Show clean corporate message
1172 $message = $langs->trans('InstallModuleFromWebHasBeenDisabledContactUs');
1173 } else {
1174 // Show technical generic message
1175 $message = $langs->trans("InstallModuleFromWebHasBeenDisabledByFile", $dolibarrdataroot.'/installmodules.lock');
1176 }
1177 setEventMessages($message, null, 'errors');
1178 }
1179 }
1180 }
1181
1182 return $error;
1183}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class Website.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_filemtime($pathoffile)
Return time of a file.
dol_filesize($pathoffile)
Return size of a file.
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
archiveOrBackupFile($srcfile, $max_versions=5, $archivedir='', $suffix="v", $moveorcopy='move')
Manage backup versions for a given file, ensuring only a maximum number of versions are kept.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_sanitizePathName($str, $newstr='_', $unaccent=0, $allowdash=0)
Clean a string to use it as a path name.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
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.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dolSaveMasterFile($filemaster)
Save content of a page on disk.
dolSaveLicense($file, $content)
Save content of a page on disk.
checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
Check that the new string $phpfullcodestring contains only php code (including <php tag)
dolSaveHtmlHeader($filehtmlheader, $htmlheadercontent)
Save content of a page on disk.
dolSaveReadme($file, $content)
Save content of a page on disk.
dolSaveManifestJson($file, $content)
Save content of a page on disk.
dolSaveIndexPage($pathofwebsite, $fileindex, $filetpl, $filewrapper, $object=null)
Save content of the index.php and/or the wrapper.php page.
dolSavePageAlias($filealias, $object, $objectpage)
Save an alias page on disk (A page that include the reference page).
dolSaveHtaccessFile($filehtaccess, $htaccess)
Save content of a page on disk.
dolSaveJsFile($filejs, $jscontent)
Save content of a page on disk.
dolSavePageContent($filetpl, Website $object, WebsitePage $objectpage, $backupold=0)
Save content of a page on disk (page name is generally ID_of_page.php).
showWebsiteTemplates(Website $website, int $refresh)
Show list of themes.
dolSaveCssFile($filecss, $csscontent)
Save content of a page on disk.
dolSaveRobotFile($filerobot, $robotcontent)
Save content of a page on disk.