PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.37.0
MailPoet – Newsletters, Email Marketing, and Automation v5.37.0
5.37.0 5.36.1 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 / API / JSON / v1 / NewsletterLinks.php
mailpoet / lib / API / JSON / v1 Last commit date
Analytics.php 1 month ago AutomatedLatestContent.php 3 months ago AutomaticEmails.php 3 years ago Captcha.php 2 days ago Coupons.php 2 years ago CustomFields.php 3 months ago DynamicProducts.php 1 year ago DynamicSegments.php 3 months ago FeatureFlags.php 3 years ago Forms.php 3 months ago Help.php 1 year ago ImportExport.php 3 months ago Mailer.php 2 weeks ago NewsletterLinks.php 2 days ago NewsletterTemplates.php 3 months ago Newsletters.php 3 months ago Premium.php 1 year ago RedirectResponse.php 2 days ago Segments.php 1 month ago SendingQueue.php 3 months ago Services.php 7 months ago Settings.php 3 months ago Setup.php 1 year ago StatisticsExport.php 4 months ago SubscriberStats.php 3 months ago Subscribers.php 3 months ago Tags.php 3 years ago UserFlags.php 2 years ago WoocommerceProductVariations.php 3 months ago WoocommerceSettings.php 3 years ago index.php 3 years ago
NewsletterLinks.php
176 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\API\JSON\v1;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\API\JSON\Endpoint as APIEndpoint;
9 use MailPoet\Config\AccessControl;
10 use MailPoet\Cron\Workers\StatsNotifications\NewsletterLinkRepository;
11 use MailPoet\EmailEditor\Integrations\MailPoet\PersonalizationTagManager;
12 use MailPoet\EmailEditor\Integrations\MailPoet\PersonalizationTags\PersonalizationTagLinkResolver;
13 use MailPoet\Entities\NewsletterEntity;
14 use MailPoet\Entities\NewsletterOptionFieldEntity;
15 use MailPoet\Newsletter\Links\Links as NewsletterLinksService;
16 use MailPoet\Newsletter\NewslettersRepository;
17
18 class NewsletterLinks extends APIEndpoint {
19
20 public $permissions = [
21 'global' => AccessControl::PERMISSION_MANAGE_SEGMENTS,
22 ];
23
24 private const AUTOMATION_EMAIL_TYPES = [
25 NewsletterEntity::TYPE_AUTOMATION,
26 NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL,
27 ];
28
29 /** @var NewsletterLinkRepository */
30 private $newsletterLinkRepository;
31
32 /** @var NewslettersRepository */
33 private $newslettersRepository;
34
35 /** @var NewsletterLinksService */
36 private $newsletterLinks;
37
38 private PersonalizationTagLinkResolver $linkResolver;
39 private PersonalizationTagManager $personalizationTagManager;
40
41 public function __construct(
42 NewsletterLinkRepository $newsletterLinkRepository,
43 NewslettersRepository $newslettersRepository,
44 NewsletterLinksService $newsletterLinks,
45 PersonalizationTagLinkResolver $linkResolver,
46 PersonalizationTagManager $personalizationTagManager
47 ) {
48 $this->newsletterLinkRepository = $newsletterLinkRepository;
49 $this->newslettersRepository = $newslettersRepository;
50 $this->newsletterLinks = $newsletterLinks;
51 $this->linkResolver = $linkResolver;
52 $this->personalizationTagManager = $personalizationTagManager;
53 }
54
55 public function get($data = []) {
56 $newsletterId = (int)($data['newsletterId'] ?? 0);
57 $newsletter = $this->newslettersRepository->findOneById($newsletterId);
58 if (!$newsletter instanceof NewsletterEntity) {
59 return $this->successResponse([]);
60 }
61
62 if (in_array($newsletter->getType(), self::AUTOMATION_EMAIL_TYPES, true)) {
63 return $this->successResponse($this->getAutomationLinks($newsletter));
64 }
65
66 $links = $this->newsletterLinkRepository->findBy(['newsletter' => $newsletterId]);
67 $response = [];
68 foreach ($links as $link) {
69 $response[] = [
70 'id' => $link->getId(),
71 'url' => $this->getLabel($link->getUrl()),
72 ];
73 }
74 return $this->successResponse($response);
75 }
76
77 private function getAutomationLinks(NewsletterEntity $newsletter): array {
78 $newsletterId = $newsletter->getId();
79 if (!$newsletterId) {
80 return [];
81 }
82
83 // Subject-dependent tags (e.g. order tags) are registered per automation; needed for their labels
84 $automationId = $newsletter->getOptionValue(NewsletterOptionFieldEntity::NAME_AUTOMATION_ID);
85 if (is_numeric($automationId)) {
86 $this->personalizationTagManager->extendPersonalizationTagsByAutomationSubjects((int)$automationId);
87 }
88
89 $urls = [];
90 foreach ($this->newsletterLinkRepository->findUrlsByNewsletterId($newsletterId) as $url) {
91 $this->addUrl($urls, $url);
92 }
93 foreach ($this->extractUrlsFromNewsletterBody($newsletter) as $url) {
94 $this->addUrl($urls, $url);
95 }
96
97 return array_map(function(string $url): array {
98 return [
99 'id' => $url,
100 'url' => $this->getLabel($url),
101 ];
102 }, array_values($urls));
103 }
104
105 /**
106 * Links stored as personalization tag tokens are shown under the tag's name.
107 */
108 private function getLabel(string $url): string {
109 return $this->linkResolver->getDisplayName($url) ?? $url;
110 }
111
112 /**
113 * @return string[]
114 */
115 private function extractUrlsFromNewsletterBody(NewsletterEntity $newsletter): array {
116 $body = $newsletter->getBody();
117 if (!is_array($body)) {
118 return [];
119 }
120
121 $urls = [];
122 $this->collectUrlsFromBody($body['content'] ?? $body, $urls);
123 return array_values($urls);
124 }
125
126 /**
127 * @param mixed $bodyPart
128 * @param array<string, string> $urls
129 */
130 private function collectUrlsFromBody($bodyPart, array &$urls): void {
131 if (!is_array($bodyPart)) {
132 return;
133 }
134
135 foreach ($bodyPart as $key => $value) {
136 if (is_string($value)) {
137 if (in_array($key, ['url', 'link'], true)) {
138 $this->addUrl($urls, $value);
139 }
140 // Links::extract parses HTML and scans for shortcodes; skip when neither marker is present
141 // to avoid running a DOM parse on every plain-text leaf of the body tree.
142 if (strpos($value, '<') !== false || strpos($value, '[') !== false) {
143 foreach ($this->newsletterLinks->extract($value) as $link) {
144 $this->addUrl($urls, $link['link'] ?? '');
145 }
146 }
147 }
148
149 if (is_array($value)) {
150 $this->collectUrlsFromBody($value, $urls);
151 }
152 }
153 }
154
155 /**
156 * @param array<string, string> $urls
157 */
158 private function addUrl(array &$urls, string $url): void {
159 $url = trim($url);
160 if (!$this->isSelectableUrl($url)) {
161 return;
162 }
163 $urls[$url] = $url;
164 }
165
166 private function isSelectableUrl(string $url): bool {
167 if ($url === '') {
168 return false;
169 }
170 // strpos returns false when '[' is absent and 0 when it's the first char; !== 0 covers both
171 // "no leading bracket" cases. Allow [link:…] shortcodes and personalization tag tokens through;
172 // reject other tokens like [postLink].
173 return strpos($url, '[') !== 0 || strpos($url, '[link:') === 0 || $this->linkResolver->isTokenUrl($url);
174 }
175 }
176