1<?php
2/* Copyright (C) 2007-2018  Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2014       Juanjo Menent       <jmenent@2byte.es>
4 * Copyright (C) 2015       Florian Henry       <florian.henry@open-concept.pro>
5 * Copyright (C) 2015       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
6 * Copyright (C) 2020 	   Nicolas ZABOURI		<info@inovea-conseil.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22/**
23 * \file    htdocs/website/class/websitepage.class.php
24 * \ingroup website
25 * \brief   File for the CRUD class of websitepage (Create/Read/Update/Delete)
26 */
27
28// Put here all includes required by your class file
29require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
30//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
31//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
32
33/**
34 * Class Websitepage
35 */
36class WebsitePage extends CommonObject
37{
38	/**
39	 * @var string Id to identify managed objects
40	 */
41	public $element = 'websitepage';
42
43	/**
44	 * @var string Name of table without prefix where object is stored
45	 */
46	public $table_element = 'website_page';
47
48	/**
49	 * @var string String with name of icon for websitepage. Must be the part after the 'object_' into object_myobject.png
50	 */
51	public $picto = 'file-code';
52
53
54	/**
55	 * @var int ID
56	 */
57	public $fk_website;
58
59	public $pageurl;
60	public $aliasalt;
61	public $type_container;
62
63	/**
64	 * @var string title
65	 */
66	public $title;
67	/**
68	 * @var string description
69	 */
70	public $description;
71	/**
72	 * @var string image
73	 */
74	public $image;
75	/**
76	 * @var string keywords
77	 */
78	public $keywords;
79	/**
80	 * @var string language code ('en', 'fr', 'en-gb', ..)
81	 */
82	public $lang;
83
84	public $allowed_in_frames;
85	public $htmlheader;
86	public $content;
87	public $grabbed_from;
88
89	/**
90	 * @var int Status
91	 */
92	public $status;
93
94	/**
95	 * @var integer|string date_creation
96	 */
97	public $date_creation;
98
99	/**
100	 * @var integer|string date_modification
101	 */
102	public $date_modification;
103
104	/**
105	 * @var string author_alias
106	 */
107	public $author_alias;
108
109   	/**
110   	 * @var string path of external object
111   	 */
112	public $object_type;
113
114	/**
115	 * @var string id of external object
116	 */
117	public $fk_object;
118
119	const STATUS_DRAFT = 0;
120	const STATUS_VALIDATED = 1;
121
122
123	/**
124	 *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
125	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
126	 *  'label' the translation key.
127	 *  'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM)
128	 *  'position' is the sort order of field.
129	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
130	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
131	 *  'noteditable' says if field is not editable (1 or 0)
132	 *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
133	 *  'index' if we want an index in database.
134	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
135	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
136	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
137	 *  'css' is the CSS style to use on field. For example: 'maxwidth200'
138	 *  'help' is a string visible as a tooltip on field
139	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
140	 *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
141	 *  'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
142	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
143	 *
144	 *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
145	 */
146
147	// BEGIN MODULEBUILDER PROPERTIES
148	/**
149	 * @var array  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
150	 */
151	public $fields = array(
152		'rowid'          =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'index'=>1, 'position'=>1, 'comment'=>'Id'),
153		'pageurl'        =>array('type'=>'varchar(16)', 'label'=>'WEBSITE_PAGENAME', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'index'=>1, 'position'=>10, 'searchall'=>1, 'comment'=>'Ref/alias of page'),
154		'aliasalt'       =>array('type'=>'varchar(255)', 'label'=>'AliasAlt', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'index'=>0, 'position'=>11, 'searchall'=>0, 'comment'=>'Alias alternative of page'),
155		'type_container' =>array('type'=>'varchar(16)', 'label'=>'Type', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'index'=>0, 'position'=>12, 'comment'=>'Type of container'),
156		'title'          =>array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>1, 'position'=>30, 'searchall'=>1, 'help'=>'UseTextBetween5And70Chars'),
157		'description'    =>array('type'=>'varchar(255)', 'label'=>'Description', 'enabled'=>1, 'visible'=>1, 'position'=>30, 'searchall'=>1),
158		'image'          =>array('type'=>'varchar(255)', 'label'=>'Image', 'enabled'=>1, 'visible'=>1, 'position'=>32, 'searchall'=>0, 'help'=>'Relative path of media. Used if Type is "blogpost"'),
159		'keywords'       =>array('type'=>'varchar(255)', 'label'=>'Keywords', 'enabled'=>1, 'visible'=>1, 'position'=>45, 'searchall'=>0),
160		'lang'           =>array('type'=>'varchar(6)', 'label'=>'Lang', 'enabled'=>1, 'notnull'=>-1, 'visible'=>1, 'position'=>45, 'searchall'=>0),
161		//'status'        =>array('type'=>'integer',      'label'=>'Status',           'enabled'=>1, 'visible'=>1,  'index'=>true,   'position'=>1000),
162		'fk_website'     =>array('type'=>'integer', 'label'=>'WebsiteId', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>40, 'searchall'=>0, 'foreignkey'=>'websitepage.rowid'),
163		'fk_page'        =>array('type'=>'integer', 'label'=>'ParentPageId', 'enabled'=>1, 'visible'=>1, 'notnull'=>-1, 'position'=>45, 'searchall'=>0, 'foreignkey'=>'website.rowid'),
164		'allowed_in_frames'   =>array('type'=>'integer', 'label'=>'AllowedInFrames', 'enabled'=>1, 'visible'=>-1, 'position'=>48, 'searchall'=>0, 'default'=>0),
165		'htmlheader'     =>array('type'=>'text', 'label'=>'HtmlHeader', 'enabled'=>1, 'visible'=>0, 'position'=>50, 'searchall'=>0),
166		'content'        =>array('type'=>'mediumtext', 'label'=>'Content', 'enabled'=>1, 'visible'=>0, 'position'=>51, 'searchall'=>0),
167		'grabbed_from'   =>array('type'=>'varchar(255)', 'label'=>'GrabbedFrom', 'enabled'=>1, 'visible'=>1, 'index'=>1, 'position'=>400, 'comment'=>'URL page content was grabbed from'),
168		'date_creation'  =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
169		'tms'            =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>501),
170		//'date_valid'    =>array('type'=>'datetime',     'label'=>'DateValidation',     'enabled'=>1, 'visible'=>-1, 'position'=>502),
171		'fk_user_creat'  =>array('type'=>'integer', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'notnull'=>true, 'position'=>510),
172		'author_alias'   =>array('type'=>'varchar(64)', 'label'=>'AuthorAlias', 'enabled'=>1, 'visible'=>-1, 'index'=>0, 'position'=>511, 'comment'=>'Author alias'),
173		'fk_user_modif'  =>array('type'=>'integer', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-1, 'position'=>512),
174		//'fk_user_valid' =>array('type'=>'integer',      'label'=>'UserValidation',        'enabled'=>1, 'visible'=>-1, 'position'=>512),
175		'import_key'     =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'index'=>1, 'position'=>1000, 'notnull'=>-1),
176		'object_type' => array('type' => 'varchar(255)', 'label' => 'ObjectType', 'enabled'=>1, 'visible'=>0, 'position'=>46, 'searchall'=>0, 'help'=>''),
177		'fk_object' => array('type' => 'varchar(255)', 'label' => 'ObjectId', 'enabled'=>1, 'visible'=>0, 'position'=>47, 'searchall'=>0, 'help'=>'')
178	);
179	// END MODULEBUILDER PROPERTIES
180
181
182	// If this object has a subtable with lines
183
184	// /**
185	//  * @var string    Name of subtable line
186	//  */
187	//public $table_element_line = 'mymodule_myobjectline';
188
189	/**
190	 * @var string 	Field with ID of parent key if this field has a parent or for child tables
191	 */
192	public $fk_element = 'fk_website_page';
193
194	// /**
195	//  * @var string    Name of subtable class that manage subtable lines
196	//  */
197	//public $class_element_line = 'MyObjectline';
198
199	/**
200	 * @var array	List of child tables. To test if we can delete object.
201	 */
202	//protected $childtables=array();
203
204	/**
205	 * @var array	List of child tables. To know object to delete on cascade.
206	 */
207	protected $childtablesoncascade = array('categorie_website_page');
208
209
210
211	/**
212	 * Constructor
213	 *
214	 * @param DoliDb $db Database handler
215	 */
216	public function __construct(DoliDB $db)
217	{
218		$this->db = $db;
219	}
220
221	/**
222	 * Create object into database
223	 *
224	 * @param  User $user      User that creates
225	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
226	 * @return int             <0 if KO, Id of created object if OK
227	 */
228	public function create(User $user, $notrigger = false)
229	{
230		$this->description = dol_trunc($this->description, 255, 'right', 'utf-8', 1);
231		$this->keywords = dol_trunc($this->keywords, 255, 'right', 'utf-8', 1);
232		if ($this->aliasalt) $this->aliasalt = ','.preg_replace('/,+$/', '', preg_replace('/^,+/', '', $this->aliasalt)).','; // content in database must be ',xxx,...,yyy,'
233
234		// Remove spaces and be sure we have main language only
235		$this->lang = preg_replace('/[_-].*$/', '', trim($this->lang)); // en_US or en-US -> en
236
237		return $this->createCommon($user, $notrigger);
238	}
239
240	/**
241	 * Load object in memory from the database
242	 *
243	 * @param int       $id             Id object.
244	 *                                  - If this is 0, the value into $page will be used. If not found or $page not defined, the default page of website_id will be used or the first page found if not set.
245	 *                                  - If value is < 0, we must exclude this ID.
246	 * @param string    $website_id     Web site id (page name must also be filled if this parameter is used)
247	 * @param string    $page           Page name (website id must also be filled if this parameter is used). Exemple 'myaliaspage' or 'fr/myaliaspage'
248	 * @param string    $aliasalt       Alternative alias to search page (slow)
249	 *
250	 * @return int <0 if KO, 0 if not found, >0 if OK
251	 */
252	public function fetch($id, $website_id = null, $page = null, $aliasalt = null)
253	{
254		dol_syslog(__METHOD__, LOG_DEBUG);
255
256		$sql = 'SELECT';
257		$sql .= ' t.rowid,';
258		$sql .= " t.fk_website,";
259		$sql .= ' t.type_container,';
260		$sql .= " t.pageurl,";
261		$sql .= " t.aliasalt,";
262		$sql .= " t.title,";
263		$sql .= " t.description,";
264		$sql .= " t.image,";
265		$sql .= " t.keywords,";
266		$sql .= " t.htmlheader,";
267		$sql .= " t.content,";
268		$sql .= " t.lang,";
269		$sql .= " t.fk_page,";
270		$sql .= " t.allowed_in_frames,";
271		$sql .= " t.status,";
272		$sql .= " t.grabbed_from,";
273		$sql .= " t.date_creation,";
274		$sql .= " t.tms as date_modification,";
275		$sql .= " t.fk_user_creat,";
276		$sql .= " t.author_alias,";
277		$sql .= " t.fk_user_modif,";
278		$sql .= " t.import_key,";
279		$sql .= " t.object_type,";
280		$sql .= " t.fk_object";
281		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
282		//$sql .= ' WHERE entity IN ('.getEntity('website').')';       // entity is on website level
283		$sql .= ' WHERE 1 = 1';
284		if ($id > 0)
285		{
286			$sql .= ' AND t.rowid = '.$id;
287		}
288		else {
289			if ($id < 0) $sql .= ' AND t.rowid <> '.abs($id);
290			if (null !== $website_id) {
291				$sql .= " AND t.fk_website = '".$this->db->escape($website_id)."'";
292				if ($page) {
293					$pagetouse = $page;
294					$langtouse = '';
295					$tmppage = explode('/', $page);
296					if (!empty($tmppage[1])) {
297						$pagetouse = $tmppage[1];
298						if (strlen($tmppage[0])) $langtouse = $tmppage[0];
299					}
300					$sql .= " AND t.pageurl = '".$this->db->escape($pagetouse)."'";
301					if ($langtouse) $sql .= " AND t.lang = '".$this->db->escape($langtouse)."'";
302				}
303				if ($aliasalt)	$sql .= " AND (t.aliasalt LIKE '%,".$this->db->escape($aliasalt).",%' OR t.aliasalt LIKE '%, ".$this->db->escape($aliasalt).",%')";
304			}
305		}
306		$sql .= $this->db->plimit(1);
307
308		$resql = $this->db->query($sql);
309		if ($resql) {
310			$numrows = $this->db->num_rows($resql);
311			if ($numrows) {
312				$obj = $this->db->fetch_object($resql);
313
314				$this->id = $obj->rowid;
315
316				$this->fk_website = $obj->fk_website;
317				$this->type_container = $obj->type_container;
318
319				$this->pageurl = $obj->pageurl;
320				$this->ref = $obj->pageurl;
321				$this->aliasalt = preg_replace('/,+$/', '', preg_replace('/^,+/', '', $obj->aliasalt));
322
323				$this->title = $obj->title;
324				$this->description = $obj->description;
325				$this->image = $obj->image;
326				$this->keywords = $obj->keywords;
327				$this->htmlheader = $obj->htmlheader;
328				$this->content = $obj->content;
329				$this->lang = $obj->lang;
330				$this->fk_page = $obj->fk_page;
331				$this->allowed_in_frames = $obj->allowed_in_frames;
332				$this->status = $obj->status;
333				$this->grabbed_from = $obj->grabbed_from;
334				$this->date_creation = $this->db->jdate($obj->date_creation);
335				$this->date_modification = $this->db->jdate($obj->date_modification);
336				$this->fk_user_creat = $obj->fk_user_creat;
337				$this->author_alias = $obj->author_alias;
338				$this->fk_user_modif = $obj->fk_user_modif;
339				$this->import_key = $obj->import_key;
340				$this->object_type = $obj->object_type;
341				$this->fk_object = $obj->fk_object;
342			}
343			$this->db->free($resql);
344
345			if ($numrows) {
346				return 1;
347			} else {
348				return 0;
349			}
350		} else {
351			$this->errors[] = 'Error '.$this->db->lasterror();
352			dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
353
354			return -1;
355		}
356	}
357
358	/**
359	 * Return array of all web site pages.
360	 *
361	 * @param  string      $websiteid    Web site
362	 * @param  string      $sortorder    Sort Order
363	 * @param  string      $sortfield    Sort field
364	 * @param  int         $limit        limit
365	 * @param  int         $offset       Offset
366	 * @param  array       $filter       Filter array
367	 * @param  string      $filtermode   Filter mode (AND or OR)
368	 * @return array|int                 int <0 if KO, array of pages if OK
369	 */
370	public function fetchAll($websiteid, $sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND')
371	{
372		dol_syslog(__METHOD__, LOG_DEBUG);
373
374		$records = array();
375
376		$sql = 'SELECT';
377		$sql .= ' t.rowid,';
378		$sql .= " t.fk_website,";
379		$sql .= " t.type_container,";
380		$sql .= " t.pageurl,";
381		$sql .= " t.aliasalt,";
382		$sql .= " t.title,";
383		$sql .= " t.description,";
384		$sql .= " t.image,";
385		$sql .= " t.keywords,";
386		$sql .= " t.htmlheader,";
387		$sql .= " t.content,";
388		$sql .= " t.lang,";
389		$sql .= " t.fk_page,";
390		$sql .= " t.allowed_in_frames,";
391		$sql .= " t.status,";
392		$sql .= " t.grabbed_from,";
393		$sql .= " t.date_creation,";
394		$sql .= " t.tms as date_modification,";
395		$sql .= " t.fk_user_creat,";
396		$sql .= " t.author_alias,";
397		$sql .= " t.fk_user_modif,";
398		$sql .= " t.import_key,";
399		$sql .= " t.object_type,";
400		$sql .= " t.fk_object";
401		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
402		$sql .= ' WHERE t.fk_website = '.$websiteid;
403		// Manage filter (same than into countAll)
404		$sqlwhere = array();
405		if (count($filter) > 0) {
406			foreach ($filter as $key => $value) {
407				if ($key == 't.rowid' || $key == 't.fk_website' || $key == 'status') {
408					$sqlwhere[] = $key.' = '.$value;
409				} elseif ($key == 'type_container') {
410					$sqlwhere[] = $key." = '".$this->db->escape($value)."'";
411				} elseif ($key == 'lang' || $key == 't.lang') {
412					$listoflang = array();
413					$foundnull = 0;
414					foreach (explode(',', $value) as $tmpvalue) {
415						if ($tmpvalue == 'null') {
416							$foundnull++;
417							continue;
418						}
419						$listoflang[] = "'".$this->db->escape(substr(str_replace("'", '', $tmpvalue), 0, 2))."'";
420					}
421					$stringtouse = $key." IN (".join(',', $listoflang).")";
422					if ($foundnull) $stringtouse = '('.$stringtouse.' OR '.$key.' IS NULL)';
423					$sqlwhere[] = $stringtouse;
424				} else {
425					$sqlwhere[] = $key.' LIKE \'%'.$this->db->escape($value).'%\'';
426				}
427			}
428		}
429		if (count($sqlwhere) > 0) {
430			$sql .= ' AND ('.implode(' '.$filtermode.' ', $sqlwhere).')';
431		}
432
433		if (!empty($sortfield)) {
434			$sql .= $this->db->order($sortfield, $sortorder);
435		}
436		if (!empty($limit)) {
437			$sql .= ' '.$this->db->plimit($limit, $offset);
438		}
439
440		$resql = $this->db->query($sql);
441		if ($resql) {
442			$num = $this->db->num_rows($resql);
443
444			while ($obj = $this->db->fetch_object($resql))
445			{
446				$record = new self($this->db);
447
448				$record->id = $obj->rowid;
449				$record->fk_website = $obj->fk_website;
450				$record->type_container = $obj->type_container;
451				$record->pageurl = $obj->pageurl;
452				$record->aliasalt = preg_replace('/,+$/', '', preg_replace('/^,+/', '', $obj->aliasalt));
453				$record->title = $obj->title;
454				$record->description = $obj->description;
455				$record->image = $obj->image;
456				$record->keywords = $obj->keywords;
457				$record->htmlheader = $obj->htmlheader;
458				$record->content = $obj->content;
459				$record->lang = $obj->lang;
460				$record->fk_page = $obj->fk_page;
461				$record->allowed_in_frames = $obj->allowed_in_frames;
462				$record->status = $obj->status;
463				$record->grabbed_from = $obj->grabbed_from;
464				$record->date_creation = $this->db->jdate($obj->date_creation);
465				$record->date_modification = $this->db->jdate($obj->date_modification);
466				$record->fk_user_creat = $obj->fk_user_creat;
467				$record->author_alias = $obj->author_alias;
468				$record->fk_user_modif = $obj->fk_user_modif;
469				$record->import_key = $obj->import_key;
470				$record->object_type = $obj->object_type;
471				$record->fk_object = $obj->fk_object;
472				//var_dump($record->id);
473				$records[$record->id] = $record;
474			}
475			$this->db->free($resql);
476
477			return $records;
478		} else {
479			$this->error = 'Error '.$this->db->lasterror();
480			$this->errors[] = $this->error;
481			dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
482
483			return -1;
484		}
485	}
486
487	/**
488	 * Count objects in the database.
489	 *
490	 * @param  string      $websiteid    Web site
491	 * @param  array       $filter       Filter array
492	 * @param  string      $filtermode   Filter mode (AND or OR)
493	 * @return int         		         int <0 if KO, array of pages if OK
494	 */
495	public function countAll($websiteid, array $filter = array(), $filtermode = 'AND')
496	{
497		dol_syslog(__METHOD__, LOG_DEBUG);
498
499		$result = 0;
500
501		$sql = 'SELECT COUNT(t.rowid) as nb';
502		$sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
503		$sql .= ' WHERE t.fk_website = '.$websiteid;
504		// Manage filter (same than into fetchAll)
505		$sqlwhere = array();
506		if (count($filter) > 0) {
507			foreach ($filter as $key => $value) {
508				if ($key == 't.rowid' || $key == 't.fk_website' || $key == 'status') {
509					$sqlwhere[] = $key.' = '.$value;
510				} elseif ($key == 'type_container') {
511					$sqlwhere[] = $key." = '".$this->db->escape($value)."'";
512				} elseif ($key == 'lang' || $key == 't.lang') {
513					$listoflang = array();
514					$foundnull = 0;
515					foreach (explode(',', $value) as $tmpvalue) {
516						if ($tmpvalue == 'null') {
517							$foundnull++;
518							continue;
519						}
520						$listoflang[] = "'".$this->db->escape(substr(str_replace("'", '', $tmpvalue), 0, 2))."'";
521					}
522					$stringtouse = $key." IN (".join(',', $listoflang).")";
523					if ($foundnull) $stringtouse = '('.$stringtouse.' OR '.$key.' IS NULL)';
524					$sqlwhere[] = $stringtouse;
525				} else {
526					$sqlwhere[] = $key.' LIKE \'%'.$this->db->escape($value).'%\'';
527				}
528			}
529		}
530		if (count($sqlwhere) > 0) {
531			$sql .= ' AND ('.implode(' '.$filtermode.' ', $sqlwhere).')';
532		}
533
534		$resql = $this->db->query($sql);
535		if ($resql) {
536			$obj = $this->db->fetch_object($resql);
537			if ($obj) {
538				$result = $obj->nb;
539			}
540
541			$this->db->free($resql);
542
543			return $result;
544		} else {
545			$this->error = 'Error '.$this->db->lasterror();
546			$this->errors[] = $this->error;
547			dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
548
549			return -1;
550		}
551	}
552
553	/**
554	 * Update object into database
555	 *
556	 * @param  User $user      User that modifies
557	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
558	 * @return int             <0 if KO, >0 if OK
559	 */
560	public function update(User $user, $notrigger = false)
561	{
562		$this->description = dol_trunc($this->description, 255, 'right', 'utf-8', 1);
563		$this->keywords = dol_trunc($this->keywords, 255, 'right', 'utf-8', 1);
564		if ($this->aliasalt) $this->aliasalt = ','.preg_replace('/,+$/', '', preg_replace('/^,+/', '', $this->aliasalt)).','; // content in database must be ',xxx,...,yyy,'
565
566		// Remove spaces and be sure we have main language only
567		$this->lang = preg_replace('/[_-].*$/', '', trim($this->lang)); // en_US or en-US -> en
568
569		if ($this->fk_page > 0) {
570			if (empty($this->lang)) {
571				$this->error = "ErrorLanguageMandatoryIfPageSetAsTranslationOfAnother";
572				return -1;
573			}
574			$tmppage = new WebsitePage($this->db);
575			$tmppage->fetch($this->fk_page);
576			if ($tmppage->lang == $this->lang) {
577				$this->error = "ErrorLanguageOfTranslatedPageIsSameThanThisPage";
578				return -1;
579			}
580		}
581
582		return $this->updateCommon($user, $notrigger);
583	}
584
585	/**
586	 * Delete object in database
587	 *
588	 * @param User $user       User that deletes
589	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
590	 * @return int             <0 if KO, >0 if OK
591	 */
592	public function delete(User $user, $notrigger = false)
593	{
594		$error = 0;
595
596		// Delete all child tables
597		if (!$error) {
598			foreach ($this->childtablesoncascade as $table)
599			{
600				$sql = "DELETE FROM ".MAIN_DB_PREFIX.$table;
601				$sql .= " WHERE fk_website_page = ".(int) $this->id;
602
603				$result = $this->db->query($sql);
604				if (!$result) {
605					$error++;
606					$this->errors[] = $this->db->lasterror();
607					break;
608				}
609			}
610		}
611
612		if (!$error) {
613			$result = $this->deleteCommon($user, $trigger);
614			if ($result <= 0)
615			{
616				$error++;
617			}
618		}
619
620		if (!$error)
621		{
622			$websiteobj = new Website($this->db);
623			$result = $websiteobj->fetch($this->fk_website);
624
625			if ($result > 0)
626			{
627				global $dolibarr_main_data_root;
628				$pathofwebsite = $dolibarr_main_data_root.'/website/'.$websiteobj->ref;
629
630				$filealias = $pathofwebsite.'/'.$this->pageurl.'.php';
631				$filetpl = $pathofwebsite.'/page'.$this->id.'.tpl.php';
632
633				dol_delete_file($filealias);
634				dol_delete_file($filetpl);
635			} else {
636				$this->error = $websiteobj->error;
637				$this->errors = $websiteobj->errors;
638			}
639		}
640
641		if (!$error) {
642			return 1;
643		} else {
644			return -1;
645		}
646	}
647
648	/**
649	 * Load an object from its id and create a new one in database
650	 *
651	 * @param	User	$user				User making the clone
652	 * @param 	int 	$fromid 			Id of object to clone
653	 * @param	string	$newref				New ref/alias of page
654	 * @param	string	$newlang			New language
655	 * @param	int		$istranslation		1=New page is a translation of the cloned page.
656	 * @param	int		$newwebsite			0=Same web site, >0=Id of new website
657	 * @param	string	$newtitle			New title
658	 * @return 	mixed 						New object created, <0 if KO
659	 */
660	public function createFromClone(User $user, $fromid, $newref, $newlang = '', $istranslation = 0, $newwebsite = 0, $newtitle = '')
661	{
662		global $hookmanager, $langs;
663
664		$now = dol_now();
665		$error = 0;
666
667		dol_syslog(__METHOD__, LOG_DEBUG);
668
669		$object = new self($this->db);
670
671		// Clean parameters
672		if (empty($newref) && !empty($newtitle)) {
673			$newref = strtolower(dol_sanitizeFileName(preg_replace('/\s+/', '-', $newtitle), '-', 1));
674		}
675
676		// Check parameters
677		if (empty($newref)) {
678			$langs->load("errors");
679			$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("WEBSITE_TITLE"));
680			return -1;
681		}
682
683		$this->db->begin();
684
685		// Load source object
686		$object->fetch($fromid);
687		// Reset object
688		$object->id = 0;
689
690		// Clear fields
691		$object->ref = $newref;
692		$object->pageurl = $newref;
693		$object->aliasalt = '';
694		$object->fk_user_creat = $user->id;
695		$object->author_alias = '';
696		$object->date_creation = $now;
697		$object->title = ($newtitle == '1' ? $object->title : ($newtitle ? $newtitle : $object->title));
698		$object->description = $object->title;
699		if (!empty($newlang)) $object->lang = $newlang;
700		if ($istranslation) $object->fk_page = $fromid;
701		else $object->fk_page = 0;
702		if (!empty($newwebsite)) $object->fk_website = $newwebsite;
703		$object->import_key = '';
704
705		// Create clone
706		$object->context['createfromclone'] = 'createfromclone';
707		$result = $object->create($user);
708		if ($result < 0) {
709			$error++;
710			$this->error = $object->error;
711			$this->errors = $object->errors;
712			dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR);
713		}
714
715		unset($object->context['createfromclone']);
716
717		// End
718		if (!$error) {
719			$this->db->commit();
720
721			return $object;
722		} else {
723			$this->db->rollback();
724
725			return -1;
726		}
727	}
728
729	/**
730	 *  Return a link to the user card (with optionaly the picto)
731	 * 	Use this->id,this->lastname, this->firstname
732	 *
733	 *	@param	int		$withpicto			Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
734	 *	@param	string	$option				On what the link point to
735	 *  @param	integer	$notooltip			1=Disable tooltip
736	 *  @param	int		$maxlen				Max length of visible user name
737	 *  @param  string  $morecss            Add more css on link
738	 *	@return	string						String with URL
739	 */
740	public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $maxlen = 24, $morecss = '')
741	{
742		global $langs, $conf, $db;
743		global $dolibarr_main_authentication, $dolibarr_main_demo;
744		global $menumanager;
745
746		$result = '';
747
748		$label = '<u>'.$langs->trans("Page").'</u>';
749		$label .= '<br>';
750		$label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref.'<br>';
751		$label .= '<b>'.$langs->trans('ID').':</b> '.$this->id.'<br>';
752		$label .= '<b>'.$langs->trans('Title').':</b> '.$this->title.'<br>';
753		$label .= '<b>'.$langs->trans('Language').':</b> '.$this->lang;
754
755		$url = DOL_URL_ROOT.'/website/index.php?websiteid='.$this->fk_website.'&pageid='.$this->id;
756
757		$linkclose = '';
758		if (empty($notooltip))
759		{
760			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
761			{
762				$label = $langs->trans("ShowMyObject");
763				$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
764			}
765			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
766			$linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
767		}
768		else $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
769
770		$linkstart = '<a href="'.$url.'"';
771		$linkstart .= $linkclose.'>';
772		$linkend = '</a>';
773
774		//$linkstart = $linkend = '';
775
776		$result .= $linkstart;
777		if ($withpicto) $result .= img_picto(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
778		if ($withpicto != 2) $result .= $this->ref;
779		$result .= $linkend;
780
781		return $result;
782	}
783
784	/**
785	 *  Retourne le libelle du status d'un user (actif, inactif)
786	 *
787	 *  @param	int		$mode          0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
788	 *  @return	string 			       Label of status
789	 */
790	public function getLibStatut($mode = 0)
791	{
792		return $this->LibStatut($this->status, $mode);
793	}
794
795	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
796	/**
797	 *  Renvoi le libelle d'un status donne
798	 *
799	 *  @param	int		$status        	Id status
800	 *  @param  int		$mode          	0=libelle long, 1=libelle court, 2=Picto + Libelle court, 3=Picto, 4=Picto + Libelle long, 5=Libelle court + Picto
801	 *  @return string 			       	Label of status
802	 */
803	public function LibStatut($status, $mode = 0)
804	{
805		// phpcs:enable
806		global $langs;
807
808		if (empty($this->labelStatus) || empty($this->labelStatusShort))
809		{
810			global $langs;
811			//$langs->load("mymodule");
812			$this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Disabled');
813			$this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Enabled');
814			$this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Disabled');
815			$this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Enabled');
816		}
817
818		$statusType = 'status5';
819		if ($status == self::STATUS_VALIDATED) $statusType = 'status4';
820
821		return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
822	}
823
824	/**
825	 * Sets object to given categories.
826	 *
827	 * Deletes object from existing categories not supplied.
828	 * Adds it to non existing supplied categories.
829	 * Existing categories are left untouch.
830	 *
831	 * @param 	int[]|int 	$categories 	Category ID or array of Categories IDs
832	 * @return	int							<0 if KO, >0 if OK
833	 */
834	public function setCategories($categories)
835	{
836		require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
837		return $this->setCategoriesCommon($categories, Categorie::TYPE_WEBSITE_PAGE);
838	}
839
840	/**
841	 * Initialise object with example values
842	 * Id must be 0 if object instance is a specimen
843	 *
844	 * @return void
845	 */
846	public function initAsSpecimen()
847	{
848		global $user;
849
850		$this->id = 0;
851
852		$now = dol_now();
853
854		$this->fk_website = '';
855		$this->type_container = 'page';
856		$this->pageurl = 'specimen';
857		$this->aliasalt = 'specimenalt';
858		$this->title = 'My Page';
859		$this->description = 'This is my page';
860		$this->image = '';
861		$this->keywords = 'keyword1, keyword2';
862		$this->allowed_in_frames = 1;
863		$this->htmlheader = '';
864		$this->content = '<html><body>This is a html content</body></html>';
865		$this->status = '';
866		$this->grabbed_from = '';
867		$this->date_creation = $now - (24 * 30 * 3600);
868		$this->date_modification = $now - (24 * 7 * 3600);
869		$this->fk_user_creat = $user->id;
870		$this->author_alias = 'mypublicpseudo';
871	}
872}
873