dolibarr 23.0.3
knowledgerecord.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
26// Put here all includes required by your class file
27require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
28//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
29//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
30
35{
39 public $module = 'knowledgemanagement';
40
44 public $element = 'knowledgerecord';
45
49 public $table_element = 'knowledgemanagement_knowledgerecord';
50
54 public $picto = 'knowledgemanagement';
55
56
57 const STATUS_DRAFT = 0;
58 const STATUS_VALIDATED = 1;
59 const STATUS_CANCELED = 9;
60
61
89 // BEGIN MODULEBUILDER PROPERTIES
93 public $fields = array(
94 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
95 'ref' => array('type' => 'varchar(128)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'notnull' => 1, 'default' => '(PROV)', 'visible' => 5, 'index' => 1, 'searchall' => 1, 'comment' => "Reference of object", "csslist" => "nowraponall", "showoncombobox" => 1),
96 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => 0, 'notnull' => 1, 'position' => 20, 'index' => 1),
97 'question' => array('type' => 'text', 'label' => 'Question', 'enabled' => 1, 'position' => 30, 'notnull' => 1, 'visible' => 1, 'searchall' => 1, 'csslist' => 'tdoverflowmax300 small', 'copytoclipboard' => 1, 'tdcss' => 'titlefieldcreate nowraponall'),
98 'lang' => array('type' => 'varchar(6)', 'label' => 'Language', 'enabled' => 1, 'position' => 40, 'notnull' => 0, 'visible' => 1, 'tdcss' => 'titlefieldcreate nowraponall', "csslist" => "minwidth100 maxwidth200"),
99 'date_creation' => array('type' => 'datetime', 'label' => 'DateCreation', 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'visible' => -2, 'csslist' => 'nowraponall'),
100 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 501, 'notnull' => 0, 'visible' => 2,),
101 'last_main_doc' => array('type' => 'varchar(255)', 'label' => 'LastMainDoc', 'enabled' => 1, 'position' => 600, 'notnull' => 0, 'visible' => 0,),
102 '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',),
103 'fk_user_modif' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserModif', 'enabled' => 1, 'position' => 511, 'notnull' => -1, 'visible' => -2,),
104 'fk_user_valid' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserValidation', 'enabled' => 1, 'position' => 512, 'notnull' => 0, 'visible' => -2,),
105 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'position' => 1000, 'notnull' => -1, 'visible' => -2,),
106 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'position' => 1010, 'notnull' => -1, 'visible' => 0,),
107 //'url' => array('type'=>'varchar(255)', 'label'=>'URL', 'enabled'=>'1', 'position'=>55, 'notnull'=>0, 'visible'=>-1, 'csslist'=>'tdoverflow200', 'help'=>'UrlForInfoPage'),
108 'fk_c_ticket_category' => array('type' => 'integer:CTicketCategory:ticket/class/cticketcategory.class.php:0:(t.active:=:1):pos', 'label' => 'SuggestedForTicketsInGroup', 'enabled' => 'isModEnabled("ticket")', 'position' => 520, 'notnull' => 0, 'visible' => -1, 'help' => 'YouCanLinkArticleToATicketCategory', 'csslist' => 'minwidth200 tdoverflowmax250'),
109 'answer' => array('type' => 'html', 'label' => 'Solution', 'enabled' => 1, 'position' => 600, 'notnull' => 0, 'visible' => 3, 'searchall' => 1, 'csslist' => 'tdoverflowmax300', 'copytoclipboard' => 1, 'tdcss' => 'titlefieldcreate nowraponall'),
110 'status' => array('type' => 'integer', 'label' => 'Status', 'enabled' => 1, 'position' => 1000, 'notnull' => 1, 'visible' => 5, 'default' => '0', 'index' => 1, 'arrayofkeyval' => array(0 => 'Draft', 1 => 'Validated', 9 => 'Obsolete'),),
111 );
112
116 public $rowid;
117
121 public $ref;
122
126 public $last_main_doc;
130 public $fk_user_creat;
134 public $fk_user_modif;
138 public $fk_user_valid;
142 public $import_key;
146 public $model_pdf;
147
151 public $question;
152
156 public $answer;
160 public $url;
161
165 public $status;
166
170 public $lang;
171 // END MODULEBUILDER PROPERTIES
172
173
174 // If this object has a subtable with lines
175
176 // /**
177 // * @var string Name of subtable line
178 // */
179 // public $table_element_line = 'knowledgemanagement_knowledgerecordline';
180
181 // /**
182 // * @var string Field with ID of parent key if this object has a parent
183 // */
184 // public $fk_element = 'fk_knowledgerecord';
185
186 // /**
187 // * @var string Name of subtable class that manage subtable lines
188 // */
189 // public $class_element_line = 'KnowledgeRecordline';
190
191 // /**
192 // * @var array List of child tables. To test if we can delete object.
193 // */
194 // protected $childtables = array();
195
196 // /**
197 // * @var array List of child tables. To know object to delete on cascade.
198 // * If name matches '@ClassNAme:FilePathClass;ParentFkFieldName' it will
199 // * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object
200 // */
201 // protected $childtablesoncascade = array('knowledgemanagement_knowledgerecorddet');
202
203 // /**
204 // * @var KnowledgeRecordLine[] Array of subtable lines
205 // */
206 // public $lines = array();
207
208
209
215 public function __construct(DoliDB $db)
216 {
217 global $langs;
218
219 $this->db = $db;
220
221 $this->ismultientitymanaged = 1;
222 $this->isextrafieldmanaged = 1;
223
224 if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid'])) {
225 $this->fields['rowid']['visible'] = 0;
226 }
227 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
228 $this->fields['entity']['enabled'] = 0;
229 }
230
231 // Unset fields that are disabled
232 foreach ($this->fields as $key => $val) {
233 if (isset($val['enabled']) && empty($val['enabled'])) {
234 unset($this->fields[$key]);
235 }
236 }
237
238 // Translate some data of arrayofkeyval
239 if (is_object($langs)) {
240 foreach ($this->fields as $key => $val) {
241 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
242 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
243 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
244 }
245 }
246 }
247 }
248 }
249
257 public function create(User $user, $notrigger = 0)
258 {
259 return $this->createCommon($user, $notrigger);
260 }
261
269 public function createFromClone(User $user, $fromid)
270 {
271 global $langs, $extrafields;
272 $error = 0;
273
274 dol_syslog(__METHOD__, LOG_DEBUG);
275
276 $object = new self($this->db);
277
278 $this->db->begin();
279
280 // Load source object
281 $result = $object->fetchCommon($fromid);
282 if ($result > 0 && !empty($object->table_element_line)) {
283 $object->fetchLines();
284 }
285
286 // get lines so they will be clone
287 //foreach($this->lines as $line)
288 // $line->fetch_optionals();
289
290 // Reset some properties
291 unset($object->id);
292 unset($object->fk_user_creat);
293 unset($object->import_key);
294
295 // Clear fields
296 if (property_exists($object, 'ref')) {
297 // @phan-suppress-next-line PhanTypeMismatchProperty
298 $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default'];
299 }
300 if (property_exists($object, 'question')) {
301 // @phan-suppress-next-line PhanTypeInvalidDimOffset
302 $object->question = empty($this->fields['question']['default']) ? $langs->trans("CopyOf")." ".$object->question : $this->fields['question']['default'];
303 }
304 if (property_exists($object, 'status')) {
305 $object->status = self::STATUS_DRAFT;
306 }
307 if (property_exists($object, 'date_creation')) {
308 $object->date_creation = dol_now();
309 }
310 if (property_exists($object, 'date_modification')) {
311 $object->date_modification = null;
312 }
313 // ...
314 // Clear extrafields that are unique
315 if (is_array($object->array_options) && count($object->array_options) > 0) {
316 $extrafields->fetch_name_optionals_label($this->table_element);
317 foreach ($object->array_options as $key => $option) {
318 $shortkey = preg_replace('/options_/', '', $key);
319 if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
320 //var_dump($key);
321 //var_dump($clonedObj->array_options[$key]); exit;
322 unset($object->array_options[$key]);
323 }
324 }
325 }
326
327 // Create clone
328 $object->context['createfromclone'] = 'createfromclone';
329 $result = $object->createCommon($user);
330 if ($result < 0) {
331 $error++;
332 $this->error = $object->error;
333 $this->errors = $object->errors;
334 }
335
336 if (!$error) {
337 // copy internal contacts
338 if ($this->copy_linked_contact($object, 'internal') < 0) {
339 $error++;
340 }
341 }
342
343 if (!$error) {
344 // copy external contacts if same company
345 if (property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) {
346 if ($this->copy_linked_contact($object, 'external') < 0) {
347 $error++;
348 }
349 }
350 }
351
352 unset($object->context['createfromclone']);
353
354 // End
355 if (!$error) {
356 $this->db->commit();
357 return $object;
358 } else {
359 $this->db->rollback();
360 return -1;
361 }
362 }
363
371 public function fetch($id, $ref = null)
372 {
373 $result = $this->fetchCommon($id, $ref);
374 if ($result > 0 && !empty($this->table_element_line)) {
375 $this->fetchLines();
376 }
377 return $result;
378 }
379
385 public function fetchLines()
386 {
387 $this->lines = array();
388
389 $result = $this->fetchLinesCommon();
390 return $result;
391 }
392
393
405 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
406 {
407 dol_syslog(__METHOD__, LOG_DEBUG);
408
409 $records = array();
410
411 $sql = 'SELECT ';
412 $sql .= $this->getFieldList('t');
413 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
414 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
415 $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')';
416 } else {
417 $sql .= ' WHERE 1 = 1';
418 }
419
420 // Manage filter
421 if (is_array($filter)) {
422 $sqlwhere = array();
423 if (count($filter) > 0) {
424 foreach ($filter as $key => $value) {
425 if ($key == 't.rowid') {
426 $sqlwhere[] = $this->db->sanitize($key)." = ".((int) $value);
427 } elseif (array_key_exists($key, $this->fields) && in_array($this->fields[$key]['type'], array('date', 'datetime', 'timestamp'))) {
428 $sqlwhere[] = $this->db->sanitize($key)." = '".$this->db->idate((int) $value)."'";
429 } elseif (strpos($value, '%') === false) {
430 $sqlwhere[] = $this->db->sanitize($key).' IN ('.$this->db->sanitize($this->db->escape($value)).')';
431 } else {
432 $sqlwhere[] = $this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
433 }
434 }
435 }
436 if (count($sqlwhere) > 0) {
437 $sql .= ' AND ('.implode(' '.$this->db->escape($filtermode).' ', $sqlwhere).')';
438 }
439
440 $filter = '';
441 }
442
443 // Manage filter
444 $errormessage = '';
445 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
446 if ($errormessage) {
447 $this->errors[] = $errormessage;
448 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
449 return -1;
450 }
451
452 if (!empty($sortfield)) {
453 $sql .= $this->db->order($sortfield, $sortorder);
454 }
455 if (!empty($limit)) {
456 $sql .= $this->db->plimit($limit, $offset);
457 }
458
459 $resql = $this->db->query($sql);
460 if ($resql) {
461 $num = $this->db->num_rows($resql);
462 $i = 0;
463 while ($i < ($limit ? min($limit, $num) : $num)) {
464 $obj = $this->db->fetch_object($resql);
465
466 $record = new self($this->db);
467 $record->setVarsFromFetchObj($obj);
468
469 $records[$record->id] = $record;
470
471 $i++;
472 }
473 $this->db->free($resql);
474
475 return $records;
476 } else {
477 $this->errors[] = 'Error '.$this->db->lasterror();
478 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
479
480 return -1;
481 }
482 }
483
491 public function update(User $user, $notrigger = 0)
492 {
493 return $this->updateCommon($user, $notrigger);
494 }
495
503 public function delete(User $user, $notrigger = 0)
504 {
505 global $conf;
506
507 $error = 0;
508 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_knowledgemanagement WHERE fk_knowledgemanagement = ".((int) $this->id);
509 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
510 $resql = $this->db->query($sql);
511 if (!$resql) {
512 $error++;
513 $this->error .= $this->db->lasterror();
514 $errorflag = -1;
515 }
516
517 // Delete all child tables
518 if (!$error) {
519 $elements = array('categorie_knowledgemanagement');
520 foreach ($elements as $table) {
521 if (!$error) {
522 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$table;
523 $sql .= " WHERE fk_knowledgemanagement = ".(int) $this->id;
524
525 $result = $this->db->query($sql);
526 if (!$result) {
527 $error++;
528 $this->errors[] = $this->db->lasterror();
529 }
530 }
531 }
532 }
533
534 $result = $this->deleteCommon($user, $notrigger);
535
536 // Remove the linked files directory (uploads attached to the article)
537 if ($result > 0 && !empty($conf->knowledgemanagement->dir_output) && !empty($this->ref)) {
538 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
539 $dir = $conf->knowledgemanagement->dir_output.'/knowledgerecord/'.dol_sanitizeFileName($this->ref);
540 if (file_exists($dir)) {
541 if (!@dol_delete_dir_recursive($dir)) {
542 $this->errors[] = 'ErrorFailToDeleteDir';
543 }
544 }
545 }
546
547 return $result;
548 }
549
558 public function deleteLine(User $user, $idline, $notrigger = 0)
559 {
560 if ($this->status < 0) {
561 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
562 return -2;
563 }
564
565 return $this->deleteLineCommon($user, $idline, $notrigger);
566 }
567
568
576 public function validate($user, $notrigger = 0)
577 {
578 global $conf, $langs;
579
580 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
581
582 $error = 0;
583
584 // Protection
585 if ($this->status == self::STATUS_VALIDATED) {
586 dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
587 return 0;
588 }
589
590 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight('knowledgemanagement', 'knowledgerecord', 'write'))
591 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->knowledgerecord->knowledgerecord_advance->validate))))
592 {
593 $this->error='NotEnoughPermissions';
594 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
595 return -1;
596 }*/
597
598 $now = dol_now();
599
600 $this->db->begin();
601
602 // Define new ref
603 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
604 $num = $this->getNextNumRef();
605 } else {
606 $num = (string) $this->ref;
607 }
608 $this->newref = $num;
609
610 if (!empty($num)) {
611 // Validate
612 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
613 $sql .= " SET ref = '".$this->db->escape($num)."',";
614 $sql .= " status = ".self::STATUS_VALIDATED;
615 if (!empty($this->fields['date_validation'])) {
616 $sql .= ", date_validation = '".$this->db->idate($now)."'";
617 }
618 if (!empty($this->fields['fk_user_valid'])) { // @phan-suppress-current-line PhanTypeMismatchProperty
619 $sql .= ", fk_user_valid = ".((int) $user->id);
620 }
621 $sql .= " WHERE rowid = ".((int) $this->id);
622
623 dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
624 $resql = $this->db->query($sql);
625 if (!$resql) {
626 dol_print_error($this->db);
627 $this->error = $this->db->lasterror();
628 $error++;
629 }
630
631 if (!$error && !$notrigger) {
632 // Call trigger
633 $result = $this->call_trigger('KNOWLEDGERECORD_VALIDATE', $user);
634 if ($result < 0) {
635 $error++;
636 }
637 // End call triggers
638 }
639 }
640
641 if (!$error) {
642 $this->oldref = $this->ref;
643
644 // Rename directory if dir was a temporary ref
645 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
646 // Now we rename also files into index
647 $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)."'";
648 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'knowledgerecord/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
649 $resql = $this->db->query($sql);
650 if (!$resql) {
651 $error++;
652 $this->error = $this->db->lasterror();
653 }
654 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'knowledgerecord/".$this->db->escape($this->newref)."'";
655 $sql .= " WHERE filepath = 'knowledgerecord/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
656 $resql = $this->db->query($sql);
657 if (!$resql) {
658 $error++;
659 $this->error = $this->db->lasterror();
660 }
661
662 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
663 $oldref = dol_sanitizeFileName($this->ref);
664 $newref = dol_sanitizeFileName($num);
665 $dirsource = $conf->knowledgemanagement->dir_output.'/knowledgerecord/'.$oldref;
666 $dirdest = $conf->knowledgemanagement->dir_output.'/knowledgerecord/'.$newref;
667 if (!$error && file_exists($dirsource)) {
668 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
669
670 if (@rename($dirsource, $dirdest)) {
671 dol_syslog("Rename ok");
672 // Rename docs starting with $oldref with $newref
673 $listoffiles = dol_dir_list($conf->knowledgemanagement->dir_output.'/knowledgerecord/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
674 foreach ($listoffiles as $fileentry) {
675 $dirsource = $fileentry['name'];
676 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
677 $dirsource = $fileentry['path'].'/'.$dirsource;
678 $dirdest = $fileentry['path'].'/'.$dirdest;
679 @rename($dirsource, $dirdest);
680 }
681 }
682 }
683 }
684 }
685
686 // Set new ref and current status
687 if (!$error) {
688 $this->ref = $num;
689 $this->status = self::STATUS_VALIDATED;
690 }
691
692 if (!$error) {
693 $this->db->commit();
694 return 1;
695 } else {
696 $this->db->rollback();
697 return -1;
698 }
699 }
700
701
709 public function setDraft($user, $notrigger = 0)
710 {
711 // Protection
712 if ($this->status <= self::STATUS_DRAFT) {
713 return 0;
714 }
715
716 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->write))
717 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->knowledgemanagement_advance->validate))))
718 {
719 $this->error='Permission denied';
720 return -1;
721 }*/
722
723 return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'KNOWLEDGERECORD_UNVALIDATE');
724 }
725
733 public function cancel($user, $notrigger = 0)
734 {
735 // Protection
736 if ($this->status != self::STATUS_VALIDATED) {
737 return 0;
738 }
739
740 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->write))
741 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->knowledgemanagement_advance->validate))))
742 {
743 $this->error='Permission denied';
744 return -1;
745 }*/
746
747 return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'KNOWLEDGERECORD_CANCEL');
748 }
749
757 public function reopen($user, $notrigger = 0)
758 {
759 // Protection
760 if ($this->status != self::STATUS_CANCELED) {
761 return 0;
762 }
763
764 /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->write))
765 || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->knowledgemanagement->knowledgemanagement_advance->validate))))
766 {
767 $this->error='Permission denied';
768 return -1;
769 }*/
770
771 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'KNOWLEDGERECORD_REOPEN');
772 }
773
780 public function getTooltipContentArray($params)
781 {
782 global $conf, $langs;
783
784 $langs->loadLangs(['knowledgemanagement', 'languages']);
785
786 $datas = array();
787 $nofetch = !empty($params['nofetch']);
788
789 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("KnowledgeRecord").'</u>';
790 if (isset($this->status)) {
791 $datas['picto'] .= ' '.$this->getLibStatut(5);
792 }
793 $datas['label'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
794 $datas['question'] = '<br><b>'.$langs->trans('Question').':</b> '.$this->question;
795 $labellang = ($this->lang ? $langs->trans('Language_'.$this->lang) : '');
796 $datas['lang'] = '<br><b>'.$langs->trans('Language').':</b> ' . picto_from_langcode($this->lang, 'class="paddingrightonly saturatemedium opacitylow"') . $labellang;
797 // show categories for this record only in ajax to not overload lists
798 if (isModEnabled('category') && !$nofetch) {
799 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
800 $form = new Form($this->db);
801 $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_KNOWLEDGEMANAGEMENT, 1);
802 }
803
804 return $datas;
805 }
806
817 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
818 {
819 global $conf, $langs, $hookmanager;
820
821 if (!empty($conf->dol_no_mouse_hover)) {
822 $notooltip = 1; // Force disable tooltips
823 }
824
825 $result = '';
826
827 $params = [
828 'id' => $this->id,
829 'objecttype' => $this->element.($this->module ? '@'.$this->module : ''),
830 'option' => $option,
831 'nofetch' => 1,
832 ];
833 $classfortooltip = 'classfortooltip';
834 $dataparams = '';
835 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
836 $classfortooltip = 'classforajaxtooltip';
837 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
838 $label = '';
839 } else {
840 $label = implode($this->getTooltipContentArray($params));
841 }
842
843 $url = dol_buildpath('/knowledgemanagement/knowledgerecord_card.php', 1).'?id='.$this->id;
844
845 if ($option != 'nolink') {
846 // Add param to save lastsearch_values or not
847 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
848 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
849 $add_save_lastsearch_values = 1;
850 }
851 if ($add_save_lastsearch_values) {
852 $url .= '&save_lastsearch_values=1';
853 }
854 }
855
856 $linkclose = '';
857 if (empty($notooltip)) {
858 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
859 $label = $langs->trans("ShowKnowledgeRecord");
860 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
861 }
862 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
863 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
864 } else {
865 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
866 }
867
868 if ($option == 'nolink') {
869 $linkstart = '<span';
870 } else {
871 $linkstart = '<a href="'.$url.'"';
872 }
873 $linkstart .= $linkclose.'>';
874 if ($option == 'nolink') {
875 $linkend = '</span>';
876 } else {
877 $linkend = '</a>';
878 }
879
880 $result .= $linkstart;
881
882 if (empty($this->showphoto_on_popup)) {
883 if ($withpicto) {
884 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1);
885 }
886 } else {
887 if ($withpicto) {
888 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
889
890 list($class, $module) = explode('@', $this->picto);
891 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref);
892 $filearray = dol_dir_list($upload_dir, "files");
893 $filename = $filearray[0]['name'];
894 if (!empty($filename)) {
895 $pospoint = strpos($filearray[0]['name'], '.');
896
897 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint);
898 if (!getDolGlobalString(strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS')) {
899 $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>';
900 } else {
901 $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>';
902 }
903
904 $result .= '</div>';
905 } else {
906 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
907 }
908 }
909 }
910
911 if ($withpicto != 2) {
912 $result .= $this->ref;
913 }
914
915 $result .= $linkend;
916 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
917
918 global $action, $hookmanager;
919 $hookmanager->initHooks(array('knowledgerecorddao'));
920 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
921 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
922 if ($reshook > 0) {
923 $result = $hookmanager->resPrint;
924 } else {
925 $result .= $hookmanager->resPrint;
926 }
927
928 return $result;
929 }
930
937 public function getLibStatut($mode = 0)
938 {
939 return $this->LibStatut($this->status, $mode);
940 }
941
942 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
950 public function LibStatut($status, $mode = 0)
951 {
952 // phpcs:enable
953 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
954 global $langs;
955 //$langs->load("knowledgemanagement");
956 $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
957 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
958 $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Obsolete');
959 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
960 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Validated');
961 $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Obsolete');
962 }
963
964 $statusType = 'status'.$status;
965 if ($status == self::STATUS_VALIDATED) {
966 $statusType = 'status4';
967 }
968 if ($status == self::STATUS_CANCELED) {
969 $statusType = 'status6';
970 }
971
972 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
973 }
974
981 public function info($id)
982 {
983 $sql = 'SELECT t.rowid, t.date_creation as datec, GREATEST(t.tms, kef.tms) as datem,';
984 $sql .= ' t.fk_user_creat, t.fk_user_modif';
985 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
986 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$this->table_element."_extrafields as kef ON kef.fk_object = t.rowid";
987 $sql .= ' WHERE t.rowid = '.((int) $id);
988 $result = $this->db->query($sql);
989 if ($result) {
990 if ($this->db->num_rows($result)) {
991 $obj = $this->db->fetch_object($result);
992
993 $this->id = $obj->rowid;
994
995 $this->user_creation_id = $obj->fk_user_creat;
996 $this->user_modification_id = $obj->fk_user_modif;
997 $this->date_creation = $this->db->jdate($obj->datec);
998 $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
999 }
1000
1001 $this->db->free($result);
1002 } else {
1003 dol_print_error($this->db);
1004 }
1005 }
1006
1013 public function initAsSpecimen()
1014 {
1015 $this->question = "ABCD";
1016
1017 return $this->initAsSpecimenCommon();
1018 }
1019
1025 public function getLinesArray()
1026 {
1027 $this->lines = array();
1028
1029 $objectline = new KnowledgeRecordLine($this->db);
1030 $result = $objectline->fetchAll('ASC', 'position', 0, 0, '(fk_knowledgerecord:=:'.((int) $this->id).')');
1031
1032 if (is_numeric($result)) {
1033 $this->error = $objectline->error;
1034 $this->errors = $objectline->errors;
1035 return $result;
1036 } else {
1037 $this->lines = $result;
1038 // @phpstan-ignore-next-line
1039 return $result; // @phan-suppress-current-line PhanTypeMismatchReturn
1040 }
1041 }
1042
1048 public function getNextNumRef()
1049 {
1050 global $langs, $conf;
1051 $langs->load("knowledgemanagement");
1052
1053 if (!getDolGlobalString('KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON')) {
1054 $conf->global->KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON = 'mod_knowledgerecord_standard';
1055 }
1056
1057 if (getDolGlobalString('KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON')) {
1058 $mybool = false;
1059
1060 $file = getDolGlobalString('KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON') . ".php";
1061 $classname = getDolGlobalString('KNOWLEDGEMANAGEMENT_KNOWLEDGERECORD_ADDON');
1062
1063 // Include file with class
1064 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1065 foreach ($dirmodels as $reldir) {
1066 $dir = dol_buildpath($reldir."core/modules/knowledgemanagement/");
1067
1068 // Load file with numbering class (if found)
1069 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1070 }
1071
1072 if (!$mybool) {
1073 dol_print_error(null, "Failed to include file ".$file);
1074 return '';
1075 }
1076
1077 if (class_exists($classname)) {
1078 $obj = new $classname();
1079 '@phan-var-force ModeleNumRefKnowledgeRecord $obj';
1080 $numref = $obj->getNextValue($this);
1081
1082 if ($numref != '' && $numref != '-1') {
1083 return $numref;
1084 } else {
1085 $this->error = $obj->error;
1086 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1087 return "";
1088 }
1089 } else {
1090 print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname;
1091 return "";
1092 }
1093 } else {
1094 print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
1095 return "";
1096 }
1097 }
1098
1110 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1111 {
1112 global $conf, $langs;
1113
1114 $result = 0;
1115 $includedocgeneration = 0;
1116
1117 $langs->load("knowledgemanagement");
1118
1119 if (!dol_strlen($modele)) {
1120 $modele = 'standard_knowledgerecord';
1121
1122 if (!empty($this->model_pdf)) {
1123 $modele = $this->model_pdf;
1124 } elseif (getDolGlobalString('KNOWLEDGERECORD_ADDON_PDF')) {
1125 $modele = getDolGlobalString('KNOWLEDGERECORD_ADDON_PDF');
1126 }
1127 }
1128
1129 $modelpath = "core/modules/knowledgemanagement/doc/";
1130
1131 if ($includedocgeneration && !empty($modele)) {
1132 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1133 }
1134
1135 return $result;
1136 }
1137
1145 public function doScheduledJob()
1146 {
1147 global $conf, $langs;
1148
1149 //$conf->global->SYSLOG_FILE = 'DOL_DATA_ROOT/dolibarr_mydedicatedlofile.log';
1150
1151 $error = 0;
1152 $this->output = '';
1153 $this->error = '';
1154
1155 dol_syslog(__METHOD__, LOG_DEBUG);
1156
1157 $now = dol_now();
1158
1159 $this->db->begin();
1160
1161 // ...
1162
1163 $this->db->commit();
1164
1165 return $error;
1166 }
1167
1178 public function setCategories($categories)
1179 {
1180 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
1181 return parent::setCategoriesCommon($categories, Categorie::TYPE_KNOWLEDGEMANAGEMENT);
1182 }
1183
1191 public function getKanbanView($option = '', $arraydata = null)
1192 {
1193 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1194
1195 $return = '<div class="box-flex-item box-flex-grow-zero">';
1196 $return .= '<div class="info-box info-box-sm">';
1197 $return .= '<span class="info-box-icon bg-infobox-action">';
1198 $return .= img_picto('', $this->picto);
1199 $return .= '</span>';
1200 $return .= '<div class="info-box-content">';
1201 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
1202 if ($selected >= 0) {
1203 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1204 }
1205 if (property_exists($this, 'lang') && !empty($this->lang)) {
1206 //$return .= '<br><span class="opacitymedium">'.$langs->trans("Language").'</span> : <span class="info-box-label" title="'.$langs->trans("Language_".$this->lang).'">'.$langs->trans("Language_".$this->lang, '', '', '', '', 12).'</span>';
1207 $return .= '<br>'.picto_from_langcode($this->lang, 'class="paddingrightonly saturatemedium opacitylow paddingrightonly"');
1208 }
1209 if (property_exists($this, 'question')) {
1210 $return .= '<div class="info-box-label tdoverflowmax150 classfortooltip" title="'.dolPrintHTMLForAttribute($this->question).'">'.dolGetFirstLineOfText($this->question).'</div>';
1211 }
1212 if (method_exists($this, 'getLibStatut')) {
1213 $return .= '<div class="info-box-status">'.$this->getLibStatut(3).'</div>';
1214 }
1215 $return .= '</div>';
1216 $return .= '</div>';
1217 $return .= '</div>';
1218 return $return;
1219 }
1220
1226 public function loadStateBoard()
1227 {
1228 global $user;
1229
1230 $this->nb = array();
1231 $clause = "WHERE";
1232
1233 $sql = "SELECT count(t.rowid) as nb";
1234 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t';
1235
1236 $resql = $this->db->query($sql);
1237 if ($resql) {
1238 while ($obj = $this->db->fetch_object($resql)) {
1239 $this->nb["knowledgebase"] = $obj->nb;
1240 }
1241
1242 $this->db->free($resql);
1243 return 1;
1244 } else {
1245 dol_print_error($this->db);
1246 $this->error = $this->db->error();
1247 return -1;
1248 }
1249 }
1250}
1251
1252
1253require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
1254
1259{
1260 // To complete with content of an object KnowledgeRecordLine
1261 // We should have a field rowid, fk_knowledgerecord and position
1262
1268 public function __construct(DoliDB $db)
1269 {
1270 $this->db = $db;
1271
1272 $this->isextrafieldmanaged = 0;
1273 }
1274}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$object ref
Definition info.php:90
Parent class of all other business classes (invoices, contracts, proposals, orders,...
deleteLineCommon(User $user, $idline, $notrigger=0)
Delete a line of object in database.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
createCommon(User $user, $notrigger=0)
Create object in the database.
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
updateCommon(User $user, $notrigger=0)
Update object into database.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
fetchLinesCommon($morewhere='', $noextrafields=0)
Load object in memory from the database.
fetchCommon($id, $ref=null, $morewhere='', $noextrafields=0)
Load object in memory from the database.
deleteCommon(User $user, $notrigger=0, $forcechilddeletion=0)
Delete object in database.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
Class to manage Dolibarr database access.
Class to manage generation of HTML components Only common components must be here.
Class for KnowledgeRecord.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with eventually picto)
fetch($id, $ref=null)
Load object in memory from the database.
validate($user, $notrigger=0)
Validate object.
getNextNumRef()
Returns the reference to the following non used object depending on the active numbering module.
fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, $filter='', $filtermode='AND')
Load list of objects in memory from the database.
info($id)
Load the info information in the object.
cancel($user, $notrigger=0)
Set cancel status.
update(User $user, $notrigger=0)
Update object into database.
fetchLines()
Load object lines in memory from the database.
LibStatut($status, $mode=0)
Return the status.
create(User $user, $notrigger=0)
Create object into database.
getTooltipContentArray($params)
getTooltipContentArray
loadStateBoard()
Load indicators into this->nb for board.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
createFromClone(User $user, $fromid)
Clone an object into another one.
deleteLine(User $user, $idline, $notrigger=0)
Delete a line of object in database.
doScheduledJob()
Action executed by scheduler CAN BE A CRON TASK.
setCategories($categories)
Sets object to supplied categories.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
getLinesArray()
Create an array of lines.
__construct(DoliDB $db)
Constructor.
getLibStatut($mode=0)
Return the label of the status.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
reopen($user, $notrigger=0)
Set back to validated status.
setDraft($user, $notrigger=0)
Set draft status.
Class KnowledgeRecordLine.
__construct(DoliDB $db)
Constructor.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
dol_now($mode='gmt')
Return date for now.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.