. */ 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::installedThemes())), ''); $language = Filter::post('language'); $contact_method = Filter::post('contact_method'); $comment = Filter::post('comment'); $auto_accept = Filter::postBool('auto_accept'); $admin = Filter::postBool('admin'); $visible_online = Filter::postBool('visible_online'); $verified = Filter::postBool('verified'); $approved = Filter::postBool('approved'); if ($user_id === 0) { // Create a new user if (User::findByIdentifier($username)) { FlashMessages::addMessage(I18N::translate('Duplicate user name. A user with that user name already exists. Please choose another user name.')); } elseif (User::findByIdentifier($email)) { FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.')); } elseif ($pass1 !== $pass2) { FlashMessages::addMessage(I18N::translate('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 login by accessing the following link: %s', WT_BASE_URL, WT_BASE_URL) ); } $user ->setPreference('theme', $theme) ->setPreference('language', $language) ->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('admin', $admin ? '1' : '0'); } foreach (Tree::getAll() as $tree) { $tree->setUserPreference($user, 'gedcomid', Filter::post('gedcomid' . $tree->getTreeId(), WT_REGEX_XREF)); $tree->setUserPreference($user, 'rootid', Filter::post('rootid' . $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, us2.setting_value, us3.setting_value, us3.setting_value, 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 $locale) { $installed_languages[$locale->languageTag()] = $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[2] is the user name $datum[2] = '' . Filter::escapeHtml($datum[2]) . ''; // $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[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[7] ? FunctionsDate::formatTimestamp($datum[7]) : ''; 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]) . '
    ' . 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 new 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 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()=="") { alert("' . I18N::translate('You must specify an individual record before you can restrict the user to their immediate family.') . '"); jQuery(this).val(""); } }); function regex_quote(str) { return str.replace(/[\\\\.?+*()[\](){}|]/g, "\\\\$&"); } '); ?>

    getPageTitle(); ?>

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

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


    When this box is unchecked, you will be completely invisible to others, and you will also not be able to see other online users. When this box is checked, exactly the opposite is true. You will be visible to others, and you will also be able to see others who are configured to be visible.'); ?>

    getPreference('contactmethod')); ?>

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

    getTitleHtml(); ?> getTreeId(), '', $tree); ?> 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('approved')) { $ucnt++; ?> getPreference('reg_timestamp')) > 604800) && !$user->getPreference('verified')) { $ucnt++; ?> getUserId() !== Auth::id() && !$user->getPreference('approved') && $user->getPreference('verified')) { $ucnt++; ?>
    ', I18N::translate('Number of months since the last login 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() . ', 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 details of a newly created user ') ->pageHeader(); ?>

    getPageTitle(); ?>