Issues (384)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/Utility.php (1 issue)

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\News;
4
5
use MyTextSanitizer;
6
use WideImage\WideImage;
7
use Xmf\Request;
8
use XoopsFormDhtmlTextArea;
9
use XoopsFormEditor;
10
use XoopsFormFckeditor;
11
use XoopsFormHtmlarea;
12
use XoopsFormTextArea;
13
use XoopsFormTinyeditorTextArea;
14
use XoopsFormWysiwygTextArea;
15
use XoopsObjectTree;
16
use XoopsTpl;
17
18
/**
19
 * Class Utility
20
 */
21
class Utility extends Common\SysUtility
22
{
23
    //--------------- Custom module methods -----------------------------
24
    /**
25
     * @param             $option
26
     * @param string      $repmodule
27
     * @return bool|mixed
28
     */
29
    public static function getModuleOption($option, $repmodule = 'news')
30
    {
31
        global $xoopsModuleConfig, $xoopsModule;
32
        static $tbloptions = [];
33
        if (\is_array($tbloptions) && \array_key_exists($option, $tbloptions)) {
34
            return $tbloptions[$option];
35
        }
36
37
        $retval = false;
38
        if (isset($xoopsModuleConfig)
39
            && (\is_object($xoopsModule) && $xoopsModule->getVar('dirname') == $repmodule
40
                && $xoopsModule->getVar('isactive'))) {
41
            if (isset($xoopsModuleConfig[$option])) {
42
                $retval = $xoopsModuleConfig[$option];
43
            }
44
        } else {
45
            /** @var \XoopsModuleHandler $moduleHandler */
46
            $moduleHandler = \xoops_getHandler('module');
47
            $module        = $moduleHandler->getByDirname($repmodule);
48
            /** @var XoopsConfigHandler $configHandler */
49
            $configHandler = \xoops_getHandler('config');
50
            if ($module) {
51
                $moduleConfig = $configHandler->getConfigsByCat(0, $module->getVar('mid'));
52
                if (isset($moduleConfig[$option])) {
53
                    $retval = $moduleConfig[$option];
54
                }
55
            }
56
        }
57
        $tbloptions[$option] = $retval;
58
59
        return $retval;
60
    }
61
62
    /**
63
     * Updates rating data in item table for a given item
64
     *
65
     * @param $storyid
66
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
67
     * @copyright (c) Hervé Thouzard
68
     */
69
    public static function updateRating($storyid): void
70
    {
71
        global $xoopsDB;
72
        $query       = 'SELECT rating FROM ' . $xoopsDB->prefix('news_stories_votedata') . ' WHERE storyid = ' . $storyid;
73
        $voteresult  = $xoopsDB->query($query);
74
        $votesDB     = $xoopsDB->getRowsNum($voteresult);
75
        $totalrating = 0;
76
        while ([$rating] = $xoopsDB->fetchRow($voteresult)) {
77
            $totalrating += $rating;
78
        }
79
        $finalrating = $totalrating / $votesDB;
80
        $finalrating = \number_format($finalrating, 4);
81
        $sql         = \sprintf('UPDATE `%s` SET rating = %u, votes = %u WHERE storyid = %u', $xoopsDB->prefix('news_stories'), $finalrating, $votesDB, $storyid);
82
        $xoopsDB->queryF($sql);
83
    }
84
85
    /**
86
     * Internal function for permissions
87
     *
88
     * Returns a list of all the permitted topics Ids for the current user
89
     *
90
     * @param string $permtype
91
     *
92
     * @return array    Permitted topics Ids
93
     *
94
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
95
     * @copyright (c) Hervé Thouzard
96
     */
97
    public static function getMyItemIds($permtype = 'news_view')
98
    {
99
        global $xoopsUser;
100
        static $tblperms = [];
101
        if (\is_array($tblperms) && \array_key_exists($permtype, $tblperms)) {
102
            return $tblperms[$permtype];
103
        }
104
105
        /** @var \XoopsModuleHandler $moduleHandler */
106
        $moduleHandler       = \xoops_getHandler('module');
107
        $newsModule          = $moduleHandler->getByDirname('news');
108
        $groups              = \is_object($xoopsUser) ? $xoopsUser->getGroups() : XOOPS_GROUP_ANONYMOUS;
109
        $grouppermHandler    = \xoops_getHandler('groupperm');
110
        $topics              = $grouppermHandler->getItemIds($permtype, $groups, $newsModule->getVar('mid'));
111
        $tblperms[$permtype] = $topics;
112
113
        return $topics;
114
    }
115
116
    /**
117
     * @param $document
118
     *
119
     * @return mixed
120
     */
121
    public static function html2text($document)
122
    {
123
        // PHP Manual:: function preg_replace
124
        // $document should contain an HTML document.
125
        // This will remove HTML tags, javascript sections
126
        // and white space. It will also convert some
127
        // common HTML entities to their text equivalent.
128
129
        $search = [
130
            "'<script[^>]*?>.*?</script>'si", // Strip out javascript
131
            "'<img.*?>'si", // Strip out img tags
132
            "'<[\/\!]*?[^<>]*?>'si", // Strip out HTML tags
133
            "'([\r\n])[\s]+'", // Strip out white space
134
            "'&(quot|#34);'i", // Replace HTML entities
135
            "'&(amp|#38);'i",
136
            "'&(lt|#60);'i",
137
            "'&(gt|#62);'i",
138
            "'&(nbsp|#160);'i",
139
            "'&(iexcl|#161);'i",
140
            "'&(cent|#162);'i",
141
            "'&(pound|#163);'i",
142
            "'&(copy|#169);'i",
143
        ]; // evaluate as php
144
145
        $replace = [
146
            '',
147
            '',
148
            '',
149
            '\\1',
150
            '"',
151
            '&',
152
            '<',
153
            '>',
154
            ' ',
155
            \chr(161),
156
            \chr(162),
157
            \chr(163),
158
            \chr(169),
159
        ];
160
161
        $text = \preg_replace($search, $replace, $document);
162
163
        \preg_replace_callback(
164
            '/&#(\d+);/',
165
            static function ($matches) {
166
                return \chr($matches[1]);
167
            },
168
            $document
169
        );
170
171
        return $text;
172
    }
173
174
    /**
175
     * Is Xoops 2.3.x ?
176
     *
177
     * @return bool need to say it ?
178
     */
179
    public static function isX23()
180
    {
181
        $x23 = false;
182
        $xv  = \str_replace('XOOPS ', '', \XOOPS_VERSION);
183
        if (mb_substr($xv, 2, 1) >= '3') {
184
            $x23 = true;
185
        }
186
187
        return $x23;
188
    }
189
190
    /**
191
     * Retrieve an editor according to the module's option "form_options"
192
     *
193
     * @param                                                                                                                                 $caption
194
     * @param                                                                                                                                 $name
195
     * @param string                                                                                                                          $value
196
     * @param string                                                                                                                          $width
197
     * @param string                                                                                                                          $height
198
     * @param string                                                                                                                          $supplemental
199
     * @return bool|\XoopsFormDhtmlTextArea|\XoopsFormEditor|\XoopsFormFckeditor|\XoopsFormHtmlarea|\XoopsFormTextArea|\XoopsFormTinyeditorTextArea
200
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
201
     * @copyright (c) Hervé Thouzard
202
     */
203
    public static function getWysiwygForm($caption, $name, $value = '', $width = '100%', $height = '400px', $supplemental = '')
204
    {
205
        $editor_option            = \mb_strtolower(static::getModuleOption('form_options'));
206
        $editor                   = false;
207
        $editor_configs           = [];
208
        $editor_configs['name']   = $name;
209
        $editor_configs['value']  = $value;
210
        $editor_configs['rows']   = 35;
211
        $editor_configs['cols']   = 60;
212
        $editor_configs['width']  = '100%';
213
        $editor_configs['height'] = '350px';
214
        $editor_configs['editor'] = $editor_option;
215
216
        if (static::isX23()) {
217
            $editor = new XoopsFormEditor($caption, $name, $editor_configs);
218
219
            return $editor;
220
        }
221
222
        // Only for Xoops 2.0.x
223
        switch ($editor_option) {
224
            case 'fckeditor':
225
                if (\is_readable(XOOPS_ROOT_PATH . '/class/fckeditor/formfckeditor.php')) {
226
                    require_once XOOPS_ROOT_PATH . '/class/fckeditor/formfckeditor.php';
227
                    $editor = new XoopsFormFckeditor($caption, $name, $value);
228
                }
229
                break;
230
            case 'htmlarea':
231
                if (\is_readable(XOOPS_ROOT_PATH . '/class/htmlarea/formhtmlarea.php')) {
232
                    require_once XOOPS_ROOT_PATH . '/class/htmlarea/formhtmlarea.php';
233
                    $editor = new XoopsFormHtmlarea($caption, $name, $value);
234
                }
235
                break;
236
            case 'dhtmltextarea':
237
            case 'dhtml':
238
                $editor = new XoopsFormDhtmlTextArea($caption, $name, $value, 10, 50, $supplemental);
239
                break;
240
            case 'textarea':
241
                $editor = new XoopsFormTextArea($caption, $name, $value);
242
                break;
243
            case 'tinyeditor':
244
            case 'tinymce':
245
                if (\is_readable(XOOPS_ROOT_PATH . '/class/xoopseditor/tinyeditor/formtinyeditortextarea.php')) {
246
                    require_once XOOPS_ROOT_PATH . '/class/xoopseditor/tinyeditor/formtinyeditortextarea.php';
247
                    $editor = new XoopsFormTinyeditorTextArea(
248
                        [
249
                            'caption' => $caption,
250
                            'name'    => $name,
251
                            'value'   => $value,
252
                            'width'   => '100%',
253
                            'height'  => '400px',
254
                        ]
255
                    );
256
                }
257
                break;
258
            case 'koivi':
259
                if (\is_readable(XOOPS_ROOT_PATH . '/class/wysiwyg/formwysiwygtextarea.php')) {
260
                    require_once XOOPS_ROOT_PATH . '/class/wysiwyg/formwysiwygtextarea.php';
261
                    $editor = new XoopsFormWysiwygTextArea($caption, $name, $value, $width, $height, '');
262
                }
263
                break;
264
        }
265
266
        return $editor;
267
    }
268
269
    /**
270
     * @param \Xmf\Module\Helper $helper
271
     * @param array|null         $options
272
     * @return \XoopsFormDhtmlTextArea|\XoopsFormEditor
273
     */
274
    public static function getEditor($helper = null, $options = null)
275
    {
276
        /** @var Helper $helper */
277
        if (null === $options) {
278
            $options           = [];
279
            $options['name']   = 'Editor';
280
            $options['value']  = 'Editor';
281
            $options['rows']   = 10;
282
            $options['cols']   = '100%';
283
            $options['width']  = '100%';
284
            $options['height'] = '400px';
285
        }
286
287
        if (null === $helper) {
288
            $helper = Helper::getInstance();
289
        }
290
291
        $isAdmin = $helper->isUserAdmin();
292
293
        if (\class_exists('XoopsFormEditor')) {
294
            if ($isAdmin) {
295
                $descEditor = new XoopsFormEditor(\ucfirst($options['name']), $helper->getConfig('editorAdmin'), $options, $nohtml = false, $onfailure = 'textarea');
296
            } else {
297
                $descEditor = new XoopsFormEditor(\ucfirst($options['name']), $helper->getConfig('editorUser'), $options, $nohtml = false, $onfailure = 'textarea');
298
            }
299
        } else {
300
            $descEditor = new XoopsFormDhtmlTextArea(\ucfirst($options['name']), $options['name'], $options['value'], '100%', '100%');
301
        }
302
303
        //        $form->addElement($descEditor);
304
305
        return $descEditor;
306
    }
307
308
    /**
309
     * Internal function
310
     *
311
     * @param $text
312
     * @return mixed
313
     * @copyright (c) Hervé Thouzard
314
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
315
     */
316
    public static function getDublinQuotes($text)
317
    {
318
        return \str_replace('"', ' ', $text);
319
    }
320
321
    /**
322
     * Creates all the meta datas :
323
     * - For Mozilla/Netscape and Opera the site navigation's bar
324
     * - The Dublin's Core Metadata
325
     * - The link for Firefox 2 micro summaries
326
     * - The meta keywords
327
     * - The meta description
328
     *
329
     * @param null $story
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $story is correct as it would always require null to be passed?
Loading history...
330
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
331
     * @copyright (c) Hervé Thouzard
332
     */
333
    public static function createMetaDatas($story = null): void
334
    {
335
        global $xoopsConfig, $xoTheme, $xoopsTpl;
336
        $content = '';
337
        $myts    = MyTextSanitizer::getInstance();
338
        //        require_once XOOPS_ROOT_PATH . '/modules/news/class/class.newstopic.php';
339
340
        /**
341
         * Firefox and Opera Navigation's Bar
342
         */
343
        if (static::getModuleOption('sitenavbar')) {
344
            $content .= \sprintf("<link rel=\"Home\" title=\"%s\" href=\"%s/\">\n", $xoopsConfig['sitename'], XOOPS_URL);
345
            $content .= \sprintf("<link rel=\"Contents\" href=\"%s\">\n", XOOPS_URL . '/modules/news/index.php');
346
            $content .= \sprintf("<link rel=\"Search\" href=\"%s\">\n", XOOPS_URL . '/search.php');
347
            $content .= \sprintf("<link rel=\"Glossary\" href=\"%s\">\n", XOOPS_URL . '/modules/news/archive.php');
348
            $content .= \sprintf("<link rel=\"%s\" href=\"%s\">\n", \htmlspecialchars(_NW_SUBMITNEWS, \ENT_QUOTES | \ENT_HTML5), XOOPS_URL . '/modules/news/submit.php');
349
            $content .= \sprintf("<link rel=\"alternate\" type=\"application/rss+xml\" title=\"%s\" href=\"%s/\">\n", $xoopsConfig['sitename'], XOOPS_URL . '/backend.php');
350
351
            // Create chapters
352
            require_once XOOPS_ROOT_PATH . '/class/tree.php';
353
            //            require_once XOOPS_ROOT_PATH . '/modules/news/class/class.newstopic.php';
354
            $xt         = new NewsTopic();
355
            $allTopics  = $xt->getAllTopics(static::getModuleOption('restrictindex'));
356
            $topic_tree = new XoopsObjectTree($allTopics, 'topic_id', 'topic_pid');
357
            $topics_arr = $topic_tree->getAllChild(0);
358
            foreach ($topics_arr as $onetopic) {
359
                $content .= \sprintf("<link rel=\"Chapter\" title=\"%s\" href=\"%s\">\n", $onetopic->topic_title(), XOOPS_URL . '/modules/news/index.php?storytopic=' . $onetopic->topic_id());
360
            }
361
        }
362
363
        /**
364
         * Meta Keywords and Description
365
         * If you have set this module's option to 'yes' and if the information was entered, then they are rendered in the page else they are computed
366
         */
367
        $meta_keywords = '';
368
        if (isset($story) && \is_object($story)) {
369
            if ('' !== \xoops_trim($story->keywords())) {
370
                $meta_keywords = $story->keywords();
371
            } else {
372
                $meta_keywords = static::createMetaKeywords($story->hometext() . ' ' . $story->bodytext());
373
            }
374
            if ('' !== \xoops_trim($story->description())) {
375
                $meta_description = \strip_tags($story->description);
376
            } else {
377
                $meta_description = \strip_tags($story->title);
378
            }
379
            if (isset($xoTheme) && \is_object($xoTheme)) {
380
                $xoTheme->addMeta('meta', 'keywords', $meta_keywords);
381
                $xoTheme->addMeta('meta', 'description', $meta_description);
382
            } elseif (isset($xoopsTpl) && \is_object($xoopsTpl)) { // Compatibility for old Xoops versions
383
                $xoopsTpl->assign('xoops_meta_keywords', $meta_keywords);
384
                $xoopsTpl->assign('xoops_meta_description', $meta_description);
385
            }
386
        }
387
388
        /**
389
         * Dublin Core's meta datas
390
         */
391
        if (static::getModuleOption('dublincore') && isset($story) && \is_object($story)) {
392
            /** @var XoopsConfigHandler $configHandler */
393
            $configHandler         = \xoops_getHandler('config');
394
            $xoopsConfigMetaFooter = $configHandler->getConfigsByCat(\XOOPS_CONF_METAFOOTER);
395
            $content               .= '<meta name="DC.Title" content="' . static::getDublinQuotes($story->title()) . "\">\n";
396
            $content               .= '<meta name="DC.Creator" content="' . static::getDublinQuotes($story->uname()) . "\">\n";
397
            $content               .= '<meta name="DC.Subject" content="' . static::getDublinQuotes($meta_keywords) . "\">\n";
398
            $content               .= '<meta name="DC.Description" content="' . static::getDublinQuotes($story->title()) . "\">\n";
399
            $content               .= '<meta name="DC.Publisher" content="' . static::getDublinQuotes($xoopsConfig['sitename']) . "\">\n";
400
            $content               .= '<meta name="DC.Date.created" scheme="W3CDTF" content="' . \date('Y-m-d', $story->created) . "\">\n";
401
            $content               .= '<meta name="DC.Date.issued" scheme="W3CDTF" content="' . \date('Y-m-d', $story->published) . "\">\n";
402
            $content               .= '<meta name="DC.Identifier" content="' . XOOPS_URL . '/modules/news/article.php?storyid=' . $story->storyid() . "\">\n";
403
            $content               .= '<meta name="DC.Source" content="' . XOOPS_URL . "\">\n";
404
            $content               .= '<meta name="DC.Language" content="' . _LANGCODE . "\">\n";
405
            $content               .= '<meta name="DC.Relation.isReferencedBy" content="' . XOOPS_URL . '/modules/news/index.php?storytopic=' . $story->topicid() . "\">\n";
406
            if (isset($xoopsConfigMetaFooter['meta_copyright'])) {
407
                $content .= '<meta name="DC.Rights" content="' . static::getDublinQuotes($xoopsConfigMetaFooter['meta_copyright']) . "\">\n";
408
            }
409
        }
410
411
        /**
412
         * Firefox 2 micro summaries
413
         */
414
        if (static::getModuleOption('firefox_microsummaries')) {
415
            $content .= \sprintf("<link rel=\"microsummary\" href=\"%s\">\n", XOOPS_URL . '/modules/news/micro_summary.php');
416
        }
417
418
        if (isset($xoopsTpl) && \is_object($xoopsTpl)) {
419
            $xoopsTpl->assign('xoops_module_header', $content);
420
        }
421
    }
422
423
    /**
424
     * Create the meta keywords based on the content
425
     *
426
     * @param $content
427
     * @return string
428
     * @copyright (c) Hervé Thouzard
429
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
430
     */
431
    public static function createMetaKeywords($content)
432
    {
433
        global $cfg;
434
        require_once XOOPS_ROOT_PATH . '/modules/news/config.php';
435
        // require_once XOOPS_ROOT_PATH . '/modules/news/class/blacklist.php';
436
        // require_once XOOPS_ROOT_PATH . '/modules/news/class/registryfile.php';
437
438
        if (!$cfg['meta_keywords_auto_generate']) {
439
            return '';
440
        }
441
        $registry = new Registryfile('news_metagen_options.txt');
442
        //    $tcontent = '';
443
        $tcontent = $registry->getfile();
444
        if ('' !== \xoops_trim($tcontent)) {
445
            [$keywordscount, $keywordsorder] = \explode(',', $tcontent);
446
        } else {
447
            $keywordscount = $cfg['meta_keywords_count'];
448
            $keywordsorder = $cfg['meta_keywords_order'];
449
        }
450
451
        $tmp = [];
452
        // Search for the "Minimum keyword length"
453
        if (Request::hasVar('news_keywords_limit', 'SESSION')) {
454
            $limit = $_SESSION['news_keywords_limit'];
455
        } else {
456
            /** @var XoopsConfigHandler $configHandler */
457
            $configHandler                   = \xoops_getHandler('config');
458
            $xoopsConfigSearch               = $configHandler->getConfigsByCat(\XOOPS_CONF_SEARCH);
459
            $limit                           = $xoopsConfigSearch['keyword_min'];
460
            $_SESSION['news_keywords_limit'] = $limit;
461
        }
462
        $myts            = MyTextSanitizer::getInstance();
463
        $content         = \str_replace('<br>', ' ', $content);
464
        $content         = $myts->undoHtmlSpecialChars($content);
465
        $content         = \strip_tags($content);
466
        $content         = \mb_strtolower($content);
467
        $search_pattern  = [
468
            '&nbsp;',
469
            "\t",
470
            "\r\n",
471
            "\r",
472
            "\n",
473
            ',',
474
            '.',
475
            "'",
476
            ';',
477
            ':',
478
            ')',
479
            '(',
480
            '"',
481
            '?',
482
            '!',
483
            '{',
484
            '}',
485
            '[',
486
            ']',
487
            '<',
488
            '>',
489
            '/',
490
            '+',
491
            '-',
492
            '_',
493
            '\\',
494
            '*',
495
        ];
496
        $replace_pattern = [
497
            ' ',
498
            ' ',
499
            ' ',
500
            ' ',
501
            ' ',
502
            ' ',
503
            ' ',
504
            ' ',
505
            '',
506
            '',
507
            '',
508
            '',
509
            '',
510
            '',
511
            '',
512
            '',
513
            '',
514
            '',
515
            '',
516
            '',
517
            '',
518
            '',
519
            '',
520
            '',
521
            '',
522
            '',
523
            '',
524
        ];
525
        $content         = \str_replace($search_pattern, $replace_pattern, $content);
526
        $keywords        = \explode(' ', $content);
527
        switch ($keywordsorder) {
528
            case 0: // Ordre d'apparition dans le texte
529
                $keywords = \array_unique($keywords);
530
                break;
531
            case 1: // Ordre de fréquence des mots
532
                $keywords = \array_count_values($keywords);
533
                \asort($keywords);
534
                $keywords = \array_keys($keywords);
535
                break;
536
            case 2: // Ordre inverse de la fréquence des mots
537
                $keywords = \array_count_values($keywords);
538
                \arsort($keywords);
539
                $keywords = \array_keys($keywords);
540
                break;
541
        }
542
        // Remove black listed words
543
        $metablack = new Blacklist();
544
        $words     = $metablack->getAllKeywords();
545
        $keywords  = $metablack->remove_blacklisted($keywords);
546
547
        foreach ($keywords as $keyword) {
548
            if (mb_strlen($keyword) >= $limit && !\is_numeric($keyword)) {
549
                $tmp[] = $keyword;
550
            }
551
        }
552
        $tmp = \array_slice($tmp, 0, $keywordscount);
553
        if (\count($tmp) > 0) {
554
            return \implode(',', $tmp);
555
        }
556
        if (!isset($configHandler) || !\is_object($configHandler)) {
557
            /** @var XoopsConfigHandler $configHandler */
558
            $configHandler = \xoops_getHandler('config');
559
        }
560
        $xoopsConfigMetaFooter = $configHandler->getConfigsByCat(\XOOPS_CONF_METAFOOTER);
561
        return $xoopsConfigMetaFooter['meta_keywords'] ?? '';
562
    }
563
564
    /**
565
     * Remove module's cache
566
     *
567
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
568
     * @copyright (c) Hervé Thouzard
569
     */
570
    public static function updateCache(): void
571
    {
572
        global $xoopsModule;
573
        $folder  = $xoopsModule->getVar('dirname');
574
        $tpllist = [];
575
        require_once XOOPS_ROOT_PATH . '/class/xoopsblock.php';
576
        require_once XOOPS_ROOT_PATH . '/class/template.php';
577
        $tplfileHandler = \xoops_getHandler('tplfile');
578
        $tpllist        = $tplfileHandler->find(null, null, null, $folder);
579
        $xoopsTpl       = new XoopsTpl();
580
        \xoops_template_clear_module_cache($xoopsModule->getVar('mid')); // Clear module's blocks cache
581
582
        // Remove cache for each page.
583
        foreach ($tpllist as $onetemplate) {
584
            if ('module' === $onetemplate->getVar('tpl_type')) {
585
                // Note, I've been testing all the other methods (like the one of Smarty) and none of them run, that's why I have used this code
586
                $files_del = [];
587
                $files_del = \glob(XOOPS_CACHE_PATH . '/*' . $onetemplate->getVar('tpl_file') . '*', \GLOB_NOSORT);
588
                if (\count($files_del) > 0) {
589
                    foreach ($files_del as $one_file) {
590
                        \unlink($one_file);
591
                    }
592
                }
593
            }
594
        }
595
    }
596
597
    /**
598
     * Verify that a mysql table exists
599
     *
600
     * @param $tablename
601
     * @return bool
602
     * @copyright (c) Hervé Thouzard
603
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
604
     */
605
    public static function existTable($tablename)
606
    {
607
        global $xoopsDB;
608
        $result = $xoopsDB->queryF("SHOW TABLES LIKE '$tablename'");
609
610
        return ($xoopsDB->getRowsNum($result) > 0);
611
    }
612
613
    /**
614
     * Verify that a field exists inside a mysql table
615
     *
616
     * @param $fieldname
617
     * @param $table
618
     * @return bool
619
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
620
     * @copyright (c) Hervé Thouzard
621
     */
622
    public static function existField($fieldname, $table)
623
    {
624
        global $xoopsDB;
625
        $result = $xoopsDB->queryF("SHOW COLUMNS FROM   $table LIKE '$fieldname'");
626
627
        return ($xoopsDB->getRowsNum($result) > 0);
628
    }
629
630
    /**
631
     * Add a field to a mysql table
632
     *
633
     * @param $field
634
     * @param $table
635
     * @return bool|\mysqli_result
636
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
637
     * @copyright (c) Hervé Thouzard
638
     */
639
    public static function addField($field, $table)
640
    {
641
        global $xoopsDB;
642
        $result = $xoopsDB->queryF('ALTER TABLE ' . $table . " ADD $field;");
643
644
        return $result;
645
    }
646
647
    /**
648
     * Verify that the current user is a member of the Admin group
649
     */
650
    public static function isAdminGroup()
651
    {
652
        global $xoopsUser, $xoopsModule;
653
        if (\is_object($xoopsUser)) {
654
            if (\in_array('1', $xoopsUser->getGroups(), true)) {
655
                return true;
656
            }
657
            if ($xoopsUser->isAdmin($xoopsModule->mid())) {
658
                return true;
659
            }
660
661
            return false;
662
        }
663
664
        return false;
665
    }
666
667
    /**
668
     * Verify if the current "user" is a bot or not
669
     *
670
     * If you have a problem with this function, insert the folowing code just before the line if (\Xmf\Request::hasVar('news_cache_bot', 'SESSION'))) { :
671
     * return false;
672
     *
673
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
674
     * @copyright (c) Hervé Thouzard
675
     */
676
    public static function isBot()
677
    {
678
        if (Request::hasVar('news_cache_bot', 'SESSION')) {
679
            return $_SESSION['news_cache_bot'];
680
        }
681
        // Add here every bot you know separated by a pipe | (not matter with the upper or lower cases)
682
        // If you want to see the result for yourself, add your navigator's user agent at the end (mozilla for example)
683
        $botlist      = 'AbachoBOT|Arachnoidea|ASPSeek|Atomz|cosmos|crawl25-public.alexa.com|CrawlerBoy Pinpoint.com|Crawler|DeepIndex|EchO!|exabot|Excalibur Internet Spider|FAST-WebCrawler|Fluffy the spider|GAIS Robot/1.0B2|GaisLab data gatherer|Google|Googlebot-Image|googlebot|Gulliver|ia_archiver|Infoseek|Links2Go|Lycos_Spider_(modspider)|Lycos_Spider_(T-Rex)|MantraAgent|Mata Hari|Mercator|MicrosoftPrototypeCrawler|[email protected]|MSNBOT|NEC Research Agent|NetMechanic|Nokia-WAPToolkit|nttdirectory_robot|Openfind|Oracle Ultra Search|PicoSearch|Pompos|Scooter|Slider_Search_v1-de|Slurp|Slurp.so|SlySearch|Spider|Spinne|SurferF3|Surfnomore Spider|suzuran|teomaagent1|TurnitinBot|Ultraseek|VoilaBot|vspider|W3C_Validator|Web Link Validator|WebTrends|WebZIP|whatUseek_winona|WISEbot|Xenu Link Sleuth|ZyBorg';
684
        $botlist      = \mb_strtoupper($botlist);
685
        $currentagent = \mb_strtoupper(\xoops_getenv('HTTP_USER_AGENT'));
686
        $retval       = false;
687
        $botarray     = \explode('|', $botlist);
688
        foreach ($botarray as $onebot) {
689
            if (false !== mb_strpos($currentagent, $onebot)) {
690
                $retval = true;
691
                break;
692
            }
693
        }
694
695
        $_SESSION['news_cache_bot'] = $retval;
696
697
        return $retval;
698
    }
699
700
    /**
701
     * Create an infotip
702
     *
703
     * @param $text
704
     * @return string|null
705
     * @copyright (c) Hervé Thouzard
706
     * @author        Hervé Thouzard (https://www.herve-thouzard.com)
707
     */
708
    public static function makeInfotips($text)
709
    {
710
        $infotips = static::getModuleOption('infotips');
711
        if ($infotips > 0) {
712
            $myts = MyTextSanitizer::getInstance();
713
714
            return \htmlspecialchars(\xoops_substr(\strip_tags($text), 0, $infotips), \ENT_QUOTES | \ENT_HTML5);
715
        }
716
717
        return null;
718
    }
719
720
    /**
721
     * @param string $string
722
     * @return string
723
     * @author   Monte Ohrt <monte at ohrt dot com>, modified by Amos Robinson
724
     *           <amos dot robinson at gmail dot com>
725
     */
726
    public static function closeTags($string)
727
    {
728
        // match opened tags
729
        if (\preg_match_all('/<([a-z\:\-]+)[^\/]>/', $string, $start_tags)) {
730
            $start_tags = $start_tags[1];
731
            // match closed tags
732
            if (\preg_match_all('/<\/([a-z]+)>/', $string, $end_tags)) {
733
                $complete_tags = [];
734
                $end_tags      = $end_tags[1];
735
736
                foreach ($start_tags as $key => $val) {
737
                    $posb = \array_search($val, $end_tags, true);
738
                    if (\is_int($posb)) {
739
                        unset($end_tags[$posb]);
740
                    } else {
741
                        $complete_tags[] = $val;
742
                    }
743
                }
744
            } else {
745
                $complete_tags = $start_tags;
746
            }
747
748
            $complete_tags = \array_reverse($complete_tags);
749
            foreach ($complete_tags as $iValue) {
750
                $string .= '</' . $iValue . '>';
751
            }
752
        }
753
754
        return $string;
755
    }
756
757
    /**
758
     * Smarty truncate_tagsafe modifier plugin
759
     *
760
     * Type:     modifier<br>
761
     * Name:     truncate_tagsafe<br>
762
     * Purpose:  Truncate a string to a certain length if necessary,
763
     *           optionally splitting in the middle of a word, and
764
     *           appending the $etc string or inserting $etc into the middle.
765
     *           Makes sure no tags are left half-open or half-closed
766
     *           (e.g. "Banana in a <a...")
767
     *
768
     * @param mixed $string
769
     * @param mixed $length
770
     * @param mixed $etc
771
     * @param mixed $break_words
772
     *
773
     * @return string
774
     * @author   Monte Ohrt <monte at ohrt dot com>, modified by Amos Robinson
775
     *           <amos dot robinson at gmail dot com>
776
     */
777
    public static function truncateTagSafe($string, $length = 80, $etc = '...', $break_words = false)
778
    {
779
        if (0 == $length) {
780
            return '';
781
        }
782
        if (mb_strlen($string) > $length) {
783
            $length -= mb_strlen($etc);
784
            if (!$break_words) {
785
                $string = \preg_replace('/\s+?(\S+)?$/', '', mb_substr($string, 0, $length + 1));
786
                $string = \preg_replace('/<[^>]*$/', '', $string);
787
                $string = static::closeTags($string);
788
            }
789
790
            return $string . $etc;
791
        }
792
793
        return $string;
794
    }
795
796
    /**
797
     * Resize a Picture to some given dimensions (using the wideImage library)
798
     *
799
     * @param string $src_path      Picture's source
800
     * @param string $dst_path      Picture's destination
801
     * @param int    $param_width   Maximum picture's width
802
     * @param int    $param_height  Maximum picture's height
803
     * @param bool   $keep_original Do we have to keep the original picture ?
804
     * @param string $fit           Resize mode (see the wideImage library for more information)
805
     *
806
     * @return bool
807
     */
808
    public static function resizePicture(
809
        $src_path,
810
        $dst_path,
811
        $param_width,
812
        $param_height,
813
        $keep_original = false,
814
        $fit = 'inside'
815
    ) {
816
        //    require_once XOOPS_PATH . '/vendor/wideimage/WideImage.php';
817
        $resize            = true;
818
        $pictureDimensions = \getimagesize($src_path);
819
        if (\is_array($pictureDimensions)) {
820
            $pictureWidth  = $pictureDimensions[0];
821
            $pictureHeight = $pictureDimensions[1];
822
            if ($pictureWidth < $param_width && $pictureHeight < $param_height) {
823
                $resize = false;
824
            }
825
        }
826
827
        $img = WideImage::load($src_path);
828
        if ($resize) {
829
            $result = $img->resize($param_width, $param_height, $fit);
830
            $result->saveToFile($dst_path);
831
        } else {
832
            @\copy($src_path, $dst_path);
833
        }
834
        if (!$keep_original) {
835
            @\unlink($src_path);
836
        }
837
838
        return true;
839
    }
840
}
841