where('id', $id);
$curUser = Hooks::fireDB('model.profile.change_pass_find_user', $curUser);
$curUser = $curUser->findOne();
$authorized = false;
if (!empty($curUser['password'])) {
$oldPasswordHash = Utils::passwordHash($oldPassword);
if (Utils::passwordVerify($oldPassword, $curUser['password']) || User::isAdminMod()) {
$authorized = true;
}
}
if (!$authorized) {
throw new Error(__('Wrong pass'), 403);
}
$newPasswordHash = Utils::passwordHash($newPassword1);
$updatePassword = DB::table('users')
->where('id', $id)
->findOne()
->set('password', $newPasswordHash);
$updatePassword = Hooks::fireDB('model.profile.change_pass_query', $updatePassword);
$updatePassword = $updatePassword->save();
if (User::get()->id == $id) {
$expire = time() + ForumSettings::get('o_timeout_visit');
$jwt = AuthModel::generateJwt(User::get(), $expire);
AuthModel::setCookie('Bearer '.$jwt, $expire);
}
Hooks::fire('model.profile.change_pass');
return Router::redirect(Router::pathFor('profileSection', ['id' => $id, 'section' => 'essentials']), __('Pass updated redirect'));
}
public function changeEmail($id)
{
$id = Hooks::fire('model.profile.change_email_start', $id);
if (Input::query('key')) {
$key = Input::query('key');
$key = Hooks::fire('model.profile.change_email_key', $key);
$newEmailKey = DB::table('users')
->where('id', $id);
$newEmailKey = Hooks::fireDB('model.profile.change_email_key_query', $newEmailKey);
$newEmailKey = $newEmailKey->findOneCol('activate_key');
if ($key == '' || $key != $newEmailKey) {
throw new Error(__('Email key bad').' '.Utils::escape(ForumSettings::get('o_admin_email')).'.', 400, true, true);
} else {
$updateMail = DB::table('users')
->where('id', $id)
->findOne()
->setExpr('email', 'activate_string')
->setExpr('activate_string', 'NULL')
->setExpr('activate_key', 'NULL');
$updateMail = Hooks::fireDB('model.profile.change_email_query', $updateMail);
$updateMail = $updateMail->save();
Hooks::fire('model.profile.change_email_updated');
return Router::redirect(Router::pathFor('home'), __('Email updated'));
}
} elseif (Request::isPost()) {
Hooks::fire('model.profile.change_email_post');
if (!Utils::passwordVerify(Input::post('req_password'), User::get()->password)) {
throw new Error(__('Wrong pass'));
}
// Validate the email address
$newEmail = strtolower(Utils::trim(Input::post('req_new_email')));
$newEmail = Hooks::fire('model.profile.change_email_new_email', $newEmail);
if (!Email::isValidEmail($newEmail)) {
throw new Error(__('Invalid email'), 400);
}
// Check if it's a banned email address
if (Email::isBannedEmail($newEmail)) {
if (ForumSettings::get('p_allow_banned_email') == '0') {
throw new Error(__('Banned email'), 403);
} elseif (ForumSettings::get('o_mailing_list') != '') {
// Load the "banned email change" template
$mailTpl = trim(file_get_contents(ForumEnv::get('FEATHER_ROOT').'featherbb/lang/'.User::getPref('language').'/mail_templates/banned_email_change.tpl'));
$mailTpl = Hooks::fire('model.profile.change_email_mail_tpl', $mailTpl);
// The first row contains the subject
$firstCrlf = strpos($mailTpl, "\n");
$mailSubject = trim(substr($mailTpl, 8, $firstCrlf-8));
$mailSubject = Hooks::fire('model.profile.change_email_mail_subject', $mailSubject);
$mailMessage = trim(substr($mailTpl, $firstCrlf));
$mailMessage = str_replace('', User::get()->username, $mailMessage);
$mailMessage = str_replace('', $newEmail, $mailMessage);
$mailMessage = str_replace('', Router::pathFor('userProfile', ['id' => $id]), $mailMessage);
$mailMessage = str_replace('', ForumSettings::get('o_board_title'), $mailMessage);
$mailMessage = Hooks::fire('model.profile.change_email_mail_message', $mailMessage);
Email::send(ForumSettings::get('o_mailing_list'), $mailSubject, $mailMessage);
}
}
// Check if someone else already has registered with that email address
$result['select'] = ['id', 'username'];
$result = DB::table('users')
->selectMany($result['select'])
->where('email', $newEmail);
$result = Hooks::fireDB('model.profile.change_email_check_mail', $result);
$result = $result->findMany();
if ($result) {
if (ForumSettings::get('p_allow_dupe_email') == '0') {
throw new Error(__('Dupe email'), 400);
} elseif (ForumSettings::get('o_mailing_list') != '') {
foreach ($result as $curDupe) {
$dupeList[] = $curDupe['username'];
}
// Load the "dupe email change" template
$mailTpl = trim(file_get_contents(ForumEnv::get('FEATHER_ROOT').'featherbb/lang/'.User::getPref('language').'/mail_templates/dupe_email_change.tpl'));
$mailTpl = Hooks::fire('model.profile.change_email_mail_dupe_tpl', $mailTpl);
// The first row contains the subject
$firstCrlf = strpos($mailTpl, "\n");
$mailSubject = trim(substr($mailTpl, 8, $firstCrlf-8));
$mailSubject = Hooks::fire('model.profile.change_email_mail_dupe_subject', $mailSubject);
$mailMessage = trim(substr($mailTpl, $firstCrlf));
$mailMessage = str_replace('', User::get()->username, $mailMessage);
$mailMessage = str_replace('', implode(', ', $dupeList), $mailMessage);
$mailMessage = str_replace('', Router::pathFor('userProfile', ['id' => $id]), $mailMessage);
$mailMessage = str_replace('', ForumSettings::get('o_board_title'), $mailMessage);
$mailMessage = Hooks::fire('model.profile.change_email_mail_dupe_message', $mailMessage);
Email::send(ForumSettings::get('o_mailing_list'), $mailSubject, $mailMessage);
}
}
$newEmailKey = Random::pass(8);
$newEmailKey = Hooks::fire('model.profile.change_email_new_email_key', $newEmailKey);
// Update the user
unset($user);
$user['update'] = [
'activate_string' => $newEmail,
'activate_key' => $newEmailKey,
];
$user = DB::table('users')
->where('id', $id)
->findOne()
->set($user['update']);
$user = Hooks::fireDB('model.profile.change_email_user_query', $user);
$user = $user->save();
// Load the "activate email" template
$mailTpl = trim(file_get_contents(ForumEnv::get('FEATHER_ROOT').'featherbb/lang/'.User::getPref('language').'/mail_templates/activate_email.tpl'));
$mailTpl = Hooks::fire('model.profile.change_email_mail_activate_tpl', $mailTpl);
// The first row contains the subject
$firstCrlf = strpos($mailTpl, "\n");
$mailSubject = trim(substr($mailTpl, 8, $firstCrlf-8));
$mailSubject = Hooks::fire('model.profile.change_email_mail_activate_subject', $mailSubject);
$mailMessage = trim(substr($mailTpl, $firstCrlf));
$mailMessage = str_replace('', User::get()->username, $mailMessage);
$mailMessage = str_replace('', Url::base(), $mailMessage);
$mailMessage = str_replace('', Router::pathFor('profileAction', ['id' => $id, 'action' => 'change_email'], ['key' => $newEmailKey]), $mailMessage);
$mailMessage = str_replace('', ForumSettings::get('o_board_title'), $mailMessage);
$mailMessage = Hooks::fire('model.profile.change_email_mail_activate_message', $mailMessage);
Email::send($newEmail, $mailSubject, $mailMessage);
Hooks::fire('model.profile.change_email_sent');
$message = __('Activate email sent').' '.Utils::escape(ForumSettings::get('o_admin_email')).'.';
return Router::redirect(Router::pathFor('userProfile', ['id' => $id]), $message);
}
}
public function uploadAvatar($id, $filesData)
{
$filesData = Hooks::fire('model.profile.upload_avatar_start', $filesData, $id);
if (!isset($filesData['req_file'])) {
throw new Error(__('No file'));
}
$uploadedFile = $filesData['req_file'];
// Make sure the upload went smooth
if (isset($uploadedFile['error'])) {
switch ($uploadedFile['error']) {
case 1: // UPLOAD_ERR_INI_SIZE
case 2: // UPLOAD_ERR_FORM_SIZE
throw new Error(__('Too large ini'));
break;
case 3: // UPLOAD_ERR_PARTIAL
throw new Error(__('Partial upload'));
break;
case 4: // UPLOAD_ERR_NO_FILE
throw new Error(__('No file'));
break;
case 6: // UPLOAD_ERR_NO_TMP_DIR
throw new Error(__('No tmp directory'));
break;
default:
// No error occured, but was something actually uploaded?
if ($uploadedFile['size'] == 0) {
throw new Error(__('No file'));
}
break;
}
}
if (is_uploaded_file($uploadedFile['tmp_name'])) {
$uploadedFile = Hooks::fire('model.profile.upload_avatar_is_uploaded_file', $uploadedFile);
// Preliminary file check, adequate in most cases
$allowedTypes = ['image/gif', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png'];
if (!in_array($uploadedFile['type'], $allowedTypes)) {
throw new Error(__('Bad type'));
}
// Make sure the file isn't too big
if ($uploadedFile['size'] > ForumSettings::get('o_avatars_size')) {
throw new Error(__('Too large').' '.Utils::forumNumberFormat(ForumSettings::get('o_avatars_size')).' '.__('bytes').'.');
}
// Move the file to the avatar directory. We do this before checking the width/height to circumvent open_basedir restrictions
if (!@move_uploaded_file($uploadedFile['tmp_name'], ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.'.tmp')) {
throw new Error(__('Move failed').' '.Utils::escape(ForumSettings::get('o_admin_email')).'.', 400, true, true);
}
list($width, $height, $type, ) = @getimagesize(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.'.tmp');
// Determine type
if ($type == IMAGETYPE_GIF) {
$extension = '.gif';
} elseif ($type == IMAGETYPE_JPEG) {
$extension = '.jpg';
} elseif ($type == IMAGETYPE_PNG) {
$extension = '.png';
} else {
// Invalid type
@unlink(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.'.tmp');
throw new Error(__('Bad type'));
}
// Now check the width/height
if (empty($width) || empty($height) || $width > ForumSettings::get('o_avatars_width') || $height > ForumSettings::get('o_avatars_height')) {
@unlink(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.'.tmp');
throw new Error(__('Too wide or high').' '.ForumSettings::get('o_avatars_width').'x'.ForumSettings::get('o_avatars_height').' '.__('pixels').'.');
}
// Delete any old avatars and put the new one in place
$this->deleteAvatar($id);
@rename(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.'.tmp', ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.$extension);
@chmod(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$id.$extension, 0644);
} else {
throw new Error(__('Unknown failure'));
}
$uploadedFile = Hooks::fire('model.profile.upload_avatar', $uploadedFile);
return Router::redirect(Router::pathFor('profileSection', ['id' => $id, 'section' => 'personality']), __('Avatar upload redirect'));
}
//
// Deletes any avatars owned by the specified user ID
//
public function deleteAvatar($userId)
{
$filetypes = ['jpg', 'gif', 'png'];
// Delete user avatar
foreach ($filetypes as $curType) {
if (file_exists(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$userId.'.'.$curType)) {
@unlink(ForumEnv::get('FEATHER_ROOT').ForumSettings::get('o_avatars_dir').'/'.$userId.'.'.$curType);
}
}
}
public function updateGroupMembership($id)
{
$id = Hooks::fire('model.profile.update_group_membership_start', $id);
$newGroupId = intval(Input::post('group_id'));
$oldGroupId = DB::table('users')
->where('id', $id);
$oldGroupId = Hooks::fireDB('model.profile.update_group_membership_old_group', $oldGroupId);
$oldGroupId = $oldGroupId->findOneCol('group_id');
$updateGroup = DB::table('users')
->where('id', $id)
->findOne()
->set('group_id', $newGroupId);
$updateGroup = Hooks::fireDB('model.profile.update_group_membership_update_group', $updateGroup);
$updateGroup = $updateGroup->save();
// Regenerate the users info cache
if (!CacheInterface::isCached('users_info')) {
CacheInterface::store('users_info', Cache::getUsersInfo());
}
$stats = CacheInterface::retrieve('users_info');
if ($oldGroupId == ForumEnv::get('FEATHER_ADMIN') || $newGroupId == ForumEnv::get('FEATHER_ADMIN')) {
CacheInterface::store('admin_ids', Cache::getAdminIds());
}
// If the user was a moderator or an administrator, we remove him/her from the moderator list in all forums as well
if ($newGroupId != ForumEnv::get('FEATHER_ADMIN') && !Perms::getGroupPermissions($newGroupId, 'mod.is_mod')) {
// Loop through all forums
$result = $this->loopModForums();
foreach ($result as $curForum) {
$curModerators = ($curForum['moderators'] != '') ? unserialize($curForum['moderators']) : [];
if (in_array($id, $curModerators)) {
$username = array_search($id, $curModerators);
unset($curModerators[$username]);
$updateForums = DB::table('forums')
->where('id', $curForum['id'])
->findOne();
if (!empty($curModerators)) {
$updateForums = $updateForums->set('moderators', serialize($curModerators));
} else {
$updateForums = $updateForums->setExpr('moderators', 'NULL');
}
$updateForums = Hooks::fireDB('model.profile.update_group_membership_mod_forums', $updateForums);
$updateForums = $updateForums->save();
}
}
}
$id = Hooks::fire('model.profile.update_group_membership', $id);
return Router::redirect(Router::pathFor('profileSection', ['id' => $id, 'section' => 'admin']), __('Group membership redirect'));
}
public function getUsername($id)
{
// Get the username of the user we are processing
$username = DB::table('users')
->where('id', $id)
->findOneCol('username');
$username = Hooks::fire('model.profile.get_username', $username);
return $username;
}
public function loopModForums()
{
$result['select'] = ['id', 'moderators'];
$result = DB::table('forums')
->selectMany($result['select']);
$result = Hooks::fireDB('model.profile.loop_mod_forums', $result);
$result = $result->findMany();
return $result;
}
public function updateModForums($id)
{
$username = $this->getUsername($id);
$moderatorIn = (Input::post('moderator_in')) ? array_keys(Input::post('moderator_in')) : [];
// Loop through all forums
$result = $this->loopModForums();
foreach ($result as $curForum) {
$curModerators = ($curForum['moderators'] != '') ? unserialize($curForum['moderators']) : [];
// If the user should have moderator access (and he/she doesn't already have it)
if (in_array($curForum['id'], $moderatorIn) && !in_array($id, $curModerators)) {
$curModerators[$username] = $id;
uksort($curModerators, [$this, 'utf8_strcasecmp']);
$updateForums = DB::table('forums')
->where('id', $curForum['id'])
->findOne()
->set('moderators', serialize($curModerators));
$updateForums = Hooks::fireDB('model.profile.update_mod_forums_query', $updateForums);
$updateForums = $updateForums->save();
}
// If the user shouldn't have moderator access (and he/she already has it)
elseif (!in_array($curForum['id'], $moderatorIn) && in_array($id, $curModerators)) {
unset($curModerators[$username]);
$updateForums = DB::table('forums')
->where('id', $curForum['id'])
->findOne();
if (!empty($curModerators)) {
$updateForums = $updateForums->set('moderators', serialize($curModerators));
} else {
$updateForums = $updateForums->setExpr('moderators', 'NULL');
}
$updateForums = Hooks::fireDB('model.profile.update_mod_forums_query', $updateForums);
$updateForums = $updateForums->save();
}
}
$id = Hooks::fire('model.profile.update_mod_forums', $id);
return Router::redirect(Router::pathFor('profileSection', ['id' => $id, 'section' => 'admin']), __('Update forums redirect'));
}
public function banUser($id)
{
$id = Hooks::fire('model.profile.ban_user_start', $id);
// Get the username of the user we are banning
$username = $this->getUsername($id);
// Check whether user is already banned
$banId = DB::table('bans')
->where('username', $username)
->orderByExpr('expire IS NULL DESC')
->orderByDesc('expire');
$banId = Hooks::fireDB('model.profile.ban_user_query', $banId);
$banId = $banId->findOneCol('id');
if ($banId) {
return Router::redirect(Router::pathFor('editBan', ['id' => $banId]), __('Ban redirect'));
} else {
return Router::redirect(Router::pathFor('addBan', ['id' => $id]), __('Ban redirect'));
}
}
public function promoteUser($id, $pid)
{
$id = Hooks::fire('model.profile.promote_user.user_id', $id);
$pid = Hooks::fire('model.profile.promote_user.post_id', $pid);
// Find the group ID to promote the user to
$nextGroupId = Hooks::fire('model.profile.promote_user.next_group_id', User::getPref('promote.next_group', $id));
if (!$nextGroupId) {
throw new Error(__('Bad request'), 404);
}
// Update the user
$updateUser = DB::table('users')
->where('id', $id)
->findOne()
->set('group_id', $nextGroupId);
$updateUser = Hooks::fireDB('model.profile.promote_user_query', $updateUser);
$updateUser = $updateUser->save();
// Get topic infos to redirect to
$topicInfos = DB::table('posts')
->tableAlias('p')
->selectMany(['t.subject', 't.id'])
->innerJoin('topics', ['t.id', '=', 'p.topic_id'], 't')
->where('p.id', $pid)
->findOne();
return Router::redirect(Router::pathFor('viewPost', ['id' => $topicInfos->id, 'name' => Url::slug($topicInfos->subject), 'pid' => $pid]).'#p'.$pid, __('User promote redirect'));
}
public function deleteUser($id)
{
$id = Hooks::fire('model.profile.delete_user_start', $id);
// Get the username and group of the user we are deleting
$result['select'] = ['group_id', 'username'];
$result = DB::table('users')
->where('id', $id)
->selectMany($result['select']);
$result = Hooks::fireDB('model.profile.delete_user_username', $result);
$result = $result->findOne();
$groupId = $result['group_id'];
$username = $result['username'];
if ($groupId == ForumEnv::get('FEATHER_ADMIN')) {
throw new Error(__('No delete admin message'));
}
if (Input::post('delete_user_comply')) {
// If the user is a moderator or an administrator, we remove him/her from the moderator list in all forums as well
if ($groupId == ForumEnv::get('FEATHER_ADMIN') || Perms::getGroupPermissions($groupId, 'mod.is_mod')) {
// Loop through all forums
$result = $this->loopModForums();
foreach ($result as $curForum) {
$curModerators = ($curForum['moderators'] != '') ? unserialize($curForum['moderators']) : [];
if (in_array($id, $curModerators)) {
unset($curModerators[$username]);
$updateForums = DB::table('forums')
->where('id', $curForum['id'])
->findOne();
if (!empty($curModerators)) {
$updateForums = $updateForums->set('moderators', serialize($curModerators));
} else {
$updateForums = $updateForums->setExpr('moderators', 'NULL');
}
$updateForums = Hooks::fireDB('model.profile.update_mod_forums_query', $updateForums);
$updateForums = $updateForums->save();
}
}
}
// Delete any subscriptions
$deleteSubscriptions = DB::table('topic_subscriptions')
->where('user_id', $id);
$deleteSubscriptions = Hooks::fireDB('model.profile.delete_user_subscriptions_topic', $deleteSubscriptions);
$deleteSubscriptions = $deleteSubscriptions->deleteMany();
unset($deleteSubscriptions);
$deleteSubscriptions = DB::table('forum_subscriptions')
->where('user_id', $id);
$deleteSubscriptions = Hooks::fireDB('model.profile.delete_user_subscriptions_forum', $deleteSubscriptions);
$deleteSubscriptions = $deleteSubscriptions->deleteMany();
// Remove him/her from the online list (if they happen to be logged in)
$deleteOnline = DB::table('online')
->where('user_id', $id);
$deleteOnline = Hooks::fireDB('model.profile.delete_user_online', $deleteOnline);
$deleteOnline = $deleteOnline->deleteMany();
// Should we delete all posts made by this user?
if (Input::post('delete_posts')) {
// Hold on, this could take some time!
@set_time_limit(0);
Hooks::fire('model.profile.delete_user_posts');
// Find all posts made by this user
unset($result);
$result['select'] = ['p.id', 'p.topic_id', 't.forum_id'];
$result = DB::table('posts')
->tableAlias('p')
->selectMany($result['select'])
->innerJoin('topics', ['t.id', '=', 'p.topic_id'], 't')
->innerJoin('forums', ['f.id', '=', 't.forum_id'], 'f')
->where('p.poster_id', $id);
$result = Hooks::fireDB('model.profile.delete_user_posts_first_query', $result);
$result = $result->findMany();
if ($result) {
foreach ($result as $curPost) {
// Determine whether this post is the "topic post" or not
$result2 = DB::table('posts')
->where('topic_id', $curPost['topic_id'])
->orderBy('posted');
$result2 = Hooks::fireDB('model.profile.delete_user_posts_second_query', $result2);
$result2 = $result2->findOneCol('id');
if ($result2 == $curPost['id']) {
Topic::delete($curPost['topic_id']);
} else {
Post::delete($curPost['id'], $curPost['topic_id']);
}
Forum::update($curPost['forum_id']);
}
}
} else {
// Set all his/her posts to guest
$updateGuest = DB::table('posts')
->whereIn('poster_id', '1');
$updateGuest = Hooks::fireDB('model.profile.delete_user_posts_guest_query', $updateGuest);
$updateGuest = $updateGuest->updateMany('poster_id', $id);
}
// Delete the user
$deleteUser = DB::table('users')
->where('id', $id);
$deleteUser = $deleteUser->deleteMany();
// Delete user avatar
$this->deleteAvatar($id);
// Regenerate the users info cache
CacheInterface::store('users_info', Cache::getUsersInfo());
$stats = CacheInterface::retrieve('users_info');
if ($groupId == ForumEnv::get('FEATHER_ADMIN')) {
CacheInterface::store('admin_ids', Cache::getAdminIds());
}
Hooks::fire('model.profile.delete_user');
return Router::redirect(Router::pathFor('home'), __('User delete redirect'));
}
}
public function getUserGroup($id)
{
$info = [];
$info['select'] = ['old_username' => 'u.username', 'group_id' => 'u.group_id'];
$info = DB::table('users')
->tableAlias('u')
->selectMany($info['select'])
->leftOuterJoin('groups', ['g.g_id', '=', 'u.group_id'], 'g')
->where('u.id', $id);
$info = Hooks::fireDB('model.profile.fetch_user_group', $info);
$info = $info->findOne();
if (!$info) {
throw new Error(__('Bad request'), 404);
}
return $info;
}
public function updateProfile($id, $info, $section)
{
$info = Hooks::fire('model.profile.update_profile_start', $info, $id, $section);
$section = Hooks::fire('model.profile.update_profile_section', $section, $id, $info);
$usernameUpdated = false;
$form = $prefs = [];
// Validate input depending on section
switch ($section) {
case 'essentials':
{
$prefs = [
'timezone' => floatval(Input::post('form_timezone')),
'dst' => Input::post('form_dst') ? '1' : '0',
'time_format' => Input::post('form_time_format'),
'date_format' => Input::post('form_date_format'),
];
// Make sure we got a valid language string
if (Input::post('form_language')) {
$languages = \FeatherBB\Core\Lister::getLangs();
$prefs['language'] = Utils::trim(Input::post('form_language'));
if (!in_array($prefs['language'], $languages)) {
throw new Error(__('Bad request'), 404);
}
}
if (User::isAdminMod()) {
$form['admin_note'] = Utils::trim(Input::post('admin_note'));
// Are we allowed to change usernames?
if (User::isAdmin() || (User::isAdminMod() && User::can('mod.rename_users'))) {
$form['username'] = Utils::trim(Input::post('req_username'));
if ($form['username'] != $info['old_username']) {
$errors = [];
$errors = $this->checkUsername($form['username'], $errors, $id);
if (!empty($errors)) {
throw new Error($errors[0]);
}
$usernameUpdated = true;
}
}
// We only allow administrators to update the post count
if (User::isAdmin()) {
$form['num_posts'] = intval(Input::post('num_posts'));
}
}
if (ForumSettings::get('o_regs_verify') == '0' || User::isAdminMod()) {
// Validate the email address
$form['email'] = strtolower(Utils::trim(Input::post('req_email')));
if (!Email::isValidEmail($form['email'])) {
throw new Error(__('Invalid email'));
}
}
break;
}
case 'personal':
{
$form = [
'realname' => Input::post('form_realname') ? Utils::trim(Input::post('form_realname')) : '',
'url' => Input::post('form_url') ? Utils::trim(Input::post('form_url')) : '',
'location' => Input::post('form_location') ? Utils::trim(Input::post('form_location')) : '',
];
// Add http:// if the URL doesn't contain it already (while allowing https://, too)
if (User::can('post.links')) {
if ($form['url'] != '') {
$url = Url::isValid($form['url']);
if ($url === false) {
throw new Error(__('Invalid website URL'));
}
$form['url'] = $url['url'];
}
} else {
if (!empty($form['url'])) {
throw new Error(__('Website not allowed'));
}
$form['url'] = '';
}
if (User::isAdmin()) {
$form['title'] = Utils::trim(Input::post('title'));
} elseif (User::can('user.set_title')) {
$form['title'] = Utils::trim(Input::post('title'));
if ($form['title'] != '') {
// A list of words that the title may not contain
// If the language is English, there will be some duplicates, but it's not the end of the world
$forbidden = ['member', 'moderator', 'administrator', 'banned', 'guest', \utf8\to_lower(__('Member')), \utf8\to_lower(__('Moderator')), \utf8\to_lower(__('Administrator')), \utf8\to_lower(__('Banned')), \utf8\to_lower(__('Guest'))];
if (in_array(\utf8\to_lower($form['title']), $forbidden)) {
throw new Error(__('Forbidden title'));
}
}
}
break;
}
case 'personality':
{
$form = [];
// Clean up signature from POST
if (ForumSettings::get('o_signatures') == '1') {
$form['signature'] = Utils::linebreaks(Utils::trim(Input::post('signature')));
// Validate signature
if (Utils::strlen($form['signature']) > ForumSettings::get('p_sig_length')) {
throw new Error(sprintf(__('Sig too long'), ForumSettings::get('p_sig_length'), Utils::strlen($form['signature']) - ForumSettings::get('p_sig_length')));
} elseif (substr_count($form['signature'], "\n") > (ForumSettings::get('p_sig_lines')-1)) {
throw new Error(sprintf(__('Sig too many lines'), ForumSettings::get('p_sig_lines')));
} elseif ($form['signature'] && ForumSettings::get('p_sig_all_caps') == '0' && Utils::isAllUppercase($form['signature']) && !User::isAdminMod()) {
$form['signature'] = \utf8\ucwords(\utf8\to_lower($form['signature']));
}
// Validate BBCode syntax
if (ForumSettings::get('p_sig_bbcode') == '1') {
$errors = [];
$form['signature'] = Parser::preparseBbcode($form['signature'], $errors, true);
if (count($errors) > 0) {
throw new Error('');
}
}
}
break;
}
case 'display':
{
$prefs = [
'disp.topics' => Input::post('form_disp_topics'),
'disp.posts' => Input::post('form_disp_posts'),
'show.smilies' => Input::post('form_show_smilies') ? '1' : '0',
'show.img' => Input::post('form_show_img') ? '1' : '0',
'show.img.sig' => Input::post('form_show_img_sig') ? '1' : '0',
'show.avatars' => Input::post('form_show_avatars') ? '1' : '0',
'show.sig' => Input::post('form_show_sig') ? '1' : '0',
];
if ($prefs['disp.topics'] != '') {
$prefs['disp.topics'] = intval($prefs['disp.topics']);
if ($prefs['disp.topics'] < 3) {
$prefs['disp.topics'] = 3;
} elseif ($prefs['disp.topics'] > 75) {
$prefs['disp.topics'] = 75;
}
} else {
unset($prefs['disp.topics']);
}
if ($prefs['disp.posts'] != '') {
$prefs['disp.posts'] = intval($prefs['disp.posts']);
if ($prefs['disp.posts'] < 3) {
$prefs['disp.posts'] = 3;
} elseif ($prefs['disp.posts'] > 75) {
$prefs['disp.posts'] = 75;
}
} else {
unset($prefs['disp.posts']);
}
// Make sure we got a valid style string
if (Input::post('form_style')) {
$styles = \FeatherBB\Core\Lister::getStyles();
$prefs['style'] = Utils::trim(Input::post('form_style'));
if (!in_array($prefs['style'], $styles)) {
$prefs['style'] = ForumSettings::get('style');
}
}
break;
}
case 'privacy':
{
$prefs = [
'email.setting' => intval(Input::post('form_email_setting')),
'notify_with_post' => Input::post('form_notify_with_post') ? '1' : '0',
'auto_notify' => Input::post('form_auto_notify') ? '1' : '0',
];
if ($prefs['email.setting'] < 0 || $prefs['email.setting'] > 2) {
$prefs['email.setting'] = ForumSettings::get('email.setting');
}
break;
}
default:
throw new Error(__('Bad request'), 404);
}
$form = Hooks::fire('model.profile.update_profile_form', $form, $section, $id, $info);
// Single quotes around non-empty values and nothing for empty values
$temp = [];
foreach ($form as $key => $input) {
$temp[$key] = $input;
}
if (empty($temp) && empty($prefs)) {
throw new Error(__('Bad request'), 404);
}
// Update general user infos
$updateUser = DB::table('users')
->where('id', $id)
->findOne()
->set($temp);
$updateUser = Hooks::fireDB('model.profile.update_profile_query', $updateUser);
$updateUser = $updateUser->save();
// Update user prefs
if (!empty($prefs)) {
Prefs::setUser($id, $prefs);
}
// If we changed the username we have to update some stuff
if ($usernameUpdated) {
$bansUpdated = DB::table('bans')
->where('username', $info['old_username']);
$bansUpdated = Hooks::fireDB('model.profile.update_profile_bans_updated', $bansUpdated);
$bansUpdated = $bansUpdated->updateMany('username', $form['username']);
$updatePosterId = DB::table('posts')
->where('poster_id', $id);
$updatePosterId = Hooks::fireDB('model.profile.update_profile_poster_id', $updatePosterId);
$updatePosterId = $updatePosterId->updateMany('poster', $form['username']);
$updatePosts = DB::table('posts')
->where('edited_by', $info['old_username']);
$updatePosts = Hooks::fireDB('model.profile.update_profile_posts', $updatePosts);
$updatePosts = $updatePosts->updateMany('edited_by', $form['username']);
$updateTopicsPoster = DB::table('topics')
->where('poster', $info['old_username']);
$updateTopicsPoster = Hooks::fireDB('model.profile.update_profile_topics_poster', $updateTopicsPoster);
$updateTopicsPoster = $updateTopicsPoster->updateMany('poster', $form['username']);
$updateTopicsLastPoster = DB::table('topics')
->where('last_poster', $info['old_username']);
$updateTopicsLastPoster = Hooks::fireDB('model.profile.update_profile_topics_last_poster', $updateTopicsLastPoster);
$updateTopicsLastPoster = $updateTopicsLastPoster->updateMany('last_poster', $form['username']);
$updateForums = DB::table('forums')
->where('last_poster', $info['old_username']);
$updateForums = Hooks::fireDB('model.profile.update_profile_forums', $updateForums);
$updateForums = $updateForums->updateMany('last_poster', $form['username']);
$updateOnline = DB::table('online')
->where('ident', $info['old_username']);
$updateOnline = Hooks::fireDB('model.profile.update_profile_online', $updateOnline);
$updateOnline = $updateOnline->updateMany('ident', $form['username']);
// If the user is a moderator or an administrator we have to update the moderator lists
$groupId = DB::table('users')
->where('id', $id);
$groupId = Hooks::fireDB('model.profile.update_profile_group_id', $groupId);
$groupId = $groupId->findOneCol('group_id');
if ($groupId == ForumEnv::get('FEATHER_ADMIN') || Perms::getGroupPermissions($groupId, 'mod.is_mod')) {
// Loop through all forums
$result = $this->loopModForums();
foreach ($result as $curForum) {
$curModerators = ($curForum['moderators'] != '') ? unserialize($curForum['moderators']) : [];
if (in_array($id, $curModerators)) {
unset($curModerators[$info['old_username']]);
$curModerators[$form['username']] = $id;
uksort($curModerators, [$this, 'utf8_strcasecmp']);
$updateMods = DB::table('forums')
->where('id', $curForum['id'])
->findOne()
->set('moderators', serialize($curModerators));
$updateMods = Hooks::fireDB('model.profile.update_profile_mods', $updateMods);
$updateMods = $updateMods->save();
}
}
}
// Regenerate the users info cache
if (!CacheInterface::isCached('users_info')) {
CacheInterface::store('users_info', Cache::getUsersInfo());
}
$stats = CacheInterface::retrieve('users_info');
// Check if the bans table was updated and regenerate the bans cache when needed
if ($bansUpdated) {
CacheInterface::store('bans', Cache::getBans());
}
}
$section = Hooks::fireDB('model.profile.update_profile', $section, $id);
return Router::redirect(Router::pathFor('profileSection', ['id' => $id, 'section' => $section]), __('Profile redirect'));
}
public function getUserInfo($id)
{
$user['select'] = ['u.id', 'u.group_id', 'u.username', 'u.email', 'u.title', 'u.realname', 'u.url', 'u.location', 'u.signature', 'u.num_posts', 'u.last_post', 'u.registered', 'u.registration_ip', 'u.admin_note', 'u.last_visit', 'g.g_id', 'g.g_user_title'];
$user = DB::table('users')
->tableAlias('u')
->selectMany($user['select'])
->leftOuterJoin('groups', ['g.g_id', '=', 'u.group_id'], 'g')
->where('u.id', $id);
$user = Hooks::fireDB('model.profile.get_user_info', $user);
$user = $user->findOne();
if (!$user) {
throw new Error(__('Bad request'), 404);
}
$user['prefs'] = ($id == User::get()->id) ? User::get()->prefs : Prefs::loadPrefs($user);
return $user;
}
public function parseUserInfo($user)
{
$userInfo = [];
$userInfo = Hooks::fire('model.profile.parse_user_info_start', $userInfo, $user);
$userInfo['personal'][] = ''.__('Username').'';
$userInfo['personal'][] = ''.Utils::escape($user['username']).'';
$userTitleField = Utils::getTitle($user);
$userInfo['personal'][] = ''.__('Title').'';
$userInfo['personal'][] = ''.((ForumSettings::get('o_censoring') == '1') ? Utils::censor($userTitleField) : $userTitleField).'';
if ($user['realname'] != '') {
$userInfo['personal'][] = ''.__('Realname').'';
$userInfo['personal'][] = ''.Utils::escape((ForumSettings::get('o_censoring') == '1') ? Utils::censor($user['realname']) : $user['realname']).'';
}
if ($user['location'] != '') {
$userInfo['personal'][] = ''.__('Location').'';
$userInfo['personal'][] = ''.Utils::escape((ForumSettings::get('o_censoring') == '1') ? Utils::censor($user['location']) : $user['location']).'';
}
if ($user['url'] != '') {
$user['url'] = Utils::escape((ForumSettings::get('o_censoring') == '1') ? Utils::censor($user['url']) : $user['url']);
$userInfo['personal'][] = ''.__('Website').'';
$userInfo['personal'][] = ''.$user['url'].'';
}
if ($user['prefs']['email.setting'] == '0' && !User::get()->is_guest && User::can('email.send')) {
$user['email_field'] = ''.Utils::escape($user['email']).'';
} elseif ($user['prefs']['email.setting'] == '1' && !User::get()->is_guest && User::can('email.send')) {
$user['email_field'] = ''.__('Send email').'';
} else {
$user['email_field'] = '';
}
if ($user['email_field'] != '') {
$userInfo['personal'][] = ''.__('Email').'';
$userInfo['personal'][] = ''.$user['email_field'].'';
}
if (ForumSettings::get('o_avatars') == '1') {
$avatarField = Utils::generateAvatarMarkup($user['id']);
if ($avatarField != '') {
$userInfo['personality'][] = ''.__('Avatar').'';
$userInfo['personality'][] = ''.$avatarField.'';
}
}
if (ForumSettings::get('o_signatures') == '1') {
if (isset($parsedSignature)) {
$userInfo['personality'][] = ''.__('Signature').'';
$userInfo['personality'][] = ''.$parsedSignature.'
';
}
}
$postsField = '';
if (ForumSettings::get('o_show_post_count') == '1' || User::isAdminMod()) {
$postsField = Utils::forumNumberFormat($user['num_posts']);
}
if (User::can('search.topics')) {
$quickSearches = [];
if ($user['num_posts'] > 0) {
$quickSearches[] = ''.__('Show topics').'';
$quickSearches[] = ''.__('Show posts').'';
}
if (User::isAdminMod() && ForumSettings::get('o_topic_subscriptions') == '1') {
$quickSearches[] = ''.__('Show subscriptions').'';
}
if (!empty($quickSearches)) {
$postsField .= (($postsField != '') ? ' - ' : '').implode(' - ', $quickSearches);
}
}
if ($postsField != '') {
$userInfo['activity'][] = ''.__('Posts').'';
$userInfo['activity'][] = ''.$postsField.'';
}
if ($user['num_posts'] > 0) {
$userInfo['activity'][] = ''.__('Last post').'';
$userInfo['activity'][] = ''.Utils::formatTime($user['last_post']).'';
}
$userInfo['activity'][] = ''.__('Registered').'';
$userInfo['activity'][] = ''.Utils::formatTime($user['registered'], true).'';
$userInfo = Hooks::fire('model.profile.parse_user_info', $userInfo);
return $userInfo;
}
public function editEssentials($id, $user)
{
$userDisp = [];
$userDisp = Hooks::fire('model.profile.edit_essentials_start', $userDisp, $id, $user);
if (User::isAdminMod()) {
if (User::isAdmin() || User::can('mod.rename_users')) {
$userDisp['username_field'] = ''."\n";
} else {
$userDisp['username_field'] = ''.sprintf(__('Username info'), Utils::escape($user['username'])).'
'."\n";
}
$userDisp['email_field'] = ''.__('Send email').'
'."\n";
} else {
$userDisp['username_field'] = ''.__('Username').': '.Utils::escape($user['username']).'
'."\n";
if (ForumSettings::get('o_regs_verify') == '1') {
$userDisp['email_field'] = ''.sprintf(__('Email info'), Utils::escape($user['email']).' - '.__('Change email').'').'
'."\n";
} else {
$userDisp['email_field'] = ''."\n";
}
}
$userDisp['posts_field'] = '';
$postsActions = [];
if (User::isAdmin()) {
$userDisp['posts_field'] .= '';
} elseif (ForumSettings::get('o_show_post_count') == '1' || User::isAdminMod()) {
$postsActions[] = sprintf(__('Posts info'), Utils::forumNumberFormat($user['num_posts']));
}
if (User::can('search.topics') || User::isAdmin()) {
$postsActions[] = ''.__('Show topics').'';
$postsActions[] = ''.__('Show posts').'';
if (ForumSettings::get('o_topic_subscriptions') == '1') {
$postsActions[] = ''.__('Show subscriptions').'';
}
}
$userDisp['posts_field'] .= (!empty($postsActions) ? ''.implode(' - ', $postsActions).'
' : '')."\n";
$userDisp = Hooks::fire('model.profile.edit_essentials', $userDisp);
return $userDisp;
}
public function groupList($user)
{
$output = '';
$user = Hooks::fire('model.profile.get_group_list_start', $user);
$result['select'] = ['g_id', 'g_title'];
$result = DB::table('groups')
->selectMany($result['select'])
->whereNotEqual('g_id', ForumEnv::get('FEATHER_GUEST'))
->orderBy('g_title');
$result = Hooks::fireDB('model.profile.get_group_list_query', $result);
$result = $result->findMany();
foreach ($result as $curGroup) {
if ($curGroup['g_id'] == $user['g_id'] || ($curGroup['g_id'] == ForumSettings::get('o_default_user_group') && $user['g_id'] == '')) {
$output .= "\t\t\t\t\t\t\t\t".''."\n";
} else {
$output .= "\t\t\t\t\t\t\t\t".''."\n";
}
}
$output = Hooks::fire('model.profile.get_group_list', $output);
return $output;
}
public function forumList($id)
{
$output = '';
$id = Hooks::fire('model.profile.get_forum_list_start', $id);
$result['select'] = ['cid' => 'c.id', 'c.cat_name', 'fid' => 'f.id', 'f.forum_name', 'f.moderators'];
$result['order_by'] = ['c.disp_position', 'c.id', 'f.disp_position'];
$result = DB::table('categories')
->tableAlias('c')
->selectMany($result['select'])
->innerJoin('forums', ['c.id', '=', 'f.cat_id'], 'f')
->whereNull('f.redirect_url')
->orderByMany($result['order_by']);
$result = Hooks::fireDB('model.profile.get_forum_list', $result);
$result = $result->findMany();
$curCategory = 0;
foreach ($result as $curForum) {
if ($curForum['cid'] != $curCategory) {
// A new category since last iteration?
if ($curCategory) {
$output .= "\n\t\t\t\t\t\t\t\t".'';
}
if ($curCategory != 0) {
$output .= "\n\t\t\t\t\t\t\t".''."\n";
}
$output .= "\t\t\t\t\t\t\t".''."\n\t\t\t\t\t\t\t\t".'
'.Utils::escape($curForum['cat_name']).'
'."\n\t\t\t\t\t\t\t\t".'
';
$curCategory = $curForum['cid'];
}
$moderators = ($curForum['moderators'] != '') ? unserialize($curForum['moderators']) : [];
$output .= "\n\t\t\t\t\t\t\t\t\t".'
'."\n";
}
$output = Hooks::fire('model.profile.get_forum_list', $output);
return $output;
}
private static function utf8_strcasecmp($strX, $strY)
{
$strX = \utf8\to_lower($strX);
$strY = \utf8\to_lower($strY);
return strcmp($strX, $strY);
}
//
// Check username
//
public function checkUsername($username, $errors, $excludeId = null)
{
Lang::load('register');
Lang::load('prof_reg');
// Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
$username = preg_replace('%\s+%s', ' ', $username);
// Validate username
if (Utils::strlen($username) < 2) {
$errors[] = __('Username too short');
} elseif (Utils::strlen($username) > 25) { // This usually doesn't happen since the form element only accepts 25 characters
$errors[] = __('Username too long');
} elseif (!strcasecmp($username, 'Guest') || !self::utf8_strcasecmp($username, __('Guest'))) {
$errors[] = __('Username guest');
} elseif (filter_var($username, FILTER_VALIDATE_IP)) {
$errors[] = __('Username IP');
} elseif ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false) {
$errors[] = __('Username reserved chars');
} elseif (preg_match('%(?:\[/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*|topic|post|forum|user)\]|\[(?:img|url|quote|list)=)%i', $username)) {
$errors[] = __('Username BBCode');
}
// Check username for any censored words
if (ForumSettings::get('o_censoring') == '1' && Utils::censor($username) != $username) {
$errors[] = __('Username censor');
}
// Check that the username (or a too similar username) is not already registered
$query = (!is_null($excludeId)) ? ' AND id!='.$excludeId : '';
$result = DB::table('online')->rawQuery('SELECT username FROM '.ForumEnv::get('DB_PREFIX').'users WHERE (UPPER(username)=UPPER(:username1) OR UPPER(username)=UPPER(:username2)) AND id>1'.$query, [':username1' => $username, ':username2' => Utils::ucpPregReplace('%[^\p{L}\p{N}]%u', '', $username)])->findOne();
if ($result) {
$busy = $result['username'];
$errors[] = __('Username dupe 1').' '.Utils::escape($busy).'. '.__('Username dupe 2');
}
// Check username for any banned usernames
foreach (Container::get('bans') as $curBan) {
if ($curBan['username'] != '' && \utf8\to_lower($username) == \utf8\to_lower($curBan['username'])) {
$errors[] = __('Banned username');
break;
}
}
return $errors;
}
public function getInfoMail($recipientId)
{
$recipientId = Hooks::fire('model.profile.get_info_mail_start', $recipientId);
$mail = DB::table('users')
->select('username', 'recipient')
->select('email', 'recipient_email')
->select('id')
->select('group_id')
->where('id', $recipientId);
$mail = Hooks::fireDB('model.profile.get_info_mail_query', $mail);
$mail = $mail->findOne();
if (!$mail) {
throw new Error(__('Bad request'), 404);
}
$mail['recipient_id'] = $mail['id'];
$mail['email_setting'] = User::getPref('email.setting', $mail);
$mail = Hooks::fireDB('model.profile.get_info_mail', $mail);
return $mail;
}
public function sendEmail($mail)
{
$mail = Hooks::fire('model.profile.send_email_start', $mail);
// Clean up message and subject from POST
$subject = Utils::trim(Input::post('req_subject'));
$message = Utils::trim(Input::post('req_message'));
if ($subject == '') {
throw new Error(__('No email subject'), 400);
} elseif ($message == '') {
throw new Error(__('No email message'), 400);
}
// Here we use strlen() not Utils::strlen() as we want to limit the post to FEATHER_MAX_POSTSIZE bytes, not characters
elseif (strlen($message) > ForumEnv::get('FEATHER_MAX_POSTSIZE')) {
throw new Error(__('Too long email message'), 400);
}
if (User::get()->last_email_sent != '' && (time() - User::get()->last_email_sent) < User::getPref('email.min_interval') && (time() - User::get()->last_email_sent) >= 0) {
throw new Error(sprintf(__('Email flood'), User::getPref('email.min_interval'), User::getPref('email.min_interval') - (time() - User::get()->last_email_sent)), 429);
}
// Load the "form email" template
$mailTpl = trim(file_get_contents(ForumEnv::get('FEATHER_ROOT').'featherbb/lang/'.User::getPref('language', $mail['recipient_id']).'/mail_templates/form_email.tpl'));
$mailTpl = Hooks::fire('model.profile.send_email_mail_tpl', $mailTpl);
// The first row contains the subject
$firstCrlf = strpos($mailTpl, "\n");
$mailSubject = Utils::trim(substr($mailTpl, 8, $firstCrlf-8));
$mailMessage = Utils::trim(substr($mailTpl, $firstCrlf));
$mailSubject = str_replace('
', $subject, $mailSubject);
$mailMessage = str_replace('', User::get()->username, $mailMessage);
$mailMessage = str_replace('', ForumSettings::get('o_board_title'), $mailMessage);
$mailMessage = str_replace('', $message, $mailMessage);
$mailMessage = str_replace('', ForumSettings::get('o_board_title'), $mailMessage);
$mailMessage = Hooks::fire('model.profile.send_email_mail_message', $mailMessage);
Email::send($mail['recipient_email'], $mailSubject, $mailMessage, User::get()->email, User::get()->username);
$updateLastMailSent = DB::table('users')->where('id', User::get()->id)
->findOne()
->set('last_email_sent', time());
$updateLastMailSent = Hooks::fireDB('model.profile.send_email_update_last_mail_sent', $updateLastMailSent);
$updateLastMailSent = $updateLastMailSent->save();
return Router::redirect(Router::pathFor('userProfile', ['id' => $mail['recipient_id']]), __('Email sent redirect'));
}
public function displayIpInfo($ip)
{
$ip = Hooks::fire('model.profile.display_ip_info', $ip);
throw new Error(sprintf(__('Host info 1'), $ip).'
'.sprintf(__('Host info 2'), @gethostbyaddr($ip)).'
'.__('Show more users').'', 400, true, true);
}
}