Skip to content

Commit

Permalink
Issue #516: Add more detail to the "review needed" email.
Browse files Browse the repository at this point in the history
  • Loading branch information
danhgov committed Sep 23, 2024
1 parent 7b11c00 commit 265f41b
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 50 deletions.
92 changes: 57 additions & 35 deletions html/modules/custom/bc_dc/bc_dc.module
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,11 @@ use Drupal\node\NodeInterface;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\tac_lite\Form\SchemeForm;
use Drupal\taxonomy\TermInterface;
use Drupal\taxonomy\Entity\Term;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;

use function PHPUnit\Framework\isNull;

/**
* Implements hook_entity_operation().
*/
Expand Down Expand Up @@ -374,10 +371,8 @@ function bc_dc_node_update(NodeInterface $entity): void {
/** @var \Drupal\flag\FlaggingInterface[] $bookmark_flaggings Array of flaggings. */
$bookmark_flaggings = $flag_service->getEntityFlaggings($bookmark_flag, $entity);

// Create an array of users who need to be notified of the change.
/** @var string[] $send_to Array of email addresses. */
$send_to = [];
// Add to the list if the data set was modified after the last viewed date.
// Email users who asked to be notified of the change,
// if the data set was modified after the last viewed date.
// We compare dates because field_modified_date might not be set to now, but
// some time in the past when it was know that an update was made.
foreach ($bookmark_flaggings as $bookmark_flagging) {
Expand All @@ -391,24 +386,33 @@ function bc_dc_node_update(NodeInterface $entity): void {
}
}

/**
* Send an email to someone re a bookmarked asset having been updated.
*
* @param \Drupal\user\Entity\User $owner
* User who bookmarked this Metadata record.
* @param [type] $asset
* The Metadata record they bookmarked.
*/
function _bc_dc_send_email_re_changed_asset($owner, $asset) {

// We want to be able to say what kind of metadata record this is:
// - Postgres database
// - CHEFS form
// etc.
// To do this, we first look up the data-type term, and its ancestors.
// Then we make a few changes (so that it reads better), and render it.

// Build an array of this asset's dataset_type's term's name, and its ancestors' names.
// We are trying to build this var: $nice_record_type_name.
// e.g. Data -> File -> CSV. => "CSV data-file"
// We want to be able to say what kind of metadata record this is:
// - Postgres database
// - CHEFS form
// etc.
// To do this, we first look up the data-type term, and its ancestors.
// Then we make a few changes (so that it reads better), and render it.
//
// Build an array of this asset's dataset_type's term's name,
// and its ancestors' names.
// We are trying to build this var: $nice_record_type_name.
/* e.g. Data -> File -> CSV. => "CSV data-file"
// Data -> Database -> Postgres => "Postgres database"
// Data -> Database => "Database"
// Form -> CHEFS => "CHEFS form"
// Report => "Report"
// Data => "Data-source"

*/
$dataset_type_names = [];
$dataset_type_tid = $asset->field_data_set_type[0]->getValue()['target_id'];
$term_ancestry = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadAllParents($dataset_type_tid);
Expand All @@ -417,7 +421,7 @@ function _bc_dc_send_email_re_changed_asset($owner, $asset) {
}

// $term_ancestry looks like 0=>'Postgres', 1='Database', 2=>'Data'.
// We want this reversed for our manipulations below.
// -- We want this reversed for our manipulations below.
$dataset_type_names = array_reverse($dataset_type_names);

if ($dataset_type_names[0] == 'Data') {
Expand All @@ -435,9 +439,12 @@ function _bc_dc_send_email_re_changed_asset($owner, $asset) {
? $dataset_type_names[1] . ' ' . strtolower($dataset_type_names[0])
: $dataset_type_names[0];

# This "remove-bookmark" link doesn't work, I think due to the CSRF token being connected to the wrong user?
# $remove_bookmark_link = Url::fromRoute('flag.action_link_unflag', ['flag'=>'bookmark', 'entity_id'=> $asset->id()], ['absolute' => TRUE])->toString(),

// This "remove-bookmark" link doesn't work, I think due to
// the CSRF token being connected to the wrong user?
/* $remove_bookmark_link = Url::fromRoute('flag.action_link_unflag',
['flag'=>'bookmark', 'entity_id'=> $asset->id()],
['absolute' => TRUE])->toString(),
*/
$subject = 'Your bookmarked metadata record has been updated';

$body_content = t(<<<END_BODY
Expand All @@ -447,14 +454,12 @@ In the Finance Data Catalog, you previously asked be notified of any changes to
- [@direct_asset_url](@asset_url)
___
If you no longer want these reminders, you can opt out of them:
- [Your Data Catalog notifications-preferences](@subscriber_alerts_url)
For more information or assistance, please contact @contact_email.
Ministry of Finance Data Catalogue
@data_cat_main_url
END_BODY
, [
'@first_name' => $owner->field_first_name->value,
Expand All @@ -463,27 +468,44 @@ END_BODY
'@direct_asset_url' => Url::fromRoute('entity.node.canonical',
['node' => $asset->id()],
['absolute' => TRUE]
)->toString(),
)->toString(),
'@asset_url' => Url::fromRoute('user.login', [], [
'query' => ['destination' => '/node/' . $asset->id()],
'absolute' => TRUE,
])->toString(),
'query' => ['destination' => '/node/' . $asset->id()],
'absolute' => TRUE,
])->toString(),
'@subscriber_alerts_url' => Url::fromRoute('user.login', [], [
'query' => ['destination' => '/user/' . $owner->id() . '/bookmarks'],
'absolute' => TRUE,
])->toString(),
'@contact_email' => '[email protected]',
'@data_cat_main_url' => Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(),
])->toString(),
]
);

$body_content .= _bc_dc_get_email_footer();

// Send the message and log the result.
$success = GcNotifyApiService::sendMessage([$owner->getEmail()], $subject, $body_content);
$logger = \Drupal::logger('bc_dc');
$logger->notice(($success ? 'Sent message' : 'Failed sending message')
. 'to user @user_num when updating data_set @nid.', [
'@user_num' => $owner->id(),
'@nid' => $asset->id(),
'@user_num' => $owner->id(),
'@nid' => $asset->id(),
]
);
}

/**
* Get the common text we put in email footers.
*/
function _bc_dc_get_email_footer() {
return t(<<<END_EMAIL_FOOTER
For more information or assistance, please contact @contact_email.
Ministry of Finance Data Catalogue
@data_cat_main_url
END_EMAIL_FOOTER
, [
'@contact_email' => '[email protected]',
'@data_cat_main_url' => Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString(),
]);
}

Expand Down
72 changes: 58 additions & 14 deletions html/modules/custom/bc_dc/src/Service/ReviewReminder.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public function sendRemindersToOneUser(int $uid, array $assets_needing_review_fo
return NULL;
}

$body = $this->generateBody($assets_needing_review_for_user);
$body = $this->generateBody($assets_needing_review_for_user, $uid);
if (!$body) {
$logger->error('ReviewReminder: Empty message for user @uid.', ['@uid' => $uid]);
return NULL;
Expand All @@ -112,41 +112,85 @@ public function sendRemindersToOneUser(int $uid, array $assets_needing_review_fo
* @param array $assets_needing_review_for_user
* An array of assets that need review, one user's worth of what is
* generated by ::getAssetsNeedingReview().
* @param int $uid
* User ID of user that these assets belong to.
*
* @return string|null
* The body of the message or NULL if there is no message to send.
*/
public function generateBody(array $assets_needing_review_for_user): ?string {
public function generateBody(array $assets_needing_review_for_user, $uid): ?string {

$user = $this->entityTypeManager->getStorage('user')->load($uid);

$body = <<<END_INTRO
Dear {$user->field_first_name->value},
To uphold the trust of our users in the Data Catalogue, it is important that the records are reviewed periodically. Your attention to this ensures the accuracy and reliability of the data we maintain.
Please review the following records:
$bc_dc_settings = $this->configFactory->get('bc_dc.settings');
END_INTRO;

$body = [];
$update_types = [
static::REVIEW_OVERDUE => trim($bc_dc_settings->get('review_overdue_message'), '.'),
static::REVIEW_NEEDED => trim($bc_dc_settings->get('review_needed_message'), '.'),
static::REVIEW_OVERDUE,
static::REVIEW_NEEDED,
];
foreach ($update_types as $update_type => $update_message) {
foreach ($update_types as $update_type) {
if (!empty($assets_needing_review_for_user[$update_type])) {
$body[] = $update_message . ':';
foreach ($assets_needing_review_for_user[$update_type] as $asset_needing_review) {
// If a user clicks one of these links to one of
// their Metadata Records in the email they receive,
// they will confusingly get a "Not Found" error if they are not logged in.
// they will confusingly get a "Not Found" error
// if they are not logged in.
// So we make the link to instead to the login page, with a REDIRECT
// to the real URL we want to take them to.
$url_via_login = Url::fromRoute('user.login', [], [
'query' => ['destination' => '/node/' . $asset_needing_review->id()],
'absolute' => TRUE,
]);
$prev_review_date = strtotime($asset_needing_review->field_last_review_date->value);
$review_freq_months = $asset_needing_review->field_review_interval->value;

// Calculate $next_review_date.
[$y, $m, $d] = explode(' ', date('Y m d', $prev_review_date));
$m += $review_freq_months;
$next_review_date = (new \DateTime())->setDate($y, $m, $d)->format('U');

$date_formatter = \Drupal::service('date.formatter');

// GC Notify requires Markdown formatting, not HTML.
$body[] = sprintf('* [%s](%s)', $asset_needing_review->getTitle(), $url_via_login->toString());
$body .= t(<<<END_ITEM_TO_REVIEW
[@asset_title](@url_via_login) @review_is_overdue
* Last Reviewed: @prev_review_date
* Review Frequency: @review_freq
* Next Review Due: @next_review_date (@next_review_ago_or_fromnow)
END_ITEM_TO_REVIEW
, [
'@asset_title' => $asset_needing_review->getTitle(),
'@url_via_login' => $url_via_login->toString(),
'@review_is_overdue' => $update_type == static::REVIEW_OVERDUE
? '(review of this record is **overdue**)'
: '',
'@prev_review_date' => date('F j, Y', $prev_review_date),
'@review_freq' => $review_freq_months == 12
? 'Annually'
: $this->formatPlural($review_freq_months, 'Every month', 'Every @count months'),
'@next_review_ago_or_fromnow' => time() > $next_review_date
? $date_formatter->formatTimeDiffSince($next_review_date) . ' overdue'
: $date_formatter->formatTimeDiffUntil($next_review_date) . ' from now',
'@next_review_date' => date('F j, Y', $next_review_date),
]
);
}
}
}
if ($body) {
return implode("\n\n", $body);
}
return NULL;

$body .= "___\n\n" . _bc_dc_get_email_footer();

return $body;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@ public function test(): void {
$this->assertSame($reminders, $expected);
// Test ::generateBody().
// Generate empty reminder body.
$reminderBody = $bc_dc_review_reminder->generateBody([]);
$reminderBody = $bc_dc_review_reminder->generateBody([], 1);
$this->assertNull($reminderBody);
// Generate reminder body for user 1.
$reminderBody = $bc_dc_review_reminder->generateBody($reminders[1]);
Expand Down

0 comments on commit 265f41b

Please sign in to comment.