Skip to content

Commit

Permalink
MMB-306: Restrict validity dates and tokens to just membership type c…
Browse files Browse the repository at this point in the history
…ertificates
  • Loading branch information
shahrukh-moby authored and Muhammad Shahrukh committed Jan 24, 2024
1 parent ba7103f commit a53c1aa
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 92 deletions.
16 changes: 15 additions & 1 deletion CRM/Certificate/Entity/AbstractEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public function getCertificateConfiguration($entityId, $contactId) {
$certificateBAO->selectAdd(CRM_Certificate_DAO_CompuCertificate::$_tableName . '.id');
$certificateBAO->find(TRUE);

if (!empty($certificateBAO->id)) {
if (!empty($certificateBAO->id) && $this->isCertificateValidForAnEntity($certificateBAO, $contactId)) {
return $certificateBAO;
}
}
Expand Down Expand Up @@ -164,6 +164,20 @@ abstract protected function addEntityConditionals($certificateBAO, $entityId, $c
*/
protected function addEntityExtraField($certificateBAO, &$certificate) {}

/**
* Validate a certificate against entity specific checks.
*
* @param \CRM_Certificate_BAO_CompuCertificate $certificate
* Certificate.
* @param int $contactId
* Contact id.
*
* @return bool
*/
protected function isCertificateValidForAnEntity(\CRM_Certificate_BAO_CompuCertificate $certificate, int $contactId) {
return TRUE;
}

/**
* Gets all entity certificates vailable for a contact.
*
Expand Down
10 changes: 10 additions & 0 deletions CRM/Certificate/Entity/Membership.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,14 @@ public function getEntity() {
return CertificateType::MEMBERSHIPS;
}

/**
* @inheritDoc
*/
protected function isCertificateValidForAnEntity(\CRM_Certificate_BAO_CompuCertificate $certificate, int $contactId) {
$membershipDates = (new CRM_Certificate_Service_CertificateMembership())->getMembershipDates($certificate->id, $contactId);

return ($membershipDates['startDate'] === NULL || $certificate->max_valid_through_date === NULL || $membershipDates['startDate'] <= $certificate->max_valid_through_date)
&& ($membershipDates['endDate'] === NULL || $certificate->min_valid_from_date === NULL || $membershipDates['endDate'] >= $certificate->min_valid_from_date);
}

}
5 changes: 3 additions & 2 deletions CRM/Certificate/Service/Certificate.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ public function store($values) {
if (!empty($values['id'])) {
$params['id'] = $values['id'];
}
$isMembershipCertificate = (int) $values['type'] === CRM_Certificate_Enum_CertificateType::MEMBERSHIPS;
$params['name'] = $values['name'];
$params['entity'] = $values['type'];
$params['end_date'] = $values['end_date'];
$params['start_date'] = $values['start_date'];
$params['min_valid_from_date'] = $values['min_valid_from_date'] ?? NULL;
$params['max_valid_through_date'] = $values['max_valid_through_date'] ?? NULL;
$params['min_valid_from_date'] = isset($values['min_valid_from_date']) && $isMembershipCertificate ? $values['min_valid_from_date'] : NULL;
$params['max_valid_through_date'] = isset($values['max_valid_through_date']) && $isMembershipCertificate ? $values['max_valid_through_date'] : NULL;
$params['template_id'] = $values['message_template_id'];
$params['download_type'] = $values['download_type'] ?? DownloadType::TEMPLATE;
$params['download_format'] = $values['download_format'] ?? DownloadFormat::PDF;
Expand Down
35 changes: 1 addition & 34 deletions CRM/Certificate/Service/CertificateAccessChecker.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?php

use Civi\Api4\Relationship;
use Civi\Api4\Membership;

class CRM_Certificate_Service_CertificateAccessChecker {

Expand All @@ -24,7 +23,7 @@ public function __construct($contactId, $certificate) {
* @return bool
*/
public function check() {
return $this->checkMembershipDates() && ($this->hasViewPermission() || $this->hasViewPermissionByRelationship());
return $this->hasViewPermission() || $this->hasViewPermissionByRelationship();
}

/**
Expand Down Expand Up @@ -81,36 +80,4 @@ private function hasViewPermissionByRelationship() {
return FALSE;
}

private function checkMembershipDates(): bool {
$membershipDates = $this->getMembershipDates();
$membershipStartDate = $membershipDates['start_date'];
$membershipEndDate = $membershipDates['end_date'];
$certificateStartDate = $this->certificate->min_valid_from_date ?? $membershipEndDate;
$certificateEndDate = $this->certificate->max_valid_through_date ?? $membershipStartDate;

return !empty($membershipStartDate) && !empty($membershipEndDate) && strtotime($membershipStartDate) <= strtotime($certificateEndDate) &&
strtotime($membershipEndDate) >= strtotime($certificateStartDate);
}

private function getMembershipDates(): array {
$startDate = NULL;
$endDate = NULL;

$memberships = Membership::get(FALSE)
->addSelect('start_date', 'end_date')
->addWhere('contact_id', '=', $this->contactId)
->addWhere('status_id', '=', 2)
->execute()
->getArrayCopy();

foreach ($memberships as $membership) {
$startDate = $startDate === NULL || strtotime($membership['start_date']) < strtotime($startDate) ?
$membership['start_date'] : $startDate;
$endDate = $endDate === NULL || strtotime($membership['end_date']) > strtotime($endDate) ?
$membership['end_date'] : $endDate;
}

return ['start_date' => $startDate, 'end_date' => $endDate];
}

}
41 changes: 41 additions & 0 deletions CRM/Certificate/Service/CertificateMembership.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,45 @@
<?php

use Civi\Api4\Membership;

class CRM_Certificate_Service_CertificateMembership extends CRM_Certificate_Service_Certificate {

public function getMembershipDates(int $certificateId, int $contactId): array {
$startDate = NULL;
$endDate = NULL;

$query = CRM_Utils_SQL_Select::from(CRM_Certificate_DAO_CompuCertificate::getTableName() . ' ccc')
->select(['GROUP_CONCAT(DISTINCT cet.entity_type_id) as entityTypes', 'GROUP_CONCAT(DISTINCT cs.status_id) as statuses'])
->join('cet', 'LEFT JOIN `' . CRM_Certificate_DAO_CompuCertificateEntityType::getTableName() . '` cet ON (cet.certificate_id = ccc.id)')
->join('cs', 'LEFT JOIN `' . CRM_Certificate_DAO_CompuCertificateStatus::getTableName() . '` cs ON (cs.certificate_id = ccc.id)')
->where('ccc.id = @certificateId', ['certificateId' => $certificateId])
->groupBy('ccc.id');

$certificates = $query->execute()->fetchAll();

$query = Membership::get(FALSE)
->addSelect('start_date', 'end_date')
->addWhere('contact_id', '=', $contactId)
->addClause('OR', ['start_date', 'IS NULL'], ['start_date', '<=', 'now'])
->addClause('OR', ['end_date', 'IS NULL'], ['end_date', '>', 'now']);

if (!empty($certificates[0]['entityTypes'])) {
$query->addWhere('membership_type_id', 'IN', explode(',', $certificates[0]['entityTypes']));
}
if (!empty($certificates[0]['statuses'])) {
$query->addWhere('status_id', 'IN', explode(',', $certificates[0]['statuses']));
}

$memberships = $query->execute()->getArrayCopy();

foreach ($memberships as $membership) {
$startDate = $startDate === NULL || strtotime($membership['start_date']) < strtotime($startDate) ?
$membership['start_date'] : $startDate;
$endDate = $endDate === NULL || strtotime($membership['end_date']) > strtotime($endDate) ?
$membership['end_date'] : $endDate;
}

return ['startDate' => $startDate, 'endDate' => $endDate];
}

}
77 changes: 29 additions & 48 deletions CRM/Certificate/Token/Certificate.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?php

use Civi\Token\Event\TokenValueEvent;
use Civi\Api4\Membership;

/**
* Class CRM_Certificate_Token_Certificate
Expand Down Expand Up @@ -61,7 +60,7 @@ public function prefetch(TokenValueEvent $e) {
return $resolvedTokens;
}

$this->resolveFields($certificate, $this->getMembershipDates($e), $resolvedTokens);
$this->resolveFields($certificate, $resolvedTokens, $e);
}
}
catch (Exception $e) {
Expand All @@ -71,64 +70,46 @@ public function prefetch(TokenValueEvent $e) {
return $resolvedTokens;
}

private function getMembershipDates(TokenValueEvent $e): array {
$startDate = NULL;
$endDate = NULL;

$contactIds = $e->getTokenProcessor()->getContextValues('contactId');
$contactId = (is_array($contactIds) && !empty($contactIds[0])) ? $contactIds[0] : 0;

// get all memberships for given contact to get min start_date and max end_date of all memberships
$memberships = Membership::get(FALSE)
->addSelect('start_date', 'end_date')
->addWhere('contact_id', '=', $contactId)
->addWhere('status_id', '=', self::MEMBERSHIP_STATUS_CURRENT)
->execute()
->getArrayCopy();

foreach ($memberships as $membership) {
$startDate = $startDate === NULL || strtotime($membership['start_date']) < strtotime($startDate) ?
$membership['start_date'] : $startDate;
$endDate = $endDate === NULL || strtotime($membership['end_date']) > strtotime($endDate) ?
$membership['end_date'] : $endDate;
}

return ['start_date' => $startDate, 'end_date' => $endDate];
}

/**
* Resolve the value of ceritificate configuration token fields.
*
* @param CRM_Certificate_DAO_CompuCertificate $certificate
* @param array $membershipDates
* @param array &$resolvedTokens
* @param Civi\Token\Event\TokenValueEvent $e
*/
private function resolveFields($certificate, array $membershipDates, &$resolvedTokens) {
$membershipStartTimestamp = !empty($membershipDates['start_date']) ? strtotime($membershipDates['start_date']) : '';
$membershipEndTimestamp = !empty($membershipDates['end_date']) ? strtotime($membershipDates['end_date']) : '';
$certificateValidityStartTimestamp = !empty($certificate->min_valid_from_date) ? strtotime($certificate->min_valid_from_date) : '';
$certificateValidityEndTimestamp = !empty($certificate->max_valid_through_date) ? strtotime($certificate->max_valid_through_date) : '';
private function resolveFields($certificate, &$resolvedTokens, $e) {
$resolvedTokens['rolling_start_or_renewal_date'] = '';

if ($membershipStartTimestamp && $membershipEndTimestamp) {
$renewalTimestamp = strtotime($membershipDates['end_date'] . " -1 year 1 day");
$resolvedTokens['rolling_start_or_renewal_date'] = $renewalTimestamp > $membershipStartTimestamp
? CRM_Utils_Date::customFormat(date('Y-m-d', $renewalTimestamp), '%e/%b/%Y')
: CRM_Utils_Date::customFormat($membershipDates['start_date'], '%e/%b/%Y');
$resolvedTokens['valid_from'] = '';
$resolvedTokens['valid_to'] = '';

if ((int) $certificate->entity === CRM_Certificate_Enum_CertificateType::MEMBERSHIPS) {
$contactIds = $e->getTokenProcessor()->getContextValues('contactId');
$contactId = (is_array($contactIds) && !empty($contactIds[0])) ? $contactIds[0] : 0;
$membershipDates = (new CRM_Certificate_Service_CertificateMembership())->getMembershipDates($certificate->id, $contactId);
$membershipStartTimestamp = !empty($membershipDates['startDate']) ? strtotime($membershipDates['startDate']) : '';
$membershipEndTimestamp = !empty($membershipDates['endDate']) ? strtotime($membershipDates['endDate']) : '';
$certificateValidityStartTimestamp = !empty($certificate->min_valid_from_date) ? strtotime($certificate->min_valid_from_date) : '';
$certificateValidityEndTimestamp = !empty($certificate->max_valid_through_date) ? strtotime($certificate->max_valid_through_date) : '';

if ($membershipStartTimestamp && $membershipEndTimestamp) {
$renewalTimestamp = strtotime($membershipDates['endDate'] . " -1 year 1 day");
$resolvedTokens['rolling_start_or_renewal_date'] = $renewalTimestamp > $membershipStartTimestamp
? CRM_Utils_Date::customFormat(date('Y-m-d', $renewalTimestamp), '%e/%b/%Y')
: CRM_Utils_Date::customFormat($membershipDates['startDate'], '%e/%b/%Y');
}
$validityStartDate = empty($certificateValidityStartTimestamp) || $membershipStartTimestamp > $certificateValidityStartTimestamp ?
$membershipDates['startDate'] : (string) $certificate->min_valid_from_date;
$validityEndDate = empty($certificateValidityEndTimestamp) || (!empty($membershipEndTimestamp) && $certificateValidityEndTimestamp > $membershipEndTimestamp) ?
$membershipDates['endDate'] : (string) $certificate->max_valid_through_date;
$resolvedTokens['valid_from'] = !empty($validityStartDate)
? CRM_Utils_Date::customFormat($validityStartDate, '%e/%b/%Y') : '';
$resolvedTokens['valid_to'] = !empty($validityEndDate)
? CRM_Utils_Date::customFormat($validityEndDate, '%e/%b/%Y') : '';
}

$validityStartDate = empty($certificateValidityStartTimestamp) || $membershipStartTimestamp > $certificateValidityStartTimestamp ?
$membershipDates['start_date'] : (string) $certificate->min_valid_from_date;
$validityEndDate = empty($certificateValidityEndTimestamp) || (!empty($membershipEndTimestamp) && $certificateValidityEndTimestamp > $membershipEndTimestamp) ?
$membershipDates['end_date'] : (string) $certificate->max_valid_through_date;

$resolvedTokens['name'] = $certificate->name;
$resolvedTokens['start_date'] = CRM_Utils_Date::customFormat($certificate->start_date, '%e/%b/%Y');
$resolvedTokens['end_date'] = CRM_Utils_Date::customFormat($certificate->end_date, '%e/%b/%Y');
$resolvedTokens['valid_from'] = !empty($validityStartDate)
? CRM_Utils_Date::customFormat($validityStartDate, '%e/%b/%Y') : '';
$resolvedTokens['valid_to'] = !empty($validityEndDate)
? CRM_Utils_Date::customFormat($validityEndDate, '%e/%b/%Y') : '';
}

}
8 changes: 4 additions & 4 deletions templates/CRM/Certificate/Form/CertificateConfigure.hlp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
<p>{ts}<b>Tokens:</b> They will create tokens that can be used on certificates as follows:{/ts}</p>
<ul>
<li>
<p>{ts}'Min Valid From Date' (certificate.valid_from){/ts}</p>
<p>{ts}"Min Valid From Date" (certificate.valid_from){/ts}</p>
<p>{ts}The latter of 1. Min Valid From Date and 2. The users membership start date.{/ts}</p>
</li>
<li>
<p>{ts}'Max Valid Through Date' (certificate.valid_to){/ts}</p>
<p>{ts}"Max Valid Through Date" (certificate.valid_to){/ts}</p>
<p>{ts}The earlier of 1. The date entered in this field and 2. The users membership end date.{/ts}</p>
</li>
</ul>
Expand All @@ -42,11 +42,11 @@
<p>{ts}<b>Tokens:</b> They will create tokens that can be used on certificates as follows:{/ts}</p>
<ul>
<li>
<p>{ts}'Min Valid From Date' (certificate.valid_from){/ts}</p>
<p>{ts}"Min Valid From Date" (certificate.valid_from){/ts}</p>
<p>{ts}The latter of 1. Min Valid From Date and 2. The users membership start date.{/ts}</p>
</li>
<li>
<p>{ts}'Max Valid Through Date' (certificate.valid_to){/ts}</p>
<p>{ts}"Max Valid Through Date" (certificate.valid_to){/ts}</p>
<p>{ts}The earlier of 1. The date entered in this field and 2. The users membership end date.{/ts}</p>
</li>
</ul>
Expand Down
19 changes: 16 additions & 3 deletions templates/CRM/Certificate/Form/CertificateConfigure.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<div class="form-group row {$elementName}">
<div class="col-sm-2 control-label">
{$form.$elementName.label}
{if in_array($elementName, $help)}
{if in_array($elementName, $help)}
{help id="$elementName" file="CRM/Certificate/Form/CertificateConfigure.hlp"}
{/if}
</div>
Expand All @@ -33,6 +33,7 @@
let performingUpdate = false
const TYPE_CASES = "1";
const TYPE_EVENTS = "2";
const TYPE_MEMBERSHIP = "3";
const FORMAT_IMAGE = "2";
const TYPE_TEMPLATE = "1";
Expand All @@ -51,19 +52,31 @@
$('.participant_type_id').hide()
}
}
let toggleValidityDateFields = ($, val) => {
if (val === TYPE_MEMBERSHIP) {
$('.row.min_valid_from_date').show();
$('.row.max_valid_through_date').show();
} else {
$('.row.min_valid_from_date').hide();
$('.row.max_valid_through_date').hide();
}
}
CRM.$(function ($) {
$('.participant_type_id').hide();
toggleValidityDateFields($, CRM.$('[name=type]').val());
/**
* if an entity is selected we want to populate the
* if an entity is selected we want to populate the
* linked_to (entity type) and status (entity status) entity reference field
* with the right values
*/
CRM.$('[name=type]').on('change', function (e) {
if (e.target.value > 0) {
toggleValidityDateFields($, e.target.value);
if (!performingUpdate) {
$('[name=linked_to]').val('')
$('[name=statuses]').val('')
Expand Down

0 comments on commit a53c1aa

Please sign in to comment.