1<?php 2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) ---Put here your own copyright and developer email--- 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19/** 20 * \file htdocs/knowledgemanagement/class/knowledgerecord.class.php 21 * \ingroup knowledgemanagement 22 * \brief This file is a CRUD class file for KnowledgeRecord (Create/Read/Update/Delete) 23 */ 24 25// Put here all includes required by your class file 26require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; 27//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; 28//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; 29 30/** 31 * Class for KnowledgeRecord 32 */ 33class KnowledgeRecord extends CommonObject 34{ 35 /** 36 * @var string ID of module. 37 */ 38 public $module = 'knowledgemanagement'; 39 40 /** 41 * @var string ID to identify managed object. 42 */ 43 public $element = 'knowledgerecord'; 44 45 /** 46 * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management. 47 */ 48 public $table_element = 'knowledgemanagement_knowledgerecord'; 49 50 /** 51 * @var int Does this object support multicompany module ? 52 * 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table 53 */ 54 public $ismultientitymanaged = 0; 55 56 /** 57 * @var int Does object support extrafields ? 0=No, 1=Yes 58 */ 59 public $isextrafieldmanaged = 1; 60 61 /** 62 * @var string String with name of icon for knowledgerecord. Must be the part after the 'object_' into object_knowledgerecord.png 63 */ 64 public $picto = 'knowledgemanagement'; 65 66 67 const STATUS_DRAFT = 0; 68 const STATUS_VALIDATED = 1; 69 const STATUS_CANCELED = 9; 70 71 72 /** 73 * 'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password') 74 * Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)" 75 * 'label' the translation key. 76 * 'picto' is code of a picto to show before value in forms 77 * 'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM) 78 * 'position' is the sort order of field. 79 * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). 80 * '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) 81 * 'noteditable' says if field is not editable (1 or 0) 82 * '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. 83 * 'index' if we want an index in database. 84 * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). 85 * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. 86 * '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). 87 * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'maxwidth200', 'wordbreak', 'tdoverflowmax200' 88 * 'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. 89 * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record 90 * '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. 91 * 'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") 92 * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. 93 * 'comment' is not used. You can store here any text of your choice. It is not used by application. 94 * 95 * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. 96 */ 97 98 // BEGIN MODULEBUILDER PROPERTIES 99 /** 100 * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. 101 */ 102 public $fields=array( 103 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id"), 104 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>'1', 'position'=>10, 'notnull'=>1, 'visible'=>5, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object"), 105 'date_creation' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>'1', 'position'=>500, 'notnull'=>1, 'visible'=>-2,), 106 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>'1', 'position'=>501, 'notnull'=>0, 'visible'=>-2,), 107 'last_main_doc' => array('type'=>'varchar(255)', 'label'=>'LastMainDoc', 'enabled'=>'1', 'position'=>600, 'notnull'=>0, 'visible'=>0,), 108 'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserCreation', 'enabled'=>'1', 'position'=>510, 'notnull'=>1, 'visible'=>-2, 'foreignkey'=>'user.rowid',), 109 'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>'1', 'position'=>511, 'notnull'=>-1, 'visible'=>-2,), 110 'fk_user_valid' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>'1', 'position'=>512, 'notnull'=>0, 'visible'=>-2,), 111 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>'1', 'position'=>1000, 'notnull'=>-1, 'visible'=>-2,), 112 'model_pdf' => array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>'1', 'position'=>1010, 'notnull'=>-1, 'visible'=>0,), 113 'question' => array('type'=>'text', 'label'=>'Question', 'enabled'=>'1', 'position'=>30, 'notnull'=>1, 'visible'=>1, 'csslist'=>'tdoverflow300'), 114 'answer' => array('type'=>'html', 'label'=>'Solution', 'enabled'=>'1', 'position'=>50, 'notnull'=>0, 'visible'=>3, 'csslist'=>'tdoverflow300'), 115 //'url' => array('type'=>'varchar(255)', 'label'=>'URL', 'enabled'=>'1', 'position'=>55, 'notnull'=>0, 'visible'=>-1, 'csslist'=>'tdoverflow200', 'help'=>'UrlForInfoPage'), 116 'fk_c_ticket_category' => array('type'=>'integer:CTicketCategory:ticket/class/cticketcategory.class.php', 'label'=>'GroupOfTicket', 'enabled'=>'$conf->ticket->enabled', 'position'=>512, 'notnull'=>0, 'visible'=>-1, 'help'=>'YouCanLinkArticleToATicketCategory'), 117 'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>'1', 'position'=>1000, 'notnull'=>1, 'visible'=>1, 'default'=>0, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Draft', '1'=>'Validated'),), 118 ); 119 public $rowid; 120 public $ref; 121 public $date_creation; 122 public $tms; 123 public $last_main_doc; 124 public $fk_user_creat; 125 public $fk_user_modif; 126 public $fk_user_valid; 127 public $import_key; 128 public $model_pdf; 129 public $question; 130 public $answer; 131 public $url; 132 public $status; 133 // END MODULEBUILDER PROPERTIES 134 135 136 // If this object has a subtable with lines 137 138 // /** 139 // * @var string Name of subtable line 140 // */ 141 // public $table_element_line = 'knowledgemanagement_knowledgerecordline'; 142 143 // /** 144 // * @var string Field with ID of parent key if this object has a parent 145 // */ 146 // public $fk_element = 'fk_knowledgerecord'; 147 148 // /** 149 // * @var string Name of subtable class that manage subtable lines 150 // */ 151 // public $class_element_line = 'KnowledgeRecordline'; 152 153 // /** 154 // * @var array List of child tables. To test if we can delete object. 155 // */ 156 // protected $childtables = array(); 157 158 // /** 159 // * @var array List of child tables. To know object to delete on cascade. 160 // * If name matches '@ClassNAme:FilePathClass;ParentFkFieldName' it will 161 // * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object 162 // */ 163 // protected $childtablesoncascade = array('knowledgemanagement_knowledgerecorddet'); 164 165 // /** 166 // * @var KnowledgeRecordLine[] Array of subtable lines 167 // */ 168 // public $lines = array(); 169 170 171 172 /** 173 * Constructor 174 * 175 * @param DoliDb $db Database handler 176 */ 177 public function __construct(DoliDB $db) 178 { 179 global $conf, $langs; 180 181 $this->db = $db; 182 183 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) { 184 $this->fields['rowid']['visible'] = 0; 185 } 186 if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) { 187 $this->fields['entity']['enabled'] = 0; 188 } 189 190 // Example to show how to set values of fields definition dynamically 191 /*if ($user->rights->knowledgemanagement->knowledgerecord->read) { 192 $this->fields['myfield']['visible'] = 1; 193 $this->fields['myfield']['noteditable'] = 0; 194 }*/ 195 196 // Unset fields that are disabled 197 foreach ($this->fields as $key => $val) { 198 if (isset($val['enabled']) && empty($val['enabled'])) { 199 unset($this->fields[$key]); 200 } 201 } 202 203 // Translate some data of arrayofkeyval 204 if (is_object($langs)) { 205 foreach ($this->fields as $key => $val) { 206 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { 207 foreach ($val['arrayofkeyval'] as $key2 => $val2) { 208 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2); 209 } 210 } 211 } 212 } 213 } 214 215 /** 216 * Create object into database 217 * 218 * @param User $user User that creates 219 * @param bool $notrigger false=launch triggers after, true=disable triggers 220 * @return int <0 if KO, Id of created object if OK 221 */ 222 public function create(User $user, $notrigger = false) 223 { 224 return $this->createCommon($user, $notrigger); 225 } 226 227 /** 228 * Clone an object into another one 229 * 230 * @param User $user User that creates 231 * @param int $fromid Id of object to clone 232 * @return mixed New object created, <0 if KO 233 */ 234 public function createFromClone(User $user, $fromid) 235 { 236 global $langs, $extrafields; 237 $error = 0; 238 239 dol_syslog(__METHOD__, LOG_DEBUG); 240 241 $object = new self($this->db); 242 243 $this->db->begin(); 244 245 // Load source object 246 $result = $object->fetchCommon($fromid); 247 if ($result > 0 && !empty($object->table_element_line)) { 248 $object->fetchLines(); 249 } 250 251 // get lines so they will be clone 252 //foreach($this->lines as $line) 253 // $line->fetch_optionals(); 254 255 // Reset some properties 256 unset($object->id); 257 unset($object->fk_user_creat); 258 unset($object->import_key); 259 260 // Clear fields 261 if (property_exists($object, 'ref')) { 262 $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default']; 263 } 264 if (property_exists($object, 'label')) { 265 $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default']; 266 } 267 if (property_exists($object, 'status')) { 268 $object->status = self::STATUS_DRAFT; 269 } 270 if (property_exists($object, 'date_creation')) { 271 $object->date_creation = dol_now(); 272 } 273 if (property_exists($object, 'date_modification')) { 274 $object->date_modification = null; 275 } 276 // ... 277 // Clear extrafields that are unique 278 if (is_array($object->array_options) && count($object->array_options) > 0) { 279 $extrafields->fetch_name_optionals_label($this->table_element); 280 foreach ($object->array_options as $key => $option) { 281 $shortkey = preg_replace('/options_/', '', $key); 282 if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) { 283 //var_dump($key); var_dump($clonedObj->array_options[$key]); exit; 284 unset($object->array_options[$key]); 285 } 286 } 287 } 288 289 // Create clone 290 $object->context['createfromclone'] = 'createfromclone'; 291 $result = $object->createCommon($user); 292 if ($result < 0) { 293 $error++; 294 $this->error = $object->error; 295 $this->errors = $object->errors; 296 } 297 298 if (!$error) { 299 // copy internal contacts 300 if ($this->copy_linked_contact($object, 'internal') < 0) { 301 $error++; 302 } 303 } 304 305 if (!$error) { 306 // copy external contacts if same company 307 if (property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) { 308 if ($this->copy_linked_contact($object, 'external') < 0) { 309 $error++; 310 } 311 } 312 } 313 314 unset($object->context['createfromclone']); 315 316 // End 317 if (!$error) { 318 $this->db->commit(); 319 return $object; 320 } else { 321 $this->db->rollback(); 322 return -1; 323 } 324 } 325 326 /** 327 * Load object in memory from the database 328 * 329 * @param int $id Id object 330 * @param string $ref Ref 331 * @return int <0 if KO, 0 if not found, >0 if OK 332 */ 333 public function fetch($id, $ref = null) 334 { 335 $result = $this->fetchCommon($id, $ref); 336 if ($result > 0 && !empty($this->table_element_line)) { 337 $this->fetchLines(); 338 } 339 return $result; 340 } 341 342 /** 343 * Load object lines in memory from the database 344 * 345 * @return int <0 if KO, 0 if not found, >0 if OK 346 */ 347 public function fetchLines() 348 { 349 $this->lines = array(); 350 351 $result = $this->fetchLinesCommon(); 352 return $result; 353 } 354 355 356 /** 357 * Load list of objects in memory from the database. 358 * 359 * @param string $sortorder Sort Order 360 * @param string $sortfield Sort field 361 * @param int $limit limit 362 * @param int $offset Offset 363 * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customurl'=>...) 364 * @param string $filtermode Filter mode (AND or OR) 365 * @return array|int int <0 if KO, array of pages if OK 366 */ 367 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND') 368 { 369 global $conf; 370 371 dol_syslog(__METHOD__, LOG_DEBUG); 372 373 $records = array(); 374 375 $sql = 'SELECT '; 376 $sql .= $this->getFieldList('t'); 377 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; 378 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) { 379 $sql .= ' WHERE t.entity IN ('.getEntity($this->table_element).')'; 380 } else { 381 $sql .= ' WHERE 1 = 1'; 382 } 383 // Manage filter 384 $sqlwhere = array(); 385 if (count($filter) > 0) { 386 foreach ($filter as $key => $value) { 387 if ($key == 't.rowid') { 388 $sqlwhere[] = $key.'='.$value; 389 } elseif (in_array($this->fields[$key]['type'], array('date', 'datetime', 'timestamp'))) { 390 $sqlwhere[] = $key.' = \''.$this->db->idate($value).'\''; 391 } elseif ($key == 'customsql') { 392 $sqlwhere[] = $value; 393 } elseif (strpos($value, '%') === false) { 394 $sqlwhere[] = $key.' IN ('.$this->db->sanitize($this->db->escape($value)).')'; 395 } else { 396 $sqlwhere[] = $key.' LIKE \'%'.$this->db->escape($value).'%\''; 397 } 398 } 399 } 400 if (count($sqlwhere) > 0) { 401 $sql .= ' AND ('.implode(' '.$filtermode.' ', $sqlwhere).')'; 402 } 403 404 if (!empty($sortfield)) { 405 $sql .= $this->db->order($sortfield, $sortorder); 406 } 407 if (!empty($limit)) { 408 $sql .= ' '.$this->db->plimit($limit, $offset); 409 } 410 411 $resql = $this->db->query($sql); 412 if ($resql) { 413 $num = $this->db->num_rows($resql); 414 $i = 0; 415 while ($i < ($limit ? min($limit, $num) : $num)) { 416 $obj = $this->db->fetch_object($resql); 417 418 $record = new self($this->db); 419 $record->setVarsFromFetchObj($obj); 420 421 $records[$record->id] = $record; 422 423 $i++; 424 } 425 $this->db->free($resql); 426 427 return $records; 428 } else { 429 $this->errors[] = 'Error '.$this->db->lasterror(); 430 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR); 431 432 return -1; 433 } 434 } 435 436 /** 437 * Update object into database 438 * 439 * @param User $user User that modifies 440 * @param bool $notrigger false=launch triggers after, true=disable triggers 441 * @return int <0 if KO, >0 if OK 442 */ 443 public function update(User $user, $notrigger = false) 444 { 445 return $this->updateCommon($user, $notrigger); 446 } 447 448 /** 449 * Delete object in database 450 * 451 * @param User $user User that deletes 452 * @param bool $notrigger false=launch triggers after, true=disable triggers 453 * @return int <0 if KO, >0 if OK 454 */ 455 public function delete(User $user, $notrigger = false) 456 { 457 return $this->deleteCommon($user, $notrigger); 458 //return $this->deleteCommon($user, $notrigger, 1); 459 } 460 461 /** 462 * Delete a line of object in database 463 * 464 * @param User $user User that delete 465 * @param int $idline Id of line to delete 466 * @param bool $notrigger false=launch triggers after, true=disable triggers 467 * @return int >0 if OK, <0 if KO 468 */ 469 public function deleteLine(User $user, $idline, $notrigger = false) 470 { 471 if ($this->status < 0) { 472 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus'; 473 return -2; 474 } 475 476 return $this->deleteLineCommon($user, $idline, $notrigger); 477 } 478 479 480 /** 481 * Validate object 482 * 483 * @param User $user User making status change 484 * @param int $notrigger 1=Does not execute triggers, 0= execute triggers 485 * @return int <=0 if OK, 0=Nothing done, >0 if KO 486 */ 487 public function validate($user, $notrigger = 0) 488 { 489 global $conf, $langs; 490 491 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 492 493 $error = 0; 494 495 // Protection 496 if ($this->status == self::STATUS_VALIDATED) { 497 dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING); 498 return 0; 499 } 500 501 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->knowledgerecord->write)) 502 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->knowledgerecord->knowledgerecord_advance->validate)))) 503 { 504 $this->error='NotEnoughPermissions'; 505 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR); 506 return -1; 507 }*/ 508 509 $now = dol_now(); 510 511 $this->db->begin(); 512 513 // Define new ref 514 if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life 515 $num = $this->getNextNumRef(); 516 } else { 517 $num = $this->ref; 518 } 519 $this->newref = $num; 520 521 if (!empty($num)) { 522 // Validate 523 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 524 $sql .= " SET ref = '".$this->db->escape($num)."',"; 525 $sql .= " status = ".self::STATUS_VALIDATED; 526 if (!empty($this->fields['date_validation'])) { 527 $sql .= ", date_validation = '".$this->db->idate($now)."'"; 528 } 529 if (!empty($this->fields['fk_user_valid'])) { 530 $sql .= ", fk_user_valid = ".((int) $user->id); 531 } 532 $sql .= " WHERE rowid = ".((int) $this->id); 533 534 dol_syslog(get_class($this)."::validate()", LOG_DEBUG); 535 $resql = $this->db->query($sql); 536 if (!$resql) { 537 dol_print_error($this->db); 538 $this->error = $this->db->lasterror(); 539 $error++; 540 } 541 542 if (!$error && !$notrigger) { 543 // Call trigger 544 $result = $this->call_trigger('KNOWLEDGERECORD_VALIDATE', $user); 545 if ($result < 0) { 546 $error++; 547 } 548 // End call triggers 549 } 550 } 551 552 if (!$error) { 553 $this->oldref = $this->ref; 554 555 // Rename directory if dir was a temporary ref 556 if (preg_match('/^[\(]?PROV/i', $this->ref)) { 557 // Now we rename also files into index 558 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'knowledgerecord/".$this->db->escape($this->newref)."'"; 559 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'knowledgerecord/".$this->db->escape($this->ref)."' and entity = ".$conf->entity; 560 $resql = $this->db->query($sql); 561 if (!$resql) { 562 $error++; $this->error = $this->db->lasterror(); 563 } 564 565 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments 566 $oldref = dol_sanitizeFileName($this->ref); 567 $newref = dol_sanitizeFileName($num); 568 $dirsource = $conf->knowledgemanagement->dir_output.'/knowledgerecord/'.$oldref; 569 $dirdest = $conf->knowledgemanagement->dir_output.'/knowledgerecord/'.$newref; 570 if (!$error && file_exists($dirsource)) { 571 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest); 572 573 if (@rename($dirsource, $dirdest)) { 574 dol_syslog("Rename ok"); 575 // Rename docs starting with $oldref with $newref 576 $listoffiles = dol_dir_list($conf->knowledgemanagement->dir_output.'/knowledgerecord/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/')); 577 foreach ($listoffiles as $fileentry) { 578 $dirsource = $fileentry['name']; 579 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource); 580 $dirsource = $fileentry['path'].'/'.$dirsource; 581 $dirdest = $fileentry['path'].'/'.$dirdest; 582 @rename($dirsource, $dirdest); 583 } 584 } 585 } 586 } 587 } 588 589 // Set new ref and current status 590 if (!$error) { 591 $this->ref = $num; 592 $this->status = self::STATUS_VALIDATED; 593 } 594 595 if (!$error) { 596 $this->db->commit(); 597 return 1; 598 } else { 599 $this->db->rollback(); 600 return -1; 601 } 602 } 603 604 605 /** 606 * Set draft status 607 * 608 * @param User $user Object user that modify 609 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 610 * @return int <0 if KO, >0 if OK 611 */ 612 public function setDraft($user, $notrigger = 0) 613 { 614 // Protection 615 if ($this->status <= self::STATUS_DRAFT) { 616 return 0; 617 } 618 619 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->write)) 620 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->knowledgemanagement_advance->validate)))) 621 { 622 $this->error='Permission denied'; 623 return -1; 624 }*/ 625 626 return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'KNOWLEDGERECORD_UNVALIDATE'); 627 } 628 629 /** 630 * Set cancel status 631 * 632 * @param User $user Object user that modify 633 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 634 * @return int <0 if KO, 0=Nothing done, >0 if OK 635 */ 636 public function cancel($user, $notrigger = 0) 637 { 638 // Protection 639 if ($this->status != self::STATUS_VALIDATED) { 640 return 0; 641 } 642 643 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->write)) 644 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->knowledgemanagement_advance->validate)))) 645 { 646 $this->error='Permission denied'; 647 return -1; 648 }*/ 649 650 return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'KNOWLEDGERECORD_CANCEL'); 651 } 652 653 /** 654 * Set back to validated status 655 * 656 * @param User $user Object user that modify 657 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 658 * @return int <0 if KO, 0=Nothing done, >0 if OK 659 */ 660 public function reopen($user, $notrigger = 0) 661 { 662 // Protection 663 if ($this->status != self::STATUS_CANCELED) { 664 return 0; 665 } 666 667 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->write)) 668 || (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && ! empty($user->rights->knowledgemanagement->knowledgemanagement_advance->validate)))) 669 { 670 $this->error='Permission denied'; 671 return -1; 672 }*/ 673 674 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'KNOWLEDGERECORD_REOPEN'); 675 } 676 677 /** 678 * Return a link to the object card (with optionaly the picto) 679 * 680 * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) 681 * @param string $option On what the link point to ('nolink', ...) 682 * @param int $notooltip 1=Disable tooltip 683 * @param string $morecss Add more css on link 684 * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking 685 * @return string String with URL 686 */ 687 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1) 688 { 689 global $conf, $langs, $hookmanager; 690 691 if (!empty($conf->dol_no_mouse_hover)) { 692 $notooltip = 1; // Force disable tooltips 693 } 694 695 $result = ''; 696 697 $label = img_picto('', $this->picto).' <u>'.$langs->trans("KnowledgeRecord").'</u>'; 698 if (isset($this->status)) { 699 $label .= ' '.$this->getLibStatut(5); 700 } 701 $label .= '<br>'; 702 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref; 703 704 $url = dol_buildpath('/knowledgemanagement/knowledgerecord_card.php', 1).'?id='.$this->id; 705 706 if ($option != 'nolink') { 707 // Add param to save lastsearch_values or not 708 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); 709 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { 710 $add_save_lastsearch_values = 1; 711 } 712 if ($add_save_lastsearch_values) { 713 $url .= '&save_lastsearch_values=1'; 714 } 715 } 716 717 $linkclose = ''; 718 if (empty($notooltip)) { 719 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { 720 $label = $langs->trans("ShowKnowledgeRecord"); 721 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; 722 } 723 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; 724 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"'; 725 } else { 726 $linkclose = ($morecss ? ' class="'.$morecss.'"' : ''); 727 } 728 729 if ($option == 'nolink') { 730 $linkstart = '<span'; 731 } else { 732 $linkstart = '<a href="'.$url.'"'; 733 } 734 $linkstart .= $linkclose.'>'; 735 if ($option == 'nolink') { 736 $linkend = '</span>'; 737 } else { 738 $linkend = '</a>'; 739 } 740 741 $result .= $linkstart; 742 743 if (empty($this->showphoto_on_popup)) { 744 if ($withpicto) { 745 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); 746 } 747 } else { 748 if ($withpicto) { 749 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 750 751 list($class, $module) = explode('@', $this->picto); 752 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref); 753 $filearray = dol_dir_list($upload_dir, "files"); 754 $filename = $filearray[0]['name']; 755 if (!empty($filename)) { 756 $pospoint = strpos($filearray[0]['name'], '.'); 757 758 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint); 759 if (empty($conf->global->{strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS'})) { 760 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$module.'" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div></div>'; 761 } else { 762 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div>'; 763 } 764 765 $result .= '</div>'; 766 } else { 767 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); 768 } 769 } 770 } 771 772 if ($withpicto != 2) { 773 $result .= $this->ref; 774 } 775 776 $result .= $linkend; 777 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); 778 779 global $action, $hookmanager; 780 $hookmanager->initHooks(array('knowledgerecorddao')); 781 $parameters = array('id'=>$this->id, 'getnomurl'=>$result); 782 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 783 if ($reshook > 0) { 784 $result = $hookmanager->resPrint; 785 } else { 786 $result .= $hookmanager->resPrint; 787 } 788 789 return $result; 790 } 791 792 /** 793 * Return the label of the status 794 * 795 * @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 796 * @return string Label of status 797 */ 798 public function getLibStatut($mode = 0) 799 { 800 return $this->LibStatut($this->status, $mode); 801 } 802 803 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 804 /** 805 * Return the status 806 * 807 * @param int $status Id status 808 * @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 809 * @return string Label of status 810 */ 811 public function LibStatut($status, $mode = 0) 812 { 813 // phpcs:enable 814 if (empty($this->labelStatus) || empty($this->labelStatusShort)) { 815 global $langs; 816 //$langs->load("knowledgemanagement"); 817 $this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft'); 818 $this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Validated'); 819 $this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Disabled'); 820 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft'); 821 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Validated'); 822 $this->labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Disabled'); 823 } 824 825 $statusType = 'status'.$status; 826 if ($status == self::STATUS_VALIDATED) $statusType = 'status4'; 827 if ($status == self::STATUS_CANCELED) { 828 $statusType = 'status6'; 829 } 830 831 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); 832 } 833 834 /** 835 * Load the info information in the object 836 * 837 * @param int $id Id of object 838 * @return void 839 */ 840 public function info($id) 841 { 842 $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; 843 $sql .= ' fk_user_creat, fk_user_modif'; 844 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; 845 $sql .= ' WHERE t.rowid = '.((int) $id); 846 $result = $this->db->query($sql); 847 if ($result) { 848 if ($this->db->num_rows($result)) { 849 $obj = $this->db->fetch_object($result); 850 $this->id = $obj->rowid; 851 if ($obj->fk_user_author) { 852 $cuser = new User($this->db); 853 $cuser->fetch($obj->fk_user_author); 854 $this->user_creation = $cuser; 855 } 856 857 if ($obj->fk_user_valid) { 858 $vuser = new User($this->db); 859 $vuser->fetch($obj->fk_user_valid); 860 $this->user_validation = $vuser; 861 } 862 863 if ($obj->fk_user_cloture) { 864 $cluser = new User($this->db); 865 $cluser->fetch($obj->fk_user_cloture); 866 $this->user_cloture = $cluser; 867 } 868 869 $this->date_creation = $this->db->jdate($obj->datec); 870 $this->date_modification = $this->db->jdate($obj->datem); 871 $this->date_validation = $this->db->jdate($obj->datev); 872 } 873 874 $this->db->free($result); 875 } else { 876 dol_print_error($this->db); 877 } 878 } 879 880 /** 881 * Initialise object with example values 882 * Id must be 0 if object instance is a specimen 883 * 884 * @return void 885 */ 886 public function initAsSpecimen() 887 { 888 $this->question = "ABCD"; 889 $this->initAsSpecimenCommon(); 890 } 891 892 /** 893 * Create an array of lines 894 * 895 * @return array|int array of lines if OK, <0 if KO 896 */ 897 public function getLinesArray() 898 { 899 $this->lines = array(); 900 901 $objectline = new KnowledgeRecordLine($this->db); 902 $result = $objectline->fetchAll('ASC', 'position', 0, 0, array('customsql'=>'fk_knowledgerecord = '.((int) $this->id))); 903 904 if (is_numeric($result)) { 905 $this->error = $this->error; 906 $this->errors = $this->errors; 907 return $result; 908 } else { 909 $this->lines = $result; 910 return $this->lines; 911 } 912 } 913 914 /** 915 * Returns the reference to the following non used object depending on the active numbering module. 916 * 917 * @return string Object free reference 918 */ 919 public function getNextNumRef() 920 { 921 global $langs, $conf; 922 $langs->load("knowledgemanagement"); 923 924 if (empty($conf->global->KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON)) { 925 $conf->global->KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON = 'mod_knowledgerecord_standard'; 926 } 927 928 if (!empty($conf->global->KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON)) { 929 $mybool = false; 930 931 $file = $conf->global->KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON.".php"; 932 $classname = $conf->global->KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON; 933 934 // Include file with class 935 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); 936 foreach ($dirmodels as $reldir) { 937 $dir = dol_buildpath($reldir."core/modules/knowledgemanagement/"); 938 939 // Load file with numbering class (if found) 940 $mybool |= @include_once $dir.$file; 941 } 942 943 if ($mybool === false) { 944 dol_print_error('', "Failed to include file ".$file); 945 return ''; 946 } 947 948 if (class_exists($classname)) { 949 $obj = new $classname(); 950 $numref = $obj->getNextValue($this); 951 952 if ($numref != '' && $numref != '-1') { 953 return $numref; 954 } else { 955 $this->error = $obj->error; 956 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); 957 return ""; 958 } 959 } else { 960 print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname; 961 return ""; 962 } 963 } else { 964 print $langs->trans("ErrorNumberingModuleNotSetup", $this->element); 965 return ""; 966 } 967 } 968 969 /** 970 * Create a document onto disk according to template module. 971 * 972 * @param string $modele Force template to use ('' to not force) 973 * @param Translate $outputlangs objet lang a utiliser pour traduction 974 * @param int $hidedetails Hide details of lines 975 * @param int $hidedesc Hide description 976 * @param int $hideref Hide ref 977 * @param null|array $moreparams Array to provide more information 978 * @return int 0 if KO, 1 if OK 979 */ 980 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) 981 { 982 global $conf, $langs; 983 984 $result = 0; 985 $includedocgeneration = 0; 986 987 $langs->load("knowledgemanagement"); 988 989 if (!dol_strlen($modele)) { 990 $modele = 'standard_knowledgerecord'; 991 992 if (!empty($this->model_pdf)) { 993 $modele = $this->model_pdf; 994 } elseif (!empty($conf->global->KNOWLEDGERECORD_ADDON_PDF)) { 995 $modele = $conf->global->KNOWLEDGERECORD_ADDON_PDF; 996 } 997 } 998 999 $modelpath = "core/modules/knowledgemanagement/doc/"; 1000 1001 if ($includedocgeneration && !empty($modele)) { 1002 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); 1003 } 1004 1005 return $result; 1006 } 1007 1008 /** 1009 * Action executed by scheduler 1010 * CAN BE A CRON TASK. In such a case, parameters come from the schedule job setup field 'Parameters' 1011 * Use public function doScheduledJob($param1, $param2, ...) to get parameters 1012 * 1013 * @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) 1014 */ 1015 public function doScheduledJob() 1016 { 1017 global $conf, $langs; 1018 1019 //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log'; 1020 1021 $error = 0; 1022 $this->output = ''; 1023 $this->error = ''; 1024 1025 dol_syslog(__METHOD__, LOG_DEBUG); 1026 1027 $now = dol_now(); 1028 1029 $this->db->begin(); 1030 1031 // ... 1032 1033 $this->db->commit(); 1034 1035 return $error; 1036 } 1037} 1038 1039 1040require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php'; 1041 1042/** 1043 * Class KnowledgeRecordLine. You can also remove this and generate a CRUD class for lines objects. 1044 */ 1045class KnowledgeRecordLine extends CommonObjectLine 1046{ 1047 // To complete with content of an object KnowledgeRecordLine 1048 // We should have a field rowid, fk_knowledgerecord and position 1049 1050 /** 1051 * @var int Does object support extrafields ? 0=No, 1=Yes 1052 */ 1053 public $isextrafieldmanaged = 0; 1054 1055 /** 1056 * Constructor 1057 * 1058 * @param DoliDb $db Database handler 1059 */ 1060 public function __construct(DoliDB $db) 1061 { 1062 $this->db = $db; 1063 } 1064} 1065