dolibarr 25.0.0-alpha
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 // Let modules rewrite the final page content (e.g. canonical/hreflang links, when a module
447 // implements its own URL rewriting scheme that core has no knowledge of). Runs on every
448 // request (not only at page compile time), so it applies no matter which code path last
449 // recompiled this page (website editor, inline AJAX edit, a module's own template reset...).
450 $tplcontent .= "// Let modules rewrite the final page content (hook)\n";
451 $tplcontent .= 'global $hookmanager;'."\n";
452 $tplcontent .= 'if (is_object($hookmanager)) {'."\n";
453 $tplcontent .= ' $hookmanager->initHooks(array(\'websitepagecontent\'));'."\n";
454 $tplcontent .= ' $parameters = array(\'tmp\' => &$tmp);'."\n";
455 $tplcontent .= ' $hookmanager->executeHooks(\'printWebsitePageContent\', $parameters, $websitepage);'."\n";
456 $tplcontent .= '}'."\n";
457
458 $tplcontent .= "// Now output the generated page content\n";
459 $tplcontent .= 'dolWebsiteOutput($tmp, "html", '.((int) $objectpage->id).'); dolWebsiteIncrementCounter('.((int) $object->id).', "'.$objectpage->type_container.'", '.((int) $objectpage->id).');'."\n";
460 $tplcontent .= "// END PHP ?>\n";
461 } else {
462 $tplcontent .= "<?php // BEGIN PHP\n";
463 $tplcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
464 $tplcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) {\n";
465 $tplcontent .= ' $pathdepth = count(explode(\'/\', $_SERVER[\'SCRIPT_NAME\'])) - 2;'."\n";
466 $tplcontent .= ' require_once ($pathdepth ? str_repeat(\'../\', $pathdepth) : \'./\').\'master.inc.php\';'."\n";
467 if ($objectpage->disable_waf != 'all') {
468 if (strpos($objectpage->disable_waf, 'NOSCANAUDIOFORINJECTION') !== false) {
469 $tplcontent .= ' define(\'NOSCANAUDIOFORINJECTION\', 1);'."\n";
470 }
471 if (strpos($objectpage->disable_waf, 'NOSCANIFRAMEFORINJECTION') !== false) {
472 $tplcontent .= ' define(\'NOSCANIFRAMEFORINJECTION\', 1);'."\n";
473 }
474 if (strpos($objectpage->disable_waf, 'NOSCANOBJECTFORINJECTION') !== false) {
475 $tplcontent .= ' define(\'NOSCANOBJECTFORINJECTION\', 1);'."\n";
476 }
477 $tplcontent .= ' require_once DOL_DOCUMENT_ROOT.\'/waf.inc.php\';'."\n";
478 }
479 $tplcontent .= "}\n";
480 $tplcontent .= "require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
481 $tplcontent .= "require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
482 if (in_array($objectpage->type_container, array('page', 'blogpost', 'service'))) {
483 $tplcontent .= 'dol_syslog("--- Prepare content of page '.((int) $objectpage->id).' - '.$objectpage->pageurl.'");'."\n";
484 }
485 $tplcontent .= "// END PHP ?>\n";
486
487 $tplcontent .= $objectpage->content;
488 }
489
490 //var_dump($filetpl);exit;
491 $result = file_put_contents($filetpl, $tplcontent);
492
493 dolChmod($filetpl);
494
495 return $result;
496}
497
498
509function dolSaveIndexPage($pathofwebsite, $fileindex, $filetpl, $filewrapper, $object = null)
510{
511 global $db;
512
513 $result1 = false;
514 $result2 = false;
515
516 dol_mkdir($pathofwebsite);
517
518 if ($fileindex) {
519 dol_delete_file($fileindex);
520 $indexcontent = '<?php'."\n";
521 $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";
522 $indexcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
523 $indexcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) { require_once './master.inc.php'; } // Load master if not already loaded\n";
524 $indexcontent .= 'if (!empty($_GET[\'pageref\']) || !empty($_GET[\'pagealiasalt\']) || !empty($_GET[\'pageid\'])) {'."\n";
525 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
526 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
527 $indexcontent .= ' redirectToContainer($_GET[\'pageref\'], $_GET[\'pagealiasalt\'], $_GET[\'pageid\']);'."\n";
528 $indexcontent .= "}\n";
529 $indexcontent .= "include_once './".basename($filetpl)."'\n";
530 $indexcontent .= '// END PHP ?>'."\n";
531
532 $result1 = file_put_contents($fileindex, $indexcontent);
533
534 dolChmod($fileindex);
535
536 if (is_object($object) && $object->fk_default_home > 0) {
537 $objectpage = new WebsitePage($db);
538 $objectpage->fetch($object->fk_default_home);
539
540 // Create a version for sublanguages
541 if (empty($objectpage->lang) || !in_array($objectpage->lang, explode(',', $object->otherlang))) {
542 if (!getDolGlobalString('WEBSITE_DISABLE_MAIN_LANGUAGE_INTO_LANGSUBDIR') && is_object($object) && !empty($object->otherlang)) {
543 $dirname = dirname($fileindex);
544 foreach (explode(',', $object->otherlang) as $sublang) {
545 // Avoid to erase main alias file if $sublang is empty string
546 if (empty(trim($sublang))) {
547 continue;
548 }
549 $fileindexsub = $dirname.'/'.$sublang.'/index.php';
550
551 // Same indexcontent than previously but with ../ instead of ./ for master and tpl file include/require_once.
552 $relpath = '..';
553 $indexcontent = '<?php'."\n";
554 $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";
555 $indexcontent .= '$websitekey=basename(__DIR__); if (empty($websitepagefile)) $websitepagefile=__FILE__;'."\n";
556 $indexcontent .= "if (! defined('USEDOLIBARRSERVER') && ! defined('USEDOLIBARREDITOR')) { require_once '".$relpath."/master.inc.php'; } // Load master if not already loaded\n";
557 $indexcontent .= 'if (!empty($_GET[\'pageref\']) || !empty($_GET[\'pagealiasalt\']) || !empty($_GET[\'pageid\'])) {'."\n";
558 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/lib/website.lib.php';\n";
559 $indexcontent .= " require_once DOL_DOCUMENT_ROOT.'/core/website.inc.php';\n";
560 $indexcontent .= ' redirectToContainer($_GET[\'pageref\'], $_GET[\'pagealiasalt\'], $_GET[\'pageid\']);'."\n";
561 $indexcontent .= "}\n";
562 $indexcontent .= "include_once '".$relpath."/".basename($filetpl)."'\n"; // use .. instead of .
563 $indexcontent .= '// END PHP ?>'."\n";
564 $result = file_put_contents($fileindexsub, $indexcontent);
565 if ($result === false) {
566 dol_syslog("Failed to write file ".$fileindexsub, LOG_WARNING);
567 }
568 dolChmod($fileindexsub);
569 }
570 }
571 }
572 }
573 } else {
574 $result1 = true;
575 }
576
577 if ($filewrapper) {
578 dol_delete_file($filewrapper);
579 $wrappercontent = file_get_contents(DOL_DOCUMENT_ROOT.'/website/samples/wrapper.php');
580
581 $result2 = file_put_contents($filewrapper, $wrappercontent);
582 dolChmod($filewrapper);
583 } else {
584 $result2 = true;
585 }
586
587 return ($result1 && $result2);
588}
589
590
598function dolSaveHtmlHeader($filehtmlheader, $htmlheadercontent)
599{
600 global $pathofwebsite;
601
602 dol_syslog("Save html header into ".$filehtmlheader);
603
604 dol_mkdir($pathofwebsite);
605 $result = file_put_contents($filehtmlheader, $htmlheadercontent);
606 dolChmod($filehtmlheader);
607
608 return $result;
609}
610
618function dolSaveCssFile($filecss, $csscontent)
619{
620 global $pathofwebsite;
621
622 dol_syslog("Save css file into ".$filecss);
623
624 dol_mkdir($pathofwebsite);
625 $result = file_put_contents($filecss, $csscontent);
626 dolChmod($filecss);
627
628 return $result;
629}
630
638function dolSaveJsFile($filejs, $jscontent)
639{
640 global $pathofwebsite;
641
642 dol_syslog("Save js file into ".$filejs);
643
644 dol_mkdir($pathofwebsite);
645 $result = file_put_contents($filejs, $jscontent);
646 dolChmod($filejs);
647
648 return $result;
649}
650
658function dolSaveRobotFile($filerobot, $robotcontent)
659{
660 global $pathofwebsite;
661
662 dol_syslog("Save robot file into ".$filerobot);
663
664 dol_mkdir($pathofwebsite);
665 $result = file_put_contents($filerobot, $robotcontent);
666 dolChmod($filerobot);
667
668 return $result;
669}
670
678function dolSaveHtaccessFile($filehtaccess, $htaccess)
679{
680 global $pathofwebsite;
681
682 dol_syslog("Save htaccess file into ".$filehtaccess);
683
684 dol_mkdir($pathofwebsite);
685 $result = file_put_contents($filehtaccess, $htaccess);
686 dolChmod($filehtaccess);
687
688 return $result;
689}
690
698function dolSaveManifestJson($file, $content)
699{
700 global $pathofwebsite;
701
702 dol_syslog("Save manifest.js.php file into ".$file);
703
704 dol_mkdir($pathofwebsite);
705 $result = file_put_contents($file, $content);
706 dolChmod($file);
707
708 return $result;
709}
710
718function dolSaveReadme($file, $content)
719{
720 global $pathofwebsite;
721
722 dol_syslog("Save README.md file into ".$file);
723
724 dol_mkdir($pathofwebsite);
725 $result = file_put_contents($file, $content);
726 dolChmod($file);
727
728 return $result;
729}
730
738function dolSaveLicense($file, $content)
739{
740 global $pathofwebsite;
741
742 dol_syslog("Save LICENSE file into ".$file);
743
744 dol_mkdir($pathofwebsite);
745 $result = file_put_contents($file, $content);
746 dolChmod($file);
747
748 return $result;
749}
750
758function showWebsiteTemplates(Website $website, int $refresh)
759{
760 global $conf, $langs, $form, $user;
761
762 // 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
763 // when module is enabled.
764 $dirthemes = array('/doctemplates/websites');
765
766 $warningtoshow = '';
767 $arrayoftemplatesfound = array();
768
769 if (count($dirthemes)) {
770 $i = 0;
771 // Scan dir to get all deployed qualified templates
772 foreach ($dirthemes as $dir) {
773 $dirtheme = DOL_DATA_ROOT.$dir;
774
775 if (is_dir($dirtheme)) {
776 $handle = opendir($dirtheme);
777 if (is_resource($handle)) {
778 while (($subdir = readdir($handle)) !== false) { // Scan files of directory
779 //var_dump($dirtheme.'/'.$subdir);
780 if (dol_is_file($dirtheme."/".$subdir) && substr($subdir, 0, 1) != '.' && substr($subdir, 0, 3) != 'CVS' && preg_match('/\.zip$/i', $subdir)) {
781 $subdirwithoutzip = preg_replace('/\.zip$/i', '', $subdir);
782 $subdirwithoutzipwithoutver = preg_replace('/(_exp|_dev)$/i', '', $subdirwithoutzip);
783
784 // Disable not stable themes (dir ends with _exp or _dev)
785 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && preg_match('/_dev$/i', $subdirwithoutzip)) {
786 continue;
787 }
788 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && preg_match('/_exp$/i', $subdirwithoutzip)) {
789 continue;
790 }
791
792 $arrayoftemplatesfound[$subdirwithoutzip] = array('id' => $subdirwithoutzip);
793 $i++;
794 }
795 }
796 }
797 }
798 }
799
800 // Now test if we found template available into source not copied into documents
801 $arrayofsourcetemplates = dol_dir_list(DOL_DOCUMENT_ROOT.'/install/doctemplates/websites', 'directories', 0, 'website_.*$');
802 $arrayofsourcetemplatesnotdeployed = array();
803 foreach ($arrayofsourcetemplates as $val) {
804 // Disable not stable themes (dir ends with _exp or _dev)
805 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && preg_match('/_dev$/i', $val['relativename'])) {
806 continue;
807 }
808 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && preg_match('/_exp$/i', $val['relativename'])) {
809 continue;
810 }
811
812 if (empty($arrayoftemplatesfound[$val['relativename']])) {
813 // We found a template into sources that is not into documents
814 if ($refresh) { // We copy it
815 $src = DOL_DOCUMENT_ROOT.'/install/doctemplates/websites/'.$val['name'];
816 $dest = DOL_DATA_ROOT.'/doctemplates/websites/'.$val['name'];
817
818 dol_delete_file($dest.'.zip');
819
820 // Compress it
821 global $errormsg; // Used by dol_compress_dir
822 $errormsg = '';
823 $result = dol_compress_dir($src, $dest.'.zip', 'zip');
824 if ($result < 0) {
825 dol_syslog("Error in compress of dir ".$src, LOG_ERR);
826 }
827
828 $srcfile = DOL_DOCUMENT_ROOT.'/install/doctemplates/websites/'.preg_replace('/(_exp|_dev)$/', '', $val['name']).'.jpg';
829 $destfile = DOL_DATA_ROOT.'/doctemplates/websites/'.preg_replace('/(_exp|_dev)$/', '', $val['name']).'.jpg';
830
831 dol_copy($srcfile, $destfile);
832 } else {
833 $arrayofsourcetemplatesnotdeployed[$val['relativename']] = $val;
834 }
835 }
836 }
837
838 if (count($arrayofsourcetemplatesnotdeployed)) {
839 $warningtoshow = img_picto($langs->trans("WarningTemplatesFoundNotDeployedClickRefresh").': '.implode(', ', array_keys($arrayofsourcetemplatesnotdeployed)).'. '.$langs->trans("WarningTemplatesFoundNotDeployedClickRefresh2"), 'warning', 'class="valignmiddle paddingright"');
840 }
841 }
842
843 $colspan = 2;
844
845 $importButtonIsDisabled = 0;
846
847 global $dolibarr_website_allow_custom_php;
848 if (!empty($dolibarr_website_allow_custom_php) && $dolibarr_website_allow_custom_php == 1) {
849 $notdisabledsystemfunction = '';
850 $systemfunctions = array("exec", "passthru", "shell_exec", "system", "popen", "proc_open");
851 foreach ($systemfunctions as $systemfunction) {
852 // @phpstan-ignore-next-line
853 if (function_exists($systemfunction)) {
854 $notdisabledsystemfunction .= ($notdisabledsystemfunction ? ', ' : '').$systemfunction;
855 }
856 }
857 if ($notdisabledsystemfunction) {
858 print '<div class="warning">';
859 print $langs->trans("ImportOfWebsiteTemplateIncludingPHPIsAllowedIf", 'warning');
860 print '</div>';
861
862 $importButtonIsDisabled = 1;
863 }
864 }
865 if (empty($dolibarr_website_allow_custom_php)) {
866 print '<div class="warning">';
867 print $langs->trans("ImportOfWebsiteTemplateIncludingPHPIsDisabled", 'warning');
868 print '</div>';
869
870 $importButtonIsDisabled = 1;
871 }
872
873 print '<!-- For website template import -->'."\n";
874 print '<table class="noborder centpercent">';
875
876 // Title
877 print '<tr class="liste_titre"><th class="titlefield">';
878 print $form->textwithpicto($langs->trans("Templates"), $langs->trans("ThemeDir").' : '.implode(", ", $dirthemes));
879 print ' ';
880 print '<a class="valignmiddle" href="'.$_SERVER["PHP_SELF"].'?website='.urlencode($website->ref).'&importsite=2" rel="noopener noreferrer external">';
881 print $warningtoshow;
882 print img_picto($langs->trans("Refresh"), 'refresh', 'class="valignmiddle"');
883 print '</a>';
884 print '</th>';
885 print '<th class="right">';
886 $url = 'https://www.dolistore.com/index.php?cat=84';
887 print '<a href="'.$url.'" target="_blank" rel="noopener noreferrer external">';
888 print img_picto('', 'globe', 'class="pictofixedwidth"').$langs->trans('DownloadMoreSkins');
889 print '</a>';
890 print '</th></tr>';
891
892 print '<tr><td colspan="'.$colspan.'">';
893
894 print '<table class="nobordernopadding centpercent"><tr><td><div class="display-flex">';
895
896 if (count($dirthemes)) {
897 $i = 0;
898 foreach ($dirthemes as $dir) {
899 $dirtheme = DOL_DATA_ROOT.$dir;
900
901 if (is_dir($dirtheme)) {
902 $handle = opendir($dirtheme);
903 if (is_resource($handle)) {
904 while (($subdir = readdir($handle)) !== false) { // Scan files of directory
905 //var_dump($dirtheme.'/'.$subdir);
906 if (dol_is_file($dirtheme."/".$subdir) && substr($subdir, 0, 1) != '.' && substr($subdir, 0, 3) != 'CVS' && preg_match('/\.zip$/i', $subdir)) {
907 $subdirwithoutzip = preg_replace('/\.zip$/i', '', $subdir);
908 $subdirwithoutzipwithoutver = preg_replace('/(_exp|_dev)$/i', '', $subdirwithoutzip);
909
910 // Disable not stable themes (dir ends with _exp or _dev)
911 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2 && preg_match('/_dev$/i', $subdirwithoutzip)) {
912 continue;
913 }
914 if (getDolGlobalInt('MAIN_FEATURES_LEVEL') < 1 && preg_match('/_exp$/i', $subdirwithoutzip)) {
915 continue;
916 }
917
918 $arrayoftemplatesfound[$subdirwithoutzip] = array('id' => $subdirwithoutzip);
919
920 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;">';
921
922 $templatedir = $dirtheme."/".$subdir;
923 $file = $dirtheme."/".$subdirwithoutzipwithoutver.".jpg";
924 $url = DOL_URL_ROOT.'/viewimage.php?modulepart=doctemplateswebsite&file='.$subdirwithoutzipwithoutver.".jpg";
925
926 if (!file_exists($file)) {
927 $url = DOL_URL_ROOT.'/public/theme/common/nophoto.png';
928 }
929
930 $originalimgfile = basename($file);
931 $entity = $conf->entity;
932 $modulepart = 'doctemplateswebsite';
933 $cache = '';
934 $title = $file;
935
936 $ret = '';
937 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalimgfile, 1, '&entity='.$entity);
938 if (!empty($urladvanced)) {
939 $ret .= '<a class="'.$urladvanced['css'].'" target="'.$urladvanced['target'].'" mime="'.$urladvanced['mime'].'" href="'.$urladvanced['url'].'">';
940 } else {
941 $ret .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.urlencode($modulepart).'&entity='.((int) $entity).'&file='.urlencode($originalimgfile).'&cache='.((int) $cache).'">';
942 }
943 print $ret;
944 print '<img class="img-skinthumb shadow" src="'.$url.'" border="0" alt="'.$title.'" title="'.$title.'" style="margin-bottom: 5px;">';
945 print '</a>';
946
947 print '<br>';
948 print $subdir;
949 print '<br>';
950 print '<span class="opacitymedium">'.dol_print_size(dol_filesize($dirtheme."/".$subdir), 1, 1).' - '.dol_print_date(dol_filemtime($templatedir), 'dayhour', 'tzuserrel').'</span>';
951 if ($user->hasRight('website', 'delete')) {
952 print ' <a href="'.$_SERVER["PHP_SELF"].'?action=deletetemplate&token='.newToken().'&website='.urlencode($website->ref).'&templateuserfile='.urlencode($subdir).'">'.img_picto('', 'delete').'</a>';
953 }
954 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>';
955 print '</div>';
956
957 $i++;
958 }
959 }
960 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>';
961 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>';
962 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>';
963 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>';
964 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>';
965 }
966 }
967 }
968 } else {
969 print '<span class="opacitymedium">'.$langs->trans("None").'</span>';
970 }
971
972 print '</div></td></tr></table>';
973
974 print '</td></tr>';
975 print '</table>';
976}
977
978
994function checkPHPCode(&$phpfullcodestringold, &$phpfullcodestring)
995{
996 global $langs, $user;
997
998 $error = 0;
999
1000 if (empty($phpfullcodestringold) && empty($phpfullcodestring)) {
1001 return 0;
1002 }
1003
1004 // First check permission
1005 if ($phpfullcodestringold != $phpfullcodestring) {
1006 global $dolibarr_website_allow_custom_php;
1007 if (empty($dolibarr_website_allow_custom_php)) { // Case of $dolibarr_website_allow_custom_php = 0
1008 $error++;
1009 setEventMessages($langs->trans("NotAllowedToAddDynamicContentDisabledGlobaly", 'dolibarr_website_allow_custom_php'), null, 'errors');
1010 } elseif ($dolibarr_website_allow_custom_php == 1) { // Case of $dolibarr_website_allow_custom_php = 1
1011 $notdisabledsystemfunction = '';
1012 $systemfunctions = array("exec", "passthru", "shell_exec", "system", "popen", "proc_open");
1013 foreach ($systemfunctions as $systemfunction) {
1014 // @phpstan-ignore-next-line
1015 if (function_exists($systemfunction)) {
1016 $notdisabledsystemfunction .= ($notdisabledsystemfunction ? ', ' : '').$systemfunction;
1017 }
1018 }
1019 if ($notdisabledsystemfunction) {
1020 $error++;
1021 $langs->load("errors");
1022 setEventMessages($langs->trans("ErrorDynamicPHPContentNotAllowed", 'dolibarr_website_allow_custom_php'), null, 'errors');
1023 }
1024 }
1025
1026 if (!$error && !$user->hasRight('website', 'writephp')) {
1027 $error++;
1028 setEventMessages($langs->trans("NotAllowedToAddDynamicContent"), null, 'errors');
1029 }
1030 }
1031
1032 $phpfullcodestringnew = $phpfullcodestring;
1033
1034 // Then check forbidden commands
1035 if (!$error) {
1036 if (getDolGlobalString("WEBSITE_DISALLOW_DOLLAR_UNDERSCORE")) {
1037 $phpfullcodestring = preg_replace('/\$_COOKIE\[/', '__DOLLARCOOKIE__', $phpfullcodestring);
1038 $phpfullcodestring = preg_replace('/\$_FILES\[/', '__DOLLARFILES__', $phpfullcodestring);
1039 $phpfullcodestring = preg_replace('/\$_SESSION\[/', '__DOLLARSESSION__', $phpfullcodestring);
1040 $forbiddenphpstrings = array('$$', '$_', '}[');
1041 } else {
1042 $forbiddenphpstrings = array('$$', '}[');
1043 }
1044 //$forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_FILES', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST', 'ReflectionFunction'));
1045 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', 'ReflectionFunction'));
1046
1047 $forbiddenphpfunctions = array();
1048 //$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
1049 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("override_function", "session_id", "session_create_id", "session_regenerate_id"));
1050 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("get_defined_functions", "get_defined_vars", "get_defined_constants", "get_declared_classes"));
1051 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
1052 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
1053
1054 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_EXEC')) { // If experimental option is not on, we TRY to disallow functions to execute commands
1055 // 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).
1056 // The following list shares a lot of content with the whitelist guard in dol_eval() function.
1057
1058 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("ob_start"));
1059
1060 // Functions with callable parameters
1061 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("call_user_func", "call_user_func_array"));
1062 //$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"));
1063 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("usort", "uasort", "uksort"));
1064 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("preg_replace_callback", "preg_replace_callback_array", "header_register_callback"));
1065 $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"));
1066 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("spl_autoload_register", "spl_autoload_unregister", "iterator_apply", "session_set_save_handler"));
1067 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("forward_static_call", "forward_static_call_array", "register_postsend_function"));
1068 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("readline_completion_function", "readline_callback_handler_install"));
1069
1070 // Exec functions
1071 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("exec", "passthru", "shell_exec", "system", "proc_open", "popen"));
1072 $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"));
1073 $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", ));
1074 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("putenv", "dl", "apache_child_terminate", "apache_setenv"));
1075 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_eval", "dol_eval_new", "dol_eval_standard", "executeCLI", "verifCond", "dolEncrypt", "dolDecrypt")); // native dolibarr functions
1076 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("eval", "create_function", "assert", "mb_ereg_replace")); // function with eval capabilities
1077
1078 // Include functions
1079 //$forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("require", "include", "require_once", "include_once"));
1080 }
1081 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_WRITE')) { // If option is not on, we disallow functions to write files
1082 $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
1083 $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"));
1084 }
1085 if (getDolGlobalString('WEBSITE_PHP_DISALLOW_READ')) { // If option is not on, we disallow functions to read files
1086 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("dol_decode")); // more dolibarr functions
1087 $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("file", "fopen", "file_get_contents", "fgets", "fgetscsv", "fgetss", "fread"));
1088 }
1089
1090 $forbiddenphpmethods = array('invoke', 'invokeArgs'); // Method of ReflectionFunction to execute a function
1091
1092 foreach ($forbiddenphpstrings as $forbiddenphpstring) {
1093 if (preg_match('/'.preg_quote($forbiddenphpstring, '/').'/ims', $phpfullcodestring)) {
1094 $error++;
1095 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpstring), null, 'errors');
1096 break;
1097 }
1098 }
1099 /* replaced with next block
1100 foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function(" but also "'function'(" and "function ("
1101 if (preg_match('/'.$forbiddenphpfunction.'[\'\s]*\‍(/ims', $phpfullcodestring)) {
1102 $error++;
1103 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
1104 break;
1105 }
1106 }*/
1107 foreach ($forbiddenphpfunctions as $forbiddenphpfunction) { // Check "function" whatever is "function(" or "function'(" or "function (" or "function"
1108 $reg = array();
1109 if (preg_match('/\b'.$forbiddenphpfunction.'(=| class=|\b)/ims', $phpfullcodestring, $reg)) {
1110 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=...'
1111 $error++;
1112 //setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction)." :<br>\n".$phpfullcodestring, null, 'errors');
1113 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpfunction), null, 'errors');
1114 break;
1115 }
1116 }
1117 }
1118
1119 foreach ($forbiddenphpmethods as $forbiddenphpmethod) {
1120 if (preg_match('/->'.$forbiddenphpmethod.'/ims', $phpfullcodestring)) {
1121 $error++;
1122 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpmethod), null, 'errors');
1123 break;
1124 }
1125 }
1126 }
1127
1128 // This char can be used to execute RCE for example by using echo `ls`
1129 if (!$error) {
1130 $forbiddenphpchars = array();
1131 if (!getDolGlobalString('WEBSITE_PHP_ALLOW_DANGEROUS_CHARS')) { // If option is not on, we disallow functions to execute commands
1132 $forbiddenphpchars = array("`");
1133 }
1134 foreach ($forbiddenphpchars as $forbiddenphpchar) {
1135 if (preg_match('/'.$forbiddenphpchar.'/ims', $phpfullcodestring)) {
1136 $error++;
1137 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", $forbiddenphpchar), null, 'errors');
1138 break;
1139 }
1140 }
1141 }
1142
1143 // Deny code to call a function obfuscated with comment, like "exec/*...*/ ('ls')";
1144 if (!$error) {
1145 if (preg_match('/\*\/\s*\‍(/ims', $phpfullcodestring)) {
1146 $error++;
1147 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", "exec/*...*/ ('ls')"), null, 'errors');
1148 }
1149 }
1150
1151 // Deny dynamic functions '${a}(' or '$a[b](' => So we refuse '}(' and ']('
1152 if (!$error) {
1153 if (preg_match('/[}\]]\s*\‍(/ims', $phpfullcodestring)) {
1154 $error++;
1155 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", ']('), null, 'errors');
1156 }
1157 }
1158
1159 // Deny dynamic functions '$xxx(' or '$xxx (' or '$xxx" ('
1160 if (!$error) {
1161 if (preg_match('/\$[a-z0-9_\-\/\*\"]+\s*\‍(/ims', $phpfullcodestring)) {
1162 $error++;
1163 setEventMessages($langs->trans("DynamicPHPCodeContainsAForbiddenInstruction", '$...('), null, 'errors');
1164 }
1165 }
1166
1167 // 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.
1168
1169 // Then check if installmodules.lock does not block dynamic PHP code change.
1170 if ($phpfullcodestringold != $phpfullcodestringnew) {
1171 if (!$error) {
1172 $dolibarrdataroot = preg_replace('/([\\/]+)$/i', '', DOL_DATA_ROOT);
1173 $allowimportsite = true;
1174 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1175 if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) {
1176 $allowimportsite = false;
1177 }
1178
1179 if (!$allowimportsite) {
1180 $error++;
1181 // Blocked by installmodules.lock
1182 if (getDolGlobalString('MAIN_MESSAGE_INSTALL_MODULES_DISABLED_CONTACT_US')) {
1183 // Show clean corporate message
1184 $message = $langs->trans('InstallModuleFromWebHasBeenDisabledContactUs');
1185 } else {
1186 // Show technical generic message
1187 $message = $langs->trans("InstallModuleFromWebHasBeenDisabledByFile", $dolibarrdataroot.'/installmodules.lock');
1188 }
1189 setEventMessages($message, null, 'errors');
1190 }
1191 }
1192 }
1193
1194 return $error;
1195}
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:65
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'.
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).
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)
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)
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.