| @@ -37,8 +37,17 @@ | ||
| 37 | 37 | */ |
| 38 | 38 | public $error = false; |
| 39 | 39 | |
| 40 | 40 | /** |
| 41 | + * Holds the Post Type to generate. | |
| 42 | + * | |
| 43 | + * @since 3.3.9 | |
| 44 | + * | |
| 45 | + * @var string | |
| 46 | + */ | |
| 47 | + public $post_type = 'page'; | |
| 48 | + | |
| 49 | + /** | |
| 41 | 50 | * The required user capability to access the setup wizard. |
| 42 | 51 | * |
| 43 | 52 | * @since 1.9.8.4 |
| 44 | 53 | * |
| @@ -50,11 +59,11 @@ | ||
| 50 | 59 | * The current step in the setup process the user is on. |
| 51 | 60 | * |
| 52 | 61 | * @since 1.9.8.4 |
| 53 | 62 | * |
| 54 | - * @var int | |
| 63 | + * @var string | |
| 55 | 64 | */ |
| 56 | - public $step = 1; | |
| 65 | + public $step = 'start'; | |
| 57 | 66 | |
| 58 | 67 | /** |
| 59 | 68 | * The programmatic name of the setup screen. |
| 60 | 69 | * |
| @@ -123,38 +132,27 @@ | ||
| 123 | 132 | } |
| 124 | 133 | |
| 125 | 134 | // Define actions to register the setup screen. |
| 126 | 135 | add_action( 'admin_menu', array( $this, 'register_screen' ) ); |
| 127 | - add_action( 'admin_head', array( $this, 'hide_screen_from_menu' ) ); | |
| 128 | 136 | add_action( 'admin_init', array( $this, 'maybe_load_setup_screen' ) ); |
| 129 | 137 | |
| 130 | 138 | } |
| 131 | 139 | |
| 132 | 140 | /** |
| 133 | - * Register the setup screen in WordPress' Dashboard, so that index.php?page={$this->page_name} | |
| 141 | + * Register the wizard screen in WordPress' Dashboard, so that options.php?page={$this->page_name} | |
| 134 | 142 | * does not 404 when in the WordPress Admin interface. |
| 135 | 143 | * |
| 144 | + * Ensures the WordPress user has the given required_capability to access this screen. | |
| 145 | + * | |
| 136 | 146 | * @since 1.9.8.4 |
| 137 | 147 | */ |
| 138 | 148 | public function register_screen() { |
| 139 | 149 | |
| 140 | - add_dashboard_page( '', '', 'edit_posts', $this->page_name, '__return_false' ); | |
| 150 | + add_submenu_page( '', '', '', $this->required_capability, $this->page_name, '__return_false' ); | |
| 141 | 151 | |
| 142 | 152 | } |
| 143 | 153 | |
| 144 | 154 | /** |
| 145 | - * Hides the menu registered when register_screen() above is called, otherwise | |
| 146 | - * we would have a blank submenu entry below the Dashboard menu. | |
| 147 | - * | |
| 148 | - * @since 1.9.8.4 | |
| 149 | - */ | |
| 150 | - public function hide_screen_from_menu() { | |
| 151 | - | |
| 152 | - remove_submenu_page( 'index.php', $this->page_name ); | |
| 153 | - | |
| 154 | - } | |
| 155 | - | |
| 156 | - /** | |
| 157 | 155 | * Loads the setup screen if the request URL is for this class |
| 158 | 156 | * |
| 159 | 157 | * @since 1.9.8.4 |
| 160 | 158 | */ |
| @@ -175,14 +173,24 @@ | ||
| 175 | 173 | set_current_screen( $this->page_name ); |
| 176 | 174 | |
| 177 | 175 | // If the convertkit-modal parameter exists and is 1, set the flag to denote |
| 178 | 176 | // this wizard is served in a modal. |
| 179 | - if ( array_key_exists( 'convertkit-modal', $_REQUEST ) && $_REQUEST['convertkit-modal'] === '1' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 177 | + if ( filter_has_var( INPUT_GET, 'convertkit-modal' ) && filter_input( INPUT_GET, 'convertkit-modal', FILTER_SANITIZE_NUMBER_INT ) === '1' ) { | |
| 180 | 178 | $this->is_modal = true; |
| 181 | 179 | } |
| 182 | 180 | |
| 181 | + /** | |
| 182 | + * Define the steps for the setup wizard. | |
| 183 | + * | |
| 184 | + * @since 3.1.8 | |
| 185 | + * | |
| 186 | + * @param array $steps The steps for the setup wizard. | |
| 187 | + * @return array The steps for the setup wizard. | |
| 188 | + */ | |
| 189 | + $this->steps = apply_filters( 'convertkit_admin_setup_wizard_steps_' . $this->page_name, $this->steps ); | |
| 190 | + | |
| 183 | 191 | // Define the step the user is on in the setup process. |
| 184 | - $this->step = ( isset( $_REQUEST['step'] ) ? absint( $_REQUEST['step'] ) : 1 ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 192 | + $this->step = $this->get_current_step(); | |
| 185 | 193 | |
| 186 | 194 | // Process any posted form data. |
| 187 | 195 | $this->process_form(); |
| 188 | 196 | |
| @@ -204,8 +212,68 @@ | ||
| 204 | 212 | |
| 205 | 213 | } |
| 206 | 214 | |
| 207 | 215 | /** |
| 216 | + * Returns the current step in the setup process. | |
| 217 | + * | |
| 218 | + * @since 3.1.7 | |
| 219 | + * | |
| 220 | + * @return string Current step. | |
| 221 | + */ | |
| 222 | + public function get_current_step() { | |
| 223 | + | |
| 224 | + $step = ( filter_has_var( INPUT_GET, 'step' ) ? filter_input( INPUT_GET, 'step', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : 'start' ); | |
| 225 | + | |
| 226 | + // Fallback to 'start' if the step is a registered step. | |
| 227 | + if ( ! array_key_exists( $step, $this->steps ) ) { | |
| 228 | + $step = 'start'; | |
| 229 | + } | |
| 230 | + | |
| 231 | + return $step; | |
| 232 | + | |
| 233 | + } | |
| 234 | + | |
| 235 | + /** | |
| 236 | + * Get the number of the current step. | |
| 237 | + * | |
| 238 | + * @since 3.1.7 | |
| 239 | + * | |
| 240 | + * @return int Step number. | |
| 241 | + */ | |
| 242 | + public function get_current_step_number() { | |
| 243 | + | |
| 244 | + return array_search( $this->step, array_keys( $this->steps ), true ) + 1; | |
| 245 | + | |
| 246 | + } | |
| 247 | + | |
| 248 | + /** | |
| 249 | + * Get the step by number. | |
| 250 | + * | |
| 251 | + * @since 3.1.7 | |
| 252 | + * | |
| 253 | + * @param int $number Step number (1 based index). | |
| 254 | + * @return string Step name/key. | |
| 255 | + */ | |
| 256 | + public function get_step_key_by_number( $number ) { | |
| 257 | + | |
| 258 | + return array_keys( $this->steps )[ $number - 1 ]; | |
| 259 | + | |
| 260 | + } | |
| 261 | + | |
| 262 | + /** | |
| 263 | + * Get the total number of steps. | |
| 264 | + * | |
| 265 | + * @since 3.1.7 | |
| 266 | + * | |
| 267 | + * @return int Total steps. | |
| 268 | + */ | |
| 269 | + public function get_total_steps() { | |
| 270 | + | |
| 271 | + return count( $this->steps ); | |
| 272 | + | |
| 273 | + } | |
| 274 | + | |
| 275 | + /** | |
| 208 | 276 | * Process submitted form data for the given setup wizard name and current step. |
| 209 | 277 | * |
| 210 | 278 | * @since 1.9.8.4 |
| 211 | 279 | */ |
| @@ -210,23 +278,14 @@ | ||
| 210 | 278 | * @since 1.9.8.4 |
| 211 | 279 | */ |
| 212 | 280 | private function process_form() { |
| 213 | 281 | |
| 214 | - // Run security checks. | |
| 215 | - if ( ! isset( $_POST['_wpnonce'] ) ) { | |
| 216 | - return; | |
| 217 | - } | |
| 218 | - if ( ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), $this->page_name ) ) { | |
| 219 | - $this->error = __( 'Invalid nonce specified.', 'convertkit' ); | |
| 220 | - return; | |
| 221 | - } | |
| 222 | - | |
| 223 | 282 | /** |
| 224 | 283 | * Process submitted form data for the given setup wizard name and current step. |
| 225 | 284 | * |
| 226 | 285 | * @since 1.9.8.4 |
| 227 | 286 | * |
| 228 | - * @param int $step Current step number. | |
| 287 | + * @param string $step Current step. | |
| 229 | 288 | */ |
| 230 | 289 | do_action( 'convertkit_admin_setup_wizard_process_form_' . $this->page_name, $this->step ); |
| 231 | 290 | |
| 232 | 291 | } |
| @@ -246,32 +305,32 @@ | ||
| 246 | 305 | 'page' => $this->page_name, |
| 247 | 306 | 'convertkit-modal' => $this->is_modal(), |
| 248 | 307 | 'step' => $this->step, |
| 249 | 308 | ), |
| 250 | - admin_url( 'index.php' ) | |
| 309 | + admin_url( 'options.php' ) | |
| 251 | 310 | ); |
| 252 | 311 | |
| 253 | 312 | // Define the previous step URL if we're not on the first or last step. |
| 254 | - if ( $this->step > 1 && $this->step < count( $this->steps ) ) { | |
| 313 | + if ( $this->get_current_step_number() > 1 && $this->get_current_step_number() < $this->get_total_steps() ) { | |
| 255 | 314 | $this->previous_step_url = add_query_arg( |
| 256 | 315 | array( |
| 257 | 316 | 'page' => $this->page_name, |
| 258 | 317 | 'convertkit-modal' => $this->is_modal(), |
| 259 | - 'step' => ( $this->step - 1 ), | |
| 318 | + 'step' => $this->get_step_key_by_number( $this->get_current_step_number() - 1 ), | |
| 260 | 319 | ), |
| 261 | - admin_url( 'index.php' ) | |
| 320 | + admin_url( 'options.php' ) | |
| 262 | 321 | ); |
| 263 | 322 | } |
| 264 | 323 | |
| 265 | 324 | // Define the next step URL if we're not on the last page. |
| 266 | - if ( $this->step < count( $this->steps ) ) { | |
| 325 | + if ( $this->get_current_step_number() < $this->get_total_steps() ) { | |
| 267 | 326 | $this->next_step_url = add_query_arg( |
| 268 | 327 | array( |
| 269 | 328 | 'page' => $this->page_name, |
| 270 | 329 | 'convertkit-modal' => $this->is_modal(), |
| 271 | - 'step' => ( $this->step + 1 ), | |
| 330 | + 'step' => $this->get_step_key_by_number( $this->get_current_step_number() + 1 ), | |
| 272 | 331 | ), |
| 273 | - admin_url( 'index.php' ) | |
| 332 | + admin_url( 'options.php' ) | |
| 274 | 333 | ); |
| 275 | 334 | } |
| 276 | 335 | |
| 277 | 336 | } |
| @@ -287,9 +346,9 @@ | ||
| 287 | 346 | * Load any data into class variables for the given setup wizard name and current step. |
| 288 | 347 | * |
| 289 | 348 | * @since 1.9.8.4 |
| 290 | 349 | * |
| 291 | - * @param int $step Current step number. | |
| 350 | + * @param string $step Current step. | |
| 292 | 351 | */ |
| 293 | 352 | do_action( 'convertkit_admin_setup_wizard_load_screen_data_' . $this->page_name, $this->step ); |
| 294 | 353 | |
| 295 | 354 | } |
| @@ -305,9 +364,9 @@ | ||
| 305 | 364 | convertkit_select2_enqueue_scripts(); |
| 306 | 365 | |
| 307 | 366 | // Enqueue JS. |
| 308 | 367 | wp_enqueue_script( 'convertkit-admin-preview-output', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/preview-output.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true ); |
| 309 | - wp_enqueue_script( 'convertkit-admin-setup-wizard', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/setup-wizard.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true ); | |
| 368 | + wp_enqueue_script( 'convertkit-admin-setup-wizard', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/setup-wizard.js', array(), CONVERTKIT_PLUGIN_VERSION, true ); | |
| 310 | 369 | |
| 311 | 370 | } |
| 312 | 371 | |
| 313 | 372 | /** |
| @@ -422,12 +481,12 @@ | ||
| 422 | 481 | return false; |
| 423 | 482 | } |
| 424 | 483 | |
| 425 | 484 | // Bail if we're not on the setup screen. |
| 426 | - if ( ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification | |
| 485 | + if ( ! filter_has_var( INPUT_GET, 'page' ) ) { | |
| 427 | 486 | return false; |
| 428 | 487 | } |
| 429 | - if ( sanitize_text_field( $_GET['page'] ) !== $this->page_name ) { // phpcs:ignore WordPress.Security.NonceVerification | |
| 488 | + if ( filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) !== $this->page_name ) { | |
| 430 | 489 | return false; |
| 431 | 490 | } |
| 432 | 491 | |
| 433 | 492 | return true; |
| @@ -453,8 +512,33 @@ | ||
| 453 | 512 | return false; |
| 454 | 513 | } |
| 455 | 514 | |
| 456 | 515 | return true; |
| 516 | + | |
| 517 | + } | |
| 518 | + | |
| 519 | + /** | |
| 520 | + * Sets the Post Type from the request, ensuring it is supported by the Plugin. | |
| 521 | + * | |
| 522 | + * @since 3.3.9 | |
| 523 | + */ | |
| 524 | + protected function set_post_type() { | |
| 525 | + | |
| 526 | + $this->post_type = ( filter_has_var( INPUT_GET, 'ck_post_type' ) ? filter_input( INPUT_GET, 'ck_post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : 'page' ); | |
| 527 | + | |
| 528 | + if ( ! in_array( $this->post_type, convertkit_get_supported_post_types(), true ) ) { | |
| 529 | + wp_die( | |
| 530 | + sprintf( | |
| 531 | + /* translators: Post Type */ | |
| 532 | + esc_html__( 'The post type `%s` is not supported for Member Content.', 'convertkit' ), | |
| 533 | + esc_html( $this->post_type ) | |
| 534 | + ), | |
| 535 | + esc_html__( 'WordPress Error', 'convertkit' ), | |
| 536 | + array( | |
| 537 | + 'back_link' => true, | |
| 538 | + ) | |
| 539 | + ); | |
| 540 | + } | |
| 457 | 541 | |
| 458 | 542 | } |
| 459 | 543 | |
| 460 | 544 | } |