PHP 7.4.33
Preview: PresentationRepository.php Size: 59.48 KB
/var/www/multi-event-cfp.bitkit.dk/httpdocs/app/Repositories/PresentationRepository.php
<?php

namespace App\Repositories;

use App\Jobs\SendPresentationSubmittedMails;
use App\Jobs\SendSpeakerTerms;
use App\Models\Abstracts;
use Illuminate\Support\Facades\Log;
use App\Models\EventUser;
use App\Models\Presentation;
use App\Models\PresentationComment;
use App\Models\PresentationReview;
use App\Models\Presenter;
use App\Models\User;
use App\Models\Event;
use URL;
use ZipArchive;
use App\Rules\PresentationDataRule;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use App\Jobs\SendPresentationEditStatusChangeMail;
use App\Jobs\SendPresentationStatusChangeMail;
use App\Jobs\SendAssignReviewerMail;
use App\Jobs\SendReviewerApprovedMail;
use App\Jobs\SendPresentationDownloadLinkEmail;
use App\Models\Slot;
use Illuminate\Support\Str;
use PhpParser\Node\Expr\AssignOp\Concat;

class PresentationRepository extends Repository
{
    public $fileName;

    protected $presentaionFields = ["submission_status", "assigned_status", "written_paper_status", "reviewer_assigned_status"];
    public function __construct($event = null)
    {
        $this->for($event);
    }

    public function model(): Presentation
    {
        return new Presentation;
    }

    public function query($as = null)
    {
        $model = $this->model();
        return $model->newQuery();
    }

    public function createOrUpdate(Request $request, Abstracts $abstract = null): Presentation
    {
        $presentationId = $request->get('id');
        $invitationResponse = $request->get('invitation_response');
        $submissionStatusValue = $request->get('submission_status');
        $input = $request->input();
        if ($presentationId || $invitationResponse || $submissionStatusValue == "Draft") {
            $role = $request->get('role');
            $event = $request->get('event');
            $submissionStatus = $request->get('submission_status', 'Draft');
            $presentersOnly = $request->get('presenter_only', false) == 'true';
            if ($role != 'event_submitter' && $role != 'event_admin')
                validationErrorResponse(['Access denied']);
            if (!$presentationId) {

                if ($abstract) {
                    $presentation = Presentation::whereEventId($event->id)
                        ->whereAbstractId($abstract->id)
                        ->first();
                    if ($presentation)
                        validationErrorResponse(['Presentation already exists']);
                }
                $presentation = new Presentation([
                    'user_id' => $abstract->user_id ?? authUser()->id,
                    'submission_status' => $submissionStatus,
                    'event_id' => $event->id,
                    'abstract_id' => $abstract->id ?? null,
                    'type' => $abstract ? 'abstract' : 'presentation',
                    'presentation_type' => $abstract->presentation_type ?? null
                ]);
                $presentation->save();
                if ($abstract) {
                    $presenters = $abstract->users('abstract_presenter')->pluck('id')->toArray();
                    $presentation->presenters()->attach($presenters, ['event_id' => $presentation->event_id]);
                    $presentationPresenters = Presenter::wherePresentationId($presentation->id)->get();

                    $eventUsers = [];
                    foreach ($presentationPresenters as $presentationPresenter) {
                        $eventUser = getEventUser($event->id, $presentationPresenter->user_id);
                        $eventUsers[] = $eventUser;
                        $this->markAsConfirmedSpeaker($eventUser, 'Abstract', $event->id);
                    }

                    if (!empty($eventUsers) && !$abstract->presentation_tc_status)
                        dispatch(new SendSpeakerTerms($eventUsers));
                }
                else {
                    // managing presenters in presentation
                    if (isset($input['presenters'])) {
                        $presenters = json_decode($input['presenters'] ?? "[]", true);
                    }
                    else {
                        $user = authUser();
                        $userData = json_decode($user, true);
                        $presenters[] = $userData;
                    // $presentationRequestCount = $request->get('presentation_request_count') ? $request->get('presentation_request_count') : null;
                    // if ($presentationRequestCount || $presentationRequestCount == 0) {

                    //     // event user
                    //     $eventUser = EventUser::whereEventId($event->id)
                    //         ->whereUserId($user->id)
                    //         ->first();

                    //     if ($eventUser) {
                    //         // create presentation request without abstract count
                    //         $eventUser->presentation_request_count = $presentationRequestCount;
                    //         // save
                    //         $eventUser->save();
                    //     }
                    // }
                    }

                    $files = $request->allFiles();
                    $removedUsers = json_decode($input['removed_presenters'] ?? "[]", true);
                    $this->managePresenters($presentation, $presenters, $files, $removedUsers, $request);
                }
            }
            else {

                $validator = Validator::make($input, [
                    'data' => ['required', new PresentationDataRule()]
                ]);

                if ($validator->fails() && $submissionStatus == 'Submitted')
                    validationErrorResponse($validator->errors());

                $presentation = Presentation::find($presentationId);

                if (!$presentersOnly) {
                    $data = json_decode($input['data'] ?? false, true);
                    //$writtenPaperData = ($input['written_paper_data'] ?? false) ? json_decode($input['written_paper_data'], true) : [];

                    $data = $this->handlePresentationFiles($request, $presentation, $data);
                    //$writtenPaperData = $this->handleWrittenPaperFiles($request, $presentation, $writtenPaperData);

                    $presentation->data = $data;
                    //$presentation->written_paper_data = $writtenPaperData;
                    $presentation->submission_status = $submissionStatus;
                    $presentation->save();
                } else {
                    // managing presenters in presentation

                    $presenters = json_decode($input['presenters'], true);
                    $files = $request->allFiles();
                    $removedUsers = json_decode($input['removed_presenters'], true);
                    $this->managePresenters($presentation, $presenters, $files, $removedUsers, $request);
                }
            }

            //send email if submitted to admins and submitter and presenters.
            if ($presentation->submission_status == 'Submitted') {
                if($role == 'event_submitter'){
                    $presentation->submission_date = date('Y-m-d H:i:s', time());
                    $presentation->save();
                }
                dispatch(new SendPresentationSubmittedMails($presentation));
            }
            if ($presentation->submission_status == 'Submitted') {
              // update related slots
              $slotRepository = new SlotRepository();
              $slotRepository->updateRelatedSlots(null,$presentation);
            }

            return $presentation;
        }
    }


    public function writtenPaper(Request $request)
    {
        $presentationId = $request->get('id');
        $input = $request->input();
        $role = $request->get('role');
        $writtenPaperStatus = $request->get('written_paper_status', 'Draft');

        // Allow both submitters and admins to edit written papers
        if ($role != 'event_submitter' && $role != 'event_admin')
            validationErrorResponse(['Access denied']);

        if ($presentationId) {
            $presentation = Presentation::find($presentationId);

            $writtenPaperData = ($input['written_paper_data'] ?? false) ? json_decode($input['written_paper_data'], true) : [];

            $writtenPaperData = $this->handleWrittenPaperFiles($request, $presentation, $writtenPaperData);

            $presentation->written_paper_data = $writtenPaperData;
            $presentation->written_paper_status = $writtenPaperStatus;
            $presentation->save();

            return $presentation;
        }
    }

    public function markAsConfirmedSpeaker(EventUser $eventUser, $via = 'Abstract', $eventId): EventUser
    {
        if (!$eventUser->confirmed_speaker) {
            $eventUser->confirmed_speaker = true;
            $eventUser->speaker_added_via = $via;

            if ($eventUser->confirmed_speaker) {
                $lastConfirmedSpeakerOrder = EventUser::where('event_id', $eventId)
                    ->where('confirmed_speaker', 1)
                    ->whereNotNull('confirmed_speaker')
                    ->max('order');

                // Set the order for the new confirmed speaker
                $eventUser->order = $lastConfirmedSpeakerOrder + 1;
            }

            $eventUser->save();
        }
        return $eventUser;
    }

    public function handleWrittenPaperFiles(Request $request, Presentation $presentation, $writtenPaperData)
    {
        $removedFiles = $request->get('removed_files');
        $removedFiles = json_decode($removedFiles, true);

        //handling uploaded files
        if ($request->hasFile('written_paper_files')) {
            $files = $request->file('written_paper_files');
            $fileContents = [];
            foreach ($files as $file) {
                $fileObj = $presentation->saveFile(
                    $file,
                    null,
                    "{$presentation->id}/files/",
                    "presentation.written_paper.file",
                    null,
                    false
                );
                $fileContents[] = $fileObj->getFileObject();
            }
            $fileField = getField($writtenPaperData, 'written_paper_files');
            $fileField = array_shift($fileField);
            $fileFieldValue = $fileField['value'];
            $fileFieldValue = array_values(array_filter(array_merge($fileFieldValue, $fileContents)));
            $writtenPaperData = updateField($writtenPaperData, 'written_paper_files', 'value', $fileFieldValue);
        }

        //handling removed files
        if (isset($removedFiles['written_paper_files'])) {
            $fileIds = $removedFiles['written_paper_files'];
            foreach ($fileIds as $fileId) {
                $this->deleteFile($fileId);
            }
        }

        return $writtenPaperData;
    }

    public function handlePresentationFiles(Request $request, Presentation $presentation, $data)
    {
        $removedFiles = $request->get('removed_files');
        $removedFiles = json_decode($removedFiles, true);

        //handling uploaded files
        if ($request->hasFile('presentation_files')) {
            $files = $request->file('presentation_files');
            $fileContents = [];
            foreach ($files as $file) {
                $fileObj = $presentation->saveFile(
                    $file,
                    null,
                    "{$presentation->id}/files/",
                    "presentation.file",
                    null,
                    false
                );
                $fileContents[] = $fileObj->getFileObject();
            }
            $fileField = getField($data, 'presentation_files');
            $fileField = array_shift($fileField);
            $fileFieldValue = $fileField['value'];
            $fileFieldValue = array_values(array_filter(array_merge($fileFieldValue, $fileContents)));
            $data = updateField($data, 'presentation_files', 'value', $fileFieldValue);
        }

        //handling removed files
        if (isset($removedFiles['presentation_files'])) {
            $fileIds = $removedFiles['presentation_files'];
            foreach ($fileIds as $fileId) {
                $this->deleteFile($fileId);
            }
        }

        return $data;
    }

    public function selectColumns(): array
    {
        return [
            'presentations.id',
            'presentations.user_id',
            'presentations.event_id',
            'presentations.abstract_id',
            'presentations.data',
            'presentations.type',
            'presentations.presentation_type',
            'presentations.submission_status',
            'presentations.submission_date',
            'presentations.written_paper_data',
            'presentations.written_paper_status',
            'presentations.created_at',
            'presentations.updated_at',
        ];
    }

    public function applyEventScope(Builder $query, $arguments): Builder
    {
        if ($arguments->event) {
            $query->where('presentations.event_id', '=', $arguments->event->id);
        }

        return $query;
    }

    public function applyUserScope(Builder $query, $arguments): Builder
    {
        if ($arguments->role == 'event_submitter') {
            $query->where(function ($query) use ($arguments) {
                $query->where('presentations.user_id', '=', $arguments->event_user->user_id)
                    ->orWhereIn(
                    'presentations.id',
                    function ($query) use ($arguments) {
                    $query->select('presenters.presentation_id')
                        ->from('presenters')
                        ->where('presenters.user_id', '=', $arguments->event_user->user_id);
                }
                );
            })->groupBy('presentations.id');
        }

        if ($arguments->role == 'event_admin') {
            $query->leftJoin('presentation_reviews as pr', 'pr.presentation_id', '=', 'presentations.id')
                ->leftJoin('users as reviewers', 'reviewers.id', '=', 'pr.reviewer_id')
                ->addSelect([
                DB::raw("
                    concat('[',
                    GROUP_CONCAT(
                        DISTINCT(
                            IF(
                            reviewers.id is not null,
                            JSON_OBJECT(
                                'name',
                                concat(reviewers.first_name, ' ', reviewers.last_name),
                                'email',
                                reviewers.email,
                                'status',
                                pr.status,
                                'do_not_send_emails',
                                reviewers.do_not_send_emails,
                                'cc_emails',
                                reviewers.cc_emails
                            ),
                            NULL )
                        )
                    ), ']'
                    ) as reviewers")
                ])->groupBy('presentations.id');
        }

        if ($arguments->role == 'event_reviewer') {
            $query->join('presentation_reviews as pr', function ($join) use ($arguments) {
                $join->on('pr.presentation_id', '=', 'presentations.id')
                    ->where('pr.reviewer_id', '=', authUser()->id)
                    ->where('pr.event_id', '=', $arguments->event->id);
            })->addSelect([
                'pr.status as review_status'
            ])->groupBy('presentations.id');
        }

        return $query;
    }

    public function applyOrder(Builder $query, $arguments): Builder
    {
        if (!$arguments->sort)
            return $query;

        $columns = [
            'id' => 'presentations.id',
            'user_id' => 'presentations.user_id',
            'event_id' => 'presentations.event_id',
            'abstract_id' => 'presentations.abstract_id',
            'type' => 'presentations.type',
            'presentation_type' => 'presentations.presentation_type',
            'submission_status' => 'presentations.submission_status',
            'selection_status' => 'abstract.selection_status',
            'written_paper_status' => 'presentations.written_paper_status',
            'submission_date' => 'presentations.submission_date',
            'created_at' => 'presentations.created_at',
            'updated_at' => 'presentations.updated_at',
            'presenters' => 'presenters',
            'reviewers' => 'reviewers',
            'submitter' => 'submitter'
        ];

        $sorts = json_decode($arguments->sort, true);
        // form settings
        $formSettings = collect($arguments->event->form_settings->toArray());
        // pluck all fields
        $dataFields = $formSettings->pluck('field_id')->toArray();

        foreach ($sorts as $sort => $method) {
            if (array_key_exists($sort, $columns))
                $query->orderBy($sort, $method);
            // checking sort in field array
            else if (in_array($sort, $dataFields)) {
                $key = array_search($sort, $dataFields);
                $query->orderByRaw("JSON_EXTRACT(abstract.data, '$[$key].value') $method");
            }
        }

        //todo: add reviewer, presenter and submitter sort

        return $query;
    }

    public function applyTab(Builder $query, $arguments): Builder
    {

        if (!($arguments->tab))
            return $query;

        // tab filter
        $filter = json_decode($arguments->tab);

        // filtering with reviewers assigned or not
        if (isset($filter->assigned)) {
            if ($filter->assigned)
                $query->whereNotNull('reviewers.id');
            else
                $query->whereNull('reviewers.id');
        }
        return $query;
    }

    public function applyFilter(Builder $query, $arguments): Builder
    {

        if (!($arguments->filter))
            return $query;

        // for filtering
        $filter = json_decode($arguments->filter);

        // filter with category
        $formSettings = collect($arguments->event->form_settings->toArray());
        $dataFields = $formSettings->pluck('field_id')->toArray();
        if ($filter->category ?? false) {
            $key = array_search('category', $dataFields);
            $values = $filter->category;

            $query->where(function ($query) use ($values, $key) {
                foreach ($values as $value) {
                   // $query->orWhereRaw(DB::raw("JSON_EXTRACT(abstract.data, '$[$key].value') = ?"), [$value]);
                    $query->orWhereJsonContains('abstract.data', [
                        ['field_id' => 'category', 'value' => $value]
                    ]);
                }
            });
        }

        // filter with subcategory
        if ($filter->subcategory ?? false) {
            $key = array_search('subcategory', $dataFields);
            $values = $filter->subcategory;

            $query->where(function ($query) use ($values, $key) {
                foreach ($values as $value) {
                    //$query->whereRaw(DB::raw("JSON_EXTRACT(abstract.data, '$[$key].value') = ?"), [$value]);
                    $query->orWhereJsonContains('abstract.data', [
                        ['field_id' => 'subcategory', 'value' => $value]
                    ]);
                }
            });
        }


        // filter with company name
        if ($filter->company ?? false) {
            $values = $filter->company;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('presenters.company', '=', $value);
                }
            });
        }

        // filter with submission status
        if ($filter->submission_status ?? false) {
            $values = $filter->submission_status;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('presentations.submission_status', '=', $value);
                }
            });
        }

         // filter with written paper status
         if ($filter->written_paper_status ?? false) {
            $values = $filter->written_paper_status;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('presentations.written_paper_status', '=', $value);
                    if ($value === 'draft') {
                        $query->orWhereNull('presentations.written_paper_status')
                              ->orWhere('presentations.written_paper_status', '=', '');
                    }
                }
            });
        }

        // assign status
        if (isset($filter->assigned_status)) {
            if (count($filter->assigned_status) == 1 && $filter->assigned_status[0] == "Assigned") {
                $query->where('pr.presentation_id', '>', 0);
            }
            else if (count($filter->assigned_status) == 1 && $filter->assigned_status[0] == "Un-Assigned") {
                $query->whereNull('pr.presentation_id');
            }
        }
        // filter with presentation type
        if ($filter->presentation_type ?? false) {
            $values = $filter->presentation_type;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('presentations.presentation_type', '=', $value);
                }
            });
        }

        // filter with Abstract ID
        if ($filter->abstractId ?? false) {
            $values = $filter->abstractId;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    if ($value == 'Not Set') {
                        $query->orWhere('presentations.abstract_id', '=', null);
                    }
                    else {
                        $query->orWhere('presentations.abstract_id', '=', $value);
                    }
                }
            });
        }

        // filter with selection Status
        if ($filter->selection_status ?? false) {
            $values = $filter->selection_status;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    if ($value == 'Not Set') {
                        $query->orWhere('abstract.selection_status', '=', null);
                    }
                    else {
                        $query->orWhere('abstract.selection_status', '=', $value);
                    }
                }
            });
        }

        //  filter with presenter name
        if ($filter->presenter ?? false) {
            $values = $filter->presenter;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere(DB::raw("CONCAT(presenters.first_name, ' ', presenters.last_name)"), '=', $value);
                }
            });
        }

        // filter with submitter name
        if ($filter->submitter ?? false) {
            $values = $filter->submitter;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere(DB::raw("CONCAT(user.first_name, ' ', user.last_name)"), '=', $value);
                }
            });
        }

        // filter with Assigned Reviewer Name
        if ($filter->reviewer ?? false) {
            $values = $filter->reviewer;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere(DB::raw("CONCAT(reviewers.first_name, ' ', reviewers.last_name)"), '=', $value);
                }
            });
        }

        // filter with Assigned Reviewer status
        if ($filter->reviewer_assigned_status ?? false) {
            $values = $filter->reviewer_assigned_status;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('pr.status', '=', $value);
                }
            });
        }

          // filter submission date
          if (isset($filter->start_date) || isset($filter->end_date)) {
            $startDate = isset($filter->start_date) ? $filter->start_date : null;
            $endDate = isset($filter->end_date) ? $filter->end_date : null;

            if (!$endDate) {
                $query->where(function ($query) use ($startDate) {

                    $query->orWhereDate(DB::raw('DATE(presentations.submission_date)'), '=', $startDate);

                });
            } else if (!$startDate) {
                $query->where(function ($query) use ($endDate) {

                    $query->orWhereDate(DB::raw('DATE(presentations.submission_date)'), '=', $endDate);

                });
            } else {
                $query->where(function ($query) use ($startDate, $endDate) {

                    $query->orWhereBetween('presentations.submission_date', [$startDate, $endDate]);
                    $query->orWhereDate(DB::raw('DATE(presentations.submission_date)'), '=', $startDate);

                });
            }
        }

        // filter sessions
        if ($filter->sessions ?? false) {
            $values = $filter->sessions;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('session.title', '=', $value);
                }
            });
        }

        // filter slots
        if ($filter->slots ?? false) {
            $values = $filter->slots;
            $query->where(function ($query) use ($values) {
                foreach ($values as $value) {
                    $query->orWhere('slot.title', '=', $value);
                }
            });
        }

        return $query;
    }

    public function applySearch(Builder $query, $arguments): Builder
    {
        if ($arguments->search) {
            $columns = [
                'id' => 'presentations.id',
                'abstract_id' => 'presentations.abstract_id',
                'submission_status' => 'presentations.submission_status'
            ];
            $search = strtolower($arguments->search);

            $query->where(function ($query) use ($arguments, $search, $columns) {
                foreach ($columns as $column) {
                    $query->orWhere(DB::raw("lower($column)"), 'like', "%{$search}%");
                }
                //user name
                $query->orWhereRaw(DB::raw("lower(CONCAT(user.first_name, ' ', user.last_name)) like ?"), ["%{$search}%"]);
                // presenter name
                $query->orWhereRaw(DB::raw("lower(CONCAT(presenters.first_name, ' ', presenters.last_name)) like ?"), ["%{$search}%"]);
                // presenter company
                $query->orWhereRaw(DB::raw("lower(presenters.company) like ?"), ["%{$search}%"]);

                // presentation abstract category search
                // form settings
                $formSettings = collect($arguments->event->form_settings->toArray());
                // pluck all fields
                $dataFields = $formSettings->pluck('field_id')->toArray();
                // key
                $key = array_search('category', $dataFields);

                //$query->orWhereRaw(DB::raw("lower(JSON_EXTRACT(abstract.data, '$[$key].value')) like ? "), ["%{$search}%"]);
                $query->orWhereRaw(DB::raw("lower(JSON_EXTRACT(abstract.data, '$[*].value')) like ? "), ["%{$search}%"]);

                // user selection status
                $query->orWhereRaw(DB::raw("lower(abstract.selection_status) like ?"), ["%{$search}%"]);

                // session title search
                $query->orWhereRaw(DB::raw("lower(session.title) like ?"), ["%{$search}%"]);

                // slot title search
                $query->orWhereRaw(DB::raw("lower(slot.title) like ?"), ["%{$search}%"]);

                $filter = json_decode($arguments->filter);
                if (isset($filter->assigned)) {
                    if ($filter->assigned) {
                        $query->orWhereRaw(DB::raw("lower(CONCAT(reviewers.first_name, ' ', reviewers.last_name)) like ?"), ["%{$search}%"]);
                        $query->orWhereRaw(DB::raw("lower(pr.status) like ?"), ["%{$search}%"]);
                    }
                }
            });
        }

        return $query;
    }

    public function applyScope(Builder $query, $arguments): Builder
    {
        $this->applyEventScope($query, $arguments);

        $this->applyUserScope($query, $arguments);

        $this->applyOrder($query, $arguments);

        $this->applyTab($query, $arguments);

        $this->applyFilter($query, $arguments);

        $this->applySearch($query, $arguments);

        return $query;
    }

    /**
     * @throws Exception
     */
    public function scope($arguments, $callback = null): Builder
    {
        $query = $this->query()
            ->select($this->selectColumns());

        //joining user to presentation
        $query->leftJoin('users as user', 'user.id', '=', 'presentations.user_id');
        $query->addSelect([DB::raw(
            "JSON_OBJECT(
            'id',user.id,
            'first_name',user.first_name,
            'last_name',user.last_name,
            'do_not_send_emails',user.do_not_send_emails,
            'cc_emails',user.cc_emails,
            'email',user.email
            ) as user, concat(user.first_name, ' ', user.last_name) as submitter"
        )]);

        //joining abstract to presentation
        $query->leftJoin('abstracts as abstract', 'abstract.id', '=', 'presentations.abstract_id');
        $query->addSelect([DB::raw(
            "JSON_OBJECT(
            'id',abstract.id,
            'data',abstract.data,
            'submission_status',abstract.submission_status,
            'selection_status',abstract.selection_status
            ) as abstract"
            )
        ]);

        //joining slots to presentation
        $query->leftJoin('slots as slot', 'slot.presentation_id', '=', 'presentations.id')->
        leftJoin('sessions as session', 'slot.session_id', '=', 'session.id')
        ->addSelect([
            DB::raw("
            JSON_ARRAYAGG(
                JSON_OBJECT(
                'title', slot.title,
                'slot_id', slot.id,
                'session_id', session.id,
                'session_title', session.title
            ))
            AS slotsArray")
        ])
        ->groupBy('presentations.id');


        //joining presenters to presentation
        $query->leftJoin('presenters as p', 'p.presentation_id', '=', 'presentations.id')
            ->leftJoin('users as presenters', 'presenters.id', '=', 'p.user_id')
            ->addSelect([
            DB::raw("GROUP_CONCAT(DISTINCT(concat(presenters.first_name, ' ', presenters.last_name))) as presenters")
        ])->addSelect([
            DB::raw("
            concat('[',
            GROUP_CONCAT(
                DISTINCT(
                    IF(
                        presenters.id is not null,
                    JSON_OBJECT(
                        'name',
                        concat(presenters.first_name, ' ', presenters.last_name),
                        'company',
                        presenters.company,
                        'do_not_send_emails',
                        presenters.do_not_send_emails,
                        'cc_emails',
                        presenters.cc_emails,
                        'email',
                        presenters.email
                    ),
                    NULL )
                )
            ), ']'
            ) as presentersArray")
        ])->groupBy('presentations.id');

        //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);
    }

    /**
     * @throws Exception
     */
    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 fetchPresentation($id, $eventId = null)
    {
        if (!$eventId)
            return Presentation::with(['user', 'abstract', 'presenters', 'reviews.reviewer'])->find($id);
        else
            return Presentation::with(['user', 'abstract', 'presenters', 'reviews.reviewer'])->where('event_id', '=', $eventId)
                ->find($id);
    }

    public function getCommentRepository(): PresentationCommentRepository
    {
        return new PresentationCommentRepository();
    }

    public function managePresenters(Presentation $presentation, $presenters, $files, $removedUsers = [], $request): Presentation
    {

        $this->removerPresentersFromPresentation($presentation, $removedUsers);

        $userRepository = new UserRepository();

        //add/edit presenters in the presentation
        foreach ($presenters as $key => $person) {
            $user = User::find($person['id'] ?? null);
            $isEdit = true;
            if (!$user) {
                $validator = Validator::make($person, [
                    'first_name' => ['required', 'string', 'max:255'],
                    'last_name' => ['required', 'string', 'max:255'],
                    'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
                ]);

                if ($validator->fails())
                    validationErrorResponse($validator->errors());

                $user = new User();
                $user->password = Hash::make(randomStringGenerator(8));
                $user->valid = true;
                $user->email = $person['email'];
                $user->created_by = authUser()->id;
                $user->new_user = true;
                $user->save();
                $user->assignRole('user');
                $isEdit = false;
            } else {
                $user->new_user = false;
                $user->save();
            }

            $eventUser = getEventUser($presentation->event->id, $user->id);
            if (!$eventUser) {
                $eventUser = $userRepository->attachUserToEvent($presentation->event, $user, 'event_submitter');
                // type
                $eventUser->type = "strategic";
                // user added via
                $eventUser->user_added_via = "Presentation";
                // save
                $eventUser->save();
            }

            if ($presentation->abstract_id) {
                $abstract = Abstracts::find($presentation->abstract_id);
                $abstractPresenters = $abstract->users('abstract_presenter')->pluck('id');
                $speakerType = $abstractPresenters->contains($user->id) ? 'Abstract' : 'Presentation';
            } else {
                $speakerType = 'Presentation';
            }

            $this->markAsConfirmedSpeaker($eventUser, $speakerType, $presentation->event->id);

            unset($person['pivot']);
            unset($person['id']);
            unset($person['checkboxes']);
            unset($person['login_email_count']);
            unset($person['valid']);
            unset($person['forgot_password']);
            unset($person['avatar']);
            unset($person['company_logo']);
            $user->fill($person);
            //keep changes field lists
            $changes = $user->getDirty();

            // Track modification if users table data changed
            if (!empty($changes)) {
                $userRepository->trackUserModification($user, authUser()->id, $presentation->event_id);
                $userRepository->trackVipMarkedBy($user, $changes);
            }

            $user->save();
            $removeAvatar = false;
            $removeCompanyLogo = false;
            $removeUserSecondaryImage = false;

            if (isset($request->get('avatar')[$key])) {
                if ($request->get('avatar')[$key] === "null" && $user->avatar) {
                    $removeAvatar = true;
                }
            }

            if (isset($request->get('company_logo')[$key])) {
                if ($request->get('company_logo')[$key] === "null" && $user->company_logo) {
                    $removeCompanyLogo = true;
                }
            }
            if (isset($request->get('user_secondary_image')[$key])) {
                if ($request->get('user_secondary_image')[$key] === "null" && $user->user_secondary_image) {
                    $removeUserSecondaryImage = true;
                }
            }


            if ($isEdit) {

                // image files
                $imageFiles = [
                    'avatar' => isset($files['avatar'][$key]) ? $files['avatar'][$key] : null,
                    'company_logo' => isset($files['company_logo'][$key]) ? $files['company_logo'][$key] : null,
                ];

                // Get the changed fields using the common function
                $changeFields = $userRepository->getChangedFields($changes, $imageFiles, $removeAvatar, $removeCompanyLogo);

                // verification changes for change fields
                if ($changeFields) {
                    // published session status changing
                    $userRepository->publishedSessionStatusUpdate($user);

                    $userRepository->userVerificationFieldsChanges($changeFields, $user);
                }
            }
            $presentersId = $presentation->presenters()->pluck('user_id')->toArray();
            if (!in_array($user->id, $presentersId)) {
                $presentation->presenters()->attach($user, ['event_id' => $presentation->event_id]);
                // event
                $event = $request->get('event');
                // general settings
                $generalSettings = $event->general_settings;
                // terms and condition
                $termsAndConditionStatus = $generalSettings['speaker_terms_prefill']['terms_and_condition_status'];
                if (!$termsAndConditionStatus) {
                    dispatch(new SendSpeakerTerms([$eventUser]));
                }
            }

            $userRepository->removeUserImages($user, $removeAvatar, $removeCompanyLogo, $removeUserSecondaryImage);

            //handling user images
            $userFiles = [];
            if (isset($files['avatar'][$key]))
                $userFiles['avatar'] = $files['avatar'][$key];
            if (isset($files['company_logo'][$key]))
                $userFiles['company_logo'] = $files['company_logo'][$key];
            if (isset($files['user_secondary_image'][$key]))
                $userFiles['user_secondary_image'] = $files['user_secondary_image'][$key];

            $userRepository->updateUserImages($userFiles, $user, authUser()->id, $presentation->event_id);
        }

        if ($presentation->submission_status == 'Submitted'){
        // update related slots
        $slotRepository = new SlotRepository();
        $slotRepository->updateRelatedSlots(null,$presentation);
        }

        return $presentation;
    }

    // adding  selected files in zip file
    public function addingToZip(object $presentations, Request $request)
    {
        // Delete older files
        $this->deleteOlderZipFiles();
        // Create a new ZipArchive instance and open or create the ZIP file
        $zip = new ZipArchive;
        $zipName = 'presentationFiles' . time() . '.zip';
        $zip->open($zipName, ZipArchive::CREATE | ZipArchive::OVERWRITE);


        // Get the selected presentation type from the request
        $presentationType = $request['presentation_type'];

        foreach ($presentations as $presentation) {
            $this->addPresentationToZip($presentation, $zip, $presentationType);

            // Reset the file name for the next iteration
            $this->fileName = null;
        }

        // 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"]);
    }

    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 1 hour
        $expirationTime = now()->subHour()->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 addPresentationToZip($presentation, $zip, $presentationType)
    {
        // Extract and format submitter name
        $submitterName = '';
        if ($presentation->user_id) {
            $submitter = User::whereId($presentation->user_id)->first();
            if ($submitter) {
                $submitterName = removeSpecialCharacters(substr($submitter->name, 0, 30));
                $submitterCompany = removeSpecialCharacters(substr($submitter->company, 0, 30));
                $this->fileName = $submitterName . ($submitterCompany ? '_' . $submitterCompany : '');
            }
        }

        // Zip presentation files if available
        if ($presentation->data && $this->shouldZipPresentationFiles($presentationType)) {
            $presentationData = json_decode(json_encode($presentation->data), true);
            $this->filesToZip($presentationData, 'presentation_files', $zip, $presentation);
        }

        // Zip written paper files if available
        if ($presentation->written_paper_data && $this->shouldZipWrittenPaperFiles($presentationType)) {
            $writtenPaperData = json_decode(json_encode($presentation->written_paper_data), true);
            $this->filesToZip($writtenPaperData, 'written_paper_files', $zip, $presentation);
        }
    }

    private function shouldZipPresentationFiles($presentationType)
    {
        return array_search('Presentation Files', array_column($presentationType['option'], 'value')) !== false;
    }

    private function shouldZipWrittenPaperFiles($presentationType)
    {
        return array_search('Written Paper Files', array_column($presentationType['option'], 'value')) !== false;
    }

    private function handleLargeFileSize($zipName, $request)
    {
        // Generate a temporary URL for the file with an expiration time
        $expiration = now()->addHours(1);
        $temporaryUrl = URL::temporarySignedRoute(
            'downloadPresentationZip',
            $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->sendPresentationDownloadLinkEmail($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 sendPresentationDownloadLinkEmail($event, $userEmail, $temporaryUrl)
    {
        SendPresentationDownloadLinkEmail::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, $field, $zip, $presentation)
    {

        // attachment field
        $attachmentFields = array_values(
            array_filter(
                $data,
                fn($presentationFile) => $presentationFile['field_id'] == $field
            )
        );

        if (isset($attachmentFields[0]['value']) && is_array($attachmentFields[0]['value'])) {
            foreach ($attachmentFields[0]['value'] as $attachmentField) {
                if (isset($attachmentField['file_id'])) {
                    $file = \App\Models\File::whereId($attachmentField['file_id'])->first();
                    if ($file) {
                        // file path
                        $filePath = $file->filepath . $file->save_name;
                        $filePath = storage_path() . "/app/" . $filePath;
                        $savedFileName = $file->filename;

                        if (strlen($savedFileName) > 30) {
                            $savedFileName = substr($savedFileName, 0, 30);
                            $savedFileName = removeSpecialCharacters($savedFileName);
                        }

                        $name = $this->fileName . '_' . $savedFileName . '_' . $presentation->id . '.' . $file->extension;

                        if (file_exists($filePath)) {
                            // add file to zip
                            $zip->addFile($filePath, $name);
                        }
                    }
                }
            }
        }

    }

    public function removerPresentersFromPresentation(Presentation $presentation, $users)
    {
        foreach ($users as $user) {
            if ($user) {
                $presentationUser = getPresentationPresenter($presentation->id, $user);
                if ($presentationUser) {
                    $presentationUser->delete();
                }
            }
        }
    }

    public function assignReviewer(Presentation $presentation, $reviewerId): PresentationReview
    {
        $reviewerEventUser = getEventUser($presentation->event_id, $reviewerId);
        if (!$reviewerEventUser->hasRole('event_reviewer'))
            validationErrorResponse(['User not a reviewer']);

        $review = PresentationReview::wherePresentationId($presentation->id)
            ->whereReviewerId($reviewerId)
            ->first();
        if ($review)
            validationErrorResponse(['Reviewer already assigned']);

        $review = new PresentationReview([
            'presentation_id' => $presentation->id,
            'event_id' => $presentation->event->id,
            'reviewer_id' => $reviewerId,
            'status' => 'Under Review',
        ]);
        $review->save();
        $review->refresh();

        // disable mail for Egyps
        // requirement from the client
        $disableSlug = 'egyps-2023';
        if ($review->presentation->event->slug_name !== $disableSlug) {
            //mail to reviewer
            dispatch(new SendAssignReviewerMail($review));
        }

        return $review;
    }

    public function removeReviewer(Presentation $presentation, $reviewerId)
    {
        $review = PresentationReview::wherePresentationId($presentation->id)
            ->whereReviewerId($reviewerId)
            ->first();
        if (!$review)
            validationErrorResponse(['Reviewer not found']);

        $review->delete();

        return true;
    }


    // remove presenter from presentation
    public function removePresenter(Presentation $presentation, $presenterId)
    {
        $presenter = Presenter::wherePresentationId($presentation->id)
            ->whereUserId($presenterId)
            ->first();
        if (!$presenter)
            validationErrorResponse(['Presenter not found']);

        $presenter->delete();

        // update related slots
        if ($presentation->submission_status == 'Submitted') {
            $slotRepository = new SlotRepository();
            $slotRepository->updateRelatedSlots(null,$presentation);
        }

        return true;
    }

    public function handleWrittenPaperEditRequest(Presentation $presentation, $status): Presentation
    {
        switch ($status) {
            case 'change':
                $status = $presentation->written_paper_status === 'Draft' ? 'Submitted' : 'Draft';
                $presentation->written_paper_status = $status;
                $presentation->save();
                break;
            default:
                validationErrorResponse(['Invalid status']);
        }
        return $presentation;
    }

    public function handleEditRequest(Presentation $presentation, $status)
    {
        switch ($status) {
            case 'accept':
                $presentation->submission_status = 'Draft';
                $presentation->edit_request = false;
                $presentation->save();

                $generalSettings = $presentation->event->general_settings;
                $requestToEdit = $generalSettings['presentation_templates']['request_to_edit'];

                if ($requestToEdit) {
                    //mail to submitter
                    dispatch(new SendPresentationEditStatusChangeMail($presentation, $status));
                }

                break;
            case 'reject':
                $presentation->edit_request = false;
                $presentation->save();

                //mail to submitter
                dispatch(new SendPresentationEditStatusChangeMail($presentation, $status));

                break;
            case 'change':
                $status = $presentation->submission_status === 'Draft' ? 'Submitted' : 'Draft';
                if ($status == "Submitted") {
                    $data = $presentation->data;
                    if (!$data)
                        validationErrorResponse(['Required fields are missing']);
                    $data = $data->toArray();
                    $fileField = getField($data, 'presentation_files')[0];
                    $titleField = getField($data, 'title')[0];
                    if (empty($fileField['value']) && empty($titleField['value'])) {
                        validationErrorResponse(['Required fields are missing']);
                    }
                }
                $presentation->submission_status = $status;
                $presentation->edit_request = false;
                $presentation->save();

                //mail to submitter
                dispatch(new SendPresentationStatusChangeMail($presentation));

                break;
            default:
                validationErrorResponse(['Invalid status']);
        }

        return $presentation;
    }

    public function handleReviewerApprovedRequest($comment, Presentation $presentation)
    {
        $user = authUser();

        $presentationReview = PresentationReview::wherePresentationId($presentation->id)
            ->whereReviewerId($user->id)
            ->first();

        if (!$presentationReview)
            return response([
                'status' => false,
                'message' => 'reviewer not found'
            ]);

        $presentationReview->status = "Approved";
        $presentationReview->approved_date = date('Y-m-d H:i:s', time());
        $presentationReview->comment = $comment;
        $presentationReview->save();

        //mail to admins
        dispatch(new SendReviewerApprovedMail($presentationReview));

        return $this->fetchPresentation($presentation->id, $presentation->event->id);
    }

    public function getPresentationCount(Request $request): array
    {
        $event = $request->get('event');
        $eventPresentations = Presentation::whereEventId($event->id)
            ->get();
        $eventPresentationsCount = $eventPresentations->count();
        $assignedPresentations = Presentation::where('presentations.event_id', '=', $event->id)
            ->join('presentation_reviews as pr', 'pr.presentation_id', '=', 'presentations.id')
            ->groupBy('presentations.id')
            ->get();
        $assignedPresentationsCount = $assignedPresentations->count();
        $unAssignedCount = $eventPresentationsCount - $assignedPresentationsCount;

        $eventPresentationsSubmitted = Presentation::whereEventId($event->id)
            ->where('submission_status', '=', 'Submitted')->get();

        $eventPresentationsSubmittedCount = $eventPresentationsSubmitted->count();
        $eventPresentationsDraftCount = $eventPresentationsCount - $eventPresentationsSubmittedCount;

        return [
            'assigned' => $assignedPresentationsCount,
            'unassigned' => $unAssignedCount,
            'submitted' => $eventPresentationsSubmittedCount,
            'draft' => $eventPresentationsDraftCount,
            'total' => $eventPresentationsCount
        ];
    }


    public function deletePresentation(Presentation $presentation): bool
    {
        $slotCount = Slot::wherePresentationId($presentation->id)
            ->count();
        if ($slotCount > 0) {
            return false;
        }

        //delete presentation comments
        $presentationComments = PresentationComment::wherePresentationId($presentation->id)
            ->get();
        foreach ($presentationComments as $presentationComment) {
            $this->getCommentRepository()->deletePresentationComment($presentationComment);
        }

        //detach presenters from presentation
        $presentation->presenters()->detach();

        //delete presentation reviews
        $presentation->reviews()->delete();

        //delete files in presentation
        $data = $presentation->data;
        if ($data) {
            $presentationFiles = getField($data, 'presentation_files')[0];
            $files = $presentationFiles['value'];
            if (is_array($files))
                foreach ($files as $file) {
                    $fileId = $file['file_id'];
                    $this->deleteFile($fileId);
                }
        }

        //delete directory
        Storage::deleteDirectory('private/media/presentations/' . $presentation->id);

        if ($presentation->abstract_id) {
            Abstracts::whereId($presentation->abstract_id)
                ->update([
                    'presentation_invite' => null
                ]);
        }
        $presentation->delete();

        return true;
    }
    /**
     * 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'];
            }
        }
        $formatedFilter = [];
        // getting all users if there is no user selected
        if (!isset($filter['users']) || count($filter['users']) <= 0) {
            $filter['users'] = $this->getAllUsersByEmailType('Presentation');
        }

        foreach ($this->presentaionFields 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,
        ]);
        $result = $this->listing($request, false);
        $eventRepo = new EventRepository();
        //getting users emails
        foreach ($result as $key => $res) {
            $body = $this->replaceMacros($res, $data, $event->event_name);
            $bcc = [];
            foreach ($filter['users'] as $user) {
                switch ($user['value']) {
                    case "Submitters":
                        $mails = getUsersEmails([json_decode(json_encode($res->user))]);
                        $bcc = [...$bcc, ...$mails['emails']];
                        break;
                    case "Presenters":
                        if ($res->presentersArray)
                            $mails = getUsersEmails(json_decode(json_encode($res->presentersArray)));
                        $bcc = [...$bcc, ...$mails['emails']];
                        break;
                    case "Reviewers":
                        if ($res->reviewers)
                            $mails = getUsersEmails(json_decode(json_encode($res->reviewers)));
                        $bcc = [...$bcc, ...$mails['emails']];
                        break;
                }

            }
            $bcc = array_unique($bcc);
            $email[] = [
                'to' => $to,
                'bcc' => $bcc,
                '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($row, object $data, string $event_name): string
    {
        $variables = config('schedule_mail_settings')->Presentation->variables;
        $submittedStatus = $row->submission_status;
        $writtenPaperStatus = $row->written_paper_status;
        $submittedDate = $row->submission_date;
        $presentationId = $row->id;
        $body = $data->body;
        $body = Str::replace("{submission_status}", $submittedStatus, $body);
        $body = Str::replace("{written_paper_status}", $writtenPaperStatus, $body);
        $body = Str::replace("{presentation_id}", $presentationId, $body);
        $body = Str::replace("{submission_date}", $submittedDate, $body);
        $body = Str::replace("{event_name}", $event_name, $body);
        return $body;
    }
}

Directory Contents

Dirs: 0 × Files: 17
Name Size Perms Modified Actions
75.27 KB lrw-rw-r-- 2026-04-30 09:24:04
Edit Download
1.21 KB lrw-rw-r-- 2025-04-21 06:11:52
Edit Download
65 B lrw-r--r-- 2024-02-09 12:37:30
Edit Download
8.53 KB lrw-rw-r-- 2025-03-03 05:39:26
Edit Download
4.14 KB lrw-rw-r-- 2025-10-28 05:24:52
Edit Download
8.53 KB lrw-rw-r-- 2025-10-28 05:24:35
Edit Download
37.58 KB lrw-rw-r-- 2026-04-07 05:00:51
Edit Download
8.71 KB lrw-r--r-- 2024-02-09 12:37:30
Edit Download
59.48 KB lrwxrwxr-x 2026-04-30 09:24:03
Edit Download
4.78 KB lrw-r--r-- 2024-02-09 12:37:30
Edit Download
10.79 KB lrw-rw-r-- 2025-04-21 06:11:52
Edit Download
11.37 KB lrw-rw-r-- 2024-07-24 04:42:48
Edit Download
72.51 KB lrw-rw-r-- 2026-04-22 04:31:21
Edit Download
11.43 KB lrw-rw-r-- 2024-09-20 05:02:14
Edit Download
6.57 KB lrw-rw-r-- 2026-03-31 07:16:20
Edit Download
4.26 KB lrw-r--r-- 2024-02-09 12:37:30
Edit Download
128.96 KB lrw-rw-r-- 2026-05-07 09:06:13
Edit Download
If ZipArchive is unavailable, a .tar will be created (no compression).