Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 249
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
acp_bbcodes
0.00% covered (danger)
0.00%
0 / 247
0.00% covered (danger)
0.00%
0 / 2
2162
0.00% covered (danger)
0.00%
0 / 1
 main
0.00% covered (danger)
0.00%
0 / 235
0.00% covered (danger)
0.00%
0 / 1
1980
 build_regexp
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14/**
15* @ignore
16*/
17if (!defined('IN_PHPBB'))
18{
19    exit;
20}
21
22class acp_bbcodes
23{
24    var $u_action;
25
26    function main($id, $mode)
27    {
28        global $db, $user, $template, $cache, $request, $phpbb_dispatcher, $phpbb_container;
29        global $phpbb_log;
30
31        $user->add_lang('acp/posting');
32
33        // Set up general vars
34        $action    = $request->variable('action', '');
35        $bbcode_id = $request->variable('bbcode', 0);
36
37        $this->tpl_name = 'acp_bbcodes';
38        $this->page_title = 'ACP_BBCODES';
39        $form_key = 'acp_bbcodes';
40
41        add_form_key($form_key);
42
43        // Set up mode-specific vars
44        switch ($action)
45        {
46            case 'add':
47                $bbcode_match = $bbcode_tpl = $bbcode_helpline = $bbcode_font_icon = '';
48                $display_on_posting = 0;
49            break;
50
51            case 'edit':
52                $sql = 'SELECT bbcode_match, bbcode_tpl, display_on_posting, bbcode_helpline, bbcode_font_icon
53                    FROM ' . BBCODES_TABLE . '
54                    WHERE bbcode_id = ' . $bbcode_id;
55                $result = $db->sql_query($sql);
56                $row = $db->sql_fetchrow($result);
57                $db->sql_freeresult($result);
58
59                if (!$row)
60                {
61                    trigger_error($user->lang['BBCODE_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING);
62                }
63
64                $bbcode_match = $row['bbcode_match'];
65                $bbcode_tpl = htmlspecialchars($row['bbcode_tpl'], ENT_COMPAT);
66                $display_on_posting = $row['display_on_posting'];
67                $bbcode_helpline = $row['bbcode_helpline'];
68                $bbcode_font_icon = $row['bbcode_font_icon'];
69            break;
70
71            case 'modify':
72                $sql = 'SELECT bbcode_id, bbcode_tag
73                    FROM ' . BBCODES_TABLE . '
74                    WHERE bbcode_id = ' . $bbcode_id;
75                $result = $db->sql_query($sql);
76                $row = $db->sql_fetchrow($result);
77                $db->sql_freeresult($result);
78
79                if (!$row)
80                {
81                    trigger_error($user->lang['BBCODE_NOT_EXIST'] . adm_back_link($this->u_action), E_USER_WARNING);
82                }
83
84            // No break here
85
86            case 'create':
87                $display_on_posting = $request->variable('display_on_posting', 0);
88
89                $bbcode_match = $request->variable('bbcode_match', '');
90                $bbcode_tpl = html_entity_decode($request->variable('bbcode_tpl', '', true), ENT_COMPAT);
91                $bbcode_helpline = $request->variable('bbcode_helpline', '', true);
92                $bbcode_font_icon = $request->variable('bbcode_font_icon', '');
93            break;
94        }
95
96        // Do major work
97        switch ($action)
98        {
99            case 'edit':
100            case 'add':
101
102                $tpl_ary = array(
103                    'S_EDIT_BBCODE'        => true,
104                    'U_BACK'            => $this->u_action,
105                    'U_ACTION'            => $this->u_action . '&amp;action=' . (($action == 'add') ? 'create' : 'modify') . (($bbcode_id) ? "&amp;bbcode=$bbcode_id" : ''),
106                    'L_BBCODE_USAGE_EXPLAIN'        => sprintf($user->lang['BBCODE_USAGE_EXPLAIN'], '<a href="#down">', '</a>'),
107                    'BBCODE_MATCH'                    => $bbcode_match,
108                    'BBCODE_TPL'                    => $bbcode_tpl,
109                    'BBCODE_HELPLINE'                => $bbcode_helpline,
110                    'BBCODE_FONT_ICON'                => $bbcode_font_icon,
111                    'DISPLAY_ON_POSTING'            => $display_on_posting,
112                );
113
114                $bbcode_tokens = array('TEXT', 'SIMPLETEXT', 'INTTEXT', 'IDENTIFIER', 'NUMBER', 'EMAIL', 'URL', 'LOCAL_URL', 'RELATIVE_URL', 'COLOR');
115                $bbcode_tokens = array_merge($bbcode_tokens, ['ALNUM', 'CHOICE', 'FLOAT', 'HASHMAP', 'INT', 'IP', 'IPPORT', 'IPV4', 'IPV6', 'MAP', 'RANGE', 'REGEXP', 'TIMESTAMP', 'UINT']);
116
117                /**
118                * Modify custom bbcode template data before we display the add/edit form
119                *
120                * @event core.acp_bbcodes_edit_add
121                * @var    string    action            Type of the action: add|edit
122                * @var    array    tpl_ary            Array with custom bbcode add/edit data
123                * @var    int        bbcode_id        When editing: the bbcode id,
124                *                                when creating: 0
125                * @var    array    bbcode_tokens    Array of bbcode tokens
126                * @since 3.1.0-a3
127                */
128                $vars = array('action', 'tpl_ary', 'bbcode_id', 'bbcode_tokens');
129                extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_edit_add', compact($vars)));
130
131                $template->assign_vars($tpl_ary);
132
133                foreach ($bbcode_tokens as $token)
134                {
135                    $template->assign_block_vars('token', array(
136                        'TOKEN'        => '{' . $token . '}',
137                        'EXPLAIN'    => ($token === 'LOCAL_URL') ? $user->lang(array('tokens', $token), generate_board_url() . '/') : $user->lang(array('tokens', $token)),
138                    ));
139                }
140
141                return;
142
143            break;
144
145            case 'modify':
146            case 'create':
147
148                $sql_ary = $hidden_fields = array();
149
150                /**
151                * Modify custom bbcode data before the modify/create action
152                *
153                * @event core.acp_bbcodes_modify_create
154                * @var    string    action                Type of the action: modify|create
155                * @var    array    sql_ary                Array with new bbcode data
156                * @var    int        bbcode_id            When editing: the bbcode id,
157                *                                    when creating: 0
158                * @var    bool    display_on_posting    Display bbcode on posting form
159                * @var    string    bbcode_match        The bbcode usage string to match
160                * @var    string    bbcode_tpl            The bbcode HTML replacement string
161                * @var    string    bbcode_helpline        The bbcode help line string
162                * @var    string    bbcode_font_icon    The name of the Font Awesome BBCode icon
163                * @var    array    hidden_fields        Array of hidden fields for use when
164                *                                    submitting form when $warn_unsafe is true
165                * @since 3.1.0-a3
166                * @changed 4.0.0-a1 Added bbcode_font_icon
167                */
168                $vars = array(
169                    'action',
170                    'sql_ary',
171                    'bbcode_id',
172                    'display_on_posting',
173                    'bbcode_match',
174                    'bbcode_tpl',
175                    'bbcode_helpline',
176                    'bbcode_font_icon',
177                    'hidden_fields',
178                );
179                extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_modify_create', compact($vars)));
180
181                $acp_utils   = $phpbb_container->get('text_formatter.acp_utils');
182                $bbcode_info = $acp_utils->analyse_bbcode($bbcode_match, $bbcode_tpl);
183                $warn_unsafe = ($bbcode_info['status'] === $acp_utils::BBCODE_STATUS_UNSAFE);
184
185                if ($bbcode_info['status'] === $acp_utils::BBCODE_STATUS_INVALID_TEMPLATE)
186                {
187                    trigger_error($user->lang['BBCODE_INVALID_TEMPLATE'] . adm_back_link($this->u_action), E_USER_WARNING);
188                }
189                if ($bbcode_info['status'] === $acp_utils::BBCODE_STATUS_INVALID_DEFINITION)
190                {
191                    trigger_error($user->lang['BBCODE_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
192                }
193
194                if (!$warn_unsafe && !check_form_key($form_key))
195                {
196                    trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
197                }
198
199                if (!$warn_unsafe || confirm_box(true))
200                {
201                    $data = $this->build_regexp($bbcode_match, $bbcode_tpl);
202
203                    // Make sure the user didn't pick a "bad" name for the BBCode tag.
204                    $hard_coded = array('code', 'quote', 'quote=', 'attachment', 'attachment=', 'b', 'i', 'url', 'url=', 'img', 'size', 'size=', 'color', 'color=', 'u', 'list', 'list=', 'email', 'email=', 'mention');
205
206                    if (($action == 'modify' && strtolower($data['bbcode_tag']) !== strtolower($row['bbcode_tag'])) || ($action == 'create'))
207                    {
208                        $sql = 'SELECT 1 as test
209                            FROM ' . BBCODES_TABLE . "
210                            WHERE LOWER(bbcode_tag) = '" . $db->sql_escape(strtolower($data['bbcode_tag'])) . "'";
211                        $result = $db->sql_query($sql);
212                        $info = $db->sql_fetchrow($result);
213                        $db->sql_freeresult($result);
214
215                        // Grab the end, interrogate the last closing tag
216                        if (isset($info['test']) && $info['test'] === '1'
217                            || in_array(strtolower($data['bbcode_tag']), $hard_coded)
218                            || (preg_match('#\[/([^[]*)]$#', $bbcode_match, $regs) && in_array(strtolower($regs[1]), $hard_coded))
219                        )
220                        {
221                            trigger_error($user->lang['BBCODE_INVALID_TAG_NAME'] . adm_back_link($this->u_action), E_USER_WARNING);
222                        }
223                    }
224
225                    if (strlen($data['bbcode_tag']) > 16)
226                    {
227                        trigger_error($user->lang['BBCODE_TAG_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING);
228                    }
229
230                    if (strlen($bbcode_match) > 4000)
231                    {
232                        trigger_error($user->lang['BBCODE_TAG_DEF_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING);
233                    }
234
235                    if (strlen($bbcode_helpline) > 3000)
236                    {
237                        trigger_error($user->lang['BBCODE_HELPLINE_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING);
238                    }
239
240                    if (strlen($bbcode_font_icon) > 64)
241                    {
242                        trigger_error($user->lang['BBCODE_FONT_ICON_TOO_LONG'] . adm_back_link($this->u_action), E_USER_WARNING);
243                    }
244
245                    if (!empty($bbcode_font_icon) && !preg_match('/^(?!-)(?!.*--)[a-z0-9-]+(?<!-)$/', $bbcode_font_icon))
246                    {
247                        trigger_error($user->lang['BBCODE_FONT_ICON_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
248                    }
249
250                    /**
251                     * Replace Emojis and other 4bit UTF-8 chars not allowed by MySQL to UCR/NCR.
252                     * Using their Numeric Character Reference's Hexadecimal notation.
253                     */
254                    $bbcode_helpline = utf8_encode_ucr($bbcode_helpline);
255
256                    $sql_ary = array_merge($sql_ary, array(
257                        'bbcode_tag'                => $data['bbcode_tag'],
258                        'bbcode_match'                => $bbcode_match,
259                        'bbcode_tpl'                => $bbcode_tpl,
260                        'display_on_posting'        => $display_on_posting,
261                        'bbcode_helpline'            => $bbcode_helpline,
262                        'bbcode_font_icon'            => $bbcode_font_icon,
263                        'first_pass_match'            => $data['first_pass_match'],
264                        'first_pass_replace'        => $data['first_pass_replace'],
265                        'second_pass_match'            => $data['second_pass_match'],
266                        'second_pass_replace'        => $data['second_pass_replace']
267                    ));
268
269                    if ($action == 'create')
270                    {
271                        $sql = 'SELECT MAX(bbcode_id) as max_bbcode_id
272                            FROM ' . BBCODES_TABLE;
273                        $result = $db->sql_query($sql);
274                        $row = $db->sql_fetchrow($result);
275                        $db->sql_freeresult($result);
276
277                        if ($row)
278                        {
279                            $bbcode_id = (int) $row['max_bbcode_id'] + 1;
280
281                            // Make sure it is greater than the core bbcode ids...
282                            if ($bbcode_id <= NUM_CORE_BBCODES)
283                            {
284                                $bbcode_id = NUM_CORE_BBCODES + 1;
285                            }
286                        }
287                        else
288                        {
289                            $bbcode_id = NUM_CORE_BBCODES + 1;
290                        }
291
292                        if ($bbcode_id > BBCODE_LIMIT)
293                        {
294                            trigger_error($user->lang['TOO_MANY_BBCODES'] . adm_back_link($this->u_action), E_USER_WARNING);
295                        }
296
297                        $sql_ary['bbcode_id'] = (int) $bbcode_id;
298
299                        $db->sql_query('INSERT INTO ' . BBCODES_TABLE . $db->sql_build_array('INSERT', $sql_ary));
300                        $cache->destroy('sql', BBCODES_TABLE);
301                        $phpbb_container->get('text_formatter.cache')->invalidate();
302
303                        $lang = 'BBCODE_ADDED';
304                        $log_action = 'LOG_BBCODE_ADD';
305                    }
306                    else
307                    {
308                        $sql = 'UPDATE ' . BBCODES_TABLE . '
309                            SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
310                            WHERE bbcode_id = ' . $bbcode_id;
311                        $db->sql_query($sql);
312                        $cache->destroy('sql', BBCODES_TABLE);
313                        $phpbb_container->get('text_formatter.cache')->invalidate();
314
315                        $lang = 'BBCODE_EDITED';
316                        $log_action = 'LOG_BBCODE_EDIT';
317                    }
318
319                    $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log_action, false, array($data['bbcode_tag']));
320
321                    /**
322                    * Event after a BBCode has been added or updated
323                    *
324                    * @event core.acp_bbcodes_modify_create_after
325                    * @var    string    action        Type of the action: modify|create
326                    * @var    int        bbcode_id    The id of the added or updated bbcode
327                    * @var    array    sql_ary        Array with bbcode data (read only)
328                    * @since 3.2.4-RC1
329                    */
330                    $vars = array(
331                        'action',
332                        'bbcode_id',
333                        'sql_ary',
334                    );
335                    extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_modify_create_after', compact($vars)));
336
337                    trigger_error($user->lang[$lang] . adm_back_link($this->u_action));
338                }
339                else
340                {
341                    confirm_box(false, $user->lang['BBCODE_DANGER'], build_hidden_fields(array_merge($hidden_fields, array(
342                        'action'                => $action,
343                        'bbcode'                => $bbcode_id,
344                        'bbcode_match'            => $bbcode_match,
345                        'bbcode_tpl'            => htmlspecialchars($bbcode_tpl, ENT_COMPAT),
346                        'bbcode_helpline'        => $bbcode_helpline,
347                        'bbcode_font_icon'        => $bbcode_font_icon,
348                        'display_on_posting'    => $display_on_posting,
349                        )))
350                    , 'confirm_bbcode.html');
351                }
352
353            break;
354
355            case 'delete':
356
357                $sql = 'SELECT bbcode_tag
358                    FROM ' . BBCODES_TABLE . "
359                    WHERE bbcode_id = $bbcode_id";
360                $result = $db->sql_query($sql);
361                $row = $db->sql_fetchrow($result);
362                $db->sql_freeresult($result);
363
364                if ($row)
365                {
366                    if (confirm_box(true))
367                    {
368                        $bbcode_tag = $row['bbcode_tag'];
369
370                        $db->sql_query('DELETE FROM ' . BBCODES_TABLE . " WHERE bbcode_id = $bbcode_id");
371                        $cache->destroy('sql', BBCODES_TABLE);
372                        $phpbb_container->get('text_formatter.cache')->invalidate();
373                        $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_BBCODE_DELETE', false, array($bbcode_tag));
374
375                        /**
376                        * Event after a BBCode has been deleted
377                        *
378                        * @event core.acp_bbcodes_delete_after
379                        * @var    string    action        Type of the action: delete
380                        * @var    int        bbcode_id    The id of the deleted bbcode
381                        * @var    string    bbcode_tag    The tag of the deleted bbcode
382                        * @since 3.2.4-RC1
383                        */
384                        $vars = array(
385                            'action',
386                            'bbcode_id',
387                            'bbcode_tag',
388                        );
389                        extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_delete_after', compact($vars)));
390
391                        if ($request->is_ajax())
392                        {
393                            $json_response = new \phpbb\json_response;
394                            $json_response->send(array(
395                                'MESSAGE_TITLE'    => $user->lang['INFORMATION'],
396                                'MESSAGE_TEXT'    => $user->lang['BBCODE_DELETED'],
397                                'REFRESH_DATA'    => array(
398                                    'time'    => 3
399                                )
400                            ));
401                        }
402                    }
403                    else
404                    {
405                        confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
406                            'bbcode'    => $bbcode_id,
407                            'i'            => $id,
408                            'mode'        => $mode,
409                            'action'    => $action))
410                        );
411                    }
412                }
413
414            break;
415        }
416
417        $u_action = $this->u_action;
418
419        $template_data = array(
420            'U_ACTION'        => $this->u_action . '&amp;action=add',
421        );
422
423        $sql_ary = array(
424            'SELECT'    => 'b.*',
425            'FROM'        => array(BBCODES_TABLE => 'b'),
426            'ORDER_BY'    => 'b.bbcode_tag',
427        );
428
429        /**
430        *  Modify custom bbcode template data before we display the form
431        *
432        * @event core.acp_bbcodes_display_form
433        * @var    string    action            Type of the action: modify|create
434        * @var    array    sql_ary            The SQL array to get custom bbcode data
435        * @var    array    template_data    Array with form template data
436        * @var    string    u_action        The u_action link
437        * @since 3.1.0-a3
438        */
439        $vars = array('action', 'sql_ary', 'template_data', 'u_action');
440        extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_display_form', compact($vars)));
441
442        $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary));
443
444        $template->assign_vars($template_data);
445
446        while ($row = $db->sql_fetchrow($result))
447        {
448            $bbcodes_array = array(
449                'BBCODE_TAG'        => $row['bbcode_tag'],
450                'U_EDIT'            => $u_action . '&amp;action=edit&amp;bbcode=' . $row['bbcode_id'],
451                'U_DELETE'            => $u_action . '&amp;action=delete&amp;bbcode=' . $row['bbcode_id'],
452            );
453
454            /**
455            *  Modify display of custom bbcodes in the form
456            *
457            * @event core.acp_bbcodes_display_bbcodes
458            * @var    array    row                Array with current bbcode data
459            * @var    array    bbcodes_array    Array of bbcodes template data
460            * @var    string    u_action        The u_action link
461            * @since 3.1.0-a3
462            */
463            $vars = array('bbcodes_array', 'row', 'u_action');
464            extract($phpbb_dispatcher->trigger_event('core.acp_bbcodes_display_bbcodes', compact($vars)));
465
466            $template->assign_block_vars('bbcodes', $bbcodes_array);
467
468        }
469        $db->sql_freeresult($result);
470    }
471
472    /*
473    * Build regular expression for custom bbcode
474    */
475    function build_regexp(&$bbcode_match, &$bbcode_tpl)
476    {
477        $bbcode_match = trim($bbcode_match);
478        $bbcode_tag = preg_replace('/.*?\[([a-z0-9_-]+).*/i', '$1', $bbcode_match);
479
480        if (!preg_match('/^[a-zA-Z0-9_-]+$/', $bbcode_tag))
481        {
482            global $user;
483            trigger_error($user->lang['BBCODE_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING);
484        }
485
486        return array(
487            'bbcode_tag'                => $bbcode_tag,
488            'first_pass_match'            => '/(?!)/',
489            'first_pass_replace'        => '',
490            // Use a non-matching, valid regexp to effectively disable this BBCode
491            'second_pass_match'            => '/(?!)/',
492            'second_pass_replace'        => ''
493        );
494    }
495}