modResource::loadTVTemplate()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 8
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 10
c 2
b 0
f 0
cc 3
nc 2
nop 0
crap 3
1
<?php
0 ignored issues
show
Coding Style introduced by
End of line character is invalid; expected "\n" but found "\r\n"
Loading history...
2 1
require_once('MODx.php');
3
4
/**
5
 * Class modResource
6
 */
7
class modResource extends MODxAPI
0 ignored issues
show
Coding Style introduced by
Class name "modResource" is not in PascalCase format

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
8
{
9
    /**
10
     * @var string
11
     */
12
    protected $mode = 'new';
13
    /**
14
     * @var array
15
     */
16
    protected $default_field = array(
17
        'type'            => 'document',
18
        'contentType'     => 'text/html',
19
        'pagetitle'       => 'New document',
20
        'longtitle'       => '',
21
        'description'     => '',
22
        'alias'           => '',
23
        'link_attributes' => '',
24
        'published'       => 1,
25
        'pub_date'        => 0,
26
        'unpub_date'      => 0,
27
        'parent'          => 0,
28
        'isfolder'        => 0,
29
        'introtext'       => '',
30
        'content'         => '',
31
        'richtext'        => 1,
32
        'template'        => 0,
33
        'menuindex'       => 0,
34
        'searchable'      => 1,
35
        'cacheable'       => 1,
36
        'createdon'       => 0,
37
        'createdby'       => 0,
38
        'editedon'        => 0,
39
        'editedby'        => 0,
40
        'deleted'         => 0,
41
        'deletedon'       => 0,
42
        'deletedby'       => 0,
43
        'publishedon'     => 0,
44
        'publishedby'     => 0,
45
        'menutitle'       => '',
46
        'donthit'         => 0,
47
        'privateweb'      => 0,
48
        'privatemgr'      => 0,
49
        'content_dispo'   => 0,
50
        'hidemenu'        => 0,
51
        'alias_visible'   => 1
52
    );
53
    /**
54
     * @var array
55
     */
56
    private $table = array(
57
        '"' => '-',
58
        "'" => '-',
59
        ' ' => '-',
60
        '.' => '-',
61
        ',' => '-',
62
        'а' => 'a',
63
        'б' => 'b',
64
        'в' => 'v',
65
        'г' => 'g',
66
        'д' => 'd',
67
        'е' => 'e',
68
        'ё' => 'e',
69
        'ж' => 'zh',
70
        'з' => 'z',
71
        'и' => 'i',
72
        'й' => 'y',
73
        'к' => 'k',
74
        'л' => 'l',
75
        'м' => 'm',
76
        'н' => 'n',
77
        'о' => 'o',
78
        'п' => 'p',
79
        'р' => 'r',
80
        'с' => 's',
81
        'т' => 't',
82
        'у' => 'u',
83
        'ф' => 'f',
84
        'х' => 'h',
85
        'ц' => 'c',
86
        'ч' => 'ch',
87
        'ш' => 'sh',
88
        'щ' => 'sch',
89
        'ь' => '',
90
        'ы' => 'y',
91
        'ъ' => '',
92
        'э' => 'e',
93
        'ю' => 'yu',
94
        'я' => 'ya',
95
        'А' => 'A',
96
        'Б' => 'B',
97
        'В' => 'V',
98
        'Г' => 'G',
99
        'Д' => 'D',
100
        'Е' => 'E',
101
        'Ё' => 'E',
102
        'Ж' => 'Zh',
103
        'З' => 'Z',
104
        'И' => 'I',
105
        'Й' => 'Y',
106
        'К' => 'K',
107
        'Л' => 'L',
108
        'М' => 'M',
109
        'Н' => 'N',
110
        'О' => 'O',
111
        'П' => 'P',
112
        'Р' => 'R',
113
        'С' => 'S',
114
        'Т' => 'T',
115
        'У' => 'U',
116
        'Ф' => 'F',
117
        'Х' => 'H',
118
        'Ц' => 'C',
119
        'Ч' => 'Ch',
120
        'Ш' => 'Sh',
121
        'Щ' => 'Sch',
122
        'Ь' => '',
123
        'Ы' => 'Y',
124
        'Ъ' => '',
125
        'Э' => 'E',
126
        'Ю' => 'Yu',
127
        'Я' => 'Ya',
128
    );
129
    /**
130
     * @var array массив ТВшек где name это ключ массива, а ID это значение
131
     */
132
    private $tv = array();
133
    /**
134
     * @var array массив ТВшек где ID это ключ массива, а name это значение
135
     */
136
    private $tvid = array();
137
    /**
138
     * @var array значения по умолчанию для ТВ параметров
139
     */
140
    private $tvd = array();
141
142
    /** @var array связи ТВ и шаблонов */
143
    private $tvTpl = array();
144
145
    /** @var array параметры ТВ с массивами */
146
    protected $tvaFields = array();
147
148
    /**
149
     * Массив администраторов
150
     * @var DLCollection
151
     */
152
    private $managerUsers = null;
153
    /** @var array группы документов */
154
    protected $groupIds = array();
155
156
    /**
157
     * modResource constructor.
158
     * @param DocumentParser $modx
159
     * @param bool $debug
160
     */
161 2
    public function __construct($modx, $debug = false)
162
    {
163 2
        parent::__construct($modx, $debug);
164 2
        $this->get_TV();
165 2
        $uTable = $this->makeTable("manager_users");
166 2
        $aTable = $this->makeTable("user_attributes");
167 2
        $query = "SELECT `u`.`id`, `a`.`email`, `u`.`username`  FROM " . $aTable . " as `a` LEFT JOIN " . $uTable . " as `u` ON `u`.`id`=`a`.`internalKey`";
168 2
        $query = $this->query($query);
169 2
        $this->managerUsers = new DLCollection($modx, empty($query) ? array() : $query);
170 2
    }
171
172
    /**
173
     * @return array
174
     */
175 1
    public function toArrayMain()
176
    {
177 1
        $out = array_intersect_key(parent::toArray(), $this->default_field);
178
179 1
        return $out;
180
    }
181
182
    /**
183
     * @param bool $render
184
     * @return array
185
     */
186 1
    public function toArrayTV($render = false)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
187
    {
188 1
        $out = array_diff_key(parent::toArray(), $this->default_field);
189 1
        $tpl = $this->get('template');
190 1
        $tvTPL = APIHelpers::getkey($this->tvTpl, $tpl, array());
191 1
        foreach ($tvTPL as $item) {
192
            if (isset($this->tvid[$item]) && !array_key_exists($this->tvid[$item], $out)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after NOT operator; 0 found
Loading history...
193
                $value = $this->get($this->tvid[$item]);
194
                $out[$this->tvid[$item]] = empty($value) ? $this->tvd[$this->tvid[$item]] : $value;
195
            }
196
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
197 1
        }
198 1
        if ($render) {
199 1
            foreach ($out as $key => $val) {
200 1
                $out[$key] = $this->renderTV($key);
201 1
            }
202 1
        }
203
204 1
        return $out;
205
    }
206
207
    /**
208
     * @param string $prefix
209
     * @param string $suffix
210
     * @param string $sep
211
     * @param bool $render
212
     * @return array
213
     */
214 1
    public function toArray($prefix = '', $suffix = '', $sep = '_', $render = true)
215
    {
216 1
        $out = array_merge(
217 1
            $this->toArrayMain(),
218 1
            $this->toArrayTV($render),
219 1
            array($this->fieldPKName() => $this->getID())
220 1
        );
221
222 1
        return \APIhelpers::renameKeyArr($out, $prefix, $suffix, $sep);
223
    }
224
225
    /**
226
     * @return null|string
227
     */
228
    public function getUrl()
229
    {
230
        $out = null;
231
        $id = (int)$this->getID();
232
        if (! empty($id)) {
233
            $out = $this->modx->makeUrl($id);
0 ignored issues
show
Bug introduced by
The method makeUrl() does not exist on DocumentParser. ( Ignorable by Annotation )

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

233
            /** @scrutinizer ignore-call */ $out = $this->modx->makeUrl($id);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
234
        }
235
236
        return $out;
237
    }
238
239
    /**
240
     * @param string $main
241
     * @param string $second
242
     * @return mixed
243
     */
244
    public function getTitle($main = 'menutitle', $second = 'pagetitle')
245
    {
246
        $title = $this->get($main);
247
        if (empty($title) && $title !== '0') {
248
            $title = $this->get($second);
249
        }
250
251
        return $title;
252
    }
253
254
    /**
255
     * @return bool
256
     */
257
    public function isWebShow()
258
    {
259
        $pub = ($this->get('publishedon') < time() && $this->get('published'));
260
        $unpub = ($this->get('unpub_date') == 0 || $this->get('unpub_date') > time());
261
        $del = ($this->get('deleted') == 0 && ($this->get('deletedon') == 0 || $this->get('deletedon') > time()));
262
263
        return ($pub && $unpub && $del);
264
    }
265
266
    /**
267
     * @return $this
268
     */
269 1
    public function touch()
270
    {
271 1
        $this->set('editedon', time());
272
273 1
        return $this;
274
    }
275
276
    /**
277
     * @param $tvname
278
     * @return null|string
279
     */
280 1
    public function renderTV($tvname)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
281
    {
282 1
        $out = null;
283 1
        if ($this->getID() > 0) {
284 1
            include_once MODX_MANAGER_PATH . "includes/tmplvars.format.inc.php";
285 1
            include_once MODX_MANAGER_PATH . "includes/tmplvars.commands.inc.php";
286 1
            $tvval = $this->get($tvname);
287 1
            if ($this->isTVarrayField($tvname) && is_array($tvval)) {
288
                $tvval = implode('||', $tvval);
289
            }
290 1
            $param = APIHelpers::getkey($this->tvd, $tvname, array());
291 1
            $display = APIHelpers::getkey($param, 'display', '');
292 1
            $display_params = APIHelpers::getkey($param, 'display_params', '');
293 1
            $type = APIHelpers::getkey($param, 'type', '');
294 1
            $out = getTVDisplayFormat($tvname, $tvval, $display, $display_params, $type, $this->getID(), '');
0 ignored issues
show
Bug introduced by
The function getTVDisplayFormat was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

294
            $out = /** @scrutinizer ignore-call */ getTVDisplayFormat($tvname, $tvval, $display, $display_params, $type, $this->getID(), '');
Loading history...
295 1
        }
296
297 1
        return $out;
298
    }
299
300
    /**
301
     * @param $key
302
     * @return mixed
303
     */
304 1
    public function get($key)
305
    {
306 1
        $out = parent::get($key);
307 1
        if (isset($this->tv[$key])) {
308 1
            $tpl = $this->get('template');
309 1
            $tvTPL = APIHelpers::getkey($this->tvTpl, $tpl, array());
310 1
            $tvID = APIHelpers::getkey($this->tv, $key, 0);
311 1
            if (in_array($tvID, $tvTPL) && is_null($out)) {
312
                $out = APIHelpers::getkey($this->tvd, $key, null);
313
                $out = $out['default'];
314
            }
315 1
        }
316
317 1
        return $out;
318
    }
319
320
    /**
321
     * @param $key
322
     * @param $value
323
     * @return $this
324
     */
325 1
    public function set($key, $value)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (4) exceeds 3; consider refactoring the function
Loading history...
326
    {
327 1
        if ((is_scalar($value) || $this->isTVarrayField($key) || $this->isJsonField($key)) && is_scalar($key) && ! empty($key)) {
328
            switch ($key) {
329 1
                case 'donthit':
330
                    $value = (int)((bool)$value);
331
                    break;
332 1
                case 'parent':
333
                    $value = (int)$value;
334
                    break;
335 1
                case 'template':
336 1
                    $value = trim($value);
337 1
                    $value = $this->setTemplate($value);
338 1
                    break;
339 1
                case 'published':
340
                    $value = (int)((bool)$value);
341
                    if ($value) {
342
                        $this->field['publishedon'] = time() + $this->modxConfig('server_offset_time');
343
                    }
344
                    break;
345 1
                case 'pub_date':
346
                    $value = $this->getTime($value);
347
                    if ($value > 0 && time() + $this->modxConfig('server_offset_time') > $value) {
348
                        $this->field['published'] = 1;
349
                        $this->field['publishedon'] = $value;
350
                    }
351
                    break;
352 1
                case 'unpub_date':
353
                    $value = $this->getTime($value);
354
                    if ($value > 0 && time() + $this->modxConfig('server_offset_time') > $value) {
355
                        $this->field['published'] = 0;
356
                        $this->field['publishedon'] = 0;
357
                    }
358
                    break;
359 1
                case 'deleted':
360
                    $value = (int)((bool)$value);
361
                    if ($value) {
362
                        $this->field['deletedon'] = time() + $this->modxConfig('server_offset_time');
363
                    } else {
364
                        $this->field['deletedon'] = 0;
365
                    }
366
                    break;
367 1
                case 'deletedon':
368
                    $value = $this->getTime($value);
369
                    if ($value > 0 && time() + $this->modxConfig('server_offset_time') < $value) {
370
                        $value = 0;
371
                    }
372
                    if ($value) {
373
                        $this->field['deleted'] = 1;
374
                    }
375
                    break;
376 1
                case 'editedon':
377 1
                case 'createdon':
378 1
                case 'publishedon':
379 1
                    $value = $this->getTime($value);
380 1
                    break;
381 1
                case 'publishedby':
382 1
                case 'editedby':
383 1
                case 'createdby':
384 1
                case 'deletedby':
385
                    $value = $this->getUser($value, $this->default_field[$key]);
386
                    break;
387
            }
388 1
            $this->field[$key] = $value;
389 1
        }
390
391 1
        return $this;
392
    }
393
394
    /**
395
     * @param $value
396
     * @param int $default
397
     * @return int|mixed
398
     */
399
    protected function getUser($value, $default = 0)
400
    {
401
        $currentAdmin = APIHelpers::getkey($_SESSION, 'mgrInternalKey', 0);
402
        $value = (int)$value;
403
        if (! empty($value)) {
404
            $by = $this->findUserBy($value);
405
            $exists = $this->managerUsers->exists(function ($key, Helpers\Collection $val) use ($by, $value) {
406
                return ($val->containsKey($by) && $val->get($by) === (string)$value);
407
            });
408
            if (! $exists) {
409
                $value = 0;
410
            }
411
        }
412
        if (empty($value)) {
413
            $value = empty($currentAdmin) ? $default : $currentAdmin;
414
        }
415
416
        return $value;
417
    }
418
419
    /**
420
     * @param $data
421
     * @return bool|string
422
     */
423
    protected function findUserBy($data)
424
    {
425
        switch (true) {
426
            case (is_int($data) || ((int)$data > 0 && (string)intval($data) === $data)):
427
                $find = 'id';
428
                break;
429
            case filter_var($data, FILTER_VALIDATE_EMAIL):
430
                $find = 'email';
431
                break;
432
            case is_scalar($data):
433
                $find = 'username';
434
                break;
435
            default:
436
                $find = false;
437
        }
438
439
        return $find;
440
    }
441
442
    /**
443
     * @param array $data
444
     * @return $this
445
     */
446 1
    public function create($data = array())
447
    {
448 1
        $this->close();
449 1
        $fld = array();
450 1
        foreach ($this->tvd as $name => $tv) {
451 1
            $fld[$name] = $tv['default'];
452 1
        };
453 1
        $this->store($fld);
454
455 1
        $this->fromArray(array_merge($fld, $data));
456 1
        $this->set('createdby', null)
457 1
            ->set('editedby', null)
458 1
            ->set('createdon', time())
459 1
            ->touch();
460
461 1
        return $this;
462
    }
463
464
    /**
465
     * @param $id
466
     * @return $this
467
     */
468
    public function edit($id)
469
    {
470
        $id = is_scalar($id) ? trim($id) : '';
471
        if ($this->getID() != $id) {
472
            $this->close();
473
            $this->markAllEncode();
474
            $this->newDoc = false;
475
            $result = $this->query("SELECT * from {$this->makeTable('site_content')} where `id`=" . (int)$id);
476
            $this->fromArray($this->modx->db->getRow($result));
477
            $result = $this->query("SELECT * from {$this->makeTable('site_tmplvar_contentvalues')} where `contentid`=" . (int)$id);
478
            while ($row = $this->modx->db->getRow($result)) {
479
                $this->field[$this->tvid[$row['tmplvarid']]] = $row['value'];
480
            }
481
            $fld = array();
482
            foreach ($this->tvd as $name => $tv) {
483
                if ($this->belongsToTemplate($this->tv[$name])) {
484
                    $fld[$name] = $tv['default'];
485
                }
486
            };
487
            $this->store(array_merge($fld, $this->field));
488
            if (empty($this->field['id'])) {
489
                $this->id = null;
490
            } else {
491
                $this->id = $this->field['id'];
492
                $this->set('editedby', null)->touch();
493
                $this->decodeFields();
494
            }
495
            unset($this->field['id']);
496
        }
497
498
        return $this;
499
    }
500
501
    /**
502
     * @param bool $fire_events
503
     * @param bool $clearCache
504
     * @return mixed
505
     */
506 1
    public function save($fire_events = false, $clearCache = false)
0 ignored issues
show
Coding Style introduced by
Function's nesting level (5) exceeds 3; consider refactoring the function
Loading history...
507
    {
508 1
        $parent = null;
509 1
        if ($this->field['pagetitle'] == '') {
510
            $this->log['emptyPagetitle'] = 'Pagetitle is empty in <pre>' . print_r($this->field, true) . '</pre>';
0 ignored issues
show
Bug introduced by
Are you sure print_r($this->field, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

510
            $this->log['emptyPagetitle'] = 'Pagetitle is empty in <pre>' . /** @scrutinizer ignore-type */ print_r($this->field, true) . '</pre>';
Loading history...
511
512
            return false;
513
        }
514
515 1
        $uid = $this->modx->getLoginUserID('mgr');
0 ignored issues
show
Unused Code introduced by
The call to DocumentParser::getLoginUserID() has too many arguments starting with 'mgr'. ( Ignorable by Annotation )

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

515
        /** @scrutinizer ignore-call */ $uid = $this->modx->getLoginUserID('mgr');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
516
517
        if (
518 1
            empty($this->field['parent']) &&
519 1
            ! $this->modxConfig('udperms_allowroot') &&
520 1
            !($uid && isset($_SESSION['mgrRole']) && $_SESSION['mgrRole'] == 1)
0 ignored issues
show
Coding Style introduced by
Expected 1 space after NOT operator; 0 found
Loading history...
521 1
        ) {
522 1
            $this->log['rootForbidden'] = 'Only Administrators can create documents in the root folder because udperms_allowroot setting is off';
523
524 1
            return false;
525
        }
526
527 1
        $this->set('alias', $this->getAlias());
528
        $data = $this->toArray(null, null, null, false);
529 1
        $this->invokeEvent('OnBeforeDocFormSave', array(
530 1
            'mode'   => $this->newDoc ? "new" : "upd",
531 1
            'id'     => isset($this->id) ? $this->id : '',
532 1
            'doc'    => &$data,
533
            'docObj' => $this
534 1
        ), $fire_events);
535
        $this->fromArray($data);
536 1
537 1
        $fld = $this->encodeFields()->toArray(null, null, null, false);
538 1
        foreach ($this->default_field as $key => $value) {
539 1
            $tmp = $this->get($key);
540 1
            if ($this->newDoc && (!is_int($tmp) && $tmp == '')) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after NOT operator; 0 found
Loading history...
541
                if ($tmp == $value) {
542 1
                    switch ($key) {
543
                        case 'cacheable':
544
                            $value = (int)$this->modxConfig('cache_default');
545 1
                            break;
546
                        case 'template':
547
                            $value = (int)$this->modxConfig('default_template');
548 1
                            break;
549
                        case 'published':
550
                            $value = (int)$this->modxConfig('publish_default');
551 1
                            break;
552
                        case 'searchable':
553
                            $value = (int)$this->modxConfig('search_default');
554 1
                            break;
555 1
                        case 'donthit':
556 1
                            $value = (int)$this->modxConfig('track_visitors');
557
                            break;
558 1
                    }
559 1
                }
560 1
                $this->field[$key] = $value;
561
            }
562 1
            switch (true) {
563 1
                case $key == 'parent':
564 1
                    $parent = (int)$this->get($key);
565 1
                    $q = $this->query("SELECT count(`id`) FROM {$this->makeTable('site_content')} WHERE `id`='{$parent}'");
566 1
                    if ($this->modx->db->getValue($q) != 1) {
567 1
                        $parent = 0;
568 1
                    }
569 1
                    $this->field[$key] = $parent;
570 1
                    $this->Uset($key);
571 1
                    break;
572
                case ($key == 'alias_visible' && ! $this->checkVersion('1.0.10', true)):
573
                    $this->eraseField('alias_visible');
574 1
                    break;
575 1
                default:
576 1
                    $this->Uset($key);
577 1
            }
578 1
            unset($fld[$key]);
579
        }
580 1
581 1
        if (! empty($this->set)) {
582 1
            if ($this->newDoc) {
583 1
                $SQL = "INSERT into {$this->makeTable('site_content')} SET " . implode(', ', $this->set);
584
            } else {
585
                $SQL = "UPDATE {$this->makeTable('site_content')} SET " . implode(', ',
586
                        $this->set) . " WHERE `id` = " . $this->id;
587 1
            }
588
            $this->query($SQL);
589 1
590 1
            if ($this->newDoc) {
591 1
                $this->id = $this->modx->db->getInsertId();
592
            }
593 1
594
            if ($parent > 0) {
595
                $this->query("UPDATE {$this->makeTable('site_content')} SET `isfolder`='1' WHERE `id`='{$parent}'");
596 1
            }
597
        }
598 1
599 1
        $_deleteTVs = $_insertTVs = array();
600 1
        foreach ($fld as $key => $value) {
601 1
            if (empty($this->tv[$key]) || ! $this->isChanged($key) || ! $this->belongsToTemplate($this->tv[$key])) {
602
                continue;
603
            } elseif ($value === '' || is_null($value) || (isset($this->tvd[$key]) && $value == $this->tvd[$key]['default'])) {
604
                $_deleteTVs[] = $this->tv[$key];
605
            } else {
606
                $_insertTVs[$this->tv[$key]] = $this->escape($value);
607 1
            }
608
        }
609 1
610
        if (! empty($_insertTVs)) {
611
            $values = array();
612
            foreach ($_insertTVs as $id => $value) {
613
                $values[] = "({$this->id}, {$id}, '{$value}')";
614
            }
615
            $values = implode(',', $values);
616
            $this->query("INSERT INTO {$this->makeTable('site_tmplvar_contentvalues')} (`contentid`,`tmplvarid`,`value`) VALUES {$values} ON DUPLICATE KEY UPDATE
617
    `value` = VALUES(`value`)");
618
        }
619 1
620
        if (! empty($_deleteTVs)) {
621
            $ids = implode(',', $_deleteTVs);
622
            $this->query("DELETE FROM {$this->makeTable('site_tmplvar_contentvalues')} WHERE `contentid` = '{$this->id}' AND `tmplvarid` IN ({$ids})");
623
        }
624 1
625
        if (!isset($this->mode)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after NOT operator; 0 found
Loading history...
626
            $this->mode = $this->newDoc ? "new" : "upd";
627
            $this->newDoc = false;
628
        }
629 1
630
        if (! empty($this->groupIds)) {
631
            $this->setDocumentGroups($this->id, $this->groupIds);
632 1
        }
633 1
        $this->invokeEvent('OnDocFormSave', array(
634 1
            'mode'   => $this->mode,
635 1
            'id'     => isset($this->id) ? $this->id : '',
636
            'doc'    => $this->toArray(null, null, null, false),
637 1
            'docObj' => $this
638
        ), $fire_events);
639
640 1
641
        $this->modx->getAliasListing($this->id);
642 1
643
        if ($clearCache) {
644
            $this->clearCache($fire_events);
645 1
        }
646
        $this->decodeFields();
647 1
648
        return $this->id;
649
    }
650
651
    /**
652
     * @param $tvId
653
     * @return bool
654
     */
655
    protected function belongsToTemplate($tvId)
656
    {
657
        $template = $this->get('template');
658
659
        return isset($this->tvTpl[$template]) && in_array($tvId, $this->tvTpl[$template]);
660
    }
661
662
    /**
663
     * @param $ids
664
     * @return $this
665
     * @throws Exception
666
     */
667
    public function toTrash($ids)
668
    {
669
        $ignore = $this->systemID();
670
        $_ids = $this->cleanIDs($ids, ',', $ignore);
671
        if (is_array($_ids) && $_ids != array()) {
672
            $id = $this->sanitarIn($_ids);
673
            $uid = (int)$this->modx->getLoginUserId();
674
            $deletedon = time() + $this->modxConfig('server_offset_time');
675
            $this->query("UPDATE {$this->makeTable('site_content')} SET `deleted`=1, `deletedby`={$uid}, `deletedon`={$deletedon} WHERE `id` IN ({$id})");
676
        } else {
677
            throw new Exception('Invalid IDs list for mark trash: <pre>' . print_r($ids,
0 ignored issues
show
Bug introduced by
Are you sure print_r($ids, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

677
            throw new Exception('Invalid IDs list for mark trash: <pre>' . /** @scrutinizer ignore-type */ print_r($ids,
Loading history...
678
                    1) . '</pre> please, check ignore list: <pre>' . print_r($ignore, 1) . '</pre>');
0 ignored issues
show
Bug introduced by
Are you sure print_r($ignore, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

678
                    1) . '</pre> please, check ignore list: <pre>' . /** @scrutinizer ignore-type */ print_r($ignore, 1) . '</pre>');
Loading history...
679
        }
680
681
        return $this;
682
    }
683
684
    /**
685
     * @param bool $fire_events
686
     * @return $this
687
     */
688
    public function clearTrash($fire_events = false)
689
    {
690
        $q = $this->query("SELECT `id` FROM {$this->makeTable('site_content')} WHERE `deleted`='1'");
691
        $_ids = $this->modx->db->getColumn('id', $q);
692
        if (is_array($_ids) && $_ids != array()) {
693
            $this->invokeEvent('OnBeforeEmptyTrash', array(
694
                "ids" => $_ids
695
            ), $fire_events);
696
697
            $id = $this->sanitarIn($_ids);
698
            $this->query("DELETE from {$this->makeTable('site_content')} where `id` IN ({$id})");
699
            $this->query("DELETE from {$this->makeTable('site_tmplvar_contentvalues')} where `contentid` IN ({$id})");
700
701
            $this->invokeEvent('OnEmptyTrash', array(
702
                "ids" => $_ids
703
            ), $fire_events);
704
        }
705
706
        return $this;
707
    }
708
709
    /**
710
     * @param $ids
711
     * @param int|bool $depth
712
     * @return array
713
     */
714
    public function children($ids, $depth)
715
    {
716
        $_ids = $this->cleanIDs($ids, ',');
717
        if (is_array($_ids) && $_ids != array()) {
718
            $id = $this->sanitarIn($_ids);
719
            if (! empty($id)) {
720
                $q = $this->query("SELECT `id` FROM {$this->makeTable('site_content')} where `parent` IN ({$id})");
721
                $id = $this->modx->db->getColumn('id', $q);
722
                if ($depth > 0 || $depth === true) {
723
                    $id = $this->children($id, is_bool($depth) ? $depth : ($depth - 1));
724
                }
725
                $_ids = array_merge($_ids, $id);
726
            }
727
        }
728
729
        return $_ids;
730
    }
731
732
    /**
733
     * @param string|array $ids
734
     * @param bool $fire_events
735
     * @return $this
736
     * @throws Exception
737
     */
738
    public function delete($ids, $fire_events = false)
739
    {
740
        $ids = $this->children($ids, true);
741
        $_ids = $this->cleanIDs($ids, ',', $this->systemID());
742
        $this->invokeEvent('OnBeforeDocFormDelete', array(
743
            'ids' => $_ids
744
        ), $fire_events);
745
        $this->toTrash($_ids);
746
        $this->invokeEvent('OnDocFormDelete', array(
747
            'ids' => $_ids
748
        ), $fire_events);
749
750
        return $this;
751
    }
752
753
    /**
754
     * @return array
755
     */
756
    private function systemID()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
757
    {
758
        $ignore = array(
759
            0, //empty document
760
            (int)$this->modxConfig('site_start'),
761
            (int)$this->modxConfig('error_page'),
762
            (int)$this->modxConfig('unauthorized_page'),
763
            (int)$this->modxConfig('site_unavailable_page')
764
        );
765
        $data = $this->query("SELECT DISTINCT setting_value FROM {$this->makeTable('web_user_settings')} WHERE `setting_name`='login_home' AND `setting_value`!=''");
766
        $data = $this->modx->db->makeArray($data);
767
        foreach ($data as $item) {
768
            $ignore[] = (int)$item['setting_value'];
769
        }
770
771
        return array_unique($ignore);
772
773
    }
0 ignored issues
show
Coding Style introduced by
Function closing brace must go on the next line following the body; found 1 blank lines before brace
Loading history...
774
775
    /**
776
     * @param $alias
777
     * @return string
778 1
     */
779
    protected function checkAlias($alias)
780 1
    {
781 1
        $alias = strtolower($alias);
782
        if ($this->modxConfig('friendly_urls')) {
783
            $_alias = $this->escape($alias);
784
            if ((! $this->modxConfig('allow_duplicate_alias') && ! $this->modxConfig('use_alias_path')) || ($this->modxConfig('allow_duplicate_alias') && $this->modxConfig('use_alias_path'))) {
785
                $flag = $this->modx->db->getValue($this->query("SELECT `id` FROM {$this->makeTable('site_content')} WHERE `alias`='{$_alias}' AND `parent`={$this->get('parent')} LIMIT 1"));
786
            } else {
787
                $flag = $this->modx->db->getValue($this->query("SELECT `id` FROM {$this->makeTable('site_content')} WHERE `alias`='{$_alias}' LIMIT 1"));
788
            }
789
            if (($flag && $this->newDoc) || (! $this->newDoc && $flag && $this->id != $flag)) {
790
                $suffix = substr($alias, -2);
791
                if (preg_match('/-(\d+)/', $suffix, $tmp) && isset($tmp[1]) && (int)$tmp[1] > 1) {
792
                    $suffix = (int)$tmp[1] + 1;
793
                    $alias = substr($alias, 0, -2) . '-' . $suffix;
794
                } else {
795
                    $alias .= '-2';
796
                }
797
                $alias = $this->checkAlias($alias);
798
            }
799
        }
800 1
801
        return $alias;
802
    }
803
804
    /**
805
     * @param $key
806
     * @return bool
807 1
     */
808
    public function issetField($key)
809 1
    {
810
        return (array_key_exists($key, $this->default_field) || (array_key_exists($key, $this->tv) && $this->belongsToTemplate($this->tv[$key])));
811
    }
812
813
    /**
814
     * @param bool $reload
815
     * @return $this
816 2
     */
817
    protected function get_TV($reload = false)
0 ignored issues
show
Coding Style introduced by
Method name "modResource::get_TV" is not in camel caps format
Loading history...
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
818 2
    {
819 2
        $this->modx->_TVnames = $this->loadFromCache('_TVnames');
0 ignored issues
show
Bug introduced by
The property _TVnames does not seem to exist on DocumentParser.
Loading history...
820 2
        if ($this->modx->_TVnames === false || empty($this->modx->_TVnames) || $reload) {
821 2
            $this->modx->_TVnames = array();
822 2
            $result = $this->query('SELECT `id`,`name`,`default_text`,`type`,`display`,`display_params` FROM ' . $this->makeTable('site_tmplvars'));
823 2
            while ($row = $this->modx->db->GetRow($result)) {
824 2
                $this->modx->_TVnames[$row['name']] = array(
825 2
                    'id'      => $row['id'],
826 2
                    'type'    => $row['type'],
827 2
                    'default' => $row['default_text'],
828 2
                    'display' => $row['display'],
829 2
                    'display_params' => $row['display_params']
830 2
                );
831 2
            }
832 2
            $this->saveToCache($this->modx->_TVnames, '_TVnames');
833 2
        }
834 2
        $arrayTypes = array('checkbox', 'listbox-multiple');
835 2
        $arrayTVs = array();
836 2
        foreach ($this->modx->_TVnames as $name => $data) {
837 2
            $this->tvid[$data['id']] = $name;
838 2
            $this->tv[$name] = $data['id'];
839
            if (in_array($data['type'], $arrayTypes)) {
840
                $arrayTVs[] = $name;
841 2
            }
842 2
        }
843 2
        if (empty($this->tvaFields)) {
844 2
            $this->tvaFields = $arrayTVs;
845 2
        }
846
        $this->loadTVTemplate()->loadTVDefault(array_values($this->tv));
847 2
848
        return $this;
849
    }
850
851
    /**
852
     * @return $this
853 2
     */
854
    protected function loadTVTemplate()
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
855 2
    {
856 2
        $this->tvTpl = $this->loadFromCache('_tvTpl');
857 2
        if ($this->tvTpl === false) {
858 2
            $q = $this->query("SELECT `tmplvarid`, `templateid` FROM " . $this->makeTable('site_tmplvar_templates'));
859 2
            $this->tvTpl = array();
860 2
            while ($item = $this->modx->db->getRow($q)) {
861 2
                $this->tvTpl[$item['templateid']][] = $item['tmplvarid'];
862 2
            }
863 2
            $this->saveToCache($this->tvTpl, '_tvTpl');
864
        }
865 2
866
        return $this;
867
    }
868
869
    /**
870
     * @param array $tvId
871
     * @return $this
872 2
     */
873
    protected function loadTVDefault(array $tvId = array())
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
874 2
    {
875 2
        if (is_array($tvId) && ! empty($tvId)) {
876 2
            $this->tvd = array();
877 2
            foreach ($tvId as $id) {
878 2
                $name = $this->tvid[$id];
879 2
                $this->tvd[$name] = $this->modx->_TVnames[$name];
0 ignored issues
show
Bug introduced by
The property _TVnames does not seem to exist on DocumentParser.
Loading history...
880 2
            }
881
        }
882 2
883
        return $this;
884
    }
885
886
    /**
887
     * @param $tpl
888
     * @return int
889
     * @throws Exception
890 1
     */
891
    public function setTemplate($tpl)
892 1
    {
893 1
        if (!is_numeric($tpl) || $tpl != (int)$tpl) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after NOT operator; 0 found
Loading history...
894 1
            if (is_scalar($tpl)) {
895 1
                $sql = "SELECT `id` FROM {$this->makeTable('site_templates')} WHERE `templatename` = '" . $this->escape($tpl) . "'";
896 1
                $rs = $this->query($sql);
897 1
                if (! $rs || $this->modx->db->getRecordCount($rs) <= 0) {
898
                    throw new Exception("Template {$tpl} is not exists");
899 1
                }
900 1
                $tpl = $this->modx->db->getValue($rs);
901
            } else {
902
                throw new Exception("Invalid template name: " . print_r($tpl, 1));
0 ignored issues
show
Bug introduced by
Are you sure print_r($tpl, 1) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

902
                throw new Exception("Invalid template name: " . /** @scrutinizer ignore-type */ print_r($tpl, 1));
Loading history...
903 1
            }
904
        }
905 1
906
        return (int)$tpl;
907
    }
908
909
    /**
910
     * @return string
911 1
     */
912
    protected function getAlias()
913 1
    {
914
        if ($this->modxConfig('friendly_urls') && $this->modxConfig('automatic_alias') && $this->get('alias') == '') {
915
            $alias = strtr($this->get('pagetitle'), $this->table);
916 1
        } else {
917
            if ($this->get('alias') != '') {
918
                $alias = $this->get('alias');
919 1
            } else {
920
                $alias = '';
921
            }
922 1
        }
923
        $alias = $this->modx->stripAlias($alias);
924 1
925
        return $this->checkAlias($alias);
926
    }
927
928
    /**
929
     * @param int $parent
930
     * @param string $criteria
931
     * @param string $dir
932
     * @return $this
933
     *
934
     * Пересчет menuindex по полю таблицы site_content
935
     */
936
    public function updateMenuindex($parent, $criteria = 'id', $dir = 'asc')
937
    {
938
        $dir = strtolower($dir) == 'desc' ? 'desc' : 'asc';
939
        if (is_integer($parent) && $criteria !== '') {
940
            $this->query("SET @index := 0");
941
            $this->query("UPDATE {$this->makeTable('site_content')} SET `menuindex` = (@index := @index + 1) WHERE `parent`={$parent} ORDER BY {$criteria} {$dir}");
942
        }
943
944
        return $this;
945
    }
946
947
    /**
948
     * Устанавливает значение шаблона согласно системной настройке
949
     *
950
     * @return $this
951
     */
952
    public function setDefaultTemplate()
0 ignored issues
show
Coding Style introduced by
Function's nesting level (5) exceeds 3; consider refactoring the function
Loading history...
953
    {
954
        $parent = $this->get('parent');
955
        $template = $this->modxConfig('default_template');
956
        switch ($this->modxConfig('auto_template_logic')) {
957
            case 'sibling':
958
                if (! $parent) {
959
                    $site_start = $this->modxConfig('site_start');
960
                    $where = "sc.isfolder=0 AND sc.id!={$site_start}";
961
                    $sibl = $this->modx->getDocumentChildren($parent, 1, 0, 'template', $where, 'menuindex', 'ASC', 1);
0 ignored issues
show
Bug introduced by
The method getDocumentChildren() does not exist on DocumentParser. ( Ignorable by Annotation )

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

961
                    /** @scrutinizer ignore-call */ $sibl = $this->modx->getDocumentChildren($parent, 1, 0, 'template', $where, 'menuindex', 'ASC', 1);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
962
                    if (isset($sibl[0]['template']) && $sibl[0]['template'] !== '') {
963
                        $template = $sibl[0]['template'];
964
                    }
965
                } else {
966
                    $sibl = $this->modx->getDocumentChildren($parent, 1, 0, 'template', 'isfolder=0', 'menuindex',
967
                        'ASC', 1);
968
                    if (isset($sibl[0]['template']) && $sibl[0]['template'] !== '') {
969
                        $template = $sibl[0]['template'];
970
                    } else {
971
                        $sibl = $this->modx->getDocumentChildren($parent, 0, 0, 'template', 'isfolder=0', 'menuindex',
972
                            'ASC', 1);
973
                        if (isset($sibl[0]['template']) && $sibl[0]['template'] !== '') {
974
                            $template = $sibl[0]['template'];
975
                        }
976
                    }
977
                }
978
                break;
979
            case 'parent':
980
                if ($parent) {
981
                    $_parent = $this->modx->getPageInfo($parent, 0, 'template');
0 ignored issues
show
Bug introduced by
The method getPageInfo() does not exist on DocumentParser. ( Ignorable by Annotation )

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

981
                    /** @scrutinizer ignore-call */ $_parent = $this->modx->getPageInfo($parent, 0, 'template');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
982
                    if (isset($_parent['template'])) {
983
                        $template = $_parent['template'];
984
                    }
985
                }
986
                break;
987
        }
988
        $this->set('template', $template);
989
990
        return $this;
991
    }
992
993
    /**
994
     * Декодирует конкретное поле
995
     * @param  string $field Имя поля
996
     * @param  bool $store обновить распакованное поле
997
     * @return array ассоциативный массив с данными из json строки
998 1
     */
999
    public function decodeField($field, $store = false)
1000
    {
1001 1
        $out = array();
1002
        if ($this->isDecodableField($field)) {
1003
            $data = $this->get($field);
1004
            if ($this->isTVarrayField($field)) {
1005
                $out = explode('||', $data);
1006
            } else {
1007
                $out = jsonHelper::jsonDecode($data, array('assoc' => true), true);
1008
            }
1009
        }
1010
        if ($store) {
1011
            $this->field[$field] = $out;
1012
            $this->markAsDecode($field);
1013
        }
1014
1015
        return $out;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $out also could return the type xNop which is incompatible with the documented return type array.
Loading history...
1016
    }
1017
1018
    /**
1019
     * Запаковывает конкретное поле в JSON
1020
     * @param  string $field Имя поля
1021
     * @param  bool $store обновить запакованное поле
1022
     * @return string|null json строка
1023 1
     */
1024
    public function encodeField($field, $store = false)
1025
    {
1026
        $out = null;
1027
        if ($this->isEncodableField($field)) {
1028
            $data = $this->get($field);
1029
            if ($this->isTVarrayField($field)) {
1030
                $out = is_array($data) ? implode('||', $data) : (string)$data;
1031
            } else {
1032
                $out = json_encode($data);
1033
            }
1034
        }
1035 1
        if ($store) {
1036
            $this->field[$field] = $out;
1037
            $this->markAsEncode($field);
1038
        }
1039
1040
        return $out;
1041
    }
1042
1043
    /**
1044
     * Может ли содержать данное поле json массив
1045
     * @param  string $field имя поля
1046
     * @return boolean
1047 1
     */
1048
    public function isTVarrayField($field)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1049 1
    {
1050
        return (is_scalar($field) && in_array($field, $this->tvaFields));
1051
    }
1052
1053
    /**
1054
     * Пометить все поля как запакованные
1055
     * @return $this
1056
     */
1057
    public function markAllEncode()
1058
    {
1059
        parent::markAllEncode();
1060
        foreach ($this->tvaFields as $field) {
1061
            $this->markAsEncode($field);
1062
        }
1063
1064
        return $this;
1065
    }
1066
1067
    /**
1068
     * Пометить все поля как распакованные
1069
     * @return $this
1070 1
     */
1071
    public function markAllDecode()
1072 1
    {
1073 1
        parent::markAllDecode();
1074
        foreach ($this->tvaFields as $field) {
1075 1
            $this->markAsDecode($field);
1076
        }
1077 1
1078
        return $this;
1079
    }
1080
1081
    /**
1082
     * @param int $docId
1083
     */
1084
    public function getDocumentGroups($docId = 0)
1085
    {
1086
        $out = array();
1087
        $doc = $this->switchObject($docId);
1088
        if (null !== $doc->getID()) {
1089
            $doc_groups = $this->makeTable('document_groups');
1090
            $docgroup_names = $this->makeTable('documentgroup_names');
1091
1092
            $rs = $this->query("SELECT `dg`.`document_group`, `dgn`.`name` FROM {$doc_groups} as `dg` INNER JOIN {$docgroup_names} as `dgn` ON `dgn`.`id`=`dg`.`document_group`
1093
                WHERE `dg`.`document` = " . $doc->getID());
1094
            while ($row = $this->modx->db->getRow($rs)) {
1095
                $out[$row['document_group']] = $row['name'];
1096
            }
1097
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
1098
        }
1099
        unset($doc);
1100
1101
        return $out;
1102
    }
1103
1104
    /**
1105
     * @param int $docId
1106
     * @param array $groupIds
1107
     * @return $this
1108
     */
1109
    public function setDocumentGroups($docId = 0, $groupIds = array())
1110
    {
1111
        if (!is_array($groupIds)) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after NOT operator; 0 found
Loading history...
introduced by
The condition is_array($groupIds) is always true.
Loading history...
1112
            return $this;
1113
        }
1114
        if ($this->newDoc && $docId == 0) {
1115
            $this->groupIds = $groupIds;
1116
        } else {
1117
            $doc = $this->switchObject($docId);
1118
            if ($id = $doc->getID()) {
1119
                foreach ($groupIds as $gid) {
1120
                    $this->query("REPLACE INTO {$this->makeTable('document_groups')} (`document_group`, `document`) VALUES ('{$gid}', '{$id}')");
1121
                }
1122
                if (! $this->newDoc) {
1123
                    $groupIds = empty($groupIds) ? '0' : implode(',', $groupIds);
1124
                    $this->query("DELETE FROM {$this->makeTable('document_groups')} WHERE `document`={$id} AND `document_group` NOT IN ({$groupIds})");
1125
                }
1126
            }
1127
            unset($doc);
1128
            $this->groupIds = array();
1129
        }
1130
1131
        return $this;
1132
    }
1133
}
1134