Passed
Push — EXTRACT_CLASSES ( d4f850...56e940 )
by Rafael
41:39
created

Documents::post()   F

Complexity

Conditions 80
Paths > 20000

Size

Total Lines 261
Code Lines 154

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 80
eloc 154
nc 383689
nop 8
dl 0
loc 261
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2016       Xebax Christy               <[email protected]>
4
 * Copyright (C) 2016	    Laurent Destailleur		    <[email protected]>
5
 * Copyright (C) 2016       Jean-François Ferry         <[email protected]>
6
 * Copyright (C) 2023       Romain Neil                 <[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\Api\Api;
24
25
use Dolibarr\Code\Adherents\Classes\Adherent;
26
use Dolibarr\Code\Api\Classes\DolibarrApiAccess;
27
use Dolibarr\Code\Categories\Classes\Categorie;
28
use Dolibarr\Code\Comm\Classes\ActionComm;
29
use Dolibarr\Code\Comm\Classes\Propal;
30
use Dolibarr\Code\Commande\Classes\Commande;
31
use Dolibarr\Code\Compta\Classes\Facture;
32
use Dolibarr\Code\Contact\Classes\Contact;
33
use Dolibarr\Code\Contrat\Classes\Contrat;
34
use Dolibarr\Code\Core\Classes\Translate;
35
use Dolibarr\Code\Ecm\Classes\EcmFiles;
36
use Dolibarr\Code\Expedition\Classes\Expedition;
37
use Dolibarr\Code\ExpenseReport\Classes\ExpenseReport;
38
use Dolibarr\Code\FichInter\Classes\Fichinter;
39
use Dolibarr\Code\Fourn\Classes\CommandeFournisseur;
40
use Dolibarr\Code\Fourn\Classes\FactureFournisseur;
41
use Dolibarr\Code\KnowledgeManagement\Classes\KnowledgeRecord;
42
use Dolibarr\Code\Product\Classes\Product;
43
use Dolibarr\Code\Projet\Classes\Project;
44
use Dolibarr\Code\Projet\Classes\Task;
45
use Dolibarr\Code\Societe\Classes\Societe;
46
use Dolibarr\Code\User\Classes\User;
47
use Dolibarr\Core\Base\DolibarrApi;
48
use Luracast\Restler\RestException;
49
50
require_once constant('DOL_DOCUMENT_ROOT') . '/main.inc.php';
51
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
52
53
/**
54
 * API class for receive files
55
 *
56
 * @access protected
57
 * @class Documents {@requires user,external}
58
 */
59
class Documents extends DolibarrApi
60
{
61
    /**
62
     * Constructor
63
     */
64
    public function __construct()
65
    {
66
        global $db;
67
        $this->db = $db;
68
    }
69
70
71
    /**
72
     * Download a document.
73
     *
74
     * Note that, this API is similar to using the wrapper link "documents.php" to download a file (used for
75
     * internal HTML links of documents into application), but with no need to have a session cookie (the token is used instead).
76
     *
77
     * @param string $modulepart Name of module or area concerned by file download ('facture', ...)
78
     * @param string $original_file Relative path with filename, relative to modulepart (for example: IN201701-999/IN201701-999.pdf)
79
     * @return  array                   List of documents
80
     *
81
     * @url GET /download
82
     *
83
     * @throws  RestException   400     Bad value for parameter modulepart or original_file
84
     * @throws  RestException   403     Access denied
85
     * @throws  RestException   404     File not found
86
     */
87
    public function index($modulepart, $original_file = '')
88
    {
89
        global $conf;
90
91
        if (empty($modulepart)) {
92
            throw new RestException(400, 'bad value for parameter modulepart');
93
        }
94
        if (empty($original_file)) {
95
            throw new RestException(400, 'bad value for parameter original_file');
96
        }
97
98
        //--- Finds and returns the document
99
        $entity = $conf->entity;
100
101
        // Special cases that need to use get_exdir to get real dir of object
102
        // If future, all object should use this to define path of documents.
103
        /*
104
        $tmpreldir = '';
105
        if ($modulepart == 'supplier_invoice') {
106
            $tmpreldir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier');
107
        }
108
109
        $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); */
110
        $relativefile = $original_file;
111
112
        $check_access = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'read');
113
        $accessallowed = $check_access['accessallowed'];
114
        $sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals'];
115
        $original_file = $check_access['original_file'];
116
117
        if (preg_match('/\.\./', $original_file) || preg_match('/[<>|]/', $original_file)) {
118
            throw new RestException(403);
119
        }
120
        if (!$accessallowed) {
121
            throw new RestException(403);
122
        }
123
124
        $filename = basename($original_file);
125
        $original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset
126
127
        if (!file_exists($original_file_osencoded)) {
128
            dol_syslog("Try to download not found file " . $original_file_osencoded, LOG_WARNING);
129
            throw new RestException(404, 'File not found');
130
        }
131
132
        $file_content = file_get_contents($original_file_osencoded);
133
        return array('filename' => $filename, 'content-type' => dol_mimetype($filename), 'filesize' => filesize($original_file), 'content' => base64_encode($file_content), 'encoding' => 'base64');
134
    }
135
136
137
    /**
138
     * Build a document.
139
     *
140
     * Test sample 1: { "modulepart": "invoice", "original_file": "FA1701-001/FA1701-001.pdf", "doctemplate": "crabe", "langcode": "fr_FR" }.
141
     *
142
     * Supported modules: invoice, order, proposal, contract, shipment
143
     *
144
     * @param string $modulepart Name of module or area concerned by file download ('thirdparty', 'member', 'proposal', 'supplier_proposal', 'order', 'supplier_order', 'invoice', 'supplier_invoice', 'shipment', 'project',  ...)
145
     * @param string $original_file Relative path with filename, relative to modulepart (for example: IN201701-999/IN201701-999.pdf).
146
     * @param string $doctemplate Set here the doc template to use for document generation (If not set, use the default template).
147
     * @param string $langcode Language code like 'en_US', 'fr_FR', 'es_ES', ... (If not set, use the default language).
148
     * @return  array                   List of documents
149
     *
150
     * @url PUT /builddoc
151
     *
152
     * @throws  RestException   400     Bad value for parameter modulepart or original_file
153
     * @throws  RestException   403     Access denied
154
     * @throws  RestException   404     Invoice, Order, Proposal, Contract or Shipment not found
155
     * @throws  RestException   500     Error generating document
156
     * @throws  RestException   501     File not found
157
     */
158
    public function builddoc($modulepart, $original_file = '', $doctemplate = '', $langcode = '')
159
    {
160
        global $conf, $langs;
161
162
        if (empty($modulepart)) {
163
            throw new RestException(400, 'bad value for parameter modulepart');
164
        }
165
        if (empty($original_file)) {
166
            throw new RestException(400, 'bad value for parameter original_file');
167
        }
168
169
        $outputlangs = $langs;
170
        if ($langcode && $langs->defaultlang != $langcode) {
171
            $outputlangs = new Translate('', $conf);
172
            $outputlangs->setDefaultLang($langcode);
173
        }
174
175
        //--- Finds and returns the document
176
        $entity = $conf->entity;
177
178
        // Special cases that need to use get_exdir to get real dir of object
179
        // If future, all object should use this to define path of documents.
180
        /*
181
        $tmpreldir = '';
182
        if ($modulepart == 'supplier_invoice') {
183
            $tmpreldir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier');
184
        }
185
186
        $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); */
187
        $relativefile = $original_file;
188
189
        $check_access = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'write');
190
        $accessallowed = $check_access['accessallowed'];
191
        $sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals'];
192
        $original_file = $check_access['original_file'];
193
194
        if (preg_match('/\.\./', $original_file) || preg_match('/[<>|]/', $original_file)) {
195
            throw new RestException(403);
196
        }
197
        if (!$accessallowed) {
198
            throw new RestException(403);
199
        }
200
201
        // --- Generates the document
202
        $hidedetails = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 0 : 1;
203
        $hidedesc = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 0 : 1;
204
        $hideref = !getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 0 : 1;
205
206
        $templateused = '';
207
208
        if ($modulepart == 'facture' || $modulepart == 'invoice') {
209
            $tmpobject = new Facture($this->db);
210
            $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file)));
211
            if (!$result) {
212
                throw new RestException(404, 'Invoice not found');
213
            }
214
215
            $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf;
216
            $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref);
217
            if ($result <= 0) {
218
                throw new RestException(500, 'Error generating document');
219
            }
220
        } elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier') {
221
            $tmpobject = new FactureFournisseur($this->db);
222
            $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file)));
223
            if (!$result) {
224
                throw new RestException(404, 'Supplier invoice not found');
225
            }
226
227
            $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf;
228
            $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref);
229
            if ($result < 0) {
230
                throw new RestException(500, 'Error generating document');
231
            }
232
        } elseif ($modulepart == 'commande' || $modulepart == 'order') {
233
            $tmpobject = new Commande($this->db);
234
            $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file)));
235
            if (!$result) {
236
                throw new RestException(404, 'Order not found');
237
            }
238
            $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf;
239
            $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref);
240
            if ($result <= 0) {
241
                throw new RestException(500, 'Error generating document');
242
            }
243
        } elseif ($modulepart == 'propal' || $modulepart == 'proposal') {
244
            $tmpobject = new Propal($this->db);
245
            $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file)));
246
            if (!$result) {
247
                throw new RestException(404, 'Proposal not found');
248
            }
249
            $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf;
250
            $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref);
251
            if ($result <= 0) {
252
                throw new RestException(500, 'Error generating document');
253
            }
254
        } elseif ($modulepart == 'contrat' || $modulepart == 'contract') {
255
256
            $tmpobject = new Contrat($this->db);
257
            $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file)));
258
259
            if (!$result) {
260
                throw new RestException(404, 'Contract not found');
261
            }
262
263
            $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf;
264
            $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref);
265
266
            if ($result <= 0) {
267
                throw new RestException(500, 'Error generating document missing doctemplate parameter');
268
            }
269
        } elseif ($modulepart == 'expedition' || $modulepart == 'shipment') {
270
            require_once constant('DOL_DOCUMENT_ROOT') . '/expedition/class/expedition.class.php';
271
272
            $tmpobject = new Expedition($this->db);
273
            $result = $tmpobject->fetch(0, preg_replace('/\.[^\.]+$/', '', basename($original_file)));
274
275
            if (!$result) {
276
                throw new RestException(404, 'Shipment not found');
277
            }
278
279
            $templateused = $doctemplate ? $doctemplate : $tmpobject->model_pdf;
280
            $result = $tmpobject->generateDocument($templateused, $outputlangs, $hidedetails, $hidedesc, $hideref);
281
282
            if ($result <= 0) {
283
                throw new RestException(500, 'Error generating document missing doctemplate parameter');
284
            }
285
        } else {
286
            throw new RestException(403, 'Generation not available for this modulepart');
287
        }
288
289
        $filename = basename($original_file);
290
        $original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset
291
292
        if (!file_exists($original_file_osencoded)) {
293
            throw new RestException(404, 'File not found');
294
        }
295
296
        $file_content = file_get_contents($original_file_osencoded);
297
        return array('filename' => $filename, 'content-type' => dol_mimetype($filename), 'filesize' => filesize($original_file), 'content' => base64_encode($file_content), 'langcode' => $outputlangs->defaultlang, 'template' => $templateused, 'encoding' => 'base64');
298
    }
299
300
    /**
301
     * Return the list of documents of a dedicated element (from its ID or Ref)
302
     *
303
     * Supported modules: thirdparty, user, member, proposal, order, supplier_order, shipment, invoice, supplier_invoice, product, event, expensereport, knowledgemanagement, category, contract
304
     *
305
     * @param string $modulepart Name of module or area concerned ('thirdparty', 'member', 'proposal', 'order', 'invoice', 'supplier_invoice', 'shipment', 'project',  ...)
306
     * @param int $id ID of element
307
     * @param string $ref Ref of element
308
     * @param string $sortfield Sort criteria ('','fullname','relativename','name','date','size')
309
     * @param string $sortorder Sort order ('asc' or 'desc')
310
     * @return  array                   Array of documents with path
311
     *
312
     * @url GET /
313
     *
314
     * @throws  RestException   400     Bad value for parameter modulepart, id or ref
315
     * @throws  RestException   403     Access denied
316
     * @throws  RestException   404     Thirdparty, User, Member, Order, Invoice or Proposal not found
317
     * @throws  RestException   500     Error while fetching object
318
     * @throws  RestException   503     Error when retrieve ecm list
319
     */
320
    public function getDocumentsListByElement($modulepart, $id = 0, $ref = '', $sortfield = '', $sortorder = '')
321
    {
322
        global $conf;
323
324
        if (empty($modulepart)) {
325
            throw new RestException(400, 'bad value for parameter modulepart');
326
        }
327
328
        if (empty($id) && empty($ref)) {
329
            throw new RestException(400, 'bad value for parameter id or ref');
330
        }
331
332
        $id = (empty($id) ? 0 : $id);
333
        $recursive = 0;
334
        $type = 'files';
335
336
        if ($modulepart == 'societe' || $modulepart == 'thirdparty') {
337
            if (!DolibarrApiAccess::$user->hasRight('societe', 'lire')) {
338
                throw new RestException(403);
339
            }
340
341
            $object = new Societe($this->db);
342
            $result = $object->fetch($id, $ref);
343
            if (!$result) {
344
                throw new RestException(404, 'Thirdparty not found');
345
            }
346
347
            $upload_dir = $conf->societe->multidir_output[$object->entity] . "/" . $object->id;
348
        } elseif ($modulepart == 'user') {
349
            // Can get doc if has permission to read all user or if it is user itself
350
            if (!DolibarrApiAccess::$user->hasRight('user', 'user', 'lire') && DolibarrApiAccess::$user->id != $id) {
351
                throw new RestException(403);
352
            }
353
354
            $object = new User($this->db);
355
            $result = $object->fetch($id, $ref);
356
            if (!$result) {
357
                throw new RestException(404, 'User not found');
358
            }
359
360
            $upload_dir = $conf->user->dir_output . '/' . get_exdir(0, 0, 0, 0, $object, 'user') . '/' . $object->id;
361
        } elseif ($modulepart == 'adherent' || $modulepart == 'member') {
362
363
            if (!DolibarrApiAccess::$user->hasRight('adherent', 'lire')) {
364
                throw new RestException(403);
365
            }
366
367
            $object = new Adherent($this->db);
368
            $result = $object->fetch($id, $ref);
369
            if (!$result) {
370
                throw new RestException(404, 'Member not found');
371
            }
372
373
            $upload_dir = $conf->adherent->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'member');
374
        } elseif ($modulepart == 'propal' || $modulepart == 'proposal') {
375
            if (!DolibarrApiAccess::$user->hasRight('propal', 'lire')) {
376
                throw new RestException(403);
377
            }
378
379
            $object = new Propal($this->db);
380
            $result = $object->fetch($id, $ref);
381
            if (!$result) {
382
                throw new RestException(404, 'Proposal not found');
383
            }
384
385
            $upload_dir = $conf->propal->multidir_output[$object->entity] . "/" . get_exdir(0, 0, 0, 1, $object, 'propal');
386
        } elseif ($modulepart == 'supplier_proposal') {
387
388
            if (!DolibarrApiAccess::$user->hasRight('supplier_proposal', 'read')) {
389
                throw new RestException(403);
390
            }
391
392
            $object = new Propal($this->db);
393
            $result = $object->fetch($id, $ref);
394
            if (!$result) {
395
                throw new RestException(404, 'Supplier proposal not found');
396
            }
397
398
            $upload_dir = $conf->propal->multidir_output[$object->entity] . "/" . get_exdir(0, 0, 0, 1, $object, 'propal');
399
        } elseif ($modulepart == 'commande' || $modulepart == 'order') {
400
            if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
401
                throw new RestException(403);
402
            }
403
404
            $object = new Commande($this->db);
405
            $result = $object->fetch($id, $ref);
406
            if (!$result) {
407
                throw new RestException(404, 'Order not found');
408
            }
409
410
            $upload_dir = $conf->commande->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'commande');
411
        } elseif ($modulepart == 'commande_fournisseur' || $modulepart == 'supplier_order') {
412
            $modulepart = 'supplier_order';
413
414
415
            if (!DolibarrApiAccess::$user->hasRight('fournisseur', 'commande', 'lire') && !DolibarrApiAccess::$user->hasRight('supplier_order', 'lire')) {
416
                throw new RestException(403);
417
            }
418
419
            $object = new CommandeFournisseur($this->db);
420
            $result = $object->fetch($id, $ref);
421
            if (!$result) {
422
                throw new RestException(404, 'Purchase order not found');
423
            }
424
425
            $upload_dir = $conf->fournisseur->dir_output . "/commande/" . dol_sanitizeFileName($object->ref);
426
        } elseif ($modulepart == 'shipment' || $modulepart == 'expedition') {
427
            require_once constant('DOL_DOCUMENT_ROOT') . '/expedition/class/expedition.class.php';
428
429
            if (!DolibarrApiAccess::$user->hasRight('expedition', 'lire')) {
430
                throw new RestException(403);
431
            }
432
433
            $object = new Expedition($this->db);
434
            $result = $object->fetch($id, $ref);
435
            if (!$result) {
436
                throw new RestException(404, 'Shipment not found');
437
            }
438
439
            $upload_dir = $conf->expedition->dir_output . "/sending/" . get_exdir(0, 0, 0, 1, $object, 'shipment');
440
        } elseif ($modulepart == 'facture' || $modulepart == 'invoice') {
441
442
            if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
443
                throw new RestException(403);
444
            }
445
446
            $object = new Facture($this->db);
447
            $result = $object->fetch($id, $ref);
448
            if (!$result) {
449
                throw new RestException(404, 'Invoice not found');
450
            }
451
452
            $upload_dir = $conf->facture->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'invoice');
453
        } elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'supplier_invoice') {
454
            $modulepart = 'supplier_invoice';
455
456
457
            if (!DolibarrApiAccess::$user->hasRight('fournisseur', 'facture', 'lire') && !DolibarrApiAccess::$user->hasRight('supplier_invoice', 'lire')) {
458
                throw new RestException(403);
459
            }
460
461
            $object = new FactureFournisseur($this->db);
462
            $result = $object->fetch($id, $ref);
463
            if (!$result) {
464
                throw new RestException(404, 'Invoice not found');
465
            }
466
467
            $upload_dir = $conf->fournisseur->dir_output . "/facture/" . get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier') . dol_sanitizeFileName($object->ref);
468
        } elseif ($modulepart == 'produit' || $modulepart == 'product') {
469
470
            if (!DolibarrApiAccess::$user->hasRight('produit', 'lire')) {
471
                throw new RestException(403);
472
            }
473
474
            $object = new Product($this->db);
475
            $result = $object->fetch($id, $ref);
476
            if ($result == 0) {
477
                throw new RestException(404, 'Product not found');
478
            } elseif ($result < 0) {
479
                throw new RestException(500, 'Error while fetching object: ' . $object->error);
480
            }
481
482
            $upload_dir = $conf->product->multidir_output[$object->entity] . '/' . get_exdir(0, 0, 0, 1, $object, 'product');
483
        } elseif ($modulepart == 'agenda' || $modulepart == 'action' || $modulepart == 'event') {
484
485
            if (!DolibarrApiAccess::$user->hasRight('agenda', 'myactions', 'read') && !DolibarrApiAccess::$user->hasRight('agenda', 'allactions', 'read')) {
486
                throw new RestException(403);
487
            }
488
489
            $object = new ActionComm($this->db);
490
            $result = $object->fetch($id, $ref);
491
            if (!$result) {
492
                throw new RestException(404, 'Event not found');
493
            }
494
495
            $upload_dir = $conf->agenda->dir_output . '/' . dol_sanitizeFileName($object->ref);
496
        } elseif ($modulepart == 'expensereport') {
497
498
            if (!DolibarrApiAccess::$user->hasRight('expensereport', 'read') && !DolibarrApiAccess::$user->hasRight('expensereport', 'read')) {
499
                throw new RestException(403);
500
            }
501
502
            $object = new ExpenseReport($this->db);
503
            $result = $object->fetch($id, $ref);
504
            if (!$result) {
505
                throw new RestException(404, 'Expense report not found');
506
            }
507
508
            $upload_dir = $conf->expensereport->dir_output . '/' . dol_sanitizeFileName($object->ref);
509
        } elseif ($modulepart == 'knowledgemanagement') {
510
511
            if (!DolibarrApiAccess::$user->hasRight('knowledgemanagement', 'knowledgerecord', 'read') && !DolibarrApiAccess::$user->hasRight('knowledgemanagement', 'knowledgerecord', 'read')) {
512
                throw new RestException(403);
513
            }
514
515
            $object = new KnowledgeRecord($this->db);
516
            $result = $object->fetch($id, $ref);
517
            if (!$result) {
518
                throw new RestException(404, 'KM article not found');
519
            }
520
521
            $upload_dir = $conf->knowledgemanagement->dir_output . '/knowledgerecord/' . dol_sanitizeFileName($object->ref);
522
        } elseif ($modulepart == 'categorie' || $modulepart == 'category') {
523
            if (!DolibarrApiAccess::$user->hasRight('categorie', 'lire')) {
524
                throw new RestException(403);
525
            }
526
527
            $object = new Categorie($this->db);
528
            $result = $object->fetch($id, $ref);
529
            if (!$result) {
530
                throw new RestException(404, 'Category not found');
531
            }
532
533
            $upload_dir = $conf->categorie->multidir_output[$object->entity] . '/' . get_exdir($object->id, 2, 0, 0, $object, 'category') . $object->id . "/photos/" . dol_sanitizeFileName($object->ref);
534
        } elseif ($modulepart == 'ecm') {
535
            throw new RestException(500, 'Modulepart Ecm not implemented yet.');
536
            // require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmdirectory.class.php';
537
538
            // if (!DolibarrApiAccess::$user->hasRight('ecm', 'read')) {
539
            //  throw new RestException(403);
540
            // }
541
542
            // // $object = new EcmDirectory($this->db);
543
            // // $result = $object->fetch($ref);
544
            // // if (!$result) {
545
            // //   throw new RestException(404, 'EcmDirectory not found');
546
            // // }
547
            // $upload_dir = $conf->ecm->dir_output;
548
            // $type = 'all';
549
            // $recursive = 0;
550
        } elseif ($modulepart == 'contrat' || $modulepart == 'contract') {
551
            $modulepart = 'contrat';
552
553
            $object = new Contrat($this->db);
554
            $result = $object->fetch($id, $ref);
555
            if (!$result) {
556
                throw new RestException(404, 'Contract not found');
557
            }
558
559
            $upload_dir = $conf->contrat->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'contract');
560
        } elseif ($modulepart == 'projet' || $modulepart == 'project') {
561
            $modulepart = 'project';
562
563
            $object = new Project($this->db);
564
            $result = $object->fetch($id, $ref);
565
            if (!$result) {
566
                throw new RestException(404, 'Project not found');
567
            }
568
569
            $upload_dir = $conf->projet->dir_output . "/" . get_exdir(0, 0, 0, 1, $object, 'project');
570
        } else {
571
            throw new RestException(500, 'Modulepart ' . $modulepart . ' not implemented yet.');
572
        }
573
574
        $objectType = $modulepart;
575
        if (!empty($object->id) && !empty($object->table_element)) {
576
            $objectType = $object->table_element;
577
        }
578
579
        $filearray = dol_dir_list($upload_dir, $type, $recursive, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
580
        if (empty($filearray)) {
581
            throw new RestException(404, 'Search for modulepart ' . $modulepart . ' with Id ' . $object->id . (!empty($object->ref) ? ' or Ref ' . $object->ref : '') . ' does not return any document.');
582
        } else {
583
            if (($object->id) > 0 && !empty($modulepart)) {
584
                require_once constant('DOL_DOCUMENT_ROOT') . '/ecm/class/ecmfiles.class.php';
585
                $ecmfile = new EcmFiles($this->db);
586
                $result = $ecmfile->fetchAll('', '', 0, 0, array('t.src_object_type' => $objectType, 't.src_object_id' => $object->id));
587
                if ($result < 0) {
588
                    throw new RestException(503, 'Error when retrieve ecm list : ' . $this->db->lasterror());
589
                } elseif (is_array($ecmfile->lines) && count($ecmfile->lines) > 0) {
590
                    $count = count($filearray);
591
                    for ($i = 0; $i < $count; $i++) {
592
                        foreach ($ecmfile->lines as $line) {
593
                            if ($filearray[$i]['name'] == $line->filename) {
594
                                $filearray[$i] = array_merge($filearray[$i], (array)$line);
595
                            }
596
                        }
597
                    }
598
                }
599
            }
600
        }
601
602
        return $filearray;
603
    }
604
605
606
    /**
607
     * Return a document.
608
     *
609
     * @param int $id ID of document
610
     * @return  array                    Array with data of file
611
     *
612
     * @throws RestException
613
     */
614
    /*
615
    public function get($id) {
616
        return array('note'=>'xxx');
617
    }*/
618
619
620
    /**
621
     * Upload a document.
622
     *
623
     * Test sample for invoice: { "filename": "mynewfile.txt", "modulepart": "invoice", "ref": "FA1701-001", "subdir": "", "filecontent": "content text", "fileencoding": "", "overwriteifexists": "0" }.
624
     * Test sample for supplier invoice: { "filename": "mynewfile.txt", "modulepart": "supplier_invoice", "ref": "FA1701-001", "subdir": "", "filecontent": "content text", "fileencoding": "", "overwriteifexists": "0" }.
625
     * Test sample for medias file: { "filename": "mynewfile.txt", "modulepart": "medias", "ref": "", "subdir": "image/mywebsite", "filecontent": "Y29udGVudCB0ZXh0Cg==", "fileencoding": "base64", "overwriteifexists": "0" }.
626
     *
627
     * Supported modules: invoice, order, supplier_order, task/project_task, product/service, expensereport, fichinter, member, propale, agenda, contact
628
     *
629
     * @param string $filename Name of file to create ('FA1705-0123.txt')
630
     * @param string $modulepart Name of module or area concerned by file upload ('product', 'service', 'invoice', 'proposal', 'project', 'project_task', 'supplier_invoice', 'expensereport', 'member', ...)
631
     * @param string $ref Reference of object (This will define subdir automatically and store submitted file into it)
632
     * @param string $subdir Subdirectory (Only if ref not provided)
633
     * @param string $filecontent File content (string with file content. An empty file will be created if this parameter is not provided)
634
     * @param string $fileencoding File encoding (''=no encoding, 'base64'=Base 64)
635
     * @param int $overwriteifexists Overwrite file if exists (1 by default)
636
     * @param int $createdirifnotexists Create subdirectories if the doesn't exists (1 by default)
637
     * @return  string
638
     *
639
     * @url POST /upload
640
     *
641
     * @throws  RestException   400     Bad Request
642
     * @throws  RestException   403     Access denied
643
     * @throws  RestException   404     Object not found
644
     * @throws  RestException   500     Error on file operationw
645
     */
646
    public function post($filename, $modulepart, $ref = '', $subdir = '', $filecontent = '', $fileencoding = '', $overwriteifexists = 0, $createdirifnotexists = 1)
647
    {
648
        global $conf;
649
650
        //var_dump($modulepart);
651
        //var_dump($filename);
652
        //var_dump($filecontent);exit;
653
654
        $modulepartorig = $modulepart;
655
656
        if (empty($modulepart)) {
657
            throw new RestException(400, 'Modulepart not provided.');
658
        }
659
660
        $newfilecontent = '';
661
        if (empty($fileencoding)) {
662
            $newfilecontent = $filecontent;
663
        }
664
        if ($fileencoding == 'base64') {
665
            $newfilecontent = base64_decode($filecontent);
666
        }
667
668
        $original_file = dol_sanitizeFileName($filename);
669
670
        // Define $uploadir
671
        $object = null;
672
        $entity = DolibarrApiAccess::$user->entity;
673
        if (empty($entity)) {
674
            $entity = 1;
675
        }
676
677
        if ($ref) {
678
            $tmpreldir = '';
679
            $fetchbyid = false;
680
681
            if ($modulepart == 'facture' || $modulepart == 'invoice') {
682
                $modulepart = 'facture';
683
684
                $object = new Facture($this->db);
685
            } elseif ($modulepart == 'facture_fournisseur' || $modulepart == 'supplier_invoice') {
686
                $modulepart = 'supplier_invoice';
687
688
                $object = new FactureFournisseur($this->db);
689
            } elseif ($modulepart == 'commande' || $modulepart == 'order') {
690
                $modulepart = 'commande';
691
692
                $object = new Commande($this->db);
693
            } elseif ($modulepart == 'commande_fournisseur' || $modulepart == 'supplier_order') {
694
                $modulepart = 'supplier_order';
695
696
                $object = new CommandeFournisseur($this->db);
697
            } elseif ($modulepart == 'projet' || $modulepart == 'project') {
698
                $object = new Project($this->db);
699
            } elseif ($modulepart == 'task' || $modulepart == 'project_task') {
700
                $modulepart = 'project_task';
701
702
                $object = new Task($this->db);
703
704
                $task_result = $object->fetch('', $ref);
705
706
                // Fetching the tasks project is required because its out_dir might be a sub-directory of the project
707
                if ($task_result > 0) {
708
                    $project_result = $object->fetch_projet();
709
710
                    if ($project_result >= 0) {
711
                        $tmpreldir = dol_sanitizeFileName($object->project->ref) . '/';
712
                    }
713
                } else {
714
                    throw new RestException(500, 'Error while fetching Task ' . $ref);
715
                }
716
            } elseif ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service') {
717
                $object = new Product($this->db);
718
            } elseif ($modulepart == 'expensereport') {
719
                $object = new ExpenseReport($this->db);
720
            } elseif ($modulepart == 'fichinter') {
721
                $object = new Fichinter($this->db);
722
            } elseif ($modulepart == 'adherent' || $modulepart == 'member') {
723
                $modulepart = 'adherent';
724
725
                $object = new Adherent($this->db);
726
            } elseif ($modulepart == 'proposal' || $modulepart == 'propal' || $modulepart == 'propale') {
727
                $modulepart = 'propale';
728
729
                $object = new Propal($this->db);
730
            } elseif ($modulepart == 'agenda' || $modulepart == 'action' || $modulepart == 'event') {
731
                $modulepart = 'agenda';
732
                $object = new ActionComm($this->db);
733
            } elseif ($modulepart == 'contact' || $modulepart == 'socpeople') {
734
                $modulepart = 'contact';
735
736
                $object = new Contact($this->db);
737
                $fetchbyid = true;
738
            } elseif ($modulepart == 'contrat' || $modulepart == 'contract') {
739
                $modulepart = 'contrat';
740
                $object = new Contrat($this->db);
741
            } else {
742
                // TODO Implement additional moduleparts
743
                throw new RestException(500, 'Modulepart ' . $modulepart . ' not implemented yet.');
744
            }
745
746
            if (is_object($object)) {
747
                if ($fetchbyid) {
748
                    // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
749
                    $result = $object->fetch($ref);
750
                } else {
751
                    $result = $object->fetch('', $ref);
752
                }
753
754
                if ($result == 0) {
755
                    throw new RestException(404, "Object with ref '" . $ref . "' was not found.");
756
                } elseif ($result < 0) {
757
                    throw new RestException(500, 'Error while fetching object: ' . $object->error);
758
                }
759
            }
760
761
            if (!($object->id > 0)) {
762
                throw new RestException(404, 'The object ' . $modulepart . " with ref '" . $ref . "' was not found.");
763
            }
764
765
            // Special cases that need to use get_exdir to get real dir of object
766
            // In future, all object should use this to define path of documents.
767
            if ($modulepart == 'supplier_invoice') {
768
                $tmpreldir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier');
769
            }
770
771
            // Test on permissions
772
            if ($modulepart != 'ecm') {
773
                $relativefile = $tmpreldir . dol_sanitizeFileName($object->ref);
774
                $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, $ref, 'write');
775
                $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir
776
            } else {
777
                if (!DolibarrApiAccess::$user->hasRight('ecm', 'upload')) {
778
                    throw new RestException(403, 'Missing permission to upload files in ECM module');
779
                }
780
                $upload_dir = $conf->medias->multidir_output[$conf->entity];
781
            }
782
783
            if (empty($upload_dir) || $upload_dir == '/') {
784
                throw new RestException(500, 'This value of modulepart (' . $modulepart . ') does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.');
785
            }
786
        } else {
787
            if ($modulepart == 'invoice') {
788
                $modulepart = 'facture';
789
            }
790
            if ($modulepart == 'member') {
791
                $modulepart = 'adherent';
792
            }
793
794
            // Test on permissions
795
            if ($modulepart != 'ecm') {
796
                $relativefile = $subdir;
797
                $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'write');
798
                $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir
799
            } else {
800
                if (!DolibarrApiAccess::$user->hasRight('ecm', 'upload')) {
801
                    throw new RestException(403, 'Missing permission to upload files in ECM module');
802
                }
803
                $upload_dir = $conf->medias->multidir_output[$conf->entity];
804
            }
805
806
            if (empty($upload_dir) || $upload_dir == '/') {
807
                if (!empty($tmp['error'])) {
808
                    throw new RestException(403, 'Error returned by dol_check_secure_access_document: ' . $tmp['error']);
809
                } else {
810
                    throw new RestException(400, 'This value of modulepart (' . $modulepart . ') is not allowed with this value of subdir (' . $relativefile . ')');
811
                }
812
            }
813
        }
814
        // $original_file here is still value of filename without any dir.
815
816
        $upload_dir = dol_sanitizePathName($upload_dir);
817
818
        if (!empty($createdirifnotexists)) {
819
            if (dol_mkdir($upload_dir) < 0) { // needed by products
820
                throw new RestException(500, 'Error while trying to create directory ' . $upload_dir);
821
            }
822
        }
823
824
        $destfile = $upload_dir . '/' . $original_file;
825
        $destfiletmp = DOL_DATA_ROOT . '/admin/temp/' . $original_file;
826
        dol_delete_file($destfiletmp);
827
        //var_dump($original_file);exit;
828
829
        if (!dol_is_dir(dirname($destfile))) {
830
            throw new RestException(400, 'Directory does not exists : ' . dirname($destfile));
831
        }
832
833
        if (!$overwriteifexists && dol_is_file($destfile)) {
834
            throw new RestException(400, "File with name '" . $original_file . "' already exists.");
835
        }
836
837
        // in case temporary directory admin/temp doesn't exist
838
        if (!dol_is_dir(dirname($destfiletmp))) {
839
            dol_mkdir(dirname($destfiletmp));
840
        }
841
842
        $fhandle = @fopen($destfiletmp, 'w');
843
        if ($fhandle) {
844
            $nbofbyteswrote = fwrite($fhandle, $newfilecontent);
845
            fclose($fhandle);
846
            dolChmod($destfiletmp);
847
        } else {
848
            throw new RestException(500, "Failed to open file '" . $destfiletmp . "' for write");
849
        }
850
851
        $disablevirusscan = 0;
852
        $src_file = $destfiletmp;
853
        $dest_file = $destfile;
854
855
        // Security:
856
        // If we need to make a virus scan
857
        if (empty($disablevirusscan) && file_exists($src_file)) {
858
            $checkvirusarray = dolCheckVirus($src_file, $dest_file);
859
            if (count($checkvirusarray)) {
860
                dol_syslog('Files.lib::dol_move_uploaded_file File "' . $src_file . '" (target name "' . $dest_file . '") KO with antivirus: errors=' . implode(',', $checkvirusarray), LOG_WARNING);
861
                throw new RestException(500, 'ErrorFileIsInfectedWithAVirus: ' . implode(',', $checkvirusarray));
862
            }
863
        }
864
865
        // Security:
866
        // Disallow file with some extensions. We rename them.
867
        // Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code.
868
        if (isAFileWithExecutableContent($dest_file) && !getDolGlobalString('MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED')) {
869
            // $upload_dir ends with a slash, so be must be sure the medias dir to compare to ends with slash too.
870
            $publicmediasdirwithslash = $conf->medias->multidir_output[$conf->entity];
871
            if (!preg_match('/\/$/', $publicmediasdirwithslash)) {
872
                $publicmediasdirwithslash .= '/';
873
            }
874
875
            if (strpos($upload_dir, $publicmediasdirwithslash) !== 0 || !getDolGlobalInt("MAIN_DOCUMENT_DISABLE_NOEXE_IN_MEDIAS_DIR")) {    // We never add .noexe on files into media directory
876
                $dest_file .= '.noexe';
877
            }
878
        }
879
880
        // Security:
881
        // We refuse cache files/dirs, upload using .. and pipes into filenames.
882
        if (preg_match('/^\./', basename($src_file)) || preg_match('/\.\./', $src_file) || preg_match('/[<>|]/', $src_file)) {
883
            dol_syslog("Refused to deliver file " . $src_file, LOG_WARNING);
884
            throw new RestException(500, "Refused to deliver file " . $src_file);
885
        }
886
887
        // Security:
888
        // We refuse cache files/dirs, upload using .. and pipes into filenames.
889
        if (preg_match('/^\./', basename($dest_file)) || preg_match('/\.\./', $dest_file) || preg_match('/[<>|]/', $dest_file)) {
890
            dol_syslog("Refused to deliver file " . $dest_file, LOG_WARNING);
891
            throw new RestException(500, "Refused to deliver file " . $dest_file);
892
        }
893
894
        $moreinfo = array('note_private' => 'File uploaded using API /documents from IP ' . getUserRemoteIP());
895
        if (!empty($object) && is_object($object) && $object->id > 0) {
896
            $moreinfo['src_object_type'] = $object->table_element;
897
            $moreinfo['src_object_id'] = $object->id;
898
        }
899
900
        // Move the temporary file at its final emplacement
901
        $result = dol_move($destfiletmp, $dest_file, 0, $overwriteifexists, 1, 1, $moreinfo);
902
        if (!$result) {
903
            throw new RestException(500, "Failed to move file into '" . $destfile . "'");
904
        }
905
906
        return dol_basename($destfile);
907
    }
908
909
    /**
910
     * Delete a document.
911
     *
912
     * @param string $modulepart Name of module or area concerned by file download ('product', ...)
913
     * @param string $original_file Relative path with filename, relative to modulepart (for example: PRODUCT-REF-999/IMAGE-999.jpg)
914
     * @return  array                   List of documents
915
     *
916
     * @url DELETE /
917
     *
918
     * @throws  RestException   400  Bad value for parameter modulepart
919
     * @throws  RestException   400  Bad value for parameter original_file
920
     * @throws  RestException   403  Access denied
921
     * @throws  RestException   404  File not found
922
     * @throws  RestException   500  Error on file operation
923
     */
924
    public function delete($modulepart, $original_file)
925
    {
926
        global $conf, $langs;
927
928
        if (empty($modulepart)) {
929
            throw new RestException(400, 'bad value for parameter modulepart');
930
        }
931
        if (empty($original_file)) {
932
            throw new RestException(400, 'bad value for parameter original_file');
933
        }
934
935
        //--- Finds and returns the document
936
        $entity = $conf->entity;
937
938
        // Special cases that need to use get_exdir to get real dir of object
939
        // If future, all object should use this to define path of documents.
940
        /*
941
        $tmpreldir = '';
942
        if ($modulepart == 'supplier_invoice') {
943
            $tmpreldir = get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier');
944
        }
945
946
        $relativefile = $tmpreldir.dol_sanitizeFileName($object->ref); */
947
        $relativefile = $original_file;
948
949
        $check_access = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'read');
950
        $accessallowed = $check_access['accessallowed'];
951
        $sqlprotectagainstexternals = $check_access['sqlprotectagainstexternals'];
952
        $original_file = $check_access['original_file'];
953
954
        if (preg_match('/\.\./', $original_file) || preg_match('/[<>|]/', $original_file)) {
955
            throw new RestException(403);
956
        }
957
        if (!$accessallowed) {
958
            throw new RestException(403);
959
        }
960
961
        $filename = basename($original_file);
962
        $original_file_osencoded = dol_osencode($original_file); // New file name encoded in OS encoding charset
963
964
        if (!file_exists($original_file_osencoded)) {
965
            dol_syslog("Try to download not found file " . $original_file_osencoded, LOG_WARNING);
966
            throw new RestException(404, 'File not found');
967
        }
968
969
        if (@unlink($original_file_osencoded)) {
970
            return array(
971
                'success' => array(
972
                    'code' => 200,
973
                    'message' => 'Document deleted'
974
                )
975
            );
976
        }
977
978
        throw new RestException(403);
979
    }
980
}
981