dolibarr 24.0.0-beta
memo.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2023-2026 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2026 John BOTELLA
5 * Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
27// Put here all includes required by your class file
28require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
29//require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
30//require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
31
35class Memo extends CommonObject
36{
40 public $module = 'quickmemo';
41
45 public $element = 'memo';
46
51 public $TRIGGER_PREFIX = 'QUICKMEMO_MEMO'; // Will be used to build trgiger keys 'QUICKMEMO_MEMO_MODIFY', ...
52
56 public $table_element = 'quickmemo_memo';
57
61 //public $element_for_permission = 'quickmemo';
62
66 public $picto = 'fa-file';
67
71 public $isextrafieldmanaged = 0;
72
77 public $ismultientitymanaged = 0;
78
79
80
81 const STATUS_TPL = 2;
82 const STATUS_VALIDATED = 1;
83 const STATUS_CANCELED = 9; // fall back in case of
84 const STATUS_ARCHIVED = 9;
85
133 public $fields = array(
134 "rowid" => array("type" => "integer", "label" => "TechnicalID", 'enabled' => 1, 'position' => 1, 'notnull' => 1, "visible" => 0, 'noteditable' => 1, 'index' => 1, "css" => "left", "comment" => "Id"),
135 "quick_note" => array("type" => "text", "label" => "Note", 'enabled' => 1, 'position' => 61, 'notnull' => 0, "visible" => -1, "cssview" => "wordbreak", "validate" => 1,),
136 "date_creation" => array("type" => "datetime", "label" => "DateCreation", 'enabled' => 1, 'position' => 500, 'notnull' => 1, "visible" => -2,),
137 "tms" => array("type" => "timestamp", "label" => "DateModification", 'enabled' => 1, 'position' => 501, 'notnull' => 0, "visible" => -2,),
138 "date_archived" => array("type" => "timestamp", "label" => "DateArchived", 'enabled' => 1, 'position' => 502, 'notnull' => 0, "visible" => -2,),
139 "fk_user_creat" => array("type" => "integer:User:user/class/user.class.php", "label" => "UserAuthor", "picto" => "user", 'enabled' => 1, 'position' => 510, 'notnull' => 1, "visible" => -2, "csslist" => "tdoverflowmax150",),
140 "fk_user_modif" => array("type" => "integer:User:user/class/user.class.php", "label" => "UserModif", "picto" => "user", 'enabled' => 1, 'position' => 511, 'notnull' => -1, "visible" => -2, "csslist" => "tdoverflowmax150",),
141 "fk_user_archived" => array("type" => "integer:User:user/class/user.class.php", "label" => "ArchivedBy", "picto" => "user", 'enabled' => 1, 'position' => 511, 'notnull' => -1, "visible" => -2, "csslist" => "tdoverflowmax150",),
142 "fk_element" => array('type' => 'integer','label' => 'MemoLinkedTo','help' => 'MemoLinkedToHelp','enabled' => 1,'visible' => 5,'notnull' => 0,'default' => 0,'index' => 1,'position' => 0),
143 "element_type" => array('type' => 'varchar(64)','label' => 'QuickMemoElementType','enabled' => 1,'visible' => 5,'position' => 10,'required' => 0),
144 "pos_z" => array("type" => "integer", "label" => "PosZ", 'enabled' => 1, 'position' => 1000, 'notnull' => 1, "visible" => 0, "default" => 0, "validate" => 1),
145 "pos_y" => array("type" => "integer", "label" => "PosY", 'enabled' => 1, 'position' => 1000, 'notnull' => 1, "visible" => 0, "default" => 0, "validate" => 1),
146 "pos_x" => array("type" => "integer", "label" => "PosX", 'enabled' => 1, 'position' => 1000, 'notnull' => 1, "visible" => 0, "default" => 0, "validate" => 1),
147 "pos_w" => array("type" => "integer", "label" => "PosW", 'enabled' => 1, 'position' => 1000, 'notnull' => 1, "visible" => 0, "default" => 0, "validate" => 1),
148 "pos_h" => array("type" => "integer", "label" => "PosH", 'enabled' => 1, 'position' => 1000, 'notnull' => 1, "visible" => 0, "default" => 0, "validate" => 1),
149 "color" => array('type' => 'varchar(10)', 'label' => 'Color','enabled' => 1,'visible' => 1,'position' => 10,'required' => 0),
150 "context_tab" => array('type' => 'varchar(64)', 'label' => 'ContextTab','enabled' => 1,'visible' => 1,'position' => 10,'required' => 0),
151 "private" => array("type" => "integer", "label" => "Private", 'enabled' => 1, 'position' => 1990, 'notnull' => 1, "visible" => 1, 'index' => 1, "arrayofkeyval" => array(0 => "No", 1 => "Yes"),'default' => 1, "validate" => 1,),
152 "private_tpl" => array("type" => "integer", "label" => "PrivateTemplate", 'enabled' => 1, 'position' => 1990, 'notnull' => 0, "visible" => 1, 'index' => 1, "arrayofkeyval" => array(0 => "No", 1 => "Yes"),'default' => 0, "validate" => 1,),
153 "rank_tpl" => array("type" => "integer", "label" => "TemplateRank", 'enabled' => 1, 'position' => 1990, 'notnull' => 0, "visible" => 0, 'index' => 1,'default' => 0, "validate" => 1,),
154 "name_tpl" => array('type' => 'varchar(256)','label' => 'QuickMemoTemplateName','enabled' => 1,'visible' => -1,'position' => 1,'required' => 0),
155 "shared_on_element" => array("type" => "integer", "label" => "SharedBetweenElement", 'enabled' => 1, 'position' => 1991, 'notnull' => 1, "visible" => 1, 'index' => 1, "arrayofkeyval" => array(0 => "No", 1 => "Yes"), "validate" => 1,),
156 "import_key" => array("type" => "varchar(14)", "label" => "ImportId", 'enabled' => 1, 'position' => 1000, 'notnull' => -1, "visible" => -2,),
157 "status" => array("type" => "integer", "label" => "Status", 'enabled' => 1, 'position' => 2000, 'notnull' => 1, "visible" => 1, 'index' => 1, "arrayofkeyval" => array(1 => "Active",2 => "Template", 9 => "Archived"), "validate" => 1,),
158 );
159
161 public $rowid;
162
164 public $date_archived;
165
167 public $fk_user_archived;
168
170 public $quick_note;
171
173 public $date_creat;
174
179 public $fk_element;
180
182 public $element_type;
183
185 public $pos_z;
186
188 public $pos_y;
189
191 public $pos_x;
192
194 public $pos_w;
195
197 public $pos_h;
198
200 public $color;
201
203 public $context_tab;
204
206 public $import_key;
207
216 public $status;
217
219 public $private;
220
222 public $private_tpl;
223
225 public $name_tpl;
226
228 public $shared_on_element;
229
235 public function __construct(DoliDB $db)
236 {
237 global $langs;
238
239 $this->db = $db;
240
241 if (!getDolGlobalInt('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid']) && !empty($this->fields['ref'])) {
242 $this->fields['rowid']['visible'] = 0;
243 }
244 if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
245 $this->fields['entity']['enabled'] = 0;
246 }
247
248 // Unset fields that are disabled
249 foreach ($this->fields as $key => $val) {
250 if (isset($val['enabled']) && empty($val['enabled'])) {
251 unset($this->fields[$key]);
252 }
253 }
254
255 // Translate some data of arrayofkeyval
256 if (is_object($langs)) {
257 foreach ($this->fields as $key => $val) {
258 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
259 foreach ($val['arrayofkeyval'] as $key2 => $val2) {
260 $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
261 }
262 }
263 }
264 }
265 }
266
274 public function create(User $user, $notrigger = 0)
275 {
276 $result = $this->createCommon($user, $notrigger);
277
278 return $result;
279 }
280
288 public function createFromClone(User $user, $fromid)
289 {
290 global $langs, $extrafields;
291 $error = 0;
292
293 dol_syslog(__METHOD__, LOG_DEBUG);
294
295 $object = new self($this->db);
296
297 $this->db->begin();
298
299 // Load source object
300 $result = $object->fetchCommon($fromid);
301 if ($result > 0 && !empty($object->table_element_line)) {
302 $object->fetchLines();
303 }
304
305 // Reset some properties
306 unset($object->id);
307 unset($object->fk_user_creat);
308 unset($object->import_key);
309
310
311 $object->status = self::STATUS_VALIDATED;
312
313 $object->date_creation = dol_now();
314
315 $object->date_modification = null;
316
317 // ...
318 // Clear extrafields that are unique
319 if (is_array($object->array_options) && count($object->array_options) > 0) {
320 $extrafields->fetch_name_optionals_label($this->table_element);
321 foreach ($object->array_options as $key => $option) {
322 $shortkey = preg_replace('/options_/', '', $key);
323 if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
324 //var_dump($key);
325 //var_dump($clonedObj->array_options[$key]); exit;
326 unset($object->array_options[$key]);
327 }
328 }
329 }
330
331 // Create clone
332 $object->context['createfromclone'] = 'createfromclone';
333 $result = $object->createCommon($user);
334 if ($result < 0) {
335 $error++;
337 }
338
339 unset($object->context['createfromclone']);
340
341 // End
342 if (!$error) {
343 $this->db->commit();
344 return $object;
345 } else {
346 $this->db->rollback();
347 return -1;
348 }
349 }
350
360 public function fetch($id, $ref = null, $noextrafields = 0, $nolines = 0)
361 {
362 $result = $this->fetchCommon($id, $ref, '', $noextrafields);
363 if ($result > 0 && !empty($this->table_element_line) && empty($nolines)) {
364 $this->fetchLines($noextrafields);
365 }
366 return $result;
367 }
368
375 public function fetchLines($noextrafields = 0)
376 {
377 $this->lines = array();
378
379 $result = $this->fetchLinesCommon('', $noextrafields);
380 return $result;
381 }
382
383
398 public function fetchAll($sortorder = '', $sortfield = '', $limit = 1000, $offset = 0, string $filter = '', $filtermode = 'AND')
399 {
400 dol_syslog(__METHOD__, LOG_DEBUG);
401
402 $records = array();
403
404 $sql = "SELECT ";
405 $sql .= $this->getFieldList('t');
406 $sql .= " FROM ".$this->db->prefix().$this->table_element." as t";
407 if (!empty($this->isextrafieldmanaged) && $this->isextrafieldmanaged == 1) {
408 $sql .= " LEFT JOIN ".$this->db->prefix().$this->table_element."_extrafields as te ON te.fk_object = t.rowid";
409 }
410 if (!empty($this->ismultientitymanaged) && (int) $this->ismultientitymanaged == 1) {
411 $sql .= " WHERE t.entity IN (".getEntity($this->element).")";
412 } elseif (preg_match('/^\w+@\w+$/', (string) $this->ismultientitymanaged)) {
413 $tmparray = explode('@', (string) $this->ismultientitymanaged);
414 $sql .= " LEFT JOIN ".$this->db->prefix().$tmparray[1]." as pt ON t.".$this->db->sanitize($tmparray[0])." = pt.rowid";
415 $sql .= " WHERE pt.entity IN (".getEntity($this->element).")";
416 } else {
417 $sql .= " WHERE 1 = 1";
418 }
419
420 // Manage filter
421 $errormessage = '';
422 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
423 if ($errormessage) {
424 $this->errors[] = $errormessage;
425 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
426 return -1;
427 }
428
429 if (!empty($sortfield)) {
430 $sql .= $this->db->order($sortfield, $sortorder);
431 }
432 if (!empty($limit)) {
433 $sql .= $this->db->plimit($limit, $offset);
434 }
435
436 $resql = $this->db->query($sql);
437 if ($resql) {
438 $num = $this->db->num_rows($resql);
439 $i = 0;
440 while ($i < ($limit ? min($limit, $num) : $num)) {
441 $obj = $this->db->fetch_object($resql);
442
443 $record = new self($this->db);
444 $record->setVarsFromFetchObj($obj);
445
446 if (!empty($record->isextrafieldmanaged)) {
447 $record->fetch_optionals();
448 }
449
450 $records[$record->id] = $record;
451
452 $i++;
453 }
454 $this->db->free($resql);
455
456 return $records;
457 } else {
458 $this->errors[] = 'Error '.$this->db->lasterror();
459 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
460
461 return -1;
462 }
463 }
464
472 public function update(User $user, $notrigger = 0)
473 {
474 return $this->updateCommon($user, $notrigger);
475 }
476
489 public function updatePosition(User $user, int $x, int $y, int $w, int $h, int $z)
490 {
491 if (empty($this->id)) {
492 return false;
493 }
494
495 $x = (int) $x;
496 $y = (int) $y;
497 $w = (int) $w;
498 $h = (int) $h;
499 $z = (int) $z;
500
501 // CREATEUR
502 if ((int) $this->fk_user_creat === (int) $user->id) {
503 $this->pos_x = $x;
504 $this->pos_y = $y;
505 $this->pos_w = $w;
506 $this->pos_h = $h;
507 $this->pos_z = $z;
508
509 if ($this->update($user) <= 0) {
510 return false;
511 }
512
513 return true;
514 }
515
516 // USER NOT CREATOR
517
518 $sql = 'SELECT rowid';
519 $sql .= ' FROM '.$this->db->prefix().'quickmemo_memo_user';
520 $sql .= ' WHERE fk_memo = '.((int) $this->id);
521 $sql .= ' AND fk_user = '.((int) $user->id);
522
523 $resql = $this->db->query($sql);
524 if (!$resql) {
525 return false;
526 }
527
528 if ($this->db->num_rows($resql) > 0) {
529 // UPDATE
530 $sql = 'UPDATE '.$this->db->prefix().'quickmemo_memo_user SET';
531 $sql .= ' pos_x = '. (int) $x.',';
532 $sql .= ' pos_y = '. (int) $y.',';
533 $sql .= ' pos_w = '. (int) $w.',';
534 $sql .= ' pos_h = '. (int) $h.',';
535 $sql .= ' pos_z = '. (int) $z;
536 $sql .= ' WHERE fk_memo = '.((int) $this->id);
537 $sql .= ' AND fk_user = '.((int) $user->id);
538
539 if (!$this->db->query($sql)) {
540 return false;
541 }
542 } else {
543 // INSERT
544 $sql = 'INSERT INTO '.$this->db->prefix().'quickmemo_memo_user (';
545 $sql .= 'fk_memo,';
546 $sql .= 'fk_user,';
547 $sql .= 'date_creation,';
548 $sql .= 'pos_x,';
549 $sql .= 'pos_y,';
550 $sql .= 'pos_w,';
551 $sql .= 'pos_h,';
552 $sql .= 'pos_z';
553 $sql .= ') VALUES (';
554 $sql .= ((int) $this->id).',';
555 $sql .= ((int) $user->id).',';
556 $sql .= '\''. $this->db->idate(dol_now()) .'\',';
557 $sql .= (int) $x.',';
558 $sql .= (int) $y.',';
559 $sql .= (int) $w.',';
560 $sql .= (int) $h.',';
561 $sql .= (int) $z;
562 $sql .= ')';
563
564 if (!$this->db->query($sql)) {
565 return false;
566 }
567 }
568
569 return true;
570 }
571
572
580 public function delete(User $user, $notrigger = 0)
581 {
582 return $this->deleteCommon($user, $notrigger);
583 //return $this->deleteCommon($user, $notrigger, 1);
584 }
585
594 public function deleteLine(User $user, $idline, $notrigger = 0)
595 {
596 if ($this->status < 0) {
597 $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
598 return -2;
599 }
600
601 return $this->deleteLineCommon($user, $idline, $notrigger);
602 }
603
604
612 public function validate($user, $notrigger = 0)
613 {
614 global $conf;
615
616 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
617
618 $error = 0;
619
620 // Protection
621 if ($this->status == self::STATUS_VALIDATED) {
622 dol_syslog(get_class($this)."::validate action abandoned: already validated", LOG_WARNING);
623 return 0;
624 }
625
626 /* if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo', 'memo', 'write'))
627 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo', 'memo_advance', 'validate')))
628 {
629 $this->error='NotEnoughPermissions';
630 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
631 return -1;
632 }*/
633
634 $now = dol_now();
635
636 $this->db->begin();
637
638 // Define new ref
639 if (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life
640 $num = $this->getNextNumRef();
641 } else {
642 $num = (string) $this->ref;
643 }
644 $this->newref = $num;
645
646 if (!empty($num)) {
647 // Validate
648 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
649 $sql .= " SET ";
650 if (!empty($this->fields['ref'])) {
651 $sql .= " ref = '".$this->db->escape($num)."',";
652 }
653 $sql .= " status = ".self::STATUS_VALIDATED;
654 if (!empty($this->fields['date_validation'])) {
655 $sql .= ", date_validation = '".$this->db->idate($now)."'";
656 }
657 if (!empty($this->fields['fk_user_valid'])) {
658 $sql .= ", fk_user_valid = ".((int) $user->id);
659 }
660 $sql .= " WHERE rowid = ".((int) $this->id);
661
662 dol_syslog(get_class($this)."::validate()", LOG_DEBUG);
663 $resql = $this->db->query($sql);
664 if (!$resql) {
665 dol_print_error($this->db);
666 $this->error = $this->db->lasterror();
667 $error++;
668 }
669
670 if (!$error && !$notrigger) {
671 // Call trigger
672 $result = $this->call_trigger('MEMO_VALIDATE', $user);
673 if ($result < 0) {
674 $error++;
675 }
676 // End call triggers
677 }
678 }
679
680 if (!$error) {
681 $this->oldref = $this->ref;
682
683 // Rename directory if dir was a temporary ref
684 if (preg_match('/^[\(]?PROV/i', $this->ref)) {
685 // Now we rename also files into index
686 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'memo/".$this->db->escape($this->newref)."'";
687 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'memo/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
688 $resql = $this->db->query($sql);
689 if (!$resql) {
690 $error++;
691 $this->error = $this->db->lasterror();
692 }
693 $sql = 'UPDATE '.$this->db->prefix()."ecm_files set filepath = 'memo/".$this->db->escape($this->newref)."'";
694 $sql .= " WHERE filepath = 'memo/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
695 $resql = $this->db->query($sql);
696 if (!$resql) {
697 $error++;
698 $this->error = $this->db->lasterror();
699 }
700
701 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
702 $oldref = dol_sanitizeFileName($this->ref);
703 $newref = dol_sanitizeFileName($num);
704 $dirsource = $conf->quickmemo->dir_output.'/memo/'.$oldref;
705 $dirdest = $conf->quickmemo->dir_output.'/memo/'.$newref;
706 if (!$error && file_exists($dirsource)) {
707 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest);
708
709 if (@rename($dirsource, $dirdest)) {
710 dol_syslog("Rename ok");
711 // Rename docs starting with $oldref with $newref
712 $listoffiles = dol_dir_list($conf->quickmemo->dir_output.'/memo/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
713 foreach ($listoffiles as $fileentry) {
714 $dirsource = $fileentry['name'];
715 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
716 $dirsource = $fileentry['path'].'/'.$dirsource;
717 $dirdest = $fileentry['path'].'/'.$dirdest;
718 @rename($dirsource, $dirdest);
719 }
720 }
721 }
722 }
723 }
724
725 // Set new ref and current status
726 if (!$error) {
727 $this->ref = $num;
728 $this->status = self::STATUS_VALIDATED;
729 }
730
731 if (!$error) {
732 $this->db->commit();
733 return 1;
734 } else {
735 $this->db->rollback();
736 return -1;
737 }
738 }
739
740
748 public function setDraft($user, $notrigger = 0)
749 {
750 // Protection
751 if ($this->status <= self::STATUS_VALIDATED) {
752 return 0;
753 }
754
755 /* if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo','write'))
756 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo','quickmemo_advance','validate'))))
757 {
758 $this->error='Permission denied';
759 return -1;
760 }*/
761
762 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'QUICKMEMO_MEMO_UNVALIDATE');
763 }
764
765
773 public function setArchived($user, $notrigger = 0)
774 {
775 $this->date_archived = dol_now();
776 $this->fk_user_archived = $user->id;
777 $this->status = self::STATUS_ARCHIVED;
778 return $this->update($user, $notrigger);
779 }
780
781
789 public function setUnArchived($user, $notrigger = 0)
790 {
791 $this->date_archived = null;
792 $this->fk_user_archived = null;
793 $this->status = self::STATUS_VALIDATED;
794 return $this->update($user, $notrigger);
795 }
796
804 public function cancel($user, $notrigger = 0)
805 {
806 // Protection
807 if ($this->status != self::STATUS_VALIDATED) {
808 return 0;
809 }
810
811 /* if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo','write'))
812 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo','quickmemo_advance','validate'))))
813 {
814 $this->error='Permission denied';
815 return -1;
816 }*/
817
818 return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'QUICKMEMO_MEMO_CANCEL');
819 }
820
828 public function reopen($user, $notrigger = 0)
829 {
830 // Protection
831 if ($this->status == self::STATUS_VALIDATED) {
832 return 0;
833 }
834
835 /*if (! ((!getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo','write'))
836 || (getDolGlobalInt('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('quickmemo','quickmemo_advance','validate'))))
837 {
838 $this->error='Permission denied';
839 return -1;
840 }*/
841
842 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'QUICKMEMO_MEMO_REOPEN');
843 }
844
852 public function getTooltipContentArray($params)
853 {
854 global $langs;
855
856 $datas = [];
857
858 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
859 return ['optimize' => $langs->trans("ShowMemo")];
860 }
861 $datas['picto'] = img_picto('', $this->picto).' <u>'.$langs->trans("Memo").'</u>';
862 if (isset($this->status)) {
863 $datas['picto'] .= ' '.$this->getLibStatut(5);
864 }
865
866 return $datas;
867 }
868
879 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
880 {
881 global $conf, $langs, $hookmanager;
882
883 if (!empty($conf->dol_no_mouse_hover)) {
884 $notooltip = 1; // Force disable tooltips
885 }
886
887 $result = '';
888 $params = [
889 'id' => (string) $this->id,
890 'objecttype' => $this->element.($this->module ? '@'.$this->module : ''),
891 'option' => $option,
892 ];
893 $classfortooltip = 'classfortooltip';
894 $dataparams = '';
895 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
896 $classfortooltip = 'classforajaxtooltip';
897 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
898 $label = '';
899 } else {
900 $label = implode($this->getTooltipContentArray($params));
901 }
902
903 $baseurl = dol_buildpath('/quickmemo/memo_card.php', 1);
904 $query = ['id' => $this->id];
905 if ($option !== 'nolink') {
906 // Add param to save lastsearch_values or not
907 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
908 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
909 $add_save_lastsearch_values = 1;
910 }
911 if ($add_save_lastsearch_values) {
912 $query = array_merge($query, ['save_lastsearch_values' => 1]);
913 }
914 }
915 $url = dolBuildUrl($baseurl, $query);
916
917 $linkclose = '';
918 if (empty($notooltip)) {
919 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER')) {
920 $label = $langs->trans("ShowMemo");
921 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
922 }
923 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
924 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
925 } else {
926 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
927 }
928
929 if ($option == 'nolink') {
930 $linkstart = '<span';
931 } else {
932 $linkstart = '<a href="'.$url.'"';
933 }
934 $linkstart .= $linkclose.'>';
935 if ($option == 'nolink') {
936 $linkend = '</span>';
937 } else {
938 $linkend = '</a>';
939 }
940
941 $result .= $linkstart;
942
943 if (empty($this->showphoto_on_popup)) {
944 if ($withpicto) {
945 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
946 }
947 } else {
948 if ($withpicto) {
949 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
950
951 [$class, $module] = explode('@', $this->picto);
952 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref);
953 $filearray = dol_dir_list($upload_dir, "files");
954 $filename = $filearray[0]['name'];
955 if (!empty($filename)) {
956 $pospoint = strpos($filearray[0]['name'], '.');
957
958 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint);
959 if (!getDolGlobalString(strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS')) {
960 $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>';
961 } else {
962 $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>';
963 }
964
965 $result .= '</div>';
966 } else {
967 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
968 }
969 }
970 }
971
972 if ($withpicto != 2) {
973 $result .= $this->ref;
974 }
975
976 $result .= $linkend;
977 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
978
979 global $action, $hookmanager;
980 $hookmanager->initHooks(array($this->element.'dao'));
981 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
982 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
983 if ($reshook > 0) {
984 $result = $hookmanager->resPrint;
985 } else {
986 $result .= $hookmanager->resPrint;
987 }
988
989 return $result;
990 }
991
999 public function getKanbanView($option = '', $arraydata = null)
1000 {
1001 global $conf, $langs;
1002
1003 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
1004
1005 $return = '<div class="box-flex-item box-flex-grow-zero">';
1006 $isDarkMode = !empty($this->color) && !colorIsLight($this->color) ? '--memo-is-dark' : '';
1007 $style = !empty($this->color) && Memo::checkColor($this->color) ? ' style="--memo-color : '.$this->color.'" ' : '';
1008 $return .= '<div class="info-box-quickmemo '.$isDarkMode.'" '.$style.' >';
1009 $return .= '<div class="info-box-content-quickmemo">';
1010
1011 $return .= '<span class="info-box-status">'.$this->getLibStatut(4).'</span>';
1012
1013 // $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
1014 if ($selected >= 0) {
1015 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
1016 }
1017
1018
1019
1020 $return .= ' <div class="info-box-content-quicknote">';
1021 $return .= nl2br(htmlentities($this->quick_note));
1022 $return .= '</div>';
1023
1024 $return .= '<div class="quickmemo-info" >';
1025 $return .= ' <div class="quickmemo-info__create" >';
1026 $return .= ' <span class="quickmemo-info__user-create-name">'.$this->showOutputField($this->fields['fk_user_creat'], 'fk_user_creat', $this->user_creation_id).'</span>';
1027 $return .= ' <span class="quickmemo-info__date_create">'.dol_print_date($this->date_creation, '%d/%m/%Y %H:%M', 'tzuserrel').'</span>';
1028 $return .= ' </div>';
1029
1030
1031 if (!empty($this->tms) && $this->tms != $this->date_creation) {
1032 $return .= ' <div class="quickmemo-info__update" >';
1033
1034 $return .= ' <span class="quickmemo-info__date_update">'.$langs->trans('QuickMemoModified') . ' ' .dol_print_date($this->date_modification, '%d/%m/%Y %H:%M', 'tzuserrel').'</span>';
1035 if ($this->user_modification_id != $this->user_creation_id) {
1036 $return .= ' <span class="quickmemo-info__user-update-name">'.$langs->trans('QuickMemoBy') . ' ' .$this->showOutputField($this->fields['fk_user_modif'], 'fk_user_modif', $this->user_modification_id).'</span>';
1037 }
1038
1039 $return .= ' </div>';
1040 }
1041
1042 if (!empty($this->date_archived) && $this->status == self::STATUS_ARCHIVED) {
1043 $return .= ' <div class="quickmemo-info__update" >';
1044
1045 $return .= ' <span class="quickmemo-info__date_update">'.$langs->trans('QuickMemoArchived') . ' ' .dol_print_date($this->date_archived, '%d/%m/%Y %H:%M', 'tzuserrel').'</span>';
1046 if ($this->fk_user_modif != $this->fk_user_archived) {
1047 $return .= ' <span class="quickmemo-info__user-update-name">'.$langs->trans('QuickMemoBy') . ' ' .$this->showOutputField($this->fields['fk_user_archived'], 'fk_user_archived', $this->fk_user_archived).'</span>';
1048 }
1049
1050 $return .= ' </div>';
1051 }
1052
1053 $return .= '</div>';
1054
1055
1056
1057
1058 $return .= '</div>';
1059 $return .= '</div>';
1060 $return .= '</div>';
1061
1062 return $return;
1063 }
1064
1071 public function getLabelStatus($mode = 0)
1072 {
1073 return $this->LibStatut($this->status, $mode);
1074 }
1075
1082 public function getLibStatut($mode = 0)
1083 {
1084 return $this->LibStatut($this->status, $mode);
1085 }
1086
1087 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1095 public function LibStatut($status, $mode = 0)
1096 {
1097 // phpcs:enable
1098 if (is_null($status)) {
1099 return '';
1100 }
1101
1102 $paramsBadge = array('badgeParams' => array('attr' => array(
1103 'data-status-element' => $this->element,
1104 'data-status' => (int) $status
1105 )));
1106
1107
1108 if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
1109 global $langs;
1110 //$langs->load("quickmemo");
1111 $this->labelStatus[self::STATUS_TPL] = $langs->transnoentitiesnoconv('Template');
1112 $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1113 $this->labelStatus[self::STATUS_ARCHIVED] = $langs->transnoentitiesnoconv('Archived');
1114 $this->labelStatusShort[self::STATUS_TPL] = $langs->transnoentitiesnoconv('Template');
1115 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
1116 $this->labelStatusShort[self::STATUS_ARCHIVED] = $langs->transnoentitiesnoconv('Archived');
1117 }
1118
1119 $statusType = 'status'.$status;
1120 //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
1121 if ($status == self::STATUS_ARCHIVED) {
1122 $statusType = 'status6';
1123 }
1124
1125 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode, '', $paramsBadge);
1126 }
1127
1134 public function info($id)
1135 {
1136 $sql = "SELECT t.rowid, t.date_creation as datec";
1137 if (!empty($this->isextrafieldmanaged) && $this->isextrafieldmanaged == 1) {
1138 $sql .= ", GREATEST(t.tms, te.tms) as datem";
1139 } else {
1140 $sql .= ", t.tms as datem";
1141 }
1142 if (!empty($this->fields['date_validation'])) {
1143 $sql .= ", t.date_validation as datev";
1144 }
1145 if (!empty($this->fields['fk_user_creat'])) {
1146 $sql .= ", t.fk_user_creat";
1147 }
1148 if (!empty($this->fields['fk_user_modif'])) {
1149 $sql .= ", t.fk_user_modif";
1150 }
1151 if (!empty($this->fields['fk_user_valid'])) {
1152 $sql .= ", t.fk_user_valid";
1153 }
1154 $sql .= " FROM ".$this->db->prefix().$this->table_element." as t";
1155 if (!empty($this->isextrafieldmanaged) && $this->isextrafieldmanaged == 1) {
1156 $sql .= " LEFT JOIN ".$this->db->prefix().$this->table_element."_extrafields as te ON te.fk_object = t.rowid";
1157 }
1158 $sql .= " WHERE t.rowid = ".((int) $id);
1159
1160 $result = $this->db->query($sql);
1161 if ($result) {
1162 if ($this->db->num_rows($result)) {
1163 $obj = $this->db->fetch_object($result);
1164
1165 $this->id = $obj->rowid;
1166
1167 if (!empty($this->fields['fk_user_creat'])) {
1168 $this->user_creation_id = $obj->fk_user_creat;
1169 }
1170 if (!empty($this->fields['fk_user_modif'])) {
1171 $this->user_modification_id = $obj->fk_user_modif;
1172 }
1173 if (!empty($this->fields['fk_user_valid'])) {
1174 $this->user_validation_id = $obj->fk_user_valid;
1175 }
1176 $this->date_creation = $this->db->jdate($obj->datec);
1177 $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
1178 if (!empty($obj->datev)) {
1179 $this->date_validation = empty($obj->datev) ? '' : $this->db->jdate($obj->datev);
1180 }
1181 }
1182
1183 $this->db->free($result);
1184 } else {
1185 dol_print_error($this->db);
1186 }
1187 }
1188
1195 public function initAsSpecimen()
1196 {
1197 // Set here init that are not commonf fields
1198 // $this->property1 = ...
1199 // $this->property2 = ...
1200
1201 return $this->initAsSpecimenCommon();
1202 }
1203
1204
1210 public function getNextNumRef()
1211 {
1212 return '';
1213 }
1214
1226 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
1227 {
1228 return 0;
1229 }
1230
1237 public static function checkColor($color)
1238 {
1239 if (!is_string($color)) {
1240 return false;
1241 }
1242
1243 // Remove spaces at the beginning/end
1244 $color = trim($color);
1245
1246 // Vérifie #fff ou #ffffff
1247 return preg_match('/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/', $color) === 1;
1248 }
1249
1255 public static function getColorPreset()
1256 {
1257
1258 $colorsConf = getDolGlobalString('QUICKMEMO_COLORS_PRESET');
1259 $colors = explode(',', $colorsConf);
1260 if (!empty($colors)) {
1261 foreach ($colors as $iColor => $color) {
1262 if (!preg_match('/^#[a-f0-9]{6}$/i', $color)) {
1263 unset($colors[$iColor]);
1264 }
1265 }
1266 }
1267
1268 if (empty($colors)) {
1269 $colors = ['#fff8a6', '#ffd6d6', '#d6ffd9', '#d6e6ff', '#f3d6ff', '#ffffff', '#f5f5f5'];
1270 }
1271
1272 return array_values($colors);
1273 }
1274
1283 public static function getMemoContext($context, $object = null)
1284 {
1285 global $db,$action, $hookmanager;
1286
1287 if (!isModEnabled('quickmemo')) {
1288 return '';
1289 }
1290
1291 if (!is_array($context)) {
1292 $context = explode(':', $context);
1293 }
1294
1295 $contextMapping = self::getAvailableMemoContextMapping();
1296
1297 $memoContext = '';
1298 foreach ($contextMapping as $key => $values) {
1299 $values = (array) $values;
1300
1301 if (array_intersect($context, $values)) {
1302 $memoContext = $key;
1303 break;
1304 }
1305 }
1306
1307 $staticMemo = new self($db);
1308
1309 $hookmanager->initHooks(array($staticMemo->element.'dao'));
1310 $parameters = array(
1311 'memoContext' => & $memoContext
1312 );
1313
1314 $reshook = $hookmanager->executeHooks('getMemoContext', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1315 if ($reshook > 0) {
1316 return $hookmanager->resPrint;
1317 }
1318
1319 return $memoContext;
1320 }
1321
1329 public static function completeMemoContextMapping(array &$contextTabMapping, string $tabContext, string $dolibarrContext = '')
1330 {
1331 $dolibarrContext = trim($dolibarrContext) ?: $tabContext;
1332
1333 if (!isset($contextTabMapping[$tabContext])) {
1334 $contextTabMapping[$tabContext] = [$dolibarrContext];
1335 return;
1336 }
1337
1338 if (!in_array($dolibarrContext, $contextTabMapping[$tabContext], true)) {
1339 $contextTabMapping[$tabContext][] = $dolibarrContext;
1340 }
1341 }
1342
1351 public static function getAvailableMemoContextMapping($object = null, $onlyActiveModules = true)
1352 {
1353 global $db,$action, $hookmanager;
1354
1355 $contextTabMapping = [];
1356
1357 // Start Correction of no standard Dolibarr context and special context
1358 if (isModEnabled('propal') || !$onlyActiveModules) {
1359 self::completeMemoContextMapping($contextTabMapping, 'contactcard', 'proposalcontactcard');
1360 }
1361
1362 if (isModEnabled('supplier_order') || !$onlyActiveModules) {
1363 self::completeMemoContextMapping($contextTabMapping, 'contactcard', 'ordersuppliercontactcard');
1364 self::completeMemoContextMapping($contextTabMapping, 'document', 'ordersuppliercarddocument');
1365 self::completeMemoContextMapping($contextTabMapping, 'agenda', 'ordersuppliercardinfo');
1366 self::completeMemoContextMapping($contextTabMapping, 'ordersupplierdispatch');
1367 }
1368
1369 if (isModEnabled('contract') || !$onlyActiveModules) {
1370 self::completeMemoContextMapping($contextTabMapping, 'agenda', 'agendacontract');
1371 }
1372
1373 if (isModEnabled('order') || !$onlyActiveModules) {
1374 self::completeMemoContextMapping($contextTabMapping, 'ordershipmentcard');
1375 }
1376
1377 if (isModEnabled('shipping')) {
1378 self::completeMemoContextMapping($contextTabMapping, 'contactcard', 'shipmentcontactcard');
1379 }
1380
1381 if (isModEnabled('product')) {
1382 self::completeMemoContextMapping($contextTabMapping, 'productpricecard');
1383 self::completeMemoContextMapping($contextTabMapping, 'pricesuppliercard');
1384 self::completeMemoContextMapping($contextTabMapping, 'producttranslationcard');
1385 self::completeMemoContextMapping($contextTabMapping, 'productcompositioncard');
1386 self::completeMemoContextMapping($contextTabMapping, 'stockproductcard');
1387 self::completeMemoContextMapping($contextTabMapping, 'productstatsinvoice');
1388 self::completeMemoContextMapping($contextTabMapping, 'productstatscard');
1389 self::completeMemoContextMapping($contextTabMapping, 'tabproductmarginlist');
1390 }
1391
1392 // End of corrections
1393
1394 // Generate standard context
1395 // TODO : Common contexts such as document, agenda, contact card, and stat, which are used across most Dolibarr objects, are not defined as standard global contexts.
1396 // Instead of having dedicated contexts like global_document, global_agenda, global_contactcard, or global_stat (similar to global_card), pages currently mix global_card with object-specific contexts (e.g. product_document, propal_agenda, etc.).
1397 // This creates inconsistent context handling. These contexts should include both their object-specific context and a proper global context (e.g. global_document or global_agenda) depending on the active tab.
1398 $commonCardContext = ['document', 'agenda', 'contactcard', 'stats']; // for common object
1399 $modules = ['order', 'propal', 'invoice', 'supplier_proposal', 'supplier_order', 'supplier_invoice', 'contract', 'product', 'shipping'];
1400 foreach ($modules as $module) {
1401 if (!isModEnabled($module) && $onlyActiveModules) {
1402 continue;
1403 }
1404
1405 $moduleClean = str_replace('_', '', $module);
1406
1407 foreach ($commonCardContext as $context) {
1408 self::completeMemoContextMapping($contextTabMapping, $context, $moduleClean.$context);
1409 }
1410 }
1411
1412 if (!empty($object) && !empty($object->element)) {
1413 foreach ($commonCardContext as $context) {
1414 self::completeMemoContextMapping($contextTabMapping, $context, $object->element.$context);
1415 }
1416 }
1417
1418 // Need to be at end of tests
1419 self::completeMemoContextMapping($contextTabMapping, 'index');
1420 self::completeMemoContextMapping($contextTabMapping, 'card', 'globalcard');
1421
1422 $staticMemo = new self($db);
1423 $hookmanager->initHooks(array($staticMemo->element.'dao'));
1424 $parameters = array(
1425 'contextTabMapping' => & $contextTabMapping,
1426 'onlyActiveModules' => $onlyActiveModules
1427 );
1428
1429 $reshook = $hookmanager->executeHooks('getAvailableMemoContextMapping', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1430 if ($reshook > 0) {
1431 return $hookmanager->resArray;
1432 }
1433
1434 return $contextTabMapping;
1435 }
1436
1437
1443 public static function getAvailableMemoContext()
1444 {
1445 global $db,$action, $hookmanager;
1446
1447 $contextMapping = self::getAvailableMemoContextMapping();
1448 $list = array_keys($contextMapping);
1449
1450 $staticMemo = new self($db);
1451 $hookmanager->initHooks(array($staticMemo->element.'dao'));
1452 $parameters = array(
1453 'list' => & $list
1454 );
1455 $object = null; // @phan-suppress-next-line PhanPluginConstantVariableNull
1456 $reshook = $hookmanager->executeHooks('getAvailableMemoContext', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1457 if ($reshook > 0) {
1458 $list = $hookmanager->resArray;
1459 }
1460
1461 // Security check
1462 $result = [];
1463 foreach ($list as $k => $v) {
1464 if (!is_string($v)) {
1465 continue;
1466 }
1467 if (strlen($v) > 64) {
1468 continue;
1469 }
1470 if (preg_match('/^[a-zA-Z0-9_]+$/', $k) !== 1) {
1471 continue;
1472 }
1473 $result[] = $v;
1474 }
1475
1476 return $result;
1477 }
1478
1479
1490 public function countArchivedMemoQuery($element_type, $element_id, $context, $id = 0)
1491 {
1492 global $user;
1493
1494 $sql = 'SELECT COUNT(m.rowid) nb
1495 FROM '.MAIN_DB_PREFIX.$this->table_element.' m
1496 WHERE m.element_type = \''.$this->db->escape($element_type).'\'
1497 AND ( m.fk_element = '.(int) $element_id.' OR m.shared_on_element = 1 )
1498 AND m.context_tab = \''.$this->db->escape($context).'\'
1499 AND m.status = '.Memo::STATUS_ARCHIVED.'
1500 AND (m.private = 0 OR m.fk_user_creat = '.(int) $user->id.')
1501 ';
1502
1503 if ((int) $id > 0) {
1504 $sql .= ' AND m.rowid = ' . (int) $id;
1505 }
1506
1507 $obj = $this->db->getRow($sql);
1508 if (!$obj) {
1509 return false;
1510 }
1511
1512 return (int) $obj->nb;
1513 }
1514
1525 public function getMemosQuery($element_type, $element_id, $context, $id = 0)
1526 {
1527 global $user;
1528
1529 $sql = 'SELECT m.rowid, m.quick_note, m.pos_x, m.pos_y, m.pos_w, m.pos_h, m.pos_z, m.color, m.fk_user_creat, m.fk_user_modif, m.shared_on_element, m.date_creation, m.tms, m.private,
1530 mu.pos_x as user_pos_x, mu.pos_y as user_pos_y, mu.pos_w as user_pos_w, mu.pos_h as user_pos_h, mu.pos_z as user_pos_z
1531 FROM '.MAIN_DB_PREFIX.$this->table_element.' m
1532 LEFT JOIN '.MAIN_DB_PREFIX.$this->table_element.'_user mu
1533 ON mu.fk_memo = m.rowid
1534 AND mu.fk_user = '.(int) $user->id.'
1535 WHERE m.element_type = \''.$this->db->escape($element_type).'\'
1536 AND ( m.fk_element = '.(int) $element_id.' OR m.shared_on_element = 1 )
1537 AND m.context_tab = \''.$this->db->escape($context).'\'
1538 AND m.status = '.self::STATUS_VALIDATED.'
1539 AND (COALESCE(m.private, 0) = 0 OR m.fk_user_creat = '.(int) $user->id.')
1540 ';
1541
1542 if ((int) $id > 0) {
1543 $sql .= ' AND m.rowid = ' . (int) $id;
1544 }
1545
1546 $sql .= ' ORDER BY COALESCE(mu.pos_z, m.pos_z) ASC';
1547 return $sql;
1548 }
1549
1550
1560 public function getTemplateMemosQuery($element_type, $context_tab = '', $id = 0)
1561 {
1562 global $user;
1563
1564 $sql = 'SELECT m.rowid, m.quick_note, m.pos_x, m.pos_y, m.pos_w, m.pos_h, m.color, m.pos_z,
1565 m.fk_user_creat, m.fk_user_modif, m.shared_on_element, m.date_creation, m.tms, m.private, m.name_tpl,m.rank_tpl,
1566 mu.pos_x as user_pos_x, mu.pos_y as user_pos_y, mu.pos_w as user_pos_w, mu.pos_h as user_pos_h, mu.pos_z as user_pos_z
1567 FROM '.MAIN_DB_PREFIX.$this->table_element.' m
1568 LEFT JOIN '.MAIN_DB_PREFIX.$this->table_element.'_user mu
1569 ON mu.fk_memo = m.rowid
1570 AND mu.fk_user = '.(int) $user->id.'
1571 WHERE m.element_type IN (\''.$this->db->escape($element_type).'\', \'\')
1572 AND m.status = '.self::STATUS_TPL.'
1573 AND (COALESCE(m.private_tpl, 0) = 0 OR m.fk_user_creat = '.(int) $user->id.')
1574 ';
1575
1576 if (!empty($context_tab)) {
1577 //$sql.= ' AND (m.context_tab = \''.$this->db->escape($context_tab).'\' OR m.element_type = \'\' ) ';
1578 }
1579
1580 if ((int) $id > 0) {
1581 $sql .= ' AND m.rowid = ' . (int) $id;
1582 }
1583
1584 $sql .= " ORDER BY m.rank_tpl DESC, m.rowid ASC "; // Due to multiple type of sort panel rank_tpl is reversed higher is first and rowid ASC to keep last add is last
1585
1586 return $sql;
1587 }
1588
1602 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
1603 {
1604 global $conf, $langs, $form;
1605
1606 if ($key == 'color' && self::checkColor($value)) {
1607 $badgeBase = colorIsLight($value) ? 'badge-light' : 'badge-dark';
1608 return '<span class="badge badge-pill '.$badgeBase.'" style="background-color: '.$value.'" >'.$value.'</span>';
1609 }
1610
1611 if ($key == 'quick_note') {
1612 return nl2br(htmlentities($value));
1613 }
1614
1615 return parent::showOutputField($val, $key, $value, $moreparam, $keysuffix, $keyprefix, $morecss);
1616 }
1617
1624 public static function loadQuickMemoJsInterface($jsConfVars)
1625 {
1626 global $user;
1627
1628 if (!isModEnabled('quickmemo')) {
1629 return false;
1630 }
1631
1632 // DO NOT LOAD TWICE
1633 if (defined('QUICKMEMOJS')) {
1634 return false;
1635 }
1636
1637 if (defined('NOREQUIRETRAN') || empty($user) || empty($user->id) || (int) $user->socid > 0) {
1638 return false;
1639 }
1640
1641 if (!$user->hasRight('quickmemo', 'memo', 'read')) {
1642 return false;
1643 }
1644
1645 $autoResizeFontMin = getDolGlobalFloat('QUICKMEMO_AUTO_RESIZE_MIN_FONT_SIZE', 1);
1646 $autoResizeFontMax = getDolGlobalFloat('QUICKMEMO_AUTO_RESIZE_MAX_FONT_SIZE', 1.4);
1647
1648 // Apply safety limits
1649 $autoResizeFontMin = max($autoResizeFontMin, 0.3);
1650 $autoResizeFontMax = min($autoResizeFontMax, 5);
1651
1652 // Ensure min value is always lower than max value
1653 if ($autoResizeFontMin >= $autoResizeFontMax) {
1654 $autoResizeFontMin = max(0.3, $autoResizeFontMax - 0.1);
1655 }
1656
1657 $defaultJsConfVars = [
1658 'interfaceUrl' => dol_buildpath('quickmemo/interface.php', 1),
1659 'archivesUrl' => dol_buildpath('quickmemo/memo_list.php', 1) . '?mode=kanban&search_status='.Memo::STATUS_ARCHIVED . ($jsConfVars['archivesUrlParams'] ?? ''),
1660 'elementId' => 0,
1661 'elementType' => '',
1662 'context' => null,
1663 'token' => newToken(),
1664 'colors' => Memo::getColorPreset(),
1665 'userReadRight' => $user->hasRight('quickmemo', 'memo', 'read'),
1666 'userWriteRight' => $user->hasRight('quickmemo', 'memo', 'write'),
1667 'userDeleteRight' => $user->hasRight('quickmemo', 'memo', 'delete'),
1668 'autoResizeFontSize' => !getDolGlobalInt('QUICKMEMO_DISABLE_AUTO_RESIZE_FONT_SIZE'),
1669 'autoResizeFontMin' => $autoResizeFontMin,
1670 'autoResizeFontMax' => $autoResizeFontMax,
1671 ];
1672 $jsConfVars = array_merge($defaultJsConfVars, $jsConfVars);
1673
1674 if (!empty($jsConfVars['elementType'])) {
1675 $jsConfVars['shareBtnStatus'] = 1;
1676 }
1677
1678 // LOAD Memo class
1679 print '<link rel="stylesheet" type="text/css" href="'.dol_buildpath('quickmemo/css/memo.css', 1) . '">'."\n";
1680 print '<link rel="stylesheet" type="text/css" href="'.dol_buildpath('quickmemo/css/memo-dialog.css', 1) . '">'."\n";
1681 print '<script nonce="'.getNonce().'" src="'.dol_buildpath('quickmemo/js/QuickMemo.js', 1).'" ></script>'."\n";
1682
1683 print '<script nonce="'.getNonce().'">
1684 document.addEventListener(\'Dolibarr:Ready\', function() {
1685 if(!Dolibarr.checkToolExist(\'quickMemo\')) {
1686 Dolibarr.defineTool(\'quickMemo\', new QuickMemo('.json_encode($jsConfVars).') );
1687 }
1688 });
1689 </script>'."\n";
1690
1691 define('QUICKMEMOJS', true);
1692 return true;
1693 }
1694
1702 public function getJsMemo(User $currentUser)
1703 {
1704 global $langs;
1705
1706 $memo = self::getJsMemoDefault();
1707 $memo->id = $this->id;
1708 $memo->color = $this->color;
1709 $memo->note = $this->quick_note;
1710
1711 $memo->pos_x = (int) $this->pos_x;
1712 $memo->pos_y = (int) $this->pos_y;
1713 $memo->pos_w = (int) $this->pos_w;
1714 $memo->pos_h = (int) $this->pos_h;
1715
1716 if ($currentUser->id != $memo->fk_user_creat) {
1717 // TODO update positions
1718 }
1719
1720 $memo->shared_on_element = $this->shared_on_element;
1721 $memo->private = (int) $this->private;
1722 $memo->date_creation = dol_print_date($this->date_creation, '%d/%m/%Y %H:%M', 'tzuserrel');
1723 $memo->date_change = '';
1724 if (!empty($this->tms) && ((int) $this->date_creation !== (int) $this->tms || (int) $this->fk_user_modif > 0)) {
1725 $memo->date_change = dol_print_date($this->tms, '%d/%m/%Y %H:%M', 'tzuserrel');
1726 }
1727
1728 $memo->fk_user_creat = $this->fk_user_creat;
1729 $memo->user_name = '';
1730 if ((int) $this->fk_user_creat > 0) {
1731 $userCreate = new User($this->db);
1732 if ($userCreate->fetch((int) $this->fk_user_creat) > 0) {
1733 $memo->user_name = $userCreate->getFullName($langs);
1734 }
1735 }
1736
1737 $memo->fk_user_modif = $this->fk_user_modif;
1738 $memo->user_change_name = '';
1739 if ((int) $this->fk_user_modif > 0) {
1740 $userMod = new User($this->db);
1741 if ($userMod->fetch((int) $this->fk_user_modif) > 0) {
1742 $memo->user_change_name = $userMod->getFullName($langs);
1743 }
1744 }
1745
1746 return $memo;
1747 }
1748
1754 public static function getJsMemoDefault()
1755 {
1756 $memo = new stdClass();
1757 $memo->id = null;
1758 $memo->color = null;
1759 $memo->note = null;
1760 $memo->pos_x = 0;
1761 $memo->pos_y = 0;
1762 $memo->pos_w = 0;
1763 $memo->pos_h = 0;
1764 $memo->shared_on_element = 0;
1765 $memo->private = 0;
1766 $memo->date_creation = '';
1767 $memo->date_change = '';
1768 $memo->fk_user_creat = null;
1769 $memo->user_name = '';
1770 $memo->fk_user_modif = null;
1771 $memo->user_change_name = '';
1772 return $memo;
1773 }
1774}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
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,...
setErrorsFromObject($object)
setErrorsFromObject
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.
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.
Class to manage Dolibarr database access.
Class for Memo.
$fields
'type' field format: 'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortf...
static getAvailableMemoContext()
Get available memo context.
createFromClone(User $user, $fromid)
Clone an object into another one.
create(User $user, $notrigger=0)
Create object into database.
static getAvailableMemoContextMapping($object=null, $onlyActiveModules=true)
Get available memo context.
fetchAll($sortorder='', $sortfield='', $limit=1000, $offset=0, string $filter='', $filtermode='AND')
Load list of objects in memory from the database.
getJsMemo(User $currentUser)
Get JS memo.
update(User $user, $notrigger=0)
Update object into database.
static getJsMemoDefault()
Get JS memo default.
getMemosQuery($element_type, $element_id, $context, $id=0)
Get memos query.
fetch($id, $ref=null, $noextrafields=0, $nolines=0)
Load object in memory from the database.
__construct(DoliDB $db)
Constructor.
fetchLines($noextrafields=0)
Load object lines in memory from the database.
static getMemoContext($context, $object=null)
Get memo context.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
validate($user, $notrigger=0)
Validate object.
updatePosition(User $user, int $x, int $y, int $w, int $h, int $z)
Update position of a memo for a specific user.
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:168
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_now($mode='gmt')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
Abort invoice creation with a given error message.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133