Post_Type::POST_TYPE, 'post_status' => 'draft', 'post_title' => __( 'New quickscan', 'funnelfork' ), ), true ); if ( is_wp_error( $id ) ) { wp_die( esc_html( $id->get_error_message() ) ); } funnelfork_save_graph( $id, Graph::default_graph() ); wp_safe_redirect( funnelfork_editor_url( $id ) ); exit; } /** * Sends post.php?action=edit for scans to the visual editor. */ public static function redirect_classic_edit(): void { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only redirect. $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; $id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; // phpcs:enable if ( 'edit' === $action && $id && Post_Type::POST_TYPE === get_post_type( $id ) ) { wp_safe_redirect( funnelfork_editor_url( $id ) ); exit; } } /** * Edit links of scans point to the visual editor. * * @param string $link Edit link. * @param int $post_id Post ID. */ public static function edit_link( $link, $post_id ) { if ( Post_Type::POST_TYPE === get_post_type( $post_id ) ) { return funnelfork_editor_url( (int) $post_id ); } return $link; } /** * Whether the current screen is the visual editor. */ public static function is_editor_screen(): bool { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; return $screen && self::$hook && $screen->id === self::$hook; } /** * Body class used to give the editor the full screen height. * * @param string $classes Body classes. */ public static function body_class( $classes ) { if ( self::is_editor_screen() ) { $classes .= ' funnelfork-editor-screen'; } return $classes; } /** * Renders the editor page; the React app mounts on the root element. */ public static function render_editor_page(): void { $id = self::current_scan_id(); $post = $id ? get_post( $id ) : null; if ( ! $post instanceof WP_Post || Post_Type::POST_TYPE !== $post->post_type ) { echo '

' . esc_html__( 'Quickscan not found.', 'funnelfork' ) . '

'; return; } if ( ! current_user_can( 'edit_post', $id ) ) { wp_die( esc_html__( 'You are not allowed to edit this quickscan.', 'funnelfork' ) ); } echo '

' . esc_html__( 'Loading editor…', 'funnelfork' ) . '

'; } /** * Scan ID from the URL. */ private static function current_scan_id(): int { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only. return isset( $_GET['scan'] ) ? absint( $_GET['scan'] ) : 0; } /** * Enqueues the editor app. */ public static function enqueue(): void { if ( ! self::is_editor_screen() ) { return; } $id = self::current_scan_id(); $asset = self::asset( 'editor' ); wp_enqueue_script( 'funnelfork-editor', FUNNELFORK_URL . 'build/editor.js', $asset['dependencies'], $asset['version'], true ); // React Flow's base styles end up in style-editor.css, ours in editor.css. wp_enqueue_style( 'funnelfork-editor-canvas', FUNNELFORK_URL . 'build/style-editor.css', array( 'wp-components' ), $asset['version'] ); wp_enqueue_style( 'funnelfork-fonts', FUNNELFORK_URL . 'assets/fonts/fonts.css', array(), FUNNELFORK_VERSION ); wp_enqueue_style( 'funnelfork-editor', FUNNELFORK_URL . 'build/editor.css', array( 'funnelfork-editor-canvas', 'funnelfork-fonts' ), $asset['version'] ); wp_set_script_translations( 'funnelfork-editor', 'funnelfork', FUNNELFORK_DIR . 'languages' ); wp_add_inline_script( 'funnelfork-editor', 'window.funnelforkEditor = ' . wp_json_encode( array( 'scanId' => $id, 'listUrl' => admin_url( 'edit.php?post_type=' . Post_Type::POST_TYPE ), 'previewUrl' => Frontend::preview_url( $id ), 'shortcode' => sprintf( '[funnelfork id="%d"]', $id ), 'isPremium' => funnelfork_is_premium(), 'proPageUrl' => Upgrade::page_url(), 'endpointTypes' => Graph::endpoint_types(), 'modes' => Graph::modes(), 'transitions' => Graph::transitions(), 'transitionLabels' => Graph::transition_labels(), 'yesnoAnswers' => Graph::yesno_answers(), ) ) . ';', 'before' ); /** * Fires after the editor assets are enqueued, so add-ons can extend the editor. * * @param int $id Scan ID. */ do_action( 'funnelfork_enqueue_editor', $id ); } /** * Reads a build/*.asset.php file. * * @param string $name Entry name. */ public static function asset( string $name ): array { $file = FUNNELFORK_DIR . 'build/' . $name . '.asset.php'; if ( is_readable( $file ) ) { return require $file; } return array( 'dependencies' => array(), 'version' => FUNNELFORK_VERSION, ); } /** * Overview columns. * * @param array $columns Columns. */ public static function columns( $columns ) { $date = $columns['date'] ?? null; unset( $columns['date'] ); $columns['funnelfork_shortcode'] = __( 'Shortcode', 'funnelfork' ); $columns['funnelfork_questions'] = __( 'Questions', 'funnelfork' ); $columns['funnelfork_endpoints'] = __( 'Endpoints', 'funnelfork' ); if ( $date ) { $columns['date'] = $date; } return $columns; } /** * Overview column content. * * @param string $column Column key. * @param int $post_id Post ID. */ public static function column_content( $column, $post_id ): void { switch ( $column ) { case 'funnelfork_shortcode': printf( '[funnelfork id="%d"]', (int) $post_id ); break; case 'funnelfork_questions': case 'funnelfork_endpoints': $counts = Graph::count_nodes( funnelfork_get_graph( (int) $post_id ) ); echo (int) ( 'funnelfork_questions' === $column ? $counts['question'] : $counts['end'] ); break; } } /** * Row actions: drop "Quick edit", add "Preview". * * @param array $actions Actions. * @param WP_Post $post Post. */ public static function row_actions( $actions, $post ) { if ( Post_Type::POST_TYPE !== $post->post_type ) { return $actions; } unset( $actions['inline hide-if-no-js'] ); $actions['funnelfork_preview'] = sprintf( '%s', esc_url( Frontend::preview_url( $post->ID ) ), esc_html__( 'Preview', 'funnelfork' ) ); return $actions; } /** * Link to the overview on the Plugins screen. * * @param array $links Links. */ public static function plugin_links( $links ) { array_unshift( $links, sprintf( '%s', esc_url( admin_url( 'edit.php?post_type=' . Post_Type::POST_TYPE ) ), esc_html__( 'Quickscans', 'funnelfork' ) ) ); return $links; } }