. */ namespace Fisharebest\Webtrees; use Fisharebest\Webtrees\Controller\PageController; use Fisharebest\Webtrees\Functions\FunctionsDate; use Fisharebest\Webtrees\Functions\FunctionsEdit; use Fisharebest\Webtrees\Functions\FunctionsPrint; use PDO; /** * Defined in session.php * * @global Tree $WT_TREE */ global $WT_TREE; define('WT_SCRIPT_NAME', 'admin_users.php'); require './includes/session.php'; $controller = new PageController; $controller->restrictAccess(Auth::isAdmin()); // Valid values for form variables $ALL_EDIT_OPTIONS = array( 'none' => /* I18N: Listbox entry; name of a role */ I18N::translate('Visitor'), 'access' => /* I18N: Listbox entry; name of a role */ I18N::translate('Member'), 'edit' => /* I18N: Listbox entry; name of a role */ I18N::translate('Editor'), 'accept' => /* I18N: Listbox entry; name of a role */ I18N::translate('Moderator'), 'admin' => /* I18N: Listbox entry; name of a role */ I18N::translate('Manager'), ); // Form actions switch (Filter::post('action')) { case 'save': if (Filter::checkCsrf()) { $user_id = Filter::postInteger('user_id'); $user = User::find($user_id); $username = Filter::post('username'); $real_name = Filter::post('real_name'); $email = Filter::postEmail('email'); $pass1 = Filter::post('pass1', WT_REGEX_PASSWORD); $pass2 = Filter::post('pass2', WT_REGEX_PASSWORD); $theme = Filter::post('theme', implode('|', array_keys(Theme::themeNames())), ''); $language = Filter::post('language'); $timezone = Filter::post('timezone'); $contact_method = Filter::post('contact_method'); $comment = Filter::post('comment'); $auto_accept = Filter::postBool('auto_accept'); $canadmin = Filter::postBool('canadmin'); $visible_online = Filter::postBool('visible_online'); $verified = Filter::postBool('verified'); $approved = Filter::postBool('approved'); if ($user_id === 0) { // Create a new user if (User::findByUserName($username)) { FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.')); } elseif (User::findByEmail($email)) { FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.')); } elseif ($pass1 !== $pass2) { FlashMessages::addMessage(I18N::translate('The passwords do not match.')); } else { $user = User::create($username, $real_name, $email, $pass1); $user->setPreference('reg_timestamp', date('U'))->setPreference('sessiontime', '0'); Log::addAuthenticationLog('User ->' . $username . '<- created'); } } else { $user = User::find($user_id); if ($user && $username && $real_name) { $user->setEmail($email); $user->setUserName($username); $user->setRealName($real_name); if ($pass1 !== null && $pass1 === $pass2) { $user->setPassword($pass1); } } } if ($user) { // Approving for the first time? Send a confirmation email if ($approved && !$user->getPreference('verified_by_admin') && $user->getPreference('sessiontime') == 0) { I18N::init($user->getPreference('language')); Mail::systemMessage( $WT_TREE, $user, I18N::translate('Approval of account at %s', WT_BASE_URL), I18N::translate('The administrator at the webtrees site %s has approved your application for an account. You may now sign in by accessing the following link: %s', WT_BASE_URL, WT_BASE_URL) ); } $user ->setPreference('theme', $theme) ->setPreference('language', $language) ->setPreference('TIMEZONE', $timezone) ->setPreference('contactmethod', $contact_method) ->setPreference('comment', $comment) ->setPreference('auto_accept', $auto_accept ? '1' : '0') ->setPreference('visibleonline', $visible_online ? '1' : '0') ->setPreference('verified', $verified ? '1' : '0') ->setPreference('verified_by_admin', $approved ? '1' : '0'); // We cannot change our own admin status. Another admin will need to do it. if ($user->getUserId() !== Auth::id()) { $user->setPreference('canadmin', $canadmin ? '1' : '0'); } foreach (Tree::getAll() as $tree) { $tree->setUserPreference($user, 'gedcomid', Filter::post('gedcomid' . $tree->getTreeId(), WT_REGEX_XREF)); $tree->setUserPreference($user, 'canedit', Filter::post('canedit' . $tree->getTreeId(), implode('|', array_keys($ALL_EDIT_OPTIONS)))); if (Filter::post('gedcomid' . $tree->getTreeId(), WT_REGEX_XREF)) { $tree->setUserPreference($user, 'RELATIONSHIP_PATH_LENGTH', Filter::postInteger('RELATIONSHIP_PATH_LENGTH' . $tree->getTreeId(), 0, 10, 0)); } else { // Do not allow a path length to be set if the individual ID is not $tree->setUserPreference($user, 'RELATIONSHIP_PATH_LENGTH', null); } } } } header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME); return; } switch (Filter::get('action')) { case 'load_json': // Generate an AJAX/JSON response for datatables to load a block of rows $search = Filter::postArray('search'); $search = $search['value']; $start = Filter::postInteger('start'); $length = Filter::postInteger('length'); $order = Filter::postArray('order'); $sql_select = "SELECT SQL_CACHE SQL_CALC_FOUND_ROWS '', u.user_id, user_name, real_name, email, us1.setting_value, us2.setting_value, NULL, us3.setting_value, NULL, us4.setting_value, us5.setting_value" . " FROM `##user` u" . " LEFT JOIN `##user_setting` us1 ON (u.user_id=us1.user_id AND us1.setting_name='language')" . " LEFT JOIN `##user_setting` us2 ON (u.user_id=us2.user_id AND us2.setting_name='reg_timestamp')" . " LEFT JOIN `##user_setting` us3 ON (u.user_id=us3.user_id AND us3.setting_name='sessiontime')" . " LEFT JOIN `##user_setting` us4 ON (u.user_id=us4.user_id AND us4.setting_name='verified')" . " LEFT JOIN `##user_setting` us5 ON (u.user_id=us5.user_id AND us5.setting_name='verified_by_admin')" . " WHERE u.user_id > 0"; $args = array(); if ($search) { $sql_select .= " AND (user_name LIKE CONCAT('%', :search_1, '%') OR real_name LIKE CONCAT('%', :search_2, '%') OR email LIKE CONCAT('%', :search_3, '%'))"; $args['search_1'] = $search; $args['search_2'] = $search; $args['search_3'] = $search; } if ($order) { $sql_select .= " ORDER BY "; foreach ($order as $key => $value) { if ($key > 0) { $sql_select .= ','; } // Datatables numbers columns 0, 1, 2 // MySQL numbers columns 1, 2, 3 switch ($value['dir']) { case 'asc': $sql_select .= (1 + $value['column']) . " ASC "; break; case 'desc': $sql_select .= (1 + $value['column']) . " DESC "; break; } } } else { $sql_select = " ORDER BY 1 ASC"; } if ($length) { Auth::user()->setPreference('admin_users_page_size', $length); $sql_select .= " LIMIT :limit OFFSET :offset"; $args['limit'] = $length; $args['offset'] = $start; } // This becomes a JSON list, not array, so need to fetch with numeric keys. $data = Database::prepare($sql_select)->execute($args)->fetchAll(PDO::FETCH_NUM); $installed_languages = array(); foreach (I18N::installedLocales() as $installed_locale) { $installed_languages[$installed_locale->languageTag()] = $installed_locale->endonym(); } // Reformat various columns for display foreach ($data as &$datum) { $user_id = $datum[1]; $user_name = $datum[2]; if ($user_id != Auth::id()) { $admin_options = '
  • ' . /* I18N: Pretend to be another user, by logging in as them */ I18N::translate('Masquerade as this user') . '
  • ' . '
  • ' . I18N::translate('Delete') . '
  • '; } else { // Do not delete ourself! $admin_options = ''; } $datum[0] = '
    '; // $datum[1] is the user ID // $datum[3] is the real name $datum[3] = '' . Filter::escapeHtml($datum[3]) . ''; // $datum[4] is the email address if ($user_id != Auth::id()) { $datum[4] = '' . Filter::escapeHtml($datum[4]) . ''; } // $datum[2] is the username $datum[2] = '' . Filter::escapeHtml($datum[2]) . ''; // $datum[5] is the langauge if (array_key_exists($datum[5], $installed_languages)) { $datum[5] = $installed_languages[$datum[5]]; } // $datum[6] is the sortable registration timestamp $datum[7] = $datum[6] ? FunctionsDate::formatTimestamp($datum[6] + WT_TIMESTAMP_OFFSET) : ''; if (date("U") - $datum[6] > 604800 && !$datum[10]) { $datum[7] = '' . $datum[7] . ''; } // $datum[8] is the sortable last-login timestamp if ($datum[8]) { $datum[9] = FunctionsDate::formatTimestamp($datum[8] + WT_TIMESTAMP_OFFSET) . '
    ' . I18N::timeAgo(WT_TIMESTAMP - $datum[8]); } else { $datum[9] = I18N::translate('Never'); } $datum[10] = $datum[10] ? I18N::translate('yes') : I18N::translate('no'); $datum[11] = $datum[11] ? I18N::translate('yes') : I18N::translate('no'); } // Total filtered/unfiltered rows $recordsFiltered = (int) Database::prepare("SELECT FOUND_ROWS()")->fetchOne(); $recordsTotal = User::count(); header('Content-type: application/json'); // See http://www.datatables.net/usage/server-side echo json_encode(array( 'draw' => Filter::getInteger('draw'), 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data, )); return; case 'edit': $user_id = Filter::getInteger('user_id'); if ($user_id === 0) { $controller->setPageTitle(I18N::translate('Add a user')); $tmp = new \stdClass; $tmp->user_id = ''; $tmp->user_name = ''; $tmp->real_name = ''; $tmp->email = ''; $user = new User($tmp); } else { $controller->setPageTitle(I18N::translate('Edit the user')); $user = User::find($user_id); } $controller ->pageHeader() ->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL) ->addInlineJavascript('autocomplete();') ->addInlineJavascript(' jQuery(".relpath").change(function() { var fieldIDx = jQuery(this).attr("id"); var idNum = fieldIDx.replace("RELATIONSHIP_PATH_LENGTH",""); var newIDx = "gedcomid"+idNum; if (jQuery("#"+newIDx).val() === "" && jQuery("#".fieldIDx).val() !== "0") { alert("' . I18N::translate('You must specify an individual record before you can restrict the user to their immediate family.') . '"); jQuery(this).val("0"); } }); function regex_quote(str) { return str.replace(/[\\\\.?+*()[\](){}|]/g, "\\\\$&"); } '); ?>

    getPageTitle(); ?>

    getUserId() ? '' : 'required'; ?> onchange="form.pass2.pattern = regex_quote(this.value);">

    getUserId() ? '' : 'required'; ?>>

    getPreference('TIMEZONE') ?: 'UTC', 'class="form-control"'); ?>

    getPreference('contactmethod')); ?>

    '), $user->getPreference('theme'), 'class="form-control"'); ?>

    getTitleHtml(); ?> getTreeId(), '', $tree); ?>
    setPageTitle(I18N::translate('Delete inactive users')) ->pageHeader(); ?>

    getPageTitle(); ?>

    '; echo ''; // Check users not logged in too long $ucnt = 0; foreach (User::all() as $user) { if ($user->getPreference('sessiontime') === '0') { $datelogin = (int) $user->getPreference('reg_timestamp'); } else { $datelogin = (int) $user->getPreference('sessiontime'); } if (mktime(0, 0, 0, (int) date('m') - $month, (int) date('d'), (int) date('Y')) > $datelogin && $user->getPreference('verified') && $user->getPreference('verified_by_admin')) { $ucnt++; ?> getPreference('reg_timestamp')) > 604800) && !$user->getPreference('verified')) { $ucnt++; ?> getUserId() !== Auth::id() && !$user->getPreference('verified_by_admin') && $user->getPreference('verified')) { $ucnt++; ?>
    ', I18N::translate('Number of months since the last sign-in for a user’s account to be considered inactive: '), '
    getUserName()); ?> — getRealNameHtml(); ?> display(); ?>
    getUserName()); ?> — getRealNameHtml(); ?>
    getUserName()); ?> — getRealNameHtml(); ?>

    getUserId()) == '1') { Log::addAuthenticationLog('Deleted user: ' . $user->getUserName()); $user->delete(); I18N::translate('The user %s has been deleted.', Filter::escapeHtml($user->getUserName())); } } header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME); break; default: $controller ->setPageTitle(I18N::translate('User administration')) ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL) ->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL) ->addInlineJavascript(' jQuery(".table-user-list").dataTable({ ' . I18N::datatablesI18N() . ', stateSave: true, stateDuration: 300, processing: true, serverSide: true, ajax: { "url": "' . WT_SCRIPT_NAME . '?action=load_json", "type": "POST" }, search: { search: "' . Filter::escapeJs(Filter::get('filter')) . '" }, autoWidth: false, pageLength: ' . Auth::user()->getPreference('admin_users_page_size', 10) . ', sorting: [[2, "asc"]], columns: [ /* details */ { sortable: false }, /* user-id */ { visible: false }, /* user_name */ null, /* real_name */ null, /* email */ null, /* language */ null, /* registered (sort) */ { visible: false }, /* registered */ { dataSort: 7 }, /* last_login (sort) */ { visible: false }, /* last_login */ { dataSort: 9 }, /* verified */ null, /* approved */ null ] }) .fnFilter("' . Filter::get('filter') . '"); // View the details of a newly created user ') ->pageHeader(); ?>

    getPageTitle(); ?>