← All changes
|
admin/class-convertkit-admin-restrict-content.php
+169
-107
2.2.6
→
3.4.3
View file →
| @@ -17,8 +17,26 @@ | ||
| 17 | 17 | */ |
| 18 | 18 | class ConvertKit_Admin_Restrict_Content { |
| 19 | 19 | |
| 20 | 20 | /** |
| 21 | + * Holds the ConvertKit Forms resource class. | |
| 22 | + * | |
| 23 | + * @since 2.7.3 | |
| 24 | + * | |
| 25 | + * @var bool|ConvertKit_Resource_Forms | |
| 26 | + */ | |
| 27 | + public $forms = false; | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * Holds the ConvertKit Tags resource class. | |
| 31 | + * | |
| 32 | + * @since 2.3.2 | |
| 33 | + * | |
| 34 | + * @var bool|ConvertKit_Resource_Tags | |
| 35 | + */ | |
| 36 | + public $tags = false; | |
| 37 | + | |
| 38 | + /** | |
| 21 | 39 | * Holds the ConvertKit Products resource class. |
| 22 | 40 | * |
| 23 | 41 | * @since 2.1.0 |
| 24 | 42 | * |
| @@ -45,8 +63,17 @@ | ||
| 45 | 63 | */ |
| 46 | 64 | public $restrict_content_filter = 0; |
| 47 | 65 | |
| 48 | 66 | /** |
| 67 | + * Holds the key for the restrict content enabled flag in the WordPress options table. | |
| 68 | + * | |
| 69 | + * @since 3.0.4 | |
| 70 | + * | |
| 71 | + * @var string | |
| 72 | + */ | |
| 73 | + public $restrict_content_enabled_key = 'convertkit_restrict_content_enabled'; | |
| 74 | + | |
| 75 | + /** | |
| 49 | 76 | * Registers action and filter hooks. |
| 50 | 77 | * |
| 51 | 78 | * @since 2.1.0 |
| 52 | 79 | */ |
| @@ -51,52 +78,81 @@ | ||
| 51 | 78 | * @since 2.1.0 |
| 52 | 79 | */ |
| 53 | 80 | public function __construct() { |
| 54 | 81 | |
| 55 | - // Initialize classes that will be used. | |
| 56 | - $this->restrict_content_settings = new ConvertKit_Settings_Restrict_Content(); | |
| 82 | + // Add New Member Content Wizard button to Pages. | |
| 83 | + add_filter( 'convertkit_admin_post_register_add_new_buttons', array( $this, 'register_add_new_button' ), 10, 2 ); | |
| 57 | 84 | |
| 58 | - // Bail if Restrict Content isn't enabled. | |
| 59 | - if ( ! $this->restrict_content_settings->enabled() ) { | |
| 60 | - return; | |
| 61 | - } | |
| 85 | + // Filter Page's post state to maybe include a label denoting that Restricted Content is enabled. | |
| 86 | + // We do this here so we don't run this on Post Types that don't support Restricted Content. | |
| 87 | + add_filter( 'display_post_states', array( $this, 'maybe_display_restrict_content_post_state' ), 10, 2 ); | |
| 62 | 88 | |
| 63 | - // Add New Member Content button. | |
| 64 | - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts_and_css' ) ); | |
| 65 | - foreach ( convertkit_get_supported_restrict_content_post_types() as $post_type ) { | |
| 66 | - add_filter( 'views_edit-' . $post_type, array( $this, 'output_wp_list_table_buttons' ) ); | |
| 67 | - } | |
| 68 | - | |
| 69 | 89 | // Filter WP_List_Table by Restrict Content setting. |
| 70 | 90 | add_action( 'pre_get_posts', array( $this, 'filter_wp_list_table_output' ) ); |
| 71 | 91 | add_action( 'restrict_manage_posts', array( $this, 'output_wp_list_table_filters' ) ); |
| 72 | 92 | |
| 93 | + // Update whether any Pages, Posts or CPTs are configured to use Restrict Content when a Page, Post or CPT is created, edited, trashed or deleted. | |
| 94 | + add_action( 'wp_insert_post', array( $this, 'update_restrict_content_enabled' ) ); | |
| 95 | + add_action( 'trashed_post', array( $this, 'update_restrict_content_enabled' ) ); | |
| 96 | + add_action( 'delete_post', array( $this, 'update_restrict_content_enabled' ) ); | |
| 97 | + | |
| 73 | 98 | } |
| 74 | 99 | |
| 75 | 100 | /** |
| 76 | - * Enqueue JavaScript and CSS when viewing a list of Pages, Posts or Custom Post Types | |
| 77 | - * in a WP_List_Table that supports Restrict Content functionality. | |
| 101 | + * Update whether any Pages, Posts or CPTs are configured to use Restrict Content when a | |
| 102 | + * Page, Post or CPT is created, edited or deleted. | |
| 78 | 103 | * |
| 79 | - * @since 2.1.0 | |
| 104 | + * @since 3.0.4 | |
| 80 | 105 | */ |
| 81 | - public function enqueue_scripts_and_css() { | |
| 106 | + public function update_restrict_content_enabled() { | |
| 82 | 107 | |
| 83 | - // Bail if we're not on a WP_List_Table screen for a supported Post Type. | |
| 84 | - if ( ! $this->is_wp_list_table_request_for_supported_post_type() ) { | |
| 85 | - return; | |
| 86 | - } | |
| 108 | + global $wpdb; | |
| 87 | 109 | |
| 88 | - // Enqueue JS and CSS. | |
| 89 | - wp_enqueue_script( 'convertkit-admin-wp-list-table-buttons', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/wp-list-table-buttons.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true ); | |
| 90 | - wp_enqueue_style( 'convertkit-admin-wp-list-table-buttons', CONVERTKIT_PLUGIN_URL . 'resources/backend/css/wp-list-table-buttons.css', array(), CONVERTKIT_PLUGIN_VERSION ); | |
| 110 | + // We don't use WP_Query, as it's expensive on sites with many Pages/Posts/CPTs. | |
| 111 | + $results = $wpdb->get_results( | |
| 112 | + $wpdb->prepare( | |
| 113 | + 'SELECT ' . $wpdb->posts . '.ID | |
| 114 | + FROM ' . $wpdb->postmeta . ' | |
| 115 | + LEFT JOIN ' . $wpdb->posts . ' ON ' . $wpdb->postmeta . '.post_id = ' . $wpdb->posts . ".ID | |
| 116 | + WHERE ( | |
| 117 | + (meta_key = '_wp_convertkit_post_meta' AND meta_value LIKE %s) | |
| 118 | + AND | |
| 119 | + (meta_key = '_wp_convertkit_post_meta' AND meta_value NOT LIKE %s) | |
| 120 | + AND | |
| 121 | + (meta_key = '_wp_convertkit_post_meta' AND meta_value NOT LIKE %s) | |
| 122 | + ) | |
| 123 | + AND | |
| 124 | + " . $wpdb->posts . ".post_status = 'publish' | |
| 125 | + AND | |
| 126 | + " . $wpdb->posts . ".post_type IN ('" . implode( "', '", convertkit_get_supported_post_types() ) . "') LIMIT 1;", // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.QuotedDynamicPlaceholderGeneration | |
| 127 | + '%' . $wpdb->esc_like( '"restrict_content";' ) . '%', | |
| 128 | + '%' . $wpdb->esc_like( '"restrict_content";s:0:' ) . '%', | |
| 129 | + '%' . $wpdb->esc_like( '"restrict_content";s:1:"0"' ) . '%' | |
| 130 | + ) | |
| 131 | + ); | |
| 91 | 132 | |
| 92 | - // Filter Page's post state to maybe include a label denoting that Restricted Content is enabled. | |
| 93 | - // We do this here so we don't run this on Post Types that don't support Restricted Content. | |
| 94 | - add_filter( 'display_post_states', array( $this, 'maybe_display_restrict_content_post_state' ), 10, 2 ); | |
| 133 | + // If results are not empty, a Page, Post or CPT is configured to use Restrict Content. | |
| 134 | + $restrict_content_enabled = ! empty( $results ); | |
| 95 | 135 | |
| 136 | + // Update option. | |
| 137 | + update_option( $this->restrict_content_enabled_key, $restrict_content_enabled ); | |
| 138 | + | |
| 96 | 139 | } |
| 97 | 140 | |
| 98 | 141 | /** |
| 142 | + * Returns whether any Pages, Posts or CPTs are configured to use Restrict Content. | |
| 143 | + * | |
| 144 | + * @since 3.0.4 | |
| 145 | + * | |
| 146 | + * @return bool | |
| 147 | + */ | |
| 148 | + public function restrict_content_enabled() { | |
| 149 | + | |
| 150 | + return (bool) get_option( $this->restrict_content_enabled_key, false ); | |
| 151 | + | |
| 152 | + } | |
| 153 | + | |
| 154 | + /** | |
| 99 | 155 | * Query Pages in the WP_List_Table by the Restrict Content filter, if a filter |
| 100 | 156 | * was included in the request. |
| 101 | 157 | * |
| 102 | 158 | * @since 2.1.0 |
| @@ -105,42 +161,58 @@ | ||
| 105 | 161 | */ |
| 106 | 162 | public function filter_wp_list_table_output( $query ) { |
| 107 | 163 | |
| 108 | 164 | // Bail if no Restrict Content filter specified. |
| 109 | - if ( ! array_key_exists( 'convertkit_restrict_content', $_REQUEST ) ) { // phpcs:ignore WordPress.Security.NonceVerification | |
| 165 | + if ( ! filter_has_var( INPUT_GET, 'convertkit_restrict_content' ) ) { | |
| 110 | 166 | return; |
| 111 | 167 | } |
| 112 | - if ( ! $_REQUEST['convertkit_restrict_content'] ) { // phpcs:ignore WordPress.Security.NonceVerification | |
| 168 | + | |
| 169 | + // Bail if the filter is empty. | |
| 170 | + if ( empty( filter_input( INPUT_GET, 'convertkit_restrict_content', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ) ) { | |
| 113 | 171 | return; |
| 114 | 172 | } |
| 115 | 173 | |
| 116 | 174 | // Don't filter if we're not querying a Post Type that supports Restricted Content. |
| 117 | - if ( ! in_array( $query->get( 'post_type' ), convertkit_get_supported_restrict_content_post_types(), true ) ) { | |
| 175 | + if ( ! in_array( $query->get( 'post_type' ), convertkit_get_supported_post_types(), true ) ) { | |
| 118 | 176 | return; |
| 119 | 177 | } |
| 120 | 178 | |
| 121 | - // Build query. | |
| 122 | - // Because WordPress stores metadata in a single serialized string for a single key, we have to search | |
| 123 | - // the string for the Restrict Content setting. However, other settings will also be in this serialized string, | |
| 124 | - // so to avoid false positives, we define our search value formatted as a serialized string comprising of the | |
| 125 | - // setting name and value, to be as accurate as possible. | |
| 126 | - $value = maybe_serialize( | |
| 127 | - array( | |
| 128 | - 'restrict_content' => sanitize_text_field( $_REQUEST['convertkit_restrict_content'] ), // phpcs:ignore WordPress.Security.NonceVerification | |
| 129 | - ) | |
| 130 | - ); | |
| 179 | + // Store Restrict Content filter value. | |
| 180 | + $this->restrict_content_filter = filter_input( INPUT_GET, 'convertkit_restrict_content', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); | |
| 131 | 181 | |
| 132 | - // Strip a:1:{ and final }, as a Post's serialized settings will include other settings. | |
| 133 | - $value = str_replace( 'a:1:{', '', $value ); // e.g. s:16:"restrict_content";s:13:"product_36377";}. | |
| 134 | - $value = substr( $value, 0, strlen( $value ) - 1 ); // e.g. s:16:"restrict_content";s:13:"product_36377";. | |
| 182 | + switch ( $this->restrict_content_filter ) { | |
| 183 | + case 'all-member-only': | |
| 184 | + $meta_query = array( | |
| 185 | + 'key' => '_wp_convertkit_post_meta', | |
| 186 | + 'value' => '.*?(form|tag|product)_[0-9]+.*?', | |
| 187 | + 'compare' => 'REGEXP', | |
| 188 | + ); | |
| 189 | + break; | |
| 190 | + default: | |
| 191 | + // Build query. | |
| 192 | + // Because WordPress stores metadata in a single serialized string for a single key, we have to search | |
| 193 | + // the string for the Restrict Content setting. However, other settings will also be in this serialized string, | |
| 194 | + // so to avoid false positives, we define our search value formatted as a serialized string comprising of the | |
| 195 | + // setting name and value, to be as accurate as possible. | |
| 196 | + $value = maybe_serialize( | |
| 197 | + array( | |
| 198 | + 'restrict_content' => $this->restrict_content_filter, | |
| 199 | + ) | |
| 200 | + ); | |
| 135 | 201 | |
| 136 | - // Add value to query. | |
| 137 | - $meta_query = array( | |
| 138 | - 'key' => '_wp_convertkit_post_meta', | |
| 139 | - 'value' => $value, | |
| 140 | - 'compare' => 'LIKE', | |
| 141 | - ); | |
| 202 | + // Strip a:1:{ and final }, as a Post's serialized settings will include other settings. | |
| 203 | + $value = str_replace( 'a:1:{', '', $value ); // e.g. s:16:"restrict_content";s:13:"product_36377";}. | |
| 204 | + $value = substr( $value, 0, strlen( $value ) - 1 ); // e.g. s:16:"restrict_content";s:13:"product_36377";. | |
| 142 | 205 | |
| 206 | + // Add value to query. | |
| 207 | + $meta_query = array( | |
| 208 | + 'key' => '_wp_convertkit_post_meta', | |
| 209 | + 'value' => $value, | |
| 210 | + 'compare' => 'LIKE', | |
| 211 | + ); | |
| 212 | + break; | |
| 213 | + } | |
| 214 | + | |
| 143 | 215 | // If the existing meta query is an array, append our query to it, so we honor |
| 144 | 216 | // any other constraints that have been defined by WordPress or third party code. |
| 145 | 217 | $existing_meta_query = $query->get( 'meta_query' ); |
| 146 | 218 | if ( is_array( $existing_meta_query ) ) { |
| @@ -149,43 +221,56 @@ | ||
| 149 | 221 | } else { |
| 150 | 222 | $query->set( 'meta_query', array( $meta_query ) ); |
| 151 | 223 | } |
| 152 | 224 | |
| 153 | - // Store Restrict Content filter value. | |
| 154 | - $this->restrict_content_filter = sanitize_text_field( $_REQUEST['convertkit_restrict_content'] ); // phpcs:ignore WordPress.Security.NonceVerification | |
| 155 | - | |
| 156 | 225 | } |
| 157 | 226 | |
| 158 | 227 | /** |
| 159 | - * Outputs a button in the WP_List_Table filters to run the Restrict Content Setup process. | |
| 228 | + * Registers a button in the Pages WP_List_Table linking to the the Restrict Content Setup Wizard. | |
| 160 | 229 | * |
| 161 | - * JS will move this button to be displayed next to the "Add New" button when viewing the table of Pages or Posts, | |
| 162 | - * as there is not a native WordPress action/filter for registering buttons next to the "Add New" button. | |
| 230 | + * @since 2.5.5 | |
| 163 | 231 | * |
| 164 | - * @since 2.1.0 | |
| 165 | - * | |
| 166 | - * @param array $views Views. | |
| 167 | - * @return array Views | |
| 232 | + * @param array $buttons Buttons. | |
| 233 | + * @param string $post_type Post Type. | |
| 234 | + * @return array Views | |
| 168 | 235 | */ |
| 169 | - public function output_wp_list_table_buttons( $views ) { | |
| 236 | + public function register_add_new_button( $buttons, $post_type ) { | |
| 170 | 237 | |
| 171 | 238 | // If no API credentials have been set, don't output the button. |
| 172 | 239 | $settings = new ConvertKit_Settings(); |
| 173 | - if ( ! $settings->has_api_key_and_secret() ) { | |
| 174 | - return $views; | |
| 240 | + if ( ! $settings->has_access_and_refresh_token() ) { | |
| 241 | + return $buttons; | |
| 175 | 242 | } |
| 176 | 243 | |
| 177 | - // Get current post type that we're viewing. | |
| 178 | - $post_type = $this->get_current_post_type(); | |
| 244 | + // If the Add New Landing Page / Member Content button is disabled, don't output the button. | |
| 245 | + if ( $settings->add_new_button_disabled() ) { | |
| 246 | + return $buttons; | |
| 247 | + } | |
| 179 | 248 | |
| 180 | - // Don't output button if we couldn't determine the current post type. | |
| 181 | - if ( ! $post_type ) { | |
| 182 | - return $views; | |
| 249 | + // Bail if the Post Type isn't supported. | |
| 250 | + if ( ! in_array( $post_type, convertkit_get_supported_post_types(), true ) ) { | |
| 251 | + return $buttons; | |
| 183 | 252 | } |
| 184 | 253 | |
| 185 | - $views['convertkit_restrict_content_setup'] = '<a href="admin.php?page=convertkit-restrict-content-setup&post_type=' . esc_attr( $post_type ) . '" class="convertkit-action page-title-action hidden">' . esc_html__( 'Add New Member Content', 'convertkit' ) . '</a>'; | |
| 186 | - return $views; | |
| 254 | + // Don't show the button if the user cannot create and publish this Post Type. | |
| 255 | + if ( ! convertkit_user_can_create_published_post_type( $post_type ) ) { | |
| 256 | + return $buttons; | |
| 257 | + } | |
| 187 | 258 | |
| 259 | + // Register button. | |
| 260 | + $buttons['convertkit_restrict_content_setup'] = array( | |
| 261 | + 'url' => add_query_arg( | |
| 262 | + array( | |
| 263 | + 'page' => 'convertkit-restrict-content-setup', | |
| 264 | + 'ck_post_type' => $post_type, | |
| 265 | + ), | |
| 266 | + admin_url( 'options.php' ) | |
| 267 | + ), | |
| 268 | + 'label' => __( 'Member Content', 'convertkit' ), | |
| 269 | + ); | |
| 270 | + | |
| 271 | + return $buttons; | |
| 272 | + | |
| 188 | 273 | } |
| 189 | 274 | |
| 190 | 275 | /** |
| 191 | 276 | * Outputs a dropdown filter on a WP_List_Table filter section to permit |
| @@ -197,23 +282,25 @@ | ||
| 197 | 282 | */ |
| 198 | 283 | public function output_wp_list_table_filters( $post_type ) { |
| 199 | 284 | |
| 200 | 285 | // Don't output filters if we're not viewing a Post Type that supports Restricted Content. |
| 201 | - if ( ! in_array( $post_type, convertkit_get_supported_restrict_content_post_types(), true ) ) { | |
| 286 | + if ( ! in_array( $post_type, convertkit_get_supported_post_types(), true ) ) { | |
| 202 | 287 | return; |
| 203 | 288 | } |
| 204 | 289 | |
| 205 | 290 | // Don't output filters if API credentials have not been defined in the Plugin's settings. |
| 206 | 291 | $settings = new ConvertKit_Settings(); |
| 207 | - if ( ! $settings->has_api_key_and_secret() ) { | |
| 292 | + if ( ! $settings->has_access_and_refresh_token() ) { | |
| 208 | 293 | return; |
| 209 | 294 | } |
| 210 | 295 | |
| 211 | - // Fetch Products. | |
| 296 | + // Initialize Forms, Products and Tags resource classes. | |
| 297 | + $this->forms = new ConvertKit_Resource_Forms(); | |
| 212 | 298 | $this->products = new ConvertKit_Resource_Products(); |
| 299 | + $this->tags = new ConvertKit_Resource_Tags(); | |
| 213 | 300 | |
| 214 | - // Don't display filter if no Products exist. | |
| 215 | - if ( ! $this->products->exist() ) { | |
| 301 | + // Don't display filter if no Tags and no Products exist. | |
| 302 | + if ( ! $this->products->exist() && ! $this->tags->exist() ) { | |
| 216 | 303 | return; |
| 217 | 304 | } |
| 218 | 305 | |
| 219 | 306 | // Output filter. |
| @@ -221,9 +308,9 @@ | ||
| 221 | 308 | |
| 222 | 309 | } |
| 223 | 310 | |
| 224 | 311 | /** |
| 225 | - * Appends the 'ConvertKit Member Content' text to a Page's Title in the WP_List_Table, | |
| 312 | + * Appends the 'Kit Member Content' text to a Page's Title in the WP_List_Table, | |
| 226 | 313 | * if the given Page has a Restrict Content setting. |
| 227 | 314 | * |
| 228 | 315 | * @param string[] $post_states An array of post display states. |
| 229 | 316 | * @param WP_Post $post The current post object. |
| @@ -230,8 +317,13 @@ | ||
| 230 | 317 | * @return string[] An array of post display states |
| 231 | 318 | */ |
| 232 | 319 | public function maybe_display_restrict_content_post_state( $post_states, $post ) { |
| 233 | 320 | |
| 321 | + // Bail if we're not on a WP_List_Table screen for a supported Post Type. | |
| 322 | + if ( ! $this->is_wp_list_table_request_for_supported_post_type() ) { | |
| 323 | + return $post_states; | |
| 324 | + } | |
| 325 | + | |
| 234 | 326 | // Fetch Post's settings. |
| 235 | 327 | $convertkit_post = new ConvertKit_Post( $post->ID ); |
| 236 | 328 | |
| 237 | 329 | // Return post states, unedited, if Restrict Content isn't enabled on this Post. |
| @@ -239,9 +331,9 @@ | ||
| 239 | 331 | return $post_states; |
| 240 | 332 | } |
| 241 | 333 | |
| 242 | 334 | // Add Post State. |
| 243 | - $post_states['convertkit_restrict_content'] = esc_html__( 'ConvertKit Member Content', 'convertkit' ); | |
| 335 | + $post_states['convertkit_restrict_content'] = esc_html__( 'Kit Member Content', 'convertkit' ); | |
| 244 | 336 | |
| 245 | 337 | // Return. |
| 246 | 338 | return $post_states; |
| 247 | 339 | |
| @@ -256,45 +348,15 @@ | ||
| 256 | 348 | * @return bool Is WP_List_Table request for a supported Post Type. |
| 257 | 349 | */ |
| 258 | 350 | private function is_wp_list_table_request_for_supported_post_type() { |
| 259 | 351 | |
| 260 | - // Bail if we cannot determine the screen. | |
| 261 | - if ( ! function_exists( 'get_current_screen' ) ) { | |
| 262 | - return false; | |
| 263 | - } | |
| 264 | - | |
| 265 | - // Get screen. | |
| 266 | - $screen = get_current_screen(); | |
| 267 | - | |
| 268 | 352 | // Bail if we're not on an edit.php screen. |
| 269 | - if ( $screen->base !== 'edit' ) { | |
| 353 | + if ( convertkit_get_current_screen( 'base' ) !== 'edit' ) { | |
| 270 | 354 | return false; |
| 271 | 355 | } |
| 272 | 356 | |
| 273 | 357 | // Return whether Post Type is supported for Restrict Content functionality. |
| 274 | - return in_array( $screen->post_type, convertkit_get_supported_restrict_content_post_types(), true ); | |
| 275 | - | |
| 276 | - } | |
| 277 | - | |
| 278 | - /** | |
| 279 | - * Get the current post type based on the screen that is viewed. | |
| 280 | - * | |
| 281 | - * @since 2.1.0 | |
| 282 | - * | |
| 283 | - * @return bool|string | |
| 284 | - */ | |
| 285 | - private function get_current_post_type() { | |
| 286 | - | |
| 287 | - // Bail if we cannot determine the screen. | |
| 288 | - if ( ! function_exists( 'get_current_screen' ) ) { | |
| 289 | - return false; | |
| 290 | - } | |
| 291 | - | |
| 292 | - // Get screen. | |
| 293 | - $screen = get_current_screen(); | |
| 294 | - | |
| 295 | - // Return post type. | |
| 296 | - return $screen->post_type; | |
| 358 | + return in_array( convertkit_get_current_screen( 'post_type' ), convertkit_get_supported_post_types(), true ); | |
| 297 | 359 | |
| 298 | 360 | } |
| 299 | 361 | |
| 300 | 362 | } |