PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / trunk
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More vtrunk
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
content-control / classes / Controllers / Frontend / Restrictions / PostContent.php

PostContent.php in Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More trunk, at classes/Controllers/Frontend/Restrictions/PostContent.php

243 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Frontend post content restrictions.
4 *
5 * @copyright (c) 2023, Code Atlantic LLC.
6 * @package ContentControl
7 */
8
9 namespace ContentControl\Controllers\Frontend\Restrictions;
10
11 use ContentControl\Base\Controller;
12
13 use function ContentControl\content_is_restricted;
14 use function ContentControl\protection_is_disabled;
15 use function ContentControl\get_applicable_restriction;
16
17 defined( 'ABSPATH' ) || exit;
18
19 /**
20 * Class for handling global restrictions of the post contents.
21 *
22 * This controller participates in WordPress's content-filter pipeline and
23 * intentionally returns rendered HTML. Restricted messages run through the
24 * same block, embed, shortcode, and media filters as `the_content`. Applying
25 * KSES after those filters would remove functional forms, embeds, SVG, and
26 * script-backed shortcode output. Values returned by the documented filters
27 * are supplied by trusted plugin/theme code.
28 *
29 * @package ContentControl
30 */
31 class PostContent extends Controller {
32
33 /**
34 * Initiate functionality.
35 */
36 public function init() {
37 $this->enable_filters();
38 }
39
40 /**
41 * Enable filters.
42 *
43 * @return void
44 */
45 public function enable_filters() {
46 add_filter( 'the_content', [ $this, 'filter_the_content_if_restricted' ], 1000 );
47 add_filter( 'get_the_excerpt', [ $this, 'filter_the_excerpt_if_restricted' ], 1000, 2 );
48 // phpcs:disable Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.InlineComment.InvalidEndChar -- These are for future use.
49 // add_filter( 'the_title', [ $this, 'filter_the_title_if_restricted'], 1000, 2 );
50 // add_filter( 'get_the_excerpt', [ $this, 'filter_the_excerpt_if_restricted' ], 1000, 2 );
51 // add_filter( 'post_class', [ $this, 'filter_post_class_if_restricted' ], 1000, 3 );
52 // add_filter( 'post_password_required', [ $this, 'require_password_if_restricted' ], 1000, 2 );
53 // add_filter( 'the_password_form', [ $this, 'filter_password_form_if_restricted' ], 1000, 2 );
54 // phpcs:enable Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.InlineComment.InvalidEndChar
55 }
56
57 /**
58 * Disable filters.
59 *
60 * @return void
61 */
62 public function disable_filters() {
63 remove_filter( 'the_content', [ $this, 'filter_the_content_if_restricted' ], 1000 );
64 remove_filter( 'get_the_excerpt', [ $this, 'filter_the_excerpt_if_restricted' ], 1000 );
65 }
66
67 /**
68 * Filter post content when needed.
69 *
70 * NOTE: If we got this far with restricted content, this is the last attempt to protect
71 * it. This serves as the default fallback protection method if all others fail.
72 *
73 * @param string $content Content of post being checked.
74 *
75 * @return string
76 */
77 public function filter_the_content_if_restricted( $content ) {
78 $filter_name = 'content_control/restricted_post_content';
79
80 // If this isn't a post type that can be restricted, bail.
81 if ( protection_is_disabled() ) {
82 return $content;
83 }
84
85 if ( ! content_is_restricted() ) {
86 return $content;
87 }
88
89 // Ensure we don't get into an infinite loop.
90 if ( doing_filter( $filter_name ) || doing_filter( 'get_the_excerpt' ) ) {
91 return $content;
92 }
93
94 $restriction = get_applicable_restriction();
95
96 if ( false === $restriction ) {
97 return $content;
98 }
99
100 // If this is a replacement page, bail.
101 if (
102 ( 'replace' === $restriction->get_setting( 'protectionMethod' ) && 'page' === $restriction->get_setting( 'replacementType' ) && is_page( $restriction->get_setting( 'replacementPage' ) ) ) ||
103 ( 'replace_archive_page' === $restriction->get_setting( 'archiveHandling' ) && is_page( $restriction->get_setting( 'archiveReplacementPage' ) ) )
104 ) {
105 return $content;
106 }
107
108 /**
109 * Prevent the default restriction message from being shown by returning a custom
110 * message or content.
111 *
112 * @param null|string $pre_restrict_content The content to display.
113 * @param string $content The content.
114 * @param \ContentControl\Models\Restriction $restriction The restriction.
115 *
116 * @return string
117 */
118 $pre_restrict_content = apply_filters( 'content_control/pre_restrict_content', null, $content, $restriction );
119
120 if ( null !== $pre_restrict_content ) {
121 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentional rendered HTML returned to the_content.
122 return $pre_restrict_content;
123 }
124
125 $message = \ContentControl\get_default_denial_message();
126
127 /**
128 * If the restriction has a custom message, use it.
129 *
130 * We could check $restriction->replacement_type, but we need a safe default for
131 * all cases. Further we do content filtering for all sub queries and currently
132 * don't offer a way to override the message for those.
133 *
134 * In this way currently users can change to content replacement, set the override
135 * message, then change back to page replacement and the override message will still
136 * be used for the post in sub queries.
137 */
138 if ( $restriction->get_setting( 'overrideMessage' ) ) {
139 $message = $restriction->get_message();
140 }
141
142 /**
143 * Filter the message to display when a post is restricted.
144 *
145 * @param string $message Message to display.
146 * @param \ContentControl\Models\Restriction $restriction The restriction.
147 * @param string $content The original filtered content.
148 *
149 * @return string
150 */
151 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentional rendered HTML returned to the_content.
152 return apply_filters(
153 $filter_name,
154 // If the default message is empty, show a generic message.
155 ! empty( $message ) ? $message : __( 'This content is restricted.', 'content-control' ),
156 $restriction,
157 $content
158 );
159 }
160
161 /**
162 * Filter post excerpt when needed.
163 *
164 * @param string $post_excerpt The post excerpt.
165 * @param \WP_Post $post Post object.
166 *
167 * @return string
168 */
169 public function filter_the_excerpt_if_restricted( $post_excerpt, $post = null ) {
170 $filter_name = 'content_control/restricted_post_excerpt';
171
172 // If this isn't a post type that can be restricted, bail.
173 if ( protection_is_disabled() ) {
174 return $post_excerpt;
175 }
176
177 if ( ! content_is_restricted( $post->ID ) ) {
178 return $post_excerpt;
179 }
180
181 if ( doing_filter( $filter_name ) ) {
182 return $post_excerpt;
183 }
184
185 $restriction = get_applicable_restriction( $post->ID );
186
187 if ( false === $restriction ) {
188 return $post_excerpt;
189 }
190
191 /**
192 * Filter to bypass the default restriction message for excerpts.
193 *
194 * @since 2.6.2
195 *
196 * @param null|string $pre_excerpt Return a non-null value to bypass.
197 * @param string $post_excerpt The original excerpt.
198 * @param \ContentControl\Models\Restriction $restriction The restriction object.
199 */
200 $pre_restrict_excerpt = apply_filters( 'content_control/pre_restrict_excerpt', null, $post_excerpt, $restriction );
201
202 if ( null !== $pre_restrict_excerpt ) {
203 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentional rendered HTML returned to the excerpt filter.
204 return $pre_restrict_excerpt;
205 }
206
207 $message = \ContentControl\get_default_denial_message();
208
209 /**
210 * If the restriction has a custom message, use it.
211 *
212 * We could check $restriction->replacement_type, but we need a safe default for
213 * all cases. Further we do content filtering for all sub queries and currently
214 * don't offer a way to override the message for those.
215 *
216 * In this way currently users can change to content replacement, set the override
217 * message, then change back to page replacement and the override message will still
218 * be used for the post in sub queries.
219 */
220 if ( $restriction->get_setting( 'overrideMessage' ) ) {
221 $message = $restriction->get_message();
222 }
223
224 /**
225 * Filter the excerpt message to display when a post is restricted.
226 *
227 * @param string $message Message to display.
228 * @param \ContentControl\Models\Restriction $restriction The restriction.
229 * @param string $post_excerpt The original filtered excerpt.
230 *
231 * @return string
232 */
233 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Intentional rendered HTML returned to the excerpt filter.
234 return apply_filters(
235 $filter_name,
236 // If the default message is empty, show a generic message.
237 ! empty( $message ) ? $message : __( 'This content is restricted.', 'content-control' ),
238 $restriction,
239 $post_excerpt
240 );
241 }
242 }
243