-
Notifications
You must be signed in to change notification settings - Fork 1
Encryption + Decryption #1357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Encryption + Decryption #1357
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,80 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Civi\Core\Event\GenericHookEvent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use Civi\Core\Service\AutoService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Provides ability to prefill afform fields from encrypted token. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @service goonjcustom.afform_prefill | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class CRM_Goonjcustom_AfformFieldPrefillService extends AutoService { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static function preprocess(\Civi\Angular\Manager $angular) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->debug('[AfformPrefill] Preprocess started.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $request = \CRM_Utils_Request::retrieve('token', 'String', CRM_Core_DAO::$_nullObject, FALSE, NULL, 'GET'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!$request) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->warning('[AfformPrefill] No token found in URL.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $decrypted = CRM_Utils_Crypt::decrypt($request); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $data = json_decode($decrypted, true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->debug('[AfformPrefill] Token decrypted: ' . print_r($data, true)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (\Exception $e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->error('[AfformPrefill] Token decryption failed: ' . $e->getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!is_array($data)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->error('[AfformPrefill] Decrypted token is not valid JSON.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $changeSet = \Civi\Angular\ChangeSet::create('injectPrefillValues') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ->alterHtml(';\\.aff\\.html$;', function ($doc, $path) use ($data) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->debug("[AfformPrefill] Altering HTML template: {$path}"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach (pq('af-field', $doc) as $afField) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $fieldName = $afField->getAttribute('name'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sanitize field names to prevent potential attacks Field names are used directly without validation, which could lead to security issues if malicious field names are processed. $fieldName = $afField->getAttribute('name');
if (!$fieldName) {
continue;
}
+// Validate field name format
+if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_\.]*$/', $fieldName)) {
+ Civi::log()->warning("[AfformPrefill] Invalid field name format: {$fieldName}");
+ continue;
+}
if (array_key_exists($fieldName, $data)) {Also applies to: 48-48 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!$fieldName) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (array_key_exists($fieldName, $data)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $fieldDefn = self::getFieldDefinition($afField); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!$fieldDefn) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->warning("[AfformPrefill] No field definition for: {$fieldName}"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $fieldDefn['afform_default'] = $data[$fieldName]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $fieldDefn['readonly'] = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pq($afField)->attr('defn', htmlspecialchars(\CRM_Utils_JS::writeObject($fieldDefn), ENT_COMPAT)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential XSS vulnerability in field definition handling The field definition is serialized and inserted into HTML attributes without proper validation of the input data values, which could lead to XSS attacks. +// Sanitize the field value before setting
+$sanitizedValue = is_string($data[$fieldName]) ?
+ htmlspecialchars($data[$fieldName], ENT_QUOTES, 'UTF-8') :
+ $data[$fieldName];
+
+$fieldDefn['afform_default'] = $sanitizedValue;
-$fieldDefn['afform_default'] = $data[$fieldName];
$fieldDefn['readonly'] = true;
pq($afField)->attr('defn', htmlspecialchars(\CRM_Utils_JS::writeObject($fieldDefn), ENT_COMPAT));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->info("[AfformPrefill] Prefilled field '{$fieldName}' with value: " . json_encode($data[$fieldName])); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->debug("[AfformPrefill] Field '{$fieldName}' not in token data, skipping."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $angular->add($changeSet); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Civi::log()->debug('[AfformPrefill] ChangeSet injected successfully.'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Break down the monolithic preprocess method This method violates the Single Responsibility Principle by handling token processing, template modification, and field updates all in one place. Consider extracting smaller, focused methods. public function preprocess(\Civi\Angular\Manager $angular) {
- // All the current logic here
+ $data = $this->processToken();
+ if (!$data) {
+ return;
+ }
+
+ $changeSet = $this->createFieldPrefillChangeSet($data);
+ $angular->add($changeSet);
}
+private function processToken() {
+ // Token retrieval and decryption logic
+}
+private function createFieldPrefillChangeSet($data) {
+ // ChangeSet creation and field processing logic
+}
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static function getFieldDefinition($afField) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $existingFieldDefn = trim(pq($afField)->attr('defn') ?: ''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ($existingFieldDefn && $existingFieldDefn[0] !== '{') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NULL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return $existingFieldDefn ? \CRM_Utils_JS::getRawProps($existingFieldDefn) : []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for field definition parsing The - return $existingFieldDefn ? \CRM_Utils_JS::getRawProps($existingFieldDefn) : [];
+ if (!$existingFieldDefn) {
+ return [];
+ }
+
+ try {
+ return \CRM_Utils_JS::getRawProps($existingFieldDefn);
+ } catch (\Exception $e) {
+ Civi::log()->error('[AfformPrefill] Failed to parse field definition: ' . $e->getMessage());
+ return [];
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,36 +95,47 @@ public function setDefaultValues() { | |
| * | ||
| * @return void | ||
| */ | ||
| public function generateLinks($contactId): void { | ||
| $organization = $this->_organization; | ||
| $contact = $this->_contact; | ||
|
|
||
| $nameOfInstitution = $organization['display_name']; | ||
| $address = $organization['address_primary.street_address']; | ||
| $pocEmail = $contact['email.email']; | ||
| $pocContactNumber = $contact['phone.phone']; | ||
|
|
||
| // Generate dropping center links. | ||
| $links = [ | ||
| [ | ||
| 'label' => 'Vehicle Dispatch', | ||
| 'url' => self::createUrl( | ||
| '/institution-dropping-center-vehicle-dispatch', | ||
| "Camp_Vehicle_Dispatch.Institution_Dropping_Center={$this->_institutionDroppingCenterId}" . | ||
| "&Eck_Collection_Camp1={$this->_institutionDroppingCenterId}" . | ||
| "&Camp_Vehicle_Dispatch.To_which_PU_Center_material_is_being_sent={$this->_processingCenterId}" . | ||
| "&Camp_Vehicle_Dispatch.Filled_by={$contactId}" . | ||
| "&Camp_Institution_Data.Name_of_the_institution={$nameOfInstitution}" . | ||
| "&Camp_Institution_Data.Address=" . urlencode($address) . | ||
| "&Camp_Institution_Data.Email={$pocEmail}" . | ||
| "&Camp_Institution_Data.Contact_Number={$pocContactNumber}", | ||
| $contactId | ||
| ), | ||
| ], | ||
| ]; | ||
|
|
||
| $this->assign('institutionDroppingCenterLinks', $links); | ||
| } | ||
| public function generateLinks($contactId): void { | ||
| $organization = $this->_organization; | ||
| $contact = $this->_contact; | ||
|
|
||
| $nameOfInstitution = $organization['display_name']; | ||
| $address = $organization['address_primary.street_address']; | ||
| $pocEmail = $contact['email.email']; | ||
| $pocContactNumber = $contact['phone.phone']; | ||
|
|
||
| // Create dispatch data array | ||
| $dispatchData = [ | ||
| 'Camp_Vehicle_Dispatch.Institution_Dropping_Center' => $this->_institutionDroppingCenterId, | ||
| 'Camp_Vehicle_Dispatch.Filled_by' => $contactId, | ||
| 'Camp_Vehicle_Dispatch.To_which_PU_Center_material_is_being_sent' => $this->_processingCenterId, | ||
| 'Camp_Vehicle_Dispatch.Goonj_Office_Name' => '', // optional if needed | ||
| 'Eck_Collection_Camp1' => $this->_institutionDroppingCenterId, | ||
| 'id' => $this->_institutionDroppingCenterId, | ||
| 'Camp_Institution_Data.Name_of_the_institution' => $nameOfInstitution, | ||
| 'Camp_Institution_Data.Address' => $address, | ||
| 'Camp_Institution_Data.Email' => $pocEmail, | ||
| 'Camp_Institution_Data.Contact_Number' => $pocContactNumber, | ||
| ]; | ||
|
|
||
| // Encrypt token | ||
| $json = json_encode($dispatchData); | ||
| $encryptedToken = \CRM_Utils_Crypt::encrypt($json); | ||
| $tokenParam = 'token=' . urlencode($encryptedToken); | ||
|
|
||
| // Authenticated token | ||
| $authUrl = self::createUrl('/institution-dropping-center-vehicle-dispatch', $tokenParam, $contactId); | ||
|
|
||
| // Assign link | ||
| $links = [ | ||
| [ | ||
| 'label' => 'Vehicle Dispatch', | ||
| 'url' => $authUrl, | ||
| ], | ||
| ]; | ||
|
|
||
| $this->assign('institutionDroppingCenterLinks', $links); | ||
| } | ||
|
|
||
| /** | ||
| * Generate an authenticated URL for viewing this form. | ||
|
|
@@ -153,7 +164,7 @@ public static function createUrl($path, $params, $contactId): string { | |
| $params = "{$params}&_authx={$bearerToken}&_authxSes=1"; | ||
| // $url = \CRM_Utils_System::url($path, $params, TRUE, NULL, FALSE, TRUE); | ||
| $config = CRM_Core_Config::singleton(); | ||
| $url = $config->userFrameworkBaseURL . $path . '#?' . $params; | ||
| $url = $config->userFrameworkBaseURL . $path . '?' . $params; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify URL construction change doesn't break CiviCRM Formbuilder The URL construction was changed from hash fragment ( Please verify that this change doesn't break the form functionality. If the forms require the hash fragment format, consider reverting this change. 🏁 Script executed: #!/bin/bash
# Search for other instances of URL construction for CiviCRM forms
rg -A 3 -B 3 "userFrameworkBaseURL.*\?" --type php
# Check for any documentation or comments about URL format requirements
rg -i "formbuilder.*url|url.*formbuilder|#\?" --type phpLength of output: 34790 Revert to using hash-fragment ( The rest of our custom Form link generators consistently use Please update: • File: • Change back from: $url = $config->userFrameworkBaseURL . $path . '?' . $params;to: $url = $config->userFrameworkBaseURL . $path . '#?' . $params;This will align it with all other 🤖 Prompt for AI Agents |
||
|
|
||
| return $url; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -543,32 +543,52 @@ public static function processDispatchEmail(string $op, string $objectName, $obj | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static function sendDispatchEmail($email, $initiatorName, $droppingCenterId, $contactId, $goonjOffice, $goonjOfficeName) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $homeUrl = \CRM_Utils_System::baseCMSURL(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $vehicleDispatchFormUrl = $homeUrl . '/vehicle-dispatch/#?Camp_Vehicle_Dispatch.Dropping_Center=' . $droppingCenterId . '&Camp_Vehicle_Dispatch.Filled_by=' . $contactId . '&Camp_Vehicle_Dispatch.To_which_PU_Center_material_is_being_sent=' . $goonjOffice . '&Camp_Vehicle_Dispatch.Goonj_Office_Name=' . $goonjOfficeName . '&Eck_Collection_Camp1=' . $droppingCenterId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $homeUrl = \CRM_Utils_System::baseCMSURL(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Step 1: Gather data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $dispatchData = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Camp_Vehicle_Dispatch.Dropping_Center' => $droppingCenterId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Camp_Vehicle_Dispatch.Filled_by' => $contactId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Camp_Vehicle_Dispatch.To_which_PU_Center_material_is_being_sent' => $goonjOffice, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Camp_Vehicle_Dispatch.Goonj_Office_Name' => $goonjOfficeName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Eck_Collection_Camp1' => $droppingCenterId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'id' => $droppingCenterId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Dropping_Centre.Where_do_you_wish_to_open_dropping_center_Address_' => 'Hardcoded test address for now' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Step 2: Convert to JSON and encrypt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $json = json_encode($dispatchData); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $token = \CRM_Utils_Crypt::encrypt($json); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Step 3: Generate encrypted URL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $vehicleDispatchFormUrl = $homeUrl . '/vehicle-dispatch/?token=' . urlencode($token); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $emailHtml = " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <html> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Step 4: Compose HTML email | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $emailHtml = " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <html> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>Dear {$initiatorName},</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>Thank you so much for your invaluable efforts in running the Goonj Dropping Center. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Your dedication plays a crucial role in our work, and we deeply appreciate your continued support.</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>Please fill out this Dispatch Form – <a href='{$vehicleDispatchFormUrl}'>Link</a> once the vehicle is loaded and ready to head to Goonj’s processing center. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| This will help us to verify and acknowledge the materials as soon as they arrive.</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>We truly appreciate your cooperation and continued commitment to our cause.</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>Warm Regards,<br>Team Goonj..</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </html> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $from = HelperService::getDefaultFromEmail(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $mailParams = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'subject' => 'Kindly fill the Dispatch Form for Material Pickup', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'from' => $from, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'toEmail' => $email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'html' => $emailHtml, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </html> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+567
to
+579
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Extract email HTML to a template file Inline HTML violates the separation of concerns principle and makes the code harder to maintain. Consider moving this to a Smarty template for better maintainability and localization support. Create a new template file - // Step 4: Compose HTML email
- $emailHtml = "
- <html>
- <body>
- <p>Dear {$initiatorName},</p>
- <p>Thank you so much for your invaluable efforts in running the Goonj Dropping Center.
- Your dedication plays a crucial role in our work, and we deeply appreciate your continued support.</p>
- <p>Please fill out this Dispatch Form – <a href='{$vehicleDispatchFormUrl}'>Link</a> once the vehicle is loaded and ready to head to Goonj's processing center.
- This will help us to verify and acknowledge the materials as soon as they arrive.</p>
- <p>We truly appreciate your cooperation and continued commitment to our cause.</p>
- <p>Warm Regards,<br>Team Goonj..</p>
- </body>
- </html>
- ";
+ // Step 4: Compose HTML email
+ $tplParams = [
+ 'initiatorName' => $initiatorName,
+ 'vehicleDispatchFormUrl' => $vehicleDispatchFormUrl,
+ ];
+ $emailHtml = CRM_Core_Smarty::singleton()->fetchWith('CRM/Goonjcustom/Email/DispatchForm.tpl', $tplParams);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Step 5: Send email | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $from = HelperService::getDefaultFromEmail(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $mailParams = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'subject' => 'Kindly fill the Dispatch Form for Material Pickup', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'from' => $from, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'toEmail' => $email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'html' => $emailHtml, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| \CRM_Utils_Mail::send($mailParams); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| \CRM_Utils_Mail::send($mailParams); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -659,13 +679,13 @@ public static function droppingCenterTabset($tabsetName, &$tabs, $context) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'template' => 'CRM/Goonjcustom/Tabs/DroppingCenter/DonationBox.tpl', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'permissions' => ['goonj_chapter_admin', 'urbanops', 'urban_ops_admin', 'urban_ops_and_accounts_chapter_team'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'outcome' => [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'title' => ts('Outcome'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'module' => 'afsearchDroppingCenterOutcome', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'directive' => 'afsearch-dropping-center-outcome', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'template' => 'CRM/Goonjcustom/Tabs/DroppingCenter/Outcome.tpl', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'permissions' => ['goonj_chapter_admin', 'urbanops', 'urban_ops_admin', 'urban_ops_and_accounts_chapter_team'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 'outcome' => [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 'title' => ts('Outcome'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 'module' => 'afsearchDroppingCenterOutcome', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 'directive' => 'afsearch-dropping-center-outcome', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 'template' => 'CRM/Goonjcustom/Tabs/DroppingCenter/Outcome.tpl', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 'permissions' => ['goonj_chapter_admin', 'urbanops', 'urban_ops_admin', 'urban_ops_and_accounts_chapter_team'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'feedback' => [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'title' => ts('Feedback'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'module' => 'afsearchFeedback', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -670,44 +670,56 @@ public static function processDispatchEmail(string $op, string $objectName, $obj | |
| /** | ||
| * | ||
| */ | ||
| public static function sendDispatchEmail($nameOfInstitution, $address, $phone, $email, $initiatorName, $droppingCenterId, $contactId, $goonjOffice, $goonjOfficeName) { | ||
| $homeUrl = \CRM_Utils_System::baseCMSURL(); | ||
| public static function sendDispatchEmail($nameOfInstitution, $address, $phone, $email, $initiatorName, $droppingCenterId, $contactId, $goonjOffice, $goonjOfficeName) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Refactor method to reduce parameter count The method has 9 parameters, which makes it difficult to maintain and call correctly. Consider using a data transfer object (DTO) or array to encapsulate the dispatch email data: -public static function sendDispatchEmail($nameOfInstitution, $address, $phone, $email, $initiatorName, $droppingCenterId, $contactId, $goonjOffice, $goonjOfficeName) {
+public static function sendDispatchEmail(array $dispatchEmailData) {
+ // Extract values from $dispatchEmailData array
🤖 Prompt for AI Agents |
||
| $homeUrl = \CRM_Utils_System::baseCMSURL(); | ||
|
|
||
| // Step 1: Gather data | ||
| $dispatchData = [ | ||
| 'Camp_Vehicle_Dispatch.Institution_Dropping_Center' => $droppingCenterId, | ||
| 'Camp_Vehicle_Dispatch.Filled_by' => $contactId, | ||
| 'Camp_Vehicle_Dispatch.To_which_PU_Center_material_is_being_sent' => $goonjOffice, | ||
| 'Camp_Vehicle_Dispatch.Goonj_Office_Name' => $goonjOfficeName, | ||
| 'Eck_Collection_Camp1' => $droppingCenterId, | ||
| 'id' => $droppingCenterId, | ||
| 'Camp_Institution_Data.Name_of_the_institution' => $nameOfInstitution, | ||
| 'Camp_Institution_Data.Address' => $address, | ||
| 'Camp_Institution_Data.Email' => $email, | ||
| 'Camp_Institution_Data.Contact_Number' => $phone, | ||
| ]; | ||
|
|
||
| // Step 2: Convert to JSON and encrypt | ||
| $json = json_encode($dispatchData); | ||
| $token = \CRM_Utils_Crypt::encrypt($json); | ||
|
|
||
|
Comment on lines
+690
to
693
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add token expiration for enhanced security The encrypted token doesn't include an expiration timestamp, which could allow indefinite reuse. Consider adding an expiration timestamp to the token data: // Step 2: Convert to JSON and encrypt
+ $dispatchData['exp'] = time() + (24 * 60 * 60); // 24 hour expiration
$json = json_encode($dispatchData);
$token = \CRM_Utils_Crypt::encrypt($json);🤖 Prompt for AI Agents |
||
| $baseUrl = '/institution-dropping-center-vehicle-dispatch/'; | ||
|
|
||
| $vehicleDispatchFormUrl = $homeUrl . $baseUrl . '#?Camp_Vehicle_Dispatch.Institution_Dropping_Center=' . $droppingCenterId | ||
| . '&Camp_Vehicle_Dispatch.Filled_by=' . $contactId | ||
| . '&Camp_Vehicle_Dispatch.To_which_PU_Center_material_is_being_sent=' . $goonjOffice | ||
| . '&Camp_Vehicle_Dispatch.Goonj_Office_Name=' . $goonjOfficeName | ||
| . '&Eck_Collection_Camp1=' . $droppingCenterId | ||
| . '&Camp_Institution_Data.Name_of_the_institution=' . $nameOfInstitution | ||
| . "&Camp_Institution_Data.Address=" . urlencode($address) | ||
| . "&Camp_Institution_Data.Email=" . $email | ||
| . "&Camp_Institution_Data.Contact_Number=" . $phone; | ||
|
|
||
| $emailHtml = " | ||
| <html> | ||
| <body> | ||
| // Step 3: Generate encrypted URL | ||
| $vehicleDispatchFormUrl = $homeUrl . '/institution-dropping-center-vehicle-dispatch/?token=' . urlencode($token); | ||
|
|
||
| // Step 4: Compose HTML email | ||
| $emailHtml = " | ||
| <html> | ||
| <body> | ||
| <p>Dear {$initiatorName},</p> | ||
| <p>Thank you so much for your invaluable efforts in running the Goonj Dropping Center. | ||
| Your dedication plays a crucial role in our work, and we deeply appreciate your continued support.</p> | ||
| <p>Please fill out this Dispatch Form – <a href='{$vehicleDispatchFormUrl}'>Link</a> once the vehicle is loaded and ready to head to Goonj’s processing center. | ||
| This will help us to verify and acknowledge the materials as soon as they arrive.</p> | ||
| <p>We truly appreciate your cooperation and continued commitment to our cause.</p> | ||
| <p>Warm Regards,<br>Team Goonj..</p> | ||
| </body> | ||
| </html> | ||
| "; | ||
| $from = HelperService::getDefaultFromEmail(); | ||
| $mailParams = [ | ||
| 'subject' => 'Kindly fill the Dispatch Form for Material Pickup', | ||
| 'from' => $from, | ||
| 'toEmail' => $email, | ||
| 'html' => $emailHtml, | ||
| ]; | ||
| </body> | ||
| </html> | ||
| "; | ||
|
|
||
| // Step 5: Send email | ||
| $from = HelperService::getDefaultFromEmail(); | ||
| $mailParams = [ | ||
| 'subject' => 'Kindly fill the Dispatch Form for Material Pickup', | ||
| 'from' => $from, | ||
| 'toEmail' => $email, | ||
| 'html' => $emailHtml, | ||
| ]; | ||
|
|
||
| \CRM_Utils_Mail::send($mailParams); | ||
| } | ||
| \CRM_Utils_Mail::send($mailParams); | ||
| } | ||
|
|
||
| /** | ||
| * | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid static methods for better testability
Static methods make unit testing difficult and violate dependency injection principles. Consider making this an instance method instead.
📝 Committable suggestion
🤖 Prompt for AI Agents