Passed
Push — EXTRACT_CLASSES ( ae6b5c...83d77a )
by Rafael
60:14 queued 23:58
created

Website::create()   F

Complexity

Conditions 34
Paths > 20000

Size

Total Lines 139
Code Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 34
eloc 87
nc 150995200
nop 2
dl 0
loc 139
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2007-2018  Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2014       Juanjo Menent               <[email protected]>
5
 * Copyright (C) 2015       Florian Henry               <[email protected]>
6
 * Copyright (C) 2015       Raphaël Doursenaud          <[email protected]>
7
 * Copyright (C) 2018-2024  Frédéric France             <[email protected]>
8
 * Copyright (C) 2024		MDW							<[email protected]>
9
 * Copyright (C) 2024       Rafael San José             <[email protected]>
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23
 */
24
25
namespace Dolibarr\Code\Website\Classes;
26
27
use Dolibarr\Core\Base\CommonObject;
28
29
/**
30
 * \file    htdocs/website/class/website.class.php
31
 * \ingroup website
32
 * \brief   File for the CRUD class of website (Create/Read/Update/Delete)
33
 */
34
35
// Put here all includes required by your class file
36
//use Dolibarr\Code\Societe\Classes\Societe;
37
//require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
38
39
40
/**
41
 * Class Website
42
 */
43
class Website extends CommonObject
44
{
45
    /**
46
     * @var string Id to identify managed objects
47
     */
48
    public $element = 'website';
49
50
    /**
51
     * @var string Name of table without prefix where object is stored
52
     */
53
    public $table_element = 'website';
54
55
    protected $childtablesoncascade = array();
56
57
    /**
58
     * @var string String with name of icon for website. Must be the part after the 'object_' into object_myobject.png
59
     */
60
    public $picto = 'globe';
61
62
    /**
63
     * @var int Entity
64
     */
65
    public $entity;
66
67
    /**
68
     * @var string Ref
69
     */
70
    public $ref;
71
72
    /**
73
     * @var string description
74
     */
75
    public $description;
76
77
    /**
78
     * @var string Main language of web site
79
     */
80
    public $lang;
81
82
    /**
83
     * @var string List of languages of web site ('fr', 'es_MX', ...)
84
     */
85
    public $otherlang;
86
87
    /**
88
     * @var int Status
89
     */
90
    public $status;
91
92
    /**
93
     * @var integer date_creation
94
     */
95
    public $date_creation;
96
97
    /**
98
     * @var integer date_modification
99
     */
100
    public $date_modification;
101
102
    /**
103
     * @var integer Default home page
104
     */
105
    public $fk_default_home;
106
107
    /**
108
     * @var int User Create Id
109
     */
110
    public $fk_user_creat;
111
112
    /**
113
     * @var int User Modification Id
114
     */
115
    public $fk_user_modif;
116
117
    /**
118
     * @var string Virtual host
119
     */
120
    public $virtualhost;
121
122
    /**
123
     * @var int Use a manifest file
124
     */
125
    public $use_manifest;
126
127
    /**
128
     * @var int Position
129
     */
130
    public $position;
131
132
    /**
133
     * @var string name of template
134
     */
135
    public $name_template;
136
137
    const STATUS_DRAFT = 0;
138
    const STATUS_VALIDATED = 1;
139
140
141
    /**
142
     * Constructor
143
     *
144
     * @param DoliDB $db Database handler
145
     */
146
    public function __construct(DoliDB $db)
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Website\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
147
    {
148
        $this->db = $db;
149
150
        $this->ismultientitymanaged = 1;
151
    }
152
153
    /**
154
     * Create object into database
155
     *
156
     * @param  User $user       User that creates
157
     * @param  int  $notrigger  0=launch triggers after, 1=disable triggers
158
     * @return int              Return integer <0 if KO, 0 if already exists, ID of created object if OK
159
     */
160
    public function create(User $user, $notrigger = 0)
161
    {
162
        global $conf, $langs;
163
164
        dol_syslog(__METHOD__, LOG_DEBUG);
165
166
        $error = 0;
167
        $now = dol_now();
168
169
        // Clean parameters
170
        if (isset($this->entity)) {
171
            $this->entity = (int) $this->entity;
172
        }
173
        if (isset($this->ref)) {
174
            $this->ref = trim($this->ref);
175
        }
176
        if (isset($this->description)) {
177
            $this->description = trim($this->description);
178
        }
179
        if (isset($this->status)) {
180
            $this->status = (int) $this->status;
181
        }
182
        if (empty($this->date_creation)) {
183
            $this->date_creation = $now;
184
        }
185
        if (empty($this->date_modification)) {
186
            $this->date_modification = $now;
187
        }
188
        // Remove spaces and be sure we have main language only
189
        $this->lang = preg_replace('/[_-].*$/', '', trim($this->lang)); // en_US or en-US -> en
190
        $tmparray = explode(',', $this->otherlang);
191
        if (is_array($tmparray)) {
192
            foreach ($tmparray as $key => $val) {
193
                // It possible we have empty val here if postparam WEBSITE_OTHERLANG is empty or set like this : 'en,,sv' or 'en,sv,'
194
                if (empty(trim($val))) {
195
                    unset($tmparray[$key]);
196
                    continue;
197
                }
198
                $tmparray[$key] = preg_replace('/[_-].*$/', '', trim($val)); // en_US or en-US -> en
199
            }
200
            $this->otherlang = implode(',', $tmparray);
201
        }
202
203
        // Check parameters
204
        if (empty($this->entity)) {
205
            $this->entity = $conf->entity;
206
        }
207
        if (empty($this->lang)) {
208
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("MainLanguage"));
209
            return -1;
210
        }
211
212
        // Insert request
213
        $sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '(';
214
        $sql .= 'entity,';
215
        $sql .= 'ref,';
216
        $sql .= 'description,';
217
        $sql .= 'lang,';
218
        $sql .= 'otherlang,';
219
        $sql .= 'status,';
220
        $sql .= 'fk_default_home,';
221
        $sql .= 'virtualhost,';
222
        $sql .= 'fk_user_creat,';
223
        $sql .= 'date_creation,';
224
        $sql .= 'position,';
225
        $sql .= 'tms';
226
        $sql .= ') VALUES (';
227
        $sql .= ' ' . ((empty($this->entity) && $this->entity != '0') ? 'NULL' : $this->entity) . ',';
228
        $sql .= ' ' . (!isset($this->ref) ? 'NULL' : "'" . $this->db->escape($this->ref) . "'") . ',';
229
        $sql .= ' ' . (!isset($this->description) ? 'NULL' : "'" . $this->db->escape($this->description) . "'") . ',';
230
        $sql .= ' ' . (!isset($this->lang) ? 'NULL' : "'" . $this->db->escape($this->lang) . "'") . ',';
231
        $sql .= ' ' . (!isset($this->otherlang) ? 'NULL' : "'" . $this->db->escape($this->otherlang) . "'") . ',';
232
        $sql .= ' ' . (!isset($this->status) ? '1' : $this->status) . ',';
233
        $sql .= ' ' . (!isset($this->fk_default_home) ? 'NULL' : $this->fk_default_home) . ',';
234
        $sql .= ' ' . (!isset($this->virtualhost) ? 'NULL' : "'" . $this->db->escape($this->virtualhost) . "'") . ",";
235
        $sql .= ' ' . (!isset($this->fk_user_creat) ? $user->id : $this->fk_user_creat) . ',';
236
        $sql .= ' ' . (!isset($this->date_creation) || dol_strlen($this->date_creation) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_creation) . "'") . ",";
237
        $sql .= ' ' . ((int) $this->position) . ",";
238
        $sql .= ' ' . (!isset($this->date_modification) || dol_strlen($this->date_modification) == 0 ? 'NULL' : "'" . $this->db->idate($this->date_modification) . "'");
239
        $sql .= ')';
240
241
        $this->db->begin();
242
243
        $resql = $this->db->query($sql);
244
        if (!$resql) {
245
            $error++;
246
            $this->errors[] = 'Error ' . $this->db->lasterror();
247
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
248
        }
249
250
        if (!$error) {
251
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
252
253
            // Create a subdirectory for each language (except main language)
254
            $tmplangarray = explode(',', $this->otherlang);
255
            if (is_array($tmplangarray)) {
256
                dol_mkdir($conf->website->dir_output . '/' . $this->ref);
257
                foreach ($tmplangarray as $val) {
258
                    if (trim($val) == $this->lang) {
259
                        continue;
260
                    }
261
                    dol_mkdir($conf->website->dir_output . '/' . $this->ref . '/' . trim($val), DOL_DATA_ROOT);
262
                }
263
            }
264
265
            // Create subdirectory for images and js
266
            dol_mkdir($conf->medias->multidir_output[$conf->entity] . '/image/' . $this->ref, DOL_DATA_ROOT);
267
            dol_mkdir($conf->medias->multidir_output[$conf->entity] . '/js/' . $this->ref, DOL_DATA_ROOT);
268
269
            // Uncomment this and change WEBSITE to your own tag if you
270
            // want this action to call a trigger.
271
            // if (!$notrigger) {
272
273
            //     // Call triggers
274
            //     $result = $this->call_trigger('WEBSITE_CREATE',$user);
275
            //     if ($result < 0) $error++;
276
            //     // End call triggers
277
            // }
278
        }
279
280
        if (!$error) {
281
            $stringtodolibarrfile = "# Some properties for Dolibarr web site CMS\n";
282
            $stringtodolibarrfile .= "param=value\n";
283
            //print $conf->website->dir_output.'/'.$this->ref.'/.dolibarr';exit;
284
            file_put_contents($conf->website->dir_output . '/' . $this->ref . '/.dolibarr', $stringtodolibarrfile);
285
        }
286
287
        // Commit or rollback
288
        if ($error) {
289
            $this->db->rollback();
290
            if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
291
                return 0;
292
            } else {
293
                return -1 * $error;
294
            }
295
        } else {
296
            $this->db->commit();
297
298
            return $this->id;
299
        }
300
    }
301
302
    /**
303
     * Load object in memory from the database
304
     *
305
     * @param   int    $id      Id object
306
     * @param   string $ref     Ref
307
     * @return  int             Return integer <0 if KO, 0 if not found, >0 if OK
308
     */
309
    public function fetch($id, $ref = null)
310
    {
311
        dol_syslog(__METHOD__, LOG_DEBUG);
312
313
        $sql = "SELECT";
314
        $sql .= " t.rowid,";
315
        $sql .= " t.entity,";
316
        $sql .= " t.ref,";
317
        $sql .= " t.position,";
318
        $sql .= " t.description,";
319
        $sql .= " t.lang,";
320
        $sql .= " t.otherlang,";
321
        $sql .= " t.status,";
322
        $sql .= " t.fk_default_home,";
323
        $sql .= " t.use_manifest,";
324
        $sql .= " t.virtualhost,";
325
        $sql .= " t.fk_user_creat,";
326
        $sql .= " t.fk_user_modif,";
327
        $sql .= " t.date_creation,";
328
        $sql .= " t.tms as date_modification,";
329
        $sql .= " t.name_template";
330
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as t";
331
        $sql .= " WHERE t.entity IN (" . getEntity('website') . ")";
332
        if (!empty($ref)) {
333
            $sql .= " AND t.ref = '" . $this->db->escape($ref) . "'";
334
        } else {
335
            $sql .= " AND t.rowid = " . (int) $id;
336
        }
337
338
        $resql = $this->db->query($sql);
339
        if ($resql) {
340
            $numrows = $this->db->num_rows($resql);
341
            if ($numrows) {
342
                $obj = $this->db->fetch_object($resql);
343
344
                $this->id = $obj->rowid;
345
346
                $this->entity = $obj->entity;
347
                $this->ref = $obj->ref;
348
                $this->position = $obj->position;
349
                $this->description = $obj->description;
350
                $this->lang = $obj->lang;
351
                $this->otherlang = $obj->otherlang;
352
                $this->status = $obj->status;
353
                $this->fk_default_home = $obj->fk_default_home;
354
                $this->virtualhost = $obj->virtualhost;
355
                $this->use_manifest = $obj->use_manifest;
356
                $this->fk_user_creat = $obj->fk_user_creat;
357
                $this->fk_user_modif = $obj->fk_user_modif;
358
                $this->date_creation = $this->db->jdate($obj->date_creation);
359
                $this->date_modification = $this->db->jdate($obj->date_modification);
360
                $this->name_template = $obj->name_template;
361
            }
362
            $this->db->free($resql);
363
364
            if ($numrows > 0) {
365
                return 1;
366
            } else {
367
                return 0;
368
            }
369
        } else {
370
            $this->errors[] = 'Error ' . $this->db->lasterror();
371
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
372
373
            return -1;
374
        }
375
    }
376
377
378
    /**
379
     * Load all object in memory ($this->records) from the database
380
     *
381
     * @param   string          $sortorder      Sort Order
382
     * @param   string          $sortfield      Sort field
383
     * @param   int             $limit          limit
384
     * @param   int             $offset         offset limit
385
     * @param   string|array    $filter         filter array
386
     * @param   string          $filtermode     filter mode (AND or OR)
387
     * @return  array|int                       int <0 if KO, array of pages if OK
388
     */
389
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
390
    {
391
        dol_syslog(__METHOD__, LOG_DEBUG);
392
393
        $records = array();
394
395
        $sql = "SELECT";
396
        $sql .= " t.rowid,";
397
        $sql .= " t.entity,";
398
        $sql .= " t.ref,";
399
        $sql .= " t.description,";
400
        $sql .= " t.lang,";
401
        $sql .= " t.otherlang,";
402
        $sql .= " t.status,";
403
        $sql .= " t.fk_default_home,";
404
        $sql .= " t.virtualhost,";
405
        $sql .= " t.fk_user_creat,";
406
        $sql .= " t.fk_user_modif,";
407
        $sql .= " t.date_creation,";
408
        $sql .= " t.tms as date_modification";
409
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as t";
410
        $sql .= " WHERE t.entity IN (" . getEntity('website') . ")";
411
412
        // Manage filter
413
        if (is_array($filter)) {
414
            $sqlwhere = array();
415
            if (count($filter) > 0) {
416
                foreach ($filter as $key => $value) {
417
                    $sqlwhere[] = $key . " LIKE '%" . $this->db->escape($value) . "%'";
418
                }
419
            }
420
            if (count($sqlwhere) > 0) {
421
                $sql .= ' AND ' . implode(' ' . $this->db->escape($filtermode) . ' ', $sqlwhere);
422
            }
423
424
            $filter = '';
425
        }
426
427
        // Manage filter
428
        $errormessage = '';
429
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
430
        if ($errormessage) {
431
            $this->errors[] = $errormessage;
432
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
433
            return -1;
434
        }
435
436
        if (!empty($sortfield)) {
437
            $sql .= $this->db->order($sortfield, $sortorder);
438
        }
439
        if (!empty($limit)) {
440
            $sql .= $this->db->plimit($limit, $offset);
441
        }
442
443
        $resql = $this->db->query($sql);
444
        if ($resql) {
445
            $num = $this->db->num_rows($resql);
446
447
            while ($obj = $this->db->fetch_object($resql)) {
448
                $record = new self($this->db);
449
450
                $record->id = $obj->rowid;
451
452
                $record->entity = $obj->entity;
453
                $record->ref = $obj->ref;
454
                $record->description = $obj->description;
455
                $record->lang = $obj->lang;
456
                $record->otherlang = $obj->otherlang;
457
                $record->status = $obj->status;
458
                $record->fk_default_home = $obj->fk_default_home;
459
                $record->virtualhost = $obj->virtualhost;
460
                $record->fk_user_creat = $obj->fk_user_creat;
461
                $record->fk_user_modif = $obj->fk_user_modif;
462
                $record->date_creation = $this->db->jdate($obj->date_creation);
463
                $record->date_modification = $this->db->jdate($obj->date_modification);
464
465
                $records[$record->id] = $record;
466
            }
467
            $this->db->free($resql);
468
469
            return $records;
470
        } else {
471
            $this->errors[] = 'Error ' . $this->db->lasterror();
472
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
473
474
            return -1;
475
        }
476
    }
477
478
    /**
479
     * Update object into database
480
     *
481
     * @param  User $user       User that modifies
482
     * @param  int  $notrigger  0=launch triggers after, 1=disable triggers
483
     * @return int              Return integer <0 if KO, >0 if OK
484
     */
485
    public function update(User $user, $notrigger = 0)
486
    {
487
        global $conf, $langs;
488
489
        $error = 0;
490
491
        dol_syslog(__METHOD__, LOG_DEBUG);
492
493
        // Clean parameters
494
495
        if (isset($this->entity)) {
496
            $this->entity = (int) $this->entity;
497
        }
498
        if (isset($this->ref)) {
499
            $this->ref = trim($this->ref);
500
        }
501
        if (isset($this->description)) {
502
            $this->description = trim($this->description);
503
        }
504
        if (isset($this->status)) {
505
            $this->status = (int) $this->status;
506
        }
507
508
        // Remove spaces and be sure we have main language only
509
        $this->lang = preg_replace('/[_-].*$/', '', trim($this->lang)); // en_US or en-US -> en
510
        $tmparray = explode(',', $this->otherlang);
511
        if (is_array($tmparray)) {
512
            foreach ($tmparray as $key => $val) {
513
                // It possible we have empty val here if postparam WEBSITE_OTHERLANG is empty or set like this : 'en,,sv' or 'en,sv,'
514
                if (empty(trim($val))) {
515
                    unset($tmparray[$key]);
516
                    continue;
517
                }
518
                $tmparray[$key] = preg_replace('/[_-].*$/', '', trim($val)); // en_US or en-US -> en
519
            }
520
            $this->otherlang = implode(',', $tmparray);
521
        }
522
        if (empty($this->lang)) {
523
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("MainLanguage"));
524
            return -1;
525
        }
526
527
        // Check parameters
528
        // Put here code to add a control on parameters values
529
530
        // Update request
531
        $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
532
        $sql .= ' entity = ' . (isset($this->entity) ? $this->entity : "null") . ',';
533
        $sql .= ' ref = ' . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ',';
534
        $sql .= ' description = ' . (isset($this->description) ? "'" . $this->db->escape($this->description) . "'" : "null") . ',';
535
        $sql .= ' lang = ' . (isset($this->lang) ? "'" . $this->db->escape($this->lang) . "'" : "null") . ',';
536
        $sql .= ' otherlang = ' . (isset($this->otherlang) ? "'" . $this->db->escape($this->otherlang) . "'" : "null") . ',';
537
        $sql .= ' status = ' . (isset($this->status) ? $this->status : "null") . ',';
538
        $sql .= ' fk_default_home = ' . (($this->fk_default_home > 0) ? $this->fk_default_home : "null") . ',';
539
        $sql .= ' use_manifest = ' . ((int) $this->use_manifest) . ',';
540
        $sql .= ' virtualhost = ' . (($this->virtualhost != '') ? "'" . $this->db->escape($this->virtualhost) . "'" : "null") . ',';
541
        $sql .= ' fk_user_modif = ' . (!isset($this->fk_user_modif) ? $user->id : $this->fk_user_modif) . ',';
542
        $sql .= ' date_creation = ' . (!isset($this->date_creation) || dol_strlen($this->date_creation) != 0 ? "'" . $this->db->idate($this->date_creation) . "'" : 'null') . ',';
543
        $sql .= ' tms = ' . (dol_strlen($this->date_modification) != 0 ? "'" . $this->db->idate($this->date_modification) . "'" : "'" . $this->db->idate(dol_now()) . "'");
544
        $sql .= ' WHERE rowid=' . ((int) $this->id);
545
546
        $this->db->begin();
547
548
        $resql = $this->db->query($sql);
549
        if (!$resql) {
550
            $error++;
551
            $this->errors[] = 'Error ' . $this->db->lasterror();
552
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
553
        }
554
555
        if (!$error && !$notrigger) {
556
            // Uncomment this and change MYOBJECT to your own tag if you
557
            // want this action calls a trigger.
558
559
            // Create subdirectory per language
560
            $tmplangarray = explode(',', $this->otherlang);
561
            if (is_array($tmplangarray)) {
562
                dol_mkdir($conf->website->dir_output . '/' . $this->ref);
563
                foreach ($tmplangarray as $val) {
564
                    if (trim($val) == $this->lang) {
565
                        continue;
566
                    }
567
                    dol_mkdir($conf->website->dir_output . '/' . $this->ref . '/' . trim($val));
568
                }
569
            }
570
571
            //// Call triggers
572
            //$result=$this->call_trigger('WEBSITE_MODIFY',$user);
573
            //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
574
            //// End call triggers
575
        }
576
577
        // Commit or rollback
578
        if ($error) {
579
            $this->db->rollback();
580
581
            return -1 * $error;
582
        } else {
583
            $this->db->commit();
584
585
            return 1;
586
        }
587
    }
588
589
    /**
590
     * Delete object in database
591
     *
592
     * @param User  $user       User that deletes
593
     * @param int   $notrigger  0=launch triggers, 1=disable triggers
594
     * @return int              Return integer <0 if KO, >0 if OK
595
     */
596
    public function delete(User $user, $notrigger = 0)
597
    {
598
        global $conf;
599
600
        dol_syslog(__METHOD__, LOG_DEBUG);
601
602
        $error = 0;
603
604
        $this->db->begin();
605
606
        if (!$error) {
607
            $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'categorie_website_page';
608
            $sql .= ' WHERE fk_website_page IN (SELECT rowid FROM ' . MAIN_DB_PREFIX . 'website_page WHERE fk_website = ' . ((int) $this->id) . ')';
609
610
            $resql = $this->db->query($sql);
611
            if (!$resql) {
612
                $error++;
613
                $this->errors[] = 'Error ' . $this->db->lasterror();
614
                dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
615
            }
616
        }
617
618
        if (!$error) {
619
            $sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'website_page';
620
            $sql .= ' WHERE fk_website = ' . ((int) $this->id);
621
622
            $resql = $this->db->query($sql);
623
            if (!$resql) {
624
                $error++;
625
                $this->errors[] = 'Error ' . $this->db->lasterror();
626
                dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
627
            }
628
        }
629
630
        // Delete common code. This include execution of trigger.
631
        $result = $this->deleteCommon($user, $notrigger);
632
        if ($result <= 0) {
633
            $error++;
634
        }
635
636
        if (!$error && !empty($this->ref)) {
637
            $pathofwebsite = DOL_DATA_ROOT . ($conf->entity > 1 ? '/' . $conf->entity : '') . '/website/' . $this->ref;
638
639
            dol_delete_dir_recursive($pathofwebsite);
640
        }
641
642
        // Commit or rollback
643
        if ($error) {
644
            $this->db->rollback();
645
646
            return -1 * $error;
647
        } else {
648
            $this->db->commit();
649
650
            return 1;
651
        }
652
    }
653
654
    /**
655
     * Load a website its id and create a new one in database.
656
     * This copy website directories, regenerate all the pages + alias pages and recreate the medias link.
657
     *
658
     * @param   User    $user       User making the clone
659
     * @param   int     $fromid     Id of object to clone
660
     * @param   string  $newref     New ref
661
     * @param   string  $newlang    New language
662
     * @return  mixed               New object created, <0 if KO
663
     */
664
    public function createFromClone($user, $fromid, $newref, $newlang = '')
665
    {
666
        global $conf, $langs;
667
        global $dolibarr_main_data_root;
668
669
        $now = dol_now();
670
        $error = 0;
671
672
        dol_syslog(__METHOD__, LOG_DEBUG);
673
674
        $newref = dol_sanitizeFileName($newref);
675
676
        if (empty($newref)) {
677
            $this->error = 'ErrorBadParameter';
678
            return -1;
679
        }
680
681
        $object = new self($this->db);
682
683
        // Check no site with ref exists
684
        if ($object->fetch(0, $newref) > 0) {
685
            $this->error = 'ErrorNewRefIsAlreadyUsed';
686
            return -1;
687
        }
688
689
        $this->db->begin();
690
691
        // Load source object
692
        $object->fetch($fromid);
693
694
        $oldidforhome = $object->fk_default_home;
695
        $oldref = $object->ref;
696
697
        $pathofwebsiteold = $dolibarr_main_data_root . ($conf->entity > 1 ? '/' . $conf->entity : '') . '/website/' . dol_sanitizeFileName($oldref);
698
        $pathofwebsitenew = $dolibarr_main_data_root . ($conf->entity > 1 ? '/' . $conf->entity : '') . '/website/' . dol_sanitizeFileName($newref);
699
        dol_delete_dir_recursive($pathofwebsitenew);
700
701
        $fileindex = $pathofwebsitenew . '/index.php';
702
703
        // Reset some properties
704
        unset($object->id);
705
        unset($object->fk_user_creat);
706
        unset($object->import_key);
707
708
        // Clear fields
709
        $object->ref = $newref;
710
        $object->fk_default_home = 0;
711
        $object->virtualhost = '';
712
        $object->date_creation = $now;
713
        $object->fk_user_creat = $user->id;
714
        $object->position = ((int) $object->position) + 1;
715
        $object->status = self::STATUS_DRAFT;
716
        if (empty($object->lang)) {
717
            $object->lang = substr($langs->defaultlang, 0, 2); // Should not happen. Protection for corrupted site with no languages
718
        }
719
720
        // Create clone
721
        $object->context['createfromclone'] = 'createfromclone';
722
        $result = $object->create($user);
723
        if ($result < 0) {
724
            $error++;
725
            $this->error = $object->error;
726
            $this->errors = $object->errors;
727
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
728
        }
729
730
        if (!$error) {
731
            // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
732
            dolCopyDir($pathofwebsiteold, $pathofwebsitenew, getDolGlobalString('MAIN_UMASK'), 0, [], 2);
733
734
            // Check symlink to medias and restore it if ko
735
            $pathtomedias = DOL_DATA_ROOT . '/medias'; // Target
736
            $pathtomediasinwebsite = $pathofwebsitenew . '/medias'; // Source / Link name
737
            if (!is_link(dol_osencode($pathtomediasinwebsite))) {
738
                dol_syslog("Create symlink for " . $pathtomedias . " into name " . $pathtomediasinwebsite);
739
                dol_mkdir(dirname($pathtomediasinwebsite)); // To be sure dir for website exists
740
                $result = symlink($pathtomedias, $pathtomediasinwebsite);
741
            }
742
743
            // Copy images and js dir
744
            $pathofmediasjsold = DOL_DATA_ROOT . '/medias/js/' . $oldref;
745
            $pathofmediasjsnew = DOL_DATA_ROOT . '/medias/js/' . $newref;
746
            dolCopyDir($pathofmediasjsold, $pathofmediasjsnew, getDolGlobalString('MAIN_UMASK'), 0);
747
748
            $pathofmediasimageold = DOL_DATA_ROOT . '/medias/image/' . $oldref;
749
            $pathofmediasimagenew = DOL_DATA_ROOT . '/medias/image/' . $newref;
750
            dolCopyDir($pathofmediasimageold, $pathofmediasimagenew, getDolGlobalString('MAIN_UMASK'), 0);
751
752
            $newidforhome = 0;
753
754
            // Duplicate pages
755
            $objectpages = new WebsitePage($this->db);
756
            $listofpages = $objectpages->fetchAll($fromid);
757
            foreach ($listofpages as $pageid => $objectpageold) {
0 ignored issues
show
Bug introduced by
The expression $listofpages of type integer is not traversable.
Loading history...
758
                // Delete old file
759
                $filetplold = $pathofwebsitenew . '/page' . $pageid . '.tpl.php';
760
                dol_delete_file($filetplold);
761
762
                // Create new file
763
                $objectpagenew = $objectpageold->createFromClone($user, $pageid, $objectpageold->pageurl, '', 0, $object->id, 1);
764
765
                //print $pageid.' = '.$objectpageold->pageurl.' -> '.$objectpagenew->id.' = '.$objectpagenew->pageurl.'<br>';
766
                if (is_object($objectpagenew) && $objectpagenew->pageurl) {
767
                    $filealias = $pathofwebsitenew . '/' . $objectpagenew->pageurl . '.php';
768
                    $filetplnew = $pathofwebsitenew . '/page' . $objectpagenew->id . '.tpl.php';
769
770
                    // Save page alias
771
                    $result = dolSavePageAlias($filealias, $object, $objectpagenew);
772
                    if (!$result) {
773
                        setEventMessages('Failed to write file ' . $filealias, null, 'errors');
774
                    }
775
776
                    $result = dolSavePageContent($filetplnew, $object, $objectpagenew);
777
                    if (!$result) {
778
                        setEventMessages('Failed to write file ' . $filetplnew, null, 'errors');
779
                    }
780
781
                    if ($pageid == $oldidforhome) {
782
                        $newidforhome = $objectpagenew->id;
783
                    }
784
                } else {
785
                    setEventMessages($objectpageold->error, $objectpageold->errors, 'errors');
786
                    $error++;
787
                }
788
            }
789
        }
790
791
        if (!$error) {
792
            // Restore id of home page
793
            $object->fk_default_home = $newidforhome;
794
            $res = $object->update($user);
795
            if (!($res > 0)) {
796
                $error++;
797
                setEventMessages($object->error, $object->errors, 'errors');
798
            }
799
800
            if (!$error) {
801
                $filetpl = $pathofwebsitenew . '/page' . $newidforhome . '.tpl.php';
802
                $filewrapper = $pathofwebsitenew . '/wrapper.php';
803
804
                // Re-generates the index.php page to be the home page, and re-generates the wrapper.php
805
                //--------------------------------------------------------------------------------------
806
                $result = dolSaveIndexPage($pathofwebsitenew, $fileindex, $filetpl, $filewrapper, $object);
807
            }
808
        }
809
810
        unset($object->context['createfromclone']);
811
812
        // End
813
        if (!$error) {
814
            $this->db->commit();
815
816
            return $object;
817
        } else {
818
            $this->db->rollback();
819
820
            return -1;
821
        }
822
    }
823
824
    /**
825
     *  Return a link to the user card (with optionally the picto)
826
     *  Use this->id,this->lastname, this->firstname
827
     *
828
     *  @param  int     $withpicto          Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
829
     *  @param  string  $option             On what the link point to
830
     *  @param  integer $notooltip          1=Disable tooltip
831
     *  @param  int     $maxlen             Max length of visible user name
832
     *  @param  string  $morecss            Add more css on link
833
     *  @return string                      String with URL
834
     */
835
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $maxlen = 24, $morecss = '')
836
    {
837
        global $langs;
838
839
        $result = '';
840
841
        $label = '<u>' . $langs->trans("WebSite") . '</u>';
842
        $label .= '<br>';
843
        $label .= '<b>' . $langs->trans('Ref') . ':</b> ' . $this->ref . '<br>';
844
        $label .= '<b>' . $langs->trans('MainLanguage') . ':</b> ' . $this->lang;
845
846
        $linkstart = '<a href="' . constant('BASE_URL') . '/website/card.php?id=' . $this->id . '"';
847
        $linkstart .= ($notooltip ? '' : ' title="' . dol_escape_htmltag($label, 1) . '" class="classfortooltip' . ($morecss ? ' ' . $morecss : '') . '"');
848
        $linkstart .= '>';
849
        $linkend = '</a>';
850
851
        $linkstart = $linkend = '';
852
853
        if ($withpicto) {
854
            $result .= ($linkstart . img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? '' : 'class="classfortooltip"')) . $linkend);
855
            if ($withpicto != 2) {
856
                $result .= ' ';
857
            }
858
        }
859
        $result .= $linkstart . $this->ref . $linkend;
860
        return $result;
861
    }
862
863
    /**
864
     *  Return the label of the status
865
     *
866
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
867
     *  @return string                 Label of status
868
     */
869
    public function getLibStatut($mode = 0)
870
    {
871
        return $this->LibStatut($this->status, $mode);
872
    }
873
874
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
875
    /**
876
     *  Return the label of a given status
877
     *
878
     *  @param  int     $status        Id status
879
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
880
     *  @return string                 Label of status
881
     */
882
    public function LibStatut($status, $mode = 0)
883
    {
884
		// phpcs:enable
885
        global $langs;
886
887
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
888
            global $langs;
889
            //$langs->load("mymodule");
890
            $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Offline');
891
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Online');
892
            $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Offline');
893
            $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Online');
894
        }
895
896
        $statusType = 'status5';
897
        if ($status == self::STATUS_VALIDATED) {
898
            $statusType = 'status4';
899
        }
900
901
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
902
    }
903
904
905
    /**
906
     * Initialise object with example values
907
     * Id must be 0 if object instance is a specimen
908
     *
909
     * @return int
910
     */
911
    public function initAsSpecimen()
912
    {
913
        global $user;
914
915
        $this->id = 0;
916
        $this->specimen = 1;
917
        $this->entity = 1;
918
        $this->ref = 'myspecimenwebsite';
919
        $this->description = 'A specimen website';
920
        $this->lang = 'en';
921
        $this->otherlang = 'fr,es';
922
        $this->status = 1;
923
        $this->fk_default_home = 0;
924
        $this->virtualhost = 'http://myvirtualhost';
925
        $this->fk_user_creat = $user->id;
926
        $this->fk_user_modif = $user->id;
927
        $this->date_creation = dol_now();
928
        $this->tms = dol_now();
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$tms has been deprecated: Use $date_modification ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

928
        /** @scrutinizer ignore-deprecated */ $this->tms = dol_now();

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
929
930
        return 1;
931
    }
932
933
934
    /**
935
     * Generate a zip with all data of web site.
936
     *
937
     * @return  string                      Path to file with zip or '' if error
938
     */
939
    public function exportWebSite()
940
    {
941
        global $conf, $mysoc;
942
943
        $website = $this;
944
945
        if (empty($website->id) || empty($website->ref)) {
946
            setEventMessages("Website id or ref is not defined", null, 'errors');
947
            return '';
948
        }
949
950
        dol_syslog("Create temp dir " . $conf->website->dir_temp);
951
        dol_mkdir($conf->website->dir_temp);
952
        if (!is_writable($conf->website->dir_temp)) {
953
            setEventMessages("Temporary dir " . $conf->website->dir_temp . " is not writable", null, 'errors');
954
            return '';
955
        }
956
957
        $destdir = $conf->website->dir_temp . '/' . $website->ref;
958
        dol_syslog("Clear temp dir " . $destdir);
959
        $count = 0;
960
        $countreallydeleted = 0;
961
        $counttodelete = dol_delete_dir_recursive($destdir, $count, 1, 0, $countreallydeleted);
962
        if ($counttodelete != $countreallydeleted) {
963
            setEventMessages("Failed to clean temp directory " . $destdir, null, 'errors');
964
            return '';
965
        }
966
967
        $arrayreplacementinfilename = array();
968
        $arrayreplacementincss = array();
969
        $arrayreplacementincss['file=image/' . $website->ref . '/'] = "file=image/__WEBSITE_KEY__/";
970
        $arrayreplacementincss['file=js/' . $website->ref . '/'] = "file=js/__WEBSITE_KEY__/";
971
        $arrayreplacementincss['medias/image/' . $website->ref . '/'] = "medias/image/__WEBSITE_KEY__/";
972
        $arrayreplacementincss['medias/js/' . $website->ref . '/'] = "medias/js/__WEBSITE_KEY__/";
973
        if ($mysoc->logo_small) {
974
            $arrayreplacementincss['file=logos%2Fthumbs%2F' . $mysoc->logo_small] = "file=logos%2Fthumbs%2F__LOGO_SMALL_KEY__";
975
        }
976
        if ($mysoc->logo_mini) {
977
            $arrayreplacementincss['file=logos%2Fthumbs%2F' . $mysoc->logo_mini] = "file=logos%2Fthumbs%2F__LOGO_MINI_KEY__";
978
        }
979
        if ($mysoc->logo) {
980
            $arrayreplacementincss['file=logos%2Fthumbs%2F' . $mysoc->logo] = "file=logos%2Fthumbs%2F__LOGO_KEY__";
981
        }
982
983
        // Create output directories
984
        dol_syslog("Create containers dir");
985
        dol_mkdir($conf->website->dir_temp . '/' . $website->ref . '/containers');
986
        dol_mkdir($conf->website->dir_temp . '/' . $website->ref . '/medias/image/websitekey');
987
        dol_mkdir($conf->website->dir_temp . '/' . $website->ref . '/medias/js/websitekey');
988
989
        // Copy files into 'containers'
990
        $srcdir = $conf->website->dir_output . '/' . $website->ref;
991
        $destdir = $conf->website->dir_temp . '/' . $website->ref . '/containers';
992
993
        dol_syslog("Copy pages from " . $srcdir . " into " . $destdir);
994
        dolCopyDir($srcdir, $destdir, 0, 1, $arrayreplacementinfilename, 2, array('old', 'back'), 1);
995
996
        // Copy file README.md and LICENSE from directory containers into directory root
997
        if (dol_is_file($conf->website->dir_temp . '/' . $website->ref . '/containers/README.md')) {
998
            dol_copy($conf->website->dir_temp . '/' . $website->ref . '/containers/README.md', $conf->website->dir_temp . '/' . $website->ref . '/README.md');
999
        }
1000
        if (dol_is_file($conf->website->dir_temp . '/' . $website->ref . '/containers/LICENSE')) {
1001
            dol_copy($conf->website->dir_temp . '/' . $website->ref . '/containers/LICENSE', $conf->website->dir_temp . '/' . $website->ref . '/LICENSE');
1002
        }
1003
1004
        // Copy files into medias/image
1005
        $srcdir = DOL_DATA_ROOT . '/medias/image/' . $website->ref;
1006
        $destdir = $conf->website->dir_temp . '/' . $website->ref . '/medias/image/websitekey';
1007
1008
        dol_syslog("Copy content from " . $srcdir . " into " . $destdir);
1009
        dolCopyDir($srcdir, $destdir, 0, 1, $arrayreplacementinfilename);
1010
1011
        // Copy files into medias/js
1012
        $srcdir = DOL_DATA_ROOT . '/medias/js/' . $website->ref;
1013
        $destdir = $conf->website->dir_temp . '/' . $website->ref . '/medias/js/websitekey';
1014
1015
        dol_syslog("Copy content from " . $srcdir . " into " . $destdir);
1016
        dolCopyDir($srcdir, $destdir, 0, 1, $arrayreplacementinfilename);
1017
1018
        // Make some replacement into some files
1019
        $cssindestdir = $conf->website->dir_temp . '/' . $website->ref . '/containers/styles.css.php';
1020
        if (dol_is_file($cssindestdir)) {
1021
            dolReplaceInFile($cssindestdir, $arrayreplacementincss);
1022
        }
1023
1024
        $htmldeaderindestdir = $conf->website->dir_temp . '/' . $website->ref . '/containers/htmlheader.html';
1025
        if (dol_is_file($htmldeaderindestdir)) {
1026
            dolReplaceInFile($htmldeaderindestdir, $arrayreplacementincss);
1027
        }
1028
1029
        // Build the website_page.sql file
1030
        $filesql = $conf->website->dir_temp . '/' . $website->ref . '/website_pages.sql';
1031
        $fp = fopen($filesql, "w");
1032
        if (empty($fp)) {
1033
            setEventMessages("Failed to create file " . $filesql, null, 'errors');
1034
            return '';
1035
        }
1036
1037
        $objectpages = new WebsitePage($this->db);
1038
        $listofpages = $objectpages->fetchAll($website->id);
1039
1040
1041
        // Assign ->newid and ->newfk_page starting at 1.
1042
        $i = 1;
1043
        foreach ($listofpages as $pageid => $objectpageold) {
0 ignored issues
show
Bug introduced by
The expression $listofpages of type integer is not traversable.
Loading history...
1044
            $objectpageold->newid = $i;
1045
            $i++;
1046
        }
1047
        $i = 1;
1048
        foreach ($listofpages as $pageid => $objectpageold) {
0 ignored issues
show
Bug introduced by
The expression $listofpages of type integer is not traversable.
Loading history...
1049
            // Search newid
1050
            $newfk_page = 0;
1051
            foreach ($listofpages as $pageid2 => $objectpageold2) {
0 ignored issues
show
Bug introduced by
The expression $listofpages of type integer is not traversable.
Loading history...
1052
                if ($pageid2 == $objectpageold->fk_page) {
1053
                    $newfk_page = $objectpageold2->newid;
1054
                    break;
1055
                }
1056
            }
1057
            $objectpageold->newfk_page = $newfk_page;
1058
            $i++;
1059
        }
1060
1061
        $line = '-- File generated by Dolibarr ' . DOL_VERSION . ' --;' . "\n";
1062
        $line .= "\n";
1063
        fwrite($fp, $line);
1064
1065
        foreach ($listofpages as $pageid => $objectpageold) {
0 ignored issues
show
Bug introduced by
The expression $listofpages of type integer is not traversable.
Loading history...
1066
            $oldpageid = $objectpageold->id;
1067
1068
            $allaliases = $objectpageold->pageurl;
1069
            $allaliases .= ($objectpageold->aliasalt ? ',' . $objectpageold->aliasalt : '');
1070
1071
            if (!getDolGlobalInt('WEBSITE_EXPORT_KEEP_FILES_OF_PAGES')) {
1072
                // We don't need to keep the PHP files of pages and aliases (they are regenerated at import) so we remove them.
1073
                // Delete the pageX.tpl.php page
1074
                dol_delete_file($conf->website->dir_temp . '/' . $website->ref . '/containers/page' . $objectpageold->id . '.tpl.php', 0, 0, 0, null, false, 0);
1075
                // Delete the alias page
1076
                dol_delete_file($conf->website->dir_temp . '/' . $website->ref . '/containers/' . $objectpageold->pageurl . '.php', 0, 0, 0, null, false, 0);
1077
                dol_delete_file($conf->website->dir_temp . '/' . $website->ref . '/containers/*/' . $objectpageold->pageurl . '.php', 0, 0, 0, null, false, 0);
1078
                // Delete alternative alias pages
1079
                $arrayofaliases = explode(',', $objectpageold->aliasalt);
1080
                foreach ($arrayofaliases as $tmpaliasalt) {
1081
                    dol_delete_file($conf->website->dir_temp . '/' . $website->ref . '/containers/' . trim($tmpaliasalt) . '.php', 0, 0, 0, null, false, 0);
1082
                    dol_delete_file($conf->website->dir_temp . '/' . $website->ref . '/containers/*/' . trim($tmpaliasalt) . '.php', 0, 0, 0, null, false, 0);
1083
                }
1084
            }
1085
1086
            // This comment syntax is important, it is parsed by import to get information on page ID and all aliases to regenerate
1087
            $line = '-- Page ID ' . $objectpageold->newid . '__+MAX_llx_website_page__ - Aliases ' . $allaliases . ' --;'; // newid start at 1, 2...
1088
            $line .= "\n";
1089
            fwrite($fp, $line);
1090
1091
            // Warning: We must keep llx_ here. It is a generic SQL.
1092
            $line = 'INSERT INTO llx_website_page(rowid, fk_page, fk_website, pageurl, aliasalt, title, description, lang, image, keywords, status, date_creation, tms, import_key, grabbed_from, type_container, htmlheader, content, author_alias, allowed_in_frames)';
1093
            $line .= " VALUES(";
1094
            $line .= $objectpageold->newid . "__+MAX_llx_website_page__, ";
1095
            $line .= ($objectpageold->newfk_page ? $this->db->escape($objectpageold->newfk_page) . "__+MAX_llx_website_page__" : "null") . ", ";
1096
            $line .= "__WEBSITE_ID__, ";
1097
            $line .= "'" . $this->db->escape($objectpageold->pageurl) . "', ";
1098
            $line .= "'" . $this->db->escape($objectpageold->aliasalt) . "', ";
1099
            $line .= "'" . $this->db->escape($objectpageold->title) . "', ";
1100
            $line .= "'" . $this->db->escape($objectpageold->description) . "', ";
1101
            $line .= "'" . $this->db->escape($objectpageold->lang) . "', ";
1102
            $line .= "'" . $this->db->escape($objectpageold->image) . "', ";
1103
            $line .= "'" . $this->db->escape($objectpageold->keywords) . "', ";
1104
            $line .= "'" . $this->db->escape($objectpageold->status) . "', ";
1105
            $line .= "'" . $this->db->idate($objectpageold->date_creation) . "', ";
1106
            $line .= "'" . $this->db->idate($objectpageold->date_modification) . "', ";
1107
            $line .= ($objectpageold->import_key ? "'" . $this->db->escape($objectpageold->import_key) . "'" : "null") . ", ";
1108
            $line .= "'" . $this->db->escape($objectpageold->grabbed_from) . "', ";
1109
            $line .= "'" . $this->db->escape($objectpageold->type_container) . "', ";
1110
1111
            // Make substitution with a generic path into htmlheader content
1112
            $stringtoexport = $objectpageold->htmlheader;
1113
            $stringtoexport = str_replace(array("\r\n", "\r", "\n"), "__N__", $stringtoexport);
1114
            $stringtoexport = str_replace('file=image/' . $website->ref . '/', "file=image/__WEBSITE_KEY__/", $stringtoexport);
1115
            $stringtoexport = str_replace('file=js/' . $website->ref . '/', "file=js/__WEBSITE_KEY__/", $stringtoexport);
1116
            $stringtoexport = str_replace('medias/image/' . $website->ref . '/', "medias/image/__WEBSITE_KEY__/", $stringtoexport);
1117
            $stringtoexport = str_replace('medias/js/' . $website->ref . '/', "medias/js/__WEBSITE_KEY__/", $stringtoexport);
1118
1119
            $stringtoexport = str_replace('file=logos%2Fthumbs%2F' . $mysoc->logo_small, "file=logos%2Fthumbs%2F__LOGO_SMALL_KEY__", $stringtoexport);
1120
            $stringtoexport = str_replace('file=logos%2Fthumbs%2F' . $mysoc->logo_mini, "file=logos%2Fthumbs%2F__LOGO_MINI_KEY__", $stringtoexport);
1121
            $stringtoexport = str_replace('file=logos%2Fthumbs%2F' . $mysoc->logo, "file=logos%2Fthumbs%2F__LOGO_KEY__", $stringtoexport);
1122
            $line .= "'" . $this->db->escape(str_replace(array("\r\n", "\r", "\n"), "__N__", $stringtoexport)) . "', "; // Replace \r \n to have record on 1 line
1123
1124
            // Make substitution with a generic path into page content
1125
            $stringtoexport = $objectpageold->content;
1126
            $stringtoexport = str_replace(array("\r\n", "\r", "\n"), "__N__", $stringtoexport);
1127
            $stringtoexport = str_replace('file=image/' . $website->ref . '/', "file=image/__WEBSITE_KEY__/", $stringtoexport);
1128
            $stringtoexport = str_replace('file=js/' . $website->ref . '/', "file=js/__WEBSITE_KEY__/", $stringtoexport);
1129
            $stringtoexport = str_replace('medias/image/' . $website->ref . '/', "medias/image/__WEBSITE_KEY__/", $stringtoexport);
1130
            $stringtoexport = str_replace('medias/js/' . $website->ref . '/', "medias/js/__WEBSITE_KEY__/", $stringtoexport);
1131
            $stringtoexport = str_replace('"image/' . $website->ref . '/', '"image/__WEBSITE_KEY__/', $stringtoexport); // When we have a link src="image/websiteref/file.png" into html content
1132
            $stringtoexport = str_replace('"/image/' . $website->ref . '/', '"/image/__WEBSITE_KEY__/', $stringtoexport);   // When we have a link src="/image/websiteref/file.png" into html content
1133
            $stringtoexport = str_replace('"js/' . $website->ref . '/', '"js/__WEBSITE_KEY__/', $stringtoexport);
1134
            $stringtoexport = str_replace('"/js/' . $website->ref . '/', '"/js/__WEBSITE_KEY__/', $stringtoexport);
1135
1136
            $stringtoexport = str_replace('file=logos%2Fthumbs%2F' . $mysoc->logo_small, "file=logos%2Fthumbs%2F__LOGO_SMALL_KEY__", $stringtoexport);
1137
            $stringtoexport = str_replace('file=logos%2Fthumbs%2F' . $mysoc->logo_mini, "file=logos%2Fthumbs%2F__LOGO_MINI_KEY__", $stringtoexport);
1138
            $stringtoexport = str_replace('file=logos%2Fthumbs%2F' . $mysoc->logo, "file=logos%2Fthumbs%2F__LOGO_KEY__", $stringtoexport);
1139
1140
1141
            $line .= "'" . $this->db->escape($stringtoexport) . "', "; // Replace \r \n to have record on 1 line
1142
            $line .= "'" . $this->db->escape($objectpageold->author_alias) . "', ";
1143
            $line .= (int) $objectpageold->allowed_in_frames;
1144
            $line .= ");";
1145
            $line .= "\n";
1146
1147
            fwrite($fp, $line);
1148
1149
            // Add line to update home page id during import
1150
            //var_dump($this->fk_default_home.' - '.$objectpageold->id.' - '.$objectpageold->newid);exit;
1151
            if ($this->fk_default_home > 0 && ($objectpageold->id == $this->fk_default_home) && ($objectpageold->newid > 0)) {  // This is the page that is set as the home page
1152
                // Warning: We must keep llx_ here. It is a generic SQL.
1153
                $line = "UPDATE llx_website SET fk_default_home = " . ($objectpageold->newid > 0 ? $this->db->escape($objectpageold->newid) . "__+MAX_llx_website_page__" : "null") . " WHERE rowid = __WEBSITE_ID__;";
1154
                $line .= "\n";
1155
                fwrite($fp, $line);
1156
            }
1157
1158
            fwrite($fp, "\n");
1159
        }
1160
1161
        $line = "\n-- For Dolibarr v14+ --;\n";
1162
        $line .= "UPDATE llx_website SET lang = '" . $this->db->escape($this->lang) . "' WHERE rowid = __WEBSITE_ID__;\n";
1163
        $line .= "UPDATE llx_website SET otherlang = '" . $this->db->escape($this->otherlang) . "' WHERE rowid = __WEBSITE_ID__;\n";
1164
        $line .= "\n";
1165
        fwrite($fp, $line);
1166
1167
        fclose($fp);
1168
1169
        dolChmod($filesql);
1170
1171
        // Build zip file
1172
        $filedir  = $conf->website->dir_temp . '/' . $website->ref . '/.';
1173
        $fileglob = $conf->website->dir_temp . '/' . $website->ref . '/website_' . $website->ref . '-*.zip';
1174
        $filename = $conf->website->dir_temp . '/' . $website->ref . '/website_' . $website->ref . '-' . dol_print_date(dol_now(), 'dayhourlog') . '-V' . ((float) DOL_VERSION) . '.zip';
1175
1176
        dol_delete_file($fileglob, 0);
1177
1178
        $result = dol_compress_dir($filedir, $filename, 'zip');
1179
1180
        if ($result > 0) {
1181
            return $filename;
1182
        } else {
1183
            global $errormsg;
1184
            $this->error = $errormsg;
1185
            return '';
1186
        }
1187
    }
1188
1189
1190
    /**
1191
     * Open a zip with all data of web site and load it into database.
1192
     *
1193
     * @param   string      $pathtofile     Full path of zip file
1194
     * @return  int                         Return integer <0 if KO, Id of new website if OK
1195
     */
1196
    public function importWebSite($pathtofile)
1197
    {
1198
        global $conf, $mysoc;
1199
1200
        $error = 0;
1201
1202
        $pathtofile = dol_sanitizePathName($pathtofile);
1203
        $object = $this;
1204
        if (empty($object->ref)) {
1205
            $this->error = 'Function importWebSite called on object not loaded (object->ref is empty)';
1206
            return -2;
1207
        }
1208
1209
        dol_delete_dir_recursive($conf->website->dir_temp . "/" . $object->ref);
1210
        dol_mkdir($conf->website->dir_temp . '/' . $object->ref);
1211
1212
        $filename = basename($pathtofile);
1213
        if (!preg_match('/^website_(.*)-(.*)$/', $filename, $reg)) {
1214
            $this->errors[] = 'Bad format for filename ' . $filename . '. Must be website_XXX-VERSION.';
1215
            return -3;
1216
        }
1217
1218
        $result = dol_uncompress($pathtofile, $conf->website->dir_temp . '/' . $object->ref);
1219
1220
        if (!empty($result['error'])) {
1221
            $this->errors[] = 'Failed to unzip file ' . $pathtofile . '.';
1222
            return -4;
1223
        }
1224
1225
        $arrayreplacement = array();
1226
        $arrayreplacement['__WEBSITE_ID__'] = $object->id;
1227
        $arrayreplacement['__WEBSITE_KEY__'] = $object->ref;
1228
        $arrayreplacement['__N__'] = $this->db->escape("\n"); // Restore \n
1229
        $arrayreplacement['__LOGO_SMALL_KEY__'] = $this->db->escape($mysoc->logo_small);
1230
        $arrayreplacement['__LOGO_MINI_KEY__'] = $this->db->escape($mysoc->logo_mini);
1231
        $arrayreplacement['__LOGO_KEY__'] = $this->db->escape($mysoc->logo);
1232
1233
1234
        // Copy containers directory
1235
        dolCopyDir($conf->website->dir_temp . '/' . $object->ref . '/containers', $conf->website->dir_output . '/' . $object->ref, 0, 1); // Overwrite if exists
1236
1237
        // Make replacement into css and htmlheader file
1238
        $cssindestdir = $conf->website->dir_output . '/' . $object->ref . '/styles.css.php';
1239
        $result = dolReplaceInFile($cssindestdir, $arrayreplacement);
1240
1241
        $htmldeaderindestdir = $conf->website->dir_output . '/' . $object->ref . '/htmlheader.html';
1242
        $result = dolReplaceInFile($htmldeaderindestdir, $arrayreplacement);
1243
1244
        // Now generate the master.inc.php page
1245
        $filemaster = $conf->website->dir_output . '/' . $object->ref . '/master.inc.php';
1246
        $result = dolSaveMasterFile($filemaster);
1247
        if (!$result) {
1248
            $this->errors[] = 'Failed to write file ' . $filemaster;
1249
            $error++;
1250
        }
1251
1252
        // Copy dir medias/image/websitekey
1253
        if (dol_is_dir($conf->website->dir_temp . '/' . $object->ref . '/medias/image/websitekey')) {
1254
            $result = dolCopyDir($conf->website->dir_temp . '/' . $object->ref . '/medias/image/websitekey', $conf->website->dir_output . '/' . $object->ref . '/medias/image/' . $object->ref, 0, 1);
1255
            if ($result < 0) {
1256
                $this->error = 'Failed to copy files into ' . $conf->website->dir_output . '/' . $object->ref . '/medias/image/' . $object->ref . '.';
1257
                dol_syslog($this->error, LOG_WARNING);
1258
                $this->errors[] = $this->error;
1259
                return -5;
1260
            }
1261
        }
1262
1263
        // Copy dir medias/js/websitekey
1264
        if (dol_is_dir($conf->website->dir_temp . '/' . $object->ref . '/medias/js/websitekey')) {
1265
            $result = dolCopyDir($conf->website->dir_temp . '/' . $object->ref . '/medias/js/websitekey', $conf->website->dir_output . '/' . $object->ref . '/medias/js/' . $object->ref, 0, 1);
1266
            if ($result < 0) {
1267
                $this->error = 'Failed to copy files into ' . $conf->website->dir_output . '/' . $object->ref . '/medias/js/' . $object->ref . '.';
1268
                dol_syslog($this->error, LOG_WARNING);
1269
                $this->errors[] = $this->error;
1270
                return -6;
1271
            }
1272
        }
1273
1274
        $sqlfile = $conf->website->dir_temp . "/" . $object->ref . '/website_pages.sql';
1275
1276
        $result = dolReplaceInFile($sqlfile, $arrayreplacement);
1277
1278
        $this->db->begin();
1279
1280
        // Search the $maxrowid because we need it later
1281
        $sqlgetrowid = 'SELECT MAX(rowid) as max from ' . MAIN_DB_PREFIX . 'website_page';
1282
        $resql = $this->db->query($sqlgetrowid);
1283
        if ($resql) {
1284
            $obj = $this->db->fetch_object($resql);
1285
            $maxrowid = $obj->max;
1286
        }
1287
1288
        // Load sql record
1289
        $runsql = run_sql($sqlfile, 1, '', 0, '', 'none', 0, 1, 0, 0, 1); // The maxrowid of table is searched into this function two
1290
        if ($runsql <= 0) {
1291
            $this->errors[] = 'Failed to load sql file ' . $sqlfile . ' (ret=' . ((int) $runsql) . ')';
1292
            $error++;
1293
        }
1294
1295
        $objectpagestatic = new WebsitePage($this->db);
1296
1297
        // Regenerate the php files for pages
1298
        $fp = fopen($sqlfile, "r");
1299
        if ($fp) {
1300
            while (!feof($fp)) {
1301
                $reg = array();
1302
1303
                // Warning fgets with second parameter that is null or 0 hang.
1304
                $buf = fgets($fp, 65000);
1305
                $newid = 0;
1306
1307
                // Scan the line
1308
                if (preg_match('/^-- Page ID (\d+)\s[^\s]+\s(\d+).*Aliases\s(.+)\s--;/i', $buf, $reg)) {
1309
                    // Example of line: "-- Page ID 179 -> 1__+MAX_llx_website_page__ - Aliases about-us --;"
1310
                    $oldid = (int) $reg[1];
1311
                    $newid = ((int) $reg[2] + $maxrowid);
1312
                    $aliasesarray = explode(',', $reg[3]);
1313
1314
                    dol_syslog("In sql source file, we have the page ID " . $oldid . " to replace with the new ID " . $newid . ", and we must create the shortcut aliases: " . $reg[3]);
1315
1316
                    //dol_move($conf->website->dir_output.'/'.$object->ref.'/page'.$oldid.'.tpl.php', $conf->website->dir_output.'/'.$object->ref.'/page'.$newid.'.tpl.php', 0, 1, 0, 0);
1317
                } elseif (preg_match('/^-- Page ID (\d+).*Aliases\s(.*)\s--;/i', $buf, /** @var string[] $reg */ $reg)) {
1318
                    // Example of line: "-- Page ID 1__+MAX_llx_website_page__ - Aliases about-us --;"
1319
                    $newid = ((int) $reg[1] + $maxrowid);
1320
                    $aliasesarray = explode(',', $reg[2]);
1321
1322
                    dol_syslog("In sql source file, we have the page with the new ID " . $newid . ", and we must create the shortcut aliases: " . $reg[2]);
1323
                }
1324
1325
                if ($newid) {
1326
                    $objectpagestatic->fetch($newid);
1327
1328
                    // We regenerate the pageX.tpl.php
1329
                    $filetpl = $conf->website->dir_output . '/' . $object->ref . '/page' . $newid . '.tpl.php';
1330
                    $result = dolSavePageContent($filetpl, $object, $objectpagestatic);
1331
                    if (!$result) {
1332
                        $this->errors[] = 'Failed to write file ' . basename($filetpl);
1333
                        $error++;
1334
                    }
1335
1336
                    // Regenerate also the main alias + alternative aliases pages
1337
                    if (is_array($aliasesarray)) {
1338
                        foreach ($aliasesarray as $aliasshortcuttocreate) {
1339
                            if (trim($aliasshortcuttocreate)) {
1340
                                $filealias = $conf->website->dir_output . '/' . $object->ref . '/' . trim($aliasshortcuttocreate) . '.php';
1341
                                $result = dolSavePageAlias($filealias, $object, $objectpagestatic);
1342
                                if (!$result) {
1343
                                    $this->errors[] = 'Failed to write file ' . basename($filealias);
1344
                                    $error++;
1345
                                }
1346
                            }
1347
                        }
1348
                    }
1349
                }
1350
            }
1351
        }
1352
1353
        // Read record of website that has been updated by the run_sql function previously called so we can get the
1354
        // value of fk_default_home that is ID of home page
1355
        $sql = "SELECT fk_default_home FROM " . MAIN_DB_PREFIX . "website WHERE rowid = " . ((int) $object->id);
1356
        $resql = $this->db->query($sql);
1357
        if ($resql) {
1358
            $obj = $this->db->fetch_object($resql);
1359
            if ($obj) {
1360
                $object->fk_default_home = $obj->fk_default_home;
1361
            } else {
1362
                //$this->errors[] = 'Failed to get the Home page';
1363
                //$error++;
1364
            }
1365
        }
1366
1367
        // Regenerate the index.php page to point to the new index page
1368
        $pathofwebsite = $conf->website->dir_output . '/' . $object->ref;
1369
        dolSaveIndexPage($pathofwebsite, $pathofwebsite . '/index.php', $pathofwebsite . '/page' . $object->fk_default_home . '.tpl.php', $pathofwebsite . '/wrapper.php', $object);
1370
1371
        //$this->initFilesStatus($pathofwebsite);
1372
1373
        if ($error) {
1374
            $this->db->rollback();
1375
            return -1;
1376
        } else {
1377
            $this->db->commit();
1378
            return $object->id;
1379
        }
1380
    }
1381
1382
    /**
1383
     * Rebuild all files of all the pages/containers of a website. Rebuild also the index and wrapper.php file.
1384
     * Note: Files are already regenerated during importWebSite so this function is useless when importing a website.
1385
     *
1386
     * @return  int                     Return integer <0 if KO, >=0 if OK
1387
     */
1388
    public function rebuildWebSiteFiles()
1389
    {
1390
        global $conf;
1391
1392
        $error = 0;
1393
1394
        $object = $this;
1395
        if (empty($object->ref)) {
1396
            $this->error = 'Function rebuildWebSiteFiles called on object not loaded (object->ref is empty)';
1397
            return -1;
1398
        }
1399
1400
        $objectpagestatic = new WebsitePage($this->db);
1401
1402
        $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "website_page WHERE fk_website = " . ((int) $this->id);
1403
1404
        $resql = $this->db->query($sql);
1405
        if (!$resql) {
1406
            $this->error = $this->db->lasterror();
1407
            return -1;
1408
        }
1409
1410
        $num = $this->db->num_rows($resql);
1411
1412
        // Loop on each container/page
1413
        $i = 0;
1414
        while ($i < $num) {
1415
            $obj = $this->db->fetch_object($resql);
1416
1417
            $newid = $obj->rowid;
1418
1419
            $objectpagestatic->fetch($newid);
1420
1421
            $aliasesarray = explode(',', $objectpagestatic->aliasalt);
1422
1423
            $filetpl = $conf->website->dir_output . '/' . $object->ref . '/page' . $newid . '.tpl.php';
1424
            $result = dolSavePageContent($filetpl, $object, $objectpagestatic);
1425
            if (!$result) {
1426
                $this->errors[] = 'Failed to write file ' . basename($filetpl);
1427
                $error++;
1428
            }
1429
1430
            // Add main alias to list of alternative aliases
1431
            if (!empty($objectpagestatic->pageurl) && !in_array($objectpagestatic->pageurl, $aliasesarray)) {
1432
                $aliasesarray[] = $objectpagestatic->pageurl;
1433
            }
1434
1435
            // Regenerate also all aliases pages (pages with a natural name) by calling dolSavePageAlias()
1436
            if (is_array($aliasesarray)) {
1437
                foreach ($aliasesarray as $aliasshortcuttocreate) {
1438
                    if (trim($aliasshortcuttocreate)) {
1439
                        $filealias = $conf->website->dir_output . '/' . $object->ref . '/' . trim($aliasshortcuttocreate) . '.php';
1440
                        $result = dolSavePageAlias($filealias, $object, $objectpagestatic); // This includes also a copy into sublanguage directories.
1441
                        if (!$result) {
1442
                            $this->errors[] = 'Failed to write file ' . basename($filealias);
1443
                            $error++;
1444
                        }
1445
                    }
1446
                }
1447
            }
1448
1449
            $i++;
1450
        }
1451
1452
        if (!$error) {
1453
            // Save index.php and wrapper.php
1454
            $pathofwebsite = $conf->website->dir_output . '/' . $object->ref;
1455
            $fileindex = $pathofwebsite . '/index.php';
1456
            $filetpl = '';
1457
            if ($object->fk_default_home > 0) {
1458
                $filetpl = $pathofwebsite . '/page' . $object->fk_default_home . '.tpl.php';
1459
            }
1460
            $filewrapper = $pathofwebsite . '/wrapper.php';
1461
            dolSaveIndexPage($pathofwebsite, $fileindex, $filetpl, $filewrapper, $object);  // This includes also a version of index.php into sublanguage directories
1462
        }
1463
1464
        if ($error) {
1465
            return -1;
1466
        } else {
1467
            return $num;
1468
        }
1469
    }
1470
1471
    /**
1472
     * Return if web site is a multilanguage web site. Return false if there is only 0 or 1 language.
1473
     *
1474
     * @return boolean          True if web site is a multilanguage web site
1475
     */
1476
    public function isMultiLang()
1477
    {
1478
        return !empty($this->otherlang);
1479
    }
1480
1481
    /**
1482
     * Component to select language inside a container (Full CSS Only)
1483
     *
1484
     * @param   array|string    $languagecodes          'auto' to show all languages available for page, or language codes array like array('en','fr','de','es')
1485
     * @param   Translate       $weblangs               Language Object
1486
     * @param   string          $morecss                More CSS class on component
1487
     * @param   string          $htmlname               Suffix for HTML name
1488
     * @return  string                                  HTML select component
1489
     */
1490
    public function componentSelectLang($languagecodes, $weblangs, $morecss = '', $htmlname = '')
1491
    {
1492
        global $websitepagefile, $website;
1493
1494
        if (!is_object($weblangs)) {
1495
            return 'ERROR componentSelectLang called with parameter $weblangs not defined';
1496
        }
1497
1498
        $arrayofspecialmainlanguages = array(
1499
            'en' => 'en_US',
1500
            'sq' => 'sq_AL',
1501
            'ar' => 'ar_SA',
1502
            'eu' => 'eu_ES',
1503
            'bn' => 'bn_DB',
1504
            'bs' => 'bs_BA',
1505
            'ca' => 'ca_ES',
1506
            'zh' => 'zh_CN',
1507
            'cs' => 'cs_CZ',
1508
            'da' => 'da_DK',
1509
            'et' => 'et_EE',
1510
            'ka' => 'ka_GE',
1511
            'el' => 'el_GR',
1512
            'he' => 'he_IL',
1513
            'kn' => 'kn_IN',
1514
            'km' => 'km_KH',
1515
            'ko' => 'ko_KR',
1516
            'lo' => 'lo_LA',
1517
            'nb' => 'nb_NO',
1518
            'fa' => 'fa_IR',
1519
            'sr' => 'sr_RS',
1520
            'sl' => 'sl_SI',
1521
            'uk' => 'uk_UA',
1522
            'vi' => 'vi_VN'
1523
        );
1524
1525
        // Load tmppage if we have $websitepagefile defined
1526
        $tmppage = new WebsitePage($this->db);
1527
1528
        $pageid = 0;
1529
        if (!empty($websitepagefile)) {
1530
            $websitepagefileshort = basename($websitepagefile);
1531
            if ($websitepagefileshort == 'index.php') {
1532
                $pageid = $website->fk_default_home;
1533
            } else {
1534
                $pageid = str_replace(array('.tpl.php', 'page'), array('', ''), $websitepagefileshort);
1535
            }
1536
            if ($pageid > 0) {
1537
                $tmppage->fetch($pageid);
1538
            }
1539
        }
1540
1541
        // Fill $languagecodes array with existing translation, nothing if none
1542
        if (!is_array($languagecodes) && $pageid > 0) {
1543
            $languagecodes = array();
1544
1545
            $sql = "SELECT wp.rowid, wp.lang, wp.pageurl, wp.fk_page";
1546
            $sql .= " FROM " . MAIN_DB_PREFIX . "website_page as wp";
1547
            $sql .= " WHERE wp.fk_website = " . ((int) $website->id);
1548
            $sql .= " AND (wp.fk_page = " . ((int) $pageid) . " OR wp.rowid  = " . ((int) $pageid);
1549
            if ($tmppage->fk_page > 0) {
1550
                $sql .= " OR wp.fk_page = " . ((int) $tmppage->fk_page) . " OR wp.rowid = " . ((int) $tmppage->fk_page);
1551
            }
1552
            $sql .= ")";
1553
1554
            $resql = $this->db->query($sql);
1555
            if ($resql) {
1556
                while ($obj = $this->db->fetch_object($resql)) {
1557
                    $newlang = $obj->lang;
1558
                    if ($obj->rowid == $pageid) {
1559
                        $newlang = $obj->lang;
1560
                    }
1561
                    if (!in_array($newlang, $languagecodes)) {
1562
                        $languagecodes[] = $newlang;
1563
                    }
1564
                }
1565
            }
1566
        }
1567
        // Now $languagecodes is always an array. Example array('en', 'fr', 'es');
1568
1569
        $languagecodeselected = substr($weblangs->defaultlang, 0, 2); // Because we must init with a value, but real value is the lang of main parent container
1570
        if (!empty($websitepagefile)) {
1571
            $pageid = str_replace(array('.tpl.php', 'page'), array('', ''), basename($websitepagefile));
1572
            if ($pageid > 0) {
1573
                $pagelang = substr($tmppage->lang, 0, 2);
1574
                $languagecodeselected = substr($pagelang, 0, 2);
1575
                if (!in_array($pagelang, $languagecodes)) {
1576
                    $languagecodes[] = $pagelang; // We add language code of page into combo list
1577
                }
1578
            }
1579
        }
1580
1581
        $weblangs->load('languages');
1582
        //var_dump($weblangs->defaultlang);
1583
1584
        $url = $_SERVER["REQUEST_URI"];
1585
        $url = preg_replace('/(\?|&)l=([a-zA-Z_]*)/', '', $url); // We remove param l from url
1586
        //$url = preg_replace('/(\?|&)lang=([a-zA-Z_]*)/', '', $url);   // We remove param lang from url
1587
        $url .= (preg_match('/\?/', $url) ? '&' : '?') . 'l=';
1588
        if (!preg_match('/^\//', $url)) {
1589
            $url = '/' . $url;
1590
        }
1591
1592
        $HEIGHTOPTION = 40;
1593
        $MAXHEIGHT = 4 * $HEIGHTOPTION;
1594
        $nboflanguage = count($languagecodes);
1595
1596
        $out = '<!-- componentSelectLang' . $htmlname . ' -->' . "\n";
1597
1598
        $out .= '<style>';
1599
        $out .= '.componentSelectLang' . $htmlname . ':hover { height: ' . min($MAXHEIGHT, ($HEIGHTOPTION * $nboflanguage)) . 'px; overflow-x: hidden; overflow-y: ' . ((($HEIGHTOPTION * $nboflanguage) > $MAXHEIGHT) ? ' scroll' : 'hidden') . '; }' . "\n";
1600
        $out .= '.componentSelectLang' . $htmlname . ' li { line-height: ' . $HEIGHTOPTION . 'px; }' . "\n";
1601
        $out .= '.componentSelectLang' . $htmlname . ' {
1602
			display: inline-block;
1603
			padding: 0;
1604
			height: ' . $HEIGHTOPTION . 'px;
1605
			overflow: hidden;
1606
			transition: all .3s ease;
1607
			margin: 0 0 0 0;
1608
			vertical-align: top;
1609
		}
1610
		.componentSelectLang' . $htmlname . ':hover, .componentSelectLang' . $htmlname . ':hover a { background-color: #fff; color: #000 !important; }
1611
		ul.componentSelectLang' . $htmlname . ' { width: 150px; }
1612
		ul.componentSelectLang' . $htmlname . ':hover .fa { visibility: hidden; }
1613
		.componentSelectLang' . $htmlname . ' a { text-decoration: none; width: 100%; }
1614
		.componentSelectLang' . $htmlname . ' li { display: block; padding: 0px 15px; margin-left: 0; margin-right: 0; }
1615
		.componentSelectLang' . $htmlname . ' li:hover { background-color: #EEE; }
1616
		';
1617
        $out .= '</style>';
1618
        $out .= '<ul class="componentSelectLang' . $htmlname . ($morecss ? ' ' . $morecss : '') . '">';
1619
1620
        if ($languagecodeselected) {
1621
            // Convert $languagecodeselected into a long language code
1622
            if (strlen($languagecodeselected) == 2) {
1623
                $languagecodeselected = (empty($arrayofspecialmainlanguages[$languagecodeselected]) ? $languagecodeselected . '_' . strtoupper($languagecodeselected) : $arrayofspecialmainlanguages[$languagecodeselected]);
1624
            }
1625
1626
            $countrycode = strtolower(substr($languagecodeselected, -2));
1627
            $label = $weblangs->trans("Language_" . $languagecodeselected);
1628
            if ($countrycode == 'us') {
1629
                $label = preg_replace('/\s*\(.*\)/', '', $label);
1630
            }
1631
            $out .= '<li><a href="' . $url . substr($languagecodeselected, 0, 2) . '"><img height="12px" src="/medias/image/common/flags/' . $countrycode . '.png" style="margin-right: 5px;"/><span class="websitecomponentlilang">' . $label . '</span>';
1632
            $out .= '<span class="fa fa-caret-down" style="padding-left: 5px;" />';
1633
            $out .= '</a></li>';
1634
        }
1635
        $i = 0;
1636
        if (is_array($languagecodes)) {
1637
            foreach ($languagecodes as $languagecode) {
1638
                // Convert $languagecode into a long language code
1639
                if (strlen($languagecode) == 2) {
1640
                    $languagecode = (empty($arrayofspecialmainlanguages[$languagecode]) ? $languagecode . '_' . strtoupper($languagecode) : $arrayofspecialmainlanguages[$languagecode]);
1641
                }
1642
1643
                if ($languagecode == $languagecodeselected) {
1644
                    continue; // Already output
1645
                }
1646
1647
                $countrycode = strtolower(substr($languagecode, -2));
1648
                $label = $weblangs->trans("Language_" . $languagecode);
1649
                if ($countrycode == 'us') {
1650
                    $label = preg_replace('/\s*\(.*\)/', '', $label);
1651
                }
1652
                $out .= '<li><a href="' . $url . substr($languagecode, 0, 2) . '"><img height="12px" src="/medias/image/common/flags/' . $countrycode . '.png" style="margin-right: 5px;"/><span class="websitecomponentlilang">' . $label . '</span>';
1653
                if (empty($i) && empty($languagecodeselected)) {
1654
                    $out .= '<span class="fa fa-caret-down" style="padding-left: 5px;" />';
1655
                }
1656
                $out .= '</a></li>';
1657
                $i++;
1658
            }
1659
        }
1660
        $out .= '</ul>';
1661
1662
        return $out;
1663
    }
1664
1665
    /**
1666
     * Overite template by copying all files
1667
     *
1668
     * @param   string  $pathtotmpzip       Path to the tmp zip file
1669
     * @param   string  $exportPath         Relative path of directory to export files into (specified by the user)
1670
     * @return  int                         Return integer <0 if KO, >0 if OK
1671
     */
1672
    public function overwriteTemplate(string $pathtotmpzip, $exportPath = '')
1673
    {
1674
        global $conf;
1675
1676
        //$error = 0;
1677
1678
        $website = $this;
1679
        if (empty($website->id) || empty($website->ref)) {
1680
            setEventMessages("Website id or ref is not defined", null, 'errors');
1681
            return -1;
1682
        }
1683
        if (empty($website->name_template) && empty($exportPath)) {
1684
            setEventMessages("To export the website template into a directory of the server, the name of the directory/template must be provided.", null, 'errors');
1685
            return -1;
1686
        }
1687
        if (!is_writable($conf->website->dir_temp)) {
1688
            setEventMessages("Temporary dir " . $conf->website->dir_temp . " is not writable", null, 'errors');
1689
            return -1;
1690
        }
1691
1692
        // Replace modified files into the doctemplates directory.
1693
        if (getDolGlobalString('WEBSITE_ALLOW_OVERWRITE_GIT_SOURCE')) {
1694
            // If the user has not specified a path
1695
            if (empty($exportPath)) {
1696
                $destdirrel = 'install/doctemplates/websites/' . $website->name_template;
1697
                $destdir = DOL_DOCUMENT_ROOT . '/' . $destdirrel;
1698
            } else {
1699
                $exportPath = rtrim($exportPath, '/');
1700
                if (strpos($exportPath, '..') !== false) {
1701
                    setEventMessages("Invalid path.", null, 'errors');
1702
                    return -1;
1703
                }
1704
                // if path start with / (absolute path)
1705
                if (strpos($exportPath, '/') === 0 || preg_match('/^[a-zA-Z]:/', $exportPath)) {
1706
                    if (!is_dir($exportPath)) {
1707
                        setEventMessages("The specified absolute path does not exist.", null, 'errors');
1708
                        return -1;
1709
                    }
1710
1711
                    if (!is_writable($exportPath)) {
1712
                        setEventMessages("The specified absolute path is not writable.", null, 'errors');
1713
                        return -1;
1714
                    }
1715
                    $destdirrel = $exportPath;
1716
                    $destdir = $exportPath;
1717
                } else {
1718
                    // relatif path
1719
                    $destdirrel = 'install/doctemplates/websites/' . $exportPath;
1720
                    $destdir = DOL_DOCUMENT_ROOT . '/' . $destdirrel;
1721
                }
1722
            }
1723
        }
1724
1725
        dol_mkdir($destdir);
1726
1727
        if (!is_writable($destdir)) {
1728
            setEventMessages("The specified path " . $destdir . " is not writable.", null, 'errors');
1729
            return -1;
1730
        }
1731
1732
        // Export on target sources
1733
        $resultarray = dol_uncompress($pathtotmpzip, $destdir);
1734
1735
        // Remove the file README and LICENSE from the $destdir/containers
1736
        if (dol_is_file($destdir . '/containers/README.md')) {
1737
            dol_move($destdir . '/containers/README.md', $destdir . '/README.md', '0', 1, 0, 0);
1738
        }
1739
        if (dol_is_file($destdir . '/containers/LICENSE')) {
1740
            dol_move($destdir . '/containers/LICENSE', $destdir . '/LICENSE', '0', 1, 0, 0);
1741
        }
1742
        /*
1743
        if (empty($exportPath)) {
1744
            dol_delete_file($destdir.'/containers/README.md');
1745
            dol_delete_file($destdir.'/containers/LICENSE');
1746
        }
1747
        */
1748
1749
        // Remove non required files (will be re-generated during the import)
1750
        dol_delete_file($destdir . '/containers/index.php');
1751
        dol_delete_file($destdir . '/containers/master.inc.php');
1752
1753
        // Now we remove the flag o+x on files
1754
        // TODO
1755
1756
        if (!empty($resultarray)) {
1757
            setEventMessages("Error, failed to unzip the export into target dir " . $destdir . ": " . implode(',', $resultarray), null, 'errors');
1758
        } else {
1759
            setEventMessages("Website content written into " . $destdirrel, null, 'mesgs');
1760
        }
1761
1762
        header("Location: " . $_SERVER["PHP_SELF"] . '?website=' . $website->ref);
1763
        exit();
1764
    }
1765
1766
    /**
1767
     * extract num of page
1768
     * @param  string  $filename   name of file
1769
     * @return int 1 if OK, -1 if KO
1770
     */
1771
    protected function extractNumberFromFilename($filename)
1772
    {
1773
        $matches = [];
1774
        if (preg_match('/page(\d+)\.tpl\.php/', $filename, $matches)) {
1775
            return (int) $matches[1];
1776
        }
1777
        return -1;
1778
    }
1779
1780
    /**
1781
     * update name_template in table after import template
1782
     * @param  string    $name_template   name of template
1783
     * @return int     1 if OK, -1 if KO
1784
     */
1785
    public function setTemplateName($name_template)
1786
    {
1787
        $sql = "UPDATE " . $this->db->prefix() . "website SET";
1788
        $sql .= " name_template = '" . $this->db->escape($name_template) . "'";
1789
        $sql .= " WHERE rowid = " . (int) $this->id;
1790
        $result = $this->db->query($sql);
1791
1792
        if ($result) {
1793
            $this->db->commit();
1794
            return 1;
1795
        } else {
1796
            $this->db->rollback();
1797
            return -1;
1798
        }
1799
    }
1800
1801
    /**
1802
     * check previous state for file
1803
     * @param  string   $pathname  path of file
1804
     * @return  array|mixed
1805
     */
1806
    public function checkPreviousState($pathname)
1807
    {
1808
        if (!file_exists($pathname)) {
1809
            if (touch($pathname)) {
1810
                dolChmod($pathname, '0664');
1811
            }
1812
            return [];
1813
        }
1814
        return unserialize(file_get_contents($pathname));
1815
    }
1816
1817
1818
    /**
1819
     * Save state for File
1820
     * @param mixed $etat   state
1821
     * @param mixed $pathname  path of file
1822
     * @return int|false
1823
     */
1824
    public function saveState($etat, $pathname)
1825
    {
1826
        return file_put_contents($pathname, serialize($etat));
1827
    }
1828
1829
    /**
1830
     * Compare two files has not same name but same content
1831
     * @param  string   $dossierSource        filepath of folder source
1832
     * @param  string   $dossierDestination   filepath of folder dest
1833
     * @param  mixed   $fichierModifie       files modified
1834
     * @return array    empty if KO, array if OK
1835
     */
1836
    public function compareFichierModifie($dossierSource, $dossierDestination, $fichierModifie)
1837
    {
1838
1839
        $fichiersSource = [];
1840
        $fichiersDestination = [];
1841
1842
        $fichierWithNoPage = [];
1843
        $fichierWithNoPageInDest = [];
1844
1845
        // Filter source files
1846
        foreach (dol_dir_list($dossierSource, "files") as $file) {
1847
            if (preg_match('/^page\d+/', $file['name']) && !str_contains($file['name'], '.old')) {
1848
                $fichiersSource[] = $file;
1849
            } else {
1850
                $fichierWithNoPage[] = $file;
1851
            }
1852
        }
1853
1854
        // Filter destination files
1855
        foreach (dol_dir_list($dossierDestination, "all", 1) as $file) {
1856
            if (preg_match('/^page\d+/', $file['name']) && !str_contains($file['name'], '.old')) {
1857
                $fichiersDestination[] = $file;
1858
            } else {
1859
                $fichierWithNoPageInDest[] = $file;
1860
            }
1861
        }
1862
1863
        // find index source and search it in folder destination
1864
        $numOfPageSource = 0;
1865
        foreach ($fichiersSource as $index => $file) {
1866
            if ($file['name'] == basename($fichierModifie['fullname'])) {
1867
                $numOfPageSource = $this->extractNumberFromFilename($file['name']);
1868
                break;
1869
            }
1870
        }
1871
1872
        //search numPage where was declared
1873
        $filesFound = array();
1874
        foreach ($fichierWithNoPage as $filesource) {
1875
            $fileContent = file_get_contents($filesource['fullname']);
1876
            if (strpos($fileContent, "require './page" . $numOfPageSource . ".tpl.php'") !== false) {
1877
                $filesFound = $filesource;
1878
                break;
1879
            }
1880
        }
1881
        // find file with same name and extract num page in destination folder
1882
        $numPagesFound = '';
1883
        foreach ($fichierWithNoPageInDest as $filedest) {
1884
            if ($filedest['name'] === $filesFound['name']) {
1885
                $fileContent = file_get_contents($filedest['fullname']);
1886
                if (preg_match("/page\d+\.tpl\.php/", $fileContent, $matches)) {
1887
                    $numPagesFound = $matches[0];
1888
                    break;
1889
                }
1890
            }
1891
        }
1892
        //search file with the number of pages found
1893
        $fileNeeded = array();
1894
        foreach ($fichiersDestination as $index => $file) {
1895
            if ($file['name'] == $numPagesFound) {
1896
                $fileNeeded = $file;
1897
                break;
1898
            }
1899
        }
1900
1901
        if (isset($fileNeeded)) {
1902
            $sourceContent = file_get_contents($fichierModifie['fullname']);
1903
            if (file_exists($fileNeeded['fullname'])) {
1904
                $destContent = file_get_contents($fileNeeded['fullname']);
1905
1906
                $numOfPageDest = $this->extractNumberFromFilename($fileNeeded['name']);
1907
                $differences = $this->showDifferences($destContent, $sourceContent, array($numOfPageDest,$numOfPageSource));
1908
                $differences['file_destination'] = $fileNeeded;
1909
            } else {
1910
                $differences = array();
1911
            }
1912
            return $differences;
1913
        }
1914
        return array();
1915
    }
1916
1917
    /**
1918
     * Remove spaces in string
1919
     * @param   string   $str    string
1920
     * @return string
1921
     */
1922
    private function normalizeString($str)
1923
    {
1924
        $str = str_replace("\r\n", "\n", $str);
1925
        $str = str_replace("\r", "\n", $str);
1926
        return $str;
1927
    }
1928
1929
    /**
1930
     * show difference between to string
1931
     * @param string  $str1   first string
1932
     * @param string  $str2   second string
1933
     * @param array  $exceptNumPge    num of page files we don't want to change
1934
     * @return array|int<-1,-1>      -1 if KO, array if OK
1935
     */
1936
    protected function showDifferences($str1, $str2, $exceptNumPge = array())
1937
    {
1938
        $diff = array();
1939
        $str1 = $this->normalizeString($str1);
1940
        $str2 = $this->normalizeString($str2);
1941
1942
        $lines1 = explode("\n", $str1);
1943
        $lines2 = explode("\n", $str2);
1944
1945
        $linesShouldChange = array();
1946
        $linesShouldNotChange = array();
1947
        $linefound = array();
1948
        $countNumPage = count($exceptNumPge);
1949
1950
        for ($i = 0;$i < $countNumPage; $i++) {
1951
            $linefound[$i] = array();
1952
            $linefound[$i]['meta'] = '/content="' . preg_quote($exceptNumPge[$i], '/') . '" \/>/';
1953
            $linefound[$i]['output'] = '/dolWebsiteOutput\(\$tmp, "html", ' . preg_quote($exceptNumPge[$i], '/') . '\);/';
1954
        }
1955
1956
        if (isset($linefound[1])) {
1957
            $maxLines = max(count($lines1), count($lines2));
1958
            for ($lineNum = 0; $lineNum < $maxLines; $lineNum++) {
1959
                $lineContent1 = $lines1[$lineNum] ?? '';
1960
                $lineContent2 = $lines2[$lineNum] ?? '';
1961
                if (preg_match($linefound[0]['output'], $lineContent1)) {
1962
                    $linesShouldChange[] = $lineContent1;
1963
                }
1964
                if (preg_match($linefound[0]['meta'], $lineContent1)) {
1965
                    $linesShouldChange[] = $lineContent1;
1966
                }
1967
                if (preg_match($linefound[1]['output'], $lineContent2)) {
1968
                    $linesShouldNotChange[] = $lineContent2;
1969
                }
1970
                if (preg_match($linefound[1]['meta'], $lineContent2)) {
1971
                    $linesShouldNotChange[] = $lineContent2;
1972
                }
1973
                if ($lineContent1 !== $lineContent2) {
1974
                    if (isset($lines1[$lineNum]) && !isset($lines2[$lineNum])) {
1975
                        // Ligne deleted de la source
1976
                        $diff["Supprimée à la ligne " . ($lineNum + 1)] = $lineContent1;
1977
                    } elseif (!isset($lines1[$lineNum]) && isset($lines2[$lineNum])) {
1978
                        // Nouvelle ligne added dans la destination
1979
                        $diff["Ajoutée à la ligne " . ($lineNum + 1)] = $lineContent2;
1980
                    } else {
1981
                        // Différence found it
1982
                        $diff["Modifiée à la ligne " . ($lineNum + 1)] = $lineContent2;
1983
                    }
1984
                }
1985
            }
1986
        }
1987
1988
1989
        if (empty($linesShouldChange)) {
1990
            $linesShouldChange[0] = '<meta name="dolibarr:pageid" content="' . $exceptNumPge[0] . '" />';
1991
            $linesShouldChange[1] = '$tmp = ob_get_contents(); ob_end_clean(); dolWebsiteOutput($tmp, "html", ' . $exceptNumPge[0] . ');';
1992
        }
1993
1994
        $replacementMapping = array();
1995
        if (!empty($linesShouldNotChange)) {
1996
            $i = 0;
1997
            foreach ($linesShouldNotChange as $numLigne => $ligneRemplacement) {
1998
                if (isset($linesShouldChange[$numLigne])) {
1999
                    $replacementMapping[$ligneRemplacement] = $linesShouldChange[$numLigne];
2000
                } else {
2001
                    $replacementMapping[$ligneRemplacement] = $linesShouldChange[$i];
2002
                }
2003
                $i++;
2004
            }
2005
            $diff['lignes_dont_change'] = $replacementMapping;
2006
        }
2007
        // search path of image and replace it with the correct path
2008
        $pattern = '/medias\/image\/' . preg_quote($this->ref, '/') . '\/([^\'"\s]+)/';
2009
2010
        foreach ($diff as $key => $value) {
2011
            // Ensure the value is a string
2012
            if (is_string($value)) {
2013
                if (preg_match($pattern, $value)) {
2014
                    $newValue = preg_replace($pattern, 'medias/image/' . $this->name_template . '/$1', $value);
2015
                    $diff[$key] = $newValue;
2016
                }
2017
            }
2018
        }
2019
        return $diff;
2020
    }
2021
2022
    /**
2023
     * Replace line by line in file using numbers of the lines
2024
     *
2025
     * @param   string      $inplaceFile    path of file to modify in place
2026
     * @param   array       $differences    array of differences between files
2027
     * @return  int                         Return 0 if we can replace, <0 if not (-2=not writable)
2028
     */
2029
    protected function replaceLineUsingNum($inplaceFile, $differences)
2030
    {
2031
        if (file_exists($inplaceFile)) {
2032
            dolChmod($inplaceFile, '0664');
2033
        }
2034
        if (!is_writable($inplaceFile)) {
2035
            return -2;
2036
        }
2037
2038
        unset($differences['file_destination']);
2039
        $contentDest = file($inplaceFile, FILE_IGNORE_NEW_LINES);
2040
        foreach ($differences as $key => $ligneSource) {
2041
            $matches = array();
2042
            if (preg_match('/(Ajoutée|Modifiée) à la ligne (\d+)/', $key, $matches)) {
2043
                $typeModification = $matches[1];
2044
                $numLigne = (int) $matches[2] - 1;
2045
2046
                if ($typeModification === 'Ajoutée') {
2047
                    array_splice($contentDest, $numLigne, 0, $ligneSource);
2048
                } elseif ($typeModification === 'Modifiée') {
2049
                    $contentDest[$numLigne] = $ligneSource;
2050
                }
2051
            } elseif (preg_match('/Supprimée à la ligne (\d+)/', $key, $matches)) {
2052
                $numLigne = (int) $matches[1] - 1;
2053
                unset($contentDest[$numLigne]);
2054
            }
2055
        }
2056
        // Reindex the table keys
2057
        $contentDest = array_values($contentDest);
2058
        $stringreplacement = implode("\n", $contentDest);
2059
        file_put_contents($inplaceFile, $stringreplacement);
2060
        foreach ($differences['lignes_dont_change'] as $linechanged => $line) {
2061
            if (in_array($linechanged, $contentDest)) {
2062
                dolReplaceInFile($inplaceFile, array($linechanged => $line));
2063
            }
2064
        }
2065
2066
        return 0;
2067
    }
2068
}
2069