Passed
Push — master ( 1af52f...a0bd55 )
by Vítězslav
02:03
created

src/FlexiPeeHP/FlexiBeeRO.php (2 issues)

1
<?php
2
/**
3
 * FlexiPeeHP - Read Only Access to FlexiBee class.
4
 *
5
 * @author     Vítězslav Dvořák <[email protected]>
6
 * @copyright  (C) 2015-2018 Spoje.Net
7
 */
8
9
namespace FlexiPeeHP;
10
11
/**
12
 * Základní třída pro čtení z FlexiBee
13
 *
14
 * @url https://demo.flexibee.eu/devdoc/
15
 */
16
class FlexiBeeRO extends \Ease\Sand
17
{
18
    /**
19
     * Where to get JSON files with evidence stricture etc.
20
     * @var string
21
     */
22
    public static $infoDir = __DIR__.'/../../static';
23
24
    /**
25
     * Version of FlexiPeeHP library
26
     *
27
     * @var string
28
     */
29
    public static $libVersion = '1.20';
30
31
    /**
32
     * Základní namespace pro komunikaci s FlexiBee.
33
     * Basic namespace for communication with FlexiBee
34
     *
35
     * @var string Jmený prostor datového bloku odpovědi
36
     */
37
    public $nameSpace = 'winstrom';
38
39
    /**
40
     * URL of object data in FlexiBee
41
     * @var string url
42
     */
43
    public $apiURL = null;
44
45
    /**
46
     * Datový blok v poli odpovědi.
47
     * Data block in response field.
48
     *
49
     * @var string
50
     */
51
    public $resultField = 'results';
52
53
    /**
54
     * Verze protokolu použitého pro komunikaci.
55
     * Communication protocol version used.
56
     *
57
     * @var string Verze použitého API
58
     */
59
    public $protoVersion = '1.0';
60
61
    /**
62
     * Evidence užitá objektem.
63
     * Evidence used by object
64
     *
65
     * @link https://demo.flexibee.eu/c/demo/evidence-list Přehled evidencí
66
     * @var string
67
     */
68
    public $evidence = null;
69
70
    /**
71
     * Výchozí formát pro komunikaci.
72
     * Default communication format.
73
     *
74
     * @link https://www.flexibee.eu/api/dokumentace/ref/format-types Přehled možných formátů
75
     *
76
     * @var string json|xml|...
77
     */
78
    public $format = 'json';
79
80
    /**
81
     * formát příchozí odpovědi
82
     * response format
83
     *
84
     * @link https://www.flexibee.eu/api/dokumentace/ref/format-types Přehled možných formátů
85
     *
86
     * @var string json|xml|...
87
     */
88
    public $responseFormat = 'json';
89
90
    /**
91
     * Curl Handle.
92
     *
93
     * @var resource
94
     */
95
    public $curl = null;
96
97
    /**
98
     * @link https://demo.flexibee.eu/devdoc/company-identifier Identifikátor firmy
99
     * @var string
100
     */
101
    public $company = null;
102
103
    /**
104
     * Server[:port]
105
     * @var string
106
     */
107
    public $url = null;
108
109
    /**
110
     * REST API Username
111
     * @var string
112
     */
113
    public $user = null;
114
115
    /**
116
     * REST API Password
117
     * @var string
118
     */
119
    public $password = null;
120
121
    /**
122
     * @var array Pole HTTP hlaviček odesílaných s každým požadavkem
123
     */
124
    public $defaultHttpHeaders = ['User-Agent' => 'FlexiPeeHP'];
125
126
    /**
127
     * Default additional request url parameters after question mark
128
     *
129
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls   Common params
130
     * @link https://www.flexibee.eu/api/dokumentace/ref/paging Paging params
131
     * @var array
132
     */
133
    public $defaultUrlParams = ['limit' => 0];
134
135
    /**
136
     * Identifikační řetězec.
137
     *
138
     * @var string
139
     */
140
    public $init = null;
141
142
    /**
143
     * Sloupeček s názvem.
144
     *
145
     * @var string
146
     */
147
    public $nameColumn = 'nazev';
148
149
    /**
150
     * Sloupeček obsahující datum vložení záznamu do shopu.
151
     *
152
     * @var string
153
     */
154
    public $myCreateColumn = 'false';
155
156
    /**
157
     * Slopecek obsahujici datum poslení modifikace záznamu do shopu.
158
     *
159
     * @var string
160
     */
161
    public $myLastModifiedColumn = 'lastUpdate';
162
163
    /**
164
     * Klíčový idendifikátor záznamu.
165
     *
166
     * @var string
167
     */
168
    public $fbKeyColumn = 'id';
169
170
    /**
171
     * Informace o posledním HTTP requestu.
172
     *
173
     * @var *
174
     */
175
    public $curlInfo;
176
177
    /**
178
     * Informace o poslední HTTP chybě.
179
     *
180
     * @var string
181
     */
182
    public $lastCurlError = null;
183
184
    /**
185
     * Used codes storage.
186
     *
187
     * @var array
188
     */
189
    public $codes = null;
190
191
    /**
192
     * Last Inserted ID.
193
     *
194
     * @var int
195
     */
196
    public $lastInsertedID = null;
197
198
    /**
199
     * Default Line Prefix.
200
     *
201
     * @var string
202
     */
203
    public $prefix = '/c/';
204
205
    /**
206
     * Raw Content of last curl response
207
     *
208
     * @var string
209
     */
210
    public $lastCurlResponse;
211
212
    /**
213
     * HTTP Response code of last request
214
     *
215
     * @var int
216
     */
217
    public $lastResponseCode = null;
218
219
    /**
220
     * Body data  for next curl POST operation
221
     *
222
     * @var string
223
     */
224
    protected $postFields = null;
225
226
    /**
227
     * Last operation result data or message(s)
228
     *
229
     * @var array
230
     */
231
    public $lastResult = null;
232
233
    /**
234
     * Number from  @rowCount in response
235
     * @var int
236
     */
237
    public $rowCount = null;
238
239
    /**
240
     * Number from  @globalVersion
241
     * @var int
242
     */
243
    public $globalVersion = null;
244
245
    /**
246
     * @link https://www.flexibee.eu/api/dokumentace/ref/zamykani-odemykani/
247
     * @var string filter query
248
     */
249
    public $filter;
250
251
    /**
252
     * @link https://demo.flexibee.eu/devdoc/actions Provádění akcí
253
     * @var string
254
     */
255
    protected $action;
256
257
    /**
258
     * Pole akcí které podporuje ta která evidence
259
     * @link https://demo.flexibee.eu/c/demo/faktura-vydana/actions.json Např. Akce faktury
260
     * @var array
261
     */
262
    public $actionsAvailable = null;
263
264
    /**
265
     * Parmetry pro URL
266
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Všechny podporované parametry
267
     * @var array
268
     */
269
    public $urlParams = [
270
        'add-global-version',
271
        'add-row-count',
272
        'as-gui',
273
        'auth',
274
        'authSessionId',
275
        'code-as-id',
276
        'code-in-response',
277
        'delimeter',
278
        'detail', //See: https://www.flexibee.eu/api/dokumentace/ref/detail-levels
279
        'dir',
280
        'dry-run', // See: https://www.flexibee.eu/api/dokumentace/ref/dry-run/
281
        'encoding',
282
        'export-settings',
283
        'fail-on-warning',
284
        'filter',
285
        'format',
286
        'idUcetniObdobi',
287
        'includes',
288
        'inDesktopApp', // Note: Undocumented function (html only)
289
        'limit',
290
        'mode',
291
        'no-ext-ids',
292
        'no-http-errors',
293
        'no-ids',
294
        'only-ext-ids',
295
        'order',
296
        'relations',
297
        'report-lang',
298
        'report-name',
299
        'report-sign',
300
        'skupina-stitku',
301
        'sort',
302
        'start',
303
        'stitky-as-ids',
304
        'use-ext-id',
305
        'use-internal-id',
306
        'xpath', // See: https://www.flexibee.eu/api/dokumentace/ref/xpath/
307
    ];
308
309
    /**
310
     * Session ID
311
     * @var string
312
     */
313
    public $authSessionId = null;
314
315
    /**
316
     * Token obtained during login procedure
317
     * @var string 
318
     */
319
    public $refreshToken = null;
320
321
    /**
322
     * Save 404 results to log ?
323
     * @var boolean
324
     */
325
    protected $ignoreNotFound = false;
326
327
    /**
328
     * Array of errors caused by last request
329
     * @var array
330
     */
331
    private $errors = [];
332
333
    /**
334
     * List of Error500 reports sent
335
     * @var array
336
     */
337
    private $reports = [];
338
339
    /**
340 70
     * Send Error500 Report to
341
     * @var string email address
342 70
     */
343
    public $reportRecipient = '[email protected]';
344 70
345 70
    /**
346 70
     * Formating string for \DateTime::format() for datetime columns
347 70
     * @var string
348 22
     */
349 22
    static public $DateTimeFormat = 'Y-m-d\TH:i:s.u+P';
350 70
351
    /**
352
     * Formating string for \DateTime::format() for date columns
353
     * @var string
354
     */
355
    static public $DateFormat = 'Y-m-d';
356
357
    /**
358 71
     * Last Request response stats
359
     * @var array 
360 71
     */
361 71
    private $responseStats = null;
362 71
363 71
    /**
364 71
     * Chained Objects
365 23
     * @var array
366 23
     */
367 71
    public $chained = [];
368 71
369 23
    /**
370 23
     * We Connect to server by default
371 71
     * @var boolean
372
     */
373
    public $offline = false;
374 71
375 71
    /**
376 71
     * Override cURL timeout
377
     * @var int seconds
378
     */
379
    public $timeout = null;
380
381
    /**
382
     * Columns Info for serveral evidencies
383
     * @var array 
384
     */
385 48
    private $columnsInfo = [];
386
387 48
    /**
388
     * Class for read only interaction with FlexiBee.
389
     *
390 48
     * @param mixed $init default record id or initial data
391 48
     * @param array $options Connection settings and other options override
392 48
     */
393
    public function __construct($init = null, $options = [])
394 48
    {
395
        $this->init = $init;
396
397
        parent::__construct();
398
        $this->setUp($options);
399 94
        $this->curlInit();
400
        if (!empty($init)) {
401 94
            $this->processInit($init);
402 94
        }
403 94
    }
404 94
405 94
    /**
406 94
     * Set internal Object name
407 94
     *
408 94
     * @param string $objectName
409 94
     *
410 94
     * @return string Jméno objektu
411
     */
412
    public function setObjectName($objectName = null)
413
    {
414
        return parent::setObjectName(is_null($objectName) ? ( empty($this->getRecordIdent())
415
                    ? $this->getObjectName() : $this->getRecordIdent().'@'.$this->getObjectName() )
416
                    : $objectName);
417
    }
418
419
    /**
420
     * SetUp Object to be ready for work
421
     *
422
     * @param array $options Object Options ( user,password,authSessionId
423 13
     *                                        company,url,evidence,
424
     *                                        prefix,defaultUrlParams,debug,
425 13
     *                                        detail,offline,filter,ignore404
426 10
     *                                        timeout
427 13
     */
428 13
    public function setUp($options = [])
429 13
    {
430 10
        $this->setupProperty($options, 'company', 'FLEXIBEE_COMPANY');
431 10
        $this->setupProperty($options, 'url', 'FLEXIBEE_URL');
432 10
        $this->setupProperty($options, 'user', 'FLEXIBEE_LOGIN');
433 8
        $this->setupProperty($options, 'password', 'FLEXIBEE_PASSWORD');
434
        $this->setupProperty($options, 'authSessionId', 'FLEXIBEE_AUTHSESSID');
435 13
        $this->setupProperty($options, 'timeout', 'FLEXIBEE_TIMEOUT');
436
        if (!empty($this->authSessionId)) {
437
            $this->defaultHttpHeaders['X-authSessionId'] = $this->authSessionId;
438
        }
439
        if (isset($options['evidence'])) {
440
            $this->setEvidence($options['evidence']);
441
        }
442 23
        $this->setupProperty($options, 'defaultUrlParams');
443
        if (isset($options['prefix'])) {
444
            $this->setPrefix($options['prefix']);
445 23
        }
446 23
        if (array_key_exists('detail', $options)) {
447 23
            $this->defaultUrlParams['detail'] = $options['detail'];
448 23
        }
449 23
        $this->setupProperty($options, 'filter');
450 23
        if (array_key_exists('offline', $options)) {
451 23
            $this->offline = (boolean) $options['offline'];
452 23
        }
453 23
454 23
        if (array_key_exists('ignore404', $options)) {
455 23
            $this->ignore404($options['ignore404']);
456 23
        }
457 23
458 23
        $this->setupProperty($options, 'debug');
459 23
        $this->updateApiURL();
460 23
    }
461 23
462 23
    /**
463
     * Set up one of properties
464
     *
465
     * @param array  $options  array of given properties
466
     * @param string $name     name of property to process
467
     * @param string $constant load default property value from constant
468
     */
469
    public function setupProperty($options, $name, $constant = null)
470
    {
471 23
        if (array_key_exists($name, $options)) {
472
            $this->$name = $options[$name];
473 23
        } else {
474 23
            if (property_exists($this, $name) && !empty($constant) && defined($constant)) {
475
                $this->$name = constant($constant);
476
            }
477
        }
478
    }
479
480 23
    /**
481 23
     * Get Current connection options for use in another object
482 23
     *
483 23
     * @return array usable as second constructor parameter
484 23
     */
485
    public function getConnectionOptions()
486
    {
487
        $conOpts = ['url' => $this->url];
488
        if (empty($this->authSessionId)) {
489
            $conOpts ['user']    = $this->user;
490
            $conOpts['password'] = $this->password;
491
        } else {
492
            $conOpts['authSessionId'] = $this->authSessionId;
493
        }
494 23
        $company = $this->getCompany();
495
        if (!empty($company)) {
496 23
            $conOpts['company'] = $company;
497 23
        }
498 23
        if (!is_null($this->timeout)) {
499 23
            $conOpts['timeout'] = $this->timeout;
500
        }
501
        return $conOpts;
502
    }
503 23
504 23
    /**
505
     * Inicializace CURL
506
     *
507 20
     * @return boolean Online Status
508 20
     */
509
    public function curlInit()
510 20
    {
511 3
        if ($this->offline === false) {
512 3
            $this->curl = \curl_init(); // create curl resource
513 3
            curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); // return content as a string from curl_exec
514 3
            curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true); // follow redirects (compatibility for future changes in FlexiBee)
515 23
            curl_setopt($this->curl, CURLOPT_HTTPAUTH, true);       // HTTP authentication
516 23
            curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); // FlexiBee by default uses Self-Signed certificates
517 23
            curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
518
            curl_setopt($this->curl, CURLOPT_VERBOSE, ($this->debug === true)); // For debugging
519
            if (empty($this->authSessionId)) {
520
                curl_setopt($this->curl, CURLOPT_USERPWD,
521
                    $this->user.':'.$this->password); // set username and password
522
            }
523
            if (!is_null($this->timeout)) {
524
                curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout);
525
            }
526 69
        }
527
        return !$this->offline;
528 69
    }
529
530
    /**
531
     * Zinicializuje objekt dle daných dat. Možné hodnoty:
532
     *
533
     *  * 234                              - interní číslo záznamu k načtení
534
     *  * code:LOPATA                      - kód záznamu
535
     *  * BAGR                             - kód záznamu k načtení
536
     *  * ['id'=>24,'nazev'=>'hoblík']     - pole hodnot k předvyplnění
537 23
     *  * 743.json?relations=adresa,vazby  - část url s parametry k načtení
538
     *
539 23
     * @param mixed $init číslo/"(code:)kód"/(část)URI záznamu k načtení | pole hodnot k předvyplnění
540 23
     */
541
    public function processInit($init)
542
    {
543
        if (is_integer($init)) {
544
            $this->loadFromFlexiBee($init);
545
        } elseif (is_array($init)) {
546
            $this->takeData($init);
547
        } elseif (preg_match('/\.(json|xml|csv)/', $init)) {
548 23
            $this->takeData($this->getFlexiData((($init[0] != '/') ? $this->evidenceUrlWithSuffix($init)
549
                            : $init)));
550 23
        } else {
551
            $this->loadFromFlexiBee($init);
552
        }
553
    }
554
555
    /**
556
     * Set Data Field value
557
     *
558 25
     * @param string $columnName field name
559
     * @param mixed  $value      field data value
560 25
     *
561 25
     * @return bool Success
562
     */
563
    public function setDataValue($columnName, $value)
564 25
    {
565 1
        switch ($columnName) {
566 1
            case 'kod':
567 24
                $value = self::uncode($value); //Alwyas uncode "kod" column
568 24
                break;
569 24
            default:
570 25
                break;
571 25
        }
572
        if (is_object($value)) {
573
            switch (get_class($value)) {
574
                case 'DateTime':
575
                    $columnInfo = $this->getColumnInfo($columnName);
576
                    switch ($columnInfo['type']) {
577
                        case 'date':
578
                            $value = self::dateToFlexiDate($value);
579
                            break;
580
                        case 'datetime':
581 23
                            $value = self::dateToFlexiDateTime($value);
582
                            break;
583 23
                    }
584 23
                    break;
585 23
            }
586 23
        }
587 23
        return parent::setDataValue($columnName, $value);
588 23
    }
589 23
590 23
    /**
591 23
     * PHP Date object to FlexiBee date format
592 23
     * 
593 23
     * @param \DateTime $date
594 23
     */
595 23
    public static function dateToFlexiDate($date)
596
    {
597
        return $date->format(self::$DateFormat);
598
    }
599 23
600
    /**
601
     * PHP Date object to FlexiBee date format
602
     * 
603
     * @param \DateTime $dateTime
604
     */
605
    public static function dateToFlexiDateTime($dateTime)
606
    {
607
        return $dateTime->format(self::$DateTimeFormat);
608
    }
609 23
610
    /**
611 23
     * Set URL prefix
612 23
     *
613 22
     * @param string $prefix
614 22
     */
615 23
    public function setPrefix($prefix)
616 17
    {
617 17
        switch ($prefix) {
618 17
            case 'a': //Access
619 17
            case 'c': //Company
620 23
            case 'u': //User
621
            case 'g': //License Groups
622
            case 'admin':
623
            case 'status':
624 23
            case 'login-logout':
625
                $this->prefix = '/'.$prefix.'/';
626
                break;
627
            case null:
628
            case '':
629
            case '/':
630
                $this->prefix = '';
631
                break;
632
            default:
633
                throw new \Exception(sprintf('Unknown prefix %s', $prefix));
634 68
        }
635
    }
636 68
637 68
    /**
638 68
     * Set communication format.
639 62
     * One of html|xml|json|csv|dbf|xls|isdoc|isdocx|edi|pdf|pdf|vcf|ical
640 62
     *
641 68
     * @param string $format
642
     * 
643
     * @return boolen format is availble
644
     */
645
    public function setFormat($format)
646
    {
647
        $result = true;
648
        if (($this->debug === true) && !empty($this->evidence) && isset(Formats::$$this->evidence)) {
649
            if (array_key_exists($format, array_flip(Formats::$$this->evidence))
650
                === false) {
651 23
                $result = false;
652
            }
653 23
        }
654 23
        if ($result === true) {
655 23
            $this->format = $format;
656 23
            $this->updateApiURL();
657 23
        }
658 23
        return $result;
659 23
    }
660 23
661 23
    /**
662
     * Nastaví Evidenci pro Komunikaci.
663
     * Set evidence for communication
664
     *
665
     * @param string $evidence evidence pathName to use
666
     * 
667 48
     * @return boolean evidence switching status
668
     */
669 48
    public function setEvidence($evidence)
670 48
    {
671 48
        switch ($this->prefix) {
672
            case '/c/':
673
                if ($this->debug === true) {
674 48
                    if (array_key_exists($evidence, EvidenceList::$name)) {
675 48
                        $this->evidence = $evidence;
676
                        $result         = true;
677
                    } else {
678
                        throw new \Exception(sprintf('Try to set unsupported evidence %s',
679
                            $evidence));
680
                    }
681
                } else {
682
                    $this->evidence = $evidence;
683
                    $result         = true;
684
                }
685
                break;
686 23
            default:
687
                $this->evidence = $evidence;
688 23
                $result         = true;
689 23
                break;
690 23
        }
691 23
        $this->updateApiURL();
692 23
        return $result;
693 23
    }
694 23
695 23
    /**
696 23
     * Vrací právě používanou evidenci pro komunikaci
697 23
     * Obtain current used evidence
698 23
     *
699 23
     * @return string
700 23
     */
701
    public function getEvidence()
702
    {
703 23
        return $this->evidence;
704 23
    }
705 23
706
    /**
707
     * Set used company.
708 23
     * Nastaví Firmu.
709
     *
710
     * @param string $company
711
     */
712
    public function setCompany($company)
713
    {
714
        $this->company = $company;
715
    }
716
717
    /**
718 23
     * Obtain company now used
719
     * Vrací právě používanou firmu
720 23
     *
721
     * @return string
722
     */
723
    public function getCompany()
724
    {
725
        return $this->company;
726
    }
727
728
    /**
729
     * Vrací název evidence použité v odpovědích z FlexiBee
730
     *
731 25
     * @return string
732
     */
733
    public function getResponseEvidence()
734 25
    {
735
        switch ($this->evidence) {
736 25
            case 'c':
737
                $evidence = 'company';
738 25
                break;
739 4
            case 'evidence-list':
740 4
                $evidence = 'evidence';
741 22
                break;
742
            default:
743
                $evidence = $this->getEvidence();
744 25
                break;
745
        }
746 25
        return $evidence;
747 25
    }
748
749
    /**
750
     * Převede rekurzivně Objekt na pole.
751
     *
752
     * @param object|array $object
753
     *
754
     * @return array
755
     */
756
    public static function object2array($object)
757
    {
758 3
        $result = null;
759
        if (is_object($object)) {
760 3
            $objectData = get_object_vars($object);
761
            if (is_array($objectData) && count($objectData)) {
762 3
                $result = array_map('self::object2array', $objectData);
763 3
            }
764 3
        } else {
765
            if (is_array($object)) {
766
                foreach ($object as $item => $value) {
767
                    $result[$item] = self::object2array($value);
768
                }
769
            } else {
770
                $result = $object;
771
            }
772
        }
773 3
774
        return $result;
775 3
    }
776
777
    /**
778
     * Převede rekurzivně v poli všechny objekty na jejich identifikátory.
779
     *
780
     * @param object|array $object
781
     *
782
     * @return array
783
     */
784
    public static function objectToID($object)
785 3
    {
786
        $resultID = null;
787 3
        if (is_object($object) && method_exists($object, '__toString')
788 3
        ) {
789 3
            $resultID = $object->__toString();
790 3
        } else {
791
            if (is_array($object)) {
792
                foreach ($object as $item => $value) {
793 3
                    $resultID[$item] = self::objectToID($value);
794
                }
795
            } else { //String
796
                $resultID = $object;
797 3
            }
798
        }
799
800
        return $resultID;
801
    }
802
803
    /**
804
     * Return basic URL for used Evidence
805
     *
806
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
807
     *
808
     * @return string Evidence URL
809
     */
810
    public function getEvidenceURL()
811
    {
812
        $evidenceUrl = $this->url.$this->prefix.$this->company;
813
        $evidence    = $this->getEvidence();
814
        if (!empty($evidence)) {
815
            $evidenceUrl .= '/'.$evidence;
816
        }
817
        return $evidenceUrl;
818
    }
819
820 3
    /**
821
     * Add suffix to Evidence URL
822 3
     *
823
     * @param string $urlSuffix
824 3
     *
825
     * @return string
826
     */
827
    public function evidenceUrlWithSuffix($urlSuffix)
828
    {
829
        $evidenceUrl = $this->getEvidenceUrl();
830
        if (!empty($urlSuffix)) {
831
            if (($urlSuffix[0] != '/') && ($urlSuffix[0] != ';') && ($urlSuffix[0]
832 3
                != '?')) {
833 3
                $evidenceUrl .= '/';
834 3
            }
835
            $evidenceUrl .= $urlSuffix;
836
        }
837 3
        return $evidenceUrl;
838
    }
839
840 3
    /**
841
     * Update $this->apiURL
842
     */
843
    public function updateApiURL()
844
    {
845
        $this->apiURL = $this->getEvidenceURL();
846
        $id           = $this->getRecordID();
847
        if (empty($id)) {
848
            $id = $this->getRecordCode();
849
        }
850
        if (!empty($id)) {
851
            $this->apiURL .= '/'.self::urlEncode($id);
852
        }
853
        $this->apiURL .= '.'.$this->format;
854
    }
855
    /*
856
     * Add Default Url params to given url if not overrided
857
     *
858
     * @param string $urlRaw
859
     *
860 3
     * @return string url with default params added
861
     */
862
863
    public function addDefaultUrlParams($urlRaw)
864
    {
865
        return \Ease\Shared::addUrlParams($urlRaw, $this->defaultUrlParams,
866
                false);
867
    }
868
869
    /**
870
     * Funkce, která provede I/O operaci a vyhodnotí výsledek.
871
     *
872
     * @param string $urlSuffix část URL za identifikátorem firmy.
873
     * @param string $method    HTTP/REST metoda
874
     * @param string $format    Requested format
875
     * 
876
     * @return array|boolean Výsledek operace
877
     */
878
    public function performRequest($urlSuffix = null, $method = 'GET',
879
                                   $format = null)
880
    {
881
        $this->rowCount      = null;
882
        $this->responseStats = [];
883
884
        if (preg_match('/^http/', $urlSuffix)) {
885
            $url = $urlSuffix;
886
        } elseif (strlen($urlSuffix) && ($urlSuffix[0] == '/')) {
887
            $url = $this->url.$urlSuffix;
888
        } else {
889
            $url = $this->evidenceUrlWithSuffix($urlSuffix);
890 3
        }
891
892 3
        $responseCode = $this->doCurlRequest($this->addDefaultUrlParams($url),
893 3
            $method, $format);
894 3
895 3
        return $this->parseResponse($this->rawResponseToArray($this->lastCurlResponse,
896
                    $this->responseFormat), $responseCode);
897 3
    }
898
899 3
    /**
900
     * Parse Raw FlexiBee response in several formats
901 3
     *
902
     * @param string $responseRaw raw response body
903 3
     * @param string $format      Raw Response format json|xml|etc
904
     *
905 3
     * @return array
906 3
     */
907 3
    public function rawResponseToArray($responseRaw, $format)
908 3
    {
909 3
        $responseDecoded = [];
910 3
        if (!empty(trim($responseRaw))) {
911 3
            switch ($format) {
912 3
                case 'json':
913 3
                    $responseDecoded = $this->rawJsonToArray($responseRaw);
914 3
                    break;
915 3
                case 'xml':
916 3
                    $responseDecoded = $this->rawXmlToArray($this->lastCurlResponse);
917 3
                    break;
918
                case 'txt':
919 3
                default:
920
                    $responseDecoded = [$this->lastCurlResponse];
921
                    break;
922 3
            }
923 3
        }
924 3
        return $responseDecoded;
925 3
    }
926 3
927 3
    /**
928 3
     * Convert FlexiBee Response JSON to Array
929 3
     *
930 3
     * @param string $rawJson
931
     *
932
     * @return array
933
     */
934
    public function rawJsonToArray($rawJson)
935 3
    {
936
        $responseDecoded = json_decode($rawJson, true, 10);
937
        $decodeError     = json_last_error_msg();
938
        if ($decodeError == 'No error') {
939 3
            if (array_key_exists($this->nameSpace, $responseDecoded)) {
940
                $responseDecoded = $responseDecoded[$this->nameSpace];
941
            }
942
        } else {
943
            $this->addStatusMessage('JSON Decoder: '.$decodeError, 'error');
944
            $this->addStatusMessage($rawJson, 'debug');
945
        }
946
        return $responseDecoded;
947
    }
948
949 23
    /**
950
     * Convert FlexiBee Response XML to Array
951 23
     *
952 23
     * @param string $rawXML
953 23
     *
954 23
     * @return array
955 15
     */
956 15
    public function rawXmlToArray($rawXML)
957 15
    {
958 23
        return self::xml2array($rawXML);
959
    }
960
961
    /**
962
     * Parse Response array
963
     *
964
     * @param array $responseDecoded
965
     * @param int $responseCode Request Response Code
966
     *
967
     * @return array main data part of response
968 23
     */
969
    public function parseResponse($responseDecoded, $responseCode)
970 23
    {
971 23
        if (is_array($responseDecoded)) {
972 23
            $mainResult          = $this->unifyResponseFormat($responseDecoded);
973 23
            $this->responseStats = array_key_exists('stats', $responseDecoded) ? (isset($responseDecoded['stats'][0])
974 23
                    ? $responseDecoded['stats'][0] : $responseDecoded['stats']) : null;
975
        } else {
976 23
            $mainResult = $responseDecoded;
977 23
        }
978 23
        switch ($responseCode) {
979 23
            case 201: //Success Write
980 23
            case 200: //Success Read
981
                if (is_array($responseDecoded)) {
982 23
                    $this->lastResult = $mainResult;
983 23
                    if (isset($responseDecoded['@rowCount'])) {
984 23
                        $this->rowCount = (int) $responseDecoded['@rowCount'];
985
                    }
986
                    if (isset($responseDecoded['@globalVersion'])) {
987
                        $this->globalVersion = (int) $responseDecoded['@globalVersion'];
988
                    }
989
                }
990 1
                break;
991
992 1
            case 500: // Internal Server Error
993 1
                if ($this->debug === true) {
994 1
                    $this->error500Reporter($responseDecoded);
995 1
                }
996 1
            case 404: // Page not found
997
                if ($this->ignoreNotFound === true) {
998
                    break;
999
                }
1000
            case 400: //Bad Request parameters
1001 1
            default: //Something goes wrong
1002
                $this->addStatusMessage($this->lastResponseCode.': '.$this->curlInfo['url'],
1003 1
                    'warning');
1004 1
                if (is_array($responseDecoded)) {
1005
                    $this->parseError($responseDecoded);
1006
                }
1007
                $this->logResult($responseDecoded, $this->curlInfo['url']);
1008
                break;
1009
        }
1010
        return $mainResult;
1011
    }
1012
1013 23
    /**
1014
     * Parse error message response
1015 23
     *
1016 23
     * @param array $responseDecoded
1017 22
     * 
1018
     * @return int number of errors processed
1019
     */
1020
    public function parseError(array $responseDecoded)
1021 22
    {
1022
        if (array_key_exists('results', $responseDecoded)) {
1023
            $this->errors = $responseDecoded['results'][0]['errors'];
1024
            foreach ($this->errors as $errorInfo) {
1025
                $this->addStatusMessage($errorInfo['message'], 'error');
1026
                if (array_key_exists('for', $errorInfo)) {
1027
                    unset($errorInfo['message']);
1028
                    $this->addStatusMessage(json_encode($errorInfo), 'debug');
1029
                }
1030
            }
1031
        } else {
1032
            if (array_key_exists('message', $responseDecoded)) {
1033
                $this->errors = [['message' => $responseDecoded['message']]];
1034
            }
1035
        }
1036
        return count($this->errors);
1037
    }
1038
1039
    /**
1040
     * Vykonej HTTP požadavek
1041
     *
1042
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
1043
     * @param string $url    URL požadavku
1044
     * @param string $method HTTP Method GET|POST|PUT|OPTIONS|DELETE
1045
     * @param string $format požadovaný formát komunikace
1046
     * 
1047
     * @return int HTTP Response CODE
1048
     */
1049
    public function doCurlRequest($url, $method, $format = null)
1050
    {
1051
        if (is_null($format)) {
1052
            $format = $this->format;
1053
        }
1054
        curl_setopt($this->curl, CURLOPT_URL, $url);
1055
// Nastavení samotné operace
1056
        curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));
1057
//Vždy nastavíme byť i prázná postdata jako ochranu před chybou 411
1058
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->postFields);
1059 15
1060
        $httpHeaders = $this->defaultHttpHeaders;
1061 15
1062 15
        $formats = Formats::bySuffix();
1063
1064 15
        if (!isset($httpHeaders['Accept'])) {
1065 8
            $httpHeaders['Accept'] = $formats[$format]['content-type'];
1066 7
        }
1067 7
        if (!isset($httpHeaders['Content-Type'])) {
1068 7
            $httpHeaders['Content-Type'] = $formats[$format]['content-type'];
1069
        }
1070 8
        $httpHeadersFinal = [];
1071 1
        foreach ($httpHeaders as $key => $value) {
1072 1
            if (($key == 'User-Agent') && ($value == 'FlexiPeeHP')) {
1073 8
                $value .= ' v'.self::$libVersion;
1074
            }
1075 15
            $httpHeadersFinal[] = $key.': '.$value;
1076 4
        }
1077 4
1078 4
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $httpHeadersFinal);
1079
1080
// Proveď samotnou operaci
1081
        $this->lastCurlResponse            = curl_exec($this->curl);
1082
        $this->curlInfo                    = curl_getinfo($this->curl);
1083 4
        $this->curlInfo['when']            = microtime();
1084
        $this->curlInfo['request_headers'] = $httpHeadersFinal;
1085 15
        $this->responseFormat              = $this->contentTypeToResponseFormat($this->curlInfo['content_type'],
1086
            $url);
1087 15
        $this->lastResponseCode            = $this->curlInfo['http_code'];
1088 15
        $this->lastCurlError               = curl_error($this->curl);
1089
        if (strlen($this->lastCurlError)) {
1090
            $this->addStatusMessage(sprintf('Curl Error (HTTP %d): %s',
1091 15
                    $this->lastResponseCode, $this->lastCurlError), 'error');
1092
        }
1093 15
1094 15
        if ($this->debug === true) {
1095 15
            $this->saveDebugFiles();
1096
        }
1097 15
1098
        return $this->lastResponseCode;
1099 15
    }
1100 15
1101 15
    /**
1102 9
     * Obtain json for application/json
1103 9
     * 
1104 6
     * @param string $contentType
1105 6
     * @param string $url
1106 9
     * 
1107 6
     * @return string response format
1108
     */
1109
    public function contentTypeToResponseFormat($contentType, $url = null)
1110 15
    {
1111
        if (!empty($url)) {
1112
            $url = parse_url($url, PHP_URL_PATH);
1113
        }
1114
1115
        $contentTypeClean = strstr($contentType, ';') ? substr($contentType, 0,
1116
                strpos($contentType, ';')) : $contentType;
1117
1118
        switch ($url) {
1119
            case '/login-logout/login';
1120
                $responseFormat = 'json';
1121 23
                break;
1122
            default :
1123 23
                switch ($contentTypeClean) {
1124 23
                    case 'text/javascript':
1125 23
                        $responseFormat = 'js';
1126 23
                        break;
1127 23
1128
                    default:
1129
                        $responseFormat = Formats::contentTypeToSuffix($contentTypeClean);
1130
                        break;
1131 23
                }
1132
                break;
1133
        }
1134
1135 23
        return $responseFormat;
1136 22
    }
1137 22
1138 9
    /**
1139 9
     * Nastaví druh prováděné akce.
1140 22
     *
1141
     * @link https://demo.flexibee.eu/devdoc/actions Provádění akcí
1142
     * @param string $action
1143
     * 
1144
     * @return boolean
1145
     */
1146
    public function setAction($action)
1147
    {
1148
        $result           = false;
1149
        $actionsAvailable = $this->getActionsInfo();
1150
        if (is_array($actionsAvailable) && array_key_exists($action,
1151 6
                $actionsAvailable)) {
1152
            $this->action = $action;
1153
            $result       = true;
1154 6
        }
1155 6
        return $result;
1156 6
    }
1157 6
1158 6
    /**
1159
     * Convert XML to array.
1160 6
     *
1161
     * @param string $xml
1162
     *
1163
     * @return array
1164
     */
1165 6
    public static function xml2array($xml)
1166
    {
1167
        $arr = [];
1168
        if (!empty($xml)) {
1169 6
            if (is_string($xml)) {
1170
                $xml = simplexml_load_string($xml);
1171
            }
1172
            foreach ($xml->attributes() as $a) {
1173
                $arr['@'.$a->getName()] = strval($a);
1174
            }
1175
            foreach ($xml->children() as $r) {
1176
                if (count($r->children()) == 0) {
1177 16
                    $arr[$r->getName()] = strval($r);
1178
                } else {
1179 16
                    $arr[$r->getName()][] = self::xml2array($r);
1180 9
                }
1181 9
            }
1182 16
        }
1183 16
        return $arr;
1184 16
    }
1185
1186 16
    /**
1187 16
     * Odpojení od FlexiBee.
1188 16
     */
1189 15
    public function disconnect()
1190 15
    {
1191
        if (is_resource($this->curl)) {
1192
            curl_close($this->curl);
1193
        }
1194
        $this->curl = null;
1195
    }
1196
1197
    /**
1198
     * Disconnect CURL befere pass away
1199 15
     */
1200
    public function __destruct()
1201
    {
1202 15
        $this->disconnect();
1203 10
    }
1204 10
1205 15
    /**
1206 15
     * Načte řádek dat z FlexiBee.
1207 15
     *
1208 15
     * @param int $recordID id požadovaného záznamu
1209
     *
1210 14
     * @return array
1211 14
     */
1212 14
    public function getFlexiRow($recordID)
1213 14
    {
1214 10
        $record   = null;
1215
        $response = $this->performRequest($this->evidence.'/'.$recordID.'.json');
1216 14
        if (isset($response[$this->evidence])) {
1217 14
            $record = $response[$this->evidence][0];
1218
        }
1219
1220
        return $record;
1221
    }
1222
1223
    /**
1224
     * Oddělí z pole podmínek ty jenž patří za ? v URL požadavku
1225
     *
1226
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
1227
     * @param array $conditions pole podmínek   - rendrují se do ()
1228
     * @param array $urlParams  pole parametrů  - rendrují za ?
1229
     */
1230
    public function extractUrlParams(&$conditions, &$urlParams)
1231
    {
1232
        foreach ($this->urlParams as $urlParam) {
1233
            if (isset($conditions[$urlParam])) {
1234
                \Ease\Sand::divDataArray($conditions, $urlParams, $urlParam);
1235
            }
1236
        }
1237
    }
1238
1239
    /**
1240
     * convert unicode to entities for use with FlexiBee queries
1241
     *
1242
     * @param string $urlRaw
1243
     * 
1244
     * @return string
1245
     */
1246
    public static function urlEncode($urlRaw)
1247
    {
1248
        return str_replace(['%27', '%3A'], ["'", ':'], rawurlencode($urlRaw));
1249
    }
1250
1251
    /**
1252 15
     * Načte data z FlexiBee.
1253
     *
1254
     * @param string $suffix     dotaz
1255 15
     * @param string|array $conditions Volitelný filtrovací výraz
1256 15
     *
1257 15
     * @return array Data obtained
1258
     */
1259 15
    public function getFlexiData($suffix = null, $conditions = null)
1260 15
    {
1261 15
        $finalUrl  = '';
1262 15
        $urlParams = $this->defaultUrlParams;
1263 15
1264 15
        if (!empty($conditions)) {
1265 15
            if (is_array($conditions)) {
1266 15
                $this->extractUrlParams($conditions, $urlParams);
1267
                $conditions = $this->flexiUrl($conditions);
1268 15
            }
1269
1270
            if (strlen($conditions) && ($conditions[0] != '/')) {
1271 15
                $conditions = '('.self::urlEncode($conditions).')';
1272
            }
1273
        }
1274 15
1275 15
        if (strlen($suffix)) {
1276 15
            if (preg_match('/^http/', $suffix) || ($suffix[0] == '/') || is_numeric($suffix)) {
1277 15
                $finalUrl = $suffix;
1278 15
            } else {
1279
                if (preg_match('/^(code|ext):(.*)/', $suffix, $matches)) {
1280 15
                    $finalUrl = $matches[1].':'.rawurlencode($matches[2]);
1281
                }
1282 15
            }
1283
        }
1284 15
1285 9
        $finalUrl .= $conditions;
1286 9
1287
        if (count($urlParams)) {
1288 15
            if (strstr($finalUrl, '?')) {
1289
                $finalUrl .= '&';
1290
            } else {
1291
                $finalUrl .= '?';
1292
            }
1293
            $finalUrl .= http_build_query($urlParams, null, '&',
1294
                PHP_QUERY_RFC3986);
1295
        }
1296
1297
        $transactions = $this->performRequest($finalUrl, 'GET');
1298
1299 23
        $responseEvidence = $this->getResponseEvidence();
1300
        if (is_array($transactions) && array_key_exists($responseEvidence,
1301 23
                $transactions)) {
1302
            $result = $transactions[$responseEvidence];
1303 23
            if ((count($result) == 1) && (count(current($result)) == 0 )) {
1304 23
                $result = null; // Response is empty Array
1305 23
            }
1306
        } else {
1307 23
            $result = $transactions;
1308 23
        }
1309 23
1310
        return $result;
1311 23
    }
1312 23
1313 23
    /**
1314 23
     * Načte záznam z FlexiBee a uloží v sobě jeho data
1315 23
     * Read FlexiBee record and store it inside od object
1316 23
     *
1317 23
     * @param int $id ID or conditions
1318 23
     *
1319 23
     * @return int počet načtených položek
1320 23
     */
1321
    public function loadFromFlexiBee($id = null)
1322
    {
1323
        $data = [];
1324 23
        if (is_null($id)) {
1325 23
            $id = $this->getMyKey();
1326 23
        }
1327
        $flexidata = $this->getFlexiData($this->getEvidenceUrl().'/'.self::urlizeId($id));
1328 23
        if ($this->lastResponseCode == 200) {
1329 23
            $this->apiURL = $this->curlInfo['url'];
1330 23
            if (is_array($flexidata) && (count($flexidata) == 1) && is_array(current($flexidata))) {
1331 23
                $data = current($flexidata);
1332
            }
1333
        }
1334 23
        return $this->takeData($data);
1335 23
    }
1336 23
1337 23
    /**
1338 23
     * Set Filter code for requests
1339 23
     *
1340 23
     * @link https://www.flexibee.eu/api/dokumentace/ref/zamykani-odemykani/
1341 23
     *
1342 23
     * @param array|string $filter filter formula or ['key'=>'value']
1343 23
     *
1344 23
     * @return string Filter code
1345 23
     */
1346
    public function setFilter($filter)
1347 23
    {
1348 23
        return $this->filter = is_array($filter) ? self::flexiUrl($filter) : $filter;
1349
    }
1350 23
1351
    /**
1352
     * Převede data do Json formátu pro FlexiBee.
1353
     * Convert data to FlexiBee like Json format
1354
     *
1355
     * @url https://www.flexibee.eu/api/dokumentace/ref/actions/
1356
     * @url https://www.flexibee.eu/api/dokumentace/ref/zamykani-odemykani/
1357
     *
1358
     * @param array $data    object data
1359
     * @param int   $options json_encode options like JSON_PRETTY_PRINT etc
1360 23
     *
1361
     * @return string
1362 23
     */
1363 23
    public function getJsonizedData($data = null, $options = 0)
1364 23
    {
1365
        if (is_null($data)) {
1366
            $data = $this->getData();
1367 23
        }
1368 23
1369 23
        $dataToJsonize = array_merge(['@version' => $this->protoVersion],
1370 23
            $this->getDataForJSON($data));
1371 23
        $jsonRaw       = json_encode([$this->nameSpace => $dataToJsonize],
1372
            $options);
1373
1374 23
        return $jsonRaw;
1375 23
    }
1376 23
1377
    /**
1378 23
     * Get Data Fragment specific for current object
1379 23
     *
1380 23
     * @param array $data
1381 23
     *
1382 23
     * @return array
1383
     */
1384 23
    public function getDataForJSON($data = null)
1385 23
    {
1386 23
        if (is_null($data)) {
1387 23
            $data = $this->getData();
1388 23
        }
1389
1390 23
        $dataForJson = [$this->getEvidence() => $this->objectToID($data)];
1391 23
1392 23
        if (!is_null($this->action)) {
1393 23
            $dataForJson[$this->evidence.'@action'] = $this->action;
1394
            $this->action                           = null;
1395
        }
1396 23
1397
        if (!is_null($this->filter)) {
1398
            $dataForJson[$this->evidence.'@filter'] = $this->filter;
1399 23
        }
1400
1401
1402 23
        foreach ($this->chained as $chained) {
1403 23
            $chainedData = $chained->getDataForJSON();
1404 23
            foreach ($chainedData as $chainedItemEvidence => $chainedItemData) {
1405 23
                if (array_key_exists($chainedItemEvidence, $dataForJson)) {
1406 23
                    if (is_string(key($dataForJson[$chainedItemEvidence]))) {
1407 23
                        $dataBackup                          = $dataForJson[$chainedItemEvidence];
1408
                        $dataForJson[$chainedItemEvidence]   = [];
1409
                        $dataForJson[$chainedItemEvidence][] = $dataBackup;
1410
                    }
1411
                    if (array_key_exists(0, $chainedItemData)) {
1412
                        foreach ($chainedItemData as $chainedItem) {
1413
                            $dataForJson[$chainedItemEvidence][] = $chainedItem;
1414
                        }
1415
                    } else {
1416
                        $dataForJson[$chainedItemEvidence][] = $chainedItemData;
1417
                    }
1418
                } else {
1419
                    $dataForJson[$chainedItemEvidence] = $chainedItemData;
1420
                }
1421
            }
1422
        }
1423
1424
1425
        return $dataForJson;
1426
    }
1427
1428
    /**
1429
     * Join another FlexiPeeHP Object
1430
     *
1431
     * @param FlexiBeeRO $object
1432
     *
1433
     * @return boolean adding to stack success
1434
     */
1435
    public function join(&$object)
1436
    {
1437
        $result = true;
1438
        if (method_exists($object, 'getDataForJSON')) {
1439
            $this->chained[] = $object;
1440
        } else {
1441
            throw new \Ease\Exception('$object->getDataForJSON() does not exist');
1442
        }
1443
1444 23
        return $result;
1445
    }
1446 23
1447
    /**
1448 23
     * Prepare record ID to use in URL
1449 23
     * 
1450 23
     * @param mixed $id
1451 23
     * 
1452 23
     * @return string id ready for use in URL
1453 23
     */
1454 23
    public static function urlizeId($id)
1455 23
    {
1456 23
        if (is_array($id)) {
1457
            $id = rawurlencode('('.self::flexiUrl($id).')');
1458 23
        } else if (preg_match('/^ext:/', $id)) {
1459 23
            $id = self::urlEncode($id);
1460 23
        } else if (preg_match('/^code:/', $id)) {
1461 23
            $id = self::code(self::urlEncode(self::uncode($id)));
1462 23
        }
1463
        return $id;
1464
    }
1465 23
1466 23
    /**
1467
     * Test if given record ID exists in FlexiBee.
1468
     *
1469 23
     * @param mixed $identifer presence state
1470
     *
1471 23
     * @return boolean
1472 23
     */
1473
    public function idExists($identifer = null)
1474 23
    {
1475
        if (is_null($identifer)) {
1476
            $identifer = $this->getMyKey();
1477 23
        }
1478 23
        $ignorestate = $this->ignore404();
1479
        $this->ignore404(true);
1480
        $this->getFlexiData(null,
1481
            [
1482
            'detail' => 'custom:'.$this->getKeyColumn(),
1483
            $this->getKeyColumn() => $identifer
1484
        ]);
1485
        $this->ignore404($ignorestate);
1486
        return $this->lastResponseCode == 200;
1487
    }
1488
1489 65
    /**
1490
     * Test if given record exists in FlexiBee.
1491 65
     *
1492 65
     * @param array|string|int $data ext:id:23|code:ITEM|['id'=>23]|23
1493
     * 
1494
     * @return boolean Record presence status
1495 65
     */
1496 65
    public function recordExists($data = [])
1497
    {
1498
1499
        if (empty($data)) {
1500
            $data = $this->getData();
1501 65
        }
1502
        $ignorestate = $this->ignore404();
1503
        $this->ignore404(true);
1504
        $keyColumn   = $this->getKeyColumn();
1505
        $res         = $this->getColumnsFromFlexibee([$keyColumn],
1506
            is_array($data) ? $data : [$keyColumn => $data]);
1507
1508
        if (empty($res) || (isset($res['success']) && ($res['success'] == 'false'))
1509
            || ((isset($res) && is_array($res)) && !isset($res[0]) )) {
1510
            $found = false;
1511 71
        } else {
1512
            $found = true;
1513 71
        }
1514
        $this->ignore404($ignorestate);
1515
        return $found;
1516
    }
1517
1518
    /**
1519
     * Subitems - ex. items of invoice
1520
     * 
1521
     * @return array of document items or null
1522 23
     */
1523
    public function getSubItems()
1524 23
    {
1525
        return array_key_exists('polozkyFaktury', $this->getData()) ? $this->getDataValue('polozkyFaktury')
1526
                : (array_key_exists('polozkyDokladu', $this->getData()) ? $this->getDataValue('polozkyDokladu')
1527
                : null);
1528
    }
1529
1530
    /**
1531
     * Vrací z FlexiBee sloupečky podle podmínek.
1532 15
     *
1533
     * @param array|int|string $conditions pole podmínek nebo ID záznamu
1534 15
     * @param string           $indexBy    klice vysledku naplnit hodnotou ze
1535 15
     *                                     sloupečku
1536 15
     * @return array
1537 15
     */
1538 15
    public function getAllFromFlexibee($conditions = null, $indexBy = null)
1539 9
    {
1540 9
        if (is_int($conditions)) {
1541 15
            $conditions = [$this->getmyKeyColumn() => $conditions];
1542
        }
1543
1544
        $flexiData = $this->getFlexiData('', $conditions);
1545
1546
        if (!is_null($indexBy)) {
1547
            $flexiData = $this->reindexArrayBy($flexiData);
1548
        }
1549
1550 23
        return $flexiData;
1551
    }
1552 23
1553 23
    /**
1554 23
     * Vrací z FlexiBee sloupečky podle podmínek.
1555 23
     *
1556 23
     * @param string[] $columnsList seznam položek
1557 23
     * @param array    $conditions  pole podmínek nebo ID záznamu
1558 23
     * @param string   $indexBy     Sloupeček podle kterého indexovat záznamy
1559 23
     *
1560 23
     * @return array
1561 23
     */
1562 23
    public function getColumnsFromFlexibee($columnsList, $conditions = [],
1563 23
                                           $indexBy = null)
1564 23
    {
1565 23
        $detail = 'full';
1566
        switch (gettype($columnsList)) {
1567 23
            case 'integer': //Record ID
1568
                $conditions = [$this->getmyKeyColumn() => $conditions];
1569
            case 'array': //Few Conditions
1570
                if (!is_null($indexBy) && !array_key_exists($indexBy,
1571
                        $columnsList)) {
1572
                    $columnsList[] = $indexBy;
1573
                }
1574
                $columns = implode(',', array_unique($columnsList));
1575
                $detail  = 'custom:'.$columns;
1576
            default:
1577 22
                switch ($columnsList) {
1578
                    case 'id':
1579 22
                        $detail = 'id';
1580
                        break;
1581 21
                    case 'summary':
1582
                        $detail = 'summary';
1583
                        break;
1584
                    default:
1585
                        break;
1586
                }
1587
                break;
1588
        }
1589 22
1590
        $conditions['detail'] = $detail;
1591 22
1592 22
        $flexiData = $this->getFlexiData(null, $conditions);
1593 22
1594
        if (is_string($indexBy) && is_array($flexiData) && array_key_exists(0,
1595
                $flexiData) && array_key_exists($indexBy, $flexiData[0])) {
1596 22
            $flexiData = $this->reindexArrayBy($flexiData, $indexBy);
1597
        }
1598
1599
        return $flexiData;
1600
    }
1601
1602
    /**
1603
     * Vrací kód záznamu.
1604
     * Obtain record CODE
1605 22
     *
1606
     * @param mixed $data
1607 22
     *
1608 22
     * @return string
1609 22
     */
1610 22
    public function getKod($data = null, $unique = true)
1611 22
    {
1612 22
        $kod = null;
1613 22
1614 22
        if (is_null($data)) {
1615 22
            $data = $this->getData();
1616 22
        }
1617 22
1618 22
        if (is_string($data)) {
1619
            $data = [$this->nameColumn => $data];
1620 22
        }
1621
1622
        if (isset($data['kod'])) {
1623
            $kod = $data['kod'];
1624
        } else {
1625
            if (isset($data[$this->nameColumn])) {
1626
                $kod = preg_replace('/[^a-zA-Z0-9]/', '',
1627
                    \Ease\Sand::rip($data[$this->nameColumn]));
1628
            } else {
1629
                if (isset($data[$this->keyColumn])) {
1630
                    $kod = \Ease\Sand::rip($data[$this->keyColumn]);
1631
                }
1632 22
            }
1633
            $kod = substr($kod, 0, 20);
1634
        }
1635
1636
        if (!strlen($kod)) {
1637
            $kod = 'NOTSET';
1638
        }
1639
1640
        if (strlen($kod) > 18) {
1641 23
            $kodfinal = strtoupper(substr($kod, 0, 18));
1642
        } else {
1643 23
            $kodfinal = strtoupper($kod);
1644 23
        }
1645 23
1646 23
        if ($unique) {
1647 16
            $counter = 0;
1648 16
            if (!empty($this->codes) && count($this->codes)) {
1649 23
                foreach ($this->codes as $codesearch => $keystring) {
1650
                    if (strstr($codesearch, $kodfinal)) {
1651
                        ++$counter;
1652
                    }
1653
                }
1654
            }
1655
            if ($counter) {
1656
                $kodfinal = $kodfinal.$counter;
1657
            }
1658 23
1659
            $this->codes[$kodfinal] = $kod;
1660 23
        }
1661 23
1662 23
        return self::code($kodfinal);
1663 23
    }
1664 23
1665 23
    /**
1666 23
     * Write Operation Result.
1667 23
     *
1668 23
     * @param array  $resultData
1669
     * @param string $url        URL
1670
     * 
1671
     * @return boolean Log save success
1672
     */
1673
    public function logResult($resultData = null, $url = null)
1674
    {
1675
        $logResult = false;
1676
        if (isset($resultData['success']) && ($resultData['success'] == 'false')) {
1677 23
            if (isset($resultData['message'])) {
1678
                $this->addStatusMessage($resultData['message'], 'warning');
1679 23
            }
1680 23
            $this->addStatusMessage('Error '.$this->lastResponseCode.': '.urldecode($url),
1681 23
                'warning');
1682 23
            unset($url);
1683 23
        }
1684 23
        if (is_null($resultData)) {
1685 13
            $resultData = $this->lastResult;
1686 13
        }
1687 23
        if (isset($url)) {
1688
            $this->logger->addStatusMessage($this->lastResponseCode.':'.urldecode($url));
1689
        }
1690
1691
        if (isset($resultData['results'])) {
1692
            if ($resultData['success'] == 'false') {
1693
                $status = 'error';
1694
            } else {
1695
                $status = 'success';
1696 23
            }
1697
            foreach ($resultData['results'] as $result) {
1698 23
                if (isset($result['request-id'])) {
1699 23
                    $rid = $result['request-id'];
1700 23
                } else {
1701 23
                    $rid = '';
1702 23
                }
1703 16
                if (isset($result['errors'])) {
1704 16
                    foreach ($result['errors'] as $error) {
1705 23
                        $message = $error['message'];
1706
                        if (isset($error['for'])) {
1707
                            $message .= ' for: '.$error['for'];
1708
                        }
1709
                        if (isset($error['value'])) {
1710
                            $message .= ' value:'.$error['value'];
1711
                        }
1712
                        if (isset($error['code'])) {
1713
                            $message .= ' code:'.$error['code'];
1714 23
                        }
1715
                        $this->addStatusMessage($rid.': '.$message, $status);
1716 23
                    }
1717 23
                }
1718 23
            }
1719 23
        }
1720 23
        return $logResult;
1721 16
    }
1722 16
1723 23
    /**
1724
     * Save RAW Curl Request & Response to files in Temp directory
1725
     */
1726
    public function saveDebugFiles()
1727
    {
1728
        $tmpdir   = sys_get_temp_dir();
1729
        $fname    = $this->evidence.'-'.$this->curlInfo['when'].'.'.$this->format;
1730
        $reqname  = $tmpdir.'/request-'.$fname;
1731 23
        $respname = $tmpdir.'/response-'.$fname;
1732
        file_put_contents($reqname, $this->postFields);
1733 23
        file_put_contents($respname, $this->lastCurlResponse);
1734 1
    }
1735 1
1736 23
    /**
1737 23
     * Připraví data pro odeslání do FlexiBee
1738
     *
1739
     * @param string $data
1740
     */
1741
    public function setPostFields($data)
1742
    {
1743
        $this->postFields = $data;
1744 20
    }
1745
1746 20
    /**
1747 20
     * Get Content ready to be send as POST body
1748 20
     * @return string
1749 20
     */
1750
    public function getPostFields()
1751
    {
1752
        return $this->postFields;
1753
    }
1754
1755 20
    /**
1756
     * Generuje fragment url pro filtrování.
1757
     *
1758
     * @see https://www.flexibee.eu/api/dokumentace/ref/filters
1759
     *
1760
     * @param array  $data   key=>values; value can bee class DatePeriod
1761
     * @param string $joiner default and/or
1762
     * @param string $defop  default operator
1763
     *
1764
     * @return string
1765
     */
1766
    public static function flexiUrl(array $data, $joiner = 'and', $defop = 'eq')
1767
    {
1768
        $parts = [];
1769
1770
        foreach ($data as $column => $value) {
1771
            if (!is_numeric($column)) {
1772
                if (is_integer($data[$column]) || is_float($data[$column])) {
1773
                    $parts[$column] = $column.' eq \''.$data[$column].'\'';
1774
                } elseif (is_bool($data[$column])) {
1775
                    $parts[$column] = $data[$column] ? $column.' eq true' : $column.' eq false';
1776
                } elseif (is_null($data[$column])) {
1777
                    $parts[$column] = $column." is null";
1778
                } elseif (is_object($data[$column])) {
1779
                    switch (get_class($data[$column])) {
1780
                        case 'DatePeriod':
1781
                            $parts[$column] = $column." between '".$data[$column]->getStartDate()->format(self::$DateFormat)."' '".$data[$column]->getEndDate()->format(self::$DateFormat)."'";
1782
                            break;
1783
                        case 'DateTime':
0 ignored issues
show
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
1784
                            $parts[$column] = $column." eq '".$data[$column]->format(self::$DateFormat)."'";
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
1785
                        default:
1786
                            $parts[$column] = $column." $defop '".$data[$column]."'";
1787
                            break;
1788
                    }
1789
                } else {
1790
                    switch ($value) {
1791
                        case '!null':
1792
                            $parts[$column] = $column." is not null";
1793
                            break;
1794
                        case 'is empty':
1795
                        case 'is not empty':
1796
                            $parts[$column] = $column.' '.$value;
1797
                            break;
1798
                        default:
1799
                            switch (explode(' ', trim($value))[0]) {
1800
                                case 'like':
1801
                                case 'begins':
1802
                                case 'ends':
1803
                                    $parts[$column] = $column         .= ' '.$value;
1804
                                    break;
1805
                                default:
1806
                                    if ($column == 'stitky') {
1807
                                        $parts[$column] = $column."='".self::code($data[$column])."'";
1808
                                    } else {
1809
                                        $parts[$column] = $column." $defop '".$data[$column]."'";
1810
                                    }
1811
                                    break;
1812
                            }
1813
1814
                            break;
1815
                    }
1816
                }
1817
            } else {
1818
                $parts[] = $value;
1819
            }
1820
        }
1821
        return implode(' '.$joiner.' ', $parts);
1822
    }
1823
1824
    /**
1825
     * Obtain record/object numeric identificator id:
1826
     * Vrací číselný identifikátor objektu id:
1827
     *
1828
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
1829
     *
1830
     * @return null|int indentifikátor záznamu reprezentovaného objektem
1831
     */
1832
    public function getRecordID()
1833
    {
1834
        $id = $this->getDataValue('id');
1835
        return is_null($id) ? null : is_numeric($id) ? intval($id) : $id;
1836
    }
1837
1838
    /**
1839
     * Obtain record/object identificator code:
1840
     * Vrací identifikátor objektu code:
1841
     *
1842
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
1843
     *
1844
     * @return string record code identifier
1845 23
     */
1846
    public function getRecordCode()
1847 23
    {
1848
        return empty($this->getDataValue('kod')) ? null : self::code($this->getDataValue('kod'));
1849
    }
1850
1851
    /**
1852
     * Obtain record/object identificator extId: code: or id:
1853
     * Vrací identifikátor objektu extId: code: nebo id:
1854
     *
1855
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
1856
     *
1857 23
     * @return string|int|null record code identifier
1858
     */
1859 23
    public function getRecordIdent()
1860
    {
1861
        $ident = $this->getExternalID();
1862
        if (empty($ident)) {
1863
            $ident = $this->getRecordCode();
1864
        }
1865
        if (empty($ident)) {
1866
            $ident = $this->getRecordID();
1867
        }
1868
        return $ident;
1869
    }
1870
1871
    /**
1872
     * Obtain record/object identificator code: or id:
1873
     * Vrací identifikátor objektu code: nebo id:
1874
     *
1875
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
1876
     * 
1877
     * @return string indentifikátor záznamu reprezentovaného objektem
1878
     */
1879
    public function __toString()
1880
    {
1881
        return strval($this->getRecordIdent());
1882
    }
1883
1884
    /**
1885
     * Gives you FlexiPeeHP class name for Given Evidence
1886
     *
1887
     * @param string $evidence
1888
     * 
1889
     * @return string Class name
1890
     */
1891
    public static function evidenceToClassName($evidence)
1892
    {
1893
        return str_replace(' ', '', ucwords(str_replace('-', ' ', $evidence)));
1894
    }
1895
1896
    /**
1897
     * Obtain ID of first record in evidence
1898
     *
1899
     * @return string|null id or null if no records
1900
     */
1901
    public function getFirstRecordID()
1902
    {
1903
        $firstID    = null;
1904
        $keyColumn  = $this->getKeyColumn();
1905
        $firstIdRaw = $this->getColumnsFromFlexibee([$keyColumn],
1906
            ['limit' => 1, 'order' => $keyColumn], $keyColumn);
1907
        if (!empty($firstIdRaw) && isset(current($firstIdRaw)[$keyColumn])) {
1908
            $firstID = current($firstIdRaw)[$keyColumn];
1909
        }
1910
        return is_numeric($firstID) ? intval($firstID) : $firstID;
1911
    }
1912
1913
    /**
1914
     * Vrací hodnotu daného externího ID
1915
     *
1916
     * @param string $want Namespace Selector. If empty,you obtain the first one.
1917
     * 
1918
     * @return string|array one id or array if multiplete
1919
     */
1920
    public function getExternalID($want = null)
1921
    {
1922
        $extid = null;
1923
        $ids   = $this->getDataValue('external-ids');
1924
        if (is_null($want)) {
1925
            if (!empty($ids)) {
1926
                $extid = current($ids);
1927
            }
1928
        } else {
1929
            if (!is_null($ids) && is_array($ids)) {
1930
                foreach ($ids as $id) {
1931
                    if (strstr($id, 'ext:'.$want)) {
1932
                        if (is_null($extid)) {
1933
                            $extid = str_replace('ext:'.$want.':', '', $id);
1934
                        } else {
1935
                            if (is_array($extid)) {
1936
                                $extid[] = str_replace('ext:'.$want.':', '', $id);
1937
                            } else {
1938
                                $extid = [$extid, str_replace('ext:'.$want.':',
1939
                                        '', $id)];
1940
                            }
1941
                        }
1942
                    }
1943
                }
1944
            }
1945
        }
1946
        return $extid;
1947
    }
1948
1949
    /**
1950
     * Obtain actual GlobalVersion
1951
     * Vrací aktuální globální verzi změn
1952
     *
1953
     * @link https://www.flexibee.eu/api/dokumentace/ref/changes-api#globalVersion Globální Verze
1954
     * 
1955
     * @return type
1956
     */
1957
    public function getGlobalVersion()
1958
    {
1959
        $this->getFlexiData(null, ['add-global-version' => 'true', 'limit' => 1]);
1960
1961
        return $this->globalVersion;
1962
    }
1963
1964
    /**
1965
     * Gives you current ApiURL with given format suffix
1966
     * 
1967
     * @param string $format json|html|xml|...
1968
     * 
1969
     * @return string API URL for current record or object/evidence
1970
     */
1971
    public function getApiURL($format = null)
1972
    {
1973
        $apiUrl = str_replace(['.'.$this->format, '?limit=0'], '', $this->apiURL);
1974
        return $apiUrl.(empty($format) ? '' : '.'.$format );
1975
    }
1976
1977
    /**
1978
     * Obtain content type of last response
1979
     *
1980
     * @return string
1981
     */
1982
    public function getResponseFormat()
1983
    {
1984
        return $this->responseFormat;
1985
    }
1986
1987
    /**
1988
     * Return the same response format for one and multiplete results
1989
     *
1990
     * @param array $responseBody
1991
     * 
1992
     * @return array
1993
     */
1994
    public function unifyResponseFormat($responseBody)
1995
    {
1996
        if (!is_array($responseBody) || array_key_exists('message',
1997
                $responseBody)) { //Unifi response format
1998
            $response = $responseBody;
1999
        } else {
2000
            $evidence = $this->getResponseEvidence();
2001
            if (array_key_exists($evidence, $responseBody)) {
2002
                $response        = [];
2003
                $evidenceContent = $responseBody[$evidence];
2004
                if (array_key_exists(0, $evidenceContent)) {
2005
                    $response[$evidence] = $evidenceContent; //Multiplete Results
2006
                } else {
2007
                    $response[$evidence][0] = $evidenceContent; //One result
2008
                }
2009
            } else {
2010
                if (isset($responseBody['priloha'])) {
2011
                    $response = $responseBody['priloha'];
2012
                } else {
2013
                    if (array_key_exists('results', $responseBody)) {
2014
                        $response = $responseBody['results'];
2015
                    } else {
2016
                        $response = $responseBody;
2017
                    }
2018
                }
2019
            }
2020
        }
2021
        return $response;
2022
    }
2023
2024
    /**
2025
     * Obtain structure for current (or given) evidence
2026
     *
2027
     * @param string $evidence
2028
     * 
2029
     * @return array Evidence structure
2030
     */
2031
    public function getOfflineColumnsInfo($evidence = null)
2032
    {
2033
        $columnsInfo = null;
2034
        $infoSource  = self::$infoDir.'/Properties.'.(empty($evidence) ? $this->getEvidence()
2035 23
                : $evidence).'.json';
2036
        if (file_exists($infoSource)) {
2037 23
            $columnsInfo = json_decode(file_get_contents($infoSource), true);
2038 23
        }
2039 23
        return $columnsInfo;
2040 23
    }
2041
2042
    /**
2043
     * Obtain Current evidence Live structure
2044
     * 
2045
     * @param string $evidence
2046
     * 
2047
     * @return array structure
2048
     */
2049
    public function getOnlineColumnsInfo($evidence = null)
2050
    {
2051
        $properties = [];
2052
        $evidence   = is_null($evidence) ? $this->getEvidence() : $evidence;
2053
        $flexinfo   = $this->performRequest('/c/'.$this->company.'/'.$evidence.'/properties.json');
2054
        if (count($flexinfo) && array_key_exists('properties', $flexinfo)) {
2055
            foreach ($flexinfo['properties']['property'] as $evidenceProperty) {
2056
                $key                      = $evidenceProperty['propertyName'];
2057
                $properties[$key]         = $evidenceProperty;
2058
                $properties[$key]['name'] = $evidenceProperty['name'];
2059
                $properties[$key]['type'] = $evidenceProperty['type'];
2060
                if (array_key_exists('url', $evidenceProperty)) {
2061
                    $properties[$key]['url'] = str_replace('?limit=0', '',
2062
                        $evidenceProperty['url']);
2063
                }
2064
            }
2065
        }
2066
        return $properties;
2067
    }
2068
2069
    /**
2070
     * Update evidence info from array or online from properties.json or offline
2071
     * 
2072
     * @param array  $columnsInfo
2073
     * @param string $evidence
2074
     */
2075
    public function updateColumnsInfo($columnsInfo = null, $evidence = null)
2076
    {
2077
        $evidence = is_null($evidence) ? $this->getEvidence() : $evidence;
2078
        if (is_null($columnsInfo)) {
2079
            $this->columnsInfo[$evidence] = $this->offline ? $this->getOfflineColumnsInfo($evidence)
2080
                    : $this->getOnlineColumnsInfo($evidence);
2081
        } else {
2082
            $this->columnsInfo[$evidence] = $columnsInfo;
2083
        }
2084
    }
2085
2086
    /**
2087
     * Gives you evidence structure. You can obtain current online by pre-calling:
2088
     * $this->updateColumnsInfo($evidence, $this->getOnlineColumnsInfo($evidence));
2089
     * 
2090
     * @param string $evidence
2091
     * 
2092
     * @return array
2093
     */
2094
    public function getColumnsInfo($evidence = null)
2095
    {
2096
        $evidence = is_null($evidence) ? $this->getEvidence() : $evidence;
2097
        if (!array_key_exists($evidence, $this->columnsInfo)) {
2098
            $this->updateColumnsInfo($this->getOfflineColumnsInfo($evidence),
2099
                $evidence);
2100
        }
2101
        return $this->columnsInfo[$evidence];
2102
    }
2103
2104
    /**
2105
     * Gives you properties for (current) evidence column
2106
     *
2107
     * @param string $column    name of column
2108
     * @param string $evidence  evidence name if different
2109
     *
2110
     * @return array column properties or null if column not exits
2111
     */
2112
    public function getColumnInfo($column, $evidence = null)
2113
    {
2114
        $columnsInfo = $this->getColumnsInfo(empty($evidence) ? $this->getEvidence()
2115
                : $evidence);
2116
        return is_array($columnsInfo) ? array_key_exists($column, $columnsInfo) ? $columnsInfo[$column]
2117
                : null : null;
2118
    }
2119
2120
    /**
2121
     * Obtain actions for current (or given) evidence
2122
     *
2123
     * @param string $evidence
2124
     * 
2125
     * @return array Evidence structure
2126
     */
2127
    public function getActionsInfo($evidence = null)
2128
    {
2129
        $actionsInfo = null;
2130
        if (is_null($evidence)) {
2131
            $evidence = $this->getEvidence();
2132
        }
2133
        $propsName = lcfirst(FlexiBeeRO::evidenceToClassName($evidence));
2134
        if (isset(\FlexiPeeHP\Actions::$$propsName)) {
2135
            $actionsInfo = Actions::$$propsName;
2136
        }
2137
        return $actionsInfo;
2138
    }
2139
2140
    /**
2141
     * Obtain relations for current (or given) evidence
2142
     *
2143
     * @param string $evidence
2144
     * 
2145
     * @return array Evidence structure
2146
     */
2147
    public function getRelationsInfo($evidence = null)
2148
    {
2149
        $relationsInfo = null;
2150
        if (is_null($evidence)) {
2151
            $evidence = $this->getEvidence();
2152
        }
2153
        $propsName = lcfirst(FlexiBeeRO::evidenceToClassName($evidence));
2154
        if (isset(\FlexiPeeHP\Relations::$$propsName)) {
2155
            $relationsInfo = Relations::$$propsName;
2156
        }
2157
        return $relationsInfo;
2158
    }
2159
2160
    /**
2161
     * Obtain info for current (or given) evidence
2162
     *
2163
     * @param string $evidence
2164
     * 
2165
     * @return array Evidence info
2166
     */
2167
    public function getEvidenceInfo($evidence = null)
2168
    {
2169
        $evidencesInfo = null;
2170
        if (is_null($evidence)) {
2171
            $evidence = $this->getEvidence();
2172
        }
2173
        if (isset(EvidenceList::$evidences[$evidence])) {
2174
            $evidencesInfo = EvidenceList::$evidences[$evidence];
2175
            $propsName     = lcfirst(FlexiBeeRO::evidenceToClassName($evidence));
2176
            if (isset(Formats::$$propsName)) {
2177
                $evidencesInfo['formats'] = Formats::$$propsName;
2178
            }
2179
        }
2180
        return $evidencesInfo;
2181
    }
2182
2183
    /**
2184
     * Obtain name for current (or given) evidence path
2185
     *
2186
     * @param string $evidence Evidence Path
2187
     * 
2188
     * @return array Evidence info
2189
     */
2190
    public function getEvidenceName($evidence = null)
2191
    {
2192
        $evidenceName = null;
2193
        if (is_null($evidence)) {
2194
            $evidence = $this->getEvidence();
2195
        }
2196
        if (isset(EvidenceList::$name[$evidence])) {
2197
            $evidenceName = EvidenceList::$name[$evidence];
2198
        }
2199
        return $evidenceName;
2200
    }
2201
2202
    /**
2203
     * Save current object to file
2204
     *
2205
     * @param string $destfile path to file
2206
     */
2207
    public function saveResponseToFile($destfile)
2208
    {
2209
        if (strlen($this->lastCurlResponse)) {
2210
            $this->doCurlRequest($this->apiURL, 'GET', $this->format);
2211
        }
2212
        file_put_contents($destfile, $this->lastCurlResponse);
2213
    }
2214
2215
    /**
2216
     * Obtain established relations listing
2217
     *
2218
     * @return array Null or Relations
2219
     */
2220
    public function getVazby($id = null)
2221
    {
2222
        if (is_null($id)) {
2223
            $id = $this->getRecordID();
2224
        }
2225
        if (!empty($id)) {
2226
            $vazbyRaw = $this->getColumnsFromFlexibee(['vazby'],
2227
                ['relations' => 'vazby', 'id' => $id]);
2228
            $vazby    = array_key_exists('vazby', $vazbyRaw[0]) ? $vazbyRaw[0]['vazby']
2229
                    : null;
2230
        } else {
2231
            throw new \Exception(_('ID requied to get record relations '));
2232
        }
2233
        return $vazby;
2234
    }
2235
2236
    /**
2237
     * Gives You URL for Current Record in FlexiBee web interface
2238
     *
2239
     * @return string url
2240
     */
2241
    public function getFlexiBeeURL()
2242
    {
2243
        $parsed_url = parse_url(str_replace('.'.$this->format, '', $this->apiURL));
2244
        $scheme     = isset($parsed_url['scheme']) ? $parsed_url['scheme'].'://'
2245
                : '';
2246
        $host       = isset($parsed_url['host']) ? $parsed_url['host'] : '';
2247
        $port       = isset($parsed_url['port']) ? ':'.$parsed_url['port'] : '';
2248
        $user       = isset($parsed_url['user']) ? $parsed_url['user'] : '';
2249
        $pass       = isset($parsed_url['pass']) ? ':'.$parsed_url['pass'] : '';
2250
        $pass       = ($user || $pass) ? "$pass@" : '';
2251
        $path       = isset($parsed_url['path']) ? $parsed_url['path'] : '';
2252
        return $scheme.$user.$pass.$host.$port.$path;
2253
    }
2254
2255
    /**
2256
     * Set Record Key
2257
     *
2258
     * @param int|string $myKeyValue
2259
     * 
2260
     * @return boolean
2261
     */
2262
    public function setMyKey($myKeyValue)
2263
    {
2264
        if (substr($myKeyValue, 0, 4) == 'ext:') {
2265
            $extIds = $this->getDataValue('external-ids');
2266
            if (!empty($extIds) && count($extIds)) {
2267
                $extIds = array_combine($extIds, $extIds);
2268
            }
2269
            $extIds[$myKeyValue] = $myKeyValue;
2270
            $res                 = $this->setDataValue('external-ids', $extIds);
2271
        } else {
2272
            $res = parent::setMyKey($myKeyValue);
2273
        }
2274
        $this->updateApiURL();
2275
        return $res;
2276
    }
2277
2278
    /**
2279
     * Set or get ignore not found pages flag
2280
     *
2281
     * @param boolean $ignore set flag to
2282
     *
2283
     * @return boolean get flag state
2284
     */
2285
    public function ignore404($ignore = null)
2286
    {
2287
        if (!is_null($ignore)) {
2288
            $this->ignoreNotFound = $ignore;
2289
        }
2290
        return $this->ignoreNotFound;
2291
    }
2292
2293
    /**
2294
     * Send Document by mail
2295
     *
2296
     * @url https://www.flexibee.eu/api/dokumentace/ref/odesilani-mailem/
2297
     *
2298
     * @param string $to         Email ecipient
2299
     * @param string $subject    Email Subject
2300
     * @param string $body       Email Text
2301
     *
2302
     * @return int http response code
2303
     */
2304
    public function sendByMail($to, $subject, $body, $cc = null)
2305
    {
2306
        $this->setPostFields($body);
2307
2308
        $this->performRequest(rawurlencode($this->getRecordID()).'/odeslani-dokladu?to='.$to.'&subject='.urlencode($subject).'&cc='.$cc
2309
            , 'PUT', 'xml');
2310
2311
        return $this->lastResponseCode == 200;
2312
    }
2313
2314
    /**
2315
     * Send all unsent Documents by eMail
2316
     *
2317
     * @url https://www.flexibee.eu/api/dokumentace/ref/odesilani-mailem/
2318
     * 
2319
     * @return int http response code
2320
     */
2321
    public function sendUnsent()
2322
    {
2323
        return $this->doCurlRequest('automaticky-odeslat-neodeslane', 'PUT',
2324
                'xml');
2325
    }
2326
2327
    /**
2328
     * FlexiBee date to PHP DateTime conversion
2329
     *
2330
     * @param string $flexidate 2017-05-26+02:00
2331
     *
2332
     * @return \DateTime | false
2333
     */
2334
    public static function flexiDateToDateTime($flexidate)
2335
    {
2336
        return \DateTime::createFromFormat(strstr($flexidate, '+') ? self::$DateFormat.'O'
2337
                    : self::$DateFormat, $flexidate)->setTime(0, 0);
2338
    }
2339
2340
    /**
2341
     * FlexiBee dateTime to PHP DateTime conversion
2342
     *
2343
     * @param string $flexidatetime 2017-09-26T10:00:53.755+02:00 or older 2017-05-19T00:00:00+02:00
2344
     *
2345
     * @return \DateTime | false
2346
     */
2347
    public static function flexiDateTimeToDateTime($flexidatetime)
2348
    {
2349
        if (strchr($flexidatetime, '.')) { //NewFormat
2350
            $format = self::$DateTimeFormat;
2351
        } else { // Old format
2352
            $format = 'Y-m-d\TH:i:s+P';
2353
        }
2354
        return \DateTime::createFromFormat($format, $flexidatetime);
2355
    }
2356
2357
    /**
2358
     * Získá dokument v daném formátu
2359
     * Obtain document in given format
2360
     *
2361
     * @link https://www.flexibee.eu/api/dokumentace/ref/pdf/ PDF Exports
2362
     *
2363
     * @param string  $format     pdf/csv/xml/json/ ...
2364
     * @param string  $reportName Template used to generate PDF
2365
     * @param string  $lang       cs|sk|en|de Template language used to generate PDF
2366
     * @param boolean $sign       sign resulting PDF by certificate ?
2367
     *
2368
     * @return string|null filename downloaded or none
2369
     */
2370
    public function getInFormat($format, $reportName = null, $lang = null,
2371
                                $sign = false)
2372
    {
2373
        $response = null;
2374
        if ($this->setFormat($format)) {
2375
            $urlParams = [];
2376
            switch ($format) {
2377
                case 'pdf':
2378
                    switch ($lang) {
2379
                        case 'cs':
2380
                        case 'sk':
2381
                        case 'en':
2382
                        case 'de':
2383
                            $urlParams['report-lang'] = $lang;
2384
                            break;
2385
                        case null:
2386
                            break;
2387
                        default:
2388
                            throw new \Ease\Exception('Unknown language '.$lang.' for PDF export');
2389
                            break;
2390
                    }
2391
                    if (boolval($sign) === true) {
2392
                        $urlParams['report-sign'] = 'true';
2393
                    }
2394
                    break;
2395
                case 'html':
2396
                    $urlParams['inDesktopApp'] = 'true';
2397
                    break;
2398
            }
2399
            if (!empty($reportName)) {
2400
                $urlParams['report-name'] = $reportName;
2401
            }
2402
            if (($this->doCurlRequest(\Ease\Shared::addUrlParams($this->apiURL,
2403
                        $urlParams), 'GET') == 200)) {
2404
                $response = $this->lastCurlResponse;
2405
            }
2406
        }
2407
        return $response;
2408
    }
2409
2410
    /**
2411
     * Uloží dokument v daném formátu do složky v systému souborů
2412
     * Save document in given format to directory in filesystem
2413
     *
2414
     * @param string $format  pdf/csv/xml/json/ ...
2415
     * @param string $destDir where to put file (prefix)
2416
     * @param string $reportName Template used to generate PDF
2417
     *
2418
     * @return string|null filename downloaded or none
2419
     */
2420
    public function downloadInFormat($format, $destDir = './',
2421
                                     $reportName = null)
2422
    {
2423
        $fileOnDisk   = null;
2424
        $formatBackup = $this->format;
2425
        if ($this->setFormat($format)) {
2426
            $downloadTo = $destDir.$this->getEvidence().'_'.$this->getMyKey().'.'.$format;
2427
            if (($this->doCurlRequest(empty($reportName) ? $this->apiURL : \Ease\Shared::addUrlParams($this->apiURL,
2428
                            ['report-name' => $reportName]), 'GET') == 200) && (file_put_contents($downloadTo,
2429
                    $this->lastCurlResponse) !== false)) {
2430
                $fileOnDisk = $downloadTo;
2431
            }
2432
            $this->setFormat($formatBackup);
2433
        }
2434
        return $fileOnDisk;
2435
    }
2436
2437
    /**
2438
     * Take data for object
2439
     *
2440
     * @param array $data Data to keep
2441
     * 
2442
     * @return int number of records taken
2443
     */
2444
    public function takeData($data)
2445
    {
2446
        $result = parent::takeData($data);
2447
        if (array_key_exists($this->getKeyColumn(), $data) || array_key_exists('kod',
2448
                $data)) {
2449
            $this->updateApiURL();
2450
        }
2451
        return $result;
2452
    }
2453
2454
    /**
2455
     * Get Current Evidence reports listing
2456
     * 
2457
     * @link https://www.flexibee.eu/api/dokumentace/casto-kladene-dotazy-pro-api/vyber-reportu-do-pdf/ Výběr reportu do PDF
2458
     * 
2459
     * @return array
2460
     */
2461
    public function getReportsInfo()
2462
    {
2463
        $reports    = [];
2464
        $reportsRaw = $this->getFlexiData($this->getEvidenceURL().'/reports');
2465
        if (!empty($reportsRaw) && array_key_exists('reports', $reportsRaw) && !empty($reportsRaw['reports'])
2466
            && array_key_exists('report', $reportsRaw['reports']) &&
2467
            !empty($reportsRaw['reports']['report'])) {
2468
            if (\Ease\jQuery\Part::isAssoc($reportsRaw['reports']['report'])) {
2469
                $reports = [$reportsRaw['reports']['report']['reportId'] => $reportsRaw['reports']['report']];
2470
            } else {
2471
                $reports = self::reindexArrayBy($reportsRaw['reports']['report'],
2472
                        'reportId');
2473
            }
2474
        }
2475
        return $reports;
2476
    }
2477
2478
    /**
2479
     * Request authSessionId from current server
2480
     * 
2481
     * @link https://www.flexibee.eu/api/dokumentace/ref/login/ description
2482
     * 
2483
     * @param string $username
2484
     * @param string $password
2485
     * @param string $otp       optional onetime password
2486
     * 
2487
     * @return string authUserId or null in case of problems
2488
     */
2489
    public function requestAuthSessionID($username, $password, $otp = null)
2490
    {
2491
        $this->postFields = http_build_query(is_null($otp) ? ['username' => $username,
2492
            'password' => $password] : ['username' => $username, 'password' => $password,
2493
            'otp' => $otp]);
2494
        $response         = $this->performRequest('/login-logout/login', 'POST',
2495
            'json');
2496
        if (array_key_exists('refreshToken', $response)) {
2497
            $this->refreshToken = $response['refreshToken'];
2498
        } else {
2499
            $this->refreshToken = null;
2500
        }
2501
        return array_key_exists('authSessionId', $response) ? $response['authSessionId']
2502
                : null;
2503
    }
2504
2505
    /**
2506
     * Try to Sign in current user to FlexiBee and keep authSessionId
2507
     * 
2508
     * @return boolen sign in success
2509
     */
2510
    public function login()
2511
    {
2512
        $this->authSessionId = $this->requestAuthSessionID($this->user,
2513
            $this->password);
2514
        return $this->lastResponseCode == 200;
2515
    }
2516
2517
    /**
2518
     * End (current's user) session
2519
     * 
2520
     * 
2521
     * @link https://www.flexibee.eu/api/dokumentace/ref/logout Logout Reference
2522
     * 
2523
     * @param string $username force username to sign off
2524
     * 
2525
     * @return array server response
2526
     */
2527
    public function logout($username = null)
2528
    {
2529
        return $this->performRequest('/status/user/'.(is_null($username) ? $this->user
2530
                    : $username).'/logout', 'POST');
2531
    }
2532
2533
    /**
2534
     * Compile and send Report about Error500 to FlexiBee developers
2535
     * If FlexiBee is running on localost try also include java backtrace
2536
     *
2537
     * @param array $errorResponse result of parseError();
2538
     */
2539
    public function error500Reporter($errorResponse)
2540
    {
2541
        $ur = str_replace('/c/'.$this->company, '',
2542
            str_replace($this->url, '', $this->curlInfo['url']));
2543
        if (!array_key_exists($ur, $this->reports)) {
2544
            $tmpdir   = sys_get_temp_dir();
2545
            $myTime   = $this->curlInfo['when'];
2546
            $curlname = $tmpdir.'/curl-'.$this->evidence.'-'.$myTime.'.json';
2547
            file_put_contents($curlname,
2548
                json_encode($this->curlInfo, JSON_PRETTY_PRINT));
2549
2550
            $report = new \Ease\Mailer($this->reportRecipient,
2551
                'Error report 500 - '.$ur);
2552
2553
            $d     = dir($tmpdir);
2554
            while (false !== ($entry = $d->read())) {
2555
                if (strstr($entry, $myTime)) {
2556
                    $ext  = pathinfo($tmpdir.'/'.$entry, PATHINFO_EXTENSION);
2557
                    $mime = Formats::suffixToContentType($ext);
2558
                    $report->addFile($tmpdir.'/'.$entry,
2559
                        empty($mime) ? 'text/plain' : $mime);
2560
                }
2561
            }
2562
            $d->close();
2563
2564
            if ((strstr($this->url, '://localhost') || strstr($this->url,
2565
                    '://127.')) && file_exists('/var/log/flexibee.log')) {
2566
2567
                $fl = fopen('/var/log/'.'flexibee.log', 'r');
2568
                if ($fl) {
2569
                    $tracelog = [];
2570
                    for ($x_pos = 0, $ln = 0, $output = array(); fseek($fl,
2571
                            $x_pos, SEEK_END) !== -1; $x_pos--) {
2572
                        $char = fgetc($fl);
2573
                        if ($char === "\n") {
2574
                            $tracelog[] = $output[$ln];
2575
                            if (strstr($output[$ln], $errorResponse['message'])) {
2576
                                break;
2577
                            }
2578
                            $ln++;
2579
                            continue;
2580
                        }
2581
                        $output[$ln] = $char.((array_key_exists($ln, $output)) ? $output[$ln]
2582
                                : '');
2583
                    }
2584
2585
                    $trace     = implode("\n", array_reverse($tracelog));
2586
                    $tracefile = $tmpdir.'/trace-'.$this->evidence.'-'.$myTime.'.log';
2587
                    file_put_contents($tracefile, $trace);
2588
                    $report->addItem("\n\n".$trace);
2589
                    fclose($fl);
2590
                }
2591
            } else {
2592
                $report->addItem($errorResponse['message']);
2593
            }
2594
2595
            $licenseInfo = $this->performRequest($this->url.'/default-license.json');
2596
2597
            $report->addItem("\n\n".json_encode($licenseInfo['license'],
2598
                    JSON_PRETTY_PRINT));
2599
2600
            if ($report->send()) {
2601
                $this->reports[$ur] = $myTime;
2602
            }
2603
        }
2604
    }
2605
2606
    /**
2607
     * Returns code:CODE
2608
     *
2609
     * @param string $code
2610
     *
2611
     * @return string
2612
     */
2613
    public static function code($code)
2614
    {
2615
        return ((substr($code, 0, 4) == 'ext:') ? $code : 'code:'.strtoupper(self::uncode($code)));
2616
    }
2617
2618
    /**
2619
     * Returns CODE without code: prefix
2620
     *
2621
     * @param string $code
2622
     *
2623
     * @return string
2624
     */
2625
    public static function uncode($code)
2626
    {
2627
        return str_replace(['code:', 'code%3A'], '', $code);
2628
    }
2629
2630
    /**
2631
     * Remove all @ items from array
2632
     *
2633
     * @param array $data original data
2634
     *
2635
     * @return array data without @ columns
2636
     */
2637
    public static function arrayCleanUP($data)
2638
    {
2639
        return array_filter(
2640
            $data,
2641
            function ($key) {
2642
            return !strchr($key, '@');
2643
        }, ARRAY_FILTER_USE_KEY);
2644
    }
2645
2646
    /**
2647
     * Add Info about used user, server and libraries
2648
     *
2649
     * @param string $additions Additional note text
2650
     */
2651
    public function logBanner($additions = null)
2652
    {
2653
        $this->addStatusMessage('FlexiBee '.str_replace('://',
2654
                '://'.$this->user.'@', $this->getApiUrl()).' FlexiPeeHP v'.self::$libVersion.' (FlexiBee '.EvidenceList::$version.') EasePHP Framework v'.\Ease\Atom::$frameworkVersion.' '.$additions,
2655
            'debug');
2656
    }
2657
2658
    /**
2659
     * Reconnect After unserialization
2660
     */
2661
    public function __wakeup()
2662
    {
2663
        parent::__wakeup();
2664
        $this->curlInit();
2665
    }
2666
}
2667