Skip to content

Add Dynamic Button for PU Activities using shortcode: Proceed to Office Visit or Material Contribution#368

Merged
pokhiii merged 2 commits intodevelopfrom
feat/add-redirection-button-for-pu-flows
Oct 14, 2024
Merged

Add Dynamic Button for PU Activities using shortcode: Proceed to Office Visit or Material Contribution#368
pokhiii merged 2 commits intodevelopfrom
feat/add-redirection-button-for-pu-flows

Conversation

@nishant22029
Copy link
Copy Markdown
Collaborator

@nishant22029 nishant22029 commented Oct 14, 2024

Target Issue: #78

This PR introduces a shortcode, goonj_pu_activity_button, which dynamically displays the appropriate button, either Proceed to Office Visit or Proceed to Material Contribution based on individual activity. If both activities (Office Visit and Material Contribution) have already been completed for the day, no button will be displayed.

Summary by CodeRabbit

  • New Features

    • Introduced new shortcodes for enhanced user interactions related to volunteer activities and collection camps.
    • Added functions to retrieve office IDs and generate activity buttons.
    • Implemented a new styled button for volunteer actions.
  • Bug Fixes

    • Removed outdated shortcodes to streamline user identification processes.
  • Refactor

    • Improved modularity of code by reorganizing functions and shortcodes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 14, 2024

Walkthrough

The changes in this pull request enhance the Goonj CRM theme by introducing new functions and shortcodes, modifying user interaction features, and updating the HTML structure for buttons. New functions in helpers.php facilitate activity management, while shortcodes.php adds several shortcodes for user engagement related to volunteer activities. The functions.php file sees the removal of some shortcodes and a shift in user identification handling. Additionally, primary-button.php is updated to create a styled button for volunteer actions.

Changes

File Path Change Summary
wp-content/themes/goonj-crm/engine/helpers.php Added functions: goonj_get_goonj_office_id, goonj_generate_activity_button.
wp-content/themes/goonj-crm/engine/shortcodes.php Added shortcodes: goonj_check_user_form, goonj_volunteer_message, goonj_contribution_volunteer_signup_button, goonj_pu_activity_button, goonj_collection_landing_page, goonj_collection_camp_past.
wp-content/themes/goonj-crm/functions.php Added require_once statements; removed several shortcodes and associated functions; shifted user identification handling to goonj_handle_user_identification_form.
wp-content/themes/goonj-crm/templates/primary-button.php Introduced new HTML structure for a styled button, including dynamic URL and text generation.

Possibly related PRs

Suggested labels

status : ready for review

Suggested reviewers

  • pokhiii

🎉 In the code's embrace, new functions take flight,
Shortcodes added, making interactions bright.
Buttons styled with care, for volunteers to see,
Goonj CRM shines, as it aims to be!
With each line crafted, a better path we pave,
Cheers to the changes, for the users we save! 🎊


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@nishant22029 nishant22029 changed the title Feat/add redirection button for pu flows Add Dynamic Button for PU Activities using shortcode: Proceed to Office Visit or Material Contribution Oct 14, 2024
@nishant22029 nishant22029 self-assigned this Oct 14, 2024
@nishant22029 nishant22029 requested a review from pokhiii October 14, 2024 11:54
@pokhiii pokhiii merged commit dcfed0a into develop Oct 14, 2024
@pokhiii pokhiii deleted the feat/add-redirection-button-for-pu-flows branch October 14, 2024 11:58
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: 12

🧹 Outside diff range and nitpick comments (4)
wp-content/themes/goonj-crm/engine/shortcodes.php (2)

67-69: Provide feedback when user is already a volunteer

Currently, if the individual is already a volunteer, the function returns without any output, possibly leaving the user confused. To enhance user experience, display a message indicating that the user is already registered as a volunteer.

Add a user-friendly message:

 if ( in_array( 'Volunteer', $contactSubTypes ) ) {
-    return;
+    return __( 'You are already registered as a volunteer. Thank you for your continued support!', 'goonj-crm' );
 }

88-113: Enhance user feedback when activity data is missing

In goonj_pu_activity_button, when essential data like activityId or Goonj Office ID is missing, the function logs the issue and returns silently. To improve the user experience, consider displaying a notice or message to inform the user of the missing data, rather than leaving them without any feedback.

Implement a user-facing message:

 if ( ! $office_id ) {
     \Civi::log()->info( 'Goonj Office ID is null for Activity ID:', array( 'activityId' => $activity_id ) );
-    return;
+    return __( 'Unable to proceed due to missing office information. Please contact support.', 'goonj-crm' );
 }
wp-content/themes/goonj-crm/functions.php (2)

Line range hint 196-280: Refactor goonj_handle_user_identification_form() for single responsibility

The goonj_handle_user_identification_form() function is lengthy and handles multiple responsibilities, including input processing, database queries, and redirects. This violates the Single Responsibility Principle and can make maintenance challenging.

Consider breaking this function into smaller, focused functions:

  • Input Validation: Validate and sanitize all user inputs.
  • Contact Retrieval: Handle database queries to retrieve contact information.
  • Redirect Logic: Separate the redirect logic based on purpose into individual functions.

This refactor will improve readability, maintainability, and testability.


Line range hint 457-498: Simplify goonj_redirect_after_individual_creation() function

The goonj_redirect_after_individual_creation() function contains complex logic with nested conditions and multiple cases. Simplifying it will enhance clarity.

Suggested steps:

  • Extract the switch cases into separate functions handling each creationFlow type.
  • Ensure consistent error handling and logging throughout the function.
  • Simplify nested conditions where possible.

This will make the function easier to understand and maintain.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between deeec1f and b97590d.

📒 Files selected for processing (4)
  • wp-content/themes/goonj-crm/engine/helpers.php (1 hunks)
  • wp-content/themes/goonj-crm/engine/shortcodes.php (1 hunks)
  • wp-content/themes/goonj-crm/functions.php (5 hunks)
  • wp-content/themes/goonj-crm/templates/primary-button.php (1 hunks)
🧰 Additional context used
🔇 Additional comments (4)
wp-content/themes/goonj-crm/engine/shortcodes.php (2)

108-113: Validate the office ID before generating the activity button

When calling goonj_generate_activity_button, ensure that the $office_id is valid to prevent potential issues in the button generation process. An invalid office_id might lead to incorrect URLs or broken buttons.

Confirm that $office_id is valid and not zero or an unexpected value.

 if ( ! $office_id ) {
     \Civi::log()->info( 'Goonj Office ID is null for Activity ID:', array( 'activityId' => $activity_id ) );
     return;
 }

+if ( $office_id <= 0 ) {
+    \Civi::log()->warning( 'Invalid Goonj Office ID:', array( 'officeId' => $office_id ) );
+    return;
+}

87-121: ⚠️ Potential issue

Ensure consistent sanitization of 'activityId' across functions

The activityId parameter is sanitized differently in goonj_contribution_volunteer_signup_button and goonj_pu_activity_button. For consistency and to follow best practices, use absint in both functions to sanitize activityId, ensuring that only positive integers are processed.

Update goonj_contribution_volunteer_signup_button as follows:

-$activity_id = isset( $_GET['activityId'] ) ? intval( $_GET['activityId'] ) : 0;
+$activity_id = isset( $_GET['activityId'] ) ? absint( $_GET['activityId'] ) : 0;

Likely invalid or redundant comment.

wp-content/themes/goonj-crm/functions.php (2)

3-4: Good practice: Modularizing code by including helper and shortcode files

Including helpers.php and shortcodes.php enhances code organization and promotes separation of concerns, making the codebase more maintainable.


212-213: Secure user input by sanitizing $state_id and $city

Adding sanitize_text_field() around $state_id and $city ensures that user-provided data is properly sanitized before being included in URLs, enhancing security.

Comment on lines +1 to +5
<div class="volunteer-button-container">
<a href="<?php echo esc_url( $args['button_url'] ); ?>" class="wp-block-button__link has-white-color has-vivid-red-background-color has-text-color has-background has-link-color wp-element-button volunteer-button-link">
<?php echo esc_html( $args['button_text'] ); ?>
</a>
</div> No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Good job on creating a reusable button component, but let's make it even better!

The component is well-structured and follows security best practices by using esc_url and esc_html. However, there are a few areas where we can improve:

  1. Class naming: The classes on the anchor tag are a mix of WordPress default classes and custom classes. Consider using more semantic class names and following a consistent naming convention (e.g., BEM).

  2. Simplify styling: The long list of classes on the anchor tag could be simplified by creating a custom CSS class that encapsulates all these styles.

  3. Indentation: Improve readability by using consistent indentation throughout the file.

Here's a suggested refactor:

-<div class="volunteer-button-container">
-	<a href="<?php echo esc_url( $args['button_url'] ); ?>" class="wp-block-button__link has-white-color has-vivid-red-background-color has-text-color has-background has-link-color wp-element-button volunteer-button-link">
-		<?php echo esc_html( $args['button_text'] ); ?>
-	</a>
-</div>
+<div class="goonj-button-container">
+    <a href="<?php echo esc_url( $args['button_url'] ); ?>" class="goonj-button goonj-button--primary">
+        <?php echo esc_html( $args['button_text'] ); ?>
+    </a>
+</div>

Then, create a CSS file (if not already present) to define the styles for .goonj-button and .goonj-button--primary.

Would you like me to create a separate CSS file for these button styles?

📝 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
<div class="volunteer-button-container">
<a href="<?php echo esc_url( $args['button_url'] ); ?>" class="wp-block-button__link has-white-color has-vivid-red-background-color has-text-color has-background has-link-color wp-element-button volunteer-button-link">
<?php echo esc_html( $args['button_text'] ); ?>
</a>
</div>
<div class="goonj-button-container">
<a href="<?php echo esc_url( $args['button_url'] ); ?>" class="goonj-button goonj-button--primary">
<?php echo esc_html( $args['button_text'] ); ?>
</a>
</div>

Comment on lines +17 to +55
function goonj_generate_activity_button( $activity, $office_id, $individual_id ) {
// Define all activity types to check pending activities
$activityTypes = array( 'Office visit', 'Material Contribution' );

// Activity type that contact has already completed
$completedActivityType = $activity['activity_type_id:name'];

// Calculate the pending activities by comparing with $activityTypes
$pendingActivities = array_diff( $activityTypes, array( $completedActivityType ) );

// Define the mapping for each activity to the corresponding button and redirect info
$activityMap = array(
'Office visit' => array(
'redirectPath' => '/processing-center/office-visit/details/',
'buttonText' => __( 'Proceed to Office Visit', 'goonj-crm' ),
'queryParam' => 'Office_Visit.Goonj_Processing_Center',
),
'Material Contribution' => array(
'redirectPath' => '/processing-center/material-contribution/details/',
'buttonText' => __( 'Proceed to Material Contribution', 'goonj-crm' ),
'queryParam' => 'Material_Contribution.Goonj_Office',
),
);

// Get the next pending activity
$nextActivity = reset( $pendingActivities );
$details = $activityMap[ $nextActivity ];

// Prepare the URL with query params
$redirectParams = array(
'source_contact_id' => $individual_id,
$details['queryParam'] => $office_id,
);

$redirectPathWithParams = $details['redirectPath'] . '#?' . http_build_query( $redirectParams );

// Generate and return the button HTML
return goonj_generate_button_html( $redirectPathWithParams, $details['buttonText'] );
}
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

Refactor goonj_generate_activity_button for improved maintainability and flexibility

The current implementation of this function has several areas for improvement:

  1. It's quite long and performs multiple tasks, violating the Single Responsibility Principle.
  2. It uses hardcoded arrays for activity types and activity mapping, which could lead to maintenance issues.
  3. It doesn't handle cases where all activities are completed.

Consider the following improvements:

  1. Break down the function into smaller, more focused functions.
  2. Move the activity types and mapping to a configuration file or database.
  3. Add handling for cases where all activities are completed.

Here's a suggested refactor:

function goonj_get_activity_types() {
    // Fetch from config or DB
    return array('Office visit', 'Material Contribution');
}

function goonj_get_activity_map() {
    // Fetch from config or DB
    return array(
        'Office visit' => array(
            'redirectPath' => '/processing-center/office-visit/details/',
            'buttonText' => __('Proceed to Office Visit', 'goonj-crm'),
            'queryParam' => 'Office_Visit.Goonj_Processing_Center',
        ),
        'Material Contribution' => array(
            'redirectPath' => '/processing-center/material-contribution/details/',
            'buttonText' => __('Proceed to Material Contribution', 'goonj-crm'),
            'queryParam' => 'Material_Contribution.Goonj_Office',
        ),
    );
}

function goonj_get_next_pending_activity($completedActivityType) {
    $activityTypes = goonj_get_activity_types();
    $pendingActivities = array_diff($activityTypes, array($completedActivityType));
    return reset($pendingActivities);
}

function goonj_generate_redirect_url($activity, $office_id, $individual_id) {
    $activityMap = goonj_get_activity_map();
    $details = $activityMap[$activity];
    $redirectParams = array(
        'source_contact_id' => $individual_id,
        $details['queryParam'] => $office_id,
    );
    return $details['redirectPath'] . '#?' . http_build_query($redirectParams);
}

function goonj_generate_activity_button($activity, $office_id, $individual_id) {
    $completedActivityType = $activity['activity_type_id:name'];
    $nextActivity = goonj_get_next_pending_activity($completedActivityType);
    
    if (!$nextActivity) {
        return ''; // No pending activities
    }
    
    $activityMap = goonj_get_activity_map();
    $redirectUrl = goonj_generate_redirect_url($nextActivity, $office_id, $individual_id);
    $buttonText = $activityMap[$nextActivity]['buttonText'];
    
    return goonj_generate_button_html($redirectUrl, $buttonText);
}

This refactor improves maintainability, adheres to the Single Responsibility Principle, and handles cases where all activities are completed.

Comment on lines +15 to +27
function goonj_generate_button_html( $button_url, $button_text ) {
ob_start();
get_template_part(
'templates/primary-button',
null,
array(
'button_url' => $button_url,
'button_text' => $button_text,
)
);

return ob_get_clean();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consolidate duplicate code for rendering templates

The functions goonj_generate_button_html (lines 15-27), goonj_collection_camp_landing_page (lines 123-127), and goonj_collection_camp_past_data (lines 129-133) all use similar patterns involving ob_start(), get_template_part(), and ob_get_clean(). To adhere to the DRY (Don't Repeat Yourself) principle, consider creating a generic function to handle rendering template parts with optional arguments. This will reduce code duplication and improve maintainability.

Apply this refactor to introduce a reusable function:

function goonj_render_template_part( $template_name, $args = array() ) {
    ob_start();
    get_template_part( $template_name, null, $args );
    return ob_get_clean();
}

Then, update the existing functions:

-function goonj_generate_button_html( $button_url, $button_text ) {
-    ob_start();
-    get_template_part(
-        'templates/primary-button',
-        null,
-        array(
-            'button_url' => $button_url,
-            'button_text' => $button_text,
-        )
-    );
-    return ob_get_clean();
-}
+function goonj_generate_button_html( $button_url, $button_text ) {
+    return goonj_render_template_part(
+        'templates/primary-button',
+        array(
+            'button_url' => $button_url,
+            'button_text' => $button_text,
+        )
+    );
+}

Similarly, update other functions:

-function goonj_collection_camp_landing_page() {
-    ob_start();
-    get_template_part( 'templates/collection-landing-page' );
-    return ob_get_clean();
-}
+function goonj_collection_camp_landing_page() {
+    return goonj_render_template_part( 'templates/collection-landing-page' );
+}
-function goonj_collection_camp_past_data() {
-    ob_start();
-    get_template_part( 'templates/collection-camp-data' );
-    return ob_get_clean();
-}
+function goonj_collection_camp_past_data() {
+    return goonj_render_template_part( 'templates/collection-camp-data' );
+}

Comment on lines +29 to +85
function goonj_contribution_volunteer_signup_button() {
$activity_id = isset( $_GET['activityId'] ) ? intval( $_GET['activityId'] ) : 0;

if ( empty( $activity_id ) ) {
\Civi::log()->warning( 'Activity ID is missing' );
return;
}

try {
$activities = \Civi\Api4\Activity::get( false )
->addSelect( 'source_contact_id' )
->addJoin( 'ActivityContact AS activity_contact', 'LEFT' )
->addWhere( 'id', '=', $activity_id )
->addWhere( 'activity_type_id:label', '=', 'Material Contribution' )
->execute();

if ( $activities->count() === 0 ) {
\Civi::log()->info( 'No activities found for Activity ID:', array( 'activityId' => $activity_id ) );
return;
}

$activity = $activities->first();
$individual_id = $activity['source_contact_id'];

$contact = \Civi\Api4\Contact::get( false )
->addSelect( 'contact_sub_type' )
->addWhere( 'id', '=', $individual_id )
->execute()
->first();

if ( empty( $contact ) ) {
\Civi::log()->info( 'Contact not found', array( 'contact' => $contact['id'] ) );
return;
}

$contactSubTypes = $contact['contact_sub_type'] ?? array();

// If the individual is already a volunteer, don't show the button
if ( in_array( 'Volunteer', $contactSubTypes ) ) {
return;
}

$redirectPath = '/volunteer-registration/form-with-details/';
$redirectPathWithParams = $redirectPath . '#?' . http_build_query(
array(
'Individual1' => $individual_id,
'message' => 'individual-user',
)
);
$buttonText = __( 'Wish to Volunteer?', 'goonj-crm' );

return goonj_generate_button_html( $redirectPathWithParams, $buttonText );
} catch ( \Exception $e ) {
\Civi::log()->error( 'Error in goonj_contribution_volunteer_signup_button: ' . $e->getMessage() );
return;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor common activity retrieval logic into a helper function

Both goonj_contribution_volunteer_signup_button and goonj_pu_activity_button retrieve activity data using similar code. To adhere to the Single Responsibility Principle and reduce code duplication, consider extracting the shared logic into a dedicated helper function. This will improve code readability and make future maintenance easier.

Create a helper function for activity retrieval:

function goonj_get_activity_by_id( $activity_id, $select_fields = array(), $activity_type = null ) {
    $query = \Civi\Api4\Activity::get( false )
        ->addSelect( $select_fields )
        ->addWhere( 'id', '=', $activity_id );

    if ( $activity_type ) {
        $query->addWhere( 'activity_type_id:label', '=', $activity_type );
    }

    return $query->execute()->first();
}

Then update the functions:

 // In goonj_contribution_volunteer_signup_button
- $activities = \Civi\Api4\Activity::get( false )
-     ->addSelect( 'source_contact_id' )
-     ->addJoin( 'ActivityContact AS activity_contact', 'LEFT' )
-     ->addWhere( 'id', '=', $activity_id )
-     ->addWhere( 'activity_type_id:label', '=', 'Material Contribution' )
-     ->execute();
+ $activity = goonj_get_activity_by_id(
+     $activity_id,
+     array( 'source_contact_id' ),
+     'Material Contribution'
+ );

 // In goonj_pu_activity_button
- $activity = \Civi\Api4\Activity::get(false)
-     ->addSelect('source_contact_id', 'Office_Visit.Goonj_Processing_Center', 'Material_Contribution.Goonj_Office', 'activity_type_id:name')
-     ->addWhere('id', '=', $activity_id)
-     ->execute()
-     ->first();
+ $activity = goonj_get_activity_by_id(
+     $activity_id,
+     array(
+         'source_contact_id',
+         'Office_Visit.Goonj_Processing_Center',
+         'Material_Contribution.Goonj_Office',
+         'activity_type_id:name'
+     )
+ );

Also applies to: 87-121


⚠️ Potential issue

Enhance input validation and sanitization for 'activityId'

In goonj_contribution_volunteer_signup_button (lines 29-85), the activityId parameter from $_GET is sanitized using intval, which can result in zero for invalid values. To ensure robust validation and prevent potential issues, use absint to guarantee a positive integer, and check if $activity_id is greater than zero before proceeding.

Apply this diff to improve input handling:

-$activity_id = isset( $_GET['activityId'] ) ? intval( $_GET['activityId'] ) : 0;
+$activity_id = isset( $_GET['activityId'] ) ? absint( $_GET['activityId'] ) : 0;

-if ( empty( $activity_id ) ) {
+if ( $activity_id === 0 ) {
     \Civi::log()->warning( 'Activity ID is missing' );
     return;
 }
📝 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
function goonj_contribution_volunteer_signup_button() {
$activity_id = isset( $_GET['activityId'] ) ? intval( $_GET['activityId'] ) : 0;
if ( empty( $activity_id ) ) {
\Civi::log()->warning( 'Activity ID is missing' );
return;
}
try {
$activities = \Civi\Api4\Activity::get( false )
->addSelect( 'source_contact_id' )
->addJoin( 'ActivityContact AS activity_contact', 'LEFT' )
->addWhere( 'id', '=', $activity_id )
->addWhere( 'activity_type_id:label', '=', 'Material Contribution' )
->execute();
if ( $activities->count() === 0 ) {
\Civi::log()->info( 'No activities found for Activity ID:', array( 'activityId' => $activity_id ) );
return;
}
$activity = $activities->first();
$individual_id = $activity['source_contact_id'];
$contact = \Civi\Api4\Contact::get( false )
->addSelect( 'contact_sub_type' )
->addWhere( 'id', '=', $individual_id )
->execute()
->first();
if ( empty( $contact ) ) {
\Civi::log()->info( 'Contact not found', array( 'contact' => $contact['id'] ) );
return;
}
$contactSubTypes = $contact['contact_sub_type'] ?? array();
// If the individual is already a volunteer, don't show the button
if ( in_array( 'Volunteer', $contactSubTypes ) ) {
return;
}
$redirectPath = '/volunteer-registration/form-with-details/';
$redirectPathWithParams = $redirectPath . '#?' . http_build_query(
array(
'Individual1' => $individual_id,
'message' => 'individual-user',
)
);
$buttonText = __( 'Wish to Volunteer?', 'goonj-crm' );
return goonj_generate_button_html( $redirectPathWithParams, $buttonText );
} catch ( \Exception $e ) {
\Civi::log()->error( 'Error in goonj_contribution_volunteer_signup_button: ' . $e->getMessage() );
return;
}
}
function goonj_contribution_volunteer_signup_button() {
$activity_id = isset( $_GET['activityId'] ) ? absint( $_GET['activityId'] ) : 0;
if ( $activity_id === 0 ) {
\Civi::log()->warning( 'Activity ID is missing' );
return;
}
try {
$activities = \Civi\Api4\Activity::get( false )
->addSelect( 'source_contact_id' )
->addJoin( 'ActivityContact AS activity_contact', 'LEFT' )
->addWhere( 'id', '=', $activity_id )
->addWhere( 'activity_type_id:label', '=', 'Material Contribution' )
->execute();
if ( $activities->count() === 0 ) {
\Civi::log()->info( 'No activities found for Activity ID:', array( 'activityId' => $activity_id ) );
return;
}
$activity = $activities->first();
$individual_id = $activity['source_contact_id'];
$contact = \Civi\Api4\Contact::get( false )
->addSelect( 'contact_sub_type' )
->addWhere( 'id', '=', $individual_id )
->execute()
->first();
if ( empty( $contact ) ) {
\Civi::log()->info( 'Contact not found', array( 'contact' => $contact['id'] ) );
return;
}
$contactSubTypes = $contact['contact_sub_type'] ?? array();
// If the individual is already a volunteer, don't show the button
if ( in_array( 'Volunteer', $contactSubTypes ) ) {
return;
}
$redirectPath = '/volunteer-registration/form-with-details/';
$redirectPathWithParams = $redirectPath . '#?' . http_build_query(
array(
'Individual1' => $individual_id,
'message' => 'individual-user',
)
);
$buttonText = __( 'Wish to Volunteer?', 'goonj-crm' );
return goonj_generate_button_html( $redirectPathWithParams, $buttonText );
} catch ( \Exception $e ) {
\Civi::log()->error( 'Error in goonj_contribution_volunteer_signup_button: ' . $e->getMessage() );
return;
}
}

Comment on lines +96 to +100
->addSelect('source_contact_id', 'Office_Visit.Goonj_Processing_Center', 'Material_Contribution.Goonj_Office', 'activity_type_id:name')
->addWhere('id', '=', $activity_id)
->execute()
->first();

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

Check for empty activity data before proceeding

After retrieving the activity, there is a possibility that $activity might be empty, which could cause errors in subsequent code. Ensure that you check if $activity exists before accessing its elements to prevent undefined variable issues.

Confirm the activity data:

 $activity = \Civi\Api4\Activity::get(false)
     // ... existing code ...
     ->execute()
     ->first();

-if ( ! $activity ) {
+if ( empty( $activity ) ) {
     \Civi::log()->info( 'No activity found', array( 'activityId' => $activity_id ) );
     return;
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +38 to +44
$activities = \Civi\Api4\Activity::get( false )
->addSelect( 'source_contact_id' )
->addJoin( 'ActivityContact AS activity_contact', 'LEFT' )
->addWhere( 'id', '=', $activity_id )
->addWhere( 'activity_type_id:label', '=', 'Material Contribution' )
->execute();

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

Add exception handling during activity retrieval

In goonj_contribution_volunteer_signup_button, the retrieval of activities does not have its own exception handling, which could lead to unhandled exceptions if the API call fails. To improve robustness, wrap the activity retrieval logic in a try-catch block to handle potential exceptions gracefully.

Apply this change:

 try {
+    $activities = \Civi\Api4\Activity::get( false )
+        ->addSelect( 'source_contact_id' )
+        ->addJoin( 'ActivityContact AS activity_contact', 'LEFT' )
+        ->addWhere( 'id', '=', $activity_id )
+        ->addWhere( 'activity_type_id:label', '=', 'Material Contribution' )
+        ->execute();
 } catch ( \Exception $e ) {
     \Civi::log()->error( 'Error retrieving activities: ' . $e->getMessage() );
     return;
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +60 to +62
\Civi::log()->info( 'Contact not found', array( 'contact' => $contact['id'] ) );
return;
}
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

Correct logging when contact is not found

At line 60, the log attempts to access $contact['id'] even though $contact is empty, which could cause a PHP notice or error. Update the log message to prevent accessing undefined array keys.

Modify the log statement:

-\Civi::log()->info( 'Contact not found', array( 'contact' => $contact['id'] ) );
+\Civi::log()->info( 'Contact not found', array( 'contactId' => $individual_id ) );
📝 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
\Civi::log()->info( 'Contact not found', array( 'contact' => $contact['id'] ) );
return;
}
\Civi::log()->info( 'Contact not found', array( 'contactId' => $individual_id ) );
return;
}

Comment on lines +196 to +199
if ( in_array( $purpose, array( 'processing-center-material-contribution', 'processing-center-office-visit' ) ) ) {
$organizationName = \Civi\Api4\Organization::get( false )
->addSelect( 'display_name' )
->addWhere( 'id', '=', $target_id )
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

Handle potential null value for $organizationName

If the organization is not found, $organizationName may be null, leading to an Undefined index: display_name notice when accessing $organizationName['display_name'].

Consider adding a check to ensure $organizationName is not null before accessing 'display_name'. Here's a suggested fix:

if ( ! empty( $organizationName['display_name'] ) ) {
    // Use $organizationName['display_name'] as intended
} else {
    // Handle the case where the organization was not found
}

Comment on lines +350 to +352
->addWhere( 'option_group_id:name', '=', 'eck_sub_types' )
->addWhere( 'name', '=', 'Collection_Camp' )
->addWhere( 'grouping', '=', 'Collection_Camp' )
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

Check for null value when fetching $optionValues

If no matching option value is found, $optionValues may be null, which could cause an Undefined index: value notice when accessing $optionValues['value'].

Add a check to ensure $optionValues is not null before using it:

if ( ! empty( $optionValues['value'] ) ) {
    $collectionCampSubtype = $optionValues['value'];
} else {
    // Handle the case where the option value was not found
}

case 'material-contribution':
if ( ! $source ) {
\Civi::log()->warning('Source is missing for material contribution flow', ['individualId' => $_GET['individualId']]);
\Civi::log()->warning( 'Source is missing for material contribution flow', array( 'individualId' => $_GET['individualId'] ) );
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

Sanitize $_GET['individualId'] before logging

Directly using $_GET['individualId'] without sanitization can be risky. Sanitize the input before logging to prevent potential issues.

Apply this fix:

-\Civi::log()->warning( 'Source is missing for material contribution flow', array( 'individualId' => $_GET['individualId'] ) );
+\Civi::log()->warning( 'Source is missing for material contribution flow', array( 'individualId' => absint( $_GET['individualId'] ) ) );
📝 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
\Civi::log()->warning( 'Source is missing for material contribution flow', array( 'individualId' => $_GET['individualId'] ) );
\Civi::log()->warning( 'Source is missing for material contribution flow', array( 'individualId' => absint( $_GET['individualId'] ) ) );

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.

2 participants