Skip to content

Commit d2db1d1

Browse files
committed
Add import failure table
Closes #528
1 parent 1b07d3b commit d2db1d1

12 files changed

Lines changed: 123 additions & 39 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Release Notes for Campaign
22

3-
## 3.6.1 - Unreleased
3+
## 3.7.0 - Unreleased
44

55
- Added a `contactInteraction` event to the `CampaignsService` class that is triggered when a contact interacts with a sendout, such as opening an email or clicking a link ([#518](https://github.com/putyourlightson/craft-campaign/issues/518)).
6+
- Added a table of import failures to the contact import page ([#528](https://github.com/putyourlightson/craft-campaign/issues/528)).
67

78
## 3.6.0 - 2025-07-21
89

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "putyourlightson/craft-campaign",
33
"description": "Send and manage email campaigns, contacts and mailing lists.",
4-
"version": "3.6.1",
4+
"version": "3.7.0",
55
"type": "craft-plugin",
66
"homepage": "https://putyourlightson.com/plugins/campaign",
77
"license": "proprietary",

src/Campaign.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public static function editions(): array
184184
/**
185185
* @inheritdoc
186186
*/
187-
public string $schemaVersion = '3.5.0';
187+
public string $schemaVersion = '3.7.0';
188188

189189
/**
190190
* @inheritdoc

src/migrations/Install.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ protected function createTables(): bool
298298
'added' => $this->integer(),
299299
'updated' => $this->integer(),
300300
'failures' => $this->integer(),
301+
'failedRows' => $this->text(),
302+
'failureMessages' => $this->text(),
301303
'dateImported' => $this->dateTime(),
302304
'dateCreated' => $this->dateTime()->notNull(),
303305
'dateUpdated' => $this->dateTime()->notNull(),
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace putyourlightson\campaign\migrations;
4+
5+
use craft\db\Migration;
6+
use putyourlightson\campaign\records\ImportRecord;
7+
8+
class m250904_120000_add_failure_messages_column extends Migration
9+
{
10+
/**
11+
* @inheritdoc
12+
*/
13+
public function safeUp(): bool
14+
{
15+
if (!$this->db->columnExists(ImportRecord::tableName(), 'failedRows')) {
16+
$this->addColumn(ImportRecord::tableName(), 'failedRows', $this->text()->after('failures'));
17+
}
18+
19+
if (!$this->db->columnExists(ImportRecord::tableName(), 'failureMessages')) {
20+
$this->addColumn(ImportRecord::tableName(), 'failureMessages', $this->text()->after('failedRows'));
21+
}
22+
23+
return true;
24+
}
25+
26+
/**
27+
* @inheritdoc
28+
*/
29+
public function safeDown(): bool
30+
{
31+
echo self::class . " cannot be reverted.\n";
32+
33+
return false;
34+
}
35+
}

src/models/ImportModel.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ class ImportModel extends Model
9292
*/
9393
public int $failures = 0;
9494

95+
/**
96+
* @var string[] Failed rows
97+
*/
98+
public array $failedRows = [];
99+
100+
/**
101+
* @var string[] Failure messages
102+
*/
103+
public array $failureMessages = [];
104+
95105
/**
96106
* @var DateTime|null Date imported
97107
*/

src/records/ImportRecord.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
* @property int $added Added
2424
* @property int $updated Updated
2525
* @property int $failures Failures
26+
* @property string[] $failedRows Failed rows
27+
* @property string[] $failureMessages Failure messages
2628
* @property DateTime $dateImported Date imported
2729
*/
2830
class ImportRecord extends ActiveRecord

src/services/ImportsService.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,10 @@ public function importRow(ImportModel $import, array $row): ImportModel
322322
if (!$success) {
323323
$import->failures++;
324324

325-
Campaign::$plugin->log(implode('. ', $contact->getErrorSummary(true)) . ' [' . implode(',', $row) . ']');
325+
$import->failedRows[] = implode(',', $row);
326+
$failureMessage = $contact->getErrorSummary(false)[0] ?? '';
327+
$import->failureMessages[] = $failureMessage;
328+
Campaign::$plugin->log('[' . implode(',', $row) . '] : ' . $failureMessage);
326329

327330
Campaign::$plugin->imports->saveImport($import);
328331

src/templates/contacts/import/_view.twig

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -51,46 +51,71 @@
5151
{{ forms.field({label: "Force Subscribe"|t('campaign')}, input) }}
5252

5353
{% set input %}
54-
{{ import.failures }}
5554
{% if import.failures > 0 %}
56-
<span class="info">
57-
{{ "View `storage/logs/campaign.log` file for errors."|t('campaign')|markdown }}
58-
</span>
55+
{% if import.failedRows is not empty %}
56+
<table class="editable fullwidth">
57+
<thead>
58+
<tr>
59+
<th>
60+
{{ "Failure Message"|t('campaign') }}
61+
</th>
62+
<th>
63+
{{ "Failed Row"|t('campaign') }}
64+
</th>
65+
</tr>
66+
</thead>
67+
{% for failedRow in import.failedRows %}
68+
<tr>
69+
<th>
70+
{{ (import.failureMessages[loop.index0] ?? '')|markdown }}
71+
</th>
72+
<td class="code">
73+
{{ failedRow }}
74+
</td>
75+
</tr>
76+
{% endfor %}
77+
</table>
78+
{% else %}
79+
{{ import.failures }}
80+
<span class="info">
81+
{{ "View `storage/logs/campaign.log` file for errors."|t('campaign')|markdown }}
82+
</span>
83+
{% endif %}
5984
{% endif %}
6085
{% endset %}
61-
{{ forms.field({label: "Failed Imports"|t('campaign')}, input) }}
86+
{{ forms.field({label: "Failed Rows"|t('campaign')}, input) }}
6287

6388
{% endblock %}
6489

6590

66-
{% block details %}
91+
{% block details %}
6792

68-
<div class="meta read-only">
69-
<div class="elementThumb data first">
70-
<h5 class="heading">{{ "Imported By"|t('campaign') }}</h5>
71-
<div class="value">
72-
{% set user = import.getUser() %}
73-
{% if user %}
74-
{{ elementChip(import.getUser()) }}
75-
{% endif %}
93+
<div class="meta read-only">
94+
<div class="elementThumb data first">
95+
<h5 class="heading">{{ "Imported By"|t('campaign') }}</h5>
96+
<div class="value">
97+
{% set user = import.getUser() %}
98+
{% if user %}
99+
{{ elementChip(import.getUser()) }}
100+
{% endif %}
101+
</div>
102+
</div>
103+
<div class="data">
104+
<h5 class="heading">{{ "Added"|t('campaign') }}</h5>
105+
<div class="value">{{ import.added }}</div>
106+
</div>
107+
<div class="data">
108+
<h5 class="heading">{{ "Updated"|t('campaign') }}</h5>
109+
<div class="value">{{ import.updated }}</div>
110+
</div>
111+
<div class="data">
112+
<h5 class="heading">{{ "Failures"|t('campaign') }}</h5>
113+
<div class="value">{{ import.failures }}</div>
114+
</div>
115+
<div class="data">
116+
<h5 class="heading">{{ "Date Imported"|t('campaign') }}</h5>
117+
<div class="value">{{ import.dateImported|datetime }}</div>
76118
</div>
77119
</div>
78-
<div class="data">
79-
<h5 class="heading">{{ "Added"|t('campaign') }}</h5>
80-
<div class="value">{{ import.added }}</div>
81-
</div>
82-
<div class="data">
83-
<h5 class="heading">{{ "Updated"|t('campaign') }}</h5>
84-
<div class="value">{{ import.updated }}</div>
85-
</div>
86-
<div class="data">
87-
<h5 class="heading">{{ "Failures"|t('campaign') }}</h5>
88-
<div class="value">{{ import.failures }}</div>
89-
</div>
90-
<div class="data">
91-
<h5 class="heading">{{ "Date Imported"|t('campaign') }}</h5>
92-
<div class="value">{{ import.dateImported|datetime }}</div>
93-
</div>
94-
</div>
95120

96-
{% endblock %}
121+
{% endblock %}

src/translations/de/campaign.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,10 @@
269269
'Expected Recipients' => 'Erwartete Empfänger',
270270
'Export contacts' => 'Kontakte exportieren',
271271
'Export' => 'Exportieren',
272-
'Failed Imports' => 'Fehlgeschlagene Importe',
273272
'Failed' => 'Fehlgeschlagen',
273+
'Failed Row' => 'Fehlgeschlagene Zeile',
274+
'Failed Rows' => 'Fehlgeschlagene Zeilen',
275+
'Failure Message' => 'Fehlermeldung',
274276
'Failures' => 'Fehlschläge',
275277
'Filter by interaction' => 'Filtere nach Interaktion',
276278
'Filter by sendout' => 'Filtere nach Sendung',

0 commit comments

Comments
 (0)