Passed
Branch master (7e303a)
by Michael
02:15
created

class/Utility.php (2 issues)

Labels
Severity
1
<?php
2
3
namespace XoopsModules\Publisher;
4
5
/*
6
 You may not change or alter any portion of this comment or credits
7
 of supporting developers from this source code or any supporting source code
8
 which is considered copyrighted (c) material of the original comment or credit authors.
9
10
 This program is distributed in the hope that it will be useful,
11
 but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 */
14
15
/**
16
 * PublisherUtil Class
17
 *
18
 * @copyright   XOOPS Project (https://xoops.org)
19
 * @license     http://www.fsf.org/copyleft/gpl.html GNU public license
20
 * @author      XOOPS Development Team
21
 * @package     Publisher
22
 * @since       1.03
23
 *
24
 */
25
26
use Xmf\Request;
27
use XoopsModules\Publisher;
28
use XoopsModules\Publisher\Common;
29
use XoopsModules\Publisher\Constants;
30
31
/**
32
 * Class Utility
33
 */
34
class Utility
35
{
36
    use Common\VersionChecks; //checkVerXoops, checkVerPhp Traits
37
38
    use Common\ServerStats; // getServerStats Trait
39
40
    use Common\FilesManagement; // Files Management Trait
41
42
    //--------------- Custom module methods -----------------------------
43
44
    /**
45
     * Function responsible for checking if a directory exists, we can also write in and create an index.html file
46
     *
47
     * @param string $folder The full path of the directory to check
48
     */
49
    public static function createFolder($folder)
50
    {
51
        try {
52
            if (!file_exists($folder)) {
53
                if (!is_dir($folder) && !mkdir($folder) && !is_dir($folder)) {
54
                    throw new \RuntimeException(sprintf('Unable to create the %s directory', $folder));
55
                }
56
                file_put_contents($folder . '/index.html', '<script>history.go(-1);</script>');
57
            }
58
        }
59
        catch (\Exception $e) {
60
            echo 'Caught exception: ', $e->getMessage(), "\n", '<br>';
61
        }
62
    }
63
64
    /**
65
     * @param $file
66
     * @param $folder
67
     * @return bool
68
     */
69
    public static function copyFile($file, $folder)
70
    {
71
        return copy($file, $folder);
72
        //        try {
73
        //            if (!is_dir($folder)) {
74
        //                throw new \RuntimeException(sprintf('Unable to copy file as: %s ', $folder));
75
        //            } else {
76
        //                return copy($file, $folder);
77
        //            }
78
        //        } catch (\Exception $e) {
79
        //            echo 'Caught exception: ', $e->getMessage(), "\n", "<br>";
80
        //        }
81
        //        return false;
82
    }
83
84
    /**
85
     * @param $src
86
     * @param $dst
87
     */
88
    public static function recurseCopy($src, $dst)
89
    {
90
        $dir = opendir($src);
91
        //    @mkdir($dst);
92
        while (false !== ($file = readdir($dir))) {
93
            if (('.' !== $file) && ('..' !== $file)) {
94
                if (is_dir($src . '/' . $file)) {
95
                    self::recurseCopy($src . '/' . $file, $dst . '/' . $file);
96
                } else {
97
                    copy($src . '/' . $file, $dst . '/' . $file);
98
                }
99
            }
100
        }
101
        closedir($dir);
102
    }
103
104
    // auto create folders----------------------------------------
105
    //TODO rename this function? And exclude image folder?
106
    public static function createDir()
107
    {
108
        // auto crate folders
109
        //        $thePath = static::getUploadDir();
110
111
        if (static::getPathStatus('root', true) < 0) {
112
            $thePath = static::getUploadDir();
113
            $res     = static::mkdir($thePath);
114
            $msg     = $res ? _AM_PUBLISHER_DIRCREATED : _AM_PUBLISHER_DIRNOTCREATED;
115
        }
116
117
        if (static::getPathStatus('images', true) < 0) {
118
            $thePath = static::getImageDir();
119
            $res     = static::mkdir($thePath);
120
121
            if ($res) {
122
                $source = PUBLISHER_ROOT_PATH . '/assets/images/blank.png';
123
                $dest   = $thePath . 'blank.png';
124
                static::copyr($source, $dest);
125
            }
126
            $msg = $res ? _AM_PUBLISHER_DIRCREATED : _AM_PUBLISHER_DIRNOTCREATED;
127
        }
128
129
        if (static::getPathStatus('images/category', true) < 0) {
130
            $thePath = static::getImageDir('category');
131
            $res     = static::mkdir($thePath);
132
133
            if ($res) {
134
                $source = PUBLISHER_ROOT_PATH . '/assets/images/blank.png';
135
                $dest   = $thePath . 'blank.png';
136
                static::copyr($source, $dest);
137
            }
138
            $msg = $res ? _AM_PUBLISHER_DIRCREATED : _AM_PUBLISHER_DIRNOTCREATED;
139
        }
140
141
        if (static::getPathStatus('images/item', true) < 0) {
142
            $thePath = static::getImageDir('item');
143
            $res     = static::mkdir($thePath);
144
145
            if ($res) {
146
                $source = PUBLISHER_ROOT_PATH . '/assets/images/blank.png';
147
                $dest   = $thePath . 'blank.png';
148
                static::copyr($source, $dest);
149
            }
150
            $msg = $res ? _AM_PUBLISHER_DIRCREATED : _AM_PUBLISHER_DIRNOTCREATED;
151
        }
152
153
        if (static::getPathStatus('content', true) < 0) {
154
            $thePath = static::getUploadDir(true, 'content');
155
            $res     = static::mkdir($thePath);
156
            $msg     = $res ? _AM_PUBLISHER_DIRCREATED : _AM_PUBLISHER_DIRNOTCREATED;
157
        }
158
    }
159
160
    public static function buildTableItemTitleRow()
161
    {
162
        echo "<table width='100%' cellspacing='1' cellpadding='3' border='0' class='outer'>";
163
        echo '<tr>';
164
        echo "<th width='40px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_ITEMID . '</strong></td>';
165
        echo "<th width='100px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_ITEMCAT . '</strong></td>';
166
        echo "<th class='bg3' align='center'><strong>" . _AM_PUBLISHER_TITLE . '</strong></td>';
167
        echo "<th width='100px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_CREATED . '</strong></td>';
168
169
        echo "<th width='50px' class='bg3' align='center'><strong>" . _CO_PUBLISHER_WEIGHT . '</strong></td>';
170
        echo "<th width='50px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_HITS . '</strong></td>';
171
        echo "<th width='60px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_RATE . '</strong></td>';
172
        echo "<th width='50px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_VOTES . '</strong></td>';
173
        echo "<th width='60px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_COMMENTS_COUNT . '</strong></td>';
174
175
        echo "<th width='90px' class='bg3' align='center'><strong>" . _CO_PUBLISHER_STATUS . '</strong></td>';
176
        echo "<th width='90px' class='bg3' align='center'><strong>" . _AM_PUBLISHER_ACTION . '</strong></td>';
177
        echo '</tr>';
178
    }
179
180
    /**
181
     * @param Publisher\Category $categoryObj
182
     * @param int                $level
183
     */
184
    public static function displayCategory(Publisher\Category $categoryObj, $level = 0)
185
    {
186
        /** @var Publisher\Helper $helper */
187
        $helper = Publisher\Helper::getInstance();
188
189
        $description = $categoryObj->description();
190
        if (!XOOPS_USE_MULTIBYTES) {
191
            if (mb_strlen($description) >= 100) {
0 ignored issues
show
It seems like $description can also be of type array and array; however, parameter $str of mb_strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

191
            if (mb_strlen(/** @scrutinizer ignore-type */ $description) >= 100) {
Loading history...
192
                $description = mb_substr($description, 0, 100 - 1) . '...';
0 ignored issues
show
It seems like $description can also be of type array and array; however, parameter $str of mb_substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

192
                $description = mb_substr(/** @scrutinizer ignore-type */ $description, 0, 100 - 1) . '...';
Loading history...
193
            }
194
        }
195
        $modify = "<a href='category.php?op=mod&amp;categoryid=" . $categoryObj->categoryid() . '&amp;parentid=' . $categoryObj->parentid() . "'><img src='" . PUBLISHER_URL . "/assets/images/links/edit.gif' title='" . _AM_PUBLISHER_EDITCOL . "' alt='" . _AM_PUBLISHER_EDITCOL . "'></a>";
196
        $delete = "<a href='category.php?op=del&amp;categoryid=" . $categoryObj->categoryid() . "'><img src='" . PUBLISHER_URL . "/assets/images/links/delete.png' title='" . _AM_PUBLISHER_DELETECOL . "' alt='" . _AM_PUBLISHER_DELETECOL . "'></a>";
197
198
        $spaces = '';
199
        for ($j = 0; $j < $level; ++$j) {
200
            $spaces .= '&nbsp;&nbsp;&nbsp;';
201
        }
202
203
        echo '<tr>';
204
        echo "<td class='even' align='center'>" . $categoryObj->categoryid() . '</td>';
205
        echo "<td class='even' align='left'>" . $spaces . "<a href='" . PUBLISHER_URL . '/category.php?categoryid=' . $categoryObj->categoryid() . "'><img src='" . PUBLISHER_URL . "/assets/images/links/subcat.gif' alt=''>&nbsp;" . $categoryObj->name() . '</a></td>';
206
        echo "<td class='even' align='center'>" . $categoryObj->weight() . '</td>';
207
        echo "<td class='even' align='center'> $modify $delete </td>";
208
        echo '</tr>';
209
        $subCategoriesObj = $helper->getHandler('Category')->getCategories(0, 0, $categoryObj->categoryid());
210
        if (count($subCategoriesObj) > 0) {
211
            ++$level;
212
            foreach ($subCategoriesObj as $key => $thiscat) {
213
                self::displayCategory($thiscat, $level);
214
            }
215
            unset($key);
216
        }
217
        //        unset($categoryObj);
218
    }
219
220
    /**
221
     * @param bool $showmenu
222
     * @param int  $categoryId
223
     * @param int  $nbSubCats
224
     * @param null $categoryObj
225
     */
226
    public static function editCategory($showmenu = false, $categoryId = 0, $nbSubCats = 4, $categoryObj = null)
227
    {
228
        /** @var Publisher\Helper $helper */
229
        $helper = Publisher\Helper::getInstance();
230
231
        // if there is a parameter, and the id exists, retrieve data: we're editing a category
232
        /* @var  $categoryObj Publisher\Category */
233
        if (0 != $categoryId) {
234
            // Creating the category object for the selected category
235
            $categoryObj = $helper->getHandler('Category')->get($categoryId);
236
            if ($categoryObj->notLoaded()) {
237
                redirect_header('category.php', 1, _AM_PUBLISHER_NOCOLTOEDIT);
238
                //            exit();
239
            }
240
        } else {
241
            if (!$categoryObj) {
242
                $categoryObj = $helper->getHandler('Category')->create();
243
            }
244
        }
245
246
        if (0 != $categoryId) {
247
            echo "<br>\n";
248
            static::openCollapsableBar('edittable', 'edittableicon', _AM_PUBLISHER_EDITCOL, _AM_PUBLISHER_CATEGORY_EDIT_INFO);
249
        } else {
250
            static::openCollapsableBar('createtable', 'createtableicon', _AM_PUBLISHER_CATEGORY_CREATE, _AM_PUBLISHER_CATEGORY_CREATE_INFO);
251
        }
252
253
        $sform = $categoryObj->getForm($nbSubCats);
254
        $sform->display();
255
256
        if (!$categoryId) {
257
            static::closeCollapsableBar('createtable', 'createtableicon');
258
        } else {
259
            static::closeCollapsableBar('edittable', 'edittableicon');
260
        }
261
262
        //Added by fx2024
263
        if ($categoryId) {
264
            $selCat = $categoryId;
265
266
            static::openCollapsableBar('subcatstable', 'subcatsicon', _AM_PUBLISHER_SUBCAT_CAT, _AM_PUBLISHER_SUBCAT_CAT_DSC);
267
            // Get the total number of sub-categories
268
            $categoriesObj = $helper->getHandler('Category')->get($selCat);
269
            $totalsubs     = $helper->getHandler('Category')->getCategoriesCount($selCat);
270
            // creating the categories objects that are published
271
            $subcatsObj    = $helper->getHandler('Category')->getCategories(0, 0, $categoriesObj->categoryid());
272
            $totalSCOnPage = count($subcatsObj);
273
            echo "<table width='100%' cellspacing=1 cellpadding=3 border=0 class = outer>";
274
            echo '<tr>';
275
            echo "<td width='60' class='bg3' align='left'><strong>" . _AM_PUBLISHER_CATID . '</strong></td>';
276
            echo "<td width='20%' class='bg3' align='left'><strong>" . _AM_PUBLISHER_CATCOLNAME . '</strong></td>';
277
            echo "<td class='bg3' align='left'><strong>" . _AM_PUBLISHER_SUBDESCRIPT . '</strong></td>';
278
            echo "<td width='60' class='bg3' align='right'><strong>" . _AM_PUBLISHER_ACTION . '</strong></td>';
279
            echo '</tr>';
280
            if ($totalsubs > 0) {
281
                foreach ($subcatsObj as $subcat) {
282
                    $modify = "<a href='category.php?op=mod&amp;categoryid=" . $subcat->categoryid() . "'><img src='" . XOOPS_URL . '/modules/' . $helper->getModule()->dirname() . "/assets/images/links/edit.gif' title='" . _AM_PUBLISHER_MODIFY . "' alt='" . _AM_PUBLISHER_MODIFY . "'></a>";
283
                    $delete = "<a href='category.php?op=del&amp;categoryid=" . $subcat->categoryid() . "'><img src='" . XOOPS_URL . '/modules/' . $helper->getModule()->dirname() . "/assets/images/links/delete.png' title='" . _AM_PUBLISHER_DELETE . "' alt='" . _AM_PUBLISHER_DELETE . "'></a>";
284
                    echo '<tr>';
285
                    echo "<td class='head' align='left'>" . $subcat->categoryid() . '</td>';
286
                    echo "<td class='even' align='left'><a href='" . XOOPS_URL . '/modules/' . $helper->getModule()->dirname() . '/category.php?categoryid=' . $subcat->categoryid() . '&amp;parentid=' . $subcat->parentid() . "'>" . $subcat->name() . '</a></td>';
287
                    echo "<td class='even' align='left'>" . $subcat->description() . '</td>';
288
                    echo "<td class='even' align='right'> {$modify} {$delete} </td>";
289
                    echo '</tr>';
290
                }
291
                //                unset($subcat);
292
            } else {
293
                echo '<tr>';
294
                echo "<td class='head' align='center' colspan= '7'>" . _AM_PUBLISHER_NOSUBCAT . '</td>';
295
                echo '</tr>';
296
            }
297
            echo "</table>\n";
298
            echo "<br>\n";
299
            static::closeCollapsableBar('subcatstable', 'subcatsicon');
300
301
            static::openCollapsableBar('bottomtable', 'bottomtableicon', _AM_PUBLISHER_CAT_ITEMS, _AM_PUBLISHER_CAT_ITEMS_DSC);
302
            $startitem = Request::getInt('startitem');
303
            // Get the total number of published ITEMS
304
            $totalitems = $helper->getHandler('Item')->getItemsCount($selCat, [Constants::PUBLISHER_STATUS_PUBLISHED]);
305
            // creating the items objects that are published
306
            $itemsObj         = $helper->getHandler('Item')->getAllPublished($helper->getConfig('idxcat_perpage'), $startitem, $selCat);
307
            $totalitemsOnPage = count($itemsObj);
308
            $allcats          = $helper->getHandler('Category')->getObjects(null, true);
309
            echo "<table width='100%' cellspacing=1 cellpadding=3 border=0 class = outer>";
310
            echo '<tr>';
311
            echo "<td width='40' class='bg3' align='center'><strong>" . _AM_PUBLISHER_ITEMID . '</strong></td>';
312
            echo "<td width='20%' class='bg3' align='left'><strong>" . _AM_PUBLISHER_ITEMCOLNAME . '</strong></td>';
313
            echo "<td class='bg3' align='left'><strong>" . _AM_PUBLISHER_ITEMDESC . '</strong></td>';
314
            echo "<td width='90' class='bg3' align='center'><strong>" . _AM_PUBLISHER_CREATED . '</strong></td>';
315
            echo "<td width='60' class='bg3' align='center'><strong>" . _AM_PUBLISHER_ACTION . '</strong></td>';
316
            echo '</tr>';
317
            if ($totalitems > 0) {
318
                for ($i = 0; $i < $totalitemsOnPage; ++$i) {
319
                    $categoryObj = $allcats[$itemsObj[$i]->categoryid()];
320
                    $modify      = "<a href='item.php?op=mod&amp;itemid=" . $itemsObj[$i]->itemid() . "'><img src='" . XOOPS_URL . '/modules/' . $helper->getModule()->dirname() . "/assets/images/links/edit.gif' title='" . _AM_PUBLISHER_EDITITEM . "' alt='" . _AM_PUBLISHER_EDITITEM . "'></a>";
321
                    $delete      = "<a href='item.php?op=del&amp;itemid=" . $itemsObj[$i]->itemid() . "'><img src='" . XOOPS_URL . '/modules/' . $helper->getModule()->dirname() . "/assets/images/links/delete.png' title='" . _AM_PUBLISHER_DELETEITEM . "' alt='" . _AM_PUBLISHER_DELETEITEM . "'></a>";
322
                    echo '<tr>';
323
                    echo "<td class='head' align='center'>" . $itemsObj[$i]->itemid() . '</td>';
324
                    echo "<td class='even' align='left'>" . $categoryObj->name() . '</td>';
325
                    echo "<td class='even' align='left'>" . $itemsObj[$i]->getitemLink() . '</td>';
326
                    echo "<td class='even' align='center'>" . $itemsObj[$i]->getDatesub('s') . '</td>';
327
                    echo "<td class='even' align='center'> $modify $delete </td>";
328
                    echo '</tr>';
329
                }
330
            } else {
331
                $itemid = -1;
332
                echo '<tr>';
333
                echo "<td class='head' align='center' colspan= '7'>" . _AM_PUBLISHER_NOITEMS . '</td>';
334
                echo '</tr>';
335
            }
336
            echo "</table>\n";
337
            echo "<br>\n";
338
            $parentid         = Request::getInt('parentid', 0, 'GET');
339
            $pagenavExtraArgs = "op=mod&categoryid=$selCat&parentid=$parentid";
340
            xoops_load('XoopsPageNav');
341
            $pagenav = new \XoopsPageNav($totalitems, $helper->getConfig('idxcat_perpage'), $startitem, 'startitem', $pagenavExtraArgs);
342
            echo '<div style="text-align:right;">' . $pagenav->renderNav() . '</div>';
343
            echo "<input type='button' name='button' onclick=\"location='item.php?op=mod&categoryid=" . $selCat . "'\" value='" . _AM_PUBLISHER_CREATEITEM . "'>&nbsp;&nbsp;";
344
            echo '</div>';
345
        }
346
        //end of fx2024 code
347
    }
348
349
350
    //======================== FUNCTIONS =================================
351
352
    /**
353
     * Includes scripts in HTML header
354
     */
355
    public static function cpHeader()
356
    {
357
        xoops_cp_header();
358
359
        //cannot use xoTheme, some conflit with admin gui
360
        echo '<link type="text/css" href="' . XOOPS_URL . '/modules/system/css/ui/' . xoops_getModuleOption('jquery_theme', 'system') . '/ui.all.css" rel="stylesheet">
361
    <link type="text/css" href="' . PUBLISHER_URL . '/assets/css/publisher.css" rel="stylesheet">
362
    <script type="text/javascript" src="' . PUBLISHER_URL . '/assets/js/funcs.js"></script>
363
    <script type="text/javascript" src="' . PUBLISHER_URL . '/assets/js/cookies.js"></script>
364
    <script type="text/javascript" src="' . XOOPS_URL . '/browse.php?Frameworks/jquery/jquery.js"></script>
365
    <!-- <script type="text/javascript" src="' . XOOPS_URL . '/browse.php?Frameworks/jquery/jquery-migrate-1.2.1.js"></script> -->
366
    <script type="text/javascript" src="' . XOOPS_URL . '/browse.php?Frameworks/jquery/plugins/jquery.ui.js"></script>
367
    <script type="text/javascript" src="' . PUBLISHER_URL . '/assets/js/ajaxupload.3.9.js"></script>
368
    <script type="text/javascript" src="' . PUBLISHER_URL . '/assets/js/publisher.js"></script>
369
    ';
370
    }
371
372
    /**
373
     * Default sorting for a given order
374
     *
375
     * @param  string $sort
376
     * @return string
377
     */
378
    public static function getOrderBy($sort)
379
    {
380
        if ('datesub' === $sort) {
381
            return 'DESC';
382
        }
383
384
        if ('counter' === $sort) {
385
            return 'DESC';
386
        } elseif ('weight' === $sort) {
387
            return 'ASC';
388
        } elseif ('votes' === $sort) {
389
            return 'DESC';
390
        } elseif ('rating' === $sort) {
391
            return 'DESC';
392
        } elseif ('comments' === $sort) {
393
            return 'DESC';
394
        }
395
396
        return null;
397
    }
398
399
    /**
400
     * @credits Thanks to Mithandir
401
     * @param  string $str
402
     * @param  int    $start
403
     * @param  int    $length
404
     * @param  string $trimMarker
405
     * @return string
406
     */
407
    public static function mb_substr($str, $start, $length, $trimMarker = '...')
408
    {
409
        // if the string is empty, let's get out ;-)
410
        if ('' == $str) {
411
            return $str;
412
        }
413
414
        // reverse a string that is shortened with '' as trimmarker
415
        $reversedString = strrev(xoops_substr($str, $start, $length, ''));
416
417
        // find first space in reversed string
418
        $positionOfSpace = mb_strpos($reversedString, ' ', 0);
419
420
        // truncate the original string to a length of $length
421
        // minus the position of the last space
422
        // plus the length of the $trimMarker
423
        $truncatedString = xoops_substr($str, $start, $length - $positionOfSpace + mb_strlen($trimMarker), $trimMarker);
424
425
        return $truncatedString;
426
    }
427
428
    /**
429
     * @param  string $document
430
     * @return mixed
431
     */
432
    public static function html2text($document)
433
    {
434
        // PHP Manual:: function preg_replace
435
        // $document should contain an HTML document.
436
        // This will remove HTML tags, javascript sections
437
        // and white space. It will also convert some
438
        // common HTML entities to their text equivalent.
439
        // Credits : newbb2
440
        $search = [
441
            "'<script[^>]*?>.*?</script>'si", // Strip out javascript
442
            "'<img.*?>'si", // Strip out img tags
443
            "'<[\/\!]*?[^<>]*?>'si", // Strip out HTML tags
444
            "'([\r\n])[\s]+'", // Strip out white space
445
            "'&(quot|#34);'i", // Replace HTML entities
446
            "'&(amp|#38);'i",
447
            "'&(lt|#60);'i",
448
            "'&(gt|#62);'i",
449
            "'&(nbsp|#160);'i",
450
            "'&(iexcl|#161);'i",
451
            "'&(cent|#162);'i",
452
            "'&(pound|#163);'i",
453
            "'&(copy|#169);'i",
454
        ]; // evaluate as php
455
456
        $replace = [
457
            '',
458
            '',
459
            '',
460
            '\\1',
461
            '"',
462
            '&',
463
            '<',
464
            '>',
465
            ' ',
466
            chr(161),
467
            chr(162),
468
            chr(163),
469
            chr(169),
470
        ];
471
472
        $text = preg_replace($search, $replace, $document);
473
474
        preg_replace_callback('/&#(\d+);/', function ($matches) {
475
            return chr($matches[1]);
476
        }, $document);
477
478
        return $text;
479
        //<?php
480
    }
481
482
    /**
483
     * @return array
484
     */
485
    public static function getAllowedImagesTypes()
486
    {
487
        return ['jpg/jpeg', 'image/bmp', 'image/gif', 'image/jpeg', 'image/jpg', 'image/x-png', 'image/png', 'image/pjpeg'];
488
    }
489
490
    /**
491
     * @param  bool $withLink
492
     * @return string
493
     */
494
    public static function moduleHome($withLink = true)
495
    {
496
        /** @var Publisher\Helper $helper */
497
        $helper = Publisher\Helper::getInstance();
498
499
        if (!$helper->getConfig('format_breadcrumb_modname')) {
500
            return '';
501
        }
502
503
        if (!$withLink) {
504
            return $helper->getModule()->getVar('name');
505
        }
506
        return '<a href="' . PUBLISHER_URL . '/">' . $helper->getModule()->getVar('name') . '</a>';
507
    }
508
509
    /**
510
     * Copy a file, or a folder and its contents
511
     *
512
     * @author      Aidan Lister <[email protected]>
513
     * @version     1.0.0
514
     * @param  string $source The source
515
     * @param  string $dest   The destination
516
     * @return bool   Returns true on success, false on failure
517
     */
518
    public static function copyr($source, $dest)
519
    {
520
        // Simple copy for a file
521
        if (is_file($source)) {
522
            return copy($source, $dest);
523
        }
524
525
        // Make destination directory
526
        if (!is_dir($dest) && !mkdir($dest) && !is_dir($dest)) {
527
            throw new \RuntimeException(sprintf('Directory "%s" was not created', $dest));
528
        }
529
530
        // Loop through the folder
531
        $dir = dir($source);
532
        while (false !== $entry = $dir->read()) {
533
            // Skip pointers
534
            if ('.' === $entry || '..' === $entry) {
535
                continue;
536
            }
537
538
            // Deep copy directories
539
            if (("$source/$entry" !== $dest) && is_dir("$source/$entry")) {
540
                static::copyr("$source/$entry", "$dest/$entry");
541
            } else {
542
                copy("$source/$entry", "$dest/$entry");
543
            }
544
        }
545
546
        // Clean up
547
        $dir->close();
548
549
        return true;
550
    }
551
552
    /**
553
     * .* @credits Thanks to the NewBB2 Development Team
554
     * @param  string $item
555
     * @param  bool   $getStatus
556
     * @return bool|int|string
557
     */
558
    public static function &getPathStatus($item, $getStatus = false)
559
    {
560
        $path = '';
561
        if ('root' !== $item) {
562
            $path = $item;
563
        }
564
565
        $thePath = static::getUploadDir(true, $path);
566
567
        if (empty($thePath)) {
568
            return false;
569
        }
570
        if (is_writable($thePath)) {
571
            $pathCheckResult = 1;
572
            $pathStatus      = _AM_PUBLISHER_AVAILABLE;
573
        } elseif (!@is_dir($thePath)) {
574
            $pathCheckResult = -1;
575
            $pathStatus      = _AM_PUBLISHER_NOTAVAILABLE . " <a href='" . PUBLISHER_ADMIN_URL . "/index.php?op=createdir&amp;path={$item}'>" . _AM_PUBLISHER_CREATETHEDIR . '</a>';
576
        } else {
577
            $pathCheckResult = -2;
578
            $pathStatus      = _AM_PUBLISHER_NOTWRITABLE . " <a href='" . PUBLISHER_ADMIN_URL . "/index.php?op=setperm&amp;path={$item}'>" . _AM_PUBLISHER_SETMPERM . '</a>';
579
        }
580
        if (!$getStatus) {
581
            return $pathStatus;
582
        }
583
        return $pathCheckResult;
584
    }
585
586
    /**
587
     * @credits Thanks to the NewBB2 Development Team
588
     * @param  string $target
589
     * @return bool
590
     */
591
    public static function mkdir($target)
592
    {
593
        // http://www.php.net/manual/en/function.mkdir.php
594
        // saint at corenova.com
595
        // bart at cdasites dot com
596
        if (empty($target) || is_dir($target)) {
597
            return true; // best case check first
598
        }
599
600
        if (file_exists($target) && !is_dir($target)) {
601
            return false;
602
        }
603
604
        if (static::mkdir(substr($target, 0, strrpos($target, '/')))) {
605
            if (!file_exists($target)) {
606
                $res = mkdir($target, 0777); // crawl back up & create dir tree
607
                static::chmod($target);
608
609
                return $res;
610
            }
611
        }
612
        $res = is_dir($target);
613
614
        return $res;
615
    }
616
617
    /**
618
     * @credits Thanks to the NewBB2 Development Team
619
     * @param  string $target
620
     * @param  int    $mode
621
     * @return bool
622
     */
623
    public static function chmod($target, $mode = 0777)
624
    {
625
        return @chmod($target, $mode);
626
    }
627
628
    /**
629
     * @param  bool   $hasPath
630
     * @param  string $item
631
     * @return string
632
     */
633
    public static function getUploadDir($hasPath = true, $item = '')
634
    {
635
        if ('' !== $item) {
636
            if ('root' === $item) {
637
                $item = '';
638
            } else {
639
                $item .= '/';
640
            }
641
        }
642
643
        if ($hasPath) {
644
            return PUBLISHER_UPLOAD_PATH . '/' . $item;
645
        }
646
        return PUBLISHER_UPLOAD_URL . '/' . $item;
647
    }
648
649
    /**
650
     * @param  string $item
651
     * @param  bool   $hasPath
652
     * @return string
653
     */
654
    public static function getImageDir($item = '', $hasPath = true)
655
    {
656
        if ($item) {
657
            $item = "images/{$item}";
658
        } else {
659
            $item = 'images';
660
        }
661
662
        return static::getUploadDir($hasPath, $item);
663
    }
664
665
    /**
666
     * @param  array $errors
667
     * @return string
668
     */
669
    public static function formatErrors($errors = [])
670
    {
671
        $ret = '';
672
        foreach ($errors as $key => $value) {
673
            $ret .= '<br> - ' . $value;
674
        }
675
676
        return $ret;
677
    }
678
679
    /**
680
     * Checks if a user is admin of Publisher
681
     *
682
     * @return bool
683
     */
684
    public static function userIsAdmin()
685
    {
686
        /** @var Publisher\Helper $helper */
687
        $helper = Publisher\Helper::getInstance();
688
689
        static $publisherIsAdmin;
690
691
        if (null !== $publisherIsAdmin) {
692
            return $publisherIsAdmin;
693
        }
694
695
        if (!$GLOBALS['xoopsUser']) {
696
            $publisherIsAdmin = false;
697
        } else {
698
            $publisherIsAdmin = $GLOBALS['xoopsUser']->isAdmin($helper->getModule()->getVar('mid'));
699
        }
700
701
        return $publisherIsAdmin;
702
    }
703
704
    /**
705
     * Check is current user is author of a given article
706
     *
707
     * @param  \XoopsObject $itemObj
708
     * @return bool
709
     */
710
    public static function userIsAuthor($itemObj)
711
    {
712
        return (is_object($GLOBALS['xoopsUser']) && is_object($itemObj) && ($GLOBALS['xoopsUser']->uid() == $itemObj->uid()));
713
    }
714
715
    /**
716
     * Check is current user is moderator of a given article
717
     *
718
     * @param  \XoopsObject $itemObj
719
     * @return bool
720
     */
721
    public static function userIsModerator($itemObj)
722
    {
723
        /** @var Publisher\Helper $helper */
724
        $helper            = Publisher\Helper::getInstance();
725
        $categoriesGranted = $helper->getHandler('Permission')->getGrantedItems('category_moderation');
726
727
        return (is_object($itemObj) && in_array($itemObj->categoryid(), $categoriesGranted));
728
    }
729
730
    /**
731
     * Saves permissions for the selected category
732
     *
733
     * @param  null|array $groups     : group with granted permission
734
     * @param  int        $categoryId : categoryid on which we are setting permissions
735
     * @param  string     $permName   : name of the permission
736
     * @return bool : TRUE if the no errors occured
737
     */
738
    public static function saveCategoryPermissions($groups, $categoryId, $permName)
739
    {
740
        /** @var Publisher\Helper $helper */
741
        $helper = Publisher\Helper::getInstance();
742
743
        $result = true;
744
745
        $moduleId = $helper->getModule()->getVar('mid');
746
        /* @var  $grouppermHandler \XoopsGroupPermHandler */
747
        $grouppermHandler = xoops_getHandler('groupperm');
748
        // First, if the permissions are already there, delete them
749
        $grouppermHandler->deleteByModule($moduleId, $permName, $categoryId);
750
751
        // Save the new permissions
752
        if (count($groups) > 0) {
753
            foreach ($groups as $groupId) {
754
                $grouppermHandler->addRight($permName, $categoryId, $groupId, $moduleId);
755
            }
756
        }
757
758
        return $result;
759
    }
760
761
    /**
762
     * @param  string $tablename
763
     * @param  string $iconname
764
     * @param  string $tabletitle
765
     * @param  string $tabledsc
766
     * @param  bool   $open
767
     */
768
    public static function openCollapsableBar($tablename = '', $iconname = '', $tabletitle = '', $tabledsc = '', $open = true)
769
    {
770
        $image   = 'open12.gif';
771
        $display = 'none';
772
        if ($open) {
773
            $image   = 'close12.gif';
774
            $display = 'block';
775
        }
776
777
        echo "<h3 style=\"color: #2F5376; font-weight: bold; font-size: 14px; margin: 6px 0 0 0; \"><a href='javascript:;' onclick=\"toggle('" . $tablename . "'); toggleIcon('" . $iconname . "')\">";
778
        echo "<img id='" . $iconname . "' src='" . PUBLISHER_URL . '/assets/images/links/' . $image . "' alt=''></a>&nbsp;" . $tabletitle . '</h3>';
779
        echo "<div id='" . $tablename . "' style='display: " . $display . ";'>";
780
        if ('' != $tabledsc) {
781
            echo '<span style="color: #567; margin: 3px 0 12px 0; font-size: small; display: block; ">' . $tabledsc . '</span>';
782
        }
783
    }
784
785
    /**
786
     * @param  string $name
787
     * @param  string $icon
788
     */
789
    public static function closeCollapsableBar($name, $icon)
790
    {
791
        echo '</div>';
792
793
        $urls = static::getCurrentUrls();
794
        $path = $urls['phpself'];
795
796
        $cookieName = $path . '_publisher_collaps_' . $name;
797
        $cookieName = str_replace('.', '_', $cookieName);
798
        $cookie     = static::getCookieVar($cookieName, '');
799
800
        if ('none' === $cookie) {
801
            echo '
802
        <script type="text/javascript"><!--
803
        toggle("' . $name . '"); toggleIcon("' . $icon . '");
804
        //-->
805
        </script>
806
        ';
807
        }
808
    }
809
810
    /**
811
     * @param  string $name
812
     * @param  string $value
813
     * @param  int    $time
814
     */
815
    public static function setCookieVar($name, $value, $time = 0)
816
    {
817
        if (0 === $time) {
818
            $time = time() + 3600 * 24 * 365;
819
        }
820
        setcookie($name, $value, $time, '/');
821
    }
822
823
    /**
824
     * @param  string $name
825
     * @param  string $default
826
     * @return string
827
     */
828
    public static function getCookieVar($name, $default = '')
829
    {
830
        //    if (isset($_COOKIE[$name]) && ($_COOKIE[$name] > '')) {
831
        //        return $_COOKIE[$name];
832
        //    } else {
833
        //        return $default;
834
        //    }
835
        return Request::getString('name', $default, 'COOKIE');
836
    }
837
838
    /**
839
     * @return array
840
     */
841
    public static function getCurrentUrls()
842
    {
843
        $http = false === mb_strpos(XOOPS_URL, 'https://') ? 'http://' : 'https://';
844
        //    $phpself     = $_SERVER['PHP_SELF'];
845
        //    $httphost    = $_SERVER['HTTP_HOST'];
846
        //    $querystring = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
847
        $phpself     = Request::getString('PHP_SELF', '', 'SERVER');
848
        $httphost    = Request::getString('HTTP_HOST', '', 'SERVER');
849
        $querystring = Request::getString('QUERY_STRING', '', 'SERVER');
850
851
        if ('' != $querystring) {
852
            $querystring = '?' . $querystring;
853
        }
854
855
        $currenturl = $http . $httphost . $phpself . $querystring;
856
857
        $urls                = [];
858
        $urls['http']        = $http;
859
        $urls['httphost']    = $httphost;
860
        $urls['phpself']     = $phpself;
861
        $urls['querystring'] = $querystring;
862
        $urls['full']        = $currenturl;
863
864
        return $urls;
865
    }
866
867
    /**
868
     * @return string
869
     */
870
    public static function getCurrentPage()
871
    {
872
        $urls = static::getCurrentUrls();
873
874
        return $urls['full'];
875
    }
876
877
    /**
878
     * @param  null|Publisher\Category $categoryObj
879
     * @param  int|array               $selectedid
880
     * @param  int                     $level
881
     * @param  string                  $ret
882
     * @return string
883
     */
884
    public static function addCategoryOption(Publisher\Category $categoryObj, $selectedid = 0, $level = 0, $ret = '')
885
    {
886
        /** @var Publisher\Helper $helper */
887
        $helper = Publisher\Helper::getInstance();
888
889
        $spaces = '';
890
        for ($j = 0; $j < $level; ++$j) {
891
            $spaces .= '--';
892
        }
893
894
        $ret .= "<option value='" . $categoryObj->categoryid() . "'";
895
        if (is_array($selectedid) && in_array($categoryObj->categoryid(), $selectedid)) {
896
            $ret .= ' selected';
897
        } elseif ($categoryObj->categoryid() == $selectedid) {
898
            $ret .= ' selected';
899
        }
900
        $ret .= '>' . $spaces . $categoryObj->name() . "</option>\n";
901
902
        $subCategoriesObj = $helper->getHandler('Category')->getCategories(0, 0, $categoryObj->categoryid());
903
        if (count($subCategoriesObj) > 0) {
904
            ++$level;
905
            foreach ($subCategoriesObj as $catID => $subCategoryObj) {
906
                $ret .= static::addCategoryOption($subCategoryObj, $selectedid, $level);
907
            }
908
        }
909
910
        return $ret;
911
    }
912
913
    /**
914
     * @param  int|array $selectedid
915
     * @param  int       $parentcategory
916
     * @param  bool      $allCatOption
917
     * @param  string    $selectname
918
     * @return string
919
     */
920
    public static function createCategorySelect($selectedid = 0, $parentcategory = 0, $allCatOption = true, $selectname = 'options[0]')
921
    {
922
        /** @var Publisher\Helper $helper */
923
        $helper = Publisher\Helper::getInstance();
924
925
        $selectedid = explode(',', $selectedid);
926
927
        $ret = "<select name='" . $selectname . "[]' multiple='multiple' size='10'>";
928
        if ($allCatOption) {
929
            $ret .= "<option value='0'";
930
            if (in_array(0, $selectedid)) {
931
                $ret .= ' selected';
932
            }
933
            $ret .= '>' . _MB_PUBLISHER_ALLCAT . '</option>';
934
        }
935
936
        // Creating category objects
937
        $categoriesObj = $helper->getHandler('Category')->getCategories(0, 0, $parentcategory);
938
939
        if (count($categoriesObj) > 0) {
940
            foreach ($categoriesObj as $catID => $categoryObj) {
941
                $ret .= static::addCategoryOption($categoryObj, $selectedid);
942
            }
943
        }
944
        $ret .= '</select>';
945
946
        return $ret;
947
    }
948
949
    /**
950
     * @param  int  $selectedid
951
     * @param  int  $parentcategory
952
     * @param  bool $allCatOption
953
     * @return string
954
     */
955
    public static function createCategoryOptions($selectedid = 0, $parentcategory = 0, $allCatOption = true)
956
    {
957
        /** @var Publisher\Helper $helper */
958
        $helper = Publisher\Helper::getInstance();
959
960
        $ret = '';
961
        if ($allCatOption) {
962
            $ret .= "<option value='0'";
963
            $ret .= '>' . _MB_PUBLISHER_ALLCAT . "</option>\n";
964
        }
965
966
        // Creating category objects
967
        $categoriesObj = $helper->getHandler('Category')->getCategories(0, 0, $parentcategory);
968
        if (count($categoriesObj) > 0) {
969
            foreach ($categoriesObj as $catID => $categoryObj) {
970
                $ret .= static::addCategoryOption($categoryObj, $selectedid);
971
            }
972
        }
973
974
        return $ret;
975
    }
976
977
    /**
978
     * @param  array  $errArray
979
     * @param  string $reseturl
980
     */
981
    public static function renderErrors(&$errArray, $reseturl = '')
982
    {
983
        if (is_array($errArray) && count($errArray) > 0) {
984
            echo '<div id="readOnly" class="errorMsg" style="border:1px solid #D24D00; background:#FEFECC url(' . PUBLISHER_URL . '/assets/images/important-32.png) no-repeat 7px 50%;color:#333;padding-left:45px;">';
985
986
            echo '<h4 style="text-align:left;margin:0; padding-top:0;">' . _AM_PUBLISHER_MSG_SUBMISSION_ERR;
987
988
            if ($reseturl) {
989
                echo ' <a href="' . $reseturl . '">[' . _AM_PUBLISHER_TEXT_SESSION_RESET . ']</a>';
990
            }
991
992
            echo '</h4><ul>';
993
994
            foreach ($errArray as $key => $error) {
995
                if (is_array($error)) {
996
                    foreach ($error as $err) {
997
                        echo '<li><a href="#' . $key . '" onclick="var e = xoopsGetElementById(\'' . $key . '\'); e.focus();">' . htmlspecialchars($err, ENT_QUOTES | ENT_HTML5) . '</a></li>';
998
                    }
999
                } else {
1000
                    echo '<li><a href="#' . $key . '" onclick="var e = xoopsGetElementById(\'' . $key . '\'); e.focus();">' . htmlspecialchars($error, ENT_QUOTES | ENT_HTML5) . '</a></li>';
1001
                }
1002
            }
1003
            echo '</ul></div><br>';
1004
        }
1005
    }
1006
1007
    /**
1008
     * Generate publisher URL
1009
     *
1010
     * @param  string $page
1011
     * @param  array  $vars
1012
     * @param  bool   $encodeAmp
1013
     * @return string
1014
     *
1015
     * @credit : xHelp module, developped by 3Dev
1016
     */
1017
    public static function makeUri($page, $vars = [], $encodeAmp = true)
1018
    {
1019
        $joinStr = '';
1020
1021
        $amp = ($encodeAmp ? '&amp;' : '&');
1022
1023
        if (!count($vars)) {
1024
            return $page;
1025
        }
1026
1027
        $qs = '';
1028
        foreach ($vars as $key => $value) {
1029
            $qs      .= $joinStr . $key . '=' . $value;
1030
            $joinStr = $amp;
1031
        }
1032
1033
        return $page . '?' . $qs;
1034
    }
1035
1036
    /**
1037
     * @param  string $subject
1038
     * @return string
1039
     */
1040
    public static function tellAFriend($subject = '')
1041
    {
1042
        if (false !== mb_strpos($subject, '%')) {
1043
            $subject = rawurldecode($subject);
1044
        }
1045
1046
        $targetUri = XOOPS_URL . Request::getString('REQUEST_URI', '', 'SERVER');
1047
1048
        return XOOPS_URL . '/modules/tellafriend/index.php?target_uri=' . rawurlencode($targetUri) . '&amp;subject=' . rawurlencode($subject);
1049
    }
1050
1051
    /**
1052
     * @param  bool        $another
1053
     * @param  bool        $withRedirect
1054
     * @param              $itemObj
1055
     * @return bool|string
1056
     */
1057
    public static function uploadFile($another = false, $withRedirect = true, &$itemObj)
1058
    {
1059
        xoops_load('XoopsMediaUploader');
1060
        //        require_once PUBLISHER_ROOT_PATH . '/class/uploader.php';
1061
1062
        //    global $publisherIsAdmin;
1063
        /** @var Publisher\Helper $helper */
1064
        $helper = Publisher\Helper::getInstance();
1065
1066
        $itemId  = Request::getInt('itemid', 0, 'POST');
1067
        $uid     = is_object($GLOBALS['xoopsUser']) ? $GLOBALS['xoopsUser']->uid() : 0;
1068
        $session = Publisher\Session::getInstance();
1069
        $session->set('publisher_file_filename', Request::getString('item_file_name', '', 'POST'));
1070
        $session->set('publisher_file_description', Request::getString('item_file_description', '', 'POST'));
1071
        $session->set('publisher_file_status', Request::getInt('item_file_status', 1, 'POST'));
1072
        $session->set('publisher_file_uid', $uid);
1073
        $session->set('publisher_file_itemid', $itemId);
1074
1075
        if (!is_object($itemObj)) {
1076
            $itemObj = $helper->getHandler('Item')->get($itemId);
1077
        }
1078
1079
        $fileObj = $helper->getHandler('File')->create();
1080
        $fileObj->setVar('name', Request::getString('item_file_name', '', 'POST'));
1081
        $fileObj->setVar('description', Request::getString('item_file_description', '', 'POST'));
1082
        $fileObj->setVar('status', Request::getInt('item_file_status', 1, 'POST'));
1083
        $fileObj->setVar('uid', $uid);
1084
        $fileObj->setVar('itemid', $itemObj->getVar('itemid'));
1085
        $fileObj->setVar('datesub', time());
1086
1087
        // Get available mimetypes for file uploading
1088
        $allowedMimetypes = $helper->getHandler('Mimetype')->getArrayByType();
1089
        // TODO : display the available mimetypes to the user
1090
        $errors = [];
1091
        if ($helper->getConfig('perm_upload') && is_uploaded_file($_FILES['item_upload_file']['tmp_name'])) {
1092
            if (!$ret = $fileObj->checkUpload('item_upload_file', $allowedMimetypes, $errors)) {
1093
                $errorstxt = implode('<br>', $errors);
1094
1095
                $message = sprintf(_CO_PUBLISHER_MESSAGE_FILE_ERROR, $errorstxt);
1096
                if ($withRedirect) {
1097
                    redirect_header('file.php?op=mod&itemid=' . $itemId, 5, $message);
1098
                } else {
1099
                    return $message;
1100
                }
1101
            }
1102
        }
1103
1104
        // Storing the file
1105
        if (!$fileObj->store($allowedMimetypes)) {
1106
            //        if ($withRedirect) {
1107
            //            redirect_header("file.php?op=mod&itemid=" . $fileObj->itemid(), 3, _CO_PUBLISHER_FILEUPLOAD_ERROR . static::formatErrors($fileObj->getErrors()));
1108
            //            exit;
1109
            //        }
1110
            try {
1111
                if ($withRedirect) {
1112
                    throw new \RuntimeException(_CO_PUBLISHER_FILEUPLOAD_ERROR . static::formatErrors($fileObj->getErrors()));
1113
                }
1114
            }
1115
            catch (\Exception $e) {
1116
                $helper->addLog($e);
1117
                redirect_header('file.php?op=mod&itemid=' . $fileObj->itemid(), 3, _CO_PUBLISHER_FILEUPLOAD_ERROR . static::formatErrors($fileObj->getErrors()));
1118
            }
1119
            //    } else {
1120
            //        return _CO_PUBLISHER_FILEUPLOAD_ERROR . static::formatErrors($fileObj->getErrors());
1121
        }
1122
1123
        if ($withRedirect) {
1124
            $redirectPage = $another ? 'file.php' : 'item.php';
1125
            redirect_header($redirectPage . '?op=mod&itemid=' . $fileObj->itemid(), 2, _CO_PUBLISHER_FILEUPLOAD_SUCCESS);
1126
        } else {
1127
            return true;
1128
        }
1129
1130
        return null;
1131
    }
1132
1133
    /**
1134
     * @return string
1135
     */
1136
    public static function newFeatureTag()
1137
    {
1138
        $ret = '<span style="padding-right: 4px; font-weight: bold; color: red;">' . _CO_PUBLISHER_NEW_FEATURE . '</span>';
1139
1140
        return $ret;
1141
    }
1142
1143
    /**
1144
     * Smarty truncate_tagsafe modifier plugin
1145
     *
1146
     * Type:     modifier<br>
1147
     * Name:     truncate_tagsafe<br>
1148
     * Purpose:  Truncate a string to a certain length if necessary,
1149
     *           optionally splitting in the middle of a word, and
1150
     *           appending the $etc string or inserting $etc into the middle.
1151
     *           Makes sure no tags are left half-open or half-closed
1152
     *           (e.g. "Banana in a <a...")
1153
     * @author   Monte Ohrt <monte at ohrt dot com>, modified by Amos Robinson
1154
     *           <amos dot robinson at gmail dot com>
1155
     * @param mixed $string
1156
     * @param mixed $length
1157
     * @param mixed $etc
1158
     * @param mixed $breakWords
1159
     * @return string
1160
     */
1161
    public static function truncateTagSafe($string, $length = 80, $etc = '...', $breakWords = false)
1162
    {
1163
        if (0 == $length) {
1164
            return '';
1165
        }
1166
1167
        if (mb_strlen($string) > $length) {
1168
            $length -= mb_strlen($etc);
1169
            if (!$breakWords) {
1170
                $string = preg_replace('/\s+?(\S+)?$/', '', mb_substr($string, 0, $length + 1));
1171
                $string = preg_replace('/<[^>]*$/', '', $string);
1172
                $string = static::closeTags($string);
1173
            }
1174
1175
            return $string . $etc;
1176
        }
1177
        return $string;
1178
    }
1179
1180
    /**
1181
     * @author   Monte Ohrt <monte at ohrt dot com>, modified by Amos Robinson
1182
     *           <amos dot robinson at gmail dot com>
1183
     * @param  string $string
1184
     * @return string
1185
     */
1186
    public static function closeTags($string)
1187
    {
1188
        // match opened tags
1189
        if (preg_match_all('/<([a-z\:\-]+)[^\/]>/', $string, $startTags)) {
1190
            $startTags = $startTags[1];
1191
            // match closed tags
1192
            if (preg_match_all('/<\/([a-z]+)>/', $string, $endTags)) {
1193
                $completeTags = [];
1194
                $endTags      = $endTags[1];
1195
1196
                foreach ($startTags as $key => $val) {
1197
                    $posb = array_search($val, $endTags);
1198
                    if (is_int($posb)) {
1199
                        unset($endTags[$posb]);
1200
                    } else {
1201
                        $completeTags[] = $val;
1202
                    }
1203
                }
1204
            } else {
1205
                $completeTags = $startTags;
1206
            }
1207
1208
            $completeTags = array_reverse($completeTags);
1209
            $elementCount = count($completeTags);
1210
            for ($i = 0; $i < $elementCount; ++$i) {
1211
                $string .= '</' . $completeTags[$i] . '>';
1212
            }
1213
        }
1214
1215
        return $string;
1216
    }
1217
1218
    /**
1219
     * @param  int $itemId
1220
     * @return string
1221
     */
1222
    public static function ratingBar($itemId)
1223
    {
1224
        /** @var Publisher\Helper $helper */
1225
        $helper          = Publisher\Helper::getInstance();
1226
        $ratingUnitWidth = 30;
1227
        $units           = 5;
1228
1229
        $criteria   = new \Criteria('itemid', $itemId);
1230
        $ratingObjs = $helper->getHandler('Rating')->getObjects($criteria);
1231
        unset($criteria);
1232
1233
        $uid           = is_object($GLOBALS['xoopsUser']) ? $GLOBALS['xoopsUser']->getVar('uid') : 0;
1234
        $count         = count($ratingObjs);
1235
        $currentRating = 0;
1236
        $voted         = false;
1237
        $ip            = getenv('REMOTE_ADDR');
1238
        $rating1       = $rating2 = $ratingWidth = 0;
1239
1240
        foreach ($ratingObjs as $ratingObj) {
1241
            $currentRating += $ratingObj->getVar('rate');
1242
            if ($ratingObj->getVar('ip') == $ip || ($uid > 0 && $uid == $ratingObj->getVar('uid'))) {
1243
                $voted = true;
1244
            }
1245
        }
1246
1247
        $tense = 1 == $count ? _MD_PUBLISHER_VOTE_VOTE : _MD_PUBLISHER_VOTE_VOTES; //plural form votes/vote
1248
1249
        // now draw the rating bar
1250
        if (0 != $count) {
1251
            $ratingWidth = number_format($currentRating / $count, 2) * $ratingUnitWidth;
1252
            $rating1     = number_format($currentRating / $count, 1);
1253
            $rating2     = number_format($currentRating / $count, 2);
1254
        }
1255
        $groups = $GLOBALS['xoopsUser'] ? $GLOBALS['xoopsUser']->getGroups() : XOOPS_GROUP_ANONYMOUS;
1256
        /* @var $grouppermHandler GroupPermHandler */
1257
        $grouppermHandler = $helper->getHandler('GroupPerm');
1258
1259
        if (!$grouppermHandler->checkRight('global', Constants::PUBLISHER_RATE, $groups, $helper->getModule()->getVar('mid'))) {
1260
            $staticRater   = [];
1261
            $staticRater[] .= "\n" . '<div class="publisher_ratingblock">';
1262
            $staticRater[] .= '<div id="unit_long' . $itemId . '">';
1263
            $staticRater[] .= '<div id="unit_ul' . $itemId . '" class="publisher_unit-rating" style="width:' . $ratingUnitWidth * $units . 'px;">';
1264
            $staticRater[] .= '<div class="publisher_current-rating" style="width:' . $ratingWidth . 'px;">' . _MD_PUBLISHER_VOTE_RATING . ' ' . $rating2 . '/' . $units . '</div>';
1265
            $staticRater[] .= '</div>';
1266
            $staticRater[] .= '<div class="publisher_static">' . _MD_PUBLISHER_VOTE_RATING . ': <strong> ' . $rating1 . '</strong>/' . $units . ' (' . $count . ' ' . $tense . ') <br><em>' . _MD_PUBLISHER_VOTE_DISABLE . '</em></div>';
1267
            $staticRater[] .= '</div>';
1268
            $staticRater[] .= '</div>' . "\n\n";
1269
1270
            return implode("\n", $staticRater);
1271
        }
1272
        $rater = '';
1273
        $rater .= '<div class="publisher_ratingblock">';
1274
        $rater .= '<div id="unit_long' . $itemId . '">';
1275
        $rater .= '<div id="unit_ul' . $itemId . '" class="publisher_unit-rating" style="width:' . $ratingUnitWidth * $units . 'px;">';
1276
        $rater .= '<div class="publisher_current-rating" style="width:' . $ratingWidth . 'px;">' . _MD_PUBLISHER_VOTE_RATING . ' ' . $rating2 . '/' . $units . '</div>';
1277
1278
        for ($ncount = 1; $ncount <= $units; ++$ncount) {
1279
            // loop from 1 to the number of units
1280
            if (!$voted) {
1281
                // if the user hasn't yet voted, draw the voting stars
1282
                $rater .= '<div><a href="' . PUBLISHER_URL . '/rate.php?itemid=' . $itemId . '&amp;rating=' . $ncount . '" title="' . $ncount . ' ' . _MD_PUBLISHER_VOTE_OUTOF . ' ' . $units . '" class="publisher_r' . $ncount . '-unit rater" rel="nofollow">' . $ncount . '</a></div>';
1283
            }
1284
        }
1285
1286
        $ncount = 0; // resets the count
1287
        $rater  .= '  </div>';
1288
        $rater  .= '  <div';
1289
1290
        if ($voted) {
1291
            $rater .= ' class="publisher_voted"';
1292
        }
1293
1294
        $rater .= '>' . _MD_PUBLISHER_VOTE_RATING . ': <strong> ' . $rating1 . '</strong>/' . $units . ' (' . $count . ' ' . $tense . ')';
1295
        $rater .= '  </div>';
1296
        $rater .= '</div>';
1297
        $rater .= '</div>';
1298
1299
        return $rater;
1300
    }
1301
1302
    /**
1303
     * @param  array $allowedEditors
1304
     * @return array
1305
     */
1306
    public static function getEditors($allowedEditors = null)
1307
    {
1308
        $ret    = [];
1309
        $nohtml = false;
1310
        xoops_load('XoopsEditorHandler');
1311
        $editorHandler = \XoopsEditorHandler::getInstance();
1312
        $editors       = array_flip($editorHandler->getList()); //$editorHandler->getList($nohtml);
1313
        foreach ($editors as $name => $title) {
1314
            $key = static::stringToInt($name);
1315
            if (is_array($allowedEditors)) {
1316
                //for submit page
1317
                if (in_array($key, $allowedEditors)) {
1318
                    $ret[] = $name;
1319
                }
1320
            } else {
1321
                //for admin permissions page
1322
                $ret[$key]['name']  = $name;
1323
                $ret[$key]['title'] = $title;
1324
            }
1325
        }
1326
1327
        return $ret;
1328
    }
1329
1330
    /**
1331
     * @param  string $string
1332
     * @param  int    $length
1333
     * @return int
1334
     */
1335
    public static function stringToInt($string = '', $length = 5)
1336
    {
1337
        $final     = '';
1338
        $substring = mb_substr(md5($string), $length);
1339
        for ($i = 0; $i < $length; ++$i) {
1340
            $final .= (int)$substring[$i];
1341
        }
1342
1343
        return (int)$final;
1344
    }
1345
1346
    /**
1347
     * @param  string $item
1348
     * @return string
1349
     */
1350
    public static function convertCharset($item)
1351
    {
1352
        if (_CHARSET !== 'windows-1256') {
1353
            return utf8_encode($item);
1354
        }
1355
1356
        if ($unserialize == unserialize($item)) {
1357
            foreach ($unserialize as $key => $value) {
1358
                $unserialize[$key] = @iconv('windows-1256', 'UTF-8', $value);
1359
            }
1360
            $serialize = serialize($unserialize);
1361
1362
            return $serialize;
1363
        }
1364
        return @iconv('windows-1256', 'UTF-8', $item);
1365
    }
1366
1367
    /**
1368
     * Verifies XOOPS version meets minimum requirements for this module
1369
     * @static
1370
     * @param \XoopsModule $module
1371
     *
1372
     * @param null|string  $requiredVer
1373
     * @return bool true if meets requirements, false if not
1374
     */
1375
    public static function checkVerXoops(\XoopsModule $module = null, $requiredVer = null)
1376
    {
1377
        $moduleDirName = basename(dirname(__DIR__));
1378
        if (null === $module) {
1379
            $module = \XoopsModule::getByDirname($moduleDirName);
1380
        }
1381
        xoops_loadLanguage('admin', $moduleDirName);
1382
1383
        //check for minimum XOOPS version
1384
        $currentVer = mb_substr(XOOPS_VERSION, 6); // get the numeric part of string
1385
        if (null === $requiredVer) {
1386
            $requiredVer = '' . $module->getInfo('min_xoops'); //making sure it's a string
1387
        }
1388
        $success = true;
1389
1390
        if (version_compare($currentVer, $requiredVer, '<')) {
1391
            $success = false;
1392
            $module->setErrors(sprintf(_AM_PUBLISHER_ERROR_BAD_XOOPS, $requiredVer, $currentVer));
1393
        }
1394
1395
        return $success;
1396
    }
1397
1398
    /**
1399
     * Verifies PHP version meets minimum requirements for this module
1400
     * @static
1401
     * @param \XoopsModule $module
1402
     *
1403
     * @return bool true if meets requirements, false if not
1404
     */
1405
    public static function checkVerPhp(\XoopsModule $module)
1406
    {
1407
        xoops_loadLanguage('admin', $module->dirname());
1408
        // check for minimum PHP version
1409
        $success = true;
1410
        $verNum  = PHP_VERSION;
1411
        $reqVer  = $module->getInfo('min_php');
1412
        if (false !== $reqVer && '' !== $reqVer) {
1413
            if (version_compare($verNum, $reqVer, '<')) {
1414
                $module->setErrors(sprintf(_AM_PUBLISHER_ERROR_BAD_PHP, $reqVer, $verNum));
1415
                $success = false;
1416
            }
1417
        }
1418
1419
        return $success;
1420
    }
1421
1422
    /**
1423
     * truncateHtml can truncate a string up to a number of characters while preserving whole words and HTML tags
1424
     * www.gsdesign.ro/blog/cut-html-string-without-breaking-the-tags
1425
     * www.cakephp.org
1426
     *
1427
     * @param string $text         String to truncate.
1428
     * @param int    $length       Length of returned string, including ellipsis.
1429
     * @param string $ending       Ending to be appended to the trimmed string.
1430
     * @param bool   $exact        If false, $text will not be cut mid-word
1431
     * @param bool   $considerHtml If true, HTML tags would be handled correctly
1432
     *
1433
     * @return string Trimmed string.
1434
     */
1435
    public static function truncateHtml($text, $length = 100, $ending = '...', $exact = false, $considerHtml = true)
1436
    {
1437
        if ($considerHtml) {
1438
            // if the plain text is shorter than the maximum length, return the whole text
1439
            if (mb_strlen(preg_replace('/<.*?' . '>/', '', $text)) <= $length) {
1440
                return $text;
1441
            }
1442
            // splits all html-tags to scanable lines
1443
            preg_match_all('/(<.+?' . '>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
1444
            $total_length = mb_strlen($ending);
1445
            $open_tags    = [];
1446
            $truncate     = '';
1447
            foreach ($lines as $line_matchings) {
1448
                // if there is any html-tag in this line, handle it and add it (uncounted) to the output
1449
                if (!empty($line_matchings[1])) {
1450
                    // if it's an "empty element" with or without xhtml-conform closing slash
1451
                    if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
1452
                        // do nothing
1453
                        // if tag is a closing tag
1454
                    } elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
1455
                        // delete tag from $open_tags list
1456
                        $pos = array_search($tag_matchings[1], $open_tags);
1457
                        if (false !== $pos) {
1458
                            unset($open_tags[$pos]);
1459
                        }
1460
                        // if tag is an opening tag
1461
                    } elseif (preg_match('/^<\s*([^\s>!]+).*?' . '>$/s', $line_matchings[1], $tag_matchings)) {
1462
                        // add tag to the beginning of $open_tags list
1463
                        array_unshift($open_tags, mb_strtolower($tag_matchings[1]));
1464
                    }
1465
                    // add html-tag to $truncate'd text
1466
                    $truncate .= $line_matchings[1];
1467
                }
1468
                // calculate the length of the plain text part of the line; handle entities as one character
1469
                $content_length = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
1470
                if ($total_length + $content_length > $length) {
1471
                    // the number of characters which are left
1472
                    $left            = $length - $total_length;
1473
                    $entities_length = 0;
1474
                    // search for html entities
1475
                    if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
1476
                        // calculate the real length of all entities in the legal range
1477
                        foreach ($entities[0] as $entity) {
1478
                            if ($entity[1] + 1 - $entities_length <= $left) {
1479
                                $left--;
1480
                                $entities_length += mb_strlen($entity[0]);
1481
                            } else {
1482
                                // no more characters left
1483
                                break;
1484
                            }
1485
                        }
1486
                    }
1487
                    $truncate .= mb_substr($line_matchings[2], 0, $left + $entities_length);
1488
                    // maximum lenght is reached, so get off the loop
1489
                    break;
1490
                }
1491
                $truncate     .= $line_matchings[2];
1492
                $total_length += $content_length;
1493
1494
                // if the maximum length is reached, get off the loop
1495
                if ($total_length >= $length) {
1496
                    break;
1497
                }
1498
            }
1499
        } else {
1500
            if (mb_strlen($text) <= $length) {
1501
                return $text;
1502
            }
1503
            $truncate = mb_substr($text, 0, $length - mb_strlen($ending));
1504
        }
1505
        // if the words shouldn't be cut in the middle...
1506
        if (!$exact) {
1507
            // ...search the last occurance of a space...
1508
            $spacepos = mb_strrpos($truncate, ' ');
1509
            if (isset($spacepos)) {
1510
                // ...and cut the text in this position
1511
                $truncate = mb_substr($truncate, 0, $spacepos);
1512
            }
1513
        }
1514
        // add the defined ending to the text
1515
        $truncate .= $ending;
1516
        if ($considerHtml) {
1517
            // close all unclosed html-tags
1518
            foreach ($open_tags as $tag) {
1519
                $truncate .= '</' . $tag . '>';
1520
            }
1521
        }
1522
1523
        return $truncate;
1524
    }
1525
}
1526