Passed
Push — EXTRACT_CLASSES ( 0382f2...c25e41 )
by Rafael
52:18
created

TimeSpent   F

Complexity

Total Complexity 153

Size/Duplication

Total Lines 977
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 411
dl 0
loc 977
rs 2
c 0
b 0
f 0
wmc 153

22 Methods

Rating   Name   Duplication   Size   Complexity  
A update() 0 3 1
A fetch() 0 5 1
B getNextNumRef() 0 47 9
A initAsSpecimen() 0 7 1
A info() 0 24 4
A cancel() 0 15 2
A getLibStatut() 0 3 1
F validate() 0 122 22
A setDraft() 0 15 2
A getTooltipContentArray() 0 16 3
A LibStatut() 0 21 4
A deleteLine() 0 8 2
F createFromClone() 0 83 21
F getNomUrl() 0 110 38
A delete() 0 3 1
A create() 0 7 1
C __construct() 0 35 14
A generateDocument() 0 26 6
B fetchAll() 0 55 9
A reopen() 0 15 2
A getLabelStatus() 0 3 1
B getKanbanView() 0 31 8

How to fix   Complexity   

Complex Class

Complex classes like TimeSpent often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TimeSpent, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2017       Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2023-2024  Frédéric France             <[email protected]>
5
 * Copyright (C) 2023       Gauthier VERDOL       	    <[email protected]>
6
 * Copyright (C) 2024		MDW							<[email protected]>
7
 * Copyright (C) 2024       Rafael San José             <[email protected]>
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 */
22
23
namespace Dolibarr\Code\Core\Classes;
24
25
/**
26
 * \file        htdocs/core/class/timespent.class.php
27
 * \ingroup     timespent
28
 * \brief       This file is a CRUD class file for TimeSpent (Create/Read/Update/Delete)
29
 */
30
31
// Put here all includes required by your class file
32
use Dolibarr\Core\Base\CommonObject;
33
//require_once constant('DOL_DOCUMENT_ROOT') . '/societe/class/societe.class.php';
34
//require_once constant('DOL_DOCUMENT_ROOT') . '/product/class/product.class.php';
35
36
/**
37
 * Class for TimeSpent
38
 */
39
class TimeSpent extends CommonObject
40
{
41
    /**
42
     * @var string ID of module.
43
     */
44
    public $module = 'timespent';
45
46
    /**
47
     * @var string ID to identify managed object.
48
     */
49
    public $element = 'timespent';
50
51
    /**
52
     * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management.
53
     */
54
    public $table_element = 'element_time';
55
56
    /**
57
     * @var string String with name of icon for timespent. Must be a 'fa-xxx' fontawesome code (or 'fa-xxx_fa_color_size') or 'timespent@timespent' if picto is file 'img/object_timespent.png'.
58
     */
59
    public $picto = 'fa-file';
60
61
62
    const STATUS_DRAFT = 0;
63
    const STATUS_VALIDATED = 1;
64
    const STATUS_CANCELED = 9;
65
66
67
    /**
68
     *  'type' field format:
69
     *      'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',
70
     *      'select' (list of values are in 'options'),
71
     *      'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]',
72
     *      'chkbxlst:...',
73
     *      'varchar(x)',
74
     *      'text', 'text:none', 'html',
75
     *      'double(24,8)', 'real', 'price',
76
     *      'date', 'datetime', 'timestamp', 'duration',
77
     *      'boolean', 'checkbox', 'radio', 'array',
78
     *      'mail', 'phone', 'url', 'password', 'ip'
79
     *      Note: Filter must be a Dolibarr filter syntax string. Example: "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.status:!=:0) or (t.nature:is:NULL)"
80
     *  'label' the translation key.
81
     *  'picto' is code of a picto to show before value in forms
82
     *  'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM' or 'isModEnabled("multicurrency")' ...)
83
     *  'position' is the sort order of field.
84
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
85
     *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
86
     *  'noteditable' says if field is not editable (1 or 0)
87
     *  'alwayseditable' says if field can be modified also when status is not draft ('1' or '0')
88
     *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
89
     *  'index' if we want an index in database.
90
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
91
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
92
     *  'isameasure' must be set to 1 or 2 if field can be used for measure. Field type must be summable like integer or double(24,8). Use 1 in most cases, or 2 if you don't want to see the column total into list (for example for percentage)
93
     *  'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'css'=>'minwidth300 maxwidth500 widthcentpercentminusx', 'cssview'=>'wordbreak', 'csslist'=>'tdoverflowmax200'
94
     *  'help' and 'helplist' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
95
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
96
     *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
97
     *  'arrayofkeyval' to set a list of values if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel"). Note that type can be 'integer' or 'varchar'
98
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
99
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
100
     *  'validate' is 1 if need to validate with $this->validateField()
101
     *  'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value)
102
     *
103
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
104
     */
105
106
    // BEGIN MODULEBUILDER PROPERTIES
107
    /**
108
     * @var array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}>  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string,array{type:...ring>,comment?:string}> at position 16 could not be parsed: Expected '}' at position 16, but found 'int'.
Loading history...
109
     */
110
    public $fields = array(
111
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
112
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 17, 'notnull' => 0, 'visible' => -2,),
113
        'import_key' => array('type' => 'varchar(14)', 'label' => 'import_key', 'enabled' => 1, 'position' => 15, 'notnull' => 0, 'visible' => -1,),
114
        'fk_element' => array('type' => 'integer', 'label' => 'fk_element', 'enabled' => 1, 'position' => 2, 'notnull' => 1, 'visible' => -1,),
115
        'elementtype' => array('type' => 'varchar(32)', 'label' => 'elementtype', 'enabled' => 1, 'position' => 3, 'notnull' => 1, 'visible' => -1,),
116
        'element_date' => array('type' => 'date', 'label' => 'element_date', 'enabled' => 1, 'position' => 4, 'notnull' => 0, 'visible' => -1,),
117
        'element_datehour' => array('type' => 'datetime', 'label' => 'element_datehour', 'enabled' => 1, 'position' => 5, 'notnull' => 0, 'visible' => -1,),
118
        'element_date_withhour' => array('type' => 'integer', 'label' => 'element_date_withhour', 'enabled' => 1, 'position' => 6, 'notnull' => 0, 'visible' => -1,),
119
        'element_duration' => array('type' => 'double', 'label' => 'element_duration', 'enabled' => 1, 'position' => 7, 'notnull' => 0, 'visible' => -1,),
120
        'fk_product' => array('type' => 'integer', 'label' => 'fk_product', 'enabled' => 1, 'position' => 8, 'notnull' => 0, 'visible' => -1,),
121
        'fk_user' => array('type' => 'integer', 'label' => 'fk_user', 'enabled' => 1, 'position' => 9, 'notnull' => 0, 'visible' => -1,),
122
        'thm' => array('type' => 'double(24,8)', 'label' => 'thm', 'enabled' => 1, 'position' => 10, 'notnull' => 0, 'visible' => -1,),
123
        'invoice_id' => array('type' => 'integer', 'label' => 'invoice_id', 'enabled' => 1, 'position' => 11, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
124
        'invoice_line_id' => array('type' => 'integer', 'label' => 'invoice_line_id', 'enabled' => 1, 'position' => 12, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
125
        'intervention_id' => array('type' => 'integer', 'label' => 'intervention_id', 'enabled' => 1, 'position' => 13, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
126
        'intervention_line_id' => array('type' => 'integer', 'label' => 'intervention_line_id', 'enabled' => 1, 'position' => 14, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
127
        'datec' => array('type' => 'datetime', 'label' => 'datec', 'enabled' => 1, 'position' => 16, 'notnull' => 0, 'visible' => -1,),
128
        'note' => array('type' => 'text', 'label' => 'note', 'enabled' => 1, 'position' => 18, 'notnull' => 0, 'visible' => -1,),
129
    );
130
    public $rowid;
131
    public $import_key;
132
    public $fk_element;
133
    public $elementtype;
134
    public $element_date;
135
    public $element_datehour;
136
    public $element_date_withhour;
137
    public $element_duration;
138
    public $fk_product;
139
    public $fk_user;
140
    public $thm;
141
    public $invoice_id;
142
    public $invoice_line_id;
143
    public $intervention_id;
144
    public $intervention_line_id;
145
    public $datec;
146
    public $note;
147
    // END MODULEBUILDER PROPERTIES
148
149
    /**
150
     * Constructor
151
     *
152
     * @param DoliDB $db Database handler
153
     */
154
    public function __construct(DoliDB $db)
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Core\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
155
    {
156
        global $langs;
157
158
        $this->db = $db;
159
160
        $this->ismultientitymanaged = 0;
161
        $this->isextrafieldmanaged = 0;
162
163
        if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid']) && !empty($this->fields['ref'])) {
164
            $this->fields['rowid']['visible'] = 0;
165
        }
166
        if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
167
            $this->fields['entity']['enabled'] = 0;
168
        }
169
170
        // Example to show how to set values of fields definition dynamically
171
        /*if ($user->rights->timespent->timespent->read) {
172
            $this->fields['myfield']['visible'] = 1;
173
            $this->fields['myfield']['noteditable'] = 0;
174
        }*/
175
176
        // Unset fields that are disabled
177
        foreach ($this->fields as $key => $val) {
178
            if (isset($val['enabled']) && empty($val['enabled'])) {
179
                unset($this->fields[$key]);
180
            }
181
        }
182
183
        // Translate some data of arrayofkeyval
184
        if (is_object($langs)) {
185
            foreach ($this->fields as $key => $val) {
186
                if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
187
                    foreach ($val['arrayofkeyval'] as $key2 => $val2) {
188
                        $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
189
                    }
190
                }
191
            }
192
        }
193
    }
194
195
    /**
196
     * Create object into database
197
     *
198
     * @param  User $user      User that creates
199
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
200
     * @return int             Return integer <0 if KO, Id of created object if OK
201
     */
202
    public function create(User $user, $notrigger = 0)
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\Core\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
203
    {
204
        $resultcreate = $this->createCommon($user, $notrigger);
205
206
        //$resultvalidate = $this->validate($user, $notrigger);
207
208
        return $resultcreate;
209
    }
210
211
    /**
212
     * Clone an object into another one
213
     *
214
     * @param   User    $user       User that creates
215
     * @param   int     $fromid     Id of object to clone
216
     * @return  mixed               New object created, <0 if KO
217
     */
218
    public function createFromClone(User $user, $fromid)
219
    {
220
        global $langs, $extrafields;
221
        $error = 0;
222
223
        dol_syslog(__METHOD__, LOG_DEBUG);
224
225
        $object = new self($this->db);
226
227
        $this->db->begin();
228
229
        // Load source object
230
        $result = $object->fetchCommon($fromid);
231
232
        // Reset some properties
233
        unset($object->id);
234
        unset($object->fk_user_creat);
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$fk_user_creat has been deprecated: Use $user_creation_id ( Ignorable by Annotation )

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

234
        unset(/** @scrutinizer ignore-deprecated */ $object->fk_user_creat);

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

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

Loading history...
235
        unset($object->import_key);
236
237
        // Clear fields
238
        if (property_exists($object, 'ref')) {
239
            $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_" . $object->ref : $this->fields['ref']['default'];
240
        }
241
        if (property_exists($object, 'label')) {
242
            $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf") . " " . $object->label : $this->fields['label']['default'];
243
        }
244
        if (property_exists($object, 'status')) {
245
            $object->status = self::STATUS_DRAFT;
246
        }
247
        if (property_exists($object, 'date_creation')) {
248
            $object->date_creation = dol_now();
249
        }
250
        if (property_exists($object, 'date_modification')) {
251
            $object->date_modification = null;
252
        }
253
        // ...
254
        // Clear extrafields that are unique
255
        if (is_array($object->array_options) && count($object->array_options) > 0) {
256
            $extrafields->fetch_name_optionals_label($this->table_element);
257
            foreach ($object->array_options as $key => $option) {
258
                $shortkey = preg_replace('/options_/', '', $key);
259
                if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) {
260
                    //var_dump($key);
261
                    //var_dump($clonedObj->array_options[$key]); exit;
262
                    unset($object->array_options[$key]);
263
                }
264
            }
265
        }
266
267
        // Create clone
268
        $object->context['createfromclone'] = 'createfromclone';
269
        $result = $object->createCommon($user);
270
        if ($result < 0) {
271
            $error++;
272
            $this->error = $object->error;
273
            $this->errors = $object->errors;
274
        }
275
276
        if (!$error) {
277
            // copy internal contacts
278
            if ($this->copy_linked_contact($object, 'internal') < 0) {
279
                $error++;
280
            }
281
        }
282
283
        if (!$error) {
284
            // copy external contacts if same company
285
            if (!empty($object->socid) && property_exists($this, 'fk_soc') && $this->fk_soc == $object->socid) {
286
                if ($this->copy_linked_contact($object, 'external') < 0) {
287
                    $error++;
288
                }
289
            }
290
        }
291
292
        unset($object->context['createfromclone']);
293
294
        // End
295
        if (!$error) {
296
            $this->db->commit();
297
            return $object;
298
        } else {
299
            $this->db->rollback();
300
            return -1;
301
        }
302
    }
303
304
    /**
305
     * Load object in memory from the database
306
     *
307
     * @param int    $id   Id object
308
     * @param string $ref  Ref
309
     * @return int         Return integer <0 if KO, 0 if not found, >0 if OK
310
     */
311
    public function fetch($id, $ref = null)
312
    {
313
        $result = $this->fetchCommon($id, $ref);
314
315
        return $result;
316
    }
317
318
    /**
319
     * Load list of objects in memory from the database.
320
     *
321
     * @param  string       $sortorder      Sort Order
322
     * @param  string       $sortfield      Sort field
323
     * @param  int          $limit          limit
324
     * @param  int          $offset         Offset
325
     * @param  string       $filter         Filter as an Universal Search string.
326
     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')'
327
     * @param  string       $filtermode     No more used
328
     * @return array|int                    int <0 if KO, array of pages if OK
329
     */
330
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND')
331
    {
332
        global $conf;
333
334
        dol_syslog(__METHOD__, LOG_DEBUG);
335
336
        $records = array();
337
338
        $sql = "SELECT ";
339
        $sql .= $this->getFieldList('t');
340
        $sql .= " FROM " . $this->db->prefix() . $this->table_element . " as t";
341
        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
342
            $sql .= " WHERE t.entity IN (" . getEntity($this->element) . ")";
343
        } else {
344
            $sql .= " WHERE 1 = 1";
345
        }
346
347
        // Manage filter
348
        $errormessage = '';
349
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
350
        if ($errormessage) {
351
            $this->errors[] = $errormessage;
352
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
353
            return -1;
354
        }
355
356
        if (!empty($sortfield)) {
357
            $sql .= $this->db->order($sortfield, $sortorder);
358
        }
359
        if (!empty($limit)) {
360
            $sql .= $this->db->plimit($limit, $offset);
361
        }
362
363
        $resql = $this->db->query($sql);
364
        if ($resql) {
365
            $num = $this->db->num_rows($resql);
366
            $i = 0;
367
            while ($i < ($limit ? min($limit, $num) : $num)) {
368
                $obj = $this->db->fetch_object($resql);
369
370
                $record = new self($this->db);
371
                $record->setVarsFromFetchObj($obj);
372
373
                $records[$record->id] = $record;
374
375
                $i++;
376
            }
377
            $this->db->free($resql);
378
379
            return $records;
380
        } else {
381
            $this->errors[] = 'Error ' . $this->db->lasterror();
382
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
383
384
            return -1;
385
        }
386
    }
387
388
    /**
389
     * Update object into database
390
     *
391
     * @param  User $user      User that modifies
392
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
393
     * @return int             Return integer <0 if KO, >0 if OK
394
     */
395
    public function update(User $user, $notrigger = 0)
396
    {
397
        return $this->updateCommon($user, $notrigger);
398
    }
399
400
    /**
401
     * Delete object in database
402
     *
403
     * @param User  $user       User that deletes
404
     * @param int   $notrigger  0=launch triggers, 1=disable triggers
405
     * @return int              Return integer <0 if KO, >0 if OK
406
     */
407
    public function delete(User $user, $notrigger = 0)
408
    {
409
        return $this->deleteCommon($user, $notrigger);
410
        //return $this->deleteCommon($user, $notrigger, 1);
411
    }
412
413
    /**
414
     *  Delete a line of object in database
415
     *
416
     *  @param  User    $user       User that delete
417
     *  @param  int     $idline     Id of line to delete
418
     *  @param  int     $notrigger  0=launch triggers after, 1=disable triggers
419
     *  @return int                 >0 if OK, <0 if KO
420
     */
421
    public function deleteLine(User $user, $idline, $notrigger = 0)
422
    {
423
        if ($this->status < 0) {
424
            $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
425
            return -2;
426
        }
427
428
        return $this->deleteLineCommon($user, $idline, $notrigger);
429
    }
430
431
432
    /**
433
     *  Validate object
434
     *
435
     *  @param      User    $user           User making status change
436
     *  @param      int     $notrigger      1=Does not execute triggers, 0= execute triggers
437
     *  @return     int                     Return integer <=0 if OK, 0=Nothing done, >0 if KO
438
     */
439
    public function validate($user, $notrigger = 0)
440
    {
441
        global $conf, $langs;
442
443
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
444
445
        $error = 0;
446
447
        // Protection
448
        if ($this->status == self::STATUS_VALIDATED) {
449
            dol_syslog(get_class($this) . "::validate action abandoned: already validated", LOG_WARNING);
450
            return 0;
451
        }
452
453
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent->write))
454
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent->timespent_advance->validate))))
455
         {
456
         $this->error='NotEnoughPermissions';
457
         dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
458
         return -1;
459
         }*/
460
461
        $now = dol_now();
462
463
        $this->db->begin();
464
465
        // Define new ref
466
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
467
            $num = $this->getNextNumRef();
468
        } else {
469
            $num = $this->ref;
470
        }
471
        $this->newref = $num;
472
473
        if (!empty($num)) {
474
            // Validate
475
            $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
476
            $sql .= " SET ref = '" . $this->db->escape($num) . "',";
477
            $sql .= " status = " . self::STATUS_VALIDATED;
478
            if (!empty($this->fields['date_validation'])) {
479
                $sql .= ", date_validation = '" . $this->db->idate($now) . "'";
480
            }
481
            if (!empty($this->fields['fk_user_valid'])) {
482
                $sql .= ", fk_user_valid = " . ((int) $user->id);
483
            }
484
            $sql .= " WHERE rowid = " . ((int) $this->id);
485
486
            dol_syslog(get_class($this) . "::validate()", LOG_DEBUG);
487
            $resql = $this->db->query($sql);
488
            if (!$resql) {
489
                dol_print_error($this->db);
490
                $this->error = $this->db->lasterror();
491
                $error++;
492
            }
493
494
            if (!$error && !$notrigger) {
495
                // Call trigger
496
                $result = $this->call_trigger('timespent_VALIDATE', $user);
497
                if ($result < 0) {
498
                    $error++;
499
                }
500
                // End call triggers
501
            }
502
        }
503
504
        if (!$error) {
505
            $this->oldref = $this->ref;
506
507
            // Rename directory if dir was a temporary ref
508
            if (preg_match('/^[\(]?PROV/i', $this->ref)) {
509
                // Now we rename also files into index
510
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'timespent/" . $this->db->escape($this->newref) . "'";
511
                $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'timespent/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
512
                $resql = $this->db->query($sql);
513
                if (!$resql) {
514
                    $error++;
515
                    $this->error = $this->db->lasterror();
516
                }
517
                $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'timespent/" . $this->db->escape($this->newref) . "'";
518
                $sql .= " WHERE filepath = 'timespent/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
519
                $resql = $this->db->query($sql);
520
                if (!$resql) {
521
                    $error++;
522
                    $this->error = $this->db->lasterror();
523
                }
524
525
                // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
526
                $oldref = dol_sanitizeFileName($this->ref);
527
                $newref = dol_sanitizeFileName($num);
528
                $dirsource = $conf->timespent->dir_output . '/timespent/' . $oldref;
529
                $dirdest = $conf->timespent->dir_output . '/timespent/' . $newref;
530
                if (!$error && file_exists($dirsource)) {
531
                    dol_syslog(get_class($this) . "::validate() rename dir " . $dirsource . " into " . $dirdest);
532
533
                    if (@rename($dirsource, $dirdest)) {
534
                        dol_syslog("Rename ok");
535
                        // Rename docs starting with $oldref with $newref
536
                        $listoffiles = dol_dir_list($conf->timespent->dir_output . '/timespent/' . $newref, 'files', 1, '^' . preg_quote($oldref, '/'));
537
                        foreach ($listoffiles as $fileentry) {
538
                            $dirsource = $fileentry['name'];
539
                            $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
540
                            $dirsource = $fileentry['path'] . '/' . $dirsource;
541
                            $dirdest = $fileentry['path'] . '/' . $dirdest;
542
                            @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

542
                            /** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
543
                        }
544
                    }
545
                }
546
            }
547
        }
548
549
        // Set new ref and current status
550
        if (!$error) {
551
            $this->ref = $num;
552
            $this->status = self::STATUS_VALIDATED;
553
        }
554
555
        if (!$error) {
556
            $this->db->commit();
557
            return 1;
558
        } else {
559
            $this->db->rollback();
560
            return -1;
561
        }
562
    }
563
564
565
    /**
566
     *  Set draft status
567
     *
568
     *  @param  User    $user           Object user that modify
569
     *  @param  int     $notrigger      1=Does not execute triggers, 0=Execute triggers
570
     *  @return int                     Return integer <0 if KO, >0 if OK
571
     */
572
    public function setDraft($user, $notrigger = 0)
573
    {
574
        // Protection
575
        if ($this->status <= self::STATUS_DRAFT) {
576
            return 0;
577
        }
578
579
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->write))
580
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent_advance->validate))))
581
         {
582
         $this->error='Permission denied';
583
         return -1;
584
         }*/
585
586
        return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'timespent_UNVALIDATE');
587
    }
588
589
    /**
590
     *  Set cancel status
591
     *
592
     *  @param  User    $user           Object user that modify
593
     *  @param  int     $notrigger      1=Does not execute triggers, 0=Execute triggers
594
     *  @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
595
     */
596
    public function cancel($user, $notrigger = 0)
597
    {
598
        // Protection
599
        if ($this->status != self::STATUS_VALIDATED) {
600
            return 0;
601
        }
602
603
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->write))
604
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent_advance->validate))))
605
         {
606
         $this->error='Permission denied';
607
         return -1;
608
         }*/
609
610
        return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'timespent_CANCEL');
611
    }
612
613
    /**
614
     *  Set back to validated status
615
     *
616
     *  @param  User    $user           Object user that modify
617
     *  @param  int     $notrigger      1=Does not execute triggers, 0=Execute triggers
618
     *  @return int                     Return integer <0 if KO, 0=Nothing done, >0 if OK
619
     */
620
    public function reopen($user, $notrigger = 0)
621
    {
622
        // Protection
623
        if ($this->status == self::STATUS_VALIDATED) {
624
            return 0;
625
        }
626
627
        /*if (! ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->write))
628
         || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->timespent->timespent_advance->validate))))
629
         {
630
         $this->error='Permission denied';
631
         return -1;
632
         }*/
633
634
        return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'timespent_REOPEN');
635
    }
636
637
    /**
638
     * getTooltipContentArray
639
     * @param array $params params to construct tooltip data
640
     * @since v18
641
     * @return array
642
     */
643
    public function getTooltipContentArray($params)
644
    {
645
        global $conf, $langs, $user;
646
647
        $datas = [];
648
649
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
650
            return ['optimize' => $langs->trans("ShowTimeSpent")];
651
        }
652
        $datas['picto'] = img_picto('', $this->picto) . ' <u>' . $langs->trans("TimeSpent") . '</u>';
653
        if (isset($this->status)) {
654
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
655
        }
656
        $datas['ref'] .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
657
658
        return $datas;
659
    }
660
661
    /**
662
     *  Return a link to the object card (with optionally the picto)
663
     *
664
     *  @param  int     $withpicto                  Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto)
665
     *  @param  string  $option                     On what the link point to ('nolink', ...)
666
     *  @param  int     $notooltip                  1=Disable tooltip
667
     *  @param  string  $morecss                    Add more css on link
668
     *  @param  int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
669
     *  @return string                              String with URL
670
     */
671
    public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
672
    {
673
        global $conf, $langs, $hookmanager;
674
675
        if (!empty($conf->dol_no_mouse_hover)) {
676
            $notooltip = 1; // Force disable tooltips
677
        }
678
679
        $result = '';
680
        $params = [
681
            'id' => $this->id,
682
            'objecttype' => $this->element,
683
            'option' => $option,
684
        ];
685
        $classfortooltip = 'classfortooltip';
686
        $dataparams = '';
687
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
688
            $classfortooltip = 'classforajaxtooltip';
689
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
690
            $label = '';
691
        } else {
692
            $label = implode($this->getTooltipContentArray($params));
693
        }
694
695
        $url = dol_buildpath('/timespent/timespent_card.php', 1) . '?id=' . $this->id;
696
697
        if ($option != 'nolink') {
698
            // Add param to save lastsearch_values or not
699
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
700
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
701
                $add_save_lastsearch_values = 1;
702
            }
703
            if ($url && $add_save_lastsearch_values) {
704
                $url .= '&save_lastsearch_values=1';
705
            }
706
        }
707
708
        $linkclose = '';
709
        if (empty($notooltip)) {
710
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
711
                $label = $langs->trans("ShowTimeSpent");
712
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
713
            }
714
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
715
            $linkclose .= $dataparams . ' class="' . $classfortooltip . ($morecss ? ' ' . $morecss : '') . '"';
716
        } else {
717
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
718
        }
719
720
        if ($option == 'nolink' || empty($url)) {
721
            $linkstart = '<span';
722
        } else {
723
            $linkstart = '<a href="' . $url . '"';
724
        }
725
        $linkstart .= $linkclose . '>';
726
        if ($option == 'nolink' || empty($url)) {
727
            $linkend = '</span>';
728
        } else {
729
            $linkend = '</a>';
730
        }
731
732
        $result .= $linkstart;
733
734
        if (empty($this->showphoto_on_popup)) {
735
            if ($withpicto) {
736
                $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);
737
            }
738
        } else {
739
            if ($withpicto) {
740
                require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
741
742
                list($class, $module) = explode('@', $this->picto);
743
                $upload_dir = $conf->$module->multidir_output[$conf->entity] . "/$class/" . dol_sanitizeFileName($this->ref);
744
                $filearray = dol_dir_list($upload_dir, "files");
745
                $filename = $filearray[0]['name'];
746
                if (!empty($filename)) {
747
                    $pospoint = strpos($filearray[0]['name'], '.');
748
749
                    $pathtophoto = $class . '/' . $this->ref . '/thumbs/' . substr($filename, 0, $pospoint) . '_mini' . substr($filename, $pospoint);
750
                    if (!getDolGlobalString(strtoupper($module . '_' . $class) . '_FORMATLISTPHOTOSASUSERS')) {
751
                        $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo' . $module . '" alt="No photo" border="0" src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div></div>';
752
                    } else {
753
                        $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="' . constant('BASE_URL') . '/viewimage.php?modulepart=' . $module . '&entity=' . $conf->entity . '&file=' . urlencode($pathtophoto) . '"></div>';
754
                    }
755
756
                    $result .= '</div>';
757
                } else {
758
                    $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . 'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
759
                }
760
            }
761
        }
762
763
        if ($withpicto != 2) {
764
            $result .= $this->ref;
765
        }
766
767
        $result .= $linkend;
768
        //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
769
770
        global $action, $hookmanager;
771
        $hookmanager->initHooks(array($this->element . 'dao'));
772
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
773
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
774
        if ($reshook > 0) {
775
            $result = $hookmanager->resPrint;
776
        } else {
777
            $result .= $hookmanager->resPrint;
778
        }
779
780
        return $result;
781
    }
782
783
    /**
784
     *  Return a thumb for kanban views
785
     *
786
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
787
     *  @param      array       $arraydata              Array of data
788
     *  @return     string                              HTML Code for Kanban thumb.
789
     */
790
    public function getKanbanView($option = '', $arraydata = null)
791
    {
792
        global $conf, $langs;
793
794
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
795
796
        $return = '<div class="box-flex-item box-flex-grow-zero">';
797
        $return .= '<div class="info-box info-box-sm">';
798
        $return .= '<span class="info-box-icon bg-infobox-action">';
799
        $return .= img_picto('', $this->picto);
800
        $return .= '</span>';
801
        $return .= '<div class="info-box-content">';
802
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref) . '</span>';
803
        if ($selected >= 0) {
804
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
805
        }
806
        if (property_exists($this, 'label')) {
807
            $return .= ' <div class="inline-block opacitymedium valignmiddle tdoverflowmax100">' . $this->label . '</div>';
808
        }
809
        if (property_exists($this, 'amount')) {
810
            $return .= '<br>';
811
            $return .= '<span class="info-box-label amount">' . price($this->amount, 0, $langs, 1, -1, -1, $conf->currency) . '</span>';
0 ignored issues
show
Bug Best Practice introduced by
The property amount does not exist on Dolibarr\Code\Core\Classes\TimeSpent. Since you implemented __get, consider adding a @property annotation.
Loading history...
812
        }
813
        if (method_exists($this, 'getLibStatut')) {
814
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(5) . '</div>';
815
        }
816
        $return .= '</div>';
817
        $return .= '</div>';
818
        $return .= '</div>';
819
820
        return $return;
821
    }
822
823
    /**
824
     *  Return the label of the status
825
     *
826
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
827
     *  @return string                 Label of status
828
     */
829
    public function getLabelStatus($mode = 0)
830
    {
831
        return $this->LibStatut($this->status, $mode);
832
    }
833
834
    /**
835
     *  Return the label of the status
836
     *
837
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
838
     *  @return string                 Label of status
839
     */
840
    public function getLibStatut($mode = 0)
841
    {
842
        return $this->LibStatut($this->status, $mode);
843
    }
844
845
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
846
    /**
847
     *  Return the status
848
     *
849
     *  @param  int     $status        Id status
850
     *  @param  int     $mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
851
     *  @return string                 Label of status
852
     */
853
    public function LibStatut($status, $mode = 0)
854
    {
855
		// phpcs:enable
856
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
857
            global $langs;
858
            //$langs->load("timespent@timespent");
859
            $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
860
            $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
861
            $this->labelStatus[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
862
            $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('Draft');
863
            $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('Enabled');
864
            $this->labelStatusShort[self::STATUS_CANCELED] = $langs->transnoentitiesnoconv('Disabled');
865
        }
866
867
        $statusType = 'status' . $status;
868
        //if ($status == self::STATUS_VALIDATED) $statusType = 'status1';
869
        if ($status == self::STATUS_CANCELED) {
870
            $statusType = 'status6';
871
        }
872
873
        return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
874
    }
875
876
    /**
877
     *  Load the info information in the object
878
     *
879
     *  @param  int     $id       Id of object
880
     *  @return void
881
     */
882
    public function info($id)
883
    {
884
        $sql = "SELECT rowid,";
885
        $sql .= " date_creation as datec, tms as datem,";
886
        $sql .= " fk_user_creat, fk_user_modif";
887
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as t";
888
        $sql .= " WHERE t.rowid = " . ((int) $id);
889
890
        $result = $this->db->query($sql);
891
        if ($result) {
892
            if ($this->db->num_rows($result)) {
893
                $obj = $this->db->fetch_object($result);
894
895
                $this->id = $obj->rowid;
896
897
                $this->user_creation_id = $obj->fk_user_creat;
898
                $this->user_modification_id = $obj->fk_user_modif;
899
                $this->date_creation     = $this->db->jdate($obj->datec);
900
                $this->date_modification = empty($obj->datem) ? '' : $this->db->jdate($obj->datem);
901
            }
902
903
            $this->db->free($result);
904
        } else {
905
            dol_print_error($this->db);
906
        }
907
    }
908
909
    /**
910
     * Initialise object with example values
911
     * Id must be 0 if object instance is a specimen
912
     *
913
     * @return int
914
     */
915
    public function initAsSpecimen()
916
    {
917
        // Set here init that are not common fields
918
        // $this->property1 = ...
919
        // $this->property2 = ...
920
921
        return $this->initAsSpecimenCommon();
922
    }
923
924
    /**
925
     *  Returns the reference to the following non used object depending on the active numbering module.
926
     *
927
     *  @return string              Object free reference
928
     */
929
    public function getNextNumRef()
930
    {
931
        global $langs, $conf;
932
        $langs->load("timespent@timespent");
933
934
        if (!getDolGlobalString('TIMESPENT_timespent_ADDON')) {
935
            $conf->global->TIMESPENT_timespent_ADDON = 'mod_timespent_standard';
936
        }
937
938
        if (getDolGlobalString('TIMESPENT_timespent_ADDON')) {
939
            $mybool = false;
940
941
            $file = getDolGlobalString('TIMESPENT_timespent_ADDON') . ".php";
942
            $classname = getDolGlobalString('TIMESPENT_timespent_ADDON');
943
944
            // Include file with class
945
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
946
            foreach ($dirmodels as $reldir) {
947
                $dir = dol_buildpath($reldir . "core/modules/timespent/");
948
949
                // Load file with numbering class (if found)
950
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
951
            }
952
953
            if ($mybool === false) {
954
                dol_print_error(null, "Failed to include file " . $file);
955
                return '';
956
            }
957
958
            if (class_exists($classname)) {
959
                $obj = new $classname();
960
                $numref = $obj->getNextValue($this);
961
962
                if ($numref != '' && $numref != '-1') {
963
                    return $numref;
964
                } else {
965
                    $this->error = $obj->error;
966
                    //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
967
                    return "";
968
                }
969
            } else {
970
                print $langs->trans("Error") . " " . $langs->trans("ClassNotFound") . ' ' . $classname;
971
                return "";
972
            }
973
        } else {
974
            print $langs->trans("ErrorNumberingModuleNotSetup", $this->element);
975
            return "";
976
        }
977
    }
978
979
    /**
980
     *  Create a document onto disk according to template module.
981
     *
982
     *  @param      string      $modele         Force template to use ('' to not force)
983
     *  @param      Translate   $outputlangs    object lang a utiliser pour traduction
984
     *  @param      int         $hidedetails    Hide details of lines
985
     *  @param      int         $hidedesc       Hide description
986
     *  @param      int         $hideref        Hide ref
987
     *  @param      null|array  $moreparams     Array to provide more information
988
     *  @return     int                         0 if KO, 1 if OK
989
     */
990
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
991
    {
992
        global $conf, $langs;
993
994
        $result = 0;
995
        $includedocgeneration = 0;
996
997
        $langs->load("timespent@timespent");
998
999
        if (!dol_strlen($modele)) {
1000
            $modele = 'standard_timespent';
1001
1002
            if (!empty($this->model_pdf)) {
1003
                $modele = $this->model_pdf;
1004
            } elseif (getDolGlobalString('timespent_ADDON_PDF')) {
1005
                $modele = getDolGlobalString('timespent_ADDON_PDF');
1006
            }
1007
        }
1008
1009
        $modelpath = "core/modules/timespent/doc/";
1010
1011
        if ($includedocgeneration && !empty($modele)) {
1012
            $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
1013
        }
1014
1015
        return $result;
1016
    }
1017
}
1018