get("https://graph.microsoft.com/v1.0/groups/$groupId/events"); return $response->json()['value']; } private static function getAccessToken() { $tenantId = env('MICROSOFT_TENANT_ID'); $clientId = env('MICROSOFT_CLIENT_ID'); $clientSecret = env('MICROSOFT_CLIENT_SECRET'); $username = env('MICROSOFT_USERNAME'); $password = env('MICROSOFT_PASSWORD'); $response = Http::asForm()->post("https://login.microsoftonline.com/{$tenantId}/oauth2/v2.0/token", [ 'grant_type' => 'password', 'client_id' => $clientId, 'client_secret' => $clientSecret, 'username' => $username, 'password' => $password, 'scope' => 'https://graph.microsoft.com/.default', ]); if ($response->failed()) { throw new \Exception('Failed to obtain access token: ' . $response->body()); } return $response->json()['access_token']; } public static function addBlockers(mixed $blockers_from_office365) { // get only new events - the ones that doesnt have any office365_id in blockers table and only events that doesnt have isCancelled => true $newBlockers = array_filter($blockers_from_office365, function ($event) { return ! Blocker::where('office365_id', $event['id'])->exists() && ! $event['isCancelled']; }); foreach ($newBlockers as $blocker) { try { self::addBlocker($blocker); } catch (\Exception $e) { // log error Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } } } private static function addBlocker(mixed $blocker) { $attendees = $blocker['attendees']; // pre kazdeho cloveka z eventu, ktory je zaroven userom v TM vytvorime blocker foreach ($attendees as $attendee) { $user = User::where('email', $attendee['emailAddress']['address'])->first(); if ( ! $user) { continue; } $blockerToInsert = []; $blockerToInsert['office365_id'] = $blocker['id']; $blockerToInsert['office365_change_key'] = $blocker['changeKey']; $blockerToInsert['user_id'] = $user->id; $blockerToInsert['name'] = $blocker['subject']; $blockerToInsert['type'] = "office365_blocker"; $blockerToInsert['start'] = Carbon::parse($blocker['start']['dateTime'])->setTimezone('Europe/Bratislava'); $blockerToInsert['end'] = Carbon::parse($blocker['end']['dateTime'])->setTimezone('Europe/Bratislava'); $blockerToInsert['approved'] = 1; $blockerToInsert['created_at'] = Carbon::now()->toDateTimeString(); $blockerToInsert['updated_at'] = Carbon::now()->toDateTimeString(); Blocker::insert($blockerToInsert); } } /** * @param mixed $events * * @return mixed */ public static function getBlockers_from_office_365_events(mixed $events): mixed { return array_filter($events, function ($event) { $projectCode = explode(' ', $event['subject'])[0]; // if project code doesnt exist in database, then it is blocker return ! Project::where('code', $projectCode)->exists(); }); } public static function editBlockers(mixed $blockers_from_office365) { // get edited blockers - the ones, that has different changeKey from our database $editedBlockers = array_filter($blockers_from_office365, function ($event) { $blocker = Blocker::where('office365_id', $event['id'])->first(); return $blocker && $blocker->office365_change_key !== $event['changeKey']; }); foreach ($editedBlockers as $editedBlocker) { try { self::editBlocker($editedBlocker); } catch (\Exception $e) { // log error Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } } } private static function editBlocker(mixed $editedBlocker) { $attendees = $editedBlocker['attendees']; // ak sa v nasej DB pod blockerom nachadza nejaky user, ktory sa uz nenachadza medzi attendees, tak mu tento blocker vymazeme $blockersToDelete = Blocker::where('office365_id', $editedBlocker['id'])->get()->filter(function ($blocker) use ( $attendees ) { $event = collect($attendees)->firstWhere('emailAddress.address', $blocker->user->email); return ! $event; }); foreach ($blockersToDelete as $blocker) { $blocker->delete(); } // pre kazdeho cloveka z eventu, ktory je zaroven userom v TM updatneme blocker foreach ($attendees as $attendee) { $user = User::where('email', $attendee['emailAddress']['address'])->first(); if ( ! $user) { continue; } $blocker = Blocker::where('office365_id', $editedBlocker['id'])->where('user_id', $user->id)->first(); // ak sa nenachadza blocker, znamena to, ze je to novy user medzi attendees, ktory este nema u nas vytvoreny blocker. tak ho vytvorime if ( ! $blocker) { Blocker::create([ 'office365_id' => $editedBlocker['id'], 'office365_change_key' => $editedBlocker['changeKey'], 'user_id' => $user->id, 'name' => $editedBlocker['subject'], 'type' => "office365_blocker", 'start' => Carbon::parse($editedBlocker['start']['dateTime'])->setTimezone('Europe/Bratislava'), 'end' => Carbon::parse($editedBlocker['end']['dateTime'])->setTimezone('Europe/Bratislava'), 'approved' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'updated_at' => Carbon::now()->toDateTimeString() ] ); } else { $blocker->update([ 'office365_change_key' => $editedBlocker['changeKey'], 'name' => $editedBlocker['subject'], 'start' => Carbon::parse($editedBlocker['start']['dateTime'])->setTimezone('Europe/Bratislava'), 'end' => Carbon::parse($editedBlocker['end']['dateTime'])->setTimezone('Europe/Bratislava'), 'updated_at' => Carbon::now()->toDateTimeString(), ]); } } } public static function addSubtasks(mixed $subtasks_from_office365) { $newSubtasks = array_filter($subtasks_from_office365, function ($event) { return ! SubTask::where('office365_id', $event['id'])->exists() && ! $event['isCancelled']; }); foreach ($newSubtasks as $newSubtask) { try { $project = Project::where('code', explode(' ', $newSubtask['subject'])[0])->first(); // ak existuje viac master taskov, pouzijeme posledny $mastertask = MasterTask::where('project_id', $project->id)->latest()->first(); // ak neexistuje master task k danemu projektu, tak sa subtask neprida if ( ! $mastertask) { continue; } self::addSubtask($newSubtask, $mastertask); } catch (\Exception $e) { // log error Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } } } private static function addSubtask(mixed $newSubtask, mixed $mastertask) { $attendees = $newSubtask['attendees']; $subtasksToInsert = []; $i = 0; // pre kazdeho cloveka z eventu, ktory je zaroven userom v TM vytvorime subtask foreach ($attendees as $attendee) { $user = User::where('email', $attendee['emailAddress']['address'])->first(); $creator = User::where('email', $newSubtask['organizer']['emailAddress']['address'])->first(); if ( ! $user || ! $creator) { continue; } $subtasksToInsert[$i]['user_id'] = $user->id; // vymazem prve slovo z title, ktore je project code. ak title ma len 1 slovo (project code), tak sa ako title vlozi "No title" $subtasksToInsert[$i]['title'] = ( ! str_contains($newSubtask['subject'], ' ')) ? 'No title' : substr($newSubtask['subject'], strpos($newSubtask['subject'], ' ') + 1); // estimated_time je rozdiel medzi end.dateTime a start.dateTime zakhruhleny na najblizsiu 0.5 hodinu $subtasksToInsert[$i]['estimated_time'] = round((strtotime($newSubtask['end']['dateTime']) - strtotime($newSubtask['start']['dateTime'])) / 3600, 1); $subtasksToInsert[$i]['tracked_time'] = 0; $subtasksToInsert[$i]['created_by'] = $creator->id; $subtasksToInsert[$i]['office365_id'] = $newSubtask['id']; $subtasksToInsert[$i]['office365_change_key'] = $newSubtask['changeKey']; $subtasksToInsert[$i]['type'] = 'office365'; $subtasksToInsert[$i]['description'] = $newSubtask['body']['content']; $subtasksToInsert[$i]['deadline'] = Carbon::parse($newSubtask['end']['dateTime'])->setTimezone('Europe/Bratislava'); $subtasksToInsert[$i]['office365_location'] = $newSubtask['location']['displayName']; $subtasksToInsert[$i++]['is_finished'] = 0; } $subtasks = $mastertask->subtasks()->createMany($subtasksToInsert); // vytvorenie timeblocks foreach ($subtasks as $subtask) { $subtask->syncTimeblocks([ [ 'sub_task_id' => $subtask->id, 'start' => Carbon::parse($newSubtask['start']['dateTime'])->setTimezone('Europe/Bratislava'), 'end' => Carbon::parse($newSubtask['end']['dateTime'])->setTimezone('Europe/Bratislava'), 'is_finished' => 0, ] ]); } // manualne musim trigernut tuto operaciu, lebo cez Observer sa to nespusti - ten je nastaveny len na update subtasku $mastertask->recalculateEstimatedTime(); } /** * @param mixed $events * * @return mixed */ public static function getSubtasks_from_office_365_events(mixed $events): mixed { return array_filter($events, function ($event) { $projectCode = explode(' ', $event['subject'])[0]; // if project code exist in database, then it is subtask return Project::where('code', $projectCode)->exists(); }); } public static function editSubtasks(mixed $subtasks_from_office365) { // get edited subtasks - the ones, that has different changeKey from our database $editedSubtasks = array_filter($subtasks_from_office365, function ($event) { $subtask = SubTask::where('office365_id', $event['id'])->first(); return $subtask && $subtask->office365_change_key !== $event['changeKey']; }); foreach ($editedSubtasks as $editedSubtask) { try { self::editSubtask($editedSubtask); } catch (\Exception $e) { Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } } } private static function editSubtask(mixed $editedSubtask) { $attendees = $editedSubtask['attendees']; // vrati subtasky pre userov, ktori sa uz nenachadzaju v evente, ale v nasej databaze tento subtask maju. ale iba v pripade, ak ich subtasky este nie su ukoncene (is_finished != 1) $subtasksToDelete = self::getSubtasksToDeleteFromEditedEvents($editedSubtask['id'], $attendees); foreach ($subtasksToDelete as $subtaskToDelete) { self::removeSubtask($subtaskToDelete); } // novym ucastnikom sa klasicky vytvori subtask ako v pripade noveho subtasku. pre 1 subtask existuje iba 1 user. self::createNewSubtaskToNewAttendee($attendees, $editedSubtask); // existujucim ucastnikom sa updatne subtask foreach ($attendees as $attendee) { $idOfUserFromOffice365 = User::where('email', $attendee['emailAddress']['address'])->first(); // if user doesnt exist in our database, we skip it if ( ! $idOfUserFromOffice365) { continue; } $subtask = SubTask::where('office365_id', $editedSubtask['id'])->where('user_id', $idOfUserFromOffice365->id)->first(); if ( ! $subtask) { continue; } $subtask->update([ 'office365_change_key' => $editedSubtask['changeKey'], 'title' => substr($editedSubtask['subject'], strpos($editedSubtask['subject'], ' ') + 1), 'estimated_time' => round((strtotime($editedSubtask['end']['dateTime']) - strtotime($editedSubtask['start']['dateTime'])) / 3600, 1), 'description' => $editedSubtask['body']['content'], 'deadline' => Carbon::parse($editedSubtask['end']['dateTime'])->setTimezone('Europe/Bratislava'), 'office365_location' => $editedSubtask['location']['displayName'], ]); $oldTimeblock = $subtask->timeblocks->first(); // ak sa zmenilo trvanie alebo casy subtasku, tak sa updatne aj timeblock if ($oldTimeblock['start'] != Carbon::parse($editedSubtask['start']['dateTime'])->setTimezone('Europe/Bratislava') || $oldTimeblock['end'] != Carbon::parse($editedSubtask['end']['dateTime'])->setTimezone('Europe/Bratislava')) { $subtask->syncTimeblocks([ [ 'sub_task_id' => $subtask->id, 'start' => Carbon::parse($editedSubtask['start']['dateTime'])->setTimezone('Europe/Bratislava'), 'end' => Carbon::parse($editedSubtask['end']['dateTime'])->setTimezone('Europe/Bratislava'), 'is_finished' => 0, ] ]); } } // manualne musim trignernut tuto operaciu, lebo cez Observer sa to nespusti - ten je nastaveny len na update subtasku Subtask::where('office365_id', $editedSubtask['id'])->first()->mastertask->recalculateEstimatedTime(); } private static function getSubtasksToDeleteFromEditedEvents( $id, mixed $attendees ): \Illuminate\Support\Collection|array|\LaravelIdea\Helper\App\Models\_IH_SubTask_C { $subtasksToDelete = SubTask::where('is_finished', 0)->where('office365_id', $id)->get()->filter(function ($subtask) use ( $attendees ) { $event = collect($attendees)->firstWhere('emailAddress.address', $subtask->user->email); return ! $event; }); return $subtasksToDelete; } /** * @param array|\LaravelIdea\Helper\App\Models\_IH_SubTask_C|\Illuminate\Support\Collection $removedSubtasks * * @return void */ private static function removeSubtask($subtask ): void { // vymazanie timeblockov foreach ($subtask->timeblocks as $timeblock) { $timeblock->delete(); } // vymazanie timesheetov foreach ($subtask->timesheets as $timesheet) { $timesheet->delete(); } $subtask->delete(); } /** * @param mixed $attendees * @param mixed $editedSubtask * * @return void */ private static function createNewSubtaskToNewAttendee(mixed $attendees, mixed $editedSubtask): void { $newAttendees = array_filter($attendees, function ($attendee) use ($editedSubtask) { $idsOfAttendees = SubTask::where('office365_id', $editedSubtask['id'])->get()->pluck('user_id')->toArray(); $idOfUserFromOffice365 = User::where('email', $attendee['emailAddress']['address'])->first(); // if user doesnt exist in our database, we skip it if ( ! $idOfUserFromOffice365) { return false; } return ! in_array($idOfUserFromOffice365->id, $idsOfAttendees); }); // nakopiruj subtask tak, ze bude obsahovat iba newAttendees $editedSubtaskWithOnlyNewAttendees = $editedSubtask; $editedSubtaskWithOnlyNewAttendees['attendees'] = $newAttendees; $project = Project::where('code', explode(' ', $editedSubtask['subject'])[0])->first(); // ak existuje viac master taskov, pouzijeme posledny $mastertask = MasterTask::where('project_id', $project->id)->latest()->first(); self::addSubtask($editedSubtaskWithOnlyNewAttendees, $mastertask); } public static function removeBlockers(mixed $blockers_from_office365) { // get all canceled blockers - the ones, that are in our database and in the response they have isCancelled => true $removedBlockers = Blocker::whereNotNull('office365_id')->get()->filter(function ($blocker) use ( $blockers_from_office365 ) { $event = collect($blockers_from_office365)->firstWhere('id', $blocker->office365_id); return $event && $event['isCancelled']; }); $removedBlockers->each(function ($blocker) { try { $blocker->delete(); } catch (\Exception $e) { // log error Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } }); } /** * @param $id * @param mixed $attendees * * @return SubTask[]|\Illuminate\Support\Collection|\LaravelIdea\Helper\App\Models\_IH_SubTask_C */ public static function removeSubtasks(mixed $subtasks_from_office365) { // get all canceled subtasks - the ones, that are in our database and in the response they have isCancelled => true $removedSubtasks = SubTask::where('is_finished', 0)->whereNotNull('office365_id')->get()->filter(function ( $subtask ) use ( $subtasks_from_office365 ) { $event = collect($subtasks_from_office365)->firstWhere('id', $subtask->office365_id); return $event && $event['isCancelled']; }); foreach ($removedSubtasks as $removed_subtask) { try { self::removeSubtask($removed_subtask); } catch (\Exception $e) { Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } } } public static function finishSubtasks(mixed $subtasks) { foreach ($subtasks as $subtask) { try { // ukoncenie timeblocks foreach ($subtask->timeblocks as $timeblock) { $timeblock->update([ 'is_finished' => 1, ]); } // pridanie odtrackovaneho casu $subtask->syncTimesheets([ [ 'sub_task_id' => $subtask->id, 'employee_id' => $subtask->user_id, 'time' => $subtask->estimated_time, ], ]); $subtask->update([ 'is_finished' => 1, ]); } catch (\Exception $e) { Mail::to('feher@studiotem.com')->send(new Error($e)); \Log::error($e->getMessage()); } } // manualne musim trignernut tuto operaciu, lebo cez Observer sa to nespusti - ten je nastaveny len na update subtasku $subtasks->first()->mastertask->recalculateTrackedTime(); } }