PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.36.0
MailPoet – Newsletters, Email Marketing, and Automation v5.36.0
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Migrations / App / Migration_20250501_114655_App.php
mailpoet / lib / Migrations / App Last commit date
Migration_20221028_105818_App.php 3 years ago Migration_20230109_144830.php 3 years ago Migration_20230131_121621.php 3 years ago Migration_20230419_080000.php 3 years ago Migration_20230425_211517.php 3 years ago Migration_20230712_180341.php 3 years ago Migration_20230803_200413_App.php 2 years ago Migration_20230825_093531_App.php 1 year ago Migration_20231128_120355_App.php 1 year ago Migration_20240202_130053_App.php 2 years ago Migration_20240207_105912_App.php 1 year ago Migration_20240322_110443_App.php 2 years ago Migration_20240730_212419_App.php 2 years ago Migration_20241015_105511_App.php 1 year ago Migration_20241128_114257_App.php 1 year ago Migration_20250120_094614_App.php 1 year ago Migration_20250501_114655_App.php 11 months ago Migration_20260421_155908_App.php 3 months ago Migration_20260515_120000_App.php 3 months ago Migration_20260623_120000_App.php 1 month ago Migration_20260805_120000_App.php 1 week ago index.php 3 years ago
Migration_20250501_114655_App.php
101 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\Migrations\App;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Doctrine\WPDB\Connection as WPDBConnection;
9 use MailPoet\Entities\StatisticsClickEntity;
10 use MailPoet\Entities\StatisticsUnsubscribeEntity;
11 use MailPoet\Entities\SubscriberEntity;
12 use MailPoet\Entities\SubscriberSegmentEntity;
13 use MailPoet\Migrator\AppMigration;
14 use MailPoetVendor\Doctrine\DBAL\Connection;
15
16 class Migration_20250501_114655_App extends AppMigration {
17 private const DB_QUERY_CHUNK_SIZE = 1000;
18
19 public function run(): void {
20 $isSQLite = WPDBConnection::isSQLite();
21 if ($isSQLite) {
22 // SQLite (used in WP Studio) does not support the TIMESTAMPDIFF function, so we skip the migration
23 return;
24 }
25
26 $clicksStatsTable = $this->entityManager->getClassMetadata(StatisticsClickEntity::class)->getTableName();
27 $unsubscribeStatsTable = $this->entityManager->getClassMetadata(StatisticsUnsubscribeEntity::class)->getTableName();
28 $subscribersTable = $this->entityManager->getClassMetadata(SubscriberEntity::class)->getTableName();
29 $subscribersSegmentsTable = $this->entityManager->getClassMetadata(SubscriberSegmentEntity::class)->getTableName();
30
31 // First get all subscriber IDs that were unsubscribed by a bot
32 $subscriberIds = $this->entityManager->getConnection()->executeQuery(
33 "SELECT DISTINCT mp_unsub.subscriber_id
34 FROM {$unsubscribeStatsTable} AS mp_unsub
35 LEFT JOIN {$clicksStatsTable} AS mp_click
36 ON mp_unsub.newsletter_id = mp_click.newsletter_id
37 AND mp_unsub.subscriber_id = mp_click.subscriber_id
38 AND ABS(TIMESTAMPDIFF(SECOND, mp_click.created_at, mp_unsub.created_at)) <= 4
39 WHERE mp_unsub.created_at > '2025-03-01'
40 GROUP BY mp_unsub.subscriber_id
41 HAVING COUNT(mp_click.id) >= 3"
42 )->fetchFirstColumn();
43
44 if (empty($subscriberIds)) {
45 return;
46 }
47
48 // Process subscriber IDs in chunks
49 foreach (array_chunk($subscriberIds, self::DB_QUERY_CHUNK_SIZE) as $chunk) {
50 $this->processSubscriberChunk(
51 $chunk,
52 $subscribersTable,
53 $subscribersSegmentsTable,
54 $unsubscribeStatsTable
55 );
56 }
57 }
58
59 private function processSubscriberChunk(
60 array $subscriberIds,
61 string $subscribersTable,
62 string $subscribersSegmentsTable,
63 string $unsubscribeStatsTable
64 ): void {
65 // Switch the global subscriber status to subscribed
66 $this->entityManager->getConnection()->executeQuery(
67 "UPDATE {$subscribersTable}
68 SET status = :subscribedStatus
69 WHERE id IN (:subscriberIds)
70 AND status = :unsubscribedStatus",
71 [
72 'subscribedStatus' => SubscriberEntity::STATUS_SUBSCRIBED,
73 'unsubscribedStatus' => SubscriberEntity::STATUS_UNSUBSCRIBED,
74 'subscriberIds' => $subscriberIds,
75 ],
76 [
77 'subscriberIds' => Connection::PARAM_INT_ARRAY,
78 ]
79 );
80
81 // Update the subscriber_segment table, find rows that were unsubscribed at the same time
82 $this->entityManager->getConnection()->executeQuery(
83 "UPDATE {$subscribersSegmentsTable} AS mp_subseg
84 JOIN {$unsubscribeStatsTable} AS mp_unsub
85 ON mp_subseg.subscriber_id = mp_unsub.subscriber_id
86 AND ABS(TIMESTAMPDIFF(SECOND, mp_subseg.updated_at, mp_unsub.created_at)) <= 2
87 SET mp_subseg.status = :subscribedStatus
88 WHERE mp_subseg.status = :unsubscribedStatus
89 AND mp_subseg.subscriber_id IN (:subscriberIds)",
90 [
91 'subscribedStatus' => SubscriberEntity::STATUS_SUBSCRIBED,
92 'unsubscribedStatus' => SubscriberEntity::STATUS_UNSUBSCRIBED,
93 'subscriberIds' => $subscriberIds,
94 ],
95 [
96 'subscriberIds' => Connection::PARAM_INT_ARRAY,
97 ]
98 );
99 }
100 }
101