forum_XoopsGroupPermForm::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 5
dl 0
loc 3
rs 10
1
<?php declare(strict_types=1);
2
/*
3
 * You may not change or alter any portion of this comment or credits
4
 * of supporting developers from this source code or any supporting source code
5
 * which is considered copyrighted (c) material of the original comment or credit authors.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
/**
13
 * @copyright      {@link https://xoops.org/ XOOPS Project}
14
 * @license        {@link https://www.gnu.org/licenses/gpl-2.0.html GNU GPL 2 or later}
15
 * @author         XOOPS Development Team
16
 */
17
18
use Xmf\Module\Admin;
19
use XoopsModules\Songlist\Helper;
20
21
require_once __DIR__ . '/header.php';
22
require_once XOOPS_ROOT_PATH . '/modules/' . $GLOBALS['songlistModule']->getVar('dirname') . '/class/xoopsformloader.php';
23
require_once XOOPS_ROOT_PATH . '/class/xoopsform/grouppermform.php';
24
25
/**
26
 * Add category navigation to forum casscade structure
27
 * <ol>Special points:
28
 *  <li> Use negative values for category IDs to avoid conflict between category and forum
29
 *  <li> Disabled checkbox for categories to avoid unnecessary permission items for categories in forum permission table
30
 * </ol>
31
 *
32
 * Note: this is a __patchy__ solution. We should have a more extensible and flexible group permission management: not only for data architecture but also for management interface
33
 */
34
class forum_XoopsGroupPermForm extends \XoopsGroupPermForm
35
{
36
    /**
37
     * forum_XoopsGroupPermForm constructor.
38
     * @param        $title
39
     * @param        $modid
40
     * @param        $permname
41
     * @param        $permdesc
42
     * @param string $url
43
     */
44
    public function __construct($title, $modid, $permname, $permdesc, $url = '')
45
    {
46
        parent::__construct($title, $modid, $permname, $permdesc, $url);
47
    }
48
49
    /**
50
     * @return string
51
     */
52
    public function render(): string
53
    {
54
        // load all child ids for javascript codes
55
        foreach (array_keys($this->_itemTree) as $item_id) {
56
            $this->_itemTree[$item_id]['allchild'] = [];
57
            $this->_loadAllChildItemIds($item_id, $this->_itemTree[$item_id]['allchild']);
58
        }
59
        /** @var \XoopsGroupPermHandler $grouppermHandler */
60
        $grouppermHandler = xoops_getHandler('groupperm');
61
        /** @var \XoopsMemberHandler $memberHandler */
62
        $memberHandler = xoops_getHandler('member');
63
        $glist         = $memberHandler->getGroupList();
64
        foreach (array_keys($glist) as $i) {
65
            // get selected item id(s) for each group
66
            $selected = $grouppermHandler->getItemIds($this->_permName, $i, $this->_modid);
67
            $ele      = new forum_XoopsGroupFormCheckBox($glist[$i], 'perms[' . $this->_permName . ']', $i, $selected);
68
            $ele->setOptionTree($this->_itemTree);
69
            $this->addElement($ele);
70
            unset($ele);
71
        }
72
        $tray = new \XoopsFormElementTray('');
73
        $tray->addElement(new \XoopsFormButton('', 'submit', _SUBMIT, 'submit'));
74
        $tray->addElement(new \XoopsFormButton('', 'reset', _CANCEL, 'reset'));
75
        $this->addElement($tray);
76
        $ret      = '<h4>' . $this->getTitle() . '</h4>' . $this->_permDesc . '<br>';
77
        $ret      .= "<form name='" . $this->getName() . "' id='" . $this->getName() . "' action='" . $this->getAction() . "' method='" . $this->getMethod() . "'" . $this->getExtra() . ">\n<table width='100%' class='outer' cellspacing='1' valign='top'>\n";
78
        $elements = $this->getElements();
79
        $hidden   = '';
80
        foreach (array_keys($elements) as $i) {
81
            if (!is_object($elements[$i])) {
82
                $ret .= $elements[$i];
83
            } elseif ($elements[$i]->isHidden()) {
84
                $hidden .= $elements[$i]->render();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $elements[$i]->render() targeting XoopsFormElement::render() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
85
            } else {
86
                $ret .= "<tr valign='top' align='left'><td class='head'>" . $elements[$i]->getCaption();
87
                if ('' != $elements[$i]->getDescription()) {
88
                    $ret .= '<br><br><span style="font-weight: normal;">' . $elements[$i]->getDescription() . '</span>';
89
                }
90
                $ret .= "</td>\n<td class='even'>\n" . $elements[$i]->render() . "\n</td></tr>\n";
0 ignored issues
show
Bug introduced by
Are you sure the usage of $elements[$i]->render() targeting XoopsFormElement::render() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
91
            }
92
        }
93
        $ret .= "</table>$hidden</form>";
94
        $ret .= $this->renderValidationJS(true);
95
96
        return $ret;
97
    }
98
}
99
100
/**
101
 * Class forum_XoopsGroupFormCheckBox
102
 */
103
class forum_XoopsGroupFormCheckBox extends \XoopsGroupFormCheckBox
104
{
105
    /**
106
     * forum_XoopsGroupFormCheckBox constructor.
107
     * @param      $caption
108
     * @param      $name
109
     * @param      $groupId
110
     * @param null $values
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $values is correct as it would always require null to be passed?
Loading history...
111
     */
112
    public function __construct($caption, $name, $groupId, $values = null)
113
    {
114
        parent::__construct($caption, $name, $groupId, $values);
115
    }
116
117
    /**
118
     * Renders checkbox options for this group
119
     *
120
     * @return string
121
     */
122
    public function render(): string
123
    {
124
        $ret  = '<table class="outer"><tr><td class="odd"><table><tr>';
125
        $cols = 1;
126
        foreach ($this->_optionTree[0]['children'] as $topitem) {
127
            if ($cols > 4) {
128
                $ret  .= '</tr><tr>';
129
                $cols = 1;
130
            }
131
            $tree   = '<td valign="top">';
132
            $prefix = '';
133
            $this->_renderOptionTree($tree, $this->_optionTree[$topitem], $prefix);
134
            $ret .= $tree . '</td>';
135
            ++$cols;
136
        }
137
        $ret .= '</tr></table></td><td class="even">';
138
        foreach (array_keys($this->_optionTree) as $id) {
139
            if (!empty($id)) {
140
                $option_ids[] = "'" . $this->getName() . '[groups][' . $this->_groupId . '][' . $id . ']' . "'";
141
            }
142
        }
143
        $checkallbtn_id = $this->getName() . '[checkallbtn][' . $this->_groupId . ']';
144
        $option_ids_str = implode(', ', $option_ids);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $option_ids does not seem to be defined for all execution paths leading up to this point.
Loading history...
145
        $ret            .= _ALL . ' <input id="' . $checkallbtn_id . '" type="checkbox" value="" onclick="var optionids = new Array(' . $option_ids_str . "); xoopsCheckAllElements(optionids, '" . $checkallbtn_id . "');\">";
146
        $ret            .= '</td></tr></table>';
147
148
        return $ret;
149
    }
150
151
    /**
152
     * @param string $tree
153
     * @param array  $option
154
     * @param string $prefix
155
     * @param array  $parentIds
156
     */
157
    public function _renderOptionTree(&$tree, $option, $prefix, $parentIds = []): void
158
    {
159
        if ($option['id'] > 0) :
160
            $tree .= $prefix . '<input type="checkbox" name="' . $this->getName() . '[groups][' . $this->_groupId . '][' . $option['id'] . ']" id="' . $this->getName() . '[groups][' . $this->_groupId . '][' . $option['id'] . ']" onclick="';
161
            foreach ($parentIds as $pid) {
162
                if ($pid <= 0) {
163
                    continue;
164
                }
165
                $parent_ele = $this->getName() . '[groups][' . $this->_groupId . '][' . $pid . ']';
166
                $tree       .= "var ele = xoopsGetElementById('" . $parent_ele . "'); if(ele.checked !== true) {ele.checked = this.checked;}";
167
            }
168
            foreach ($option['allchild'] as $cid) {
169
                $child_ele = $this->getName() . '[groups][' . $this->_groupId . '][' . $cid . ']';
170
                $tree      .= "var ele = xoopsGetElementById('" . $child_ele . "'); if(this.checked !== true) {ele.checked = false;}";
171
            }
172
            $tree .= '" value="1"';
173
            if (in_array($option['id'], $this->_value, true)) {
174
                $tree .= ' checked';
175
            }
176
            $tree .= '>' . $option['name'] . '<input type="hidden" name="' . $this->getName() . '[parents][' . $option['id'] . ']" value="' . implode(':', $parentIds) . '"><input type="hidden" name="' . $this->getName() . '[itemname][' . $option['id'] . ']" value="' . htmlspecialchars(
177
                    $option['name'],
178
                    ENT_QUOTES | ENT_HTML5
179
                ) . "\"><br>\n";
180
        else :
181
            $tree .= $prefix . $option['name'] . '<input type="hidden" id="' . $this->getName() . '[groups][' . $this->_groupId . '][' . $option['id'] . "]\"><br>\n";
182
        endif;
183
        if (isset($option['children'])) {
184
            foreach ($option['children'] as $child) {
185
                if ($option['id'] > 0) {
186
                    $parentIds[] = $option['id'];
187
                }
188
                $this->_renderOptionTree($tree, $this->_optionTree[$child], $prefix . '&nbsp;-', $parentIds);
189
            }
190
        }
191
    }
192
}
193
194
xoops_cp_header();
195
196
$adminObject = Admin::getInstance();
197
$adminObject->displayNavigation(basename(__FILE__));
198
$action    = isset($_REQUEST['action']) ? \mb_strtolower($_REQUEST['action']) : '';
199
$module_id = $GLOBALS['songlistModule']->getVar('mid');
200
$perms     = array_map('\trim', explode(',', FORUM_PERM_ITEMS));
0 ignored issues
show
Bug introduced by
The constant FORUM_PERM_ITEMS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
201
202
switch ($action) {
203
    case 'template':
204
        $opform    = new \XoopsSimpleForm(_AM_SONGLIST_PERM_ACTION, 'actionform', 'permissions.php', 'get');
205
        $op_select = new \XoopsFormSelect('', 'action');
206
        $op_select->setExtra('onchange="document.forms.actionform.submit()"');
207
        $op_select->addOptionArray(
208
            [
209
                'no'       => _SELECT,
210
                'template' => _AM_SONGLIST_PERM_TEMPLATE,
211
                'apply'    => _AM_SONGLIST_PERM_TEMPLATEAPP,
212
                'default'  => _AM_SONGLIST_PERM_SETBYGROUP,
213
            ]
214
        );
215
        $opform->addElement($op_select);
216
        $opform->display();
217
218
        /** @var \XoopsMemberHandler $memberHandler */
219
        $memberHandler       = xoops_getHandler('member');
220
        $glist               = $memberHandler->getGroupList();
221
        $elements            = [];
222
        $songlistpermHandler = Helper::getInstance()->getHandler('Permission');
223
        $perm_template       = $songlistpermHandler->getTemplate($groupid = 0);
224
        foreach (array_keys($glist) as $i) {
225
            $selected   = !empty($perm_template[$i]) ? array_keys($perm_template[$i]) : [];
226
            $ret_ele    = '<tr align="left" valign="top"><td class="head">' . $glist[$i] . '</td>';
227
            $ret_ele    .= '<td class="even">';
228
            $ret_ele    .= '<table class="outer"><tr><td class="odd"><table><tr>';
229
            $ii         = 0;
230
            $option_ids = [];
231
            foreach ($perms as $perm) {
232
                ++$ii;
233
                if (0 == $ii % 5) {
234
                    $ret_ele .= '</tr><tr>';
235
                }
236
                $checked      = in_array('forum_' . $perm, $selected, true) ? ' checked' : '';
237
                $option_id    = $perm . '_' . $i;
238
                $option_ids[] = $option_id;
239
                $ret_ele      .= '<td><input name="perms[' . $i . '][' . 'forum_' . $perm . ']" id="' . $option_id . '" onclick="" value="1" type="checkbox"' . $checked . '>' . constant('_AM_SONGLIST_CAN_' . \mb_strtoupper($perm)) . '<br></td>';
240
            }
241
            $ret_ele    .= '</tr></table></td><td class="even">';
242
            $ret_ele    .= _ALL . ' <input id="checkall[' . $i . ']" type="checkbox" value="" onclick="var optionids = new Array(' . implode(', ', $option_ids) . '); xoopsCheckAllElements(optionids, \'checkall[' . $i . ']\')">';
243
            $ret_ele    .= '</td></tr></table>';
244
            $ret_ele    .= '</td></tr>';
245
            $elements[] = $ret_ele;
246
        }
247
        $tray = new \XoopsFormElementTray('');
248
        $tray->addElement(new \XoopsFormHidden('action', 'template_save'));
249
        $tray->addElement(new \XoopsFormButton('', 'submit', _SUBMIT, 'submit'));
250
        $tray->addElement(new \XoopsFormButton('', 'reset', _CANCEL, 'reset'));
251
        $ret = '<h4>' . _AM_SONGLIST_PERM_TEMPLATE . '</h4>' . _AM_SONGLIST_PERM_TEMPLATE_DESC . '<br><br><br>';
252
        $ret .= "<form name='template' id='template' method='post'>\n<table width='100%' class='outer' cellspacing='1'>\n";
253
        $ret .= implode("\n", $elements);
254
        $ret .= '<tr align="left" valign="top"><td class="head"></td><td class="even">';
255
        $ret .= $tray->render();
256
        $ret .= '</td></tr>';
257
        $ret .= '</table></form>';
258
        echo $ret;
259
        break;
260
    case 'template_save':
261
        $songlistpermHandler = Helper::getInstance()->getHandler('Permission');
262
        $res                 = $songlistpermHandler->setTemplate($_POST['perms'], $groupid = 0);
263
        if ($res) {
264
            redirect_header('permissions.php?action=template', 2, _AM_SONGLIST_PERM_TEMPLATE_CREATED);
265
        } else {
266
            redirect_header('permissions.php?action=template', 2, _AM_SONGLIST_PERM_TEMPLATE_ERROR);
267
        }
268
        break;
269
    case 'apply':
270
        $songlistpermHandler = Helper::getInstance()->getHandler('Permission');
271
        $perm_template       = $songlistpermHandler->getTemplate();
272
        if (null === $perm_template) {
273
            redirect_header('permissions.php?action=template', 2, _AM_SONGLIST_PERM_TEMPLATE);
274
        }
275
276
        $opform    = new \XoopsSimpleForm(_AM_SONGLIST_PERM_ACTION, 'actionform', 'permissions.php', 'get');
277
        $op_select = new \XoopsFormSelect('', 'action');
278
        $op_select->setExtra('onchange="document.forms.actionform.submit()"');
279
        $op_select->addOptionArray(['no' => _SELECT, 'template' => _AM_SONGLIST_PERM_TEMPLATE, 'apply' => _AM_SONGLIST_PERM_TEMPLATEAPP]);
280
        $opform->addElement($op_select);
281
        $opform->display();
282
283
        $categoryHandler = Helper::getInstance()->getHandler('Category');
284
        $categories      = $categoryHandler->getAllCats('', true, false, true);
0 ignored issues
show
Bug introduced by
The method getAllCats() does not exist on XoopsModules\Songlist\CategoryHandler. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

284
        /** @scrutinizer ignore-call */ 
285
        $categories      = $categoryHandler->getAllCats('', true, false, true);
Loading history...
285
286
        $GLOBALS['forumHandler'] = Helper::getInstance()->getHandler('Forum');
287
        $songlists               = $GLOBALS['forumHandler']->getForumsByCategory(0, '', false, false, true);
288
        $fm_options              = [];
289
        foreach (array_keys($categories) as $c) {
0 ignored issues
show
Bug introduced by
It seems like $categories can also be of type null; however, parameter $array of array_keys() does only seem to accept array, 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

289
        foreach (array_keys(/** @scrutinizer ignore-type */ $categories) as $c) {
Loading history...
290
            $fm_options[-1 * $c] = '[' . $categories[$c]->getVar('cat_title') . ']';
291
            foreach (array_keys($songlists[$c]) as $f) {
292
                $fm_options[$f] = $songlists[$c][$f]['title'];
293
                if (!isset($songlists[$c][$f]['sub'])) {
294
                    continue;
295
                }
296
                foreach (array_keys($songlists[$c][$f]['sub']) as $s) {
297
                    $fm_options[$s] = '-- ' . $songlists[$c][$f]['sub'][$s]['title'];
298
                }
299
            }
300
        }
301
        unset($songlists, $categories);
302
        $fmform    = new \XoopsThemeForm(_AM_SONGLIST_PERM_TEMPLATEAPP, 'fmform', 'permissions.php', 'post', true);
303
        $fm_select = new \XoopsFormSelect(_AM_SONGLIST_PERM_FORUMS, 'forums', null, 10, true);
304
        $fm_select->addOptionArray($fm_options);
305
        $fmform->addElement($fm_select);
306
        $tray = new \XoopsFormElementTray('');
307
        $tray->addElement(new \XoopsFormHidden('action', 'apply_save'));
308
        $tray->addElement(new \XoopsFormButton('', 'submit', _SUBMIT, 'submit'));
309
        $tray->addElement(new \XoopsFormButton('', 'reset', _CANCEL, 'reset'));
310
        $fmform->addElement($tray);
311
        $fmform->display();
312
        break;
313
    case 'apply_save':
314
        if (empty($_POST['forums'])) {
315
            break;
316
        }
317
        $songlistpermHandler = Helper::getInstance()->getHandler('Permission');
318
        foreach ($_POST['forums'] as $songlist) {
319
            if ($songlist < 1) {
320
                continue;
321
            }
322
            $songlistpermHandler->applyTemplate($songlist, $module_id);
323
        }
324
        redirect_header('permissions.php', 2, _AM_SONGLIST_PERM_TEMPLATE_APPLIED);
325
        break;
326
    default:
327
        $opform    = new \XoopsSimpleForm(_AM_SONGLIST_PERM_ACTION, 'actionform', 'permissions.php', 'get');
328
        $op_select = new \XoopsFormSelect('', 'action');
329
        $op_select->setExtra('onchange="document.forms.actionform.submit()"');
330
        $op_select->addOptionArray(
331
            [
332
                'no'       => _SELECT,
333
                'template' => _AM_SONGLIST_PERM_TEMPLATE,
334
                'apply'    => _AM_SONGLIST_PERM_TEMPLATEAPP,
335
                'default'  => _AM_SONGLIST_PERM_SETBYGROUP,
336
            ]
337
        );
338
        $opform->addElement($op_select);
339
        $opform->display();
340
341
        $GLOBALS['forumHandler'] = Helper::getInstance()->getHandler('Forum');
342
        $songlists               = $GLOBALS['forumHandler']->getForumsByCategory(0, '', false, false, true);
343
        $op_options              = ['category' => _AM_SONGLIST_CAT_ACCESS];
344
        $fm_options              = ['category' => ['title' => _AM_SONGLIST_CAT_ACCESS, 'item' => 'category_access', 'desc' => '', 'anonymous' => true]];
345
        foreach ($perms as $perm) {
346
            $op_options[$perm] = constant('_AM_SONGLIST_CAN_' . \mb_strtoupper($perm));
347
            $fm_options[$perm] = ['title' => constant('_AM_SONGLIST_CAN_' . \mb_strtoupper($perm)), 'item' => 'forum_' . $perm, 'desc' => '', 'anonymous' => true];
348
        }
349
350
        $op_keys = array_keys($op_options);
351
        $op      = isset($_GET['op']) ? \mb_strtolower($_GET['op']) : (isset($_COOKIE['op']) ? \mb_strtolower($_COOKIE['op']) : '');
352
        if (empty($op)) {
353
            $op = $op_keys[0];
354
            setcookie('op', $op_keys[1] ?? '');
355
        } else {
356
            for ($i = 0, $iMax = count($op_keys); $i < $iMax; ++$i) {
357
                if ($op_keys[$i] == $op) {
358
                    break;
359
                }
360
            }
361
            setcookie('op', $op_keys[$i + 1] ?? '');
362
        }
363
364
        $opform    = new \XoopsSimpleForm('', 'opform', 'permissions.php', 'get');
365
        $op_select = new \XoopsFormSelect('', 'op', $op);
366
        $op_select->setExtra('onchange="document.forms.opform.submit()"');
367
        $op_select->addOptionArray($op_options);
368
        $opform->addElement($op_select);
369
        $opform->display();
370
371
        $perm_desc = '';
372
373
        $form = new forum_XoopsGroupPermForm($fm_options[$op]['title'], $module_id, $fm_options[$op]['item'], $fm_options[$op]['desc'], 'admin/permissions.php', $fm_options[$op]['anonymous']);
0 ignored issues
show
Unused Code introduced by
The call to forum_XoopsGroupPermForm::__construct() has too many arguments starting with $fm_options[$op]['anonymous']. ( Ignorable by Annotation )

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

373
        $form = /** @scrutinizer ignore-call */ new forum_XoopsGroupPermForm($fm_options[$op]['title'], $module_id, $fm_options[$op]['item'], $fm_options[$op]['desc'], 'admin/permissions.php', $fm_options[$op]['anonymous']);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
374
375
        $categoryHandler = Helper::getInstance()->getHandler('Category');
376
        $categories      = $categoryHandler->getObjects(null, true);
377
        if ('category' === $op) {
378
            foreach (array_keys($categories) as $c) {
379
                $form->addItem($c, $categories[$c]->getVar('cat_title'));
380
            }
381
            unset($categories);
382
        } else {
383
            foreach (array_keys($categories) as $c) {
384
                $key_c = -1 * $c;
385
                $form->addItem($key_c, '<strong>[' . $categories[$c]->getVar('cat_title') . ']</strong>');
386
                foreach (array_keys($songlists[$c]) as $f) {
387
                    $form->addItem($f, $songlists[$c][$f]['title'], $key_c);
388
                    if (!isset($songlists[$c][$f]['sub'])) {
389
                        continue;
390
                    }
391
                    foreach (array_keys($songlists[$c][$f]['sub']) as $s) {
392
                        $form->addItem($s, '&rarr;' . $songlists[$c][$f]['sub'][$s]['title'], $f);
393
                    }
394
                }
395
            }
396
            unset($songlists, $categories);
397
        }
398
        $form->display();
399
400
        break;
401
}
402
403
echo chronolabs_inline(false);
0 ignored issues
show
Bug introduced by
The function chronolabs_inline was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

403
echo /** @scrutinizer ignore-call */ chronolabs_inline(false);
Loading history...
404
xoops_cp_footer();
405