PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
← All changes | lib/pinterest.php +323 -0 21.13.322.7.0 View file →
@@ -1,0 +1,323 @@
1 +<?php
2 +/*
3 + * License: GPLv3
4 + * License URI: https://www.gnu.org/licenses/gpl.txt
5 + * Copyright 2012-2026 Jean-Sebastien Morisset (https://wpsso.com/)
6 + */
7 +
8 +if ( ! defined( 'ABSPATH' ) ) {
9 +
10 + die( 'These aren\'t the droids you\'re looking for.' );
11 +}
12 +
13 +if ( ! defined( 'WPSSO_PLUGINDIR' ) ) {
14 +
15 + die( 'Do. Or do not. There is no try.' );
16 +}
17 +
18 +if ( ! class_exists( 'WpssoPinterest' ) ) {
19 +
20 + class WpssoPinterest {
21 +
22 + private $p; // Wpsso class object.
23 +
24 + /*
25 + * Note that options from the WPSSO Core setting pages and Document SSO metabox use a "p" option prefix.
26 + */
27 + public function __construct( &$plugin ) {
28 +
29 + $this->p =& $plugin;
30 +
31 + if ( $this->p->debug->enabled ) {
32 +
33 + $this->p->debug->mark();
34 + }
35 +
36 + add_action( 'init', array( $this, 'allow_img_data_attributes' ) );
37 +
38 + add_filter( 'wp_get_attachment_image_attributes', array( $this, 'add_attachment_image_attributes' ), 10, 2 );
39 + add_filter( 'get_header_image_tag', array( $this, 'get_header_image_tag' ), 10, 3 );
40 + add_filter( 'get_avatar', array( $this, 'get_avatar_image_tag' ), 10, 6 );
41 + add_filter( 'get_image_tag', array( $this, 'get_image_tag' ), 10, 6 );
42 +
43 + if ( empty( $this->p->options[ 'pin_add_img_html' ] ) ) {
44 +
45 + if ( $this->p->debug->enabled ) {
46 +
47 + $this->p->debug->log( 'pinterest hidden image is disabled' );
48 + }
49 +
50 + } else {
51 +
52 + if ( $this->p->debug->enabled ) {
53 +
54 + $this->p->debug->log( 'pinterest hidden image is enabled' );
55 + }
56 +
57 + $this->p->util->add_plugin_filters( $this, array(
58 + 'plugin_image_sizes' => 1,
59 + ) );
60 +
61 + /*
62 + * Filters the post content.
63 + *
64 + * See https://developer.wordpress.org/reference/hooks/the_content/.
65 + */
66 + add_filter( 'the_content', array( $this, 'prepend_image_html' ), PHP_INT_MAX );
67 +
68 + /*
69 + * Filters the author description, post type archive description, and the term description.
70 + *
71 + * See https://developer.wordpress.org/reference/functions/get_the_archive_description/.
72 + */
73 + add_filter( 'get_the_archive_description', array( $this, 'prepend_image_html' ), PHP_INT_MAX );
74 + }
75 + }
76 +
77 + public function allow_img_data_attributes() {
78 +
79 + global $allowedposttags;
80 +
81 + if ( ! empty( $this->p->options[ 'pin_add_nopin_media_img_tag' ] ) ) {
82 +
83 + $allowedposttags[ 'img' ][ 'data-pin-nopin' ] = true;
84 + }
85 + }
86 +
87 + /*
88 + * $attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment );
89 + */
90 + public function add_attachment_image_attributes( $attr, $attach ) {
91 +
92 + if ( ! empty( $this->p->options[ 'pin_add_nopin_media_img_tag' ] ) ) {
93 +
94 + $attr[ 'data-pin-nopin' ] = 'nopin';
95 + }
96 +
97 + return $attr;
98 + }
99 +
100 + /*
101 + * $html = apply_filters( 'get_header_image_tag', $html, $header, $attr );
102 + */
103 + public function get_header_image_tag( $html, $header, $attr ) {
104 +
105 + if ( ! empty( $this->p->options[ 'pin_add_nopin_header_img_tag' ] ) ) {
106 +
107 + $html = SucomUtil::insert_html_tag_attributes( $html, array( 'data-pin-nopin' => 'nopin' ) );
108 + }
109 +
110 + return $html;
111 + }
112 +
113 + /*
114 + * $html = apply_filters( 'get_avatar', $html, $id_or_email, $size_px, $default_type, $alt, $data_args );
115 + */
116 + public function get_avatar_image_tag( $html, $id_or_email, $size_px, $default_type, $alt, $data_args ) {
117 +
118 + if ( ! empty( $this->p->options[ 'pin_add_nopin_header_img_tag' ] ) ) {
119 +
120 + $html = SucomUtil::insert_html_tag_attributes( $html, array( 'data-pin-nopin' => 'nopin' ) );
121 + }
122 +
123 + return $html;
124 + }
125 +
126 + /*
127 + * $html = apply_filters( 'get_image_tag', $html, $id, $alt, $title, $align, $size );
128 + */
129 + public function get_image_tag( $html, $id, $alt, $title, $align, $size ) {
130 +
131 + if ( ! empty( $this->p->options[ 'pin_add_nopin_media_img_tag' ] ) ) {
132 +
133 + $html = SucomUtil::insert_html_tag_attributes( $html, array( 'data-pin-nopin' => 'nopin' ) );
134 + }
135 +
136 + return $html;
137 + }
138 +
139 + public function filter_plugin_image_sizes( array $sizes ) {
140 +
141 + if ( ! empty( $this->p->options[ 'pin_add_img_html' ] ) ) { // Just in case.
142 +
143 + $sizes[ 'pin' ] = array( // Option prefix.
144 + 'name' => 'pinterest',
145 + 'label_transl' => _x( 'Pinterest Pin It', 'option label', 'wpsso' ),
146 + );
147 + }
148 +
149 + return $sizes;
150 + }
151 +
152 + /*
153 + * Hooked to the 'woocommerce_archive_description' action.
154 + */
155 + public function show_image_html() {
156 +
157 + echo $this->prepend_image_html();
158 + }
159 +
160 + /*
161 + * Hooked to 'the_content' and 'get_the_archive_description' filters.
162 + */
163 + public function prepend_image_html( $content = '' ) {
164 +
165 + if ( $this->p->debug->enabled ) {
166 +
167 + $this->p->debug->mark();
168 + }
169 +
170 + /*
171 + * Do not add a Pinterest image if the current webpage is amp, an rss feed, we're in the loop, or WPSSO is
172 + * applying the content filter to create a Schema or meta tag description value.
173 + */
174 + if ( SucomUtilWP::is_amp() ) {
175 +
176 + if ( $this->p->debug->enabled ) {
177 +
178 + $this->p->debug->log( 'exiting early: is amp' );
179 + }
180 +
181 + return $content; // Stop here.
182 +
183 + } elseif ( is_feed() ) {
184 +
185 + if ( $this->p->debug->enabled ) {
186 +
187 + $this->p->debug->log( 'exiting early: is feed' );
188 + }
189 +
190 + return $content; // Stop here.
191 +
192 + /*
193 + * Do not add a Pinterest image for individual posts within an archive page.
194 + *
195 + * Note that in_the_loop() can be true in both archive and singular pages.
196 + */
197 + } elseif ( is_archive() && in_the_loop() ) {
198 +
199 + if ( $this->p->debug->enabled ) {
200 +
201 + $this->p->debug->log( 'exiting early: archive page and in the loop' );
202 + }
203 +
204 + return $content; // Stop here.
205 +
206 + } elseif ( ! empty( $GLOBALS[ 'wpsso_doing_filter_the_content' ] ) ) {
207 +
208 + if ( $this->p->debug->enabled ) {
209 +
210 + $this->p->debug->log( 'exiting early: doing the content filter' );
211 + }
212 +
213 + return $content; // Stop here.
214 + }
215 +
216 + if ( $this->p->debug->enabled ) {
217 +
218 + $this->p->debug->log( 'required call to WpssoPage->get_mod()' );
219 + }
220 +
221 + /*
222 + * Note that in_the_loop() can be true in both archive and singular pages.
223 + */
224 + $use_post = apply_filters( 'wpsso_use_post', in_the_loop() ? true : false );
225 +
226 + $mod = $this->p->page->get_mod( $use_post ); // $use_post is true by default.
227 +
228 + $image_html = $this->get_mod_image_html( $mod );
229 +
230 + return $image_html . $content;
231 + }
232 +
233 + /*
234 + * See WpssoPinterest->prepend_image_html().
235 + */
236 + public function get_mod_image_html( array $mod ) {
237 +
238 + if ( $this->p->debug->enabled ) {
239 +
240 + $this->p->debug->mark();
241 + }
242 +
243 + $image_html = '';
244 +
245 + if ( empty( $mod[ 'name' ] ) || empty( $mod[ 'id' ] ) ) {
246 +
247 + if ( $this->p->debug->enabled ) {
248 +
249 + $this->p->debug->log( 'exiting early: mod name or id is empty' );
250 + }
251 +
252 + return $image_html; // Stop here.
253 + }
254 +
255 + /*
256 + * Note that the sort order, page number, locale, amp and embed checks are provided by
257 + * WpssoHead->get_head_cache_index() and not SucomUtil::get_mod_salt().
258 + */
259 + $cache_salt = SucomUtil::get_mod_salt( $mod );
260 +
261 + static $do_once = array(); // Just in case - prevent recursion and duplicate CSS IDs in the webpage.
262 +
263 + if ( ! empty( $do_once[ $cache_salt ] ) ) {
264 +
265 + if ( $this->p->debug->enabled ) {
266 +
267 + $this->p->debug->log( 'exiting early: already added for ' . $cache_salt );
268 + }
269 +
270 + return $image_html; // Stop here.
271 + }
272 +
273 + $do_once[ $cache_salt ] = true; // Set early to prevent recursion.
274 +
275 + $size_name = 'wpsso-pinterest';
276 + $mt_images = $this->p->media->get_all_images( $num = 1, $size_name, $mod, $md_pre = array( 'pin', 'schema', 'og' ) );
277 + $image_url = SucomUtil::get_first_mt_media_url( $mt_images );
278 + $css_id = 'pin-it-' . SucomUtil::sanitize_css_id( $cache_salt );
279 +
280 + /*
281 + * Avoid newline characters and HTML comments as they can be wrapped in paragraph tags by some WordPress filters.
282 + */
283 + $image_html = '<div class="wpsso-pinterest-image" id="' . $css_id . '" style="width:0;height:0;display:none !important;">';
284 +
285 + if ( empty( $image_url ) ) {
286 +
287 + if ( $this->p->debug->enabled ) {
288 +
289 + $this->p->debug->log( 'image URL for pinterest is empty' );
290 + }
291 +
292 + } else {
293 +
294 + if ( $this->p->debug->enabled ) {
295 +
296 + $this->p->debug->log( 'adding image URL for pinterest = ' . $image_url );
297 + }
298 +
299 + $data_pin_desc = $this->p->page->get_description( $mod, $md_key = 'pin_img_desc', $max_len = 'pin_img_desc', $num_hashtags = true );
300 +
301 + if ( $this->p->debug->enabled ) {
302 +
303 + $this->p->debug->log( 'pinterest data pin description = ' . $data_pin_desc );
304 + }
305 +
306 + /*
307 + * Note that an empty alt attribute is required for W3C validation.
308 + *
309 + * Adding a 'loading="lazy"' attribute breaks the Pinterest Save button.
310 + *
311 + * The 'skip-lazy' class is used by WP Rocket to skip the lazy loading of an image.
312 + */
313 + $image_html .= '<img src="' . SucomUtil::esc_url_encode( $image_url ) . '" ' .
314 + 'width="0" height="0" class="skip-lazy" style="width:0;height:0;" alt="" ' .
315 + 'data-pin-description="' . esc_attr( $data_pin_desc ) . '" />';
316 + }
317 +
318 + $image_html .= '</div>';
319 +
320 + return $image_html;
321 + }
322 + }
323 +}