PopulatorData
2 months ago
AccessControl.php
2 years ago
Activator.php
2 months ago
AssetsLoader.php
2 weeks ago
Capabilities.php
2 months ago
Changelog.php
2 months ago
DeactivationPoll.php
3 years ago
DeferredAdminNotices.php
2 months ago
Env.php
6 months ago
Hooks.php
1 month ago
HooksWooCommerce.php
1 month ago
Initializer.php
1 month ago
Installer.php
10 months ago
Localizer.php
3 years ago
Menu.php
1 month ago
PersonalDataErasers.php
1 month ago
PersonalDataExporters.php
1 month ago
PluginActivatedHook.php
3 years ago
Populator.php
2 weeks ago
PrivacyPolicy.php
1 month ago
Renderer.php
1 year ago
RendererFactory.php
3 years ago
RequirementsChecker.php
2 months ago
Router.php
2 months ago
ServicesChecker.php
3 years ago
Shortcodes.php
1 month ago
SilentUpgraderSkin.php
3 years ago
SubscriberChangesNotifier.php
2 months ago
TranslationUpdater.php
3 months ago
TwigEnvironment.php
1 year ago
TwigFileSystemCache.php
3 years ago
Updater.php
4 days ago
index.php
3 years ago
Shortcodes.php
323 lines
| 1 | <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing |
| 2 | |
| 3 | namespace MailPoet\Config; |
| 4 | |
| 5 | if (!defined('ABSPATH')) exit; |
| 6 | |
| 7 | |
| 8 | use MailPoet\Entities\NewsletterEntity; |
| 9 | use MailPoet\Entities\SendingQueueEntity; |
| 10 | use MailPoet\Entities\SubscriberEntity; |
| 11 | use MailPoet\Form\Widget; |
| 12 | use MailPoet\Newsletter\Embed\NewsletterEmbedService; |
| 13 | use MailPoet\Newsletter\NewslettersRepository; |
| 14 | use MailPoet\Newsletter\Sharing\ShareVisibility; |
| 15 | use MailPoet\Newsletter\Shortcodes\Categories\Date; |
| 16 | use MailPoet\Newsletter\Shortcodes\Categories\Link; |
| 17 | use MailPoet\Newsletter\Shortcodes\Categories\Newsletter; |
| 18 | use MailPoet\Newsletter\Shortcodes\Categories\Site; |
| 19 | use MailPoet\Newsletter\Shortcodes\Categories\Subscriber as SubscriberCategory; |
| 20 | use MailPoet\Newsletter\Shortcodes\Shortcodes as NewsletterShortcodes; |
| 21 | use MailPoet\Newsletter\Url as NewsletterUrl; |
| 22 | use MailPoet\Segments\SegmentSubscribersRepository; |
| 23 | use MailPoet\Subscribers\SubscribersRepository; |
| 24 | use MailPoet\Subscription\Pages; |
| 25 | use MailPoet\WP\Functions as WPFunctions; |
| 26 | use MailPoetVendor\Carbon\CarbonImmutable; |
| 27 | |
| 28 | class Shortcodes { |
| 29 | const DEFAULT_ARCHIVE_LIMIT = 100; |
| 30 | |
| 31 | /** @var Pages */ |
| 32 | private $subscriptionPages; |
| 33 | |
| 34 | /** @var WPFunctions */ |
| 35 | private $wp; |
| 36 | |
| 37 | /** @var SegmentSubscribersRepository */ |
| 38 | private $segmentSubscribersRepository; |
| 39 | |
| 40 | /** @var SubscribersRepository */ |
| 41 | private $subscribersRepository; |
| 42 | |
| 43 | /** @var NewsletterUrl */ |
| 44 | private $newsletterUrl; |
| 45 | |
| 46 | /** @var NewslettersRepository */ |
| 47 | private $newslettersRepository; |
| 48 | |
| 49 | /** @var ShareVisibility */ |
| 50 | private $shareVisibility; |
| 51 | |
| 52 | /** @var Date */ |
| 53 | private $dateCategory; |
| 54 | |
| 55 | /** @var Link */ |
| 56 | private $linkCategory; |
| 57 | |
| 58 | /** @var Newsletter */ |
| 59 | private $newsletterCategory; |
| 60 | |
| 61 | /** @var SubscriberCategory */ |
| 62 | private $subscriberCategory; |
| 63 | |
| 64 | /** @var Site */ |
| 65 | private $siteCategory; |
| 66 | |
| 67 | /** @var NewsletterEmbedService */ |
| 68 | private $newsletterEmbedService; |
| 69 | |
| 70 | public function __construct( |
| 71 | Pages $subscriptionPages, |
| 72 | WPFunctions $wp, |
| 73 | SegmentSubscribersRepository $segmentSubscribersRepository, |
| 74 | SubscribersRepository $subscribersRepository, |
| 75 | NewsletterUrl $newsletterUrl, |
| 76 | NewslettersRepository $newslettersRepository, |
| 77 | ShareVisibility $shareVisibility, |
| 78 | Date $dateCategory, |
| 79 | Link $linkCategory, |
| 80 | Newsletter $newsletterCategory, |
| 81 | SubscriberCategory $subscriberCategory, |
| 82 | Site $siteCategory, |
| 83 | NewsletterEmbedService $newsletterEmbedService |
| 84 | ) { |
| 85 | $this->subscriptionPages = $subscriptionPages; |
| 86 | $this->wp = $wp; |
| 87 | $this->segmentSubscribersRepository = $segmentSubscribersRepository; |
| 88 | $this->subscribersRepository = $subscribersRepository; |
| 89 | $this->newsletterUrl = $newsletterUrl; |
| 90 | $this->newslettersRepository = $newslettersRepository; |
| 91 | $this->shareVisibility = $shareVisibility; |
| 92 | $this->dateCategory = $dateCategory; |
| 93 | $this->linkCategory = $linkCategory; |
| 94 | $this->newsletterCategory = $newsletterCategory; |
| 95 | $this->subscriberCategory = $subscriberCategory; |
| 96 | $this->siteCategory = $siteCategory; |
| 97 | $this->newsletterEmbedService = $newsletterEmbedService; |
| 98 | } |
| 99 | |
| 100 | public function init() { |
| 101 | // form widget shortcode |
| 102 | $this->wp->addShortcode('mailpoet_form', [$this, 'formWidget']); |
| 103 | |
| 104 | // subscribers count shortcode |
| 105 | $this->wp->addShortcode('mailpoet_subscribers_count', [ |
| 106 | $this, 'getSubscribersCount', |
| 107 | ]); |
| 108 | $this->wp->addShortcode('wysija_subscribers_count', [ |
| 109 | $this, 'getSubscribersCount', |
| 110 | ]); |
| 111 | |
| 112 | // archives page |
| 113 | $this->wp->addShortcode('mailpoet_archive', [ |
| 114 | $this, 'getArchive', |
| 115 | ]); |
| 116 | $this->wp->addShortcode('mailpoet_newsletter', [ |
| 117 | $this, 'getNewsletterEmbed', |
| 118 | ]); |
| 119 | |
| 120 | $this->wp->addFilter('mailpoet_archive_email_processed_date', [ |
| 121 | $this, 'renderArchiveDate', |
| 122 | ], 2); |
| 123 | $this->wp->addFilter('mailpoet_archive_email_subject_line', [ |
| 124 | $this, 'renderArchiveSubject', |
| 125 | ], 2, 3); |
| 126 | |
| 127 | // initialize subscription pages data |
| 128 | $this->subscriptionPages->init(); |
| 129 | // initialize subscription management shortcodes |
| 130 | $this->subscriptionPages->initShortcodes(); |
| 131 | } |
| 132 | |
| 133 | public function getNewsletterEmbed($params = []): string { |
| 134 | if (!is_array($params)) { |
| 135 | $params = []; |
| 136 | } |
| 137 | |
| 138 | return $this->newsletterEmbedService->render([ |
| 139 | 'newsletterId' => $params['id'] ?? null, |
| 140 | 'height' => $params['height'] ?? null, |
| 141 | 'width' => $params['width'] ?? null, |
| 142 | 'showFallbackLink' => $params['show_fallback_link'] ?? true, |
| 143 | 'fallbackLinkAlignment' => $params['fallback_link_alignment'] ?? 'center', |
| 144 | 'iframeAlignment' => $params['iframe_alignment'] ?? 'center', |
| 145 | 'showEmailBackground' => $params['show_email_background'] ?? true, |
| 146 | ]); |
| 147 | } |
| 148 | |
| 149 | public function formWidget($params = []) { |
| 150 | // IMPORTANT: fixes conflict with MagicMember |
| 151 | $this->wp->removeShortcode('user_list'); |
| 152 | |
| 153 | if (isset($params['id']) && (int)$params['id'] > 0) { |
| 154 | $formWidget = new Widget(); |
| 155 | return $formWidget->widget([ |
| 156 | 'form' => (int)$params['id'], |
| 157 | 'form_type' => 'shortcode', |
| 158 | ]); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | public function getSubscribersCount($params) { |
| 163 | if (!empty($params['segments'])) { |
| 164 | $segmentIds = array_map(function($segmentId) { |
| 165 | return (int)trim($segmentId); |
| 166 | }, explode(',', $params['segments'])); |
| 167 | } |
| 168 | |
| 169 | if (empty($segmentIds)) { |
| 170 | return $this->wp->numberFormatI18n( |
| 171 | $this->subscribersRepository->countBy(['status' => SubscriberEntity::STATUS_SUBSCRIBED, 'deletedAt' => null]) |
| 172 | ); |
| 173 | } else { |
| 174 | return $this->wp->numberFormatI18n( |
| 175 | $this->segmentSubscribersRepository->getSubscribersCountBySegmentIds($segmentIds, SubscriberEntity::STATUS_SUBSCRIBED) |
| 176 | ); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | public function getArchive($params = '') { |
| 181 | $html = ''; |
| 182 | |
| 183 | $parsedParams = $this->getParsedArchiveParams($params); |
| 184 | $newsletters = $this->newslettersRepository->getArchives($parsedParams); |
| 185 | |
| 186 | $subscriber = $this->subscribersRepository->getCurrentWPUser(); |
| 187 | |
| 188 | if (empty($newsletters)) { |
| 189 | return $this->wp->applyFilters( |
| 190 | 'mailpoet_archive_no_newsletters', |
| 191 | __('Oops! There are no newsletters to display.', 'mailpoet') |
| 192 | ); |
| 193 | } else { |
| 194 | $title = $this->wp->applyFilters('mailpoet_archive_title', ''); |
| 195 | if (!empty($title) && is_scalar($title)) { |
| 196 | $html .= '<h3 class="mailpoet_archive_title">' . (string)$title . '</h3>'; |
| 197 | } |
| 198 | $html .= '<ul class="mailpoet_archive">'; |
| 199 | foreach ($newsletters as $newsletter) { |
| 200 | $queue = $newsletter->getLatestQueue(); |
| 201 | $processedDate = $this->wp->applyFilters('mailpoet_archive_email_processed_date', $newsletter); |
| 202 | $subjectLine = $this->wp->applyFilters('mailpoet_archive_email_subject_line', $newsletter, $subscriber, $queue); |
| 203 | |
| 204 | $html .= '<li>' . |
| 205 | '<span class="mailpoet_archive_date">' . |
| 206 | (is_scalar($processedDate) ? (string)$processedDate : '') . |
| 207 | '</span> |
| 208 | <span class="mailpoet_archive_subject">' . |
| 209 | (is_scalar($subjectLine) ? (string)$subjectLine : '') . |
| 210 | '</span> |
| 211 | </li>'; |
| 212 | } |
| 213 | $html .= '</ul>'; |
| 214 | } |
| 215 | return $html; |
| 216 | } |
| 217 | |
| 218 | public function getParsedArchiveParams($params): array { |
| 219 | $parsedParams = [ |
| 220 | 'startDate' => null, |
| 221 | 'endDate' => null, |
| 222 | 'segmentIds' => [], |
| 223 | 'subjectContains' => '', |
| 224 | 'limit' => self::DEFAULT_ARCHIVE_LIMIT, |
| 225 | ]; |
| 226 | |
| 227 | if (!is_array($params)) { |
| 228 | return $parsedParams; |
| 229 | } |
| 230 | |
| 231 | if (!empty($params['segments']) && is_string($params['segments'])) { |
| 232 | $parsedParams['segmentIds'] = array_map(function($segmentId) { |
| 233 | return (int)trim($segmentId); |
| 234 | }, explode(',', $params['segments'])); |
| 235 | } |
| 236 | |
| 237 | if (isset($params['start_date']) && is_string($params['start_date']) && $params['start_date'] !== '') { |
| 238 | try { |
| 239 | $parsedParams['startDate'] = new CarbonImmutable(trim($params['start_date'])); |
| 240 | } catch (\Throwable $throwable) { |
| 241 | // Don't error out if invalid date |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | if (isset($params['end_date']) && is_string($params['end_date']) && $params['end_date'] !== '') { |
| 246 | try { |
| 247 | $parsedParams['endDate'] = new CarbonImmutable(trim($params['end_date'])); |
| 248 | } catch (\Throwable $throwable) { |
| 249 | // Don't error out if invalid date |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | $lastDays = $params['in_the_last_days'] ?? null; |
| 254 | if (is_scalar($lastDays) && $lastDays > 0) { |
| 255 | $parsedParams['endDate'] = null; |
| 256 | $parsedParams['startDate'] = CarbonImmutable::now()->subDays(intval($lastDays))->startOfDay(); |
| 257 | } |
| 258 | |
| 259 | if (isset($params['subject_contains']) && is_string($params['subject_contains']) && $params['subject_contains'] !== '') { |
| 260 | $parsedParams['subjectContains'] = trim($params['subject_contains']); |
| 261 | } |
| 262 | |
| 263 | $limit = $params['limit'] ?? null; |
| 264 | if (is_scalar($limit) && intval($limit) > 0) { |
| 265 | $parsedParams['limit'] = intval($limit); |
| 266 | } |
| 267 | |
| 268 | return $parsedParams; |
| 269 | } |
| 270 | |
| 271 | public function renderArchiveDate(NewsletterEntity $newsletter) { |
| 272 | $timestamp = null; |
| 273 | $processedAt = $newsletter->getProcessedAt(); |
| 274 | |
| 275 | if (!is_null($processedAt)) { |
| 276 | $timestamp = $processedAt->getTimestamp(); |
| 277 | } |
| 278 | |
| 279 | return $this->wp->dateI18n( |
| 280 | $this->wp->getOption('date_format'), |
| 281 | $timestamp |
| 282 | ); |
| 283 | } |
| 284 | |
| 285 | public function renderArchiveSubject(NewsletterEntity $newsletter, ?SubscriberEntity $subscriber, ?SendingQueueEntity $queue) { |
| 286 | if (is_null($subscriber)) { |
| 287 | $subscriber = new SubscriberEntity(); |
| 288 | } |
| 289 | |
| 290 | /** |
| 291 | * An ugly workaround to make sure state is not shared via NewsletterShortcodes service |
| 292 | * This should be replaced with injected service when state is removed from this service |
| 293 | * (i.e. after https://mailpoet.atlassian.net/browse/MAILPOET-4087 is done and merged to master) |
| 294 | */ |
| 295 | $shortcodeProcessor = new NewsletterShortcodes( |
| 296 | $this->dateCategory, |
| 297 | $this->linkCategory, |
| 298 | $this->newsletterCategory, |
| 299 | $this->subscriberCategory, |
| 300 | $this->siteCategory, |
| 301 | $this->wp |
| 302 | ); |
| 303 | |
| 304 | $shortcodeProcessor->setNewsletter($newsletter); |
| 305 | |
| 306 | $shortcodeProcessor->setSubscriber($subscriber); |
| 307 | $shortcodeProcessor->setQueue($queue); |
| 308 | $subject = (string)$shortcodeProcessor->replace($queue ? $queue->getNewsletterRenderedSubject() : ''); |
| 309 | if ($newsletter->getType() === NewsletterEntity::TYPE_STANDARD && $this->shareVisibility->canShare($newsletter)) { |
| 310 | $previewUrl = $this->newsletterUrl->getPublicShareUrl($newsletter); |
| 311 | $linkTitle = __('Read in a new tab', 'mailpoet'); |
| 312 | } else { |
| 313 | $previewUrl = $this->newsletterUrl->getViewInBrowserUrl($newsletter, $subscriber, $queue); |
| 314 | $linkTitle = __('Preview in a new tab', 'mailpoet'); |
| 315 | } |
| 316 | |
| 317 | return '<a href="' . $this->wp->escUrl($previewUrl) . '" target="_blank" rel="noopener noreferrer" title="' |
| 318 | . $this->wp->escAttr($linkTitle) . '">' |
| 319 | . $this->wp->escHtml($subject) . |
| 320 | '</a>'; |
| 321 | } |
| 322 | } |
| 323 |