Preview: UserRepository.php
Size: 128.96 KB
/var/www/multi-event-cfp.bitkit.dk/httpdocs/app/Repositories/UserRepository.php
<?php
namespace App\Repositories;
use App\Exports\ExportUsersClass;
use App\Jobs\SendSpeakerTerms;
use App\Models\Abstracts;
use App\Models\AbstractUser;
use App\Models\AmendSpeakerTerms;
use App\Models\Event;
use App\Models\EventUser;
use App\Models\File;
use App\Models\Presenter;
use App\Models\SpeakerNotes;
use App\Models\User;
use App\Support\Entity;
use App\Support\Query;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Jobs\SendUserCreateMail;
use App\Jobs\SendAdminResetPasswordMail;
use App\Jobs\SendLoginInfoMail;
use App\Jobs\SendUserVerificationFieldsChangedMail;
use App\Jobs\SendUserFilesDownloadLinkEmail;
use Laravel\Sanctum\NewAccessToken;
use Maatwebsite\Excel\Facades\Excel;
use App\Imports\ImportUserTemplate;
use App\Imports\UsersImport;
use App\Models\Presentation;
use App\Models\PresentationReview;
use App\Models\Score;
use App\Models\Session;
use App\Models\SessionUser;
use App\Models\Slot;
use App\Models\SlotUser;
use App\Jobs\SendSessionUpdatedMail;
use Illuminate\Support\Str;
use App\Rules\DateRule;
use App\Rules\SessionDateRule;
use Exception;
use ZipArchive;
use URL;
use Intervention\Image\Facades\Image;
use Carbon\Carbon;
class UserRepository extends Repository
{
protected $userFields = ["verification_status", "biography", "avatar", "company_logo", "company", "mobile", "job_title", "type"];
public function __construct($event = null)
{
$this->for($event);
}
public function model(): User
{
return new User;
}
public function query($as = null)
{
$model = $this->model();
return $model->newQuery();
}
/**
* @param $admins
* @param Event|null $event
* @return Entity
*/
public function createEventAdmin($admins, Event $event = null): Entity
{
$newUsers = [];
$newUserPasswords = [];
$existingUsers = [];
if (!$event)
$event = Event::find($this->for[0]);
if (!$event)
validationErrorResponse(['Event not found']);
foreach ($admins as $admin) {
$email = $admin['email'];
$developerOptions = isset($admin['developer_options']) ? $admin['developer_options'] : false;
$allUserAPIAccess = isset($admin['all_user_api_access']) ? $admin['all_user_api_access'] : false;
$userExportAccess = isset($admin['user_export_access']) ? $admin['user_export_access'] : false;
$abstractExportAccess = isset($admin['abstract_export_access']) ? $admin['abstract_export_access'] : false;
$presentationFilesDownloadAccess = isset($admin['presentation_files_download_access']) ? $admin['presentation_files_download_access'] : false;
$adminUser = User::whereEmail($email)->first();
if (!$adminUser) {
$adminPassword = randomStringGenerator(8);
$adminUser = User::create([
'email' => $email,
'password' => Hash::make($adminPassword),
'valid' => true
]);
$adminUser->assignRole('user');
$newUsers[] = $email;
$newUserPasswords[] = $adminPassword;
} else {
if (!$adminUser->hasRole('user'))
$adminUser->assignRole('user');
$existingUsers[] = $email;
}
$eventUser = $this->attachUserToEvent($event, $adminUser, 'event_admin');
$eventUser->developer_options = $developerOptions;
$eventUser->all_user_api_access = $allUserAPIAccess;
$eventUser->user_export_access = $userExportAccess;
$eventUser->abstract_export_access = $abstractExportAccess;
$eventUser->presentation_files_download_access = $presentationFilesDownloadAccess;
$eventUser->access_level = ($admin['access_level'] ?? null) === 'full' ? 'full' : 'limited';
$eventUser->vip_categories = ($admin['access_level'] ?? null) === 'limited' ? ($admin['vip_categories'] ?? []) : null;
$eventUser->save();
}
return entity([
'new_users' => $newUsers,
'new_user_passwords' => $newUserPasswords,
'existing_users' => $existingUsers
]);
}
public function removeEventUser($eventId, $emails, $role = null): bool
{
foreach ($emails as $email) {
$admin = User::whereEmail($email)->first();
$eventUser = getEventUser($eventId, $admin->id);
if (!$role) {
$eventUser->syncRoles([]);
} else if ($eventUser->hasRole($role)) {
$eventUser->removeRole($role);
}
if (!$eventUser->hasAnyRole())
$eventUser->delete();
}
return true;
}
public function checkEventUserExists(Event $event, $email, $role)
{
$user = User::whereEmail($email)->first();
if (!$user)
return response([
'status' => true,
'user_status' => 0, // new user
'message' => 'User not in the platform'
]);
$eventUser = getEventUser($event, $user);
if (!$eventUser || !$eventUser->hasRole($role))
return response([
'status' => true,
'user_status' => 1, // user found not registered for the event
'message' => 'User exists but not registered for event'
]);
return response([
'status' => true,
'user_status' => 2, //user already registered for event
'message' => "User already registered for event"
]);
}
public function attachUserToEvent(Event $event, User $user, $role = null): ?EventUser
{
$event->users()->attach($user->id);
$eventUser = getEventUser($event->id, $user->id);
if ($role)
$eventUser->assignRole($role);
return $eventUser;
}
public function attachUserToAbstract(Abstracts $abstract, User $user, $role = null): AbstractUser
{
$abstract->users()->attach($user->id);
$abstractUser = getAbstractUser($abstract->id, $user->id);
if ($role)
$abstractUser->assignRole($role);
return $abstractUser;
}
/**
* Track user profile modification
*/
public function trackUserModification(User $user, $modifiedBy = null, $eventId = null): void
{
$user->last_modified_by = $modifiedBy;
$user->last_modified_at = now();
$user->last_modified_event_id = $eventId;
}
/**
* Track who marked the user as VIP
* Only sets vip_marked_by when profile_type changes to a VIP type
*/
public function trackVipMarkedBy(User $user, array $changes): void
{
if (array_key_exists('profile_type', $changes) && in_array($user->profile_type, VIP_PROFILE_TYPES)) {
$user->vip_marked_by = authUser()->id;
}
}
public function createUser($input)
{
// form type
$formType = isset($input['form_type']) ? $input['form_type'] : false;
// common rules
$commonRules = [
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'cc_emails' => ['string', 'nullable'],
'do_not_send_emails' => ['boolean'],
];
// additional rules
$additionalRules = [
'password' => ['required', 'string', 'min:8', 'confirmed'],
'job_title' => ['required', 'string', 'max:255'],
'company' => ['required', 'string', 'max:255'],
'country' => ['required', 'string', 'max:255'],
'mobile' => ['required', 'string', 'max:255'],
'dob' => [new DateRule],
'nationality' => ['string', 'max:255', 'nullable'],
'emirates_id' => ['regex:/^[0-9]{15}$/', 'nullable'],
'passport_first_last_name' => ['string', 'max:255', 'nullable'],
'salesforce_opportunity_id' => ['string', 'size:18', 'regex:/^[a-zA-Z0-9]{18}$/', 'nullable'],
];
// Merge rules based on the form type
$rules = $formType === 'minimal' ? $commonRules : array_merge($commonRules, $additionalRules);
// validation
$validator = Validator::make($input, $rules);
// return error response
if ($validator->fails())
validationErrorResponse($validator->errors());
// password
$input['password'] = Hash::make($input['password']);
// create user with input
$user = User::create($input);
// set valid true
$user->valid = true;
// auth user
$authUser = authUser();
// set created by
$user->created_by = $authUser ? $authUser->id : null;
// set new user true
$user->new_user = true;
// save user
$user->save();
// assign role
$user->assignRole('user');
return $user;
}
/**
* @param Request $request
* @param User $user
* @return User
*/
public function updateUser($input, User $user, $request, $modifiedBy = null, $eventId = null): User
{
$input = cleanInput($input);
$validator = Validator::make($input, [
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'job_title' => ['required', 'string', 'max:255'],
'company' => ['required', 'string', 'max:255'],
'country' => ['required', 'string', 'max:255'],
'mobile' => ['required', 'string', 'max:255'],
'password' => ['string', 'min:8', 'confirmed'],
'dob' => [new DateRule],
'nationality' => ['string', 'max:255', 'nullable'],
'passport_number' => ['string', 'max:255', 'nullable'],
// 15 digits only, numeric
'emirates_id' => ['regex:/^[0-9]{15}$/', 'nullable'],
'passport_first_last_name' => ['string', 'max:255', 'nullable'],
]);
if ($validator->fails())
validationErrorResponse($validator->errors());
if ($input['password']) {
$password = Hash::make($input['password']);
$input = array_merge($input, [
'password' => $password,
'password_confirmation' => $password
]);
} else {
unset($input['password']);
unset($input['password_confirmation']);
}
unset($input['id']);
$input['emirates_id'] = isset($input['emirates_id']) && $input['emirates_id'] !== '' ? $input['emirates_id'] : null;
$user->fill($input);
//keep changes field lists
$changes = $user->getDirty();
$user->new_user = false;
// Track modification if users table data changed
if (!empty($changes)) {
$this->trackUserModification($user, $modifiedBy, $eventId);
}
$user->save();
$imageFiles = $request->allFiles();
$removeAvatar = $request->get('avatar') === "null" && $user->avatar;
$removeCompanyLogo = $request->get('company_logo') === "null" && $user->company_logo;
// Get the changed fields using the common function
$changeFields = $this->getChangedFields($changes, $imageFiles, $removeAvatar, $removeCompanyLogo);
// verification changes for change fields
if ($changeFields) {
// published session status changing
$this->publishedSessionStatusUpdate($user);
$this->userVerificationFieldsChanges($changeFields, $user);
}
return $user;
}
/**
* @param User $user
* @param false $avatar
* @param false $companyLogo
* @return User
*/
public function removeUserImages(User $user, $avatar = false, $companyLogo = false, $userSecondaryImage = false): User
{
if ($avatar) {
$avatar = $user->avatar;
if ($avatar) {
$file = File::find($avatar['file_id']);
if ($file) {
// Get the thumbnail image name
$thumbnailImageName = $this->getThumbnailImageName($file);
// Check if the original image exists before deleting it
if (Storage::exists($file->filepath . '/' . $file->save_name)) {
Storage::delete($file->filepath . '/' . $file->save_name);
}
// Check if the thumbnail image exists before deleting it
if (Storage::exists($file->filepath . '/' . $thumbnailImageName)) {
Storage::delete($file->filepath . '/' . $thumbnailImageName);
}
// Delete the file record from the database
$file->delete();
}
$user->avatar = null;
}
}
if ($companyLogo) {
$companyLogo = $user->company_logo;
if ($companyLogo) {
$file = File::find($companyLogo['file_id']);
if ($file) {
Storage::delete($file->filepath . '/' . $file->save_name);
$file->delete();
}
$user->company_logo = null;
}
}
if ($userSecondaryImage) {
$userSecondaryImage = $user->user_secondary_image;
if ($userSecondaryImage) {
$file = File::find($userSecondaryImage['file_id']);
if ($file) {
Storage::delete($file->filepath . '/' . $file->save_name);
$file->delete();
}
$user->user_secondary_image = null;
}
}
$user->save();
return $user;
}
public function updateUserImages($imageFiles, User $user, $modifiedBy = null, $eventId = null): User
{
$hasChanges = false;
if (isset($imageFiles['avatar'])) {
$file = $imageFiles['avatar'];
$currentAvatar = $user->avatar;
$avatarFile = $user->saveFile(
$file,
$currentAvatar['file_id'] ?? null,
"{$user->id}/images/",
'user.avatar',
'avatar'
);
$currentAvatar = new Entity($currentAvatar);
$avatar = $avatarFile->getFileObject($currentAvatar);
// save thumbnail image - smaller version of actual image
$this->saveThumbnailImage($file, $avatar, $user->id);
$user->avatar = $avatar;
$hasChanges = true;
}
if (isset($imageFiles['company_logo'])) {
$file = $imageFiles['company_logo'];
$currentLogo = $user->company_logo;
$logoFile = $user->saveFile(
$file,
$currentLogo['file_id'] ?? null,
"{$user->id}/images/",
'user.company_logo',
'company_logo',
);
$currentLogo = new Entity($currentLogo);
$companyLogo = $logoFile->getFileObject($currentLogo);
$user->company_logo = $companyLogo;
$hasChanges = true;
}
if (isset($imageFiles['user_secondary_image'])) {
$file = $imageFiles['user_secondary_image'];
$currentUserSecondaryImage = $user->user_secondary_image;
$logoFile = $user->saveFile(
$file,
$currentUserSecondaryImage['file_id'] ?? null,
"{$user->id}/images/",
'user.user_secondary_image',
'user_secondary_image',
);
$currentUserSecondaryImage = new Entity($currentUserSecondaryImage);
$userSecondaryImage = $logoFile->getFileObject($currentUserSecondaryImage);
$user->user_secondary_image = $userSecondaryImage;
$hasChanges = true;
}
if ($hasChanges) {
$this->trackUserModification($user, $modifiedBy, $eventId);
}
$user->save();
return $user;
}
public function saveThumbnailImage($uploadFile, $avatar, $userId)
{
// Get extension
$extension = $avatar['extension'];
// Get the base name without the extension
$baseName = pathinfo($avatar['save_name'], PATHINFO_FILENAME);
// thumbnail image name
$thumbnailImageName = "{$baseName}_thumbnail.{$extension}";
// Define the full thumbnail save path based on avatar's existing path
$thumbnailDirectory = $avatar['filepath'];
// thumbnail path
$thumbnailRelativePath = "{$thumbnailDirectory}{$thumbnailImageName}";
// Create thumbnail using the Intervention Image library
$image = Image::make($uploadFile->getRealPath());
// Resize the image to 500x500
$image->resize(500, 500, function ($constraint) {
$constraint->aspectRatio(); // Maintain aspect ratio
$constraint->upsize(); // Prevent upsizing if the image is smaller than 500x500
});
// Generate image stream
$imageStream = $image->stream($avatar['extension'], 85);
Storage::put($thumbnailRelativePath, $imageStream->__toString(), 'public');
// Generate the public path for the thumbnail
$thumbnailPath = User::FILE_PUBLIC_PATH . "{$userId}/images/{$thumbnailImageName}";
// Update the saved file's thumbnail path in the database
$savedFile = File::find($avatar['file_id']);
if ($savedFile) {
$savedFile->thumbnail_path = $thumbnailPath;
$savedFile->save();
}
// Update the avatar object with the new thumbnail path
$avatar['thumbnail_path'] = $thumbnailPath . "?v=" . md5(Carbon::now());
return $avatar; // Return updated avatar for further use if needed
}
//handling uploading files by user
public function handleUserFiles(Request $request, $data, User $user, $modifiedBy = null, $eventId = null)
{
$hasChanges = false;
// handling uploaded files
if ($request->hasFile('passport_images')) {
$passportFiles = $request->file('passport_images');
foreach ($passportFiles as $file) {
$fileObj = $user->saveFile(
$file,
null,
"{$user->id}/files/passportImages/",
"user.passport_images.file",
null,
true
);
$passportFileContents[] = $fileObj->getFileObject();
}
$passportImages = array_values(array_filter(array_merge($data['passport_images'], $passportFileContents)));
$user->passport_images = $passportImages;
$hasChanges = true;
$user->save();
} else {
// update user data with updated data
if (isset($data['passport_images'])) {
$passportImagesData = $data['passport_images'];
if ($user->passport_images != $passportImagesData) {
$user->passport_images = $passportImagesData;
$hasChanges = true;
$user->save();
}
}
}
if ($request->hasFile('emirates_id_images')) {
$emiratesIdFiles = $request->file('emirates_id_images');
foreach ($emiratesIdFiles as $file) {
$fileObj = $user->saveFile(
$file,
null,
"{$user->id}/files/emiratesIdImages/",
"user.emirates_id_images.file",
null,
true
);
$emiratesIdFileContents[] = $fileObj->getFileObject();
}
$emiratesIdImages = array_values(array_filter(array_merge($data['emirates_id_images'], $emiratesIdFileContents)));
$user->emirates_id_images = $emiratesIdImages;
$hasChanges = true;
$user->save();
} else {
// update user data with updated data
if (isset($data['emirates_id_images'])) {
$emiratesImageData = $data['emirates_id_images'];
if ($user->emirates_id_images != $emiratesImageData) {
$user->emirates_id_images = $emiratesImageData;
$hasChanges = true;
$user->save();
}
}
}
if (isset($data['removed_files'])) {
//handling removed files
$removedFiles = $data['removed_files'];
if (isset($removedFiles)) {
$fileIds = $removedFiles;
foreach ($fileIds as $fileId) {
$this->deleteFile($fileId);
}
$hasChanges = true;
}
}
if ($hasChanges) {
$this->trackUserModification($user, $modifiedBy, $eventId);
}
return $hasChanges;
}
public function selectColumns(): array
{
return [
'users.id',
'users.first_name',
'users.last_name',
'users.company',
'users.nationality',
'users.passport_number',
'users.country',
DB::raw('concat(users.first_name," ",users.last_name) as name'),
DB::raw('CASE WHEN users.do_not_send_emails = 0 THEN users.email ELSE users.cc_emails END as send_mail'),
'users.email',
'users.do_not_send_emails',
'users.cc_emails',
DB::raw('CASE WHEN users.salutation = "Prefer not to say" THEN NULL ELSE users.salutation END as salutation'),
'users.job_title',
'users.mobile',
'users.profile_type',
'users.dietary_requirements',
'users.last_modified_by',
'users.last_modified_at',
'users.last_modified_event_id',
DB::raw('(SELECT CONCAT(u2.first_name, " ", u2.last_name) FROM users u2 WHERE u2.id = users.last_modified_by) as last_modified_by_name'),
DB::raw('(SELECT u2.email FROM users u2 WHERE u2.id = users.last_modified_by) as last_modified_by_email'),
DB::raw('(SELECT e.event_name FROM events e WHERE e.id = users.last_modified_event_id) as last_modified_event_name'),
];
}
public function applyEventScope(Builder $query, $arguments): Builder
{
if ($arguments->event) {
$query->join('event_user as eu', function ($join) use ($arguments) {
$join->on('eu.user_id', '=', 'users.id')
->where('eu.event_id', '=', $arguments->event->id);
})->addSelect([
DB::raw("eu.verification_status as verification_status"),
DB::raw("eu.type as type"),
DB::raw("eu.user_added_via as user_added_via")
])->groupBy('eu.id')->whereIn('eu.id', function ($subquery) use ($arguments) {
$subquery->select(DB::raw('MIN(id)'))
->from('event_user')
->where('event_id', $arguments->event->id)
->groupBy('user_id');
});
}
return $query;
}
public function applyRoleScope(Builder $query, $arguments): Builder
{
if ($arguments->event_role) {
$eventRole = $arguments->event_role;
$query->join('model_has_roles as mhs', 'mhs.model_id', '=', 'eu.id')
->where('mhs.model_type', '=', EventUser::class)
->join('roles as r', 'r.id', '=', 'mhs.role_id')
->where('r.name', '=', $eventRole);
if ($eventRole == 'event_submitter') {
$query->leftJoin('abstracts as abstracts', function ($join) use ($arguments) {
$join->on('abstracts.user_id', '=', 'users.id')
->where('abstracts.event_id', '=', $arguments->event->id);
})->addSelect([
DB::raw("SUM(if(abstracts.id is not null, 1, 0)) as abstract_count")
])->groupBy('users.id');
}
if ($eventRole == 'event_reviewer') {
$query->addSelect(['eu.event_profile_data']);
$query->addSelect('eu.id as event_user_id');
if (!$arguments->presentation_id) {
$query->leftJoin('scores as s', function ($join) use ($arguments) {
$join->on('s.user_id', '=', 'users.id')
->where('s.event_id', '=', $arguments->event->id);
})->addSelect([
DB::raw("SUM(if(s.id is not null, 1, 0)) as score_count"),
DB::raw("REPLACE(REPLACE(REPLACE(JSON_EXTRACT(eu.event_profile_data, '$.category[*].value'), '[', ''), ']', ''), '\"', '\'') as category"),
DB::raw("REPLACE(REPLACE(REPLACE(JSON_EXTRACT(eu.event_profile_data, '$.subcategory[*].value'), '[', ''), ']', ''), '\"', '\'') as subcategory")
])->groupBy('users.id');
}
if ($arguments->presentation_id ?? false) {
// presentation
$presentation = Presentation::find($arguments->presentation_id);
if ($presentation->abstract_id) {
// abstract
$abstract = Abstracts::find($presentation->abstract_id);
// category
$category_field = array_values(array_filter(
$abstract->data->toArray(),
fn ($settings) => $settings['field_id'] == "category"
));
// searching with category value
if ($category_field[0]['value']) {
$search = strtolower($category_field[0]['value']);
// $special_char = '–'; // en dash
// $special_char_pos = mb_strpos($search, $special_char);
// if ($special_char_pos !== false) {
// mb_internal_encoding("UTF-8");
// $search = mb_substr($search, $special_char_pos + mb_strlen($special_char));
// $search = trim($search);
// }
$special_chars = ['–', '/']; // en dash and forward slash
foreach ($special_chars as $special_char) {
$special_char_pos = mb_strpos($search, $special_char);
if ($special_char_pos !== false) {
mb_internal_encoding("UTF-8");
$search = mb_substr($search, $special_char_pos + mb_strlen($special_char));
$search = trim($search);
// Break after processing the first found special character
break;
}
}
$query->where(DB::raw("lower(REPLACE(REPLACE(REPLACE(JSON_EXTRACT(eu.event_profile_data, '$.category[*].value'), '[', ''), ']', ''), '\"', '\''))"), 'like', "%{$search}%");
}
}
$query->leftJoin('presentation_reviews as pr', function ($join) use ($arguments) {
$join->on('pr.reviewer_id', '=', 'users.id')
->where('pr.event_id', '=', $arguments->event->id)
->where('pr.presentation_id', '=', $arguments->presentation_id);
})->addSelect([
DB::raw("(CASE WHEN pr.id is null THEN 0 ELSE 1 END) as assigned")
]);
$query->leftJoin('presentation_reviews as prs', function ($join) use ($arguments) {
$join->on('prs.reviewer_id', '=', 'users.id')
->where('prs.event_id', '=', $arguments->event->id);
})->addSelect([
DB::raw("SUM(CASE WHEN prs.id is null THEN 0 ELSE 1 END) as assigned_count")
]);
}
}
}
return $query;
}
public function applyOrder(Builder $query, Query $arguments): Builder
{
if (!$arguments->sort)
return $query;
$userColumns = [
'id' => 'users.id',
'first_name' => 'users.first_name',
'last_name' => 'users.last_name',
'email' => 'users.email',
'name' => 'name',
'abstract_count' => 'abstract_count',
'score_count' => 'score_count',
'category' => 'category',
'subcategory' => 'subcategory',
'avatar' => 'avatar',
'biography' => 'biography',
'company_logo' => 'company_logo',
'mobile' => 'mobile',
'slot_identifiers' => 'slot_identifiers',
'agreement_status' => 'agreement_status',
'speaker_added_via' => 'speaker_added_via',
'verification_status' => 'verification_status',
'type' => 'type',
];
$sorts = is_array($arguments->sort) ? json_decode(json_encode($arguments->sort), FALSE) : json_decode($arguments->sort, true);
// order by event user order field
if (empty($sorts) && $arguments->orderSort) {
$query->orderBy('eu.order', 'asc');
return $query;
}
foreach ($sorts as $sort => $method) {
if (array_key_exists($sort, $userColumns)) {
$query->orderBy($sort, $method);
}
}
return $query;
}
public function applyAbstractScope(Builder $query, $arguments)
{
if ($arguments->abstract_ids) {
$abstractIds = array_map('intval', $arguments->abstract_ids);
$abstractUsers = $arguments->abstract_users;
$query->whereIn('users.id', function ($query) use ($abstractIds, $abstractUsers) {
$query->select('au.user_id')
->from('abstract_user as au')
->whereIn('au.abstracts_id', $abstractIds);
if ($abstractUsers) {
$query->join('model_has_roles as mhs', 'mhs.model_id', '=', 'au.id')
->where('mhs.model_type', '=', AbstractUser::class)
->join('roles as r', 'r.id', '=', 'mhs.role_id')
->whereIn('r.name', $abstractUsers);
}
})->groupBy('users.id')->distinct();
}
}
public function applySearch(Builder $query, $arguments): Builder
{
if ($arguments->search) {
$userColumns = [
'id' => 'users.id',
'first_name' => 'users.first_name',
'last_name' => 'users.last_name',
'email' => 'users.email',
'name' => DB::raw('concat(users.first_name," ",users.last_name)'),
];
$search = $arguments->search;
$search = strtolower($search);
foreach ($userColumns as $userColumn) {
$query->orHaving(DB::raw("lower($userColumn)"), 'like', "%{$search}%");
}
if ($arguments->event_role == 'event_reviewer') {
$query->orHaving(DB::raw("lower(REPLACE(REPLACE(REPLACE(JSON_EXTRACT(eu.event_profile_data, '$.category[*].value'), '[', ''), ']', ''), '\"', '\''))"), 'like', "%{$search}%");
$query->orHaving(DB::raw("lower(REPLACE(REPLACE(REPLACE(JSON_EXTRACT(eu.event_profile_data, '$.subcategory[*].value'), '[', ''), ']', ''), '\"', '\''))"), 'like', "%{$search}%");
}
if ($arguments->confirmed_speakers ?? false) {
$query->orHaving("slot_identifiers", 'like', "%{$arguments->search}%");
$query->orHaving("agreement_status", 'like', "%{$search}%");
$query->orHaving("speaker_added_via", 'like', "%{$search}%");
}
}
// Add logic to include the last note added for confirmed speakers
if ($arguments->confirmed_speakers ?? false) {
$query->addSelect([
DB::raw("(SELECT note FROM speaker_notes WHERE speaker_notes.speaker_id = users.id AND speaker_notes.event_id = {$arguments->event->id} ORDER BY created_at DESC LIMIT 1) as last_note_added")
]);
}
return $query;
}
public function applyContactRestrictionScope(Builder $query, $arguments): Builder
{
if (!($arguments->include_contact_restriction ?? false)) {
return $query;
}
$authUser = authUser();
$authEventUser = $arguments->event ? getEventUser($arguments->event, $authUser) : null;
if ($authEventUser && $authEventUser->access_level === 'limited') {
$allowedCategories = $authEventUser->vip_categories ?? [];
$vipTypes = implode("','", VIP_PROFILE_TYPES);
if (!empty($allowedCategories)) {
$placeholders = implode(',', array_fill(0, count($allowedCategories), '?'));
$query->addSelect([DB::raw("CASE WHEN users.profile_type IN ('{$vipTypes}') AND users.profile_type NOT IN ({$placeholders}) THEN 1 ELSE 0 END as contact_restricted")])
->addBinding($allowedCategories, 'select');
} else {
$query->addSelect([DB::raw("CASE WHEN users.profile_type IN ('{$vipTypes}') THEN 1 ELSE 0 END as contact_restricted")]);
}
} else {
$query->addSelect([DB::raw("0 as contact_restricted")]);
}
return $query;
}
public function applyScope(Builder $query, $arguments): Builder
{
$this->applyEventScope($query, $arguments);
$this->applyRoleScope($query, $arguments);
$this->applyOrder($query, $arguments);
$this->applySearch($query, $arguments);
$this->applyAbstractScope($query, $arguments);
$this->applyPresentationScope($query, $arguments);
$this->applySpeakerScope($query, $arguments);
$this->applyFilter($query, $arguments);
$this->applyFullData($query, $arguments);
$this->applyContactRestrictionScope($query, $arguments);
return $query;
}
public function applyFullData(Builder $query, $arguments): Builder
{
if (($arguments->full_data ?? false) && ($arguments->full_data == 'true' || $arguments->full_data === true)) {
$query->addSelect([
//'users.do_not_send_emails',
//'users.job_title',
//'users.country',
//'users.phone',
//'users.mobile',
'users.fax',
'users.po_box',
'users.street',
'users.city',
'users.state',
'users.post_code',
'users.linkedin_link',
'users.dietary_requirements',
'users.industry_code',
//'users.company',
//'users.company_address',
'users.biography',
'users.avatar',
'users.company_logo',
'users.checkboxes',
//'users.login_email_count',
//'eu.event_profile_data',
//'eu.confirmed_speaker',
'eu.speaker_added_via',
'eu.agreement_status',
'eu.agreement_details',
'users.created_by',
'users.created_at',
'users.updated_at',
]);
}
return $query;
}
public function applyFilter(Builder $query, $arguments): Builder
{
if (!$arguments->filter)
return $query;
$filter = is_array($arguments->filter) ? json_decode(json_encode($arguments->filter), FALSE) : json_decode($arguments->filter);
if ($filter->biography ?? false) {
$values = $filter->biography;
$this->getFilteredQuery($query, $values, 'biography');
}
if ($filter->avatar ?? false) {
$values = $filter->avatar;
$this->getFilteredQuery($query, $values, 'avatar');
}
if ($filter->company_logo ?? false) {
$values = $filter->company_logo;
$this->getFilteredQuery($query, $values, 'company_logo');
}
if ($filter->mobile ?? false) {
$values = $filter->mobile;
$this->getFilteredQuery($query, $values, 'mobile');
}
if ($filter->company ?? false) {
$values = $filter->company;
$this->getFilteredQuery($query, $values, 'company');
}
if ($filter->job_title ?? false) {
$values = $filter->job_title;
$this->getFilteredQuery($query, $values, 'job_title');
}
if ($filter->verification_status ?? false) {
$values = $filter->verification_status;
$this->getFilteredQuery($query, $values, 'verification_status');
}
if ($filter->confirmed_speaker ?? false) {
$values = $filter->confirmed_speaker;
$this->getFilteredQuery($query, $values, 'confirmed_speaker');
}
if ($filter->type ?? false) {
$values = $filter->type;
$this->getFilteredQuery($query, $values, 'type');
}
if ($filter->agreement_status ?? false) {
$values = $filter->agreement_status;
$this->getFilteredQuery($query, $values, 'agreement_status');
}
if ($arguments->confirmed_speakers ?? false) {
if ($filter->day ?? false) {
$values = $filter->day;
$this->getFilteredQuery($query, $values, 'start_date');
}
if ($filter->sessions ?? false) {
$values = $filter->sessions;
$this->getFilteredQuery($query, $values, 'id');
}
}
// Filter based on last synced date
if ($filter->last_synced_date ?? false) {
$query->where('users.updated_at', '>=', $filter->last_synced_date);
}
// event role
$eventRole = $arguments->event_role;
if ($eventRole == 'event_submitter') {
// abstract repository
$abstractRepository = new AbstractsRepository();
// Convert the Eloquent builder to a query builder
$queryBuilder = $query->getQuery();
// filter using submitter abstracts data
$abstractRepository->applyFilter($queryBuilder, $arguments, true);
}
return $query;
}
public function applySpeakerScope(Builder $query, $arguments)
{
if (isset($arguments->confirmed_speakers) && $arguments->confirmed_speakers !== 'false') {
$query->where('eu.confirmed_speaker', '=', true)
->leftJoin('slot_users as su', 'su.user_id', '=', 'users.id')
->leftJoin('session_users as seu', 'seu.user_id', '=', 'users.id')
->leftJoin('sessions as sess', function ($query) use ($arguments) {
$query->on('seu.session_id', '=', 'sess.id')
->where('sess.event_id', '=', $arguments->event->id);
})->leftJoin('slots', function ($query) use ($arguments) {
$query->on('su.slot_id', '=', 'slots.id')
->where('slots.event_id', '=', $arguments->event->id);
})->addSelect([
DB::raw('GROUP_CONCAT(DISTINCT slots.identifier) as slot_identifiers'),
DB::raw('eu.order as speaker_order')
]);
if (!($arguments->full_data ?? false) || $arguments->full_data == 'false' || $arguments->full_data === false) {
$query->addSelect([
'users.biography',
'users.avatar',
'users.company_logo',
'eu.agreement_status',
'eu.speaker_added_via',
'eu.verified_data',
'eu.previously_verified_data',
'users.passport_first_last_name',
'users.dob',
'users.uae_resident',
'users.emirates_id',
'users.passport_images',
'users.emirates_id_images',
//'eu.type',
]);
$query->with([
'presentations' => function ($query) use ($arguments) {
$query->select(
'presentations.id',
'presentations.abstract_id',
'presentations.submission_status',
'presentations.written_paper_status'
)->where('presentations.event_id', '=', $arguments->event->id);
},
'sessions' => function ($query) use ($arguments) {
$query->select(
'sessions.id',
'sessions.title',
'sessions.start_date',
'sessions.end_date',
'sessions.type',
'sessions.category',
'sessions.session_categories'
)->where('sessions.event_id', '=', $arguments->event->id);
},
'slots' => function ($query) use ($arguments) {
$query->select(
'slots.id',
'slots.session_id',
)->where('slots.event_id', '=', $arguments->event->id);
}, 'slots.session',
]);
}
} elseif ($arguments->confirmed_speakers === 'false') {
$query->where(function($q) {
$q->where('eu.confirmed_speaker', '=', false)
->orWhereNull('eu.confirmed_speaker');
});
}
}
public function applyPresentationScope(Builder $query, $arguments)
{
if ($arguments->presentation_ids) {
$presentationIds = array_map('intval', $arguments->presentation_ids);
$presentationUsers = $arguments->presentation_users;
$query->where(function ($query) use ($presentationUsers, $presentationIds) {
foreach ($presentationUsers as $presentationUser) {
if ($presentationUser == 'submitter') {
$query->orWhereIn('users.id', function ($query) use ($presentationIds) {
$query->select('p.user_id')
->from('presentations as p')
->whereIn('p.id', $presentationIds);
});
}
if ($presentationUser == 'presenter') {
$query->orWhereIn('users.id', function ($query) use ($presentationIds) {
$query->select('ps.user_id')
->from('presenters as ps')
->whereIn('ps.presentation_id', $presentationIds);
});
}
if ($presentationUser == 'reviewer') {
$query->orWhereIn('users.id', function ($query) use ($presentationIds) {
$query->select('pr.reviewer_id')
->from('presentation_reviews as pr')
->whereIn('pr.presentation_id', $presentationIds);
});
}
}
});
$query->groupBy('users.id');
}
}
public function scope($arguments, $callback = null)
{
//get query builder
$query = $this->query()
->select($this->selectColumns());
//applying different scopes based on the arguments
$this->applyScope($query, $arguments);
//resolve if there is any callback functions available
return $this->resolve($query, $arguments, $callback);
}
public function listing(Request $request, $paginate)
{
//building arguments
$arguments = $this->arguments($request);
//building query
$query = $this->scope($arguments);
return ($paginate && $arguments->paging != 'All') ? $query->paginate($arguments->paging, ['*'], 'page', $arguments->page) : $query->get();
}
public function getSpeakerCount(Event $event): int
{
return $event->users()
->where('event_user.confirmed_speaker', '=', true)
->count();
}
public function getEventUserCount(Event $event): array
{
$submitters = $event->users('event_submitter')->count();
$reviewers = $event->users('event_reviewer')->count();
$admins = $event->users('event_admin')->count();
$coChairs = $event->users('event_co_chair')->count();
$speakers = $event->speakers()->count();
return [
'submitter_count' => $submitters,
'reviewer_count' => $reviewers,
'admin_count' => $admins,
'co_chair_count' => $coChairs,
'confirmed_speakers' => $speakers
];
}
public function createOrUpdate($data, Event $event, $modifiedBy = null, $eventId = null): array
{
// roles
$roles = $data['roles'] ?? false;
// form type
$formType = isset($data['form_type']) ? $data['form_type'] : false;
if (!$roles)
validationErrorResponse(['Roles are missing']);
// id
$id = $data['id'] ?? null;
// user
$user = User::find($id);
if (!$user) {
if (!isset($data['password_confirmation']) || empty($data['password_confirmation'])) {
$password = randomStringGenerator(10);
$data = array_merge($data, [
'password' => $password,
'password_confirmation' => $password
]);
}
$user = null;
$existedUser = false;
// checking the user is already exist when the post request coming from directly in API
if(isset($data['email'])){
// get the user
$user = User::where('email', '=', $data['email'])->first();
$existedUser = true;
}
if(!$user){
// create user
$user = $this->createUser($data);
// track vip marked by on create
if (in_array($user->profile_type, VIP_PROFILE_TYPES)) {
$user->vip_marked_by = authUser()->id;
$user->save();
}
}
// attach user to th event
$eventUser = $this->attachUserToEvent($event, $user);
if (in_array('event_submitter', $data['roles'])) {
// type
$eventUser->type = $data['type'] ?? null;
}
// user added via
$eventUser->user_added_via = $formType == 'minimal' ? "MinimalForm" : "Form";
if($existedUser){
// sync roles
$eventUser->syncRoles($roles);
}else{
// assign roles
$eventUser->assignRole($roles);
}
if ($data['send_login'] ?? false) {
// send email to user with password
dispatch(new SendUserCreateMail($event, $user, 'new', $roles, $data['password_confirmation'] ? $data['password_confirmation'] : $password));
}
}
else {
// common rules
$commonRules = [
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'cc_emails' => ['string', 'nullable'],
'do_not_send_emails' => ['boolean'],
'profile_type' => ['string', 'nullable'],
];
// additional rules
$additionalRules = [
'job_title' => ['required', 'string', 'max:255'],
'company' => ['required', 'string', 'max:255'],
'country' => ['required', 'string', 'max:255'],
'mobile' => ['required', 'string', 'max:255'],
'password' => ['string', 'min:8', 'confirmed'],
'salutation' => ['string', 'nullable'],
'phone' => ['string', 'nullable'],
'fax' => ['string', 'nullable'],
'po_box' => ['string', 'nullable'],
'street' => ['string', 'nullable'],
'city' => ['string', 'nullable'],
'state' => ['string', 'nullable'],
'post_code' => ['string', 'nullable'],
'linkedin_link' => ['string', 'nullable'],
'industry_code' => ['string', 'nullable'],
'company_address' => ['string', 'nullable'],
'company_country' => ['string', 'nullable'],
'biography' => ['string', 'nullable'],
'dietary_requirements' => ['string', 'nullable'],
'nationality' => ['string', 'nullable'],
'passport_number' => ['string', 'nullable'],
'emirates_id' => ['regex:/^[0-9]{15}$/', 'nullable'],
'passport_first_last_name' => ['string', 'nullable'],
'user_secondary_image' => ['array', 'nullable'],
'avatar' => ['array', 'nullable'],
'dob' => [new DateRule],
'company_logo' => ['array', 'nullable'],
'send_login' => ['boolean', 'nullable'],
'uae_resident' => ['boolean', 'nullable'],
];
// Merge rules based on the form type
$rules = $formType === 'minimal' ? $commonRules : array_merge($commonRules, $additionalRules);
// validation
$validator = Validator::make($data, $rules);
// user data
$userData = $validator->validated();
if (isset($userData['password'])) {
$password = Hash::make($userData['password']);
$userData = array_merge($userData, [
'password' => $password,
'password_confirmation' => $password
]);
}
$userData['emirates_id'] = isset($userData['emirates_id']) && $userData['emirates_id'] !== '' ? $userData['emirates_id'] : null;
$user->fill($userData);
//keep changes field lists
$changes = $user->getDirty();
// is it new user
$user->new_user = false;
// Track modification if users table data changed
if (!empty($changes)) {
$this->trackUserModification($user, $modifiedBy, $eventId);
$this->trackVipMarkedBy($user, $changes);
}
$user->save();
// Get the changed fields using the common function
$changeFields = $this->getChangedFields($changes);
// verification changes for change fields
if ($changeFields) {
// published session status changing
$this->publishedSessionStatusUpdate($user);
// user verification fields changes
$this->userVerificationFieldsChanges($changeFields, $user);
}
$eventUser = getEventUser($event, $user);
if ($eventUser) {
$new_confirmed_speaker = $data['confirmed_speaker'] ?? null;
if ($eventUser->confirmed_speaker && !$new_confirmed_speaker) {
// Get the order of the speaker being removed
$removedSpeakerOrder = $eventUser->order;
// Decrement the order for speakers with an order greater than the removed speaker's order
EventUser::where('event_id', $event->id)
->where('confirmed_speaker', 1)
->whereNotNull('confirmed_speaker')
->where('order', '>', $removedSpeakerOrder)
->decrement('order');
// set the order of the removed speaker to null
$eventUser->order = null;
}
// type
$eventUser->type = $data['type'] ?? null;
}
if (!$eventUser) {
$eventUser = $this->attachUserToEvent($event, $user);
// type
$eventUser->type = $data['type'] ?? null;
// user added via
$eventUser->user_added_via = "Form";
}
$eventUser->syncRoles($roles);
if ($userData['send_login'] ?? false) {
//send email to user
dispatch(new SendUserCreateMail($event, $user, 'existing', $roles));
}
}
return [$user, $eventUser];
}
/**
* Get the list of changed fields from the user's data, including image file checks.
*
* @param array $changes
* @param array $imageFiles (optional)
* @param bool $removeAvatar (optional)
* @param bool $removeCompanyLogo (optional)
* @return array
*/
public function getChangedFields($changes, $imageFiles = [], $removeAvatar = false, $removeCompanyLogo = false)
{
// Initialize an empty array to store the changed fields
$changeFields = [];
// 1. Check regular fields (e.g., 'mobile', 'biography', 'job_title')
$changeFields = array_merge($changeFields, $this->checkRegularFields($changes));
// 2. Check image-related fields (e.g., 'avatar', 'company_logo') based on changes and file uploads
$changeFields = array_merge($changeFields, $this->checkImageFields($changes, $imageFiles, $removeAvatar, $removeCompanyLogo));
return $changeFields;
}
/**
* Check if any regular fields (e.g., 'mobile', 'biography', 'job_title') have been changed.
*
* @param array $changes
* @return array
*/
public function checkRegularFields($changes)
{
// List of fields to check
$fieldsToCheck = ['salutation','first_name','last_name','mobile', 'biography', 'job_title'];
// Filter out fields that have changed
return array_filter($fieldsToCheck, function($field) use ($changes) {
return array_key_exists($field, $changes);
});
}
/**
* Check if any image-related fields (e.g., 'avatar', 'company_logo') have been changed, including file uploads or removals.
*
* @param array $changes
* @param array $imageFiles
* @param bool $removeAvatar
* @param bool $removeCompanyLogo
* @return array
*/
public function checkImageFields($changes, $imageFiles, $removeAvatar, $removeCompanyLogo)
{
$imageFields = [];
// Check for 'avatar' change based on changes or file upload/removal
if (array_key_exists('avatar', $changes) || isset($imageFiles['avatar']) || $removeAvatar) {
$imageFields[] = 'avatar';
}
// Check for 'company_logo' change based on changes or file upload/removal
if (array_key_exists('company_logo', $changes) || isset($imageFiles['company_logo']) || $removeCompanyLogo) {
$imageFields[] = 'company_logo';
}
return $imageFields;
}
/**
* published session status changing
* @param $field
*/
public function publishedSessionStatusUpdate($user)
{
// session published status updating based on session users
$sessionUsers = SessionUser::all()->where('user_id', '=', $user->id);
$sessionIds = [];
foreach ($sessionUsers as $sessionUser) {
$session = Session::find($sessionUser->session_id);
$eventUsers = EventUser::all()->where('user_id', '=', $user->id)
->where('event_id', '=', $session->event_id);
foreach ($eventUsers as $eventUser) {
if ($eventUser->verification_status) {
if ($session->published_status) {
$session->published_status = false;
$session->save();
}
dispatch(new SendSessionUpdatedMail($session, $eventUser, 'speaker'));
}
array_push($sessionIds, $session->id);
}
}
// session published status updating based on slot users
$slotUsers = SlotUser::select('slots.session_id as session_id')
->join('slots', 'slots.id', '=', 'slot_users.slot_id')
->where('slot_users.user_id', '=', $user->id)->distinct()
->get();
foreach ($slotUsers as $slotUser) {
$session = Session::find($slotUser->session_id);
$eventUsers = EventUser::all()->where('user_id', '=', $user->id)
->where('event_id', '=', $session->event_id);
foreach ($eventUsers as $eventUser) {
if ($eventUser->verification_status) {
if ($session->published_status) {
$session->published_status = false;
$session->save();
}
if (!in_array($session->id, $sessionIds)) {
dispatch(new SendSessionUpdatedMail($session, $eventUser, 'slot user'));
}
}
}
}
}
/**
* user verification fields changes
* @param $field
*/
public function userVerificationFieldsChanges($changeFields, $user)
{
// event users
$eventUsers = EventUser::all()->where('user_id', '=', $user->id);
foreach ($eventUsers as $eventUser) {
if ($eventUser['verification_data']) {
$verificationData = $eventUser['verification_data'];
foreach ($changeFields as $field) {
$verificationData[$field] = false;
}
$verificationData['verify_all'] = false;
$eventUser['verification_data'] = $verificationData;
if ($eventUser->verified_data && $eventUser->verification_status) {
//send mail to event admins
dispatch(new SendUserVerificationFieldsChangedMail($user, $eventUser->event, $changeFields));
}
$eventUser['verification_status'] = false;
$eventUser->save();
}
}
return true;
}
public function deleteUser(User $user, EventUser $eventUser, $removeRole)
{
$sessionUserCount = SessionUser::whereUserId($user->id)->whereEventId($eventUser->event->id)
->count();
if ($sessionUserCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added to a session'
]);
$sessionsSlotUserCount = SlotUser::whereUserId($user->id)->whereEventId($eventUser->event->id)
->count();
if ($sessionsSlotUserCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added to a slot'
]);
if ($removeRole == 'event_submitter') {
$abstractUserCount = AbstractUser::where('abstract_user.user_id', '=', $user->id)
->join('abstracts as a', 'abstract_user.abstracts_id', '=', 'a.id')
->where('a.user_id', '!=', $user->id)
->where('a.event_id', '=', $eventUser->event->id)
->count();
if ($abstractUserCount)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added in an abstract'
]);
$presentersCount = Presenter::where('presenters.user_id', '=', $user->id)
->join('presentations as p', 'presenters.presentation_id', '=', 'p.id')
->join('abstracts as a', 'p.abstract_id', '=', 'a.id')
->where('a.user_id', '!=', $user->id)
->where('presenters.event_id', '=', $eventUser->event->id)
->count();
if ($presentersCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added to a presentation as a presenter'
]);
$reviewersCount = PresentationReview::where('presentation_reviews.reviewer_id', '=', $user->id)
->join('presentations as p', 'presentation_reviews.presentation_id', '=', 'p.id')
->join('abstracts as a', 'p.abstract_id', '=', 'a.id')
->where('a.user_id', '!=', $user->id)
->where('presentation_reviews.event_id', '=', $eventUser->event->id)
->count();
if ($reviewersCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added to a presentation as a reviewer'
]);
$abstracts = $user->abstracts($eventUser->event->id)->get();
if ($abstracts) {
$abstractInSlotCount = 0;
$presentationInSlotCount = 0;
foreach ($abstracts as $abstract) {
$slotCount = Slot::whereAbstractId($abstract->id)
->count();
if ($slotCount > 0)
$abstractInSlotCount = +1;
$presentation = Presentation::whereAbstractId($abstract->id)->first();
if ($presentation) {
$slotCount = Slot::wherePresentationId($presentation->id)
->count();
if ($slotCount > 0) {
$presentationInSlotCount = +1;
}
}
}
if ($abstractInSlotCount <= 0 && $presentationInSlotCount <= 0) {
foreach ($abstracts as $abstract) {
$presentation = Presentation::whereAbstractId($abstract->id)->first();
if ($presentation) {
$presentationRepo = new PresentationRepository();
$presentationRepo->deletePresentation($presentation);
}
//detaching abstract users
$abstractUsers = AbstractUser::whereAbstractsId($abstract->id)->get();
foreach ($abstractUsers as $abstractUser) {
$abstractUser->delete();
}
$event = $abstract->event;
//deleting abstract files
$fileFields = getField($event->form_settings, 'file', 'type');
$data = $abstract->data->toArray();
foreach ($fileFields as $fileField) {
$dataField = getField($data, $fileField['field_id']);
$dataField = array_shift($dataField);
$values = $dataField['value'] ?? [];
$values = $values != "" ? $values : [];
foreach ($values as $value) {
$fileId = $value['file_id'];
$this->deleteFile($fileId);
}
}
//deleting abstract scores
$scores = Score::whereAbstractId($abstract->id)->get();
$scoreRepo = new ScoreRepository();
foreach ($scores as $score) {
$scoreRepo->delete($score);
}
Storage::deleteDirectory('private/media/abstracts/' . $abstract->id);
$abstract->delete();
}
}
else {
return response([
'status' => false,
'message' => 'Cannot delete the user. Some abstracts or presentation created by this user is added to a slot'
]);
}
}
}
else if ($removeRole == 'event_reviewer' || $removeRole == 'event_co_chair' || $removeRole == 'event_admin') {
if ($eventUser->hasRole($removeRole)) {
$abstractUserCount = AbstractUser::where('abstract_user.user_id', '=', $user->id)
->join('abstracts as a', 'abstract_user.abstracts_id', '=', 'a.id')
->where('a.event_id', '=', $eventUser->event->id)
->count();
if ($abstractUserCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added in an abstract'
]);
$presentersCount = Presenter::whereUserId($user->id)->whereEventId($eventUser->event->id)
->count();
if ($presentersCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added to a presentation as a presenter'
]);
$reviewersCount = PresentationReview::whereReviewerId($user->id)->whereEventId($eventUser->event->id)
->count();
if ($reviewersCount > 0)
return response([
'status' => false,
'message' => 'Cannot delete the user. The user is added to a presentation as a reviewer'
]);
$scores = Score::where('user_id', '=', $user->id)->whereEventId($eventUser->event->id)->get();
if ($scores) {
$scoreRepo = new ScoreRepository();
foreach ($scores as $score) {
$scoreRepo->delete($score);
}
}
}
}
//delete speaker notes
$speakerNotes = SpeakerNotes::where(function($query) use ($user) {
$query->where('user_id', '=', $user->id)
->orWhere('speaker_id', '=', $user->id);
})
->whereEventId($eventUser->event->id)
->get();
if ($speakerNotes) {
foreach ($speakerNotes as $speakerNote) {
if($speakerNote->files){
foreach($speakerNote->files as $speakerFile){
$file = File::find($speakerFile['file_id']);
$file->delete();
}
}
$speakerNote->delete();
}
}
$eventUser->removeRole($removeRole);
//delete event user
if ($eventUser->roles()->count() == 0)
{
if ($eventUser->confirmed_speaker) {
// Get the order of the speaker being removed
$removedSpeakerOrder = $eventUser->order;
// Decrement the order for speakers with an order greater than the removed speaker's order
EventUser::where('event_id', $eventUser->event->id)
->where('confirmed_speaker', 1)
->whereNotNull('confirmed_speaker')
->where('order', '>', $removedSpeakerOrder)
->decrement('order');
// set the order of the removed speaker to null
$eventUser->order = null;
}
$eventUser->delete();
}
$events = $user->events()->count();
if ($events == 0) {
//deleting the entire user
if (!$user->hasRole('super_admin')) {
$this->removeUserImages($user, true, true, true);
$user->delete();
}
}
return response([
'status' => true,
'message' => 'Successfully removed user'
]);
}
public function resetPassword(Request $request)
{
$userId = $request->get('id');
$user = User::find($userId);
$event = $request->get('event');
$hasEventSlug = false;
if (!$event) {
$eventSlug = $request->get('event_slug');
$event = Event::where('slug_name', '=', $eventSlug)->first();
$hasEventSlug = true;
}
if (!$user) {
return response([
'status' => false,
'message' => 'User not found'
]);
}
else {
$password = randomStringGenerator(10);
$user->password = Hash::make($password);
$user->save();
//send mail to user with new password
dispatch(new SendAdminResetPasswordMail($user, $event, $password, $hasEventSlug));
return response([
'status' => true,
'message' => "Password reset successfully"
]);
}
}
public function sendLoginInfo(Request $request)
{
$userId = $request->get('id');
$user = User::find($userId);
$event = $request->get('event');
if (!$user) {
return response([
'status' => false,
'message' => 'User not found'
]);
} else {
$password = randomStringGenerator(10);
$user->password = Hash::make($password);
$user->save();
//send mail to user with new password
dispatch(new SendLoginInfoMail($user, $event, $password));
return response([
'status' => true,
'message' => "Email Sent Successfully"
]);
}
}
public function importUserTemplate($form_settings, $dtcm_fields_enabled, $short_form_terms_option)
{
//user headings
$heading =
[
'Email',
'Do Not Send Emails',
'Password',
'Password Confirmation',
'Cc Emails',
'Salutation',
'First Name',
'Last Name',
'Job Title',
'Linkedin Link',
'Country',
'Street',
'Town/City',
'State/Province',
'Zip Code',
'Mobile Number',
'Phone Number',
'Fax',
'Company Name',
'Company Address',
'Company Country',
'Industry Area',
'Biography',
'Roles',
'Send Login information',
];
$category_field = array_values(
array_filter(
$form_settings->toArray(),
fn($settings) => $settings['field_id'] == "category"
)
);
$sub_category_field = array_values(
array_filter(
$form_settings->toArray(),
fn($settings) => $settings['field_id'] == "subcategory",
)
);
$event_profile_data_headings = [
$category_field[0]['label'],
'Excluded Companies',
'Presentation submission',
'Maximum number of presentations',
'Confirmed Speaker',
'Type',
];
// if short term option not enabled
if(!$short_form_terms_option){
$short_form_option = [
'Send Adobe Contract',
];
$event_profile_data_headings = array_merge($event_profile_data_headings, $short_form_option);
}
//event profile data headings
if ($sub_category_field[0]['enabled']) {
$event_profile_data_headings = array_merge(
array_slice($event_profile_data_headings, 0, 1),
array($sub_category_field[0]['label']),
array_slice($event_profile_data_headings, 1)
);
}
$heading = array_merge($heading, $event_profile_data_headings);
if ($dtcm_fields_enabled) {
$DTCM_form_fields = [
'DOB',
'Nationality',
'Profile Type'
];
$heading = array_merge($heading, $DTCM_form_fields);
} else {
// Add Profile Type even when DTCM is disabled
$heading[] = 'Profile Type';
}
$excel = Excel::download(new ImportUserTemplate([$heading], $category_field, $sub_category_field, $short_form_terms_option), 'userTemplate.xlsx');
return $excel;
}
public function importUser($request, $event)
{
try {
$userFile = $request->allFiles();
//saving to local storage
$userFile['file']->store('import');
Excel::import(new UsersImport($event), $userFile['file']);
return response([
'status' => true,
'message' => "User Imported Successfully"
]);
}
catch (\Maatwebsite\Excel\Validators\ValidationException $e) {
$failures = $e->failures();
return response([
'status' => false,
'failures' => $failures
]);
}
}
public function fetchAgreementStatus(EventUser $eventUser): EventUser
{
$event = $eventUser->event;
$generalSettings = $event->general_settings;
$accessToken = $generalSettings['speaker_terms_prefill']['access_token'];
$adobeSign = getAdobeSign($accessToken);
if (!$eventUser->agreement_details)
return $eventUser;
$agreementDetails = $eventUser->agreement_details->toArray();
$agreementId = $agreementDetails['id'];
$response = $adobeSign->getAgreement($agreementId);
$responseContent = $response->getBody()->getContents();
$responseContent = json_decode($responseContent, true);
$eventUser->agreement_details = $responseContent;
$eventUser->agreement_status = $responseContent['status'];
$eventUser->save();
return $eventUser;
}
public function downloadAgreement(EventUser $eventUser): array
{
$event = $eventUser->event;
$generalSettings = $event->general_settings;
$accessToken = $generalSettings['speaker_terms_prefill']['access_token'];
$adobeSign = getAdobeSign($accessToken);
$agreementDetails = $eventUser->agreement_details->toArray();
if (!$agreementDetails)
validationErrorResponse(['Agreement not found']);
$agreementId = $agreementDetails['id'];
$response = $adobeSign->getAgreementCombinedDocument($agreementId);
$headers = $response->getHeaders();
$streamFileData = $response->getBody()->getContents();
$filename = "Speaker terms - " . $eventUser->user->name;
$headers['Content-Disposition'] = ['attachment;filename=' . $filename . '.pdf;'];
return [$headers, $streamFileData];
}
public function remindAgreementToSign(EventUser $eventUser)
{
$agreementStatus = $eventUser->agreement_status;
if (!$agreementStatus) {
dispatch(new SendSpeakerTerms([$eventUser]));
}
else {
$agreementStatuses = ["SIGNED", "CANCELLED", "EXPIRED"];
if (!in_array($agreementStatus, $agreementStatuses)){
$agreementDetails = $eventUser->agreement_details->toArray();
$agreementId = $agreementDetails['id'];
// Check if the user's email has changed since the agreement was created
$currentEmail = $eventUser->user->email;
// Try to get the email from agreement details (adjust the key if needed)
$agreementEmail = $agreementDetails['participantSetsInfo'][0]['memberInfos'][0]['email'] ?? null;
if ($agreementEmail && $agreementEmail !== $currentEmail) {
// Email has changed, regenerate agreement
// Optionally, you may want to cancel the old agreement here
$eventUser->agreement_details = null;
$eventUser->agreement_status = null;
$eventUser->save();
dispatch(new SendSpeakerTerms([$eventUser]));
return;
}
$participantId = $agreementDetails['participantSetsInfo'][0]['memberInfos'][0]['id'];
$reminderData = [
"recipientParticipantIds" => [
$participantId
],
"status" => 'ACTIVE',
];
$event = $eventUser->event;
$generalSettings = $event->general_settings;
$accessToken = $generalSettings['speaker_terms_prefill']['access_token'];
$adobeSign = getAdobeSign($accessToken);
$adobeSign->sendAgreementReminder($agreementId, $reminderData);
}
}
}
public function cancelAgreement(EventUser $eventUser, Event $event)
{
// agreement status
$agreementStatus = $eventUser->agreement_status;
// agreement details
$agreementDetails = $eventUser->agreement_details;
if (!$agreementStatus && $agreementDetails) {
return response([
'status' => false,
'message' => "No agreement found"
]);
}
else {
// new cancelled status set
$eventUser->agreement_status = 'CANCELLED';
// set agreement details null
$eventUser->agreement_details = null;
// Retrieve event profile data
$profileData = $eventUser->event_profile_data;
// Modify send adobe contract
if(isset($profileData['send_adobe_contract'])) {
$profileData['send_adobe_contract'] = false; // Modify the array directly
}
// Update event profile data
$eventUser->event_profile_data = $profileData;
// save event user
$eventUser->save();
}
}
public function generateToken($name = 'API'): NewAccessToken
{
$user = authUser();
$tokens = $user->tokens()->get();
if ($tokens->count() >= 5)
validationErrorResponse(['Only 5 tokens are allowed per user']);
$token = $user->createToken($name);
$token->accessToken->plain_text_token = $token->plainTextToken;
$token->accessToken->save();
return $token;
}
/**
* @param Builder $query
* @param $values
* @param $field
* @return void
*/
public function getFilteredQuery(Builder $query, $values, $field): void
{
$query->where(function ($query) use ($values, $field) {
foreach ($values as $value) {
if ($value == 'set')
$query->orWhere(function ($query) use ($field) {
$query->whereNotNull("users.$field")
->Where("users.$field", '!=', "");
});
else if ($value == 'not_set')
$query->orWhereNull("users.$field")
->orWhere("users.$field", '=', "");
else if ($value == 'verified')
$query->orWhere("eu.$field", '=', true);
else if ($value == 'not_verified')
$query->orWhere("eu.$field", '=', false);
else if ($value == 'strategic' || $value == 'technical' || $value == 'strategic,technical')
$query->orWhere("eu.$field", '=', $value);
else if ($value == 'yes' || $value == 'no'){
$query->orWhere("eu.$field", '=', $value == 'yes' ? 1 : 0)
->orWhere($value == 'no' ? "eu.$field" : null, null);
}
else if ($value == 'SIGNED' || $value == 'DOCUMENTS_NOT_YET_PROCESSED' || $value == 'OUT_FOR_SIGNATURE' || $value == 'CANCELLED')
$query->orWhere("eu.$field", '=', $value);
else if ($value == 'NULL')
$query->orWhere("eu.$field");
else if ($field == 'start_date') {
$query->where(function ($query) use ($value) {
$query->whereRaw('? between sess.start_date and sess.end_date', [$value]);
$query->orWhere('sess.start_date', 'LIKE', '%' . $value . '%');
$query->orWhere('slots.start_date', 'LIKE', '%' . $value . '%');
}
);
}
else if ($field == 'id') {
$query->orWhere("sess.$field", '=', $value);
$query->orWhere("slots.session_id", '=', $value);
}
else
$query->orWhere("users.$field", '=', $value);
}
});
}
/**
* user verification
* @param $request
*/
public function userVerification($request)
{
$userId = $request->get('id');
$user = User::find($userId);
if (!$user)
return response([
'status' => false,
'message' => 'User not found'
]);
$event = $request->get('event');
$eventUser = getEventUser($event, $user);
if (!$eventUser)
return response([
'status' => false,
'message' => 'Event User not found'
]);
$verificationData = $request->get('user_verification_data');
$loginUser = authUser();
$verifiedUserAvatar = null;
$verifiedUserCompanyLogo = null;
if ($user->avatar)
$verifiedUserAvatar = $this->verificationFilesHandling($user->avatar, $user, $eventUser, $verificationData['avatar'], 'avatar');
if ($user->company_logo)
$verifiedUserCompanyLogo = $this->verificationFilesHandling($user->company_logo, $user, $eventUser, $verificationData['company_logo'], 'company_logo');
// Get original verification_data from database BEFORE making changes
$previousVerificationData = $eventUser->verification_data ?? [];
// Get changed fields by comparing new verification_data with original verification_data
$changedFields = [];
if ($eventUser->verified_data) {
foreach ($verificationData as $field => $isVerified) {
// Skip verify_all field from changed_fields tracking
if ($field === 'verify_all') continue;
$previousValue = $previousVerificationData[$field] ?? true;
if ($isVerified !== $previousValue) {
$changedFields[] = $field;
}
}
$this->pushPreviousVerification($eventUser, $eventUser->verified_data, $previousVerificationData, $changedFields);
}
// Now update event user with new verification_data
$eventUser->verification_data = $verificationData;
$eventUser->verified_data = [
'id' => $user->id,
'avatar' => $verificationData['avatar'] ? $verifiedUserAvatar : null,
'company_logo' => $verificationData['company_logo'] ? $verifiedUserCompanyLogo : null,
'mobile' => $verificationData['mobile'] ? $user->mobile : null,
'biography' => $verificationData['biography'] ? $user->biography : null,
'job_title' => $verificationData['job_title'] ? $user->job_title : null,
'salutation' => $verificationData['salutation'] ? $user->salutation : null,
'first_name' => $verificationData['first_name'] ? $user->first_name : null,
'last_name' => $verificationData['last_name'] ? $user->last_name : null,
'verified_by' => $loginUser->email,
'verification_date' => date('Y-m-d H:i:s', time())
];
$eventUser->verification_status = true;
$eventUser->verification_date = date('Y-m-d H:i:s', time());
$eventUser->verified_by = $loginUser->id;
// event user save
$eventUser->save();
// user updates for view
$user->verification_data = $eventUser->verification_data;
$user->verified_data = $eventUser->verified_data;
$user->verification_status = $eventUser->verification_status;
$user->verification_date = $eventUser->verification_date;
$user->verified_by = $eventUser->verified_by;
// published session's status changed to false
$this->publishedSessionsChange($user);
return response([
'status' => true,
'user' => $user
]);
}
/**
* Push previous verification data to the event user
*
* @param EventUser $eventUser
* @param array $verifiedData
* @param array $previousVerificationData
* @param array $changedFields
*/
private function pushPreviousVerification($eventUser, $verifiedData, $previousVerificationData, $changedFields = [])
{
// Initialize or update the previously verified data
$history = (array) $eventUser->previously_verified_data ?? [];
// If data is provided, add it to the history
if ($verifiedData) {
$newEntry = $verifiedData;
// Exclude avatar and company_logo data from history
unset($newEntry['avatar'], $newEntry['company_logo']);
$newEntry['verified_at'] = $eventUser->verification_date;
// Get verifier user email from verified_by user ID
$verifier = \App\Models\User::find($eventUser->verified_by);
$newEntry['verified_by'] = $verifier ? $verifier->email : null;
$newEntry['changed_fields'] = $changedFields;
$newEntry['verification_data'] = $previousVerificationData;
array_unshift($history, $newEntry);
}
// Limit the history to the last 5 entries
$history = array_slice($history, 0, 5);
// Add order to each entry (1 = most recent, 5 = oldest)
foreach ($history as $index => $entry) {
$history[$index]['previous_verified_data_order'] = $index + 1;
}
// Update the event user with the new history
$eventUser->previously_verified_data = $history;
}
public function publishedSessionsChange($user)
{
// session users
$sessionUsers = SessionUser::whereUserId($user->id)->get();
// slot users
$slotUsers = SlotUser::select('slots.session_id as session_id')
->join('slots', 'slots.id', '=', 'slot_users.slot_id')
->where('slot_users.user_id', '=', $user->id)->distinct()
->get();
if ($sessionUsers) {
foreach ($sessionUsers as $sessionUser) {
// session
$session = Session::find($sessionUser->session_id);
if ($session->published_status) {
$session->published_status = false;
$session->save();
}
}
}
if ($slotUsers) {
foreach ($slotUsers as $slotUser) {
// session
$session = Session::find($slotUser->session_id);
// session
if ($session->published_status) {
$session->published_status = false;
$session->save();
}
}
}
return true;
}
/**
* handling user files
* @param $userFile
* @param $user
* @param $eventUser
* @param $fieldVerificationStatus
* @param $type
*/
public function verificationFilesHandling($userFile, $user, $eventUser, $fieldVerificationStatus, $type)
{
// delete previous files
$this->deletePreviousFiles($eventUser, $type);
if ($fieldVerificationStatus) {
$userPreviousFile = $userFile['public_path'];
// update file paths
$this->updateFilePaths($userFile, $user, $eventUser);
$thumbnailImageName = null;
$thumbnailPath = null;
if ($type === 'avatar') {
$thumbnailImageName = $this->getThumbnailImageName($userFile);
$thumbnailPath = $this->getThumbnailPath($eventUser, $thumbnailImageName);
}
// create and save file record
$file = $this->createFileRecord($userFile, $eventUser, $type, $thumbnailPath);
// move file to new location
Storage::putFileAs($userFile['filepath'], $userPreviousFile, $userFile['save_name'], 'public');
// handle avatar thumbnail
if ($type === 'avatar') {
$this->createAndSaveThumbnail($file, $thumbnailImageName);
$userFile['thumbnail_path'] = $thumbnailPath . "?v=" . md5(Carbon::now());
}
// save file ID to userFile
$userFile['file_id'] = $file->id;
}
return $userFile;
}
// Private helper methods
private function deletePreviousFiles($eventUser, $type)
{
if ($eventUser->verified_data && isset($eventUser->verified_data[$type])) {
$filePath = $eventUser->verified_data[$type]['filepath'];
$saveName = $eventUser->verified_data[$type]['save_name'];
// Check if the original image exists before deleting it
if (Storage::exists($filePath . '/' . $saveName)) {
Storage::delete($filePath . '/' . $saveName);
}
if ($type === 'avatar') {
$baseName = pathinfo($saveName, PATHINFO_FILENAME);
$thumbnailImageName = "{$baseName}_thumbnail.{$eventUser->verified_data[$type]['extension']}";
// Check if the thumbnail image exists before deleting it
if (Storage::exists($filePath . '/' . $thumbnailImageName)) {
Storage::delete($filePath . '/' . $thumbnailImageName);
}
}
// Check if the file record exists in the database before deleting it
$file = File::find($eventUser->verified_data[$type]['file_id']);
if ($file) {
$file->delete();
}
}
}
private function updateFilePaths(&$userFile, $user, $eventUser)
{
$userFile['filepath'] = str_replace("users/{$user->id}", "verifiedUsers/{$eventUser->id}", $userFile['filepath']);
$userFile['public_path'] = str_replace("users/{$user->id}", "verifiedUsers/{$eventUser->id}", $userFile['public_path']);
$userFile['value'] = str_replace("users/{$user->id}", "verifiedUsers/{$eventUser->id}", $userFile['value']);
}
private function getThumbnailImageName($userFile)
{
$baseName = pathinfo($userFile['save_name'], PATHINFO_FILENAME);
return "{$baseName}_thumbnail.{$userFile['extension']}";
}
private function getThumbnailPath($eventUser, $thumbnailImageName)
{
return User::FILE_VERIFIED_PUBLIC_PATH . "{$eventUser->id}/images/{$thumbnailImageName}";
}
private function createFileRecord($userFile, $eventUser, $type, $thumbnailPath)
{
$file = new File([
'model_id' => $eventUser->id,
'model' => 'App\Models\EventUser',
'filename' => $userFile['filename'],
'filepath' => $userFile['filepath'],
'extension' => $userFile['extension'],
'type' => "image",
'context' => "eventUser.$type",
'public' => 1
]);
$file->public_path = $userFile['public_path'];
$file->thumbnail_path = $thumbnailPath;
$file->save_name = $userFile['save_name'];
$file->save();
return $file;
}
private function createAndSaveThumbnail($file, $thumbnailImageName)
{
$imagePath = storage_path("app/{$file->filepath}{$file->save_name}");
$image = Image::make($imagePath);
// Resize the image to 500x500
$image->resize(500, 500, function ($constraint) {
$constraint->aspectRatio(); // Maintain aspect ratio
$constraint->upsize(); // Prevent upsizing if the image is smaller than 500x500
});
// thumbnail path
$thumbnailRelativePath = "{$file->filepath}{$thumbnailImageName}";
// Generate image stream
$imageStream = $image->stream($file->extension, 85);
Storage::put($thumbnailRelativePath, $imageStream->__toString(), 'public');
}
public function exportUsers($users, $event, $generalSettings, $confirmedSpeaker)
{
// Check if DTCM fields are enabled
$dtcmFieldsEnabled = $generalSettings['dtcm_form_fields_section']['dtcm_form_fields'] ?? false;
// Check if Amend Speaker Terms feature is enabled
$amendSpeakerTermsEnabled = $generalSettings['amend_speaker_terms_section']['enabled'] ?? false;
// Get Amend Speaker Terms checkboxes
$amendSpeakerTermsOptions = $generalSettings['amend_speaker_terms_section']['checkboxes'] ?? [];
// Generate heading row
$heading = $this->generateHeading($confirmedSpeaker, $amendSpeakerTermsEnabled, $dtcmFieldsEnabled, $amendSpeakerTermsOptions);
$excelData = [$heading];
// Loop through users
foreach ($users as $userObj) {
$user = User::find($userObj->id);
$eventUser = getEventUser($event->id, $user->id);
$eventRoles = $eventUser->roles->pluck('name')->toArray();
// Get avatar and company logo URLs
$headShotLogo = $user->avatar ? config('app.url') . '/' . $user->avatar['public_path'] : '';
$companyLogo = $user->company_logo ? config('app.url') . '/' . $user->company_logo['public_path'] : '';
// Base user fields
$data = $this->getUserBaseData($user, $event, $eventUser, $eventRoles, $headShotLogo, $companyLogo);
// Append speaker-specific fields if confirmed speaker
if ($confirmedSpeaker) {
$data += $this->getSpeakerData($user, $event, $eventUser, $amendSpeakerTermsEnabled, $amendSpeakerTermsOptions);
}
// Append DTCM-specific fields if enabled
if ($dtcmFieldsEnabled) {
$data += $this->getDtcmData($user);
}
$excelData[] = $data;
}
// Export the Excel file
return Excel::download(new ExportUsersClass($excelData, count($heading)), 'users.xlsx');
}
/**
* Generate Excel header row
*/
private function generateHeading($confirmedSpeaker, $amendSpeakerTermsEnabled, $dtcmFieldsEnabled, $amendSpeakerTermsOptions)
{
$heading = [
'User ID', 'Salutation', 'First Name', 'Last Name', 'Email', 'Send Email or not', 'CC Emails',
'Job Title', 'LinkedIn Profile Link', 'Country', 'Street', 'Town/City', 'State/Province', 'Zip Code',
'Mobile Number', 'Phone Number', 'Fax', 'Company Name', 'Company Country',
'HeadShot URL', 'Company Logo URL', 'Industry Area', 'Biography', 'Type',
'Is Confirmed Speaker?', 'Is Reviewer?', 'Is Submitter?'
];
if ($confirmedSpeaker) {
$heading = array_merge($heading, [
'Speaker Term Status', 'Last Note Added', 'Last Note Attachment'
]);
if ($amendSpeakerTermsEnabled) {
$heading[] = 'Amend Speaker Term';
}
}
if ($dtcmFieldsEnabled) {
$heading = array_merge($heading, [
'First and Last Name As Stated On Passport', 'Date of birth', 'Nationality',
'Passport Number', 'Emirates ID'
]);
}
return $heading;
}
/**
* Base user data row
*/
private function getUserBaseData($user, $event, $eventUser, $eventRoles, $headShotLogo, $companyLogo)
{
// Get auth user for access check
$authUser = authUser();
$authEventUser = getEventUser($event, $authUser);
// Check if contact should be masked
$maskContact = shouldRestrictContact($user, $authEventUser);
$maskedValue = '********';
return [
'id' => $user->id,
'salutation' => $user->salutation ?? '',
'first_name' => $user->first_name ?? '',
'last_name' => $user->last_name ?? '',
'email' => $maskContact ? $maskedValue : ($user->email ?? ''),
'do_not_send_emails' => $user->do_not_send_emails ? 'Do not send' : 'Send',
'cc_emails' => $user->cc_emails ?? '',
'job_title' => $user->job_title ?? '',
'linkedin_link' => $user->linkedin_link ?? '',
'country' => $user->country ?? '',
'street' => $user->street ?? '',
'city' => $user->city ?? '',
'state' => $user->state ?? '',
'post_code' => $user->post_code ?? '',
'mobile' => $maskContact ? $maskedValue : ($user->mobile ?? ''),
'phone' => $maskContact ? $maskedValue : ($user->phone ?? ''),
'fax' => $user->fax ?? '',
'company' => $user->company ?? '',
'company_country' => $user->company_country ?? '',
'avatar' => $headShotLogo,
'company_logo' => $companyLogo,
'industry_code' => $user->industry_code ?? '',
'biography' => $user->biography ?? '',
'type' => ucfirst($eventUser->type == 'strategic,technical' ? 'strategic and technical' : $eventUser->type),
'is_confirmed_speaker' => $eventUser->confirmed_speaker ? 'Yes' : 'No',
'is_reviewer' => in_array('event_reviewer', $eventRoles) ? 'Yes' : 'No',
'is_submitter' => in_array('event_submitter', $eventRoles) ? 'Yes' : 'No',
];
}
/**
* Confirmed speaker-related data
*/
private function getSpeakerData($user, $event, $eventUser, $amendSpeakerTermsEnabled, $amendSpeakerTermsOptions)
{
$data = [];
// Get latest note
$latestNote = SpeakerNotes::where('speaker_id', $user->id)
->where('event_id', $event->id)
->latest('created_at')
->first();
$attachments = [];
if ($latestNote && $latestNote->files) {
foreach ($latestNote->files as $file) {
$attachments[] = URL::temporarySignedRoute(
'downloadAttachment',
now()->addHour(),
['file_id' => $file['file_id']]
);
}
}
$data += [
'speaker_term_status' => $eventUser->agreement_status ?? '',
'last_note_added' => $latestNote->note ?? '',
'notes_attachments' => !empty($attachments) ? implode(', ', $attachments) : null,
];
// Get selected checkboxes if enabled
if ($amendSpeakerTermsEnabled) {
$amendTerms = AmendSpeakerTerms::where('speaker_id', $user->id)
->where('event_id', $event->id)
->first();
$data['selected_checkboxes'] = $amendTerms && $amendTerms->selected_checkboxes
? implode(', ', array_filter(array_map(function ($checkboxId) use ($amendSpeakerTermsOptions) {
$checkbox = collect($amendSpeakerTermsOptions)->firstWhere('id', $checkboxId);
return $checkbox['label'] ?? null;
}, $amendTerms->selected_checkboxes)))
: null;
}
return $data;
}
/**
* DTCM-related data
*/
private function getDtcmData($user)
{
return [
'passport_first_last_name' => $user->passport_first_last_name ?? '',
'dob' => $user->dob ? date('d/m/Y', strtotime($user->dob)) : '',
'nationality' => $user->nationality ?? '',
'passport_number' => $user->passport_number ?? '',
'emirates_id' => $user->emirates_id ?? '',
];
}
/**
* getScheduleMailData()
*
* @return array
*/
public function getScheduleMailData(object $data):Array{
$filter = json_decode($data->filter, 1);
$to = [];
$email = [];
// storing recipients
if(isset($filter['recipients'])){
foreach($filter['recipients'] as $recipients){
$to[] = $recipients['email'];
}
}
// getting all users if there is no user selected
if(!isset($filter['users']) || count($filter['users']) <= 0){
$filter['users'] = $this->getAllUsersByEmailType('User');
}
$formatedFilter = [];
foreach($this->userFields as $fields){
$arrayColumn = (isset($filter[$fields])) ? $filter[$fields] : [];
$formatedFilter[$fields] = array_column($arrayColumn, 'value');
}
$event = Event::find($data->event_id);
$request = new Request();
$sendList = [];
foreach($filter['users'] as $user){
switch($user['value']){
case "Submitters":
$event_role = 'event_submitter';
break;
case "Admins":
$event_role = 'event_admin';
break;
case "Reviewers":
$event_role = 'event_reviewer';
break;
case "Co-chairs":
$event_role = 'event_co_chair';
break;
}
$request->replace([
'paging' => 'All',
'sort' => '{}',
'filter' => json_encode($formatedFilter),
'role' => 'event_admin',
'event' => $event,
'event_role' => $event_role
]);
$userController = app(\App\Http\Controllers\UserController::class);
$result = $userController->list($request, false);
$resultArray = json_decode($result->getContent(), true);
$sendList = array_merge($sendList, $resultArray['users']['data']);
}
// making unique array
$temp = array_unique(array_column($sendList, 'email'));
$sendListUnique = array_intersect_key($sendList, $temp);
$variables = config('schedule_mail_settings')->User->variables;
$valueToCheck = "Reviewers";
if (in_array($valueToCheck, array_column($filter['users'], "value"))) {
$variables = collect($variables);
$newVariables = collect([
(object) [
'label' => 'Assigned Abstract Count',
'value' => 'assigned_abstract_count',
],
(object) [
'label' => 'Score Count',
'value' => 'score_count',
],
(object) [
'label' => 'Remaining Score Count',
'value' => 'remaining_score_count',
],
]);
$variables = $variables->concat($newVariables);
}
foreach ($sendListUnique as $key => $res) {
$body = $this->replaceMacros($res, $data, $variables, $event->event_name);
$substring = '{remaining_score_count}';
$valuesToCheck = ["Submitters", "Admins", "Co-chairs"];
if (
(
in_array($valueToCheck, array_column($filter['users'], "value")) &&
strpos($data->body, $substring) &&
isset($res['remaining_score_count']) &&
$res['remaining_score_count'] > 0
)
||
(
!strpos($data->body, $substring)
)
||
(
strpos($data->body, $substring) &&
count(array_intersect($valuesToCheck, array_column($filter['users'], "value"))) > 0 &&
(isset($res['remaining_score_count']) ? $res['remaining_score_count'] > 0 : true)
)
) {
$event = Event::find($data->event_id);
$email[] = [
'to' => $to,
'bcc' => $res['send_mail'],
'subject' => $data->subject,
'body' => $body,
'attachments' => $data->attachments,
'event' => $event,
'event_id' => $data->event_id,
'id' => $data->id,
];
}
}
return $email;
}
/**
* Replace macros
*
* @return string
*/
public function replaceMacros(array $row, object $data, $variables, string $event_name): String {
$body = $data->body;
foreach($variables as $key => $var){
if($var->value == 'event_name')
continue;
$replace = @$row[$var->value];
if($var->value == 'user_id')
$replace = $row['id'];
if($var->value == "verification_status"){
if($row['verification_status']){
$replace = "Verified";
}else{
$replace = "Not Verified";
}
}
$body = Str::replace("{".$var->value."}", $replace, $body);
}
$body = Str::replace("{event_name}", $event_name, $body);
return $body;
}
/**
* getSessionScheduleMailData()
*
* @return array
*/
public function getSpeakersScheduleMailData(object $data):Array{
$filter = json_decode($data->filter, 1);
$to = [];
$email = [];
// storing recipients
if(isset($filter['recipients'])){
foreach($filter['recipients'] as $recipients){
$to[] = $recipients['email'];
}
}
// getting all users if there is no user selected
if(!isset($filter['users']) || count($filter['users']) <= 0){
$filter['users'] = $this->getAllUsersByEmailType('Confirmed Speakers');
}
$formatedFilter = [];
foreach($this->userFields as $fields){
$arrayColumn = (isset($filter[$fields])) ? $filter[$fields] : [];
$formatedFilter[$fields] = array_column($arrayColumn, 'value');
}
$event = Event::find($data->event_id);
$request = new Request();
$request->replace([
'paging' => 'all',
'sort' => '{}',
'filter' => json_encode($formatedFilter),
'role' => 'event_admin',
'event' => $event,
'confirmed_speakers' => true
]);
$sendList = [];
foreach($filter['users'] as $user){
switch($user['value']){
case "Submitters":
$request->event_role = 'event_submitter';
break;
case "Admins":
$request->event_role = 'event_admin';
break;
case "Reviewers":
$request->event_role = 'event_reviewer';
break;
case "Co-chairs":
$request->event_role = 'event_co_chair';
break;
}
$result = $this->listing($request, false)->toArray();
$sendList = [...$sendList, ...$result];
}
// making unique array
$temp = array_unique(array_column($sendList, 'email'));
$sendListUnique = array_intersect_key($sendList, $temp);
foreach($sendListUnique as $key => $res){
$formSettings = json_decode(json_encode(config('schedule_mail_settings')), 1);
$formSettings = json_decode(json_encode($formSettings['Confirmed Speakers']['variables']));
$body = $this->replaceMacros($res, $data, $formSettings, $event->event_name);
$event = Event::find($data->event_id);
$email[] = [
'to' => $to,
'bcc' => $res['send_mail'],
'userId' => $res['id'],
'subject' => $data->subject,
'body' => $body,
'attachments' => $data->attachments,
'event' => $event,
'event_id' => $data->event_id,
'id' => $data->id,
];
}
return $email;
}
// adding selected files in zip file
public function addingToZip(object $users, Request $request, $downloadMode = 'multiple')
{
// Delete older files
$this->deleteOlderZipFiles();
// Create a new ZipArchive instance and open or create the ZIP file
$zip = new ZipArchive;
$zipName = 'userFiles' . time() . '.zip';
$zip->open($zipName, ZipArchive::CREATE | ZipArchive::OVERWRITE);
// Get the dtcm form status
$dtcmFormStatus = $request['dtcm_form'];
$selectedDownloads = $request['selected_downloads'];
if ($downloadMode === 'single') {
// All files in root, with [User Name]_[File Type].[extension]
foreach ($users as $user) {
$this->addUserFilesToZipSingleFolder($user, $zip, $dtcmFormStatus, $selectedDownloads);
}
} else {
// Per-user folders, original naming
foreach ($users as $user) {
$this->addUserFilesToZip($user, $zip, $dtcmFormStatus, $selectedDownloads);
}
}
// Close the ZIP archive
$zip->close();
// Set the file path
$filepath = public_path($zipName);
if (file_exists($filepath)) {
// maximum size
$maximumSizeInMB = 50;
// Check if the file size is too large
$fileSize = filesize($zipName);
if ($fileSize > $maximumSizeInMB * 1024 * 1024) {
// Handle large file size
return $this->handleLargeFileSize($zipName, $request);
}
}
// Return a response with the ZIP file for download or an error message
return file_exists($filepath)
? $this->returnZipFileResponse($filepath, $zipName)
: response(['status' => false, 'message' => "File not found"]);
}
/**
* Add user files to ZIP in a single folder.
* All files are placed in the root of the ZIP with the format: [User Name]_[File Type].[extension]
* No subfolders are created.
*
* @param object $user
* @param \ZipArchive $zip
* @param bool $dtcmFormStatus
* @param array $selectedDownloads
*/
private function addUserFilesToZipSingleFolder($user, $zip, $dtcmFormStatus, $selectedDownloads)
{
// Create a safe user name for the file prefix
$userName = $user->name
? preg_replace('/[^A-Za-z0-9_\-]/', '_', $user->name)
: preg_replace('/[^A-Za-z0-9_\-]/', '_', $user->email);
// Helper to add a file to the root with the user name prefix
$addFile = function ($fileData, $typeLabel) use ($zip, $userName) {
if (isset($fileData['file_id'])) {
$file = \App\Models\File::whereId($fileData['file_id'])->first();
if ($file && file_exists(storage_path('app/' . $file->filepath . '/' . $file->save_name))) {
$ext = pathinfo($file->save_name, PATHINFO_EXTENSION);
// File is added to the root with [User Name]_[File Type].[extension] format
$filename = "{$userName}_{$typeLabel}.{$ext}";
$zip->addFile(storage_path('app/' . $file->filepath . '/' . $file->save_name), $filename);
}
}
};
// Add profile image (headshot) if selected
if ($user->avatar && $selectedDownloads['profileImage']) {
$addFile($user->avatar, 'Headshot');
}
// Add company logo if selected
if ($user->company_logo && $selectedDownloads['companyLogo']) {
$addFile($user->company_logo, 'Company_Logo');
}
// Add passport images if selected and dtcmFormStatus is true
if ($dtcmFormStatus && $user->passport_images && $selectedDownloads['passportImage']) {
foreach ($user->passport_images as $idx => $img) {
$addFile($img, 'Passport_Image_' . ($idx + 1));
}
}
// Add Emirates ID images if selected and dtcmFormStatus is true
if ($dtcmFormStatus && $user->emirates_id_images && $selectedDownloads['emiratesIdImage']) {
foreach ($user->emirates_id_images as $idx => $img) {
$addFile($img, 'Emirates_ID_Image_' . ($idx + 1));
}
}
}
public function deleteOlderZipFiles()
{
// public folder path
$publicPath = public_path();
// Get a list of all files in the public folder
$publicFiles = scandir($publicPath);
// delete zip files older than 5 hour
$expirationTime = now()->subHours(5)->timestamp;
foreach ($publicFiles as $file) {
$filePath = $publicPath . '/' . $file;
if (is_file($filePath) && pathinfo($filePath, PATHINFO_EXTENSION) === 'zip') {
$fileModificationTime = filemtime($filePath);
if ($fileModificationTime < $expirationTime) {
// Delete the zip file
unlink($filePath);
}
}
}
}
private function addUserFilesToZip($user, $zip, $dtcmFormStatus, $selectedDownloads)
{
// Create a folder with the email and user ID
$folderName = $user->name ? $user->name : $user->email;
// Avatar to zip
if ($user->avatar && $selectedDownloads['profileImage']) {
$this->filesToZip($user->avatar, $zip, $folderName, $folderName);
}
// Company logo to zip
if ($user->company_logo && $selectedDownloads['companyLogo']) {
$this->filesToZip($user->company_logo, $zip, $folderName, 'company_logo');
}
if ($dtcmFormStatus) {
// Passport copy to zip
if ($user->passport_images && $selectedDownloads['passportImage']) {
$this->filesToZip($user->passport_images, $zip, $folderName, 'passport_image');
}
// Emirates ID to zip
if ($user->emirates_id_images && $selectedDownloads['emiratesIdImage']) {
$this->filesToZip($user->emirates_id_images, $zip, $folderName, 'emirates_id_image');
}
}
}
private function handleLargeFileSize($zipName, $request)
{
// Generate a temporary URL for the file with an expiration time
$expiration = now()->addHours(3);
$temporaryUrl = URL::temporarySignedRoute(
'downloadUserFilesZip',
$expiration,
['filename' => $zipName]
);
// Send an email to the user with the temporary download link
$userEmail = $request->user()->email;
// event
$event = $request->get('event');
// send email to user with download link
$this->sendUserFilesDownloadLinkMail($event, $userEmail, $temporaryUrl);
// Return the temporary URL to the user
return response()->json([
'status' => true,
'temporary_url' => urldecode($temporaryUrl),
'message' => "File is too large for direct download.
We've emailed you a download link, Please use that link to download the file.
",
]);
}
public function sendUserFilesDownloadLinkMail($event, $userEmail, $temporaryUrl)
{
SendUserFilesDownloadLinkEmail::dispatch($event, $userEmail, $temporaryUrl);
}
private function returnZipFileResponse($filepath, $zipName)
{
return response()->download($filepath, $zipName, ['Content-Type: application/zip', 'Content-Length: ' . filesize($filepath)])->deleteFileAfterSend(true);
}
public function filesToZip($data, $zip, $folderName, $prefix)
{
if (!is_array($data)) {
$counter = 1;
foreach ($data as $userFile) {
if (isset($userFile['file_id'])) {
$file = \App\Models\File::whereId($userFile['file_id'])->first();
if ($file) {
// Ensure there's a "/" after "passportImages" in the file path
$file->filepath = rtrim($file->filepath, '/') . '/';
// file path
$filePath = storage_path("app/{$file->filepath}{$file->save_name}");
// file name
$name = $folderName . '/' . $prefix . '_' . $counter . '.' . $file->extension;
// add to file
if (file_exists($filePath)) {
$zip->addFile($filePath, $name);
$counter++;
}
}
}
}
} else {
if(isset($data['file_id'])) {
// Handle a single file
$file = \App\Models\File::whereId($data['file_id'])->first();
if ($file) {
// file path
$filePath = storage_path("app/{$file->filepath}{$file->save_name}");
// file name
$name = $folderName . '/' . $prefix . '.' . $file->extension;
// add to file
if (file_exists($filePath)) {
$zip->addFile($filePath, $name);
}
}
}
}
}
public function getSpeakerNotesRepository(): SpeakerNotesRepository
{
return new SpeakerNotesRepository();
}
/**
* Global user search across all events
* Reuses existing search patterns but removes event scope
*
* @param Request $request
* @param Event $currentEvent
* @return array
*/
public function globalUserSearch(Request $request, Event $currentEvent): array
{
$query = User::select([
'users.id',
'users.first_name',
'users.last_name',
'users.salutation',
'users.email',
'users.cc_emails',
'users.do_not_send_emails',
'users.company',
'users.company_address',
'users.company_country',
'users.company_logo',
'users.job_title',
'users.country',
'users.phone',
'users.mobile',
'users.fax',
'users.street',
'users.city',
'users.state',
'users.post_code',
'users.linkedin_link',
'users.industry_code',
'users.biography',
'users.dietary_requirements',
'users.nationality',
'users.profile_type',
'users.avatar',
'users.vip_marked_by',
'users.last_modified_by',
'users.last_modified_at',
DB::raw('concat(users.first_name," ",users.last_name) as name')
]);
// Apply search filters
$this->applyGlobalSearchFilters($query, $request);
// Get users with pagination
$users = $query->limit(50)->get();
// Auth user for VIP restriction check
$authUser = authUser();
$authEventUser = getEventUser($currentEvent, $authUser);
// Add events and current event status for each user
return $users->map(function ($user) use ($currentEvent, $authEventUser) {
// Get user's events with roles
$userEvents = $this->getUserEventsWithRoles($user->id);
// Check if user is in current event
$isInCurrentEvent = $userEvents->contains('id', $currentEvent->id);
// Check VIP contact restriction
$contactRestricted = shouldRestrictContact($user, $authEventUser);
return [
'id' => $user->id,
'name' => $user->name,
'salutation' => $user->salutation,
'email' => $user->email,
'cc_emails' => $user->cc_emails,
'do_not_send_emails' => $user->do_not_send_emails,
'phone' => $user->phone,
'mobile' => $user->mobile,
'fax' => $user->fax,
'company' => $user->company,
'company_address' => $user->company_address,
'company_country' => $user->company_country,
'company_logo' => $user->company_logo,
'job_title' => $user->job_title,
'country' => $user->country,
'street' => $user->street,
'city' => $user->city,
'state' => $user->state,
'post_code' => $user->post_code,
'linkedin_link' => $user->linkedin_link,
'industry_code' => $user->industry_code,
'biography' => $user->biography,
'dietary_requirements' => $user->dietary_requirements,
'nationality' => $user->nationality,
'profile_type' => $user->profile_type,
'avatar' => $user->avatar,
'vip_marked_by' => $user->vip_marked_by,
'last_modified_by' => $user->last_modified_by,
'last_modified_at' => $user->last_modified_at,
'contact_restricted' => $contactRestricted,
'events' => $userEvents->map(function ($event) {
return [
'name' => $event->event_name,
'year' => $event->year,
'roles' => explode(',', $event->roles),
'confirmed_speaker' => (bool) $event->confirmed_speaker,
'type' => $event->type,
];
}),
'is_in_current_event' => $isInCurrentEvent
];
})->toArray();
}
/**
* Apply search filters for global search
* Reuses existing filter patterns
*
* @param Builder $query
* @param Request $request
*/
private function applyGlobalSearchFilters(Builder $query, Request $request): void
{
// Name search (first_name + last_name)
if ($request->filled('name')) {
$name = strtolower($request->get('name'));
$query->where(function ($q) use ($name) {
$q->whereRaw('lower(concat(first_name," ",last_name)) like ?', ["%{$name}%"])
->orWhereRaw('lower(first_name) like ?', ["%{$name}%"])
->orWhereRaw('lower(last_name) like ?', ["%{$name}%"]);
});
}
// Email search
if ($request->filled('email')) {
$email = strtolower($request->get('email'));
$query->whereRaw('lower(email) like ?', ["%{$email}%"]);
}
// Company search
if ($request->filled('company')) {
$company = strtolower($request->get('company'));
$query->whereRaw('lower(company) like ?', ["%{$company}%"]);
}
// Job title search
if ($request->filled('job_title')) {
$jobTitle = strtolower($request->get('job_title'));
$query->whereRaw('lower(job_title) like ?', ["%{$jobTitle}%"]);
}
// Country search
if ($request->filled('country')) {
$country = strtolower($request->get('country'));
$query->whereRaw('lower(country) like ?', ["%{$country}%"]);
}
}
/**
* Get user's events with roles
* Reuses existing relationship logic
*
* @param int $userId
* @return \Illuminate\Support\Collection
*/
private function getUserEventsWithRoles(int $userId)
{
return DB::table('events')
->join('event_user', 'events.id', '=', 'event_user.event_id')
->join('model_has_roles as mhs', 'mhs.model_id', '=', 'event_user.id')
->where('mhs.model_type', '=', EventUser::class)
->join('roles as r', 'r.id', '=', 'mhs.role_id')
->where('event_user.user_id', '=', $userId)
->select([
'events.id',
'events.event_name',
'events.year',
'event_user.confirmed_speaker',
'event_user.type',
DB::raw('GROUP_CONCAT(DISTINCT r.name) as roles')
])
->groupBy('events.id', 'events.event_name', 'events.year', 'event_user.confirmed_speaker', 'event_user.type')
->get();
}
/**
* Get autocomplete data for specified field
*
* @param string $field
* @param string $query
* @return array
*/
public function getAutocompleteData(string $field, string $query): array
{
$query = strtolower(trim($query));
switch ($field) {
case 'name':
return User::select(DB::raw('concat(first_name," ",last_name) as value'))
->whereRaw('lower(concat(first_name," ",last_name)) like ?', ["%{$query}%"])
->distinct()
->limit(10)
->pluck('value')
->toArray();
case 'email':
return User::select('email as value')
->whereRaw('lower(email) like ?', ["%{$query}%"])
->distinct()
->limit(10)
->pluck('value')
->toArray();
case 'company':
return User::select('company as value')
->whereNotNull('company')
->where('company', '!=', '')
->whereRaw('lower(company) like ?', ["%{$query}%"])
->distinct()
->limit(10)
->pluck('value')
->toArray();
default:
return [];
}
}
}
Directory Contents
Dirs: 0 × Files: 17