Skip to content

Commit

Permalink
Add “never opened” operator to campaign activity
Browse files Browse the repository at this point in the history
  • Loading branch information
bencroker committed Jul 2, 2024
1 parent c5a2f03 commit 5af2324
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 17 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Release Notes for Campaign

## 3.3.0 - 2024-07-02

### Added

- Added the ability to segment contacts by campaign activity with a “never opened” operator ([#482](https://github.com/putyourlightson/craft-campaign/issues/482)).

### Changed

- At most one campaign activity rule can now be added to the contact condition in a segment.

## 3.2.0 - 2024-06-25

### Added
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "putyourlightson/craft-campaign",
"description": "Send and manage email campaigns, contacts and mailing lists.",
"version": "3.2.0",
"version": "3.3.0",
"type": "craft-plugin",
"homepage": "https://putyourlightson.com/plugins/campaign",
"license": "proprietary",
Expand Down
41 changes: 25 additions & 16 deletions src/elements/conditions/contacts/CampaignActivityConditionRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,14 @@ public function getExclusiveQueryParams(): array
return [];
}

/**
* @inheritdoc
*/
public function modifyQuery(ElementQueryInterface $query): void
{
/** @var ContactElementQuery $query */
$query->innerJoin(ContactCampaignRecord::tableName(), '[[campaign_contacts.id]] = [[contactId]]');

$query->andWhere([
'not', [
$this->operatorColumn($this->operator) => null,
],
]);
$query->innerJoin(ContactCampaignRecord::tableName(), '[[campaign_contacts.id]] = [[contactId]]')
->andWhere($this->getOperatorCondition());

$elementId = $this->getElementId();
if ($elementId !== null) {
Expand All @@ -76,7 +74,7 @@ public function modifyQuery(ElementQueryInterface $query): void
protected function inputHtml(): string
{
return match ($this->operator) {
'openedCampaign', 'clickedCampaign' => parent::inputHtml(),
'openedCampaign', 'clickedCampaign', 'neverOpenedCampaign' => parent::inputHtml(),
default => '',
};
}
Expand All @@ -86,8 +84,10 @@ protected function operators(): array
return [
'opened',
'clicked',
'neverOpened',
'openedCampaign',
'clickedCampaign',
'neverOpenedCampaign',
];
}

Expand All @@ -99,8 +99,10 @@ protected function operatorLabel(string $operator): string
return match ($operator) {
'opened' => Craft::t('campaign', 'opened any campaign'),
'clicked' => Craft::t('campaign', 'clicked a link in any campaign'),
'neverOpened' => Craft::t('campaign', 'never opened any campaign'),
'openedCampaign' => Craft::t('campaign', 'opened the campaign'),
'clickedCampaign' => Craft::t('campaign', 'clicked a link in the campaign'),
'neverOpenedCampaign' => Craft::t('campaign', 'never opened the campaign'),
default => parent::operatorLabel($operator),
};
}
Expand All @@ -114,11 +116,7 @@ public function matchElement(ElementInterface $element): bool
->where([
'contactId' => $element->id,
])
->andWhere([
'not', [
$this->operatorColumn($this->operator) => null,
],
]);
->andWhere($this->getOperatorCondition());

$elementId = $this->getElementId();
if ($elementId !== null) {
Expand All @@ -129,13 +127,24 @@ public function matchElement(ElementInterface $element): bool
}

/**
* Returns the column to query on based on the operator.
* Returns the condition for the operator.
*/
private function operatorColumn(string $operator): string
private function getOperatorCondition(): array
{
return match ($operator) {
$operatorColumn = match ($this->operator) {
'clicked', 'clickedCampaign' => 'clicked',
default => 'opened',
};

if (str_starts_with($this->operator, 'never')) {
return [$operatorColumn => null];
}

return [
'not',
[
$operatorColumn => null,
],
];
}
}
22 changes: 22 additions & 0 deletions src/elements/conditions/contacts/ContactCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace putyourlightson\campaign\elements\conditions\contacts;

use craft\base\conditions\ConditionRuleInterface;
use craft\elements\conditions\ElementCondition;

/**
Expand All @@ -17,6 +18,27 @@ class ContactCondition extends ElementCondition
*/
public bool $sortable = true;

/**
* @inheritdoc
*/
protected function isConditionRuleSelectable(ConditionRuleInterface $rule): bool
{
if (!parent::isConditionRuleSelectable($rule)) {
return false;
}

// Allow at most one instance of `CampaignActivityConditionRule`, since modifying the query multiple times is not possible due to the `INNER JOIN`.
if ($rule instanceof CampaignActivityConditionRule) {
foreach ($this->getConditionRules() as $existingRule) {
if ($existingRule instanceof CampaignActivityConditionRule) {
return false;
}
}
}

return true;
}

/**
* @inheritdoc
*/
Expand Down

0 comments on commit 5af2324

Please sign in to comment.