xref: /plugin/usersettings/admin.php (revision 8f16c88bf96af7bd6c6c2a42a38e2ef475a77889)
11ab40613Stracker-user<?php
21ab40613Stracker-user
31ab40613Stracker-user/**
41ab40613Stracker-user * User Settings plugin — admin component.
51ab40613Stracker-user *
61ab40613Stracker-user * Provides an admin-only overview of every user's preferences as a flat,
71ab40613Stracker-user * sortable table: one row per (user x setting), with the columns
81ab40613Stracker-user *   Display name | Setting | Value | Changed by | Changed at.
91ab40613Stracker-user * A row's value is the user's explicit choice, or the toggle's default
101ab40613Stracker-user * (marked as such) when they never set one. A filter narrows the table to a
111ab40613Stracker-user * single setting; the table never grows wider as more toggles are added.
121ab40613Stracker-user *
131ab40613Stracker-user * Clicking a display name opens a per-user edit form (Model A+: an admin may
141ab40613Stracker-user * change anyone's preferences). Such a change is stored with the admin as the
151ab40613Stracker-user * recorded actor, and the user can still change it back themselves later.
161ab40613Stracker-user */
171ab40613Stracker-user
181ab40613Stracker-user// must be run within DokuWiki
191ab40613Stracker-userif (!defined('DOKU_INC')) die();
201ab40613Stracker-user
211ab40613Stracker-userclass admin_plugin_usersettings extends DokuWiki_Admin_Plugin
221ab40613Stracker-user{
231ab40613Stracker-user    /** @var string[] sort key => row field used for that sort */
241ab40613Stracker-user    protected $sortFields = [
251ab40613Stracker-user        'name'      => 'display_name',
261ab40613Stracker-user        'setting'   => 'setting_label',
271ab40613Stracker-user        'value'     => 'value_display',
281ab40613Stracker-user        'changedby' => 'changed_by_display',
291ab40613Stracker-user        'changedat' => 'changed_at',
301ab40613Stracker-user    ];
311ab40613Stracker-user
321ab40613Stracker-user    // ---------------------------------------------------------------------
331ab40613Stracker-user    //  Admin plugin metadata
341ab40613Stracker-user    // ---------------------------------------------------------------------
351ab40613Stracker-user
361ab40613Stracker-user    /** Only administrators may see other users' preferences. */
371ab40613Stracker-user    public function forAdminOnly()
381ab40613Stracker-user    {
391ab40613Stracker-user        return true;
401ab40613Stracker-user    }
411ab40613Stracker-user
421ab40613Stracker-user    /** Position in the admin menu. */
431ab40613Stracker-user    public function getMenuSort()
441ab40613Stracker-user    {
451ab40613Stracker-user        return 350;
461ab40613Stracker-user    }
471ab40613Stracker-user
481ab40613Stracker-user    /** Admin menu label — distinct from the user menu's "Preferences". */
491ab40613Stracker-user    public function getMenuText($language)
501ab40613Stracker-user    {
511ab40613Stracker-user        return $this->getLang('admin_menu');
521ab40613Stracker-user    }
531ab40613Stracker-user
541ab40613Stracker-user    // ---------------------------------------------------------------------
551ab40613Stracker-user    //  Component access
561ab40613Stracker-user    // ---------------------------------------------------------------------
571ab40613Stracker-user
581ab40613Stracker-user    /** @return helper_plugin_usersettings|null */
591ab40613Stracker-user    protected function getHelper()
601ab40613Stracker-user    {
611ab40613Stracker-user        return plugin_load('helper', 'usersettings');
621ab40613Stracker-user    }
631ab40613Stracker-user
641ab40613Stracker-user    /** @return action_plugin_usersettings|null */
651ab40613Stracker-user    protected function getActionPlugin()
661ab40613Stracker-user    {
671ab40613Stracker-user        return plugin_load('action', 'usersettings');
681ab40613Stracker-user    }
691ab40613Stracker-user
701ab40613Stracker-user    // ---------------------------------------------------------------------
711ab40613Stracker-user    //  Request handling
721ab40613Stracker-user    // ---------------------------------------------------------------------
731ab40613Stracker-user
741ab40613Stracker-user    /**
751ab40613Stracker-user     * Handle a submitted per-user edit form.
761ab40613Stracker-user     *
771ab40613Stracker-user     * Runs only for admins (DokuWiki's admin dispatcher enforces
781ab40613Stracker-user     * forAdminOnly() before this is called). Uses Post/Redirect/Get.
791ab40613Stracker-user     */
801ab40613Stracker-user    public function handle()
811ab40613Stracker-user    {
821ab40613Stracker-user        global $INPUT, $ID;
831ab40613Stracker-user
841ab40613Stracker-user        if (!$INPUT->post->bool('usersettings_adminsave')) {
851ab40613Stracker-user            return;
861ab40613Stracker-user        }
871ab40613Stracker-user        if (!checkSecurityToken()) {
881ab40613Stracker-user            return;
891ab40613Stracker-user        }
901ab40613Stracker-user
911ab40613Stracker-user        $this->processAdminSave();
921ab40613Stracker-user
931ab40613Stracker-user        // Post/Redirect/Get back to the overview
941ab40613Stracker-user        send_redirect(wl($ID, ['do' => 'admin', 'page' => 'usersettings'], true, '&'));
951ab40613Stracker-user    }
961ab40613Stracker-user
971ab40613Stracker-user    /**
981ab40613Stracker-user     * Validate the target user and store the submitted preferences for them,
991ab40613Stracker-user     * recording the acting admin as the actor. Kept redirect-free so it can
1001ab40613Stracker-user     * be tested directly.
1011ab40613Stracker-user     *
1021ab40613Stracker-user     * @return bool
1031ab40613Stracker-user     */
1041ab40613Stracker-user    public function processAdminSave()
1051ab40613Stracker-user    {
1061ab40613Stracker-user        global $INPUT, $auth;
1071ab40613Stracker-user
1081ab40613Stracker-user        $target = $INPUT->post->str('edituser');
1091ab40613Stracker-user        $admin  = $INPUT->server->str('REMOTE_USER');
1101ab40613Stracker-user
1111ab40613Stracker-user        $userData = ($auth !== null) ? $auth->getUserData($target) : false;
1121ab40613Stracker-user        if ($userData === false) {
1131ab40613Stracker-user            msg($this->getLang('badidentuser'), -1);
1141ab40613Stracker-user            return false;
1151ab40613Stracker-user        }
1161ab40613Stracker-user
1171ab40613Stracker-user        $action = $this->getActionPlugin();
1181ab40613Stracker-user        $ok = ($action !== null) && $action->saveSubmittedPreferences($target, $admin);
1191ab40613Stracker-user
1201ab40613Stracker-user        $name = ($userData['name'] !== '') ? $userData['name'] : $target;
1211ab40613Stracker-user        msg(
1221ab40613Stracker-user            sprintf($this->getLang($ok ? 'adminsaved' : 'adminsavefail'), hsc($name)),
1231ab40613Stracker-user            $ok ? 1 : -1
1241ab40613Stracker-user        );
1251ab40613Stracker-user        return $ok;
1261ab40613Stracker-user    }
1271ab40613Stracker-user
1281ab40613Stracker-user    // ---------------------------------------------------------------------
1291ab40613Stracker-user    //  Output
1301ab40613Stracker-user    // ---------------------------------------------------------------------
1311ab40613Stracker-user
1321ab40613Stracker-user    /**
1331ab40613Stracker-user     * Render either the overview table or, when an edituser parameter is
1341ab40613Stracker-user     * present, the per-user edit form.
1351ab40613Stracker-user     */
1361ab40613Stracker-user    public function html()
1371ab40613Stracker-user    {
1381ab40613Stracker-user        global $INPUT;
1391ab40613Stracker-user
1401ab40613Stracker-user        $edituser = $INPUT->get->str('edituser');
1411ab40613Stracker-user        if ($edituser !== '') {
1421ab40613Stracker-user            echo $this->renderEditForm($edituser);
1431ab40613Stracker-user        } else {
1441ab40613Stracker-user            echo $this->renderTable();
1451ab40613Stracker-user        }
1461ab40613Stracker-user    }
1471ab40613Stracker-user
1481ab40613Stracker-user    // ---- overview table --------------------------------------------------
1491ab40613Stracker-user
1501ab40613Stracker-user    /**
1511ab40613Stracker-user     * Build the rows of the overview: one per (user x toggle).
1521ab40613Stracker-user     *
1531ab40613Stracker-user     * @param array $users   [username => userdata] as from $auth->retrieveUsers()
1541ab40613Stracker-user     * @param array $toggles registered toggle definitions
1551ab40613Stracker-user     * @return array list of row arrays
1561ab40613Stracker-user     */
1571ab40613Stracker-user    public function buildRows(array $users, array $toggles)
1581ab40613Stracker-user    {
1591ab40613Stracker-user        $helper = $this->getHelper();
1601ab40613Stracker-user        if ($helper === null) {
1611ab40613Stracker-user            return [];
1621ab40613Stracker-user        }
1631ab40613Stracker-user
1641ab40613Stracker-user        $rows = [];
1651ab40613Stracker-user        foreach ($users as $username => $userData) {
1661ab40613Stracker-user            $displayName = (!empty($userData['name'])) ? $userData['name'] : $username;
1671ab40613Stracker-user            $stored = $helper->loadUserData($username);
1681ab40613Stracker-user
1691ab40613Stracker-user            foreach ($toggles as $key => $def) {
1701ab40613Stracker-user                if (isset($stored[$key]) && array_key_exists('value', $stored[$key])) {
1711ab40613Stracker-user                    $value     = $stored[$key]['value'];
1721ab40613Stracker-user                    $changedBy = $stored[$key]['changed_by'] ?? '';
1731ab40613Stracker-user                    $changedAt = (int) ($stored[$key]['changed_at'] ?? 0);
1741ab40613Stracker-user                    $isDefault = false;
1751ab40613Stracker-user                } else {
1761ab40613Stracker-user                    $value     = $def['default'];
1771ab40613Stracker-user                    $changedBy = '';
1781ab40613Stracker-user                    $changedAt = 0;
1791ab40613Stracker-user                    $isDefault = true;
1801ab40613Stracker-user                }
1811ab40613Stracker-user
1821ab40613Stracker-user                $rows[] = [
1831ab40613Stracker-user                    'user'               => $username,
1841ab40613Stracker-user                    'display_name'       => $displayName,
1851ab40613Stracker-user                    'setting_key'        => $key,
1861ab40613Stracker-user                    'setting_label'      => $def['label'],
1871ab40613Stracker-user                    'value_display'      => $this->displayValue($def, $value),
1881ab40613Stracker-user                    'is_default'         => $isDefault,
1891ab40613Stracker-user                    'changed_by_display' => $isDefault ? '' : $this->resolveActor($changedBy, $users),
1901ab40613Stracker-user                    'changed_at'         => $changedAt,
1911ab40613Stracker-user                ];
1921ab40613Stracker-user            }
1931ab40613Stracker-user        }
1941ab40613Stracker-user        return $rows;
1951ab40613Stracker-user    }
1961ab40613Stracker-user
1971ab40613Stracker-user    /**
1981ab40613Stracker-user     * Sort overview rows by the given column and direction.
1991ab40613Stracker-user     *
2001ab40613Stracker-user     * @param array  $rows
2011ab40613Stracker-user     * @param string $sort one of the keys of $this->sortFields
2021ab40613Stracker-user     * @param string $dir  'asc' or 'desc'
2031ab40613Stracker-user     * @return array
2041ab40613Stracker-user     */
2051ab40613Stracker-user    public function sortRows(array $rows, $sort, $dir)
2061ab40613Stracker-user    {
2071ab40613Stracker-user        $field = $this->sortFields[$sort] ?? 'display_name';
2081ab40613Stracker-user
2091ab40613Stracker-user        usort($rows, function ($a, $b) use ($field) {
2101ab40613Stracker-user            if ($field === 'changed_at') {
2111ab40613Stracker-user                return $a[$field] <=> $b[$field];
2121ab40613Stracker-user            }
2131ab40613Stracker-user            return strcasecmp((string) $a[$field], (string) $b[$field]);
2141ab40613Stracker-user        });
2151ab40613Stracker-user
2161ab40613Stracker-user        if ($dir === 'desc') {
2171ab40613Stracker-user            $rows = array_reverse($rows);
2181ab40613Stracker-user        }
2191ab40613Stracker-user        return $rows;
2201ab40613Stracker-user    }
2211ab40613Stracker-user
2221ab40613Stracker-user    /**
2231ab40613Stracker-user     * Human-readable value of a toggle: On/Off for a checkbox, the option
2241ab40613Stracker-user     * label for a select.
2251ab40613Stracker-user     *
2261ab40613Stracker-user     * @param array $def
2271ab40613Stracker-user     * @param mixed $value
2281ab40613Stracker-user     * @return string
2291ab40613Stracker-user     */
2301ab40613Stracker-user    public function displayValue(array $def, $value)
2311ab40613Stracker-user    {
2321ab40613Stracker-user        if ($def['type'] === 'select') {
2331ab40613Stracker-user            if (isset($def['options'][$value])) {
2341ab40613Stracker-user                return (string) $def['options'][$value];
2351ab40613Stracker-user            }
2361ab40613Stracker-user            return (string) $value; // stored value no longer a defined option
2371ab40613Stracker-user        }
2381ab40613Stracker-user        return $this->getLang(empty($value) ? 'val_off' : 'val_on');
2391ab40613Stracker-user    }
2401ab40613Stracker-user
2411ab40613Stracker-user    /**
2421ab40613Stracker-user     * Resolve an actor username to a display name, falling back to the raw
2431ab40613Stracker-user     * username when the actor is not (or no longer) a known user.
2441ab40613Stracker-user     *
2451ab40613Stracker-user     * @param string $actor
2461ab40613Stracker-user     * @param array  $users [username => userdata]
2471ab40613Stracker-user     * @return string
2481ab40613Stracker-user     */
2491ab40613Stracker-user    protected function resolveActor($actor, array $users)
2501ab40613Stracker-user    {
2511ab40613Stracker-user        if ($actor === '') {
2521ab40613Stracker-user            return '';
2531ab40613Stracker-user        }
2541ab40613Stracker-user        if (isset($users[$actor]) && !empty($users[$actor]['name'])) {
2551ab40613Stracker-user            return $users[$actor]['name'];
2561ab40613Stracker-user        }
2571ab40613Stracker-user        return $actor;
2581ab40613Stracker-user    }
2591ab40613Stracker-user
2601ab40613Stracker-user    /**
2611ab40613Stracker-user     * Render the overview table.
2621ab40613Stracker-user     *
2631ab40613Stracker-user     * @return string
2641ab40613Stracker-user     */
2651ab40613Stracker-user    protected function renderTable()
2661ab40613Stracker-user    {
2671ab40613Stracker-user        global $INPUT, $auth;
2681ab40613Stracker-user
2691ab40613Stracker-user        $helper  = $this->getHelper();
2701ab40613Stracker-user        $toggles = $helper ? $helper->getRegisteredToggles() : [];
2711ab40613Stracker-user
2721ab40613Stracker-user        $html  = '<div class="plugin_usersettings_admin">';
2731ab40613Stracker-user        $html .= '<h1>' . hsc($this->getLang('admin_heading')) . '</h1>';
2741ab40613Stracker-user
2751ab40613Stracker-user        if (empty($toggles)) {
2761ab40613Stracker-user            return $html . '<p>' . hsc($this->getLang('notoggles')) . '</p></div>';
2771ab40613Stracker-user        }
2781ab40613Stracker-user
2791ab40613Stracker-user        $users = ($auth !== null) ? $auth->retrieveUsers(0, 0) : [];
2801ab40613Stracker-user        if (empty($users)) {
2811ab40613Stracker-user            return $html . '<p>' . hsc($this->getLang('nousers')) . '</p></div>';
2821ab40613Stracker-user        }
2831ab40613Stracker-user
2841ab40613Stracker-user        // request parameters (sort links and the filter form are GET)
2851ab40613Stracker-user        $sort   = $INPUT->get->str('sort', 'name');
2861ab40613Stracker-user        $dir    = ($INPUT->get->str('dir') === 'desc') ? 'desc' : 'asc';
2871ab40613Stracker-user        $filter = $INPUT->get->str('filter');
2881ab40613Stracker-user        if (!isset($this->sortFields[$sort])) {
2891ab40613Stracker-user            $sort = 'name';
2901ab40613Stracker-user        }
2911ab40613Stracker-user        if ($filter !== '' && !isset($toggles[$filter])) {
2921ab40613Stracker-user            $filter = '';
2931ab40613Stracker-user        }
2941ab40613Stracker-user
2951ab40613Stracker-user        $html .= '<p>' . hsc($this->getLang('admin_intro')) . '</p>';
2961ab40613Stracker-user        $html .= $this->renderFilter($toggles, $sort, $dir, $filter);
2971ab40613Stracker-user
2981ab40613Stracker-user        // rows
2991ab40613Stracker-user        $rows = $this->buildRows($users, $toggles);
3001ab40613Stracker-user        if ($filter !== '') {
3011ab40613Stracker-user            $rows = array_values(array_filter($rows, function ($r) use ($filter) {
3021ab40613Stracker-user                return $r['setting_key'] === $filter;
3031ab40613Stracker-user            }));
3041ab40613Stracker-user        }
3051ab40613Stracker-user        $rows = $this->sortRows($rows, $sort, $dir);
3061ab40613Stracker-user
3071ab40613Stracker-user        // table
3081ab40613Stracker-user        $html .= '<table class="inline plugin_usersettings_table">';
3091ab40613Stracker-user        $html .= '<thead><tr>';
3101ab40613Stracker-user        $html .= $this->sortHeader($this->getLang('th_name'), 'name', $sort, $dir, $filter);
3111ab40613Stracker-user        $html .= $this->sortHeader($this->getLang('th_setting'), 'setting', $sort, $dir, $filter);
3121ab40613Stracker-user        $html .= $this->sortHeader($this->getLang('th_value'), 'value', $sort, $dir, $filter);
3131ab40613Stracker-user        $html .= $this->sortHeader($this->getLang('th_changedby'), 'changedby', $sort, $dir, $filter);
3141ab40613Stracker-user        $html .= $this->sortHeader($this->getLang('th_changedat'), 'changedat', $sort, $dir, $filter);
3151ab40613Stracker-user        $html .= '</tr></thead><tbody>';
3161ab40613Stracker-user
3171ab40613Stracker-user        foreach ($rows as $row) {
3181ab40613Stracker-user            $rowClass = $row['is_default'] ? ' class="us-default-row"' : '';
3191ab40613Stracker-user            $editUrl  = $this->pageURL(['edituser' => $row['user']]);
3201ab40613Stracker-user
3211ab40613Stracker-user            $html .= '<tr' . $rowClass . '>';
3221ab40613Stracker-user            $html .= '<td><a href="' . $editUrl . '">' . hsc($row['display_name']) . '</a></td>';
3231ab40613Stracker-user            $html .= '<td>' . hsc($row['setting_label']) . '</td>';
3241ab40613Stracker-user            $html .= '<td>' . hsc($row['value_display']) . '</td>';
3251ab40613Stracker-user            $html .= '<td>' . ($row['is_default']
3261ab40613Stracker-user                        ? '<span class="us-default-mark">' . hsc($this->getLang('bydefault')) . '</span>'
3271ab40613Stracker-user                        : hsc($row['changed_by_display'])) . '</td>';
3281ab40613Stracker-user            $html .= '<td>' . ($row['changed_at'] > 0 ? hsc(dformat($row['changed_at'])) : '&mdash;') . '</td>';
3291ab40613Stracker-user            $html .= '</tr>';
3301ab40613Stracker-user        }
3311ab40613Stracker-user
3321ab40613Stracker-user        $html .= '</tbody></table>';
3331ab40613Stracker-user        return $html . '</div>';
3341ab40613Stracker-user    }
3351ab40613Stracker-user
3361ab40613Stracker-user    /**
3371ab40613Stracker-user     * Render the setting filter (a small GET form).
3381ab40613Stracker-user     *
3391ab40613Stracker-user     * @param array  $toggles
3401ab40613Stracker-user     * @param string $sort
3411ab40613Stracker-user     * @param string $dir
3421ab40613Stracker-user     * @param string $filter currently selected setting key
3431ab40613Stracker-user     * @return string
3441ab40613Stracker-user     */
3451ab40613Stracker-user    protected function renderFilter(array $toggles, $sort, $dir, $filter)
3461ab40613Stracker-user    {
3471ab40613Stracker-user        global $ID;
3481ab40613Stracker-user
3491ab40613Stracker-user        // GET forms drop the action URL's query string, so every parameter
3501ab40613Stracker-user        // travels as an explicit field — this also survives URL rewriting.
3511ab40613Stracker-user        $html  = '<form class="us-filter" method="get" action="' . DOKU_BASE . DOKU_SCRIPT . '">';
3521ab40613Stracker-user        $html .= '<input type="hidden" name="id" value="' . hsc($ID) . '" />';
3531ab40613Stracker-user        $html .= '<input type="hidden" name="do" value="admin" />';
3541ab40613Stracker-user        $html .= '<input type="hidden" name="page" value="usersettings" />';
3551ab40613Stracker-user        $html .= '<input type="hidden" name="sort" value="' . hsc($sort) . '" />';
3561ab40613Stracker-user        $html .= '<input type="hidden" name="dir" value="' . hsc($dir) . '" />';
3571ab40613Stracker-user
3581ab40613Stracker-user        $html .= '<label>' . hsc($this->getLang('filter_label')) . ' ';
3591ab40613Stracker-user        $html .= '<select name="filter">';
3601ab40613Stracker-user        $html .= '<option value="">' . hsc($this->getLang('filter_all')) . '</option>';
3611ab40613Stracker-user        foreach ($toggles as $key => $def) {
3621ab40613Stracker-user            $selected = ($filter === $key) ? ' selected="selected"' : '';
3631ab40613Stracker-user            $html .= '<option value="' . hsc($key) . '"' . $selected . '>'
3641ab40613Stracker-user                   . hsc($def['label']) . '</option>';
3651ab40613Stracker-user        }
3661ab40613Stracker-user        $html .= '</select></label> ';
3671ab40613Stracker-user        $html .= '<button type="submit">' . hsc($this->getLang('filter_apply')) . '</button>';
3681ab40613Stracker-user        return $html . '</form>';
3691ab40613Stracker-user    }
3701ab40613Stracker-user
3711ab40613Stracker-user    /**
3721ab40613Stracker-user     * Render one sortable column header.
3731ab40613Stracker-user     *
3741ab40613Stracker-user     * @param string $label
3751ab40613Stracker-user     * @param string $col    sort key for this column
3761ab40613Stracker-user     * @param string $sort   currently active sort key
3771ab40613Stracker-user     * @param string $dir    currently active direction
3781ab40613Stracker-user     * @param string $filter currently active filter (preserved in the link)
3791ab40613Stracker-user     * @return string
3801ab40613Stracker-user     */
3811ab40613Stracker-user    protected function sortHeader($label, $col, $sort, $dir, $filter)
3821ab40613Stracker-user    {
3831ab40613Stracker-user        // clicking the active column flips direction; others start ascending
3841ab40613Stracker-user        $newDir = ($sort === $col && $dir === 'asc') ? 'desc' : 'asc';
3851ab40613Stracker-user        $arrow  = '';
3861ab40613Stracker-user        if ($sort === $col) {
3871ab40613Stracker-user            $arrow = ($dir === 'asc') ? " \u{25B2}" : " \u{25BC}";
3881ab40613Stracker-user        }
3891ab40613Stracker-user
3901ab40613Stracker-user        $url = $this->pageURL(['sort' => $col, 'dir' => $newDir, 'filter' => $filter]);
3911ab40613Stracker-user        return '<th><a href="' . $url . '">' . hsc($label) . $arrow . '</a></th>';
3921ab40613Stracker-user    }
3931ab40613Stracker-user
3941ab40613Stracker-user    // ---- per-user edit form ---------------------------------------------
3951ab40613Stracker-user
3961ab40613Stracker-user    /**
3971ab40613Stracker-user     * Render the edit form for one user's preferences.
3981ab40613Stracker-user     *
3991ab40613Stracker-user     * @param string $user
4001ab40613Stracker-user     * @return string
4011ab40613Stracker-user     */
4021ab40613Stracker-user    protected function renderEditForm($user)
4031ab40613Stracker-user    {
4041ab40613Stracker-user        global $auth, $ID;
4051ab40613Stracker-user
406*8f16c88bStracker-user        $html = '<div class="plugin_usersettings_admin plugin_usersettings">';
4071ab40613Stracker-user
4081ab40613Stracker-user        $userData = ($auth !== null) ? $auth->getUserData($user) : false;
4091ab40613Stracker-user        if ($userData === false) {
4101ab40613Stracker-user            $html .= '<h1>' . hsc($this->getLang('admin_heading')) . '</h1>';
4111ab40613Stracker-user            $html .= '<p>' . hsc($this->getLang('badidentuser')) . '</p>';
4121ab40613Stracker-user            $html .= '<p><a href="' . $this->pageURL() . '">'
4131ab40613Stracker-user                   . hsc($this->getLang('edit_back')) . '</a></p>';
4141ab40613Stracker-user            return $html . '</div>';
4151ab40613Stracker-user        }
4161ab40613Stracker-user
4171ab40613Stracker-user        $displayName = ($userData['name'] !== '') ? $userData['name'] : $user;
4181ab40613Stracker-user        $html .= '<h1>' . hsc(sprintf($this->getLang('edit_heading'), $displayName)) . '</h1>';
4191ab40613Stracker-user
4201ab40613Stracker-user        $helper  = $this->getHelper();
4211ab40613Stracker-user        $action  = $this->getActionPlugin();
4221ab40613Stracker-user        $toggles = $helper ? $helper->getRegisteredToggles() : [];
4231ab40613Stracker-user
4241ab40613Stracker-user        if (empty($toggles) || $action === null) {
4251ab40613Stracker-user            $html .= '<p>' . hsc($this->getLang('notoggles')) . '</p>';
4261ab40613Stracker-user            $html .= '<p><a href="' . $this->pageURL() . '">'
4271ab40613Stracker-user                   . hsc($this->getLang('edit_back')) . '</a></p>';
4281ab40613Stracker-user            return $html . '</div>';
4291ab40613Stracker-user        }
4301ab40613Stracker-user
4311ab40613Stracker-user        $formAction = wl($ID, ['do' => 'admin', 'page' => 'usersettings'], false, '&amp;');
4321ab40613Stracker-user        $html .= '<form method="post" action="' . $formAction . '" class="us-form">';
4331ab40613Stracker-user        $html .= formSecurityToken(false);
4341ab40613Stracker-user        $html .= '<input type="hidden" name="edituser" value="' . hsc($user) . '" />';
4351ab40613Stracker-user        $html .= '<input type="hidden" name="usersettings_adminsave" value="1" />';
4361ab40613Stracker-user
4371ab40613Stracker-user        foreach ($toggles as $key => $def) {
4381ab40613Stracker-user            $html .= $action->renderToggleRow($def, $helper->getPreference($key, $user));
4391ab40613Stracker-user        }
4401ab40613Stracker-user
4411ab40613Stracker-user        $html .= '<div class="us-actions">';
4421ab40613Stracker-user        $html .= '<button type="submit" class="button">'
4431ab40613Stracker-user               . hsc($this->getLang('save')) . '</button> ';
4441ab40613Stracker-user        $html .= '<a href="' . $this->pageURL() . '" class="us-back">'
4451ab40613Stracker-user               . hsc($this->getLang('edit_back')) . '</a>';
4461ab40613Stracker-user        $html .= '</div>';
4471ab40613Stracker-user        $html .= '</form>';
4481ab40613Stracker-user
4491ab40613Stracker-user        return $html . '</div>';
4501ab40613Stracker-user    }
4511ab40613Stracker-user
4521ab40613Stracker-user    // ---- helpers ---------------------------------------------------------
4531ab40613Stracker-user
4541ab40613Stracker-user    /**
4551ab40613Stracker-user     * Build a URL back to this admin page with the given extra parameters.
4561ab40613Stracker-user     *
4571ab40613Stracker-user     * @param array $params
4581ab40613Stracker-user     * @return string  HTML-attribute-safe URL
4591ab40613Stracker-user     */
4601ab40613Stracker-user    protected function pageURL(array $params = [])
4611ab40613Stracker-user    {
4621ab40613Stracker-user        global $ID;
4631ab40613Stracker-user        $base = ['do' => 'admin', 'page' => 'usersettings'];
4641ab40613Stracker-user        return wl($ID, array_merge($base, $params), false, '&amp;');
4651ab40613Stracker-user    }
4661ab40613Stracker-user}
467