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