dolibarr 24.0.0-beta
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(constant("__SEO_PAGE_TITLE__"), 1) . "</title>", $tmp);'."\n";
434 $tplcontent .= '$tmp = preg_replace("/^<meta name=\"title\" content=\".*?\" \/>/ms", "<meta name=\"title\" content=\"" . dolPrintHTMLForAttribute(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
981function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
982{
983 global $langs, $user;
984
985 $error = 0;
986
987 if (empty($phpfullcodestringold) && empty($phpfullcodestring)) {
988 return 0;
989 }
990
991 // First check permission
992 if ($phpfullcodestringold != $phpfullcodestring) {
993 global $dolibarr_website_allow_custom_php;
994 if (empty($dolibarr_website_allow_custom_php)) { // Case of $dolibarr_website_allow_custom_php = 0
995 $error++;
996 setEventMessages($langs->trans("NotAllowedToAddDynamicContentDisabledGlobaly", 'dolibarr_website_allow_custom_php'), null, 'errors');
997 } elseif ($dolibarr_website_allow_custom_php == 1) { // Case of $dolibarr_website_allow_custom_php = 1
998 $notdisabledsystemfunction = '';
999 $systemfunctions = array("exec", "passthru", "shell_exec", "system", "popen", "proc_open");
1000 foreach ($systemfunctions as $systemfunction) {
1001 // @phpstan-ignore-next-line
1002 if (function_exists($systemfunction)) {
1003 $notdisabledsystemfunction .= ($notdisabledsystemfunction ? ', ' : '').$systemfunction;
1004 }
1005 }
1006 if ($notdisabledsystemfunction) {
1007 $error++;
1008 $langs->load("errors");
1009 setEventMessages($langs->trans("ErrorDynamicPHPContentNotAllowed", 'dolibarr_website_allow_custom_php'), null, 'errors');
1010 }
1011 }
1012
1013 if (!$error && !$user->hasRight('website', 'writephp')) {
1014 $error++;
1015 setEventMessages($langs->trans("NotAllowedToAddDynamicContent"), null, 'errors');
1016 }
1017 }
1018
1019 $phpfullcodestringnew = $phpfullcodestring;
1020
1021 // Then check forbidden commands
1022 if (!$error) {
1023 if (getDolGlobalString("WEBSITE_DISALLOW_DOLLAR_UNDERSCORE")) {
1024 $phpfullcodestring = preg_replace('/\$_COOKIE\[/', '__DOLLARCOOKIE__', $phpfullcodestring);
1025 $phpfullcodestring = preg_replace('/\$_FILES\[/', '__DOLLARFILES__', $phpfullcodestring);
1026 $phpfullcodestring = preg_replace('/\$_SESSION\[/', '__DOLLARSESSION__', $phpfullcodestring);
1027 $forbiddenphpstrings = array('$$', '$_', '}[');
1028 } else {
1029 $forbiddenphpstrings = array('$$', '}[');
1030 }
1031 //$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_FILES', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
1032 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', 'ReflectionFunction'));
1033
1034 $forbiddenphpfunctions = array();
1035 //$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
1036 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
1037 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
1038 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
1039 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
1040 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_EXEC')) { // If option is not on, we disallow functions to execute commands
1041 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
1042 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "executeCLI", "verifCond")); // native dolibarr functions
1043 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
1044 }
1045 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_WRITE')) { // If option is not on, we disallow functions to write files
1046 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_compress_dir", "dol_delete_file", "dol_delete_dir", "dol_delete_dir_recursive", "dol_copy", "archiveOrBackupFile")); // more dolibarr functions
1047 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "flock", "fputs", "fputscsv", "fwrite", "fpassthru", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
1048 }
1049 if (getDolGlobalString('WEBSITE_PHP_DISALLOW_READ')) { // If option is not on, we disallow functions to read files
1050 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_decode")); // more dolibarr functions
1051 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("file", "fopen", "file_get_contents", "fgets", "fgetscsv", "fgetss", "fread"));
1052 }
1053
1054 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include"));
1055
1056 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
1057
1058 foreach ($forbiddenphpstrings as $forbiddenphpstring) {
1059 if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ims', $phpfullcodestring)) {
1060 $error++;
1061 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpstring), null, 'errors');
1062 break;
1063 }
1064 }
1065 /* replaced with next block
1066 foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function(" but also "'function'(" and "function ("
1067 if (preg_match('/'.$forbiddenphpfunction.'[\'\s]*\‍(/ims', $phpfullcodestring)) {
1068 $error++;
1069 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
1070 break;
1071 }
1072 }*/
1073 foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function" whatever is "function(" or "function'(" or "function (" or "function"
1074 if (preg_match('/\b'.$forbiddenphpfunction.'\b/ims', $phpfullcodestring)) {
1075 $error++;
1076 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
1077 break;
1078 }
1079 }
1080
1081 foreach ($forbiddenphpmethods as $forbiddenphpmethod) {
1082 if (preg_match('/->'.$forbiddenphpmethod.'/ims', $phpfullcodestring)) {
1083 $error++;
1084 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpmethod), null, 'errors');
1085 break;
1086 }
1087 }
1088 }
1089
1090 // This char can be used to execute RCE for example by using echo `ls`
1091 if (!$error) {
1092 $forbiddenphpchars = array();
1093 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_DANGEROUS_CHARS')) { // If option is not on, we disallow functions to execute commands
1094 $forbiddenphpchars = array("`");
1095 }
1096 foreach ($forbiddenphpchars as $forbiddenphpchar) {
1097 if (preg_match('/'.$forbiddenphpchar.'/ims', $phpfullcodestring)) {
1098 $error++;
1099 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors');
1100 break;
1101 }
1102 }
1103 }
1104
1105 // Deny code to call a function obfuscated with comment, like "exec/*...*/ ('ls')";
1106 if (!$error) {
1107 if (preg_match('/\*\/\s*\‍(/ims', $phpfullcodestring)) {
1108 $error++;
1109 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", "exec/*...*/ ('ls')"), null, 'errors');
1110 }
1111 }
1112
1113 // Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
1114 if (!$error) {
1115 if (preg_match('/[}\]]\s*\‍(/ims', $phpfullcodestring)) {
1116 $error++;
1117 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", ']('), null, 'errors');
1118 }
1119 }
1120
1121 // Deny dynamic functions '$xxx(' or '$xxx (' or '$xxx" ('
1122 if (!$error) {
1123 if (preg_match('/\$[a-z0-9_\-\/\*\"]+\s*\‍(/ims', $phpfullcodestring)) {
1124 $error++;
1125 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", '$...('), null, 'errors');
1126 }
1127 }
1128
1129 // 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.
1130
1131 // Then check if installmodules.lock does not block dynamic PHP code change.
1132 if ($phpfullcodestringold != $phpfullcodestringnew) {
1133 if (!$error) {
1134 $dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT);
1135 $allowimportsite = true;
1136 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1137 if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) {
1138 $allowimportsite = false;
1139 }
1140
1141 if (!$allowimportsite) {
1142 $error++;
1143 // Blocked by installmodules.lock
1144 if (getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')) {
1145 // Show clean corporate message
1146 $message = $langs->trans('InstallModuleFromWebHasBeenDisabledContactUs');
1147 } else {
1148 // Show technical generic message
1149 $message = $langs->trans("InstallModuleFromWebHasBeenDisabledByFile", $dolibarrdataroot.'/installmodules.lock');
1150 }
1151 setEventMessages($message, null, 'errors');
1152 }
1153 }
1154 }
1155
1156 return $error;
1157}
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.
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.