From 5e65575607da6f68da6447be79425ff95a7a8478 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Tue, 2 Dec 2025 18:22:47 -0300 Subject: [PATCH 1/9] feat: add news formatter for entitys related to Presentation for speakers --- app/Audit/AuditLogFormatterFactory.php | 2 +- .../PresentationSpeakerAuditLogFormatter.php | 89 +++++++++++++ ...resentationSubmissionAuditLogFormatter.php | 113 ++++++++++++++++ .../SelectionPlanAuditLogFormatter.php | 122 ++++++++++++++++++ ...erRegistrationRequestAuditLogFormatter.php | 81 ++++++++++++ config/audit_log.php | 30 ++++- 6 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php diff --git a/app/Audit/AuditLogFormatterFactory.php b/app/Audit/AuditLogFormatterFactory.php index 405e2e786..b05799c2b 100644 --- a/app/Audit/AuditLogFormatterFactory.php +++ b/app/Audit/AuditLogFormatterFactory.php @@ -45,7 +45,7 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt if (count($subject) > 0) { $child_entity = $subject[0]; } - if (is_null($child_entity) && count($subject->getSnapshot()) > 0) { + if (is_null($child_entity) && isset($subject->getSnapshot()[0]) && count($subject->getSnapshot()) > 0) { $child_entity = $subject->getSnapshot()[0]; } $child_entity_formatter = $child_entity != null ? ChildEntityFormatterFactory::build($child_entity) : null; diff --git a/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php new file mode 100644 index 000000000..83e7384e6 --- /dev/null +++ b/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php @@ -0,0 +1,89 @@ +event_type = $event_type; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof PresentationSpeaker) { + return null; + } + + try { + $full_name = sprintf("%s %s", $subject->getFirstName() ?? 'Unknown', $subject->getLastName() ?? 'Unknown'); + $email = $subject->getEmail() ?? 'unknown@example.com'; + $speaker_id = $subject->getId() ?? 'unknown'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + $bio = $subject->getBio() ? sprintf(" - Bio: %s", mb_substr($subject->getBio(), 0, 50)) : ''; + return sprintf( + "Speaker '%s' (%s) created with email '%s'%s", + $full_name, + $speaker_id, + $email, + $bio + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + if (isset($change_set['FirstName']) || isset($change_set['LastName'])) { + $changed_fields[] = "name"; + } + if (isset($change_set['Email'])) { + $changed_fields[] = "email"; + } + if (isset($change_set['Title'])) { + $changed_fields[] = "title"; + } + + if (isset($change_set['Country'])) { + $changed_fields[] = "country"; + } + if (isset($change_set['AvailableForBureau'])) { + $changed_fields[] = "available_for_bureau"; + } + if (isset($change_set['FundedTravel'])) { + $changed_fields[] = "funded_travel"; + } + if (isset($change_set['WillingToTravel'])) { + $changed_fields[] = "willing_to_travel"; + } + if (isset($change_set['WillingToPresentVideo'])) { + $changed_fields[] = "willing_to_present_video"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + return sprintf( + "Speaker '%s' (%s) updated (%s changed)", + $full_name, + $speaker_id, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Speaker '%s' (%s) with email '%s' was deleted", + $full_name, + $speaker_id, + $email + ); + } + } catch (\Exception $ex) { + Log::warning("PresentationSpeakerAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php new file mode 100644 index 000000000..88eb14efb --- /dev/null +++ b/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php @@ -0,0 +1,113 @@ +event_type = $event_type; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof Presentation) { + return null; + } + + try { + $title = $subject->getTitle() ?? 'Unknown Presentation'; + $id = $subject->getId() ?? 'unknown'; + $creator = $subject->getCreator(); + $creator_name = $creator ? sprintf("%s %s", $creator->getFirstName() ?? '', $creator->getLastName() ?? '') : 'Unknown'; + $creator_name = trim($creator_name) ?: 'Unknown'; + $category = $subject->getCategory(); + $category_name = $category ? $category->getTitle() : 'Unassigned Track'; + $selection_plan = $subject->getSelectionPlan(); + $plan_name = $selection_plan ? $selection_plan->getName() : 'Unknown Plan'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Presentation '%s' (%d) submitted by '%s' to track '%s' (Plan: %s)", + $title, + $id, + $creator_name, + $category_name, + $plan_name + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + $old_status = null; + $new_status = null; + + if (isset($change_set['Title'])) { + $changed_fields[] = "title"; + } + if (isset($change_set['Abstract'])) { + $changed_fields[] = "abstract"; + } + if (isset($change_set['ProblemAddressed'])) { + $changed_fields[] = "problem_addressed"; + } + if (isset($change_set['AttendeesExpectedLearnt'])) { + $changed_fields[] = "attendees_expected_learnt"; + } + + if (isset($change_set['Status'])) { + $changed_fields[] = "status"; + $old_status = $change_set['Status'][0] ?? null; + $new_status = $change_set['Status'][1] ?? null; + } + if (isset($change_set['CategoryID']) || isset($change_set['category'])) { + $changed_fields[] = "track"; + } + if (isset($change_set['Published'])) { + $changed_fields[] = "published"; + } + if (isset($change_set['SelectionPlanID'])) { + $changed_fields[] = "selection_plan"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + + if ($old_status && $new_status) { + return sprintf( + "Presentation '%s' (%d) status changed: %s → %s (%s changed)", + $title, + $id, + strtoupper($old_status), + strtoupper($new_status), + $fields_str + ); + } + + return sprintf( + "Presentation '%s' (%d) updated (%s changed)", + $title, + $id, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Presentation '%s' (%d) submitted by '%s' to track '%s' was deleted", + $title, + $id, + $creator_name, + $category_name + ); + } + } catch (\Exception $ex) { + Log::warning("PresentationSubmissionAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php new file mode 100644 index 000000000..07e5fbf9f --- /dev/null +++ b/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php @@ -0,0 +1,122 @@ +event_type = $event_type; + } + + private function formatDate($date): string + { + if ($date instanceof \DateTime) { + return $date->format('Y-m-d H:i:s'); + } + return $date ?? 'N/A'; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof SelectionPlan) { + return null; + } + + try { + $name = $subject->getName() ?? 'Unknown Plan'; + $id = $subject->getId() ?? 'unknown'; + $summit = $subject->getSummit(); + $summit_name = $summit ? $summit->getName() : 'Unknown Summit'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + $submission_dates = $subject->getSubmissionBeginDate() && $subject->getSubmissionEndDate() + ? sprintf( + "[%s - %s]", + $this->formatDate($subject->getSubmissionBeginDate()), + $this->formatDate($subject->getSubmissionEndDate()) + ) + : 'No dates set'; + + return sprintf( + "Selection Plan '%s' (%d) created for Summit '%s' with CFP period: %s", + $name, + $id, + $summit_name, + $submission_dates + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + + if (isset($change_set['Name'])) { + $changed_fields[] = "name"; + } + if (isset($change_set['Enabled'])) { + $status = $change_set['Enabled'][1] ? 'enabled' : 'disabled'; + $changed_fields[] = "status ($status)"; + } + if (isset($change_set['IsHidden'])) { + $changed_fields[] = "visibility"; + } + if (isset($change_set['AllowNewPresentations'])) { + $changed_fields[] = "allow_new_presentations"; + } + if (isset($change_set['AllowProposedSchedules'])) { + $changed_fields[] = "allow_proposed_schedules"; + } + if (isset($change_set['AllowTrackChangeRequests'])) { + $changed_fields[] = "allow_track_change_requests"; + } + if (isset($change_set['SubmissionBeginDate']) || isset($change_set['SubmissionEndDate'])) { + $old_begin = isset($change_set['SubmissionBeginDate']) ? $this->formatDate($change_set['SubmissionBeginDate'][0]) : 'N/A'; + $new_begin = isset($change_set['SubmissionBeginDate']) ? $this->formatDate($change_set['SubmissionBeginDate'][1]) : 'N/A'; + $old_end = isset($change_set['SubmissionEndDate']) ? $this->formatDate($change_set['SubmissionEndDate'][0]) : 'N/A'; + $new_end = isset($change_set['SubmissionEndDate']) ? $this->formatDate($change_set['SubmissionEndDate'][1]) : 'N/A'; + $changed_fields[] = sprintf("CFP period [%s - %s] → [%s - %s]", $old_begin, $old_end, $new_begin, $new_end); + } + if (isset($change_set['VotingBeginDate']) || isset($change_set['VotingEndDate'])) { + $changed_fields[] = "voting period"; + } + if (isset($change_set['SelectionBeginDate']) || isset($change_set['SelectionEndDate'])) { + $changed_fields[] = "selection period"; + } + if (isset($change_set['MaxSubmissionAllowedPerUser'])) { + $old_val = $change_set['MaxSubmissionAllowedPerUser'][0] ?? 0; + $new_val = $change_set['MaxSubmissionAllowedPerUser'][1] ?? 0; + $changed_fields[] = sprintf("max_submissions (%d → %d)", $old_val, $new_val); + } + if (isset($change_set['SubmissionPeriodDisclaimer'])) { + $changed_fields[] = "disclaimer"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + return sprintf( + "Selection Plan '%s' (%d) for Summit '%s' updated (%s changed)", + $name, + $id, + $summit_name, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Selection Plan '%s' (%d) for Summit '%s' was deleted", + $name, + $id, + $summit_name + ); + } + } catch (\Exception $ex) { + Log::warning("SelectionPlanAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php new file mode 100644 index 000000000..c082d65fd --- /dev/null +++ b/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php @@ -0,0 +1,81 @@ +event_type = $event_type; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof SpeakerRegistrationRequest) { + return null; + } + + try { + $email = $subject->getEmail() ?? 'unknown@example.com'; + $speaker = $subject->getSpeaker(); + $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'Unknown'; + $speaker_name = trim($speaker_name) ?: 'Unknown'; + $is_confirmed = $subject->isConfirmed(); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Speaker registration request created for email '%s'", + $email + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + + if (isset($change_set['Email'])) { + $old_email = $change_set['Email'][0] ?? 'N/A'; + $new_email = $change_set['Email'][1] ?? 'N/A'; + $changed_fields[] = sprintf("email %s → %s", $old_email, $new_email); + } + if (isset($change_set['IsConfirmed'])) { + $old_status = $change_set['IsConfirmed'][0] ? 'confirmed' : 'pending'; + $new_status = $change_set['IsConfirmed'][1] ? 'confirmed' : 'pending'; + $changed_fields[] = sprintf("status %s → %s", $old_status, $new_status); + } + if (isset($change_set['ConfirmationDate'])) { + $changed_fields[] = "confirmation_date"; + } + if (isset($change_set['ProposerID'])) { + $changed_fields[] = "proposer"; + } + if (isset($change_set['SpeakerID'])) { + $changed_fields[] = "speaker"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + return sprintf( + "Speaker registration request for '%s' (%s changed)", + $email, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + $status = $is_confirmed ? 'confirmed' : 'pending'; + return sprintf( + "Speaker registration request for email '%s' (status: %s) was deleted", + $email, + $status + ); + } + } catch (\Exception $ex) { + Log::warning("SpeakerRegistrationRequestAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/config/audit_log.php b/config/audit_log.php index bb26f9521..92d43804d 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -5,6 +5,34 @@ \models\main\SummitMemberSchedule::class => [ 'enabled' => true, 'strategy' => \App\Audit\ConcreteFormatters\SummitMemberScheduleAuditLogFormatter::class, - ] + ], + \models\summit\SummitTrackChair::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SummitTrackChairAuditLogFormatter::class, + ], + \App\Models\Foundation\Summit\Events\Presentations\TrackChairs\PresentationTrackChairRatingType::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\PresentationTrackChairRatingTypeAuditLogFormatter::class, + ], + \App\Models\Foundation\Summit\Events\Presentations\TrackChairs\PresentationTrackChairScoreType::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\PresentationTrackChairScoreTypeAuditLogFormatter::class, + ], + \models\summit\PresentationSpeaker::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\PresentationSpeakerAuditLogFormatter::class, + ], + \models\summit\Presentation::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\PresentationSubmissionAuditLogFormatter::class, + ], + \App\Models\Foundation\Summit\SelectionPlan::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SelectionPlanAuditLogFormatter::class, + ], + \models\summit\SpeakerRegistrationRequest::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SpeakerRegistrationRequestAuditLogFormatter::class, + ], ] ]; From 7b0424695c0bf4570e07270b3a7d9bb6217012cb Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Wed, 3 Dec 2025 15:47:58 -0300 Subject: [PATCH 2/9] feat: add a formatter for entities related to speaker presentations --- .../FeaturedSpeakerAuditLogFormatter.php | 79 ++++++++++++++ .../SpeakerAssistanceAuditLogFormatter.php | 103 ++++++++++++++++++ .../SubmissionInvitationAuditLogFormatter.php | 89 +++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php diff --git a/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php b/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php new file mode 100644 index 000000000..d371ade0e --- /dev/null +++ b/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php @@ -0,0 +1,79 @@ +event_type = $event_type; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof FeaturedSpeaker) { + return null; + } + + try { + $speaker = $subject->getSpeaker(); + $speaker_email = $speaker ? ($speaker->getEmail() ?? 'unknown@example.com') : 'unknown@example.com'; + $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'Unknown'; + $speaker_name = trim($speaker_name) ?: $speaker_name; + $speaker_id = $speaker ? ($speaker->getId() ?? 'unknown') : 'unknown'; + + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + + $order = $subject->getOrder(); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return sprintf( + "Speaker '%s' (%s) added as featured speaker for Summit '%s' with display order %d", + $speaker_name, + $speaker_id, + $summit_name, + $order + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + + if (isset($change_set['Order'])) { + $old_order = $change_set['Order'][0] ?? 'unknown'; + $new_order = $change_set['Order'][1] ?? 'unknown'; + $changed_fields[] = sprintf("display_order %s → %s", $old_order, $new_order); + } + if (isset($change_set['PresentationSpeakerID'])) { + $changed_fields[] = "speaker"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + return sprintf( + "Featured speaker '%s' (%s) updated (%s changed)", + $speaker_name, + $speaker_id, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return sprintf( + "Speaker '%s' (%s) removed from featured speakers list of Summit '%s'", + $speaker_name, + $speaker_id, + $summit_name + ); + } + } catch (\Exception $ex) { + Log::warning("FeaturedSpeakerAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php new file mode 100644 index 000000000..cc8108d7e --- /dev/null +++ b/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php @@ -0,0 +1,103 @@ +event_type = $event_type; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof PresentationSpeakerSummitAssistanceConfirmationRequest) { + return null; + } + + try { + $speaker = $subject->getSpeaker(); + $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'Unknown'; + $speaker_email = $speaker ? ($speaker->getEmail() ?? 'unknown@example.com') : 'unknown@example.com'; + $speaker_name = trim($speaker_name) ?: $speaker_email; + $speaker_id = $speaker ? ($speaker->getId() ?? 'unknown') : 'unknown'; + + $summit = $subject->getSummit(); + $summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit'; + + $is_confirmed = $subject->isConfirmed(); + $is_registered = $subject->isRegistered(); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + $status = $is_confirmed ? 'confirmed' : 'pending'; + $registration_status = $is_registered ? 'registered' : 'unregistered'; + return sprintf( + "Speaker assistance created for '%s' (%s) on Summit '%s' [confirmation: %s, registration: %s]", + $speaker_name, + $speaker_id, + $summit_name, + $status, + $registration_status + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + + if (isset($change_set['IsConfirmed'])) { + $old_status = $change_set['IsConfirmed'][0] ? 'confirmed' : 'pending'; + $new_status = $change_set['IsConfirmed'][1] ? 'confirmed' : 'pending'; + $changed_fields[] = sprintf("confirmation %s → %s", $old_status, $new_status); + } + if (isset($change_set['CheckedIn'])) { + $old_status = $change_set['CheckedIn'][0] ? 'checked_in' : 'not_checked_in'; + $new_status = $change_set['CheckedIn'][1] ? 'checked_in' : 'not_checked_in'; + $changed_fields[] = sprintf("check_in %s → %s", $old_status, $new_status); + } + if (isset($change_set['RegisteredForSummit'])) { + $old_status = $change_set['RegisteredForSummit'][0] ? 'registered' : 'unregistered'; + $new_status = $change_set['RegisteredForSummit'][1] ? 'registered' : 'unregistered'; + $changed_fields[] = sprintf("registration %s → %s", $old_status, $new_status); + } + if (isset($change_set['OnSitePhoneNumber'])) { + $old_phone = $change_set['OnSitePhoneNumber'][0] ?? 'N/A'; + $new_phone = $change_set['OnSitePhoneNumber'][1] ?? 'N/A'; + $changed_fields[] = "on_site_phone"; + } + if (isset($change_set['ConfirmationDate'])) { + $changed_fields[] = "confirmation_date"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + return sprintf( + "Speaker assistance for '%s' (%s) on Summit '%s' updated (%s changed)", + $speaker_name, + $speaker_id, + $summit_name, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + $status = $is_confirmed ? 'confirmed' : 'pending'; + $registration_status = $is_registered ? 'registered' : 'unregistered'; + return sprintf( + "Speaker assistance for '%s' (%s) on Summit '%s' [confirmation: %s, registration: %s] was deleted", + $speaker_name, + $speaker_id, + $summit_name, + $status, + $registration_status + ); + } + } catch (\Exception $ex) { + Log::warning("SpeakerAssistanceAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} diff --git a/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php new file mode 100644 index 000000000..ac142a20e --- /dev/null +++ b/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php @@ -0,0 +1,89 @@ +event_type = $event_type; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof SummitSubmissionInvitation) { + return null; + } + + try { + $email = $subject->getEmail() ?? 'unknown@example.com'; + $first_name = $subject->getFirstName() ?? 'Unknown'; + $last_name = $subject->getLastName() ?? ''; + $full_name = trim(sprintf("%s %s", $first_name, $last_name)) ?: 'Unknown'; + $is_sent = $subject->isSent(); + $speaker = $subject->getSpeaker(); + $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'None'; + $speaker_name = trim($speaker_name) ?: 'None'; + $id = $subject->getId() ?? 'unknown'; + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + $sent_status = $is_sent ? 'sent' : 'not sent'; + return sprintf( + "Submission invitation created for '%s' (%s) with email '%s' [status: %s]", + $full_name, + $id, + $email, + $sent_status + ); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $changed_fields = []; + + if (isset($change_set['FirstName']) || isset($change_set['LastName'])) { + $changed_fields[] = "name"; + } + if (isset($change_set['Email'])) { + $old_email = $change_set['Email'][0] ?? 'N/A'; + $new_email = $change_set['Email'][1] ?? 'N/A'; + $changed_fields[] = sprintf("email %s to %s", $old_email, $new_email); + } + if (isset($change_set['SentDate'])) { + $old_status = isset($change_set['SentDate'][0]) && $change_set['SentDate'][0] ? 'sent' : 'not sent'; + $new_status = isset($change_set['SentDate'][1]) && $change_set['SentDate'][1] ? 'sent' : 'not sent'; + $changed_fields[] = sprintf("status %s to %s", $old_status, $new_status); + } + if (isset($change_set['SpeakerID'])) { + $changed_fields[] = "speaker"; + } + + $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; + return sprintf( + "Submission invitation for '%s' (%s) updated (%s changed)", + $email, + $id, + $fields_str + ); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + $sent_status = $is_sent ? 'sent' : 'pending'; + return sprintf( + "Submission invitation for '%s' (%s) with email '%s' [status: %s] was deleted", + $full_name, + $id, + $email, + $sent_status + ); + } + } catch (\Exception $ex) { + Log::warning("SubmissionInvitationAuditLogFormatter error: " . $ex->getMessage()); + } + + return null; + } +} From ebf0e276793ba503c4478afeb4751452b63f6dd7 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Wed, 3 Dec 2025 16:33:38 -0300 Subject: [PATCH 3/9] chore: replace the otlp interface with abstractFactory. Add the user context to the formatters --- .../FeaturedSpeakerAuditLogFormatter.php | 55 ++++++++++++++---- .../PresentationSpeakerAuditLogFormatter.php | 55 ++++++++++++++---- ...resentationSubmissionAuditLogFormatter.php | 58 +++++++++++++++---- .../SelectionPlanAuditLogFormatter.php | 53 ++++++++++++++--- .../SpeakerAssistanceAuditLogFormatter.php | 57 ++++++++++++++---- ...erRegistrationRequestAuditLogFormatter.php | 55 ++++++++++++++---- .../SubmissionInvitationAuditLogFormatter.php | 55 ++++++++++++++---- config/audit_log.php | 24 ++++---- 8 files changed, 328 insertions(+), 84 deletions(-) diff --git a/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php b/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php index d371ade0e..16bfb0089 100644 --- a/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + public function format($subject, array $change_set): ?string { if (!$subject instanceof FeaturedSpeaker) { @@ -22,7 +54,7 @@ public function format($subject, array $change_set): ?string try { $speaker = $subject->getSpeaker(); - $speaker_email = $speaker ? ($speaker->getEmail() ?? 'unknown@example.com') : 'unknown@example.com'; + $speaker_email = $speaker ? ($speaker->getEmail() ?? 'unknown') : 'unknown'; $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'Unknown'; $speaker_name = trim($speaker_name) ?: $speaker_name; $speaker_id = $speaker ? ($speaker->getId() ?? 'unknown') : 'unknown'; @@ -35,11 +67,12 @@ public function format($subject, array $change_set): ?string switch ($this->event_type) { case IAuditStrategy::EVENT_ENTITY_CREATION: return sprintf( - "Speaker '%s' (%s) added as featured speaker for Summit '%s' with display order %d", + "Speaker '%s' (%s) added as featured speaker for Summit '%s' with display order %d by user %s", $speaker_name, $speaker_id, $summit_name, - $order + $order, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -56,18 +89,20 @@ public function format($subject, array $change_set): ?string $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; return sprintf( - "Featured speaker '%s' (%s) updated (%s changed)", + "Featured speaker '%s' (%s) updated (%s changed) by user %s", $speaker_name, $speaker_id, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: return sprintf( - "Speaker '%s' (%s) removed from featured speakers list of Summit '%s'", + "Speaker '%s' (%s) removed from featured speakers list of Summit '%s' by user %s", $speaker_name, $speaker_id, - $summit_name + $summit_name, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php index 83e7384e6..86e71071c 100644 --- a/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + public function format($subject, array $change_set): ?string { if (!$subject instanceof PresentationSpeaker) { @@ -22,18 +54,19 @@ public function format($subject, array $change_set): ?string try { $full_name = sprintf("%s %s", $subject->getFirstName() ?? 'Unknown', $subject->getLastName() ?? 'Unknown'); - $email = $subject->getEmail() ?? 'unknown@example.com'; + $email = $subject->getEmail() ?? 'unknown'; $speaker_id = $subject->getId() ?? 'unknown'; switch ($this->event_type) { case IAuditStrategy::EVENT_ENTITY_CREATION: $bio = $subject->getBio() ? sprintf(" - Bio: %s", mb_substr($subject->getBio(), 0, 50)) : ''; return sprintf( - "Speaker '%s' (%s) created with email '%s'%s", + "Speaker '%s' (%s) created with email '%s'%s by user %s", $full_name, $speaker_id, $email, - $bio + $bio, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -66,18 +99,20 @@ public function format($subject, array $change_set): ?string $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; return sprintf( - "Speaker '%s' (%s) updated (%s changed)", + "Speaker '%s' (%s) updated (%s changed) by user %s", $full_name, $speaker_id, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: return sprintf( - "Speaker '%s' (%s) with email '%s' was deleted", + "Speaker '%s' (%s) with email '%s' was deleted by user %s", $full_name, $speaker_id, - $email + $email, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php index 88eb14efb..8442f0d5e 100644 --- a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + public function format($subject, array $change_set): ?string { if (!$subject instanceof Presentation) { @@ -34,12 +66,13 @@ public function format($subject, array $change_set): ?string switch ($this->event_type) { case IAuditStrategy::EVENT_ENTITY_CREATION: return sprintf( - "Presentation '%s' (%d) submitted by '%s' to track '%s' (Plan: %s)", + "Presentation '%s' (%d) submitted by '%s' to track '%s' (Plan: %s) by user %s", $title, $id, $creator_name, $category_name, - $plan_name + $plan_name, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -79,29 +112,32 @@ public function format($subject, array $change_set): ?string if ($old_status && $new_status) { return sprintf( - "Presentation '%s' (%d) status changed: %s → %s (%s changed)", + "Presentation '%s' (%d) status changed: %s → %s (%s changed) by user %s", $title, $id, strtoupper($old_status), strtoupper($new_status), - $fields_str + $fields_str, + $this->getUserInfo() ); } return sprintf( - "Presentation '%s' (%d) updated (%s changed)", + "Presentation '%s' (%d) updated (%s changed) by user %s", $title, $id, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: return sprintf( - "Presentation '%s' (%d) submitted by '%s' to track '%s' was deleted", + "Presentation '%s' (%d) submitted by '%s' to track '%s' was deleted by user %s", $title, $id, $creator_name, - $category_name + $category_name, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php index 07e5fbf9f..b4927a0a4 100644 --- a/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + private function formatDate($date): string { if ($date instanceof \DateTime) { @@ -45,11 +77,12 @@ public function format($subject, array $change_set): ?string : 'No dates set'; return sprintf( - "Selection Plan '%s' (%d) created for Summit '%s' with CFP period: %s", + "Selection Plan '%s' (%d) created for Summit '%s' with CFP period: %s by user %s", $name, $id, $summit_name, - $submission_dates + $submission_dates, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -98,19 +131,21 @@ public function format($subject, array $change_set): ?string $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; return sprintf( - "Selection Plan '%s' (%d) for Summit '%s' updated (%s changed)", + "Selection Plan '%s' (%d) for Summit '%s' updated (%s changed) by user %s", $name, $id, $summit_name, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: return sprintf( - "Selection Plan '%s' (%d) for Summit '%s' was deleted", + "Selection Plan '%s' (%d) for Summit '%s' was deleted by user %s", $name, $id, - $summit_name + $summit_name, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php index cc8108d7e..d0ac09961 100644 --- a/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + public function format($subject, array $change_set): ?string { if (!$subject instanceof PresentationSpeakerSummitAssistanceConfirmationRequest) { @@ -23,7 +55,7 @@ public function format($subject, array $change_set): ?string try { $speaker = $subject->getSpeaker(); $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'Unknown'; - $speaker_email = $speaker ? ($speaker->getEmail() ?? 'unknown@example.com') : 'unknown@example.com'; + $speaker_email = $speaker ? ($speaker->getEmail() ?? 'unknown') : 'unknown'; $speaker_name = trim($speaker_name) ?: $speaker_email; $speaker_id = $speaker ? ($speaker->getId() ?? 'unknown') : 'unknown'; @@ -38,12 +70,13 @@ public function format($subject, array $change_set): ?string $status = $is_confirmed ? 'confirmed' : 'pending'; $registration_status = $is_registered ? 'registered' : 'unregistered'; return sprintf( - "Speaker assistance created for '%s' (%s) on Summit '%s' [confirmation: %s, registration: %s]", + "Speaker assistance created for '%s' (%s) on Summit '%s' [confirmation: %s, registration: %s] by user %s", $speaker_name, $speaker_id, $summit_name, $status, - $registration_status + $registration_status, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -65,8 +98,6 @@ public function format($subject, array $change_set): ?string $changed_fields[] = sprintf("registration %s → %s", $old_status, $new_status); } if (isset($change_set['OnSitePhoneNumber'])) { - $old_phone = $change_set['OnSitePhoneNumber'][0] ?? 'N/A'; - $new_phone = $change_set['OnSitePhoneNumber'][1] ?? 'N/A'; $changed_fields[] = "on_site_phone"; } if (isset($change_set['ConfirmationDate'])) { @@ -75,23 +106,25 @@ public function format($subject, array $change_set): ?string $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; return sprintf( - "Speaker assistance for '%s' (%s) on Summit '%s' updated (%s changed)", + "Speaker assistance for '%s' (%s) on Summit '%s' updated (%s changed) by user %s", $speaker_name, $speaker_id, $summit_name, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: $status = $is_confirmed ? 'confirmed' : 'pending'; $registration_status = $is_registered ? 'registered' : 'unregistered'; return sprintf( - "Speaker assistance for '%s' (%s) on Summit '%s' [confirmation: %s, registration: %s] was deleted", + "Speaker assistance for '%s' (%s) on Summit '%s' [confirmation: %s, registration: %s] was deleted by user %s", $speaker_name, $speaker_id, $summit_name, $status, - $registration_status + $registration_status, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php index c082d65fd..41f2d29f0 100644 --- a/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + public function format($subject, array $change_set): ?string { if (!$subject instanceof SpeakerRegistrationRequest) { @@ -21,7 +53,7 @@ public function format($subject, array $change_set): ?string } try { - $email = $subject->getEmail() ?? 'unknown@example.com'; + $email = $subject->getEmail() ?? 'unknown'; $speaker = $subject->getSpeaker(); $speaker_name = $speaker ? sprintf("%s %s", $speaker->getFirstName() ?? '', $speaker->getLastName() ?? '') : 'Unknown'; $speaker_name = trim($speaker_name) ?: 'Unknown'; @@ -30,8 +62,9 @@ public function format($subject, array $change_set): ?string switch ($this->event_type) { case IAuditStrategy::EVENT_ENTITY_CREATION: return sprintf( - "Speaker registration request created for email '%s'", - $email + "Speaker registration request created for email '%s' by user %s", + $email, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -59,17 +92,19 @@ public function format($subject, array $change_set): ?string $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; return sprintf( - "Speaker registration request for '%s' (%s changed)", + "Speaker registration request for '%s' (%s changed) by user %s", $email, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: $status = $is_confirmed ? 'confirmed' : 'pending'; return sprintf( - "Speaker registration request for email '%s' (status: %s) was deleted", + "Speaker registration request for email '%s' (status: %s) was deleted by user %s", $email, - $status + $status, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php index ac142a20e..1eeb0b809 100644 --- a/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php @@ -1,11 +1,26 @@ -event_type = $event_type; } + private function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + public function format($subject, array $change_set): ?string { if (!$subject instanceof SummitSubmissionInvitation) { @@ -21,7 +53,7 @@ public function format($subject, array $change_set): ?string } try { - $email = $subject->getEmail() ?? 'unknown@example.com'; + $email = $subject->getEmail() ?? 'unknown'; $first_name = $subject->getFirstName() ?? 'Unknown'; $last_name = $subject->getLastName() ?? ''; $full_name = trim(sprintf("%s %s", $first_name, $last_name)) ?: 'Unknown'; @@ -35,11 +67,12 @@ public function format($subject, array $change_set): ?string case IAuditStrategy::EVENT_ENTITY_CREATION: $sent_status = $is_sent ? 'sent' : 'not sent'; return sprintf( - "Submission invitation created for '%s' (%s) with email '%s' [status: %s]", + "Submission invitation created for '%s' (%s) with email '%s' [status: %s] by user %s", $full_name, $id, $email, - $sent_status + $sent_status, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_UPDATE: @@ -64,20 +97,22 @@ public function format($subject, array $change_set): ?string $fields_str = !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties'; return sprintf( - "Submission invitation for '%s' (%s) updated (%s changed)", + "Submission invitation for '%s' (%s) updated (%s changed) by user %s", $email, $id, - $fields_str + $fields_str, + $this->getUserInfo() ); case IAuditStrategy::EVENT_ENTITY_DELETION: $sent_status = $is_sent ? 'sent' : 'pending'; return sprintf( - "Submission invitation for '%s' (%s) with email '%s' [status: %s] was deleted", + "Submission invitation for '%s' (%s) with email '%s' [status: %s] was deleted by user %s", $full_name, $id, $email, - $sent_status + $sent_status, + $this->getUserInfo() ); } } catch (\Exception $ex) { diff --git a/config/audit_log.php b/config/audit_log.php index 92d43804d..44bed5cfe 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -6,18 +6,6 @@ 'enabled' => true, 'strategy' => \App\Audit\ConcreteFormatters\SummitMemberScheduleAuditLogFormatter::class, ], - \models\summit\SummitTrackChair::class => [ - 'enabled' => true, - 'strategy' => \App\Audit\ConcreteFormatters\SummitTrackChairAuditLogFormatter::class, - ], - \App\Models\Foundation\Summit\Events\Presentations\TrackChairs\PresentationTrackChairRatingType::class => [ - 'enabled' => true, - 'strategy' => \App\Audit\ConcreteFormatters\PresentationTrackChairRatingTypeAuditLogFormatter::class, - ], - \App\Models\Foundation\Summit\Events\Presentations\TrackChairs\PresentationTrackChairScoreType::class => [ - 'enabled' => true, - 'strategy' => \App\Audit\ConcreteFormatters\PresentationTrackChairScoreTypeAuditLogFormatter::class, - ], \models\summit\PresentationSpeaker::class => [ 'enabled' => true, 'strategy' => \App\Audit\ConcreteFormatters\PresentationSpeakerAuditLogFormatter::class, @@ -34,5 +22,17 @@ 'enabled' => true, 'strategy' => \App\Audit\ConcreteFormatters\SpeakerRegistrationRequestAuditLogFormatter::class, ], + \models\summit\SummitSubmissionInvitation::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SubmissionInvitationAuditLogFormatter::class, + ], + \App\Models\Foundation\Summit\Speakers\FeaturedSpeaker::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\FeaturedSpeakerAuditLogFormatter::class, + ], + \models\summit\PresentationSpeakerSummitAssistanceConfirmationRequest::class => [ + 'enabled' => true, + 'strategy' => \App\Audit\ConcreteFormatters\SpeakerAssistanceAuditLogFormatter::class, + ] ] ]; From 4eb7f818d0c06dfe843118160f165c278d8bfda4 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Fri, 5 Dec 2025 10:17:12 -0300 Subject: [PATCH 4/9] chore: refactor getUserInfo to AbstractAuditLog --- app/Audit/AbstractAuditLogFormatter.php | 17 +++++++++++ .../FeaturedSpeakerAuditLogFormatter.php | 17 ----------- .../PresentationSpeakerAuditLogFormatter.php | 17 ----------- ...resentationSubmissionAuditLogFormatter.php | 17 ----------- .../SelectionPlanAuditLogFormatter.php | 30 +++++++------------ .../SpeakerAssistanceAuditLogFormatter.php | 17 ----------- ...erRegistrationRequestAuditLogFormatter.php | 17 ----------- .../SubmissionInvitationAuditLogFormatter.php | 17 ----------- .../Foundation/Summit/SelectionPlan.php | 4 +++ 9 files changed, 32 insertions(+), 121 deletions(-) diff --git a/app/Audit/AbstractAuditLogFormatter.php b/app/Audit/AbstractAuditLogFormatter.php index 634b5a431..6fc8e7364 100644 --- a/app/Audit/AbstractAuditLogFormatter.php +++ b/app/Audit/AbstractAuditLogFormatter.php @@ -24,5 +24,22 @@ final public function setContext(AuditContext $ctx): void $this->ctx = $ctx; } + protected function getUserInfo(): string + { + if (!$this->ctx) { + return 'Unknown (unknown)'; + } + + $user_name = 'Unknown'; + if ($this->ctx->userFirstName || $this->ctx->userLastName) { + $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; + } elseif ($this->ctx->userEmail) { + $user_name = $this->ctx->userEmail; + } + + $user_id = $this->ctx->userId ?? 'unknown'; + return sprintf("%s (%s)", $user_name, $user_id); + } + abstract public function format($subject, array $change_set): ?string; } diff --git a/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php b/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php index 16bfb0089..b793766e9 100644 --- a/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/FeaturedSpeakerAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - public function format($subject, array $change_set): ?string { if (!$subject instanceof FeaturedSpeaker) { diff --git a/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php index 86e71071c..3d81a3f3c 100644 --- a/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/PresentationSpeakerAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - public function format($subject, array $change_set): ?string { if (!$subject instanceof PresentationSpeaker) { diff --git a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php index 8442f0d5e..5fca4ab36 100644 --- a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - public function format($subject, array $change_set): ?string { if (!$subject instanceof Presentation) { diff --git a/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php index b4927a0a4..1366ec613 100644 --- a/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SelectionPlanAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - private function formatDate($date): string { if ($date instanceof \DateTime) { @@ -68,7 +51,7 @@ public function format($subject, array $change_set): ?string switch ($this->event_type) { case IAuditStrategy::EVENT_ENTITY_CREATION: - $submission_dates = $subject->getSubmissionBeginDate() && $subject->getSubmissionEndDate() + $submission_dates = $subject->hasSubmissionPeriodDefined() ? sprintf( "[%s - %s]", $this->formatDate($subject->getSubmissionBeginDate()), @@ -76,12 +59,21 @@ public function format($subject, array $change_set): ?string ) : 'No dates set'; + $selection_dates = $subject->hasSelectionPeriodDefined() + ? sprintf( + "[%s - %s]", + $this->formatDate($subject->getSelectionBeginDate()), + $this->formatDate($subject->getSelectionEndDate()) + ) + : 'No dates set'; + return sprintf( - "Selection Plan '%s' (%d) created for Summit '%s' with CFP period: %s by user %s", + "Selection Plan '%s' (%d) created for Summit '%s' with CFP period: %s, selection period: %s by user %s", $name, $id, $summit_name, $submission_dates, + $selection_dates, $this->getUserInfo() ); diff --git a/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php index d0ac09961..857b9728a 100644 --- a/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SpeakerAssistanceAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - public function format($subject, array $change_set): ?string { if (!$subject instanceof PresentationSpeakerSummitAssistanceConfirmationRequest) { diff --git a/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php index 41f2d29f0..21b8eac5a 100644 --- a/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SpeakerRegistrationRequestAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - public function format($subject, array $change_set): ?string { if (!$subject instanceof SpeakerRegistrationRequest) { diff --git a/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php b/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php index 1eeb0b809..260795fbd 100644 --- a/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/SubmissionInvitationAuditLogFormatter.php @@ -29,23 +29,6 @@ public function __construct(string $event_type) $this->event_type = $event_type; } - private function getUserInfo(): string - { - if (!$this->ctx) { - return 'Unknown (unknown)'; - } - - $user_name = 'Unknown'; - if ($this->ctx->userFirstName || $this->ctx->userLastName) { - $user_name = trim(sprintf("%s %s", $this->ctx->userFirstName ?? '', $this->ctx->userLastName ?? '')) ?: 'Unknown'; - } elseif ($this->ctx->userEmail) { - $user_name = $this->ctx->userEmail; - } - - $user_id = $this->ctx->userId ?? 'unknown'; - return sprintf("%s (%s)", $user_name, $user_id); - } - public function format($subject, array $change_set): ?string { if (!$subject instanceof SummitSubmissionInvitation) { diff --git a/app/Models/Foundation/Summit/SelectionPlan.php b/app/Models/Foundation/Summit/SelectionPlan.php index d68e1dab7..c602dc9e0 100644 --- a/app/Models/Foundation/Summit/SelectionPlan.php +++ b/app/Models/Foundation/Summit/SelectionPlan.php @@ -743,6 +743,10 @@ public function hasSelectionPeriodDefined():bool{ return !is_null($this->selection_begin_date) && !is_null($this->selection_end_date); } + public function hasSubmissionPeriodDefined():bool{ + return !is_null($this->submission_begin_date) && !is_null($this->submission_end_date); + } + /** * @return bool */ From 1d98087cfee5cf584846eff93ff0382e65df0d73 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Fri, 5 Dec 2025 10:37:39 -0300 Subject: [PATCH 5/9] chore: refactor strategy for get Formatters --- app/Audit/AuditLogFormatterFactory.php | 54 +++++++- .../BasePresentationAuditLogFormatter.php | 126 ++++++++++++++++++ .../PresentationEventApiAuditLogFormatter.php | 67 ++++++++++ ...resentationSubmissionAuditLogFormatter.php | 67 ++++++++++ ...tationUserSubmissionAuditLogFormatter.php} | 17 +-- config/audit_log.php | 15 ++- 6 files changed, 327 insertions(+), 19 deletions(-) create mode 100644 app/Audit/ConcreteFormatters/PresentationFormatters/BasePresentationAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/PresentationFormatters/PresentationEventApiAuditLogFormatter.php create mode 100644 app/Audit/ConcreteFormatters/PresentationFormatters/PresentationSubmissionAuditLogFormatter.php rename app/Audit/ConcreteFormatters/{PresentationSubmissionAuditLogFormatter.php => PresentationUserSubmissionAuditLogFormatter.php} (85%) diff --git a/app/Audit/AuditLogFormatterFactory.php b/app/Audit/AuditLogFormatterFactory.php index b05799c2b..de7bf902e 100644 --- a/app/Audit/AuditLogFormatterFactory.php +++ b/app/Audit/AuditLogFormatterFactory.php @@ -52,20 +52,29 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt $formatter = new EntityCollectionUpdateAuditLogFormatter($child_entity_formatter); break; case IAuditStrategy::EVENT_ENTITY_CREATION: - $formatter = $this->getStrategyClass($subject, $eventType); + $formatter = $this->getFormatterByContext($subject, $eventType, $ctx); + if (is_null($formatter)) { + $formatter = $this->getStrategyClass($subject, $eventType); + } if(is_null($formatter)) { $formatter = new EntityCreationAuditLogFormatter(); } break; case IAuditStrategy::EVENT_ENTITY_DELETION: - $formatter = $this->getStrategyClass($subject, $eventType); + $formatter = $this->getFormatterByContext($subject, $eventType, $ctx); + if (is_null($formatter)) { + $formatter = $this->getStrategyClass($subject, $eventType); + } if(is_null($formatter)) { $child_entity_formatter = ChildEntityFormatterFactory::build($subject); $formatter = new EntityDeletionAuditLogFormatter($child_entity_formatter); } break; case IAuditStrategy::EVENT_ENTITY_UPDATE: - $formatter = $this->getStrategyClass($subject, $eventType); + $formatter = $this->getFormatterByContext($subject, $eventType, $ctx); + if (is_null($formatter)) { + $formatter = $this->getStrategyClass($subject, $eventType); + } if(is_null($formatter)) { $child_entity_formatter = ChildEntityFormatterFactory::build($subject); $formatter = new EntityUpdateAuditLogFormatter($child_entity_formatter); @@ -75,4 +84,43 @@ public function make(AuditContext $ctx, $subject, $eventType): ?IAuditLogFormatt $formatter->setContext($ctx); return $formatter; } + + private function getFormatterByContext(object $subject, string $event_type, AuditContext $ctx): ?IAuditLogFormatter + { + $class = get_class($subject); + $entity_config = $this->config['entities'][$class] ?? null; + + if (!$entity_config || !isset($entity_config['strategies'])) { + return null; + } + + foreach ($entity_config['strategies'] as $strategy) { + if (!$this->matchesStrategy($strategy, $ctx)) { + continue; + } + + $formatter_class = $strategy['formatter'] ?? null; + return $formatter_class ? new $formatter_class($event_type) : null; + } + + return null; + } + + private function matchesStrategy(array $strategy, AuditContext $ctx): bool + { + if (isset($strategy['route']) && !$this->routeMatches($strategy['route'], $ctx->route)) { + return false; + } + + return true; + } + + private function routeMatches(string $pattern, string $actual_route): bool + { + $normalized_pattern = preg_replace('/\{[a-zA-Z_]+\}/', '\d+', $pattern); + $regex = '/^' . preg_quote($normalized_pattern, '/') . '$/'; + $regex = str_replace('\\\d+', '\d+', $regex); + + return preg_match($regex, $actual_route) === 1; + } } diff --git a/app/Audit/ConcreteFormatters/PresentationFormatters/BasePresentationAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationFormatters/BasePresentationAuditLogFormatter.php new file mode 100644 index 000000000..6d548b07e --- /dev/null +++ b/app/Audit/ConcreteFormatters/PresentationFormatters/BasePresentationAuditLogFormatter.php @@ -0,0 +1,126 @@ +event_type = $event_type; + } + + protected function extractChangedFields(array $change_set): array + { + $changed_fields = []; + $old_status = null; + $new_status = null; + + if (isset($change_set['Title'])) { + $changed_fields[] = "title"; + } + if (isset($change_set['Abstract'])) { + $changed_fields[] = "abstract"; + } + if (isset($change_set['ProblemAddressed'])) { + $changed_fields[] = "problem_addressed"; + } + if (isset($change_set['AttendeesExpectedLearnt'])) { + $changed_fields[] = "attendees_expected_learnt"; + } + + if (isset($change_set['Status'])) { + $changed_fields[] = "status"; + $old_status = $change_set['Status'][0] ?? null; + $new_status = $change_set['Status'][1] ?? null; + } + if (isset($change_set['CategoryID']) || isset($change_set['category'])) { + $changed_fields[] = "track"; + } + if (isset($change_set['Published'])) { + $changed_fields[] = "published"; + } + if (isset($change_set['SelectionPlanID'])) { + $changed_fields[] = "selection_plan"; + } + + return [ + 'fields' => !empty($changed_fields) ? implode(', ', $changed_fields) : 'properties', + 'old_status' => $old_status, + 'new_status' => $new_status, + ]; + } + + protected function getPresentationData(Presentation $subject): array + { + $creator = $subject->getCreator(); + $creator_name = $creator ? sprintf("%s %s", $creator->getFirstName() ?? '', $creator->getLastName() ?? '') : 'Unknown'; + $creator_name = trim($creator_name) ?: 'Unknown'; + + $category = $subject->getCategory(); + $category_name = $category ? $category->getTitle() : 'Unassigned Track'; + + $selection_plan = $subject->getSelectionPlan(); + $plan_name = $selection_plan ? $selection_plan->getName() : 'Unknown Plan'; + + return [ + 'title' => $subject->getTitle() ?? 'Unknown Presentation', + 'id' => $subject->getId() ?? 'unknown', + 'creator_name' => $creator_name, + 'category_name' => $category_name, + 'plan_name' => $plan_name, + ]; + } + + public function format($subject, array $change_set): ?string + { + if (!$subject instanceof Presentation) { + return null; + } + + try { + $data = $this->getPresentationData($subject); + + switch ($this->event_type) { + case IAuditStrategy::EVENT_ENTITY_CREATION: + return $this->formatCreation($data); + + case IAuditStrategy::EVENT_ENTITY_UPDATE: + $extracted = $this->extractChangedFields($change_set); + return $this->formatUpdate($data, $extracted); + + case IAuditStrategy::EVENT_ENTITY_DELETION: + return $this->formatDeletion($data); + } + } catch (\Exception $ex) { + Log::warning(static::class . " error: " . $ex->getMessage()); + } + + return null; + } + + abstract protected function formatCreation(array $data): string; + + abstract protected function formatUpdate(array $data, array $extracted): string; + + abstract protected function formatDeletion(array $data): string; +} diff --git a/app/Audit/ConcreteFormatters/PresentationFormatters/PresentationEventApiAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationFormatters/PresentationEventApiAuditLogFormatter.php new file mode 100644 index 000000000..78cf0c3e2 --- /dev/null +++ b/app/Audit/ConcreteFormatters/PresentationFormatters/PresentationEventApiAuditLogFormatter.php @@ -0,0 +1,67 @@ +getUserInfo() + ); + } + + protected function formatUpdate(array $data, array $extracted): string + { + if ($extracted['old_status'] && $extracted['new_status']) { + return sprintf( + "Presentation '%s' (%s) status changed: %s → %s (%s changed) by user %s", + $data['title'], + $data['id'], + strtoupper($extracted['old_status']), + strtoupper($extracted['new_status']), + $extracted['fields'], + $this->getUserInfo() + ); + } + + return sprintf( + "Presentation '%s' (%s) modified (%s changed) by user %s", + $data['title'], + $data['id'], + $extracted['fields'], + $this->getUserInfo() + ); + } + + protected function formatDeletion(array $data): string + { + return sprintf( + "Presentation '%s' (%s) created by '%s' under track '%s' was removed by user %s", + $data['title'], + $data['id'], + $data['creator_name'], + $data['category_name'], + $this->getUserInfo() + ); + } +} diff --git a/app/Audit/ConcreteFormatters/PresentationFormatters/PresentationSubmissionAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationFormatters/PresentationSubmissionAuditLogFormatter.php new file mode 100644 index 000000000..1343f4c0f --- /dev/null +++ b/app/Audit/ConcreteFormatters/PresentationFormatters/PresentationSubmissionAuditLogFormatter.php @@ -0,0 +1,67 @@ +getUserInfo() + ); + } + + protected function formatUpdate(array $data, array $extracted): string + { + if ($extracted['old_status'] && $extracted['new_status']) { + return sprintf( + "Presentation '%s' (%s) status changed: %s → %s (%s changed) by user %s", + $data['title'], + $data['id'], + strtoupper($extracted['old_status']), + strtoupper($extracted['new_status']), + $extracted['fields'], + $this->getUserInfo() + ); + } + + return sprintf( + "Presentation '%s' (%s) updated (%s changed) by user %s", + $data['title'], + $data['id'], + $extracted['fields'], + $this->getUserInfo() + ); + } + + protected function formatDeletion(array $data): string + { + return sprintf( + "Presentation '%s' (%s) submitted by '%s' to track '%s' was deleted by user %s", + $data['title'], + $data['id'], + $data['creator_name'], + $data['category_name'], + $this->getUserInfo() + ); + } +} diff --git a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php b/app/Audit/ConcreteFormatters/PresentationUserSubmissionAuditLogFormatter.php similarity index 85% rename from app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php rename to app/Audit/ConcreteFormatters/PresentationUserSubmissionAuditLogFormatter.php index 5fca4ab36..011a7395e 100644 --- a/app/Audit/ConcreteFormatters/PresentationSubmissionAuditLogFormatter.php +++ b/app/Audit/ConcreteFormatters/PresentationUserSubmissionAuditLogFormatter.php @@ -2,25 +2,12 @@ namespace App\Audit\ConcreteFormatters; -/** - * Copyright 2025 OpenStack Foundation - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - use App\Audit\AbstractAuditLogFormatter; use App\Audit\Interfaces\IAuditStrategy; use models\summit\Presentation; use Illuminate\Support\Facades\Log; -class PresentationSubmissionAuditLogFormatter extends AbstractAuditLogFormatter +class PresentationUserSubmissionAuditLogFormatter extends AbstractAuditLogFormatter { private string $event_type; @@ -124,7 +111,7 @@ public function format($subject, array $change_set): ?string ); } } catch (\Exception $ex) { - Log::warning("PresentationSubmissionAuditLogFormatter error: " . $ex->getMessage()); + Log::warning("PresentationUserSubmissionAuditLogFormatter error: " . $ex->getMessage()); } return null; diff --git a/config/audit_log.php b/config/audit_log.php index 44bed5cfe..3edae82af 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -12,7 +12,20 @@ ], \models\summit\Presentation::class => [ 'enabled' => true, - 'strategy' => \App\Audit\ConcreteFormatters\PresentationSubmissionAuditLogFormatter::class, + 'strategies' => [ + [ + 'route' => 'api/v1/summits/{id}/events', + 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, + ], + [ + 'route' => 'api/v1/summits/{id}/presentations', + 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, + ], + [ + 'route' => 'api/v1/summits/{id}/presentations/{presentation_id}', + 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, + ], + ] ], \App\Models\Foundation\Summit\SelectionPlan::class => [ 'enabled' => true, From fd3ef3cf3f800e9647d3b48ef75f6e29c4ff8834 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Tue, 9 Dec 2025 11:36:07 -0300 Subject: [PATCH 6/9] chore: change path to actual routes --- app/Audit/AuditContext.php | 1 + app/Audit/AuditEventListener.php | 8 +++++++- app/Audit/AuditLogFormatterFactory.php | 8 ++------ config/audit_log.php | 12 ++++++++---- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/Audit/AuditContext.php b/app/Audit/AuditContext.php index 5150f59dc..8918543c5 100644 --- a/app/Audit/AuditContext.php +++ b/app/Audit/AuditContext.php @@ -21,6 +21,7 @@ public function __construct( public ?string $uiApp = null, public ?string $uiFlow = null, public ?string $route = null, + public ?string $rawRoute = null, public ?string $httpMethod = null, public ?string $clientIp = null, public ?string $userAgent = null, diff --git a/app/Audit/AuditEventListener.php b/app/Audit/AuditEventListener.php index 9bc4d7f32..afce19496 100644 --- a/app/Audit/AuditEventListener.php +++ b/app/Audit/AuditEventListener.php @@ -16,6 +16,7 @@ use Doctrine\ORM\Event\OnFlushEventArgs; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Route; /** * Class AuditEventListener @@ -97,7 +98,11 @@ private function buildAuditContext(): AuditContext //$ui = app()->bound('ui.context') ? app('ui.context') : []; $req = request(); - + + $route = Route::getRoutes()->match(request()); + $method = isset($route->methods[0]) ? $route->methods[0] : null; + $rawRoute = $method."|".$route->uri; + return new AuditContext( userId: $member?->getId(), userEmail: $member?->getEmail(), @@ -109,6 +114,7 @@ private function buildAuditContext(): AuditContext httpMethod: $req?->method(), clientIp: $req?->ip(), userAgent: $req?->userAgent(), + rawRoute: $rawRoute ); } } diff --git a/app/Audit/AuditLogFormatterFactory.php b/app/Audit/AuditLogFormatterFactory.php index de7bf902e..cf1ee2544 100644 --- a/app/Audit/AuditLogFormatterFactory.php +++ b/app/Audit/AuditLogFormatterFactory.php @@ -115,12 +115,8 @@ private function matchesStrategy(array $strategy, AuditContext $ctx): bool return true; } - private function routeMatches(string $pattern, string $actual_route): bool + private function routeMatches(string $route, string $actual_route): bool { - $normalized_pattern = preg_replace('/\{[a-zA-Z_]+\}/', '\d+', $pattern); - $regex = '/^' . preg_quote($normalized_pattern, '/') . '$/'; - $regex = str_replace('\\\d+', '\d+', $regex); - - return preg_match($regex, $actual_route) === 1; + return strcmp($actual_route, $route) === 0; } } diff --git a/config/audit_log.php b/config/audit_log.php index 3edae82af..61c61742f 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -14,16 +14,20 @@ 'enabled' => true, 'strategies' => [ [ - 'route' => 'api/v1/summits/{id}/events', + 'route' => 'POST|api/v1/summits/{id}/events', 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, ], [ - 'route' => 'api/v1/summits/{id}/presentations', + 'route' => 'PUT|api/v1/summits/{id}/events', 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, ], [ - 'route' => 'api/v1/summits/{id}/presentations/{presentation_id}', - 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, + 'route' => 'POST|api/v1/summits/{id}/presentations', + 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationSubmissionAuditLogFormatter::class, + ], + [ + 'route' => 'PUT|api/v1/summits/{id}/presentations/{presentation_id}', + 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationSubmissionAuditLogFormatter::class, ], ] ], From e6043e675eb875f189f6cc26d22e20f007a82f78 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Tue, 9 Dec 2025 15:34:33 -0300 Subject: [PATCH 7/9] fix: change path to actual routes --- app/Audit/AuditEventListener.php | 11 ++++++----- app/Audit/AuditLogFormatterFactory.php | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/Audit/AuditEventListener.php b/app/Audit/AuditEventListener.php index afce19496..230c50b52 100644 --- a/app/Audit/AuditEventListener.php +++ b/app/Audit/AuditEventListener.php @@ -24,10 +24,11 @@ */ class AuditEventListener { + private const ROUTE_METHOD_SEPARATOR = '|'; public function onFlush(OnFlushEventArgs $eventArgs): void { - if (app()->environment('testing')){ + if (app()->environment('testing')) { return; } $em = $eventArgs->getObjectManager(); @@ -68,7 +69,7 @@ public function onFlush(OnFlushEventArgs $eventArgs): void /** * Get the appropriate audit strategy based on environment configuration */ - private function getAuditStrategy($em) + private function getAuditStrategy($em): ?IAuditStrategy { // Check if OTLP audit is enabled if (config('opentelemetry.enabled', false)) { @@ -99,9 +100,9 @@ private function buildAuditContext(): AuditContext $req = request(); - $route = Route::getRoutes()->match(request()); - $method = isset($route->methods[0]) ? $route->methods[0] : null; - $rawRoute = $method."|".$route->uri; + $route = Route::getRoutes()->match($req); + $method = $route->methods[0] ?? 'UNKNOWN'; + $rawRoute = $method . self::ROUTE_METHOD_SEPARATOR . $route->uri; return new AuditContext( userId: $member?->getId(), diff --git a/app/Audit/AuditLogFormatterFactory.php b/app/Audit/AuditLogFormatterFactory.php index cf1ee2544..4536bbfd0 100644 --- a/app/Audit/AuditLogFormatterFactory.php +++ b/app/Audit/AuditLogFormatterFactory.php @@ -108,7 +108,7 @@ private function getFormatterByContext(object $subject, string $event_type, Audi private function matchesStrategy(array $strategy, AuditContext $ctx): bool { - if (isset($strategy['route']) && !$this->routeMatches($strategy['route'], $ctx->route)) { + if (isset($strategy['route']) && !$this->routeMatches($strategy['route'], $ctx->rawRoute)) { return false; } From c2a173b7732f1f578c5769a4dc7b45cc225d3661 Mon Sep 17 00:00:00 2001 From: Matias Perrone Date: Tue, 9 Dec 2025 19:27:31 +0000 Subject: [PATCH 8/9] fix: OpenAPI doc --- app/Swagger/SummitRegistrationSchemas.php | 37 ----------------------- 1 file changed, 37 deletions(-) diff --git a/app/Swagger/SummitRegistrationSchemas.php b/app/Swagger/SummitRegistrationSchemas.php index e9440c4e6..2d2860b2a 100644 --- a/app/Swagger/SummitRegistrationSchemas.php +++ b/app/Swagger/SummitRegistrationSchemas.php @@ -6,43 +6,6 @@ // Badge Types -#[OA\Schema( - schema: "SummitBadgeType", - description: "Summit badge type", - type: "object", - properties: [ - new OA\Property(property: "id", type: "integer", example: 1), - new OA\Property(property: "created", type: "integer", description: "Unix timestamp", example: 1640995200), - new OA\Property(property: "last_edited", type: "integer", description: "Unix timestamp", example: 1640995200), - new OA\Property(property: "name", type: "string", example: "Attendee"), - new OA\Property(property: "description", type: "string", example: "Standard attendee badge"), - new OA\Property(property: "template_content", type: "string", nullable: true, example: "Badge template content"), - new OA\Property(property: "is_default", type: "boolean", example: false), - new OA\Property(property: "summit_id", type: "integer", example: 1), - new OA\Property( - property: "access_levels", - type: "array", - items: new OA\Items(type: "integer"), - description: "SummitAccessLevelType IDs, full objects when expanded" - ), - new OA\Property( - property: "badge_features", - type: "array", - items: new OA\Items(type: "integer"), - description: "SummitBadgeFeatureType IDs, full objects when expanded", - ), - new OA\Property( - property: "allowed_view_types", - type: "array", - items: new OA\Items(type: "integer"), - description: "SummitBadgeViewType IDs, full objects when expanded", - ), - ], -)] -class SummitBadgeType -{ -} - #[OA\Schema( schema: "PaginatedSummitBadgeTypesResponse", description: "Paginated list of summit badge types", From a32105824c0fd184ff4597782fe7936e08893d94 Mon Sep 17 00:00:00 2001 From: Jose Andres Tejerina Date: Tue, 9 Dec 2025 16:35:18 -0300 Subject: [PATCH 9/9] fix: add missing param --- config/audit_log.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/audit_log.php b/config/audit_log.php index 61c61742f..9b576c608 100644 --- a/config/audit_log.php +++ b/config/audit_log.php @@ -18,7 +18,7 @@ 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, ], [ - 'route' => 'PUT|api/v1/summits/{id}/events', + 'route' => 'PUT|api/v1/summits/{id}/events/{event_id}', 'formatter' => \App\Audit\ConcreteFormatters\PresentationFormatters\PresentationEventApiAuditLogFormatter::class, ], [