freescout/freescout-dist/app/Http/Controllers/UsersController.php

661 lines
23 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Events\UserDeleted;
use App\Folder;
use App\Mailbox;
use App\Subscription;
use App\Thread;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\Rule;
use Validator;
class UsersController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Users list.
*/
public function users()
{
$this->authorize('create', 'App\User');
$users = User::nonDeleted()->get();
$users = User::sortUsers($users);
return view('users/users', ['users' => $users]);
}
/**
* New user.
*/
public function create()
{
$this->authorize('create', 'App\User');
$mailboxes = Mailbox::all();
return view('users/create', ['mailboxes' => $mailboxes]);
}
/**
* Create new user.
*
* @param \Illuminate\Http\Request $request
*/
public function createSave(Request $request)
{
$invalid = false;
$this->authorize('create', 'App\User');
$auth_user = auth()->user();
$rules = [
'first_name' => 'required|string|max:20',
'last_name' => 'required|string|max:30',
'email' => 'required|string|email|max:100|unique:users',
//'role' => ['required', Rule::in(array_keys(User::$roles))],
];
if ($auth_user->isAdmin()) {
$rules['role'] = ['required', Rule::in(array_keys(User::$roles))];
}
if (empty($request->send_invite)) {
$rules['password'] = 'required|string|max:255';
}
$validator = Validator::make($request->all(), $rules);
if (User::mailboxEmailExists($request->email)) {
$invalid = true;
$validator->errors()->add('email', __('There is a mailbox with such email. Users and mailboxes can not have the same email addresses.'));
}
if ($invalid || $validator->fails()) {
return redirect()->route('users.create')
->withErrors($validator)
->withInput();
}
$user = new User();
$user->fill($request->all());
if (!$auth_user->can('changeRole', $user)) {
$user->role = User::ROLE_USER;
}
if (empty($request->send_invite)) {
// Set password from request
$user->password = Hash::make($request->password);
} else {
// Set some random password before sending invite
$user->password = Hash::make($user->generateRandomPassword());
}
// Set system timezone.
$user->timezone = config('app.timezone') ?: User::DEFAULT_TIMEZONE;
$user = \Eventy::filter('user.create_save', $user, $request);
$user->save();
$user->mailboxes()->sync($request->mailboxes ?: []);
$user->syncPersonalFolders($request->mailboxes);
// Send invite
if (!empty($request->send_invite)) {
try {
$user->sendInvite(true);
} catch (\Exception $e) {
// Admin is allowed to see exceptions
\Session::flash('flash_error_floating', $e->getMessage().' — '.__('Check mail settings in "Manage » Settings » Mail Settings"'));
}
}
\Session::flash('flash_success_floating', __('User created successfully'));
return redirect()->route('users.profile', ['id' => $user->id]);
}
/**
* User profile.
*/
public function profile($id)
{
$user = User::findOrFail($id);
if ($user->isDeleted()) {
abort(404);
}
$this->authorize('update', $user);
$users = $this->getUsersForSidebar($id);
return view('users/profile', ['user' => $user, 'users' => $users]);
}
public function getUsersForSidebar($except_id)
{
if (auth()->user()->isAdmin()) {
return User::sortUsers(User::nonDeleted()->get());/*->except($except_id)*/;
} else {
return [];
}
}
/**
* Handle a registration request for the application.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function profileSave($id, Request $request)
{
$invalid = false;
$user = User::findOrFail($id);
$this->authorize('update', $user);
// This is also present in PublicController::userSetup
$validator = Validator::make($request->all(), [
'first_name' => 'required|string|max:20',
'last_name' => 'required|string|max:30',
'email' => 'required|string|email|max:100|unique:users,email,'.$id,
//'emails' => 'max:100',
'job_title' => 'max:100',
'phone' => 'max:60',
'timezone' => 'required|string|max:255',
'time_format' => 'required',
'role' => ['nullable', Rule::in(array_keys(User::$roles))],
'photo_url' => 'nullable|image|mimes:jpeg,png,jpg,gif',
]);
$validator->setAttributeNames([
'photo_url' => __('Photo'),
]);
// Photo
$validator->after(function ($validator) use ($user, $request) {
if ($request->hasFile('photo_url')) {
$path_url = $user->savePhoto($request->file('photo_url'));
if ($path_url) {
$user->photo_url = $path_url;
} else {
$invalid = true;
$validator->errors()->add('photo_url', __('Error occurred processing the image. Make sure that PHP GD extension is enabled.'));
}
}
// Do not allow to remove last administrator
if ($user->isAdmin() && isset($request->role) && $request->role != User::ROLE_ADMIN) {
$admins_count = User::where('role', User::ROLE_ADMIN)->count();
if ($admins_count < 2) {
$invalid = true;
$validator->errors()->add('role', __('Role of the only one administrator can not be changed.'));
}
}
});
if (User::mailboxEmailExists($request->email)) {
$invalid = true;
$validator->errors()->add('email', __('There is a mailbox with such email. Users and mailboxes can not have the same email addresses.'));
}
if ($invalid || $validator->fails()) {
return redirect()->route('users.profile', ['id' => $id])
->withErrors($validator)
->withInput();
}
// Save language into session.
if (auth()->user()->id == $id && $request->locale) {
session()->put('user_locale', $request->locale);
}
$request_data = $request->all();
if (isset($request_data['photo_url'])) {
unset($request_data['photo_url']);
}
if (!auth()->user()->can('changeRole', $user)) {
unset($request_data['role']);
}
if ($user->status != User::STATUS_DELETED) {
if (!empty($request_data['disabled'])) {
$request_data['status'] = User::STATUS_DISABLED;
} else {
$request_data['status'] = User::STATUS_ACTIVE;
}
}
$user->setData($request_data);
if (empty($request->input('enable_kb_shortcuts'))) {
$user->enable_kb_shortcuts = false;
}
$user = \Eventy::filter('user.save_profile', $user, $request);
$user->save();
\Session::flash('flash_success_floating', __('Profile saved successfully'));
return redirect()->route('users.profile', ['id' => $id]);
}
/**
* User permissions.
*/
public function permissions($id)
{
$user = auth()->user();
if (!$user->isAdmin()) {
abort(403);
}
$user = User::findOrFail($id);
$mailboxes = Mailbox::all();
$users = $this->getUsersForSidebar($id);
return view('users/permissions', [
'user' => $user,
'mailboxes' => $mailboxes,
'user_mailboxes' => $user->mailboxes,
'users' => $users,
]);
}
/**
* Save user permissions.
*
* @param int $id
* @param \Illuminate\Http\Request $request
*/
public function permissionsSave($id, Request $request)
{
$user = auth()->user();
if (!$user->isAdmin()) {
abort(403);
}
$user = User::findOrFail($id);
$user->mailboxes()->sync($request->mailboxes ?: []);
$user->syncPersonalFolders($request->mailboxes);
// Save permissions.
$user_permissions = $request->user_permissions ?? [];
$permissions = [];
foreach (User::getUserPermissionsList() as $permission_id) {
$new_has_permission = in_array($permission_id, $user_permissions);
if ($user->hasPermission($permission_id, false) != $new_has_permission) {
$permissions[$permission_id] = (int)(bool)$new_has_permission;
$save_user = true;
}
}
$user->permissions = $permissions;
$user->save();
\Session::flash('flash_success_floating', __('Permissions saved successfully'));
return redirect()->route('users.permissions', ['id' => $id]);
}
/**
* User notifications settings.
*/
public function notifications($id)
{
$user = User::findOrFail($id);
$this->authorize('update', $user);
$subscriptions = $user->subscriptions()->select('medium', 'event')->get();
$person = '';
if ($id != auth()->user()->id) {
$person = $user->getFirstName(true);
}
$users = $this->getUsersForSidebar($id);
return view('users/notifications', [
'user' => $user,
'subscriptions' => $subscriptions,
'person' => $person,
'users' => $users,
'mobile_available' => \Eventy::filter('notifications.mobile_available', false),
]);
}
/**
* Save user notifications settings.
*
* @param int $id
* @param \Illuminate\Http\Request $request
*/
public function notificationsSave($id, Request $request)
{
$user = User::findOrFail($id);
$this->authorize('update', $user);
Subscription::saveFromArray($request->subscriptions, $user->id);
\Session::flash('flash_success_floating', __('Notifications saved successfully'));
return redirect()->route('users.notifications', ['id' => $id]);
}
/**
* Users ajax controller.
*/
public function ajax(Request $request)
{
$response = [
'status' => 'error',
'msg' => '', // this is error message
];
$auth_user = auth()->user();
switch ($request->action) {
// Both send and resend
case 'send_invite':
if (!$auth_user->isAdmin()) {
$response['msg'] = __('Not enough permissions');
}
if (empty($request->user_id)) {
$response['msg'] = __('Incorrect user');
}
if (!$response['msg']) {
$user = User::find($request->user_id);
if (!$user) {
$response['msg'] = __('User not found');
} elseif ($user->invite_state == User::INVITE_STATE_ACTIVATED) {
$response['msg'] = __('User already accepted invitation');
}
}
if (!$response['msg']) {
try {
$user->sendInvite(true);
$response['status'] = 'success';
} catch (\Exception $e) {
// Admin is allowed to see exceptions.
$response['msg'] = $e->getMessage().' — '.__('Check mail settings in "Manage » Settings » Mail Settings"');
}
}
break;
// Reset password
case 'reset_password':
if (!auth()->user()->isAdmin()) {
$response['msg'] = __('Not enough permissions');
}
if (empty($request->user_id)) {
$response['msg'] = __('Incorrect user');
}
if (!$response['msg']) {
$user = User::find($request->user_id);
if (!$user) {
$response['msg'] = __('User not found');
}
}
if (!$response['msg']) {
$reset_result = Password::broker()->sendResetLink(
//['id' => $request->user_id]
['id' => $request->user_id]
);
if ($reset_result == Password::RESET_LINK_SENT) {
$response['status'] = 'success';
$response['msg_success'] = __('Password reset email has been sent');
}
}
break;
// Load website notifications
case 'web_notifications':
if (!$auth_user) {
$response['msg'] = __('You are not logged in');
}
if (!$response['msg']) {
$web_notifications_info = $auth_user->getWebsiteNotificationsInfo(false);
$response['html'] = view('users/partials/web_notifications', [
'web_notifications_info_data' => $web_notifications_info['data'],
])->render();
$response['has_more_pages'] = (int) $web_notifications_info['notifications']->hasMorePages();
$response['status'] = 'success';
}
break;
// Mark all user website notifications as read
case 'mark_notifications_as_read':
if (!$auth_user) {
$response['msg'] = __('You are not logged in');
}
if (!$response['msg']) {
$auth_user->unreadNotifications()->update(['read_at' => now()]);
$auth_user->clearWebsiteNotificationsCache();
$response['status'] = 'success';
}
break;
// Delete user photo
case 'delete_photo':
$user = User::find($request->user_id);
if (!$user) {
$response['msg'] = __('User not found');
} elseif (!$auth_user->can('update', $user)) {
$response['msg'] = __('Not enough permissions');
}
if (!$response['msg']) {
$user->removePhoto();
$user->save();
$response['status'] = 'success';
}
break;
// Delete user
case 'delete_user':
$user = User::find($request->user_id);
if (!$user) {
$response['msg'] = __('User not found');
} elseif (!$auth_user->can('delete', $user)) {
$response['msg'] = __('Not enough permissions');
}
// Check if the user is the only one admin
if (!$response['msg'] && $user->isAdmin()) {
$admins_count = User::where('role', User::ROLE_ADMIN)->count();
if ($admins_count < 2) {
$response['msg'] = __('Administrator can not be deleted');
}
}
if (!$response['msg']) {
// We have to process conversations one by one to move them to Unassigned folder,
// as conversations may be in different mailboxes
// $user->conversations()->update(['user_id' => null, 'folder_id' => ]);
$mailbox_unassigned_folders = [];
$user->conversations->each(function ($conversation) use ($auth_user, $request) {
// We don't fire ConversationUserChanged event to avoid sending notifications to users
if (!empty($request->assign_user)
&& !empty($request->assign_user[$conversation->mailbox_id])
&& (int) $request->assign_user[$conversation->mailbox_id] != -1
) {
// Set assignee.
// In this case conversation stays assigned, just assignee changes.
$conversation->user_id = $request->assign_user[$conversation->mailbox_id];
} else {
// Make convesation Unassigned.
// Unset assignee.
// Maybe use changeUser() here.
$conversation->user_id = null;
if ($conversation->isPublished()
&& ($conversation->isActive() || $conversation->isPending())
) {
// Change conversation folder to UNASSIGNED.
$folder_id = null;
if (!empty($mailbox_unassigned_folders[$conversation->mailbox_id])) {
$folder_id = $mailbox_unassigned_folders[$conversation->mailbox_id];
} else {
$folder = $conversation->mailbox->folders()
->where('type', Folder::TYPE_UNASSIGNED)
->first();
if ($folder) {
$folder_id = $folder->id;
$mailbox_unassigned_folders[$conversation->mailbox_id] = $folder_id;
}
}
if ($folder_id) {
$conversation->folder_id = $folder_id;
}
}
}
$conversation->save();
// Create lineitem thread
$thread = new Thread();
$thread->conversation_id = $conversation->id;
$thread->user_id = $conversation->user_id;
$thread->type = Thread::TYPE_LINEITEM;
$thread->state = Thread::STATE_PUBLISHED;
$thread->status = Thread::STATUS_NOCHANGE;
$thread->action_type = Thread::ACTION_TYPE_USER_CHANGED;
$thread->source_via = Thread::PERSON_USER;
$thread->source_type = Thread::SOURCE_TYPE_WEB;
$thread->customer_id = $conversation->customer_id;
$thread->created_by_user_id = $auth_user->id;
$thread->save();
});
// Recalculate counters for folders
//if ($user->isAdmin()) {
// Admin has access to all mailboxes
Mailbox::all()->each(function ($mailbox) {
$mailbox->updateFoldersCounters();
});
// } else {
// $user->mailboxes->each(function ($mailbox) {
// $mailbox->updateFoldersCounters();
// });
// }
// Disconnect user from mailboxes.
$user->mailboxes()->sync([]);
$user->folders()->delete();
$user->status = \App\User::STATUS_DELETED;
// Update email.
$email_suffix = User::EMAIL_DELETED_SUFFIX.date('YmdHis');
// We have to truncate email to avoid "Data too long" error.
$user->email = mb_substr($user->email, 0, User::EMAIL_MAX_LENGTH - mb_strlen($email_suffix)).$email_suffix;
$user->save();
event(new UserDeleted($user, $auth_user));
\Session::flash('flash_success_floating', __('User deleted').': '.$user->getFullName());
$response['status'] = 'success';
}
break;
default:
$response['msg'] = 'Unknown action';
break;
}
if ($response['status'] == 'error' && empty($response['msg'])) {
$response['msg'] = 'Unknown error occurred';
}
return \Response::json($response);
}
/**
* Change user password.
*/
public function password($id)
{
$user = User::findOrFail($id);
$this->authorize('update', $user);
$users = User::all()->except($id);
return view('users/password', ['user' => $user, 'users' => $users]);
}
/**
* Save changed user password.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function passwordSave($id, Request $request)
{
// It is allowed to edit only your own password
$user = auth()->user();
if ($user->id != $id) {
abort(403);
}
// This is also present in PublicController::userSetup
$validator = Validator::make($request->all(), [
'password_current' => 'required|string',
'password' => 'required|string|min:8|confirmed',
]);
$validator->after(function ($validator) use ($user, $request) {
// Check current password
if (!Hash::check($request->password_current, $user->password)) {
$validator->errors()->add('password_current', __('This password is incorrect.'));
} elseif (Hash::check($request->password, $user->password)) {
// Check new password
$validator->errors()->add('password', __('The new password is the same as the old password.'));
}
});
if ($validator->fails()) {
return redirect()->route('users.password', ['id' => $id])
->withErrors($validator)
->withInput();
}
$user->password = bcrypt($request->password);
$user->save();
$user->sendPasswordChanged();
\Session::flash('flash_success_floating', __('Password saved successfully!'));
return redirect()->route('users.profile', ['id' => $id]);
}
}