• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dragomano / Optimus / 19691793586

26 Nov 2025 03:44AM UTC coverage: 97.631% (+0.01%) from 97.619%
19691793586

push

github

dragomano
Update tests

1896 of 1942 relevant lines covered (97.63%)

3.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.09
/src/Sources/Optimus/Handlers/TopicHandler.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * @package Optimus
5
 * @link https://custom.simplemachines.org/mods/index.php?mod=2659
6
 * @author Bugo https://dragomano.ru/mods/optimus
7
 * @copyright 2010-2025 Bugo
8
 * @license https://opensource.org/licenses/artistic-license-2.0 Artistic-2.0
9
 *
10
 * @version 3.0 RC5
11
 */
12

13
namespace Bugo\Optimus\Handlers;
14

15
use Bugo\Compat\{Board, Config, IntegrationHook};
16
use Bugo\Compat\{Lang, Theme, Db, Topic, User, Utils};
17
use Bugo\Compat\Parsers\BBCodeParser;
18
use Bugo\Optimus\Utils\{Input, Str};
19

20
if (! defined('SMF'))
21
        die('No direct access...');
22

23
final class TopicHandler
24
{
25
        public function __invoke(): void
26
        {
27
                IntegrationHook::add(
1✔
28
                        'integrate_menu_buttons', self::class . '::prepareOgImage#', false, __FILE__
1✔
29
                );
1✔
30

31
                IntegrationHook::add(
1✔
32
                        'integrate_menu_buttons', self::class . '::menuButtons#', false, __FILE__
1✔
33
                );
1✔
34

35
                IntegrationHook::add(
1✔
36
                        'integrate_load_permissions', self::class . '::loadPermissions#', false, __FILE__
1✔
37
                );
1✔
38

39
                IntegrationHook::add(
1✔
40
                        'integrate_permissions_list', self::class . '::permissionsList#', false, __FILE__
1✔
41
                );
1✔
42

43
                IntegrationHook::add(
1✔
44
                        'integrate_optimus_basic_settings', self::class . '::basicSettings#', false, __FILE__
1✔
45
                );
1✔
46

47
                IntegrationHook::add(
1✔
48
                        'integrate_display_topic', self::class . '::displayTopic#', false, __FILE__
1✔
49
                );
1✔
50

51
                IntegrationHook::add(
1✔
52
                        'integrate_before_create_topic', self::class . '::beforeCreateTopic#', false, __FILE__
1✔
53
                );
1✔
54

55
                IntegrationHook::add(
1✔
56
                        'integrate_modify_post', self::class . '::modifyPost#', false, __FILE__
1✔
57
                );
1✔
58

59
                IntegrationHook::add(
1✔
60
                        'integrate_post_end', self::class . '::postEnd#', false, __FILE__
1✔
61
                );
1✔
62
        }
63

64
        public function prepareOgImage(): void
65
        {
66
                if (empty(Config::$modSettings['optimus_og_image']) || empty(Utils::$context['topicinfo']['id_first_msg']))
4✔
67
                        return;
1✔
68

69
                $firstMessageId = Utils::$context['topicinfo']['id_first_msg'];
3✔
70

71
                // Looking for an image in attachments of the topic first message
72
                if (
73
                        ! empty(Utils::$context['loaded_attachments'])
3✔
74
                        && isset(Utils::$context['loaded_attachments'][$firstMessageId])
3✔
75
                ) {
76
                        $attachments = Utils::$context['loaded_attachments'][$firstMessageId];
1✔
77
                        $attach = ';attach=' . ($key = array_key_first($attachments)) . ';image';
1✔
78
                        Theme::$current->settings['og_image'] = Config::$scripturl . '?action=dlattach;topic='
1✔
79
                                . Utils::$context['current_topic'] . $attach;
1✔
80

81
                        Utils::$context['optimus_og_image'] = [
1✔
82
                                'url'    => Theme::$current->settings['og_image'],
1✔
83
                                'width'  => $attachments[$key]['width'],
1✔
84
                                'height' => $attachments[$key]['height'],
1✔
85
                                'mime'   => $attachments[$key]['mime_type'],
1✔
86
                        ];
1✔
87
                }
88

89
                // Looking for an image in the text of the topic first message
90
                if (empty(Utils::$context['topicinfo']['topic_first_message']))
3✔
91
                        return;
1✔
92

93
                if (empty(Utils::$context['optimus_og_image'])) {
2✔
94
                        $image = preg_match(
1✔
95
                                '/\[img.*]([^]\[]+)\[\/img]/U',
1✔
96
                                Utils::$context['topicinfo']['topic_first_message'],
1✔
97
                                $value
1✔
98
                        );
1✔
99

100
                        Theme::$current->settings['og_image'] = $image
1✔
101
                                ? array_pop($value)
1✔
102
                                : (Theme::$current->settings['og_image'] ?? '');
×
103
                }
104
        }
105

106
        public function loadPermissions(array $permissionGroups, array &$permissionList): void
107
        {
108
                if (empty(Config::$modSettings['optimus_allow_change_topic_desc']))
2✔
109
                        return;
1✔
110

111
                $permissionList['membergroup']['optimus_add_descriptions'] = [true, 'general', 'view_basic_info'];
1✔
112
        }
113

114
        public function permissionsList(array &$permissions): void
115
        {
116
                if (empty(Config::$modSettings['optimus_allow_change_topic_desc']))
2✔
117
                        return;
1✔
118

119
                $permissions['optimus_add_descriptions_own'] = [
1✔
120
                        'generic_name' => 'optimus_add_descriptions',
1✔
121
                        'own_any'      => 'own',
1✔
122
                        'view_group'   => 'general',
1✔
123
                        'scope'        => 'global',
1✔
124
                        'never_guests' => true,
1✔
125
                ];
1✔
126

127
                $permissions['optimus_add_descriptions_any'] = [
1✔
128
                        'generic_name' => 'optimus_add_descriptions',
1✔
129
                        'own_any'      => 'any',
1✔
130
                        'view_group'   => 'general',
1✔
131
                        'scope'        => 'global',
1✔
132
                        'never_guests' => true,
1✔
133
                ];
1✔
134
        }
135

136
        public function basicSettings(array &$config_vars): void
137
        {
138
                $counter = 0;
2✔
139
                foreach ($config_vars as $key => $dump) {
2✔
140
                        if (isset($dump[1]) && $dump[1] === 'optimus_topic_extend_title') {
1✔
141
                                $counter = $key + 1;
1✔
142
                                break;
1✔
143
                        }
144
                }
145

146
                $config_vars = array_merge(
2✔
147
                        array_slice($config_vars, 0, $counter, true),
2✔
148
                        [
2✔
149
                                '',
2✔
150
                                ['check', 'optimus_topic_description'],
2✔
151
                                [
2✔
152
                                        'check',
2✔
153
                                        'optimus_allow_change_topic_desc',
2✔
154
                                        'subtext' => Lang::getTxt('optimus_allow_change_topic_desc_subtext')
2✔
155
                                ],
2✔
156
                        ],
2✔
157
                        array_slice($config_vars, $counter, null, true)
2✔
158
                );
2✔
159
        }
160

161
        public function displayTopic(array &$columns): void
162
        {
163
                if (! empty(Config::$modSettings['optimus_allow_change_topic_desc'])) {
2✔
164
                        $columns[] = 't.optimus_description';
1✔
165
                }
166

167
                if (! in_array('ms.modified_time AS topic_modified_time', $columns)) {
2✔
168
                        $columns[] = 'ms.modified_time AS topic_modified_time';
2✔
169
                }
170

171
                if (
172
                        empty(Config::$modSettings['optimus_topic_description'])
2✔
173
                        && empty(Config::$modSettings['optimus_og_image'])
2✔
174
                ) {
175
                        return;
1✔
176
                }
177

178
                if (! in_array('ms.body AS topic_first_message', $columns)) {
2✔
179
                        $columns[] = 'ms.body AS topic_first_message';
2✔
180
                }
181
        }
182

183
        /**
184
         * Prepare a description and additional Open Graph data
185
         */
186
        public function menuButtons(): void
187
        {
188
                if (empty(Utils::$context['first_message']))
4✔
189
                        return;
1✔
190

191
                $this->makeDescriptionFromFirstMessage();
3✔
192
                $this->makeDescriptionByOptimus();
3✔
193

194
                // Additional data
195
                $startedName = Utils::$context['topicinfo']['topic_started_name'] ?? '';
3✔
196
                $modifiedTime = Utils::$context['topicinfo']['topic_modified_time'] ?? 0;
3✔
197
                Utils::$context['optimus_og_type']['article'] = [
3✔
198
                        'published_time' => date('Y-m-d\TH:i:s', (int) Utils::$context['topicinfo']['topic_started_time']),
3✔
199
                        'modified_time'  => empty($modifiedTime) ? null : date('Y-m-d\TH:i:s', (int) $modifiedTime),
3✔
200
                        'author'         => empty($startedName) ? null : $startedName,
3✔
201
                        'section'        => Board::$info['name'],
3✔
202
                        'tag'            => Utils::$context['optimus_keywords'] ?? null,
3✔
203
                ];
3✔
204
        }
205

206
        public function beforeCreateTopic(
207
                array $msgOptions,
208
                array $topicOptions,
209
                array $posterOptions,
210
                array &$topicColumns,
211
                array &$topicParameters
212
        ): void
213
        {
214
                if (! $this->canChangeDescription())
2✔
215
                        return;
1✔
216

217
                $topicColumns['optimus_description'] = 'string-255';
1✔
218
                $topicParameters[] = Input::xss(Input::request('optimus_description', ''));
1✔
219
        }
220

221
        public function modifyPost(
222
                array $messagesColumns,
223
                array $updateParameters,
224
                array $msgOptions,
225
                array $topicOptions
226
        ): void
227
        {
228
                if (empty($topicOptions['first_msg']) || $topicOptions['first_msg'] != $msgOptions['id'])
2✔
229
                        return;
×
230

231
                $this->modifyDescription($topicOptions['id']);
2✔
232
        }
233

234
        public function postEnd(): void
235
        {
236
                if (Utils::$context['is_new_topic']) {
3✔
237
                        Utils::$context['optimus']['description'] = Input::xss(
1✔
238
                                Input::request('optimus_description', '')
1✔
239
                        );
1✔
240
                } else {
241
                        $result = Db::$db->query('
2✔
242
                                SELECT optimus_description, id_member_started
243
                                FROM {db_prefix}topics
244
                                WHERE id_topic = {int:id_topic}
245
                                LIMIT 1',
2✔
246
                                [
2✔
247
                                        'id_topic' => Utils::$context['current_topic'],
2✔
248
                                ]
2✔
249
                        );
2✔
250

251
                        [Utils::$context['optimus']['description'], $topicAuthor] = Db::$db->fetch_row($result);
2✔
252

253
                        Db::$db->free_result($result);
2✔
254

255
                        Utils::$context['user']['started'] = Utils::$context['user']['id'] == $topicAuthor
2✔
256
                                && ! Utils::$context['user']['is_guest'];
2✔
257
                }
258

259
                $this->addFields();
3✔
260
        }
261

262
        private function addFields(): void
263
        {
264
                if (! $this->canChangeDescription() || empty(Utils::$context['is_first_post']))
3✔
265
                        return;
×
266

267
                Utils::$context['posting_fields']['optimus_description']['label']['text'] = Lang::getTxt('optimus_seo_description');
3✔
268
                Utils::$context['posting_fields']['optimus_description']['input'] = [
3✔
269
                        'type' => 'textarea',
3✔
270
                        'attributes' => [
3✔
271
                                'id'          => 'optimus_description',
3✔
272
                                'maxlength'   => 255,
3✔
273
                                'value'       => Utils::$context['optimus']['description'],
3✔
274
                                'placeholder' => Lang::getTxt('optimus_enter_description'),
3✔
275
                        ],
3✔
276
                ];
3✔
277
        }
278

279
        private function makeDescriptionFromFirstMessage(): void
280
        {
281
                if (
282
                        empty(Config::$modSettings['optimus_topic_description'])
3✔
283
                        || empty(Utils::$context['topicinfo']['topic_first_message'])
3✔
284
                ) {
285
                        return;
2✔
286
                }
287

288
                Utils::$context['meta_description'] = Str::teaser(Utils::$context['topicinfo']['topic_first_message']);
1✔
289
        }
290

291
        private function makeDescriptionByOptimus(): void
292
        {
293
                if (empty(Utils::$context['topicinfo']['optimus_description']))
3✔
294
                        return;
2✔
295

296
                Utils::$context['meta_description'] = Utils::$context['topicinfo']['optimus_description'];
1✔
297
        }
298

299
        private function modifyDescription(int $topic): void
300
        {
301
                if (! $this->canChangeDescription())
2✔
302
                        return;
×
303

304
                $description = Input::xss(Input::request('optimus_description', ''));
2✔
305

306
                Db::$db->query('
2✔
307
                        UPDATE {db_prefix}topics
308
                        SET optimus_description = {string:description}
309
                        WHERE id_topic = {int:current_topic}',
2✔
310
                        [
2✔
311
                                'description'   => Utils::shorten(strip_tags(BBCodeParser::load()->parse($description)), 200),
2✔
312
                                'current_topic' => $topic,
2✔
313
                        ]
2✔
314
                );
2✔
315
        }
316

317
        private function canChangeDescription(): bool
318
        {
319
                if (! isset(Utils::$context['user']['started'])) {
8✔
320
                        Utils::$context['user']['started'] = empty(Topic::$id);
×
321
                }
322

323
                if (empty(Config::$modSettings['optimus_allow_change_topic_desc'])) {
8✔
324
                        return false;
2✔
325
                }
326

327
                return User::$me->allowedTo('optimus_add_descriptions_any')
7✔
328
                        || (
7✔
329
                                User::$me->allowedTo('optimus_add_descriptions_own')
7✔
330
                                && ! empty(Utils::$context['user']['started'])
7✔
331
                        );
7✔
332
        }
333
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc