Completed
Push — master ( fc2c42...d0941c )
by Richard
15s
created

PublisherItemHandler   F

Complexity

Total Complexity 114

Size/Duplication

Total Lines 680
Duplicated Lines 10.88 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
dl 74
loc 680
rs 3.5664
c 0
b 0
f 0
wmc 114
lcom 1
cbo 12

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
D insert() 8 27 9
A delete() 4 18 4
A getItemCount() 0 20 4
C getItemsCount() 14 37 13
A getAllPublished() 0 5 1
A getPreviousPublished() 0 11 2
A getNextPublished() 0 12 2
A getAllSubmitted() 0 4 1
A getAllOffline() 0 4 1
A getAllRejected() 0 4 1
D getItems() 15 46 14
A getRandomItem() 0 17 3
A updateCounter() 0 14 2
B addNotNullFieldClause() 0 21 5
F getItemsFromSearch() 15 92 28
B getLastPublishedByCat() 6 45 5
A countArticlesByCat() 0 14 3
C getCountsByCat() 6 51 8
C getItemObjects() 6 35 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PublisherItemHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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

1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
use Xoops\Core\Database\Connection;
13
use Xoops\Core\Kernel\XoopsObject;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, XoopsObject.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
14
use Xoops\Core\Kernel\XoopsPersistableObjectHandler;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, XoopsPersistableObjectHandler.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use Xoops\Core\Kernel\Criteria;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Criteria.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
use Xoops\Core\Kernel\CriteriaCompo;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, CriteriaCompo.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
17
use Xmf\Request;
18
19
/**
20
 * @copyright       The XUUPS Project http://sourceforge.net/projects/xuups/
21
 * @license         GNU GPL V2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
22
 * @package         Publisher
23
 * @since           1.0
24
 * @author          trabis <[email protected]>
25
 * @author          The SmartFactory <www.smartfactory.ca>
26
 * @version         $Id$
27
 */
28
29
include_once dirname(__DIR__) . '/include/common.php';
30
31
class PublisherItem extends XoopsObject
32
{
33
    /**
34
     * @var Publisher
35
     * @access public
36
     */
37
    public $publisher = null;
38
39
    /**
40
     * @var PublisherCategory
41
     * @access public
42
     */
43
    public $_category = null;
44
45
    /**
46
     * @param int|null $id
47
     */
48
    public function __construct($id = null)
49
    {
50
        $this->publisher = Publisher::getInstance();
51
        $this->initVar("itemid", XOBJ_DTYPE_INT, 0);
52
        $this->initVar("categoryid", XOBJ_DTYPE_INT, 0, false);
53
        $this->initVar("title", XOBJ_DTYPE_TXTBOX, '', true, 255);
54
        $this->initVar("subtitle", XOBJ_DTYPE_TXTBOX, '', false, 255);
55
        $this->initVar("summary", XOBJ_DTYPE_TXTAREA, '', false);
56
        $this->initVar("body", XOBJ_DTYPE_TXTAREA, '', false);
57
        $this->initVar("uid", XOBJ_DTYPE_INT, 0, false);
58
        $this->initVar("author_alias", XOBJ_DTYPE_TXTBOX, '', false, 255);
59
        $this->initVar("datesub", XOBJ_DTYPE_INT, '', false);
60
        $this->initVar("status", XOBJ_DTYPE_INT, -1, false);
61
        $this->initVar("image", XOBJ_DTYPE_INT, 0, false);
62
        $this->initVar("images", XOBJ_DTYPE_TXTBOX, '', false, 255);
63
        $this->initVar("counter", XOBJ_DTYPE_INT, 0, false);
64
        $this->initVar("rating", XOBJ_DTYPE_OTHER, 0, false);
65
        $this->initVar("votes", XOBJ_DTYPE_INT, 0, false);
66
        $this->initVar("weight", XOBJ_DTYPE_INT, 0, false);
67
        $this->initVar("dohtml", XOBJ_DTYPE_INT, 1, true);
68
        $this->initVar("dosmiley", XOBJ_DTYPE_INT, 1, true);
69
        $this->initVar("doimage", XOBJ_DTYPE_INT, 1, true);
70
        $this->initVar("dobr", XOBJ_DTYPE_INT, 1, false);
71
        $this->initVar("doxcode", XOBJ_DTYPE_INT, 1, true);
72
        $this->initVar("cancomment", XOBJ_DTYPE_INT, 1, true);
73
        $this->initVar("comments", XOBJ_DTYPE_INT, 0, false);
74
        $this->initVar("notifypub", XOBJ_DTYPE_INT, 1, false);
75
        $this->initVar("meta_keywords", XOBJ_DTYPE_TXTAREA, '', false);
76
        $this->initVar("meta_description", XOBJ_DTYPE_TXTAREA, '', false);
77
        $this->initVar("short_url", XOBJ_DTYPE_TXTBOX, '', false, 255);
78
        $this->initVar("item_tag", XOBJ_DTYPE_TXTAREA, '', false);
79
        // Non consistent values
80
        $this->initVar("pagescount", XOBJ_DTYPE_INT, 0, false);
81 View Code Duplication
        if (isset($id)) {
82
            $item = $this->publisher->getItemHandler()->get($id);
83
            foreach ($item->vars as $k => $v) {
84
                $this->assignVar($k, $v['value']);
85
            }
86
        }
87
    }
88
89
    /**
90
     * @return null|PublisherCategory
91
     */
92
    public function category()
93
    {
94
        if (!isset($this->_category)) {
95
            $this->_category = $this->publisher->getCategoryHandler()->get($this->getVar('categoryid'));
96
        }
97
        return $this->_category;
98
    }
99
100
    /**
101
     * @param int    $maxLength
102
     * @param string $format
103
     *
104
     * @return string
105
     */
106 View Code Duplication
    public function title($maxLength = 0, $format = "S")
107
    {
108
        $ret = $this->getVar("title", $format);
109
        if ($maxLength != 0) {
110
            if (!XoopsLocale::isMultiByte()) {
0 ignored issues
show
Deprecated Code introduced by
The method Xoops\Locale\AbstractLocale::isMultiByte() has been deprecated with message: since 2.6.0 -- UTF-8 is always used

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

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

Loading history...
111
                if (strlen($ret) >= $maxLength) {
112
                    $ret = PublisherUtils::substr($ret, 0, $maxLength);
113
                }
114
            }
115
        }
116
        return $ret;
117
    }
118
119
    /**
120
     * @param int    $maxLength
121
     * @param string $format
122
     *
123
     * @return mixed|string
124
     */
125 View Code Duplication
    public function subtitle($maxLength = 0, $format = "S")
126
    {
127
        $ret = $this->getVar("subtitle", $format);
128
        if ($maxLength != 0) {
129
            if (!XoopsLocale::isMultiByte()) {
0 ignored issues
show
Deprecated Code introduced by
The method Xoops\Locale\AbstractLocale::isMultiByte() has been deprecated with message: since 2.6.0 -- UTF-8 is always used

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

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

Loading history...
130
                if (strlen($ret) >= $maxLength) {
131
                    $ret = PublisherUtils::substr($ret, 0, $maxLength);
132
                }
133
            }
134
        }
135
        return $ret;
136
    }
137
138
    /**
139
     * @param int    $maxLength
140
     * @param string $format
141
     * @param string $stripTags
142
     *
143
     * @return mixed|string
144
     */
145
    public function summary($maxLength = 0, $format = "S", $stripTags = '')
146
    {
147
        $ret = $this->getVar("summary", $format);
148
        if (!empty($stripTags)) {
149
            $ret = strip_tags($ret, $stripTags);
150
        }
151 View Code Duplication
        if ($maxLength != 0) {
152
            if (!XoopsLocale::isMultiByte()) {
0 ignored issues
show
Deprecated Code introduced by
The method Xoops\Locale\AbstractLocale::isMultiByte() has been deprecated with message: since 2.6.0 -- UTF-8 is always used

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

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

Loading history...
153
                if (strlen($ret) >= $maxLength) {
154
                    //$ret = PublisherUtils::substr($ret , 0, $maxLength);
155
                    $ret = PublisherUtils::truncateTagSafe($ret, $maxLength, $etc = '...', $break_words = false);
156
                }
157
            }
158
        }
159
        return $ret;
160
    }
161
162
    /**
163
     * @param int  $maxLength
164
     * @param bool $fullSummary
165
     *
166
     * @return mixed|string
167
     */
168
    public function getBlockSummary($maxLength = 0, $fullSummary = false)
169
    {
170
        if ($fullSummary) {
171
            $ret = $this->summary(0, 's', '<br></ br>');
172
        } else {
173
            $ret = $this->summary($maxLength, 's', '<br></ br>');
174
        }
175
        //no summary? get body!
176
        if (strlen($ret) == 0) {
177
            $ret = $this->body($maxLength, 's', '<br></ br>');
178
        }
179
        return $ret;
180
    }
181
182
    /**
183
     * @param string $file_name
184
     *
185
     * @return string
186
     */
187
    public function wrappage($file_name)
188
    {
189
        $content = '';
190
        $page = PublisherUtils::getUploadDir(true, 'content') . $file_name;
191
        if (XoopsLoad::fileExists($page)) {
192
            // this page uses smarty template
193
            ob_start();
194
            include($page);
195
            $content = ob_get_contents();
196
            ob_end_clean();
197
            // Cleaning the content
198
            $body_start_pos = strpos($content, '<body>');
199
            if ($body_start_pos) {
200
                $body_end_pos = strpos($content, '</body>', $body_start_pos);
201
                $content = substr($content, $body_start_pos + strlen('<body>'), $body_end_pos - strlen('<body>') - $body_start_pos);
202
            }
203
            // Check if ML Hack is installed, and if yes, parse the $content in formatForML
204
            $myts = \Xoops\Core\Text\Sanitizer::getInstance();
205
            if (method_exists($myts, 'formatForML')) {
206
                $content = $myts->formatForML($content);
0 ignored issues
show
Bug introduced by
The method formatForML() does not seem to exist on object<Xoops\Core\Text\Sanitizer>.

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...
207
            }
208
        }
209
        return $content;
210
    }
211
212
    /**
213
     * This method returns the body to be displayed. Not to be used for editing
214
     *
215
     * @param int    $maxLength
216
     * @param string $format
217
     * @param string $stripTags
218
     *
219
     * @return mixed|string
220
     */
221
    public function body($maxLength = 0, $format = 'S', $stripTags = '')
222
    {
223
        $ret = $this->getVar('body', $format);
224
        $wrap_pos = strpos($ret, '[pagewrap=');
225
        if (!($wrap_pos === false)) {
226
            $wrap_pages = array();
227
            $wrap_code_length = strlen('[pagewrap=');
228
            while (!($wrap_pos === false)) {
229
                $end_wrap_pos = strpos($ret, ']', $wrap_pos);
230
                if ($end_wrap_pos) {
231
                    $wrap_page_name = substr($ret, $wrap_pos + $wrap_code_length, $end_wrap_pos - $wrap_code_length - $wrap_pos);
232
                    $wrap_pages[] = $wrap_page_name;
233
                }
234
                $wrap_pos = strpos($ret, '[pagewrap=', $end_wrap_pos - 1);
235
            }
236
            foreach ($wrap_pages as $page) {
237
                $wrap_page_content = $this->wrappage($page);
238
                $ret = str_replace("[pagewrap={$page}]", $wrap_page_content, $ret);
239
            }
240
        }
241
        if ($this->publisher->getConfig('item_disp_blocks_summary')) {
242
            $summary = $this->summary($maxLength, $format, $stripTags);
243
            if ($summary) {
244
                $ret = $this->summary() . '<br /><br />' . $ret;
245
            }
246
        }
247
        if (!empty($stripTags)) {
248
            $ret = strip_tags($ret, $stripTags);
249
        }
250 View Code Duplication
        if ($maxLength != 0) {
251
            if (!XoopsLocale::isMultiByte()) {
0 ignored issues
show
Deprecated Code introduced by
The method Xoops\Locale\AbstractLocale::isMultiByte() has been deprecated with message: since 2.6.0 -- UTF-8 is always used

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

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

Loading history...
252
                if (strlen($ret) >= $maxLength) {
253
                    //$ret = PublisherUtils::substr($ret , 0, $maxLength);
254
                    $ret = PublisherUtils::truncateTagSafe($ret, $maxLength, $etc = '...', $break_words = false);
255
                }
256
            }
257
        }
258
        return $ret;
259
    }
260
261
    /**
262
     * @param string $dateFormat
263
     * @param string $format
264
     *
265
     * @return string
266
     */
267
    public function datesub($dateFormat = '', $format = 'S')
268
    {
269
        if (empty($dateformat)) {
0 ignored issues
show
Bug introduced by
The variable $dateformat does not exist. Did you mean $dateFormat?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
270
            $dateFormat = $this->publisher->getConfig('format_date');
271
        }
272
        return XoopsLocale::formatTimestamp($this->getVar('datesub', $format), $dateFormat);
273
    }
274
275
    /**
276
     * @param int $realName
277
     *
278
     * @return string
279
     */
280
    public function posterName($realName = -1)
281
    {
282
        if ($realName == -1) {
283
            $realName = $this->publisher->getConfig('format_realname');
284
        }
285
        $ret = $this->getVar('author_alias');
286
        if ($ret == '') {
287
            $ret = XoopsUserUtility::getUnameFromId($this->getVar('uid'), $realName);
288
        }
289
        return $ret;
290
    }
291
292
    /**
293
     * @return string
294
     */
295
    public function posterAvatar()
296
    {
297
        $xoops = Xoops::getInstance();
298
        $ret = 'blank.gif';
299
        $member_handler = $xoops->getHandlerMember();
300
        $thisUser = $member_handler->getUser($this->getVar('uid'));
301
        if (is_object($thisUser)) {
302
            $ret = $xoops->service('avatar')->getAvatarUrl($thisUser)->getValue();
303
        }
304
        return $ret;
305
    }
306
307
    /**
308
     * @return string
309
     */
310
    public function linkedPosterName()
311
    {
312
        $ret = $this->getVar('author_alias');
313
        if ($ret == '') {
314
            $ret = XoopsUserUtility::getUnameFromId($this->getVar('uid'), $this->publisher->getConfig('format_realname'), true);
315
        }
316
        return $ret;
317
    }
318
319
    /**
320
     * @return mixed
321
     */
322
    public function updateCounter()
323
    {
324
        return $this->publisher->getItemHandler()->updateCounter($this->getVar('itemid'));
325
    }
326
327
    /**
328
     * @param bool $force
329
     *
330
     * @return bool
331
     */
332
    public function store($force = true)
333
    {
334
        $xoops = Xoops::getInstance();
335
        $isNew = $this->isNew();
336
        if (!$this->publisher->getItemHandler()->insert($this, $force)) {
337
            return false;
338
        }
339
        if ($isNew && $this->getVar('status') == _PUBLISHER_STATUS_PUBLISHED) {
340
            // Increment user posts
341
            $user_handler = $xoops->getHandlerUser();
342
            $member_handler = $xoops->getHandlerMember();
343
            $poster = $user_handler->get($this->getVar('uid'));
344
            if (is_object($poster) && !$poster->isNew()) {
345
                $poster->setVar('posts', $poster->getVar('posts') + 1);
346
                if (!$member_handler->insertUser($poster, true)) {
347
                    $this->setErrors('Article created but could not increment user posts.');
348
                    return false;
349
                }
350
            }
351
        }
352
        return true;
353
    }
354
355
    /**
356
     * @return string
357
     */
358
    public function getCategoryName()
359
    {
360
        return $this->category()->getVar('name');
361
    }
362
363
    /**
364
     * @return string
365
     */
366
    public function getCategoryUrl()
367
    {
368
        return $this->category()->getCategoryUrl();
369
    }
370
371
    /**
372
     * @return string
373
     */
374
    public function getCategoryLink()
375
    {
376
        return $this->category()->getCategoryLink();
377
    }
378
379
    /**
380
     * @param bool $withAllLink
381
     *
382
     * @return string
383
     */
384
    public function getCategoryPath($withAllLink = true)
385
    {
386
        return $this->category()->getCategoryPath($withAllLink);
387
    }
388
389
    /**
390
     * @return string
391
     */
392
    public function getCategoryImagePath()
393
    {
394
        return PublisherUtils::getImageDir('category', false) . $this->category()->image();
395
    }
396
397
    /**
398
     * @return mixed
399
     */
400
    public function getFiles()
401
    {
402
        return $this->publisher->getFileHandler()->getAllFiles($this->getVar('itemid'), _PUBLISHER_STATUS_FILE_ACTIVE);
403
    }
404
405
    /**
406
     * @return string
407
     */
408
    public function getAdminLinks()
409
    {
410
        $xoops = Xoops::getInstance();
411
        $adminLinks = '';
412
        if ($xoops->isUser() && (PublisherUtils::IsUserAdmin() || PublisherUtils::IsUserAuthor($this) || $this->publisher->getPermissionHandler()->isGranted('item_submit', $this->getVar('categoryid')))) {
413
            if (PublisherUtils::IsUserAdmin() || PublisherUtils::IsUserAuthor($this) || PublisherUtils::IsUserModerator($this)) {
414 View Code Duplication
                if ($this->publisher->getConfig('perm_edit') || PublisherUtils::IsUserModerator($this) || PublisherUtils::IsUserAdmin()) {
415
                    // Edit button
416
                    $adminLinks .= "<a href='" . PUBLISHER_URL . "/submit.php?itemid=" . $this->getVar('itemid') . "'><img src='" . PUBLISHER_URL . "/images/links/edit.gif'" . " title='" . _CO_PUBLISHER_EDIT . "' alt='" . _CO_PUBLISHER_EDIT . "'/></a>";
417
                    $adminLinks .= " ";
418
                }
419 View Code Duplication
                if ($this->publisher->getConfig('perm_delete') || PublisherUtils::IsUserModerator($this) || PublisherUtils::IsUserAdmin()) {
420
                    // Delete button
421
                    $adminLinks .= "<a href='" . PUBLISHER_URL . "/submit.php?op=del&amp;itemid=" . $this->getVar('itemid') . "'><img src='" . PUBLISHER_URL . "/images/links/delete.png'" . " title='" . _CO_PUBLISHER_DELETE . "' alt='" . _CO_PUBLISHER_DELETE . "' /></a>";
422
                    $adminLinks .= " ";
423
                }
424
            }
425 View Code Duplication
            if ($this->publisher->getConfig('perm_clone') || PublisherUtils::IsUserModerator($this) || PublisherUtils::IsUserAdmin()) {
426
                // Duplicate button
427
                $adminLinks .= "<a href='" . PUBLISHER_URL . "/submit.php?op=clone&amp;itemid=" . $this->getVar('itemid') . "'><img src='" . PUBLISHER_URL . "/images/links/clone.gif'" . " title='" . _CO_PUBLISHER_CLONE . "' alt='" . _CO_PUBLISHER_CLONE . "' /></a>";
428
                $adminLinks .= " ";
429
            }
430
        }
431
        // PDF button
432
        if ($xoops->service('htmltopdf')->isAvailable()) {
433
            $adminLinks .= "<a href='" . PUBLISHER_URL . "/makepdf.php?itemid=" . $this->getVar('itemid') . "' rel='nofollow' target='_blank'><img src='" . PUBLISHER_URL . "/images/links/pdf.gif' title='" . _CO_PUBLISHER_PDF . "' alt='" . _CO_PUBLISHER_PDF . "' /></a>";
434
            $adminLinks .= " ";
435
        }
436
        // Print button
437
        $adminLinks .= "<a href='" . PublisherUtils::seoGenUrl("print", $this->getVar('itemid'), $this->getVar('short_url')) . "' rel='nofollow' target='_blank'><img src='" . PUBLISHER_URL . "/images/links/print.gif' title='" . _CO_PUBLISHER_PRINT . "' alt='" . _CO_PUBLISHER_PRINT . "' /></a>";
438
        $adminLinks .= " ";
439
        // Email button
440
        if ($xoops->isActiveModule('tellafriend')) {
441
            $subject = sprintf(_CO_PUBLISHER_INTITEMFOUND, $xoops->getConfig('sitename'));
442
            $subject = $this->_convert_for_japanese($subject);
443
            $maillink = PublisherUtils::tellafriend($subject);
444
            $adminLinks .= '<a href="' . $maillink . '"><img src="' . PUBLISHER_URL . '/images/links/friend.gif" title="' . _CO_PUBLISHER_MAIL . '" alt="' . _CO_PUBLISHER_MAIL . '" /></a>';
445
            $adminLinks .= " ";
446
        }
447
        return $adminLinks;
448
    }
449
450
    /**
451
     * @param array $notifications
452
     */
453
    public function sendNotifications($notifications = array())
454
    {
455
        $xoops = Xoops::getInstance();
456
        if ($xoops->isActiveModule('notifications')) {
457
458
            $notification_handler = Notifications::getInstance()->getHandlerNotification();
0 ignored issues
show
Bug introduced by
The method getHandlerNotification() does not exist on Xoops\Module\Helper\HelperAbstract. Did you maybe mean getHandler()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
459
            $tags = array();
460
            $tags['MODULE_NAME'] = $this->publisher->getModule()->getVar('name');
461
            $tags['ITEM_NAME'] = $this->title();
462
            $tags['CATEGORY_NAME'] = $this->getCategoryName();
463
            $tags['CATEGORY_URL'] = PUBLISHER_URL . '/category.php?categoryid=' . $this->getVar('categoryid');
464
            $tags['ITEM_BODY'] = $this->body();
465
            $tags['DATESUB'] = $this->datesub();
466
            foreach ($notifications as $notification) {
467
                switch ($notification) {
468
                    case _PUBLISHER_NOT_ITEM_PUBLISHED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
469
                        $tags['ITEM_URL'] = PUBLISHER_URL . '/item.php?itemid=' . $this->getVar('itemid');
470
                        $notification_handler->triggerEvent('global', 0, 'published', $tags, array(), $this->publisher->getModule()->getVar('mid'));
471
                        $notification_handler->triggerEvent('category', $this->getVar('categoryid'), 'published', $tags, array(), $this->publisher->getModule()->getVar('mid'));
472
                        $notification_handler->triggerEvent('item', $this->getVar('itemid'), 'approved', $tags, array(), $this->publisher->getModule()->getVar('mid'));
473
                        break;
474
                    case _PUBLISHER_NOT_ITEM_SUBMITTED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
475
                        $tags['WAITINGFILES_URL'] = PUBLISHER_URL . '/admin/item.php?itemid=' . $this->getVar('itemid');
476
                        $notification_handler->triggerEvent('global', 0, 'submitted', $tags, array(), $this->publisher->getModule()->getVar('mid'));
477
                        $notification_handler->triggerEvent('category', $this->getVar('categoryid'), 'submitted', $tags, array(), $this->publisher->getModule()->getVar('mid'));
478
                        break;
479
                    case _PUBLISHER_NOT_ITEM_REJECTED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
480
                        $notification_handler->triggerEvent('item', $this->getVar('itemid'), 'rejected', $tags, array(), $this->publisher->getModule()->getVar('mid'));
481
                        break;
482
                    case -1 :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
483
                    default:
484
                        break;
485
                }
486
            }
487
        }
488
    }
489
490
    /**
491
     * @return bool
492
     */
493
    public function notLoaded()
494
    {
495
        return $this->getVar('itemid') == -1;
496
    }
497
498
    /**
499
     * @return string
500
     */
501
    public function getItemUrl()
502
    {
503
        return PublisherUtils::seoGenUrl('item', $this->getVar('itemid'), $this->getVar('short_url'));
504
    }
505
506
    /**
507
     * @param bool $class
508
     * @param int  $maxsize
509
     *
510
     * @return string
511
     */
512
    public function getItemLink($class = false, $maxsize = 0)
513
    {
514
        if ($class) {
515
            return '<a class=' . $class . ' href="' . $this->getItemUrl() . '">' . $this->title($maxsize) . '</a>';
516
        } else {
517
            return '<a href="' . $this->getItemUrl() . '">' . $this->title($maxsize) . '</a>';
518
        }
519
    }
520
521
    /**
522
     * @return string
523
     */
524
    public function getWhoAndWhen()
525
    {
526
        $posterName = $this->linkedPosterName();
527
        $postdate = $this->datesub();
528
        return sprintf(_CO_PUBLISHER_POSTEDBY, $posterName, $postdate);
529
    }
530
531
    /**
532
     * @param null|string $body
533
     *
534
     * @return string
535
     */
536
    public function plain_maintext($body = null)
537
    {
538
        $ret = '';
539
        if (!$body) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $body of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
540
            $body = $this->body();
541
        }
542
        $ret .= str_replace('[pagebreak]', '<br /><br />', $body);
543
        return $ret;
544
    }
545
546
    /**
547
     * @param int         $item_page_id
548
     * @param null|string $body
549
     *
550
     * @return string
551
     */
552
    public function buildmaintext($item_page_id = -1, $body = null)
553
    {
554
        if (!$body) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $body of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
555
            $body = $this->body();
556
        }
557
        $body_parts = explode('[pagebreak]', $body);
558
        $this->setVar('pagescount', count($body_parts));
559
        if (count($body_parts) <= 1) {
560
            return $this->plain_maintext($body);
561
        }
562
        $ret = '';
563
        if ($item_page_id == -1) {
564
            $ret .= trim($body_parts[0]);
565
            return $ret;
566
        }
567
        if ($item_page_id >= count($body_parts)) {
568
            $item_page_id = count($body_parts) - 1;
569
        }
570
        $ret .= trim($body_parts[$item_page_id]);
571
        return $ret;
572
    }
573
574
    /**
575
     * @return mixed
576
     */
577
    public function getImages()
578
    {
579
        static $ret;
580
581
        $xoops = Xoops::getInstance();
582
        if (!$xoops->isActiveModule('images')) {
583
            return array();
584
        }
585
        $itemid = $this->getVar('itemid');
586
        if (!isset($ret[$itemid])) {
587
            $ret[$itemid]['main'] = '';
588
            $ret[$itemid]['others'] = array();
589
            $images_ids = array();
590
            $image = $this->getVar('image');
591
            $images = $this->getVar('images');
592
            if ($images != '') {
593
                $images_ids = explode('|', $images);
594
            }
595
            if ($image > 0) {
596
                $images_ids = array_merge($images_ids, array($image));
597
            }
598
            $imageObjs = array();
599
            if (count($images_ids) > 0) {
600
                $image_handler = Images::getInstance()->getHandlerImages();
601
                $criteria = new CriteriaCompo(new Criteria('image_id', '(' . implode(',', $images_ids) . ')', 'IN'));
602
                $imageObjs = $image_handler->getObjects($criteria, true);
0 ignored issues
show
Bug introduced by
The method getObjects does only exist in XoopsPersistableObjectHandler, but not in XoopsObjectHandler.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
603
                unset($criteria);
604
            }
605
            foreach ($imageObjs as $id => $imageObj) {
606
                if ($id == $image) {
607
                    $ret[$itemid]['main'] = $imageObj;
608
                } else {
609
                    $ret[$itemid]['others'][] = $imageObj;
610
                }
611
                unset($imageObj);
612
            }
613
            unset($imageObjs);
614
        }
615
        return $ret[$itemid];
616
    }
617
618
    /**
619
     * @param string $display
620
     * @param int    $max_char_title
621
     * @param int    $max_char_summary
622
     * @param bool   $full_summary
623
     *
624
     * @return array
625
     */
626
    public function toArray($display = 'default', $max_char_title = 0, $max_char_summary = 0, $full_summary = false)
0 ignored issues
show
Unused Code introduced by
The parameter $full_summary is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
toArray uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
627
    {
628
        $item_page_id = -1;
629
        if (is_numeric($display)) {
630
            $item_page_id = $display;
631
            $display = 'all';
632
        }
633
        $item['itemid'] = $this->getVar('itemid');
0 ignored issues
show
Coding Style Comprehensibility introduced by
$item was never initialized. Although not strictly required by PHP, it is generally a good practice to add $item = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
634
        $item['uid'] = $this->getVar('uid');
635
        $item['titlelink'] = $this->getItemLink(false, $max_char_title);
636
        $item['subtitle'] = $this->subtitle();
637
        $item['datesub'] = $this->datesub();
638
        $item['counter'] = $this->getVar('counter');
639
        switch ($display) {
640
            case 'summary':
641
            case 'list':
642
                break;
643
            case 'full':
644
            case 'wfsection':
645
            case 'default':
646
                $summary = $this->summary($max_char_summary);
647
                if (!$summary) {
648
                    $summary = $this->body($max_char_summary);
649
                }
650
                $item['summary'] = $summary;
651
                $item = $this->toArrayFull($item);
652
                break;
653
            case 'all':
654
                $item = $this->toArrayFull($item);
655
                $item = $this->toArrayAll($item, $item_page_id);
656
                break;
657
        }
658
        // Highlighting searched words
659
        $highlight = true;
660
        if ($highlight && isset($_GET['keywords'])) {
661
            $myts = \Xoops\Core\Text\Sanitizer::getInstance();
662
            $keywords = $myts->htmlSpecialChars(trim(urldecode($_GET['keywords'])));
663
            $fields = array('title', 'maintext', 'summary');
664
            foreach ($fields as $field) {
665
                if (isset($item[$field])) {
666
                    $item[$field] = $this->highlight($item[$field], $keywords);
667
                }
668
            }
669
        }
670
        return $item;
671
    }
672
673
    /**
674
     * @param array $item
675
     *
676
     * @return array
677
     */
678
    public function toArrayFull($item)
679
    {
680
        $item['title'] = $this->title();
681
        $item['clean_title'] = $this->title();
682
        $item['itemurl'] = $this->getItemUrl();
683
        $item['cancomment'] = $this->getVar('cancomment');
684
        $item['comments'] = $this->getVar('comments');
685
        $item['adminlink'] = $this->getAdminLinks();
686
        $item['categoryPath'] = $this->getCategoryPath($this->publisher->getConfig('format_linked_path'));
687
        $item['who_when'] = $this->getWhoAndWhen();
688
        $item = $this->getMainImage($item);
689
        return $item;
690
    }
691
692
    /**
693
     * @param array $item
694
     * @param int   $item_page_id
695
     *
696
     * @return array
697
     */
698
    public function toArrayAll($item, $item_page_id)
699
    {
700
        $item['maintext'] = $this->buildmaintext($item_page_id, $this->body());
701
        $item = $this->getOtherImages($item);
702
        return $item;
703
    }
704
705
    /**
706
     * @param array $item
707
     *
708
     * @return array
709
     */
710
    public function getMainImage($item = array())
711
    {
712
        $images = $this->getImages();
713
        $item['image_path'] = '';
714
        $item['image_name'] = '';
715
        if (is_object($images['main'])) {
716
            /* @var $image ImagesImage */
717
            $image = $images['main'];
718
            $dimensions = getimagesize(\XoopsBaseConfig::get('root-path') . '/uploads/' . $image->getVar('image_name'));
719
            $item['image_width'] = $dimensions[0];
720
            $item['image_height'] = $dimensions[1];
721
            $item['image_path'] = \XoopsBaseConfig::get('url') . '/uploads/' . $image->getVar('image_name');
722
            // pass this on since some consumers build custom thumbnails
723
            $item['image_vpath'] = 'uploads/' . $image->getVar('image_name');
724
            $item['image_thumb'] = \Xoops::getInstance()
725
                ->service('thumbnail')
726
                ->getImgUrl($item['image_vpath'], 0, 180)
727
                ->getValue();
728
            $item['image_name'] = $image->getVar('image_nicename');
729
        }
730
        return $item;
731
    }
732
733
    /**
734
     * @param array $item
735
     *
736
     * @return array
737
     */
738
    public function getOtherImages($item = array())
739
    {
740
        $thumbService = \Xoops::getInstance()->service('thumbnail');
741
        $images = $this->getImages();
742
        $item['images'] = array();
743
        $i = 0;
744
        /* @var $image ImagesImage */
745
        foreach ($images['others'] as $image) {
746
            $dimensions = getimagesize(\XoopsBaseConfig::get('root-path') . '/uploads/' . $image->getVar('image_name'));
747
            $item['images'][$i]['width'] = $dimensions[0];
748
            $item['images'][$i]['height'] = $dimensions[1];
749
            $item['images'][$i]['path'] = \XoopsBaseConfig::get('url') . '/uploads/' . $image->getVar('image_name');
750
            $item['images'][$i]['thumb'] = $thumbService
751
                ->getImgUrl('uploads/' . $image->getVar('image_name'), 240, 0)
752
                ->getValue();
753
            $item['images'][$i]['name'] = $image->getVar('image_nicename');
754
            ++$i;
755
        }
756
        return $item;
757
    }
758
759
    /**
760
     * @param string       $content
761
     * @param string|array $keywords
762
     *
763
     * @return string Text
764
     */
765
    public function highlight($content, $keywords)
766
    {
767
        $color = $this->publisher->getConfig('format_highlight_color');
768
        if (substr($color, 0, 1) !== '#') {
769
            $color = '#' . $color;
770
        }
771
        $pre = '<span style="font-weight: bolder; background-color: ' . $color . ';">';
772
        $post = '</span>';
773
        return \Xmf\Highlighter::apply($keywords, $content, $pre, $post);
0 ignored issues
show
Bug introduced by
It seems like $keywords defined by parameter $keywords on line 765 can also be of type array; however, Xmf\Highlighter::apply() does only seem to accept string|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
774
    }
775
776
    /**
777
     *  Create metada and assign it to template
778
     */
779
    public function createMetaTags()
780
    {
781
        $publisher_metagen = new PublisherMetagen($this->title(), $this->getVar('meta_keywords'), $this->getVar('meta_description'), $this->_category->_categoryPath);
782
        $publisher_metagen->createMetaTags();
783
    }
784
785
    /**
786
     * @param string $str
787
     *
788
     * @return string
789
     */
790
    public function _convert_for_japanese($str)
0 ignored issues
show
Coding Style introduced by
_convert_for_japanese uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
791
    {
792
        global $xoopsConfig;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
793
        // no action, if not flag
794
        if (!defined('_PUBLISHER_FLAG_JP_CONVERT')) {
795
            return $str;
796
        }
797
        // no action, if not Japanese
798
        if ($xoopsConfig['language'] !== 'japanese') {
799
            return $str;
800
        }
801
        // presume OS Browser
802
        $agent = $_SERVER["HTTP_USER_AGENT"];
803
        $os = '';
804
        $browser = '';
805
        if (preg_match("/Win/i", $agent)) {
806
            $os = 'win';
807
        }
808
        if (preg_match("/MSIE/i", $agent)) {
809
            $browser = 'msie';
810
        }
811
        // if msie
812
        if (($os === 'win') && ($browser === 'msie')) {
813
            // if multibyte
814
            if (function_exists('mb_convert_encoding')) {
815
                $str = mb_convert_encoding($str, 'SJIS', 'EUC-JP');
816
                $str = rawurlencode($str);
817
            }
818
        }
819
        return $str;
820
    }
821
822
    /**
823
     * Checks if a user has access to a selected item. if no item permissions are
824
     * set, access permission is denied. The user needs to have necessary category
825
     * permission as well.
826
     * Also, the item needs to be Published
827
     *
828
     * @return boolean : TRUE if the no errors occured
829
     */
830
    public function accessGranted()
831
    {
832
        if (PublisherUtils::IsUserAdmin()) {
833
            return true;
834
        }
835
        if ($this->getVar('status') != _PUBLISHER_STATUS_PUBLISHED) {
836
            return false;
837
        }
838
        // Do we have access to the parent category
839
        if ($this->publisher->getPermissionHandler()->isGranted('category_read', $this->getVar('categoryid'))) {
840
            return true;
841
        }
842
        return false;
843
    }
844
845
    /**
846
     * The name says it all
847
     */
848
    public function setVarsFromRequest()
0 ignored issues
show
Coding Style introduced by
setVarsFromRequest uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
849
    {
850
        $xoops = Xoops::getInstance();
851
        //Required fields
852
        if (isset($_REQUEST['categoryid'])) {
853
            $this->setVar('categoryid', Request::getInt('categoryid'));
854
        }
855
        if (isset($_REQUEST['title'])) {
856
            $this->setVar('title', Request::getString('title'));
857
        }
858
        if (isset($_REQUEST['body'])) {
859
            $this->setVar('body', Request::getText('body'));
860
        }
861
        //Not required fields
862
        if (isset($_REQUEST['summary'])) {
863
            $this->setVar('summary', Request::getText('summary'));
864
        }
865
        if (isset($_REQUEST['subtitle'])) {
866
            $this->setVar('subtitle', Request::getString('subtitle'));
867
        }
868
        if (isset($_REQUEST['item_tag'])) {
869
            $this->setVar('item_tag', Request::getString('item_tag'));
870
        }
871
        if (isset($_REQUEST['image_featured'])) {
872
            $image_item = Request::getArray('image_item');
873
            $image_featured = Request::getString('image_featured');
874
            //Todo: get a better image class for xoops!
875
            //Image hack
876
            $image_item_ids = array();
877
878
            $qb = \Xoops::getInstance()->db()->createXoopsQueryBuilder();
879
            $qb ->select('i.image_id', 'i.image_name')
880
                ->fromPrefix('image', 'i')
881
                ->orderBy('i.image_id');
882
            $result = $qb->execute();
883
884
            while ($myrow = $result->fetch(\PDO::FETCH_ASSOC)) {
885
                $image_name = $myrow['image_name'];
886
                $id = $myrow['image_id'];
887
                if ($image_name == $image_featured) {
888
                    $this->setVar('image', $id);
889
                }
890
                if (in_array($image_name, $image_item)) {
891
                    $image_item_ids[] = $id;
892
                }
893
            }
894
            $this->setVar('images', implode('|', $image_item_ids));
895
        }
896 View Code Duplication
        if (isset($_REQUEST['uid'])) {
897
            $this->setVar('uid', Request::getInt('uid'));
898
        } elseif ($this->isNew()) {
899
            $this->setVar('uid', $xoops->isUser() ? $xoops->user->getVar('uid') : 0);
900
        }
901
        if (isset($_REQUEST['author_alias'])) {
902
            $this->setVar('author_alias', Request::getString('author_alias'));
903
            if ($this->getVar('author_alias') != '') {
904
                $this->setVar('uid', 0);
905
            }
906
        }
907
        if (isset($_REQUEST['datesub'])) {
908
            $this->setVar('datesub', strtotime($_REQUEST['datesub']['date']) + $_REQUEST['datesub']['time']);
909
        } elseif ($this->isNew()) {
910
            $this->setVar('datesub', time());
911
        }
912
        if (isset($_REQUEST['item_short_url'])) {
913
            $this->setVar('short_url', Request::getString('item_short_url'));
914
        }
915
        if (isset($_REQUEST['item_meta_keywords'])) {
916
            $this->setVar('meta_keywords', Request::getString('item_meta_keywords'));
917
        }
918
        if (isset($_REQUEST['item_meta_description'])) {
919
            $this->setVar('meta_description', Request::getString('item_meta_description'));
920
        }
921
        if (isset($_REQUEST['weight'])) {
922
            $this->setVar('weight', Request::getInt('weight'));
923
        }
924 View Code Duplication
        if (isset($_REQUEST['allowcomments'])) {
925
            $this->setVar('cancomment', Request::getInt('allowcomments'));
926
        } elseif ($this->isNew()) {
927
            $this->setVar('cancoment', $this->publisher->getConfig('submit_allowcomments'));
928
        }
929 View Code Duplication
        if (isset($_REQUEST['status'])) {
930
            $this->setVar('status', Request::getInt('status'));
931
        } elseif ($this->isNew()) {
932
            $this->setVar('status', $this->publisher->getConfig('submit_status'));
933
        }
934 View Code Duplication
        if (isset($_REQUEST['dohtml'])) {
935
            $this->setVar('dohtml', Request::getInt('dohtml'));
936
        } elseif ($this->isNew()) {
937
            $this->setVar('dohtml', $this->publisher->getConfig('submit_dohtml'));
938
        }
939 View Code Duplication
        if (isset($_REQUEST['dosmiley'])) {
940
            $this->setVar('dosmiley', Request::getInt('dosmiley'));
941
        } elseif ($this->isNew()) {
942
            $this->setVar('dosmiley', $this->publisher->getConfig('submit_dosmiley'));
943
        }
944 View Code Duplication
        if (isset($_REQUEST['doxcode'])) {
945
            $this->setVar('doxcode', Request::getInt('doxcode'));
946
        } elseif ($this->isNew()) {
947
            $this->setVar('doxcode', $this->publisher->getConfig('submit_doxcode'));
948
        }
949 View Code Duplication
        if (isset($_REQUEST['doimage'])) {
950
            $this->setVar('doimage', Request::getInt('doimage'));
951
        } elseif ($this->isNew()) {
952
            $this->setVar('doimage', $this->publisher->getConfig('submit_doimage'));
953
        }
954 View Code Duplication
        if (isset($_REQUEST['dolinebreak'])) {
955
            $this->setVar('dobr', Request::getInt('dolinebreak'));
956
        } elseif ($this->isNew()) {
957
            $this->setVar('dobr', $this->publisher->getConfig('submit_dobr'));
958
        }
959
        if (isset($_REQUEST['notify'])) {
960
            $this->setVar('notifypub', Request::getInt('notify'));
961
        }
962
    }
963
}
964
965
/**
966
 * Items handler class.
967
 * This class is responsible for providing data access mechanisms to the data source
968
 * of Q&A class objects.
969
 *
970
 * @author  marcan <[email protected]>
971
 * @package Publisher
972
 */
973
class PublisherItemHandler extends XoopsPersistableObjectHandler
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
974
{
975
    /**
976
     * @var Publisher
977
     * @access public
978
     */
979
    public $publisher = null;
980
981
    /**
982
     * @param Connection $db
983
     */
984
    public function __construct(Connection $db)
985
    {
986
        parent::__construct($db, "publisher_items", 'PublisherItem', "itemid", "title");
987
        $this->publisher = Publisher::getInstance();
988
    }
989
990
    /**
991
     * insert a new item in the database
992
     *
993
     * @param XoopsObject $item reference to the {@link PublisherItem} object
994
     * @param bool        $force
995
     *
996
     * @return bool FALSE if failed, TRUE if already present and unchanged or successful
997
     */
998
    public function insert(XoopsObject $item, $force = true)
999
    {
1000
        $xoops = Xoops::getInstance();
1001
        if (!$item->getVar('meta_keywords') || !$item->getVar('meta_description') || !$item->getVar('short_url')) {
1002
            $publisher_metagen = new PublisherMetagen($item->title(), $item->getVar('meta_keywords'), $item->getVar('summary'));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xoops\Core\Kernel\XoopsObject as the method title() does only exist in the following sub-classes of Xoops\Core\Kernel\XoopsObject: PublisherItem, XoopsBlock, Xoops\Core\Kernel\Handlers\XoopsBlock. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1003
            // Auto create meta tags if empty
1004
            if (!$item->getVar('meta_keywords')) {
1005
                $item->setVar('meta_keywords', $publisher_metagen->_keywords);
1006
            }
1007
            if (!$item->getVar('meta_description')) {
1008
                $item->setVar('meta_description', $publisher_metagen->_description);
1009
            }
1010
            // Auto create short_url if empty
1011 View Code Duplication
            if (!$item->getVar('short_url')) {
1012
                $item->setVar('short_url', $publisher_metagen->generateSeoTitle($item->getVar('title', 'n'), false));
1013
            }
1014
        }
1015
        if (!parent::insert($item, $force)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression parent::insert($item, $force) of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1016
            return false;
1017
        }
1018 View Code Duplication
        if ($xoops->isActiveModule('tag')) {
1019
            // Storing tags information
1020
            $tag_handler = $xoops->getModuleHandler('tag', 'tag');
1021
            $tag_handler->updateByItem($item->getVar('item_tag'), $item->getVar('itemid'), PUBLISHER_DIRNAME, 0);
1022
        }
1023
        return true;
1024
    }
1025
1026
    /**
1027
     * delete an item from the database
1028
     *
1029
     * @param XoopsObject $item reference to the ITEM to delete
1030
     * @param bool        $force
1031
     *
1032
     * @return bool FALSE if failed.
1033
     */
1034
    public function delete(XoopsObject $item, $force = false)
1035
    {
1036
        $xoops = Xoops::getInstance();
1037
        // Deleting the files
1038
        if (!$this->publisher->getFileHandler()->deleteItemFiles($item)) {
1039
            $item->setErrors('An error while deleting a file.');
1040
        }
1041
        if (!parent::delete($item, $force)) {
1042
            $item->setErrors('An error while deleting.');
1043
            return false;
1044
        }
1045
        // Removing tags information
1046 View Code Duplication
        if ($xoops->isActiveModule('tag')) {
1047
            $tag_handler = $xoops->getModuleHandler('tag', 'tag');
1048
            $tag_handler->updateByItem('', $item->getVar('itemid'), PUBLISHER_DIRNAME, 0);
1049
        }
1050
        return true;
1051
    }
1052
1053
    /**
1054
     * retrieve items from the database
1055
     *
1056
     * @param object $criteria      {@link CriteriaElement} conditions to be met
1057
     * @param string $id_key        what shall we use as array key ? none, itemid, categoryid
1058
     * @param string $notNullFields fields that cannot be null or empty
1059
     *
1060
     * @return array array of {@link PublisherItem} objects
1061
     */
1062
    public function getItemObjects($criteria = null, $id_key = 'none', $notNullFields = '')
1063
    {
1064
        $ret = array();
1065
        $whereMode = '';
1066
1067
        $qb = $this->db2->createXoopsQueryBuilder();
1068
        $qb ->select('*')
1069
            ->fromPrefix('publisher_items', '');
1070
        if (isset($criteria) && is_subclass_of($criteria, 'Xoops\Core\Kernel\CriteriaElement')) {
1071
            $criteria->renderQb($qb, '');
1072
            $whereMode = 'AND';
1073
        }
1074
        $this->addNotNullFieldClause($qb, $notNullFields, $whereMode);
1075
        $theObjects = array();
1076
        $result = $qb->execute();
1077 View Code Duplication
        while ($myrow = $result->fetch(\PDO::FETCH_ASSOC)) {
1078
            $item = new PublisherItem();
1079
            $item->assignVars($myrow);
1080
            $theObjects[$myrow['itemid']] = $item;
1081
            unset($item);
1082
        }
1083
1084
        /* @var $theObject PublisherItem */
1085
        foreach ($theObjects as $theObject) {
1086
            if ($id_key === 'none') {
1087
                $ret[] = $theObject;
1088
            } elseif ($id_key === 'itemid') {
1089
                $ret[$theObject->getVar('itemid')] = $theObject;
1090
            } else {
1091
                $ret[$theObject->getVar($id_key)][$theObject->getVar('itemid')] = $theObject;
1092
            }
1093
            unset($theObject);
1094
        }
1095
        return $ret;
1096
    }
1097
1098
    /**
1099
     * count items matching a condition
1100
     *
1101
     * @param object $criteria {@link CriteriaElement} to match
1102
     * @param string $notNullFields fields that cannot be null or empty
1103
     *
1104
     * @return int count of items
1105
     */
1106
    public function getItemCount($criteria = null, $notNullFields = '')
1107
    {
1108
        $whereMode = '';
1109
1110
        $qb = $this->db2->createXoopsQueryBuilder();
1111
        $qb ->select('COUNT(*)')
1112
            ->fromPrefix('publisher_items', '');
1113
        if (isset($criteria) && is_subclass_of($criteria, 'Xoops\Core\Kernel\CriteriaElement')) {
1114
            $whereClause = $criteria->renderQb($qb, '');
0 ignored issues
show
Unused Code introduced by
$whereClause is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1115
            $whereMode = 'AND';
1116
        }
1117
        $this->addNotNullFieldClause($qb, $notNullFields, $whereMode);
1118
        $result = $qb->execute();
1119
1120
        if (!$result) {
1121
            return 0;
1122
        }
1123
        list($count) = $result->fetch(PDO::FETCH_NUM);
1124
        return $count;
1125
    }
1126
1127
    /**
1128
     * @param        $categoryid
1129
     * @param string $status
1130
     * @param string $notNullFields
1131
     *
1132
     * @return int
1133
     */
1134
    public function getItemsCount($categoryid = -1, $status = '', $notNullFields = '')
1135
    {
1136
        global $publisher_isAdmin;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1137 View Code Duplication
        if (!$publisher_isAdmin) {
1138
            $criteriaPermissions = new CriteriaCompo();
1139
            // Categories for which user has access
1140
            $categoriesGranted = $this->publisher->getPermissionHandler()->getGrantedItems('category_read');
1141
            if (!empty($categoriesGranted)) {
1142
                $grantedCategories = new Criteria('categoryid', "(" . implode(',', $categoriesGranted) . ")", 'IN');
1143
                $criteriaPermissions->add($grantedCategories, 'AND');
1144
            } else {
1145
                return 0;
1146
            }
1147
        }
1148
        if (isset($categoryid) && $categoryid != -1) {
1149
            $criteriaCategory = new criteria('categoryid', $categoryid);
1150
        }
1151
        $criteriaStatus = new CriteriaCompo();
1152
        if (!empty($status) && is_array($status)) {
1153
            foreach ($status as $v) {
1154
                $criteriaStatus->add(new Criteria('status', $v), 'OR');
1155
            }
1156 View Code Duplication
        } elseif (!empty($status) && $status != -1) {
1157
            $criteriaStatus->add(new Criteria('status', $status), 'OR');
1158
        }
1159
        $criteria = new CriteriaCompo();
1160
        if (!empty($criteriaCategory)) {
1161
            $criteria->add($criteriaCategory);
1162
        }
1163
        if (!empty($criteriaPermissions)) {
1164
            $criteria->add($criteriaPermissions);
1165
        }
1166
        if (!empty($criteriaStatus)) {
1167
            $criteria->add($criteriaStatus);
1168
        }
1169
        return $this->getItemCount($criteria, $notNullFields);
1170
    }
1171
1172
    /**
1173
     * @param int    $limit
1174
     * @param int    $start
1175
     * @param int    $categoryid
1176
     * @param string $sort
1177
     * @param string $order
1178
     * @param string $notNullFields
1179
     * @param bool   $asobject
1180
     * @param string $id_key
1181
     *
1182
     * @return array
1183
     */
1184
    public function getAllPublished($limit = 0, $start = 0, $categoryid = -1, $sort = 'datesub', $order = 'DESC', $notNullFields = '', $asobject = true, $id_key = 'none')
1185
    {
1186
        $otherCriteria = new Criteria('datesub', time(), '<=');
1187
        return $this->getItems($limit, $start, array(_PUBLISHER_STATUS_PUBLISHED), $categoryid, $sort, $order, $notNullFields, $asobject, $otherCriteria, $id_key);
1188
    }
1189
1190
    /**
1191
     * @param PublisherItem $obj
1192
     *
1193
     * @return bool
1194
     */
1195
    public function getPreviousPublished($obj)
1196
    {
1197
        $ret = false;
1198
        $otherCriteria = new CriteriaCompo();
1199
        $otherCriteria->add(new Criteria('datesub', $obj->getVar('datesub'), '<'));
1200
        $objs = $this->getItems(1, 0, array(_PUBLISHER_STATUS_PUBLISHED), $obj->getVar('categoryid'), 'datesub', 'DESC', '', true, $otherCriteria, 'none');
1201
        if (count($objs) > 0) {
1202
            $ret = $objs[0];
1203
        }
1204
        return $ret;
1205
    }
1206
1207
    /**
1208
     * @param PublisherItem $obj
1209
     *
1210
     * @return bool
1211
     */
1212
    public function getNextPublished($obj)
1213
    {
1214
        $ret = false;
1215
        $otherCriteria = new CriteriaCompo();
1216
        $otherCriteria->add(new Criteria('datesub', $obj->getVar('datesub'), '>'));
1217
        $otherCriteria->add(new Criteria('datesub', time(), '<='));
1218
        $objs = $this->getItems(1, 0, array(_PUBLISHER_STATUS_PUBLISHED), $obj->getVar('categoryid'), 'datesub', 'ASC', '', true, $otherCriteria, 'none');
1219
        if (count($objs) > 0) {
1220
            $ret = $objs[0];
1221
        }
1222
        return $ret;
1223
    }
1224
1225
    /**
1226
     * @param int    $limit
1227
     * @param int    $start
1228
     * @param int    $categoryid
1229
     * @param string $sort
1230
     * @param string $order
1231
     * @param string $notNullFields
1232
     * @param bool   $asobject
1233
     * @param string $id_key
1234
     *
1235
     * @return array
1236
     */
1237
    public function getAllSubmitted($limit = 0, $start = 0, $categoryid = -1, $sort = 'datesub', $order = 'DESC', $notNullFields = '', $asobject = true, $id_key = 'none')
1238
    {
1239
        return $this->getItems($limit, $start, array(_PUBLISHER_STATUS_SUBMITTED), $categoryid, $sort, $order, $notNullFields, $asobject, null, $id_key);
1240
    }
1241
1242
    /**
1243
     * @param int    $limit
1244
     * @param int    $start
1245
     * @param int    $categoryid
1246
     * @param string $sort
1247
     * @param string $order
1248
     * @param string $notNullFields
1249
     * @param bool   $asobject
1250
     * @param string $id_key
1251
     *
1252
     * @return array
1253
     */
1254
    public function getAllOffline($limit = 0, $start = 0, $categoryid = -1, $sort = 'datesub', $order = 'DESC', $notNullFields = '', $asobject = true, $id_key = 'none')
1255
    {
1256
        return $this->getItems($limit, $start, array(_PUBLISHER_STATUS_OFFLINE), $categoryid, $sort, $order, $notNullFields, $asobject, null, $id_key);
1257
    }
1258
1259
    /**
1260
     * @param int    $limit
1261
     * @param int    $start
1262
     * @param int    $categoryid
1263
     * @param string $sort
1264
     * @param string $order
1265
     * @param string $notNullFields
1266
     * @param bool   $asobject
1267
     * @param string $id_key
1268
     *
1269
     * @return array
1270
     */
1271
    public function getAllRejected($limit = 0, $start = 0, $categoryid = -1, $sort = 'datesub', $order = 'DESC', $notNullFields = '', $asobject = true, $id_key = 'none')
1272
    {
1273
        return $this->getItems($limit, $start, array(_PUBLISHER_STATUS_REJECTED), $categoryid, $sort, $order, $notNullFields, $asobject, null, $id_key);
1274
    }
1275
1276
    /**
1277
     * @param int    $limit
1278
     * @param int    $start
1279
     * @param string $status
1280
     * @param  int   $categoryid
1281
     * @param string $sort
1282
     * @param string $order
1283
     * @param string $notNullFields
1284
     * @param bool   $asobject
1285
     * @param null   $otherCriteria
1286
     * @param string $id_key
1287
     *
1288
     * @return array
1289
     */
1290
    public function getItems($limit = 0, $start = 0, $status = '', $categoryid = -1, $sort = 'datesub', $order = 'DESC', $notNullFields = '', $asobject = true, $otherCriteria = null, $id_key = 'none')
0 ignored issues
show
Unused Code introduced by
The parameter $asobject is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1291
    {
1292
        global $publisher_isAdmin;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1293 View Code Duplication
        if (!$publisher_isAdmin) {
1294
            $criteriaPermissions = new CriteriaCompo();
1295
            // Categories for which user has access
1296
            $categoriesGranted = $this->publisher->getPermissionHandler()->getGrantedItems('category_read');
1297
            if (!empty($categoriesGranted)) {
1298
                $grantedCategories = new Criteria('categoryid', "(" . implode(',', $categoriesGranted) . ")", 'IN');
1299
                $criteriaPermissions->add($grantedCategories, 'AND');
1300
            } else {
1301
                return array();
1302
            }
1303
        }
1304
        if (isset($categoryid) && ($categoryid != -1)) {
1305
            $criteriaCategory = new criteria('categoryid', $categoryid);
1306
        }
1307
        if (!empty($status) && is_array($status)) {
1308
            $criteriaStatus = new CriteriaCompo();
1309
            foreach ($status as $v) {
1310
                $criteriaStatus->add(new Criteria('status', $v), 'OR');
1311
            }
1312 View Code Duplication
        } elseif (!empty($status) && $status != -1) {
1313
            $criteriaStatus = new CriteriaCompo();
1314
            $criteriaStatus->add(new Criteria('status', $status), 'OR');
1315
        }
1316
        $criteria = new CriteriaCompo();
1317
        if (!empty($criteriaCategory)) {
1318
            $criteria->add($criteriaCategory);
1319
        }
1320
        if (!empty($criteriaPermissions)) {
1321
            $criteria->add($criteriaPermissions);
1322
        }
1323
        if (!empty($criteriaStatus)) {
1324
            $criteria->add($criteriaStatus);
1325
        }
1326
        if (!empty($otherCriteria)) {
1327
            $criteria->add($otherCriteria);
1328
        }
1329
        $criteria->setLimit($limit);
1330
        $criteria->setStart($start);
1331
        $criteria->setSort($sort);
1332
        $criteria->setOrder($order);
1333
        $ret = $this->getItemObjects($criteria, $id_key, $notNullFields);
1334
        return $ret;
1335
    }
1336
1337
    /**
1338
     * @param string $field
1339
     * @param string $status
1340
     * @param int    $categoryId
1341
     *
1342
     * @return bool
1343
     */
1344
    public function getRandomItem($field = '', $status = '', $categoryId = -1)
1345
    {
1346
        $ret = false;
1347
        $notNullFields = $field;
1348
        // Getting the number of published Items
1349
        $totalItems = $this->getItemsCount($categoryId, $status, $notNullFields);
1350
        if ($totalItems > 0) {
1351
            $totalItems = $totalItems - 1;
1352
            mt_srand((double)microtime() * 1000000);
1353
            $entrynumber = mt_rand(0, $totalItems);
1354
            $item = $this->getItems(1, $entrynumber, $status, $categoryId, $sort = 'datesub', $order = 'DESC', $notNullFields);
1355
            if ($item) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $item of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1356
                $ret = $item[0];
1357
            }
1358
        }
1359
        return $ret;
1360
    }
1361
1362
    /**
1363
     * @param $itemid
1364
     *
1365
     * @return bool
1366
     */
1367
    public function updateCounter($itemid)
1368
    {
1369
        $qb = $this->db2->createXoopsQueryBuilder();
1370
        $qb->updatePrefix('publisher_items', 'i')
1371
            ->set('i.counter', 'i.counter+1')
1372
            ->where('i.itemid = :itemid')
1373
            ->setParameter(':itemid', $itemid, \PDO::PARAM_INT);
1374
        $result = $qb->execute();
1375
        if ($result) {
1376
            return true;
1377
        } else {
1378
            return false;
1379
        }
1380
    }
1381
1382
    /**
1383
     * addNotNullFieldClause exclude rows where specified columns are empty or null
1384
     *
1385
     * @param QueryBuilder $qb            QueryBuilder instance
1386
     * @param string|array $notNullFields fields that should not be empty
1387
     * @param string       $whereMode     Initial where method, 'AND' andWhere(), otherwise where()
1388
     *
1389
     * @return QueryBuilder instance
1390
     */
1391
    protected function addNotNullFieldClause(\Xoops\Core\Database\QueryBuilder $qb, $notNullFields = array(), $whereMode = '')
1392
    {
1393
        $eb = $qb->expr();
1394
        if (!empty($notNullFields)) {
1395
            if (!is_array($notNullFields)) {
1396
                $notNullFields = (array) $notNullFields;
1397
            }
1398
            foreach ($notNullFields as $v) {
1399
                if ($whereMode === 'AND') {
1400
                    $qb ->andWhere($eb->isNotNull($v, ''))
0 ignored issues
show
Unused Code introduced by
The call to ExpressionBuilder::isNotNull() has too many arguments starting with ''.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1401
                        ->andWhere($eb->neq($v, "''"));
1402
                } else {
1403
                    $qb ->where($eb->isNotNull($v, ''))
0 ignored issues
show
Unused Code introduced by
The call to ExpressionBuilder::isNotNull() has too many arguments starting with ''.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1404
                        ->andWhere($eb->neq($v, "''"));
1405
                    $whereMode = 'AND';
1406
                }
1407
            }
1408
        }
1409
1410
        return $qb;
1411
    }
1412
1413
    /**
1414
     * @param array  $queryarray
1415
     * @param string $andor
1416
     * @param int    $limit
1417
     * @param int    $offset
1418
     * @param int    $userid
1419
     * @param array  $categories
1420
     * @param int    $sortby
1421
     * @param string $searchin
1422
     * @param string $extra
1423
     *
1424
     * @return array
1425
     */
1426
    public function getItemsFromSearch($queryarray = array(), $andor = 'AND', $limit = 0, $offset = 0, $userid = 0, $categories = array(), $sortby = 0, $searchin = "", $extra = "")
0 ignored issues
show
Unused Code introduced by
The parameter $extra is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1427
    {
1428
        $xoops = Xoops::getInstance();
1429
        $ret = array();
1430
        $gperm_handler = $xoops->getHandlerGroupPermission();
1431
        $groups = $xoops->getUserGroups();
1432
        $searchin = empty($searchin) ? array(
1433
            "title",
1434
            "body",
1435
            "summary"
1436
        ) : (is_array($searchin) ? $searchin : array($searchin));
1437
        if (in_array("all", $searchin) || count($searchin) == 0) {
1438
            $searchin = array("title", "subtitle", "body", "summary", "meta_keywords");
1439
        }
1440
        if (is_array($userid) && count($userid) > 0) {
1441
            $userid = array_map("intval", $userid);
1442
            $criteriaUser = new CriteriaCompo();
1443
            $criteriaUser->add(new Criteria('uid', '(' . implode(',', $userid) . ')', 'IN'), 'OR');
1444
        } elseif (is_numeric($userid) && $userid > 0) {
1445
            $criteriaUser = new CriteriaCompo();
1446
            $criteriaUser->add(new Criteria('uid', $userid), 'OR');
1447
        }
1448
        $count = count($queryarray);
1449
        if (is_array($queryarray) && $count > 0) {
1450
            $criteriaKeywords = new CriteriaCompo();
1451
            for ($i = 0; $i < count($queryarray); ++$i) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
1452
                $criteriaKeyword = new CriteriaCompo();
1453 View Code Duplication
                if (in_array('title', $searchin)) {
1454
                    $criteriaKeyword->add(new Criteria('title', '%' . $queryarray[$i] . '%', 'LIKE'), 'OR');
1455
                }
1456 View Code Duplication
                if (in_array('subtitle', $searchin)) {
1457
                    $criteriaKeyword->add(new Criteria('subtitle', '%' . $queryarray[$i] . '%', 'LIKE'), 'OR');
1458
                }
1459 View Code Duplication
                if (in_array('body', $searchin)) {
1460
                    $criteriaKeyword->add(new Criteria('body', '%' . $queryarray[$i] . '%', 'LIKE'), 'OR');
1461
                }
1462 View Code Duplication
                if (in_array('summary', $searchin)) {
1463
                    $criteriaKeyword->add(new Criteria('summary', '%' . $queryarray[$i] . '%', 'LIKE'), 'OR');
1464
                }
1465 View Code Duplication
                if (in_array('meta_keywords', $searchin)) {
1466
                    $criteriaKeyword->add(new Criteria('meta_keywords', '%' . $queryarray[$i] . '%', 'LIKE'), 'OR');
1467
                }
1468
                $criteriaKeywords->add($criteriaKeyword, $andor);
1469
                unset($criteriaKeyword);
1470
            }
1471
        }
1472
        if (!PublisherUtils::IsUserAdmin() && (count($categories) > 0)) {
1473
            $criteriaPermissions = new CriteriaCompo();
1474
            // Categories for which user has access
1475
            $categoriesGranted = $gperm_handler->getItemIds('category_read', $groups, $this->publisher->getModule()->getVar('mid'));
1476
            if (count($categories) > 0) {
1477
                $categoriesGranted = array_intersect($categoriesGranted, $categories);
1478
            }
1479
            if (count($categoriesGranted) == 0) {
1480
                return $ret;
1481
            }
1482
            $grantedCategories = new Criteria('categoryid', "(" . implode(',', $categoriesGranted) . ")", 'IN');
1483
            $criteriaPermissions->add($grantedCategories, 'AND');
1484
        } elseif (count($categories) > 0) {
1485
            $criteriaPermissions = new CriteriaCompo();
1486
            $grantedCategories = new Criteria('categoryid', "(" . implode(',', $categories) . ")", 'IN');
1487
            $criteriaPermissions->add($grantedCategories, 'AND');
1488
        }
1489
        $criteriaItemsStatus = new CriteriaCompo();
1490
        $criteriaItemsStatus->add(new Criteria('status', _PUBLISHER_STATUS_PUBLISHED));
1491
        $criteria = new CriteriaCompo();
1492
        if (!empty($criteriaUser)) {
1493
            $criteria->add($criteriaUser, 'AND');
1494
        }
1495
        if (!empty($criteriaKeywords)) {
1496
            $criteria->add($criteriaKeywords, 'AND');
1497
        }
1498
        if (!empty($criteriaPermissions)) {
1499
            $criteria->add($criteriaPermissions);
1500
        }
1501
        if (!empty($criteriaItemsStatus)) {
1502
            $criteria->add($criteriaItemsStatus, 'AND');
1503
        }
1504
        $criteria->setLimit($limit);
1505
        $criteria->setStart($offset);
1506
        if (empty($sortby)) {
1507
            $sortby = "datesub";
1508
        }
1509
        $criteria->setSort($sortby);
1510
        $order = 'ASC';
1511
        if ($sortby === "datesub") {
1512
            $order = 'DESC';
1513
        }
1514
        $criteria->setOrder($order);
1515
        $ret = $this->getItemObjects($criteria);
1516
        return $ret;
1517
    }
1518
1519
    /**
1520
     * @param array  $categoriesObj
1521
     * @param array  $status
1522
     *
1523
     * @return array
1524
     */
1525
    public function getLastPublishedByCat($categoriesObj, $status = array(_PUBLISHER_STATUS_PUBLISHED))
1526
    {
1527
        $ret = array();
1528
        $catIds = array();
1529
        /* @var $category PublisherCategory */
1530
        foreach ($categoriesObj as $parentid) {
1531
            foreach ($parentid as $category) {
1532
                $catId = $category->getVar('categoryid');
1533
                $catIds[$catId] = $catId;
1534
            }
1535
        }
1536
        if (empty($catIds)) {
1537
            return $ret;
1538
        }
1539
1540
        // $sql = "SELECT mi.categoryid, mi.itemid, mi.title, mi.short_url, mi.uid, mi.datesub";
1541
        // $sql .= " FROM (SELECT categoryid, MAX(datesub) AS date FROM " . $this->db->prefix('publisher_items');
1542
        // $sql .= " WHERE status IN (" . implode(',', $status) . ")";
1543
        // $sql .= " AND categoryid IN (" . implode(',', $catIds) . ")";
1544
        // $sql .= " GROUP BY categoryid)mo";
1545
        // $sql .= " JOIN " . $this->db->prefix('publisher_items') . " mi ON mi.datesub = mo.date";
1546
1547
        $qb = $this->db2->createXoopsQueryBuilder();
1548
        $qb->select('mi.categoryid', 'mi.itemid', 'mi.title', 'mi.short_url', 'mi.uid', 'mi.datesub');
1549
1550
        $subqb = $this->db2->createXoopsQueryBuilder();
1551
        $subqb->select('categoryid', 'MAX(datesub) AS date')
1552
            ->fromPrefix('publisher_items', '')
1553
            ->where($subqb->expr()->in('status', $status))
1554
            ->andWhere($subqb->expr()->in('categoryid', $catIds))
1555
            ->groupBy('categoryid');
1556
        $subquery = '('.$subqb->getSQL().')';
1557
1558
        $qb ->from($subquery, 'mo')
1559
            ->joinPrefix('mo', 'publisher_items', 'mi', 'mi.datesub = mo.date');
1560
1561
        $result = $qb->execute();
1562 View Code Duplication
        while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
1563
            $item = new PublisherItem();
1564
            $item->assignVars($row);
1565
            $ret[$row['categoryid']] = $item;
1566
            unset($item);
1567
        }
1568
        return $ret;
1569
    }
1570
1571
    /**
1572
     * @param int         $parentid
1573
     * @param array       $catsCount
1574
     * @param string      $spaces
1575
     * @param array       $resultCatCounts
1576
     *
1577
     * @return int
1578
     */
1579
    public function countArticlesByCat($parentid, &$catsCount, $spaces = '', $resultCatCounts = array())
1580
    {
1581
        $newspaces = $spaces . '--';
1582
        $thecount = 0;
1583
        foreach ($catsCount[$parentid] as $subCatId => $count) {
1584
            $thecount = $thecount + $count;
1585
            $resultCatCounts[$subCatId] = $count;
1586
            if (isset($catsCount[$subCatId])) {
1587
                $thecount = $thecount + $this->countArticlesByCat($subCatId, $catsCount, $newspaces, $resultCatCounts);
1588
                $resultCatCounts[$subCatId] = $thecount;
1589
            }
1590
        }
1591
        return $thecount;
1592
    }
1593
1594
    /**
1595
     * @param int     $cat_id
1596
     * @param array   $status
1597
     * @param bool    $inSubCat
1598
     *
1599
     * @return array
1600
     */
1601
    public function getCountsByCat($cat_id = 0, $status, $inSubCat = false)
1602
    {
1603
        $ret = array();
1604
        $catsCount = array();
1605
1606
        $qb = $this->db2->createXoopsQueryBuilder();
1607
        $qb ->select('c.parentid', 'i.categoryid', 'COUNT(*) AS count')
1608
            ->fromPrefix('publisher_items', 'i')
1609
            ->innerJoinPrefix('i', 'publisher_categories', 'c', 'i.categoryid=c.categoryid')
1610
            ->where($qb->expr()->in('i.status', $status))
1611
            ->groupBy('i.categoryid')
1612
            ->orderBy('c.parentid', 'ASC')
1613
            ->addOrderBy('i.categoryid', 'ASC');
1614
        if ((int)($cat_id) > 0) {
1615
            $qb ->andWhere($qb->expr()->eq('i.categoryid', ':catid'))
1616
                ->setParameter(':catid', $cat_id, \PDO::PARAM_INT);
1617
        }
1618
1619
        //$sql = 'SELECT c.parentid, i.categoryid, COUNT(*) AS count FROM ' . $this->db->prefix('publisher_items')
1620
        //. ' AS i INNER JOIN ' . $this->db->prefix('publisher_categories') . ' AS c ON i.categoryid=c.categoryid';
1621
        //if ((int)($cat_id) > 0) {
1622
        //    $sql .= ' WHERE i.categoryid = ' . (int)($cat_id);
1623
        //    $sql .= ' AND i.status IN (' . implode(',', $status) . ')';
1624
        //} else {
1625
        //    $sql .= ' WHERE i.status IN (' . implode(',', $status) . ')';
1626
        //}
1627
        //$sql .= ' GROUP BY i.categoryid ORDER BY c.parentid ASC, i.categoryid ASC';
1628
1629
        $result = $qb->execute();
1630
1631
        if (!$result) {
1632
            return $ret;
1633
        }
1634
        if (!$inSubCat) {
1635 View Code Duplication
            while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
1636
                $catsCount[$row['categoryid']] = $row['count'];
1637
            }
1638
            return $catsCount;
1639
        }
1640 View Code Duplication
        while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
1641
            $catsCount[$row['parentid']][$row['categoryid']] = $row['count'];
1642
        }
1643
        $resultCatCounts = array();
1644
        foreach ($catsCount[0] as $subCatId => $count) {
1645
            $resultCatCounts[$subCatId] = $count;
1646
            if (isset($catsCount[$subCatId])) {
1647
                $resultCatCounts[$subCatId] = $resultCatCounts[$subCatId] + $this->countArticlesByCat($subCatId, $catsCount, $spaces = '', $resultCatCounts);
1648
            }
1649
        }
1650
        return $resultCatCounts;
1651
    }
1652
}
1653