Passed
Push — main ( f1540e...02d90d )
by Rafael
45:15
created

TimeSpent::createFromClone()   F

Complexity

Conditions 21
Paths 6912

Size

Total Lines 83
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 45
nc 6912
nop 2
dl 0
loc 83
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
/**
24
 * \file        class/timespent.class.php
25
 * \ingroup     timespent
26
 * \brief       This file is a CRUD class file for TimeSpent (Create/Read/Update/Delete)
27
 */
28
29
use DoliCore\Base\GenericDocument;
30
31
/**
32
 * Class for TimeSpent
33
 */
34
class TimeSpent extends GenericDocument
35
{
36
    /**
37
     * @var string ID of module.
38
     */
39
    public $module = 'timespent';
40
41
    /**
42
     * @var string ID to identify managed object.
43
     */
44
    public $element = 'timespent';
45
46
    /**
47
     * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management.
48
     */
49
    public $table_element = 'element_time';
50
51
    /**
52
     * @var int  Does this object support multicompany module ?
53
     * 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
54
     */
55
    public $ismultientitymanaged = 0;
56
57
    /**
58
     * @var int  Does object support extrafields ? 0=No, 1=Yes
59
     */
60
    public $isextrafieldmanaged = 0;
61
62
    /**
63
     * @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'.
64
     */
65
    public $picto = 'fa-file';
66
67
68
    const STATUS_DRAFT = 0;
69
    const STATUS_VALIDATED = 1;
70
    const STATUS_CANCELED = 9;
71
72
73
    /**
74
     *  'type' field format:
75
     *      'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',
76
     *      'select' (list of values are in 'options'),
77
     *      'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:Sortfield]]]]',
78
     *      'chkbxlst:...',
79
     *      'varchar(x)',
80
     *      'text', 'text:none', 'html',
81
     *      'double(24,8)', 'real', 'price',
82
     *      'date', 'datetime', 'timestamp', 'duration',
83
     *      'boolean', 'checkbox', 'radio', 'array',
84
     *      'mail', 'phone', 'url', 'password', 'ip'
85
     *      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)"
86
     *  'label' the translation key.
87
     *  'picto' is code of a picto to show before value in forms
88
     *  'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM' or 'isModEnabled("multicurrency")' ...)
89
     *  'position' is the sort order of field.
90
     *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
91
     *  '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)
92
     *  'noteditable' says if field is not editable (1 or 0)
93
     *  'alwayseditable' says if field can be modified also when status is not draft ('1' or '0')
94
     *  '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.
95
     *  'index' if we want an index in database.
96
     *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommended to name the field fk_...).
97
     *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
98
     *  '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)
99
     *  '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'
100
     *  '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.
101
     *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
102
     *  '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.
103
     *  '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'
104
     *  'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1.
105
     *  'comment' is not used. You can store here any text of your choice. It is not used by application.
106
     *  'validate' is 1 if need to validate with $this->validateField()
107
     *  'copytoclipboard' is 1 or 2 to allow to add a picto to copy value into clipboard (1=picto after label, 2=picto after value)
108
     *
109
     *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
110
     */
111
112
    // BEGIN MODULEBUILDER PROPERTIES
113
    /**
114
     * @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...
115
     */
116
    public $fields = array(
117
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'visible' => 0, 'noteditable' => 1, 'index' => 1, 'css' => 'left', 'comment' => "Id"),
118
        'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'position' => 17, 'notnull' => 0, 'visible' => -2,),
119
        'import_key' => array('type' => 'varchar(14)', 'label' => 'import_key', 'enabled' => 1, 'position' => 15, 'notnull' => 0, 'visible' => -1,),
120
        'fk_element' => array('type' => 'integer', 'label' => 'fk_element', 'enabled' => 1, 'position' => 2, 'notnull' => 1, 'visible' => -1,),
121
        'elementtype' => array('type' => 'varchar(32)', 'label' => 'elementtype', 'enabled' => 1, 'position' => 3, 'notnull' => 1, 'visible' => -1,),
122
        'element_date' => array('type' => 'date', 'label' => 'element_date', 'enabled' => 1, 'position' => 4, 'notnull' => 0, 'visible' => -1,),
123
        'element_datehour' => array('type' => 'datetime', 'label' => 'element_datehour', 'enabled' => 1, 'position' => 5, 'notnull' => 0, 'visible' => -1,),
124
        'element_date_withhour' => array('type' => 'integer', 'label' => 'element_date_withhour', 'enabled' => 1, 'position' => 6, 'notnull' => 0, 'visible' => -1,),
125
        'element_duration' => array('type' => 'double', 'label' => 'element_duration', 'enabled' => 1, 'position' => 7, 'notnull' => 0, 'visible' => -1,),
126
        'fk_product' => array('type' => 'integer', 'label' => 'fk_product', 'enabled' => 1, 'position' => 8, 'notnull' => 0, 'visible' => -1,),
127
        'fk_user' => array('type' => 'integer', 'label' => 'fk_user', 'enabled' => 1, 'position' => 9, 'notnull' => 0, 'visible' => -1,),
128
        'thm' => array('type' => 'double(24,8)', 'label' => 'thm', 'enabled' => 1, 'position' => 10, 'notnull' => 0, 'visible' => -1,),
129
        'invoice_id' => array('type' => 'integer', 'label' => 'invoice_id', 'enabled' => 1, 'position' => 11, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
130
        'invoice_line_id' => array('type' => 'integer', 'label' => 'invoice_line_id', 'enabled' => 1, 'position' => 12, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
131
        'intervention_id' => array('type' => 'integer', 'label' => 'intervention_id', 'enabled' => 1, 'position' => 13, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
132
        'intervention_line_id' => array('type' => 'integer', 'label' => 'intervention_line_id', 'enabled' => 1, 'position' => 14, 'notnull' => 0, 'visible' => -1, 'default' => 'NULL',),
133
        'datec' => array('type' => 'datetime', 'label' => 'datec', 'enabled' => 1, 'position' => 16, 'notnull' => 0, 'visible' => -1,),
134
        'note' => array('type' => 'text', 'label' => 'note', 'enabled' => 1, 'position' => 18, 'notnull' => 0, 'visible' => -1,),
135
    );
136
    public $rowid;
137
    public $import_key;
138
    public $fk_element;
139
    public $elementtype;
140
    public $element_date;
141
    public $element_datehour;
142
    public $element_date_withhour;
143
    public $element_duration;
144
    public $fk_product;
145
    public $fk_user;
146
    public $thm;
147
    public $invoice_id;
148
    public $invoice_line_id;
149
    public $intervention_id;
150
    public $intervention_line_id;
151
    public $datec;
152
    public $note;
153
    // END MODULEBUILDER PROPERTIES
154
155
    /**
156
     * Constructor
157
     *
158
     * @param DoliDB $db Database handler
159
     */
160
    public function __construct(DoliDB $db)
161
    {
162
        global $conf, $langs;
163
164
        $this->db = $db;
165
166
        if (!getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && isset($this->fields['rowid']) && !empty($this->fields['ref'])) {
167
            $this->fields['rowid']['visible'] = 0;
168
        }
169
        if (!isModEnabled('multicompany') && isset($this->fields['entity'])) {
170
            $this->fields['entity']['enabled'] = 0;
171
        }
172
173
        // Example to show how to set values of fields definition dynamically
174
        /*if ($user->rights->timespent->timespent->read) {
175
            $this->fields['myfield']['visible'] = 1;
176
            $this->fields['myfield']['noteditable'] = 0;
177
        }*/
178
179
        // Unset fields that are disabled
180
        foreach ($this->fields as $key => $val) {
181
            if (isset($val['enabled']) && empty($val['enabled'])) {
182
                unset($this->fields[$key]);
183
            }
184
        }
185
186
        // Translate some data of arrayofkeyval
187
        if (is_object($langs)) {
188
            foreach ($this->fields as $key => $val) {
189
                if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
190
                    foreach ($val['arrayofkeyval'] as $key2 => $val2) {
191
                        $this->fields[$key]['arrayofkeyval'][$key2] = $langs->trans($val2);
192
                    }
193
                }
194
            }
195
        }
196
    }
197
198
    /**
199
     * Create object into database
200
     *
201
     * @param  User $user      User that creates
202
     * @param  int  $notrigger 0=launch triggers after, 1=disable triggers
203
     * @return int             Return integer <0 if KO, Id of created object if OK
204
     */
205
    public function create(User $user, $notrigger = 0)
206
    {
207
        $resultcreate = $this->createCommon($user, $notrigger);
208
209
        //$resultvalidate = $this->validate($user, $notrigger);
210
211
        return $resultcreate;
212
    }
213
214
    /**
215
     * Clone an object into another one
216
     *
217
     * @param   User    $user       User that creates
218
     * @param   int     $fromid     Id of object to clone
219
     * @return  mixed               New object created, <0 if KO
220
     */
221
    public function createFromClone(User $user, $fromid)
222
    {
223
        global $langs, $extrafields;
224
        $error = 0;
225
226
        dol_syslog(__METHOD__, LOG_DEBUG);
227
228
        $object = new self($this->db);
229
230
        $this->db->begin();
231
232
        // Load source object
233
        $result = $object->fetchCommon($fromid);
234
235
        // Reset some properties
236
        unset($object->id);
237
        unset($object->fk_user_creat);
0 ignored issues
show
Deprecated Code introduced by
The property DoliCore\Base\GenericDocument::$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

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

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