PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / trunk
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress vtrunk
4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / src / ContentProtection / Frontend / PostContent.php
wp-user-avatar / src / ContentProtection / Frontend Last commit date
Checker.php 1 year ago PostContent.php 9 months ago Redirect.php 1 year ago RestrictionShortcode.php 3 months ago SearchAndAPI.php 1 year ago index.php 5 years ago restricted-template.php 1 year ago
PostContent.php
392 lines
1 <?php
2
3 namespace ProfilePress\Core\ContentProtection\Frontend;
4
5
6 use ProfilePress\Core\ContentProtection\SettingsPage;
7 use ProfilePress\Core\Classes\PROFILEPRESS_sql;
8
9 class PostContent
10 {
11 public $restrictedAccessConditions = [];
12
13 public function __construct()
14 {
15 add_filter('the_content', [$this, 'the_content'], PHP_INT_MAX - 1);
16
17 add_action('wp', function () {
18
19 $is_restricted = $this->is_post_content_restricted();
20
21 if ($is_restricted && $this->can_use_restricted_template()) {
22
23 $this->restrictedAccessConditions = $is_restricted;
24
25 if (ppress_var($is_restricted, 'noaccess_action_message_style') == 'custom_template') {
26 add_filter('template_include', [$this, 'restricted_page_template'], PHP_INT_MAX - 1);
27 }
28 }
29
30 }, -1);
31
32 // Avada theme incompatibility fix
33 add_action('awb_remove_third_party_the_content_changes', function () {
34 remove_filter('the_content', [$this, 'the_content'], PHP_INT_MAX - 1);
35 });
36
37 add_action('awb_readd_third_party_the_content_changes', function () {
38 add_filter('the_content', [$this, 'the_content'], PHP_INT_MAX - 1);
39 });
40
41 $this->uncode_theme_compatibility();
42
43 add_action('wp', function () {
44 $this->woocommerce_compatibility();
45 }, 9999);
46 }
47
48 public function uncode_theme_compatibility()
49 {
50 add_filter('uncode_apply_the_content', function () {
51 return ! is_singular('post');
52 });
53
54 if (apply_filters('uncode_mp_single_content_raw', '__return_true')) {
55
56 add_filter('uncode_get_single_content_raw', function ($content) {
57 return $this->the_content($content);
58 }, 999999, 1);
59 }
60 }
61
62 public function woocommerce_compatibility()
63 {
64 if (class_exists('\WooCommerce') && ! apply_filters('ppress_content_protection_disable_woocommerce_compatibility', false)) {
65
66 $is_restricted = $this->is_post_content_restricted();
67
68 if (false !== $is_restricted) {
69
70 add_filter('woocommerce_product_tabs', function ($tabs) {
71 return array_filter($tabs, function ($v, $k) {
72 return $k == 'description';
73 }, ARRAY_FILTER_USE_BOTH);
74 });
75
76 add_filter('woocommerce_product_description_heading', '__return_empty_string');
77
78 add_filter('woocommerce_short_description', function () {
79 $msg = esc_html__('You are not allowed to purchase this product.', 'wp-user-avatar');
80
81 return apply_filters('ppress_content_protection_woocommerce_product_purchase_error_message', $msg);
82 });
83
84 remove_action('woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30);
85 }
86
87 add_filter('woocommerce_loop_add_to_cart_link', function ($add_to_cart_html, $product) {
88
89 // is_post_content_restricted can be used because global $post; will always be WP_Post of each loop product item
90 if ($this->is_post_content_restricted(true)) {
91 return '';
92 }
93
94 return $add_to_cart_html;
95
96 }, 99, 2);
97 }
98 }
99
100 /**
101 * Checks whether current page can use custom template.
102 *
103 * @return bool
104 */
105 public function can_use_restricted_template()
106 {
107 return apply_filters('ppress_content_protection_can_use_restricted_template', is_singular());
108 }
109
110 public function restricted_page_template($template)
111 {
112 $path = dirname(__FILE__) . '/restricted-template.php';
113 if (file_exists($path)) {
114 load_template($path, true, $this->restrictedAccessConditions);
115
116 return;
117 }
118
119 return $template;
120 }
121
122 public function protection_disabled()
123 {
124 $checks = [
125 is_preview() && current_user_can('edit_post', get_the_ID()),
126 did_action('elementor/loaded') &&
127 class_exists('\Elementor\Plugin') &&
128 isset(\Elementor\Plugin::$instance) &&
129 isset(\Elementor\Plugin::$instance->preview) &&
130 method_exists(\Elementor\Plugin::$instance->preview, 'is_preview_mode') &&
131 \Elementor\Plugin::$instance->preview->is_preview_mode(),
132 ];
133
134 return apply_filters('ppress_content_protection_is_protection_disabled', in_array(true, $checks, true));
135 }
136
137 /**
138 * @param bool $skip_cache
139 *
140 * @return bool
141 */
142 public function is_post_content_restricted($skip_cache = false)
143 {
144 $callback = function () {
145
146 $is_restricted = false;
147
148 if ( ! $this->protection_disabled()) {
149
150 $metas = PROFILEPRESS_sql::get_meta_data_by_key(SettingsPage::META_DATA_KEY);
151
152 if (is_array($metas)) {
153
154 foreach ($metas as $meta) {
155
156 $meta_value = ppress_var($meta, 'meta_value', []);
157
158 if ( ! in_array(ppress_var($meta_value, 'is_active', true), ['true', true], true)) continue;
159
160 $access_condition = ppress_var($meta_value, 'access_condition', []);
161
162 /* commented this out because we want content to be restricted when accessed via rss and rest api
163 this is even redundant because redirect happens before the_content hook is fired.
164 $noaccess_action = ppress_var($access_condition, 'noaccess_action');
165
166 if ('message' != $noaccess_action) continue;
167 */
168
169 $who_can_access = ppress_var($access_condition, 'who_can_access', 'everyone');
170
171 $access_roles = ppress_var($access_condition, 'access_roles', []);
172
173 $access_wp_users = ppress_var($access_condition, 'access_wp_users', []);
174
175 $access_membership_plans = ppress_var($access_condition, 'access_membership_plans', []);
176
177 $is_new = ppress_var($meta_value, 'is_new') == 'true';
178
179 if (Checker::content_match($meta_value['content'], false, $is_new)) {
180
181 if (ppress_var($meta_value, 'exempt', []) && Checker::content_match($meta_value['exempt'])) continue;
182
183 if (Checker::is_blocked($who_can_access, $access_roles, $access_wp_users, $access_membership_plans)) {
184 $is_restricted = $access_condition;
185 }
186 break;
187 }
188 }
189 }
190 }
191
192 return $is_restricted;
193 };
194
195 if ($skip_cache === true) return $callback();
196
197 // Use post-specific cache if available
198 global $post;
199
200 if ( ! empty($post->ID)) {
201 static $cache = [];
202 if ( ! isset($cache[$post->ID])) $cache[$post->ID] = $callback();
203
204 return $cache[$post->ID];
205 }
206
207 static $cache2 = null;
208
209 if (is_null($cache2)) $cache2 = $callback();
210
211 return $cache2;
212 }
213
214 public function the_content($content)
215 {
216 $access_condition = $this->is_post_content_restricted();
217
218 if (false !== $access_condition) {
219
220 $noaccess_message_type = ppress_var($access_condition, 'noaccess_action_message_type', 'global');
221
222 $custom_message = ppress_var($access_condition, 'noaccess_action_message_custom', 'global');
223
224 $noaccess_action_message_style = ppress_var($access_condition, 'noaccess_action_message_style', 'none');
225
226 $content = $this->get_restricted_message($content, $noaccess_message_type, $custom_message, $noaccess_action_message_style);
227 }
228
229 return $content;
230 }
231
232 public function get_restricted_message($post_content, $noaccess_message_type = 'global', $custom_message = '', $message_style = 'none')
233 {
234 $message = '';
235
236 $global_message = ppress_settings_by_key(
237 'global_restricted_access_message',
238 esc_html__('You are unauthorized to view this page.', 'wp-user-avatar'),
239 true
240 );
241
242 switch ($noaccess_message_type) {
243 case 'custom':
244 $message = $this->style_paywall_message(wpautop($custom_message), $message_style);
245 break;
246 case 'post_excerpt':
247 $message = $this->get_post_excerpt($post_content);
248 break;
249 case 'post_excerpt_global':
250 $message = $this->get_post_excerpt($post_content) . $this->style_paywall_message($this->parse_message($global_message), $message_style);
251 break;
252 case 'post_excerpt_custom':
253 $message = $this->get_post_excerpt($post_content) . $this->style_paywall_message($this->parse_message($custom_message), $message_style);
254 break;
255 }
256
257 if (empty($message)) {
258 $message = $this->style_paywall_message($this->parse_message($global_message), $message_style);
259 }
260
261 return $message;
262 }
263
264 public function parse_message($message)
265 {
266 return apply_filters('ppress_content_protection_parsed_message', do_shortcode(wpautop($message)), $message);
267 }
268
269 public function style_paywall_message($message, $style = 'none')
270 {
271 if ('default' == $style) {
272 $message = sprintf('<div class="ppress-paywall-message-wrap"><div class="ppress-paywall-message">%s</div></div>', $message);
273 }
274
275 return $message;
276 }
277
278 public function get_post_excerpt($post_content)
279 {
280 global $post;
281
282 if ( ! is_object($post)) return '';
283
284 $length = apply_filters('ppress_content_protection_excerpt_length', 100);
285
286 $more = false;
287
288 if ( ! apply_filters('ppress_content_protection_ignore_post_excerpt', false) && has_excerpt($post->ID)) {
289 $the_excerpt = $post->post_excerpt;
290 } elseif (strstr($post->post_content, '<!--more-->')) {
291 $more = true;
292 $length = strpos($post->post_content, '<!--more-->');
293 $the_excerpt = $post->post_content;
294 } else {
295 $the_excerpt = $post->post_content;
296 }
297
298 $the_excerpt = self::trim_content($the_excerpt, $length, $more);
299
300 // if after trimming and stuff above, an empty content/string is return, re-trim again,
301 //this time with actual post content from _the_content filter
302 if (apply_filters('ppress_content_protection_use_filter_post_content', false, $post) || empty($the_excerpt)) {
303 $the_excerpt = self::trim_content($post_content, $length, $more);
304 }
305
306 return apply_filters('ppress_content_protection_excerpt', $the_excerpt, $post, $length);
307 }
308
309 public static function trim_content($the_excerpt = '', $length = 100, $more = false)
310 {
311 if ( ! empty($the_excerpt)) {
312
313 $tags = apply_filters('ppress_content_protection_excerpt_tags', '<style><a><img><em><i><code><ins><del><strong><blockquote><ul><ol><li><h1><h2><h3><h4><h5><h6><b><div><span>');
314
315 if ($more) {
316 $the_excerpt = strip_shortcodes(strip_tags(stripslashes(substr($the_excerpt, 0, $length)), $tags));
317 } else {
318 $the_excerpt = strip_shortcodes(strip_tags(stripslashes($the_excerpt), $tags));
319
320 // Enhanced regex pattern to support both English word boundaries and CJK (Chinese, Japanese, and Korean) character boundaries
321 $the_excerpt = preg_split('/\b|(?<=[\x{4e00}-\x{9fff}])(?=[\x{4e00}-\x{9fff}])/u', $the_excerpt, $length * 2 + 1);
322 $excerpt_waste = array_pop($the_excerpt);
323 $the_excerpt = implode($the_excerpt);
324
325 if ( ! empty($the_excerpt)) {
326
327 $ellipsis = apply_filters('ppress_content_protection_excerpt_extra', '. . .');
328
329 $the_excerpt .= $ellipsis;
330
331 // when truncated text ends with malfunctioned link eg <a href="https://hello.com, <img src="http://hey.com/img.png, remove them
332 $the_excerpt = preg_replace(sprintf("/<(img|a|em)[^>]+(%s)/", preg_quote($ellipsis, '/')), '$2', $the_excerpt);
333 }
334 }
335
336 $the_excerpt = wpautop(self::close_tags($the_excerpt));
337 }
338
339 return $the_excerpt;
340 }
341
342 /**
343 * See https://stackoverflow.com/a/3810341/2648410
344 *
345 * @param $content
346 *
347 * @return false|mixed|string
348 */
349 public static function close_tags($content)
350 {
351 if ( ! empty($content)) {
352 // remove cos it can be unreliable
353 //if (class_exists('\DOMDocument')) {
354 // $doc = new \DOMDocument();
355 // $result = $doc->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
356 /** @see https://stackoverflow.com/a/20675396/2648410 */
357 // if ($result) utf8_decode($doc->saveHTML($doc->documentElement));
358 // } else {
359 /** @see https://stackoverflow.com/a/3810341/2648410 */
360 preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $content, $result);
361 $openedtags = $result[1];
362 preg_match_all('#</([a-z]+)>#iU', $content, $result);
363 $closedtags = $result[1];
364 $len_opened = count($openedtags);
365 if (count($closedtags) == $len_opened) {
366 return $content;
367 }
368 $openedtags = array_reverse($openedtags);
369 for ($i = 0; $i < $len_opened; $i++) {
370 if ( ! in_array($openedtags[$i], $closedtags)) {
371 $content .= '</' . $openedtags[$i] . '>';
372 } else {
373 unset($closedtags[array_search($openedtags[$i], $closedtags)]);
374 }
375 }
376 }
377
378 return $content;
379 }
380
381 public static function get_instance()
382 {
383 static $instance = null;
384
385 if (is_null($instance)) {
386 $instance = new self();
387 }
388
389 return $instance;
390 }
391 }
392