LayoutHelper.php
3 years ago
MetaInformationManager.php
2 months ago
PostContentManager.php
11 months ago
PostListTransformer.php
3 years ago
PostTransformer.php
1 year ago
PostTransformerContentsExtractor.php
2 months ago
StructureTransformer.php
3 years ago
TitleListTransformer.php
1 year ago
Transformer.php
3 years ago
index.php
3 years ago
PostContentManager.php
207 lines
| 1 | <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing |
| 2 | |
| 3 | namespace MailPoet\Newsletter\Editor; |
| 4 | |
| 5 | if (!defined('ABSPATH')) exit; |
| 6 | |
| 7 | |
| 8 | use MailPoet\Util\pQuery\pQuery; |
| 9 | use MailPoet\WooCommerce\Helper as WooCommerceHelper; |
| 10 | use MailPoet\WP\Functions as WPFunctions; |
| 11 | |
| 12 | class PostContentManager { |
| 13 | const WP_POST_CLASS = 'mailpoet_wp_post'; |
| 14 | |
| 15 | public $maxExcerptLength = 60; |
| 16 | |
| 17 | /** @var WooCommerceHelper */ |
| 18 | private $woocommerceHelper; |
| 19 | |
| 20 | /** @var WPFunctions */ |
| 21 | private $wp; |
| 22 | |
| 23 | public function __construct( |
| 24 | ?WooCommerceHelper $woocommerceHelper = null, |
| 25 | ?WPFunctions $wp = null |
| 26 | ) { |
| 27 | $this->wp = $wp ?: new WPFunctions; |
| 28 | $this->maxExcerptLength = $this->wp->applyFilters('mailpoet_newsletter_post_excerpt_length', $this->maxExcerptLength); |
| 29 | $this->woocommerceHelper = $woocommerceHelper ?: new WooCommerceHelper($this->wp); |
| 30 | } |
| 31 | |
| 32 | public function getContent($post, $displayType) { |
| 33 | if ($displayType === 'titleOnly') { |
| 34 | return ''; |
| 35 | } |
| 36 | |
| 37 | |
| 38 | |
| 39 | if ($this->woocommerceHelper->isWooCommerceActive()) { |
| 40 | if ($this->isWcProduct($post)) { |
| 41 | return $this->getContentForProduct($post, $displayType); |
| 42 | } |
| 43 | if ($this->wp->getPostType($post) === 'product') { |
| 44 | $product = $this->woocommerceHelper->wcGetProduct($post->ID); |
| 45 | if ($product) { |
| 46 | return $this->getContentForProduct($product, $displayType); |
| 47 | } |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | if ($displayType === 'excerpt') { |
| 52 | if ($this->wp->hasExcerpt($post)) { |
| 53 | $content = self::stripShortCodes($this->wp->getTheExcerpt($post)); |
| 54 | return $this->fixAnchorLinks($content, $post); |
| 55 | } |
| 56 | $content = self::stripShortCodes( |
| 57 | $this->wp->applyFilters( |
| 58 | 'get_the_excerpt', |
| 59 | $this->generateExcerpt($post->post_content), // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps |
| 60 | $post |
| 61 | ) |
| 62 | ); |
| 63 | return $this->fixAnchorLinks($content, $post); |
| 64 | } |
| 65 | $content = self::stripShortCodes($post->post_content); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps |
| 66 | return $this->fixAnchorLinks($content, $post); |
| 67 | } |
| 68 | |
| 69 | public function filterContent($content, $displayType, $withPostClass = true) { |
| 70 | $content = self::convertEmbeddedContent($content); |
| 71 | |
| 72 | // convert h4 h5 h6 to h3 |
| 73 | $content = preg_replace('/<([\/])?h[456](.*?)>/', '<$1h3$2>', $content); |
| 74 | |
| 75 | // convert currency signs |
| 76 | $content = str_replace( |
| 77 | ['$', '€', '£', '¥'], |
| 78 | ['$', '€', '£', '¥'], |
| 79 | $content |
| 80 | ); |
| 81 | |
| 82 | // strip useless tags |
| 83 | $tagsNotBeingStripped = [ |
| 84 | '<p>', '<em>', '<span>', '<b>', '<strong>', '<i>', |
| 85 | '<a>', '<ul>', '<ol>', '<li>', '<br>', '<blockquote>', |
| 86 | ]; |
| 87 | if ($displayType === 'full') { |
| 88 | $tagsNotBeingStripped = array_merge($tagsNotBeingStripped, ['<figure>', '<img>', '<h1>', '<h2>', '<h3>', '<hr>']); |
| 89 | } |
| 90 | |
| 91 | if (is_array($content)) { |
| 92 | $content = implode(' ', $content); |
| 93 | } |
| 94 | |
| 95 | $content = strip_tags($content, implode('', $tagsNotBeingStripped)); |
| 96 | if ($withPostClass) { |
| 97 | $dOMParser = new pQuery(); |
| 98 | $DOM = $dOMParser->parseStr(WPFunctions::get()->wpautop($content)); |
| 99 | $paragraphs = $DOM->query('p'); |
| 100 | foreach ($paragraphs as $paragraph) { |
| 101 | // We replace the class attribute to avoid conflicts in the newsletter editor |
| 102 | $paragraph->removeAttr('class'); |
| 103 | $paragraph->addClass(self::WP_POST_CLASS); |
| 104 | } |
| 105 | $content = $DOM->__toString(); |
| 106 | } else { |
| 107 | $content = WPFunctions::get()->wpautop($content); |
| 108 | } |
| 109 | $content = trim($content); |
| 110 | |
| 111 | return $content; |
| 112 | } |
| 113 | |
| 114 | private function getContentForProduct($product, $displayType) { |
| 115 | if ($displayType === 'excerpt') { |
| 116 | return $product->get_short_description(); |
| 117 | } |
| 118 | return $product->get_description(); |
| 119 | } |
| 120 | |
| 121 | private function generateExcerpt($content) { |
| 122 | // remove image captions in gutenberg |
| 123 | $content = preg_replace( |
| 124 | "/<figcaption.*?>.*?<\/figcaption>/", |
| 125 | '', |
| 126 | $content |
| 127 | ); |
| 128 | // remove image captions in classic posts |
| 129 | $content = preg_replace( |
| 130 | "/\[caption.*?\](.*?)\[\/caption\]/", |
| 131 | '', |
| 132 | $content |
| 133 | ); |
| 134 | |
| 135 | $content = self::stripShortCodes($content); |
| 136 | |
| 137 | // if excerpt is empty then try to find the "more" tag |
| 138 | $excerpts = explode('<!--more-->', $content); |
| 139 | if (count($excerpts) > 1) { |
| 140 | // <!--more--> separator was present |
| 141 | return $excerpts[0]; |
| 142 | } else { |
| 143 | // Separator not present, try to shorten long posts |
| 144 | return WPFunctions::get()->wpTrimWords($content, $this->maxExcerptLength, ' …'); |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | private function stripShortCodes($content) { |
| 149 | // remove captions |
| 150 | $content = preg_replace( |
| 151 | "/\[caption.*?\](.*<\/a>)(.*?)\[\/caption\]/", |
| 152 | '$1', |
| 153 | $content |
| 154 | ); |
| 155 | |
| 156 | // remove other shortcodes |
| 157 | $content = preg_replace('/\[[^\[\]]*\]/', '', $content); |
| 158 | |
| 159 | return $content; |
| 160 | } |
| 161 | |
| 162 | private function convertEmbeddedContent($content = '') { |
| 163 | // remove embedded video and replace with links |
| 164 | $content = preg_replace( |
| 165 | '#<iframe.*?src=\"(.+?)\".*><\/iframe>#', |
| 166 | '<a href="$1">' . __('Click here to view media.', 'mailpoet') . '</a>', |
| 167 | $content |
| 168 | ); |
| 169 | |
| 170 | // replace youtube links |
| 171 | $content = preg_replace( |
| 172 | '#http://www.youtube.com/embed/([a-zA-Z0-9_-]*)#Ui', |
| 173 | 'http://www.youtube.com/watch?v=$1', |
| 174 | $content |
| 175 | ); |
| 176 | |
| 177 | return $content; |
| 178 | } |
| 179 | |
| 180 | private function isWcProduct($post) { |
| 181 | return class_exists('\WC_Product') && $post instanceof \WC_Product; |
| 182 | } |
| 183 | |
| 184 | private function fixAnchorLinks($content, $post) { |
| 185 | if (empty($content) || !$post) { |
| 186 | return $content; |
| 187 | } |
| 188 | |
| 189 | // Get the post's permalink to use as base URL |
| 190 | $postUrl = $this->wp->getPermalink($post); |
| 191 | if (!$postUrl) { |
| 192 | return $content; |
| 193 | } |
| 194 | |
| 195 | // Find and replace anchor-only links (href="#...") with full URLs |
| 196 | $content = preg_replace_callback( |
| 197 | '/(<a[^>]+href=["\'])#([^"\']*)(["\'])/i', |
| 198 | function($matches) use ($postUrl) { |
| 199 | return $matches[1] . $postUrl . '#' . $matches[2] . $matches[3]; |
| 200 | }, |
| 201 | $content |
| 202 | ); |
| 203 | |
| 204 | return $content; |
| 205 | } |
| 206 | } |
| 207 |