Skip to content

Meeting workflow#1633

Merged
tarunnjoshi merged 3 commits intodevfrom
meeting-workflow
Apr 17, 2026
Merged

Meeting workflow#1633
tarunnjoshi merged 3 commits intodevfrom
meeting-workflow

Conversation

@tarunnjoshi
Copy link
Copy Markdown
Member

@tarunnjoshi tarunnjoshi commented Apr 6, 2026

Meeting workflow

Summary by CodeRabbit

  • New Features
    • Urban External Meeting Session form submissions now automatically create meeting activity records for selected participants.
    • Created activities are marked as Completed and carry the meeting subject and related institution details from the form.
    • Contact records receive the new activity entries so participant timelines reflect the session.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dc65668a-dfc1-4db2-81f8-39763fefd13b

📥 Commits

Reviewing files that changed from the base of the PR and between 94175c7 and de4f142.

📒 Files selected for processing (1)
  • wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php

Walkthrough

Adds a new Civi\UrbanExternalMeetingService event subscriber that listens for afform submissions named afformUrbanExternalMeetingSession, filters by entity type, extracts contact IDs from submission records, and creates CiviCRM Activity records for each contact via API4 with error logging.

Changes

Cohort / File(s) Summary
Urban External Meeting Service
wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php
New class implementing an event subscriber. Adds constants for form/type/subject/status, subscribes to civi.afform.submit, validates form name and entity (Eck_Meetings_Sessions), iterates records to read Urban_Meetings.Select_Individual / Urban_Meetings.Institution, and creates activities via Civi\Api4\Activity::create. Catches \CiviCRM_API4_Exception and logs errors. Potential attention areas: hardcoded field/entity names, static/private method testability, and in-loop exception handling.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Afform as Afform Submit Event
    participant Service as UrbanExternalMeetingService
    participant API as CiviCRM API4
    participant DB as Database

    User->>Afform: Submit form (afformUrbanExternalMeetingSession)
    Afform->>Service: civi.afform.submit
    Service->>Service: Check form name & entity type
    Service->>Service: Extract contact IDs from records
    loop for each contact ID
        Service->>API: Create Activity (API4::create with fields)
        API->>DB: Persist activity
        API-->>Service: Success / throws \CiviCRM_API4_Exception
        Service->>Service: Log error on exception
    end
    Service-->>Afform: Processing complete
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

📄 A form is sent, a trigger wakes,

Contacts named and action takes,
Activities sprout, one by one,
Errors logged when tasks are done,
Urban meetings now begun ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Meeting workflow' is vague and generic. It does not clearly describe the specific change being made. While it relates to the PR's domain, it lacks specificity about what aspect of the meeting workflow is being implemented (e.g., event subscription, activity creation, form handling). Revise the title to be more specific and descriptive, such as 'Add event subscriber to create meeting activities from afform submissions' to clearly communicate the primary change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch meeting-workflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php`:
- Around line 80-93: The createActivityForContact function currently only
catches \CiviCRM_API4_Exception so unexpected runtime errors can abort the
submission loop; update the exception handling around Activity::create(...) in
createActivityForContact to also catch generic failures (preferably \Throwable
or at least \Exception) and log the full error (message and optionally trace)
using \Civi::log()->error, while preserving the existing CiviCRM_API4_Exception
handling semantics so the method swallows errors and allows processing to
continue for remaining records.
- Around line 38-64: Remove the raw PHP error_log calls in
UrbanExternalMeetingService.php that print full $fields, $individualOrPocId, and
$institutionId within the event handling loop; instead use the application's
logger (or a debug-only logger) to emit non-PII, sanitized messages (e.g., log
only a boolean/presence flag, record ID, or masked identifiers) and guard those
logs behind a configurable debug flag. Locate the logging in the block that
checks self::FORM_NAME and entityType (Eck_Meetings_Sessions) and inside the
foreach over $event->records where $fields, $individualOrPocId, and
$institutionId are read, remove or replace the error_log calls with
sanitized/conditional logging so PII is not written to PHP error logs.
- Around line 57-73: The record parsing assumes $record['fields'] exists and may
call createActivityForContact twice for the same ID; update the loop over
$event->records in UrbanExternalMeetingService to first verify that
$record['fields'] is set and is an array (skip or log and continue otherwise),
safely extract and normalize the two candidate IDs
(Urban_Meetings.Select_Individual and Urban_Meetings.Institution) into scalars
(cast/trim), build a deduplicated list or set of non-empty IDs, and then iterate
that deduplicated list calling self::createActivityForContact only once per
unique ID; reference the variables $record['fields'], $individualOrPocId,
$institutionId and the method createActivityForContact when making the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 986d3f42-71fc-45fc-8218-28ae1a0047dc

📥 Commits

Reviewing files that changed from the base of the PR and between 829f31e and 94175c7.

📒 Files selected for processing (1)
  • wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php

Comment thread wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php Outdated
Comment on lines +57 to +73
foreach ($event->records as $record) {
$fields = $record['fields'];
error_log('Record fields: ' . print_r($fields, TRUE));

$individualOrPocId = $fields['Urban_Meetings.Select_Individual'] ?? NULL;
error_log('individualOrPocId: ' . print_r($individualOrPocId, TRUE));
$institutionId = $fields['Urban_Meetings.Institution'] ?? NULL;
error_log('institutionId: ' . print_r($institutionId, TRUE));


if ($individualOrPocId) {
self::createActivityForContact($individualOrPocId);
}

if ($institutionId) {
self::createActivityForContact($institutionId);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden record parsing and deduplicate contact IDs before activity creation.

Line 58 assumes fields always exists, and Lines 67-73 can create duplicate activities if both fields resolve to the same contact ID.

Proposed fix
     foreach ($event->records as $record) {
-      $fields = $record['fields'];
-
-      $individualOrPocId = $fields['Urban_Meetings.Select_Individual'] ?? NULL;
-      $institutionId = $fields['Urban_Meetings.Institution'] ?? NULL;
-
-
-      if ($individualOrPocId) {
-        self::createActivityForContact($individualOrPocId);
-      }
-
-      if ($institutionId) {
-        self::createActivityForContact($institutionId);
-      }
+      $fields = $record['fields'] ?? [];
+      if (!$fields) {
+        continue;
+      }
+
+      $contactIds = array_filter([
+        $fields['Urban_Meetings.Select_Individual'] ?? NULL,
+        $fields['Urban_Meetings.Institution'] ?? NULL,
+      ], static fn($id) => is_scalar($id) && (int) $id > 0);
+
+      foreach (array_unique(array_map('intval', $contactIds)) as $contactId) {
+        self::createActivityForContact($contactId);
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php`
around lines 57 - 73, The record parsing assumes $record['fields'] exists and
may call createActivityForContact twice for the same ID; update the loop over
$event->records in UrbanExternalMeetingService to first verify that
$record['fields'] is set and is an array (skip or log and continue otherwise),
safely extract and normalize the two candidate IDs
(Urban_Meetings.Select_Individual and Urban_Meetings.Institution) into scalars
(cast/trim), build a deduplicated list or set of non-empty IDs, and then iterate
that deduplicated list calling self::createActivityForContact only once per
unique ID; reference the variables $record['fields'], $individualOrPocId,
$institutionId and the method createActivityForContact when making the changes.

Comment on lines +80 to +93
private static function createActivityForContact(int $contactId) {
try {
Activity::create(FALSE)
->addValue('subject', self::ACTIVITY_SUBJECT)
->addValue('activity_type_id:name', self::ACTIVITY_TYPE_NAME)
->addValue('status_id:name', self::ACTIVITY_STATUS)
->addValue('activity_date_time', date('Y-m-d H:i:s'))
->addValue('source_contact_id', $contactId)
->addValue('target_contact_id', $contactId)
->execute();
}
catch (\CiviCRM_API4_Exception $ex) {
\Civi::log()->error('Exception while creating Urban External Meeting Session activity for contact ' . $contactId . ': ' . $ex->getMessage());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Broaden exception handling to avoid aborting the submission loop on unexpected failures.

Only \CiviCRM_API4_Exception is caught. Runtime issues (type/conversion/data shape) will currently bubble up and stop processing remaining records.

Proposed fix
-    catch (\CiviCRM_API4_Exception $ex) {
+    catch (\Throwable $ex) {
       \Civi::log()->error('Exception while creating Urban External Meeting Session activity for contact ' . $contactId . ': ' . $ex->getMessage());
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static function createActivityForContact(int $contactId) {
try {
Activity::create(FALSE)
->addValue('subject', self::ACTIVITY_SUBJECT)
->addValue('activity_type_id:name', self::ACTIVITY_TYPE_NAME)
->addValue('status_id:name', self::ACTIVITY_STATUS)
->addValue('activity_date_time', date('Y-m-d H:i:s'))
->addValue('source_contact_id', $contactId)
->addValue('target_contact_id', $contactId)
->execute();
}
catch (\CiviCRM_API4_Exception $ex) {
\Civi::log()->error('Exception while creating Urban External Meeting Session activity for contact ' . $contactId . ': ' . $ex->getMessage());
}
private static function createActivityForContact(int $contactId) {
try {
Activity::create(FALSE)
->addValue('subject', self::ACTIVITY_SUBJECT)
->addValue('activity_type_id:name', self::ACTIVITY_TYPE_NAME)
->addValue('status_id:name', self::ACTIVITY_STATUS)
->addValue('activity_date_time', date('Y-m-d H:i:s'))
->addValue('source_contact_id', $contactId)
->addValue('target_contact_id', $contactId)
->execute();
}
catch (\Throwable $ex) {
\Civi::log()->error('Exception while creating Urban External Meeting Session activity for contact ' . $contactId . ': ' . $ex->getMessage());
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wp-content/civi-extensions/goonjcustom/Civi/UrbanExternalMeetingService.php`
around lines 80 - 93, The createActivityForContact function currently only
catches \CiviCRM_API4_Exception so unexpected runtime errors can abort the
submission loop; update the exception handling around Activity::create(...) in
createActivityForContact to also catch generic failures (preferably \Throwable
or at least \Exception) and log the full error (message and optionally trace)
using \Civi::log()->error, while preserving the existing CiviCRM_API4_Exception
handling semantics so the method swallows errors and allows processing to
continue for remaining records.

@tarunnjoshi tarunnjoshi merged commit 53a85da into dev Apr 17, 2026
1 check was pending
@tarunnjoshi tarunnjoshi deleted the meeting-workflow branch April 17, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant