true ) ), array( 'attachment' ) ) ); if ( ! $types ) { return array(); } $params = array_merge( array( '%' . $wpdb->esc_like( '[careback' ) . '%' ), self::STATUSES, $types, array( self::MAX_PAGES ) ); $ids = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- the Setup tab only; WP_Query cannot search the content for a shortcode. $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE %s AND post_status IN (" . implode( ',', array_fill( 0, count( self::STATUSES ), '%s' ) ) . ') AND post_type IN (' . implode( ',', array_fill( 0, count( $types ), '%s' ) ) . ') ORDER BY ID ASC LIMIT %d', // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- one placeholder per item. $params ) ); $regex = '/' . get_shortcode_regex( array( 'careback' ) ) . '/'; $out = array(); foreach ( (array) $ids as $id ) { $post = get_post( (int) $id ); if ( $post && 1 === preg_match( $regex, (string) $post->post_content ) ) { $out[] = $post; } } return $out; } /** * The published ones of those. * * @param \WP_Post[] $pages Posts carrying the form. * @return \WP_Post[] */ public static function published( array $pages ) { return array_values( array_filter( $pages, static function ( $post ) { return 'publish' === $post->post_status; } ) ); } /** * Where a link to the page is found: the menus, the navigations and the * template parts of a block theme, and the widgets — each place named. * * @param \WP_Post $page The page that carries the form. * @return string[] */ public static function find( \WP_Post $page ) { $url = (string) get_permalink( $page ); $found = array(); foreach ( wp_get_nav_menus() as $menu ) { foreach ( (array) wp_get_nav_menu_items( $menu->term_id, array( 'post_status' => 'publish' ) ) as $item ) { $to_page = 'post_type' === $item->type && (int) $item->object_id === (int) $page->ID; if ( $to_page || self::same_address( (string) $item->url, $url ) ) { /* translators: %s: name of a menu. */ $found[] = sprintf( __( '%s (menu)', 'careback' ), $menu->name ); break; } } } $navigations = get_posts( array( 'post_type' => 'wp_navigation', 'post_status' => 'publish', 'posts_per_page' => 50, 'no_found_rows' => true, ) ); foreach ( $navigations as $navigation ) { if ( self::mentions( (string) $navigation->post_content, $page, $url ) ) { /* translators: %s: name of a navigation of the theme. */ $found[] = sprintf( __( '%s (navigation)', 'careback' ), '' !== $navigation->post_title ? $navigation->post_title : __( 'Navigation', 'careback' ) ); } } if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() && function_exists( 'get_block_templates' ) ) { foreach ( get_block_templates( array(), 'wp_template_part' ) as $part ) { if ( self::mentions( (string) $part->content, $page, $url ) ) { /* translators: %s: name of a template part, such as "Footer". */ $found[] = sprintf( __( '%s (template part)', 'careback' ), (string) $part->title ); } } } foreach ( array( 'widget_block', 'widget_text', 'widget_custom_html' ) as $option ) { if ( self::mentions( (string) wp_json_encode( get_option( $option, array() ) ), $page, $url ) ) { $found[] = __( 'Widgets', 'careback' ); break; } } return array_values( array_unique( $found ) ); } /** * True when two addresses lead to the same page: equal, or equal paths on * this site (a path alone, as a menu item may hold it). * * @param string $given An address as stored. * @param string $target The page's address. * @return bool */ private static function same_address( $given, $target ) { $given = trim( $given ); if ( '' === $given ) { return false; } if ( untrailingslashit( $given ) === untrailingslashit( $target ) ) { return true; } $path = (string) wp_parse_url( $target, PHP_URL_PATH ); $host = (string) wp_parse_url( $given, PHP_URL_HOST ); return '' !== trim( $path, '/' ) && ( '' === $host || (string) wp_parse_url( $target, PHP_URL_HOST ) === $host ) && untrailingslashit( (string) wp_parse_url( $given, PHP_URL_PATH ) ) === untrailingslashit( $path ); } /** * True when block or widget content links the page: its address, its path * (not the front page's "/"), its "page_id" address, or a navigation link * naming its id. * * @param string $content Markup or JSON. * @param \WP_Post $page The page. * @param string $url Its address. * @return bool */ private static function mentions( $content, \WP_Post $page, $url ) { if ( '' === $content ) { return false; } $content = str_replace( '\\/', '/', $content ); if ( false !== strpos( $content, untrailingslashit( $url ) ) || false !== strpos( $content, 'page_id=' . $page->ID ) ) { return true; } if ( 1 === preg_match( '/"id":' . (int) $page->ID . '\b[^}]*"(type|kind)":"(page|post-type)"|"(type|kind)":"(page|post-type)"[^}]*"id":' . (int) $page->ID . '\b/', $content ) ) { return true; } $path = untrailingslashit( (string) wp_parse_url( $url, PHP_URL_PATH ) ); return '' !== trim( $path, '/' ) && 1 === preg_match( '#["\']' . preg_quote( $path, '#' ) . '/?["\'?\#]#', $content ); } /** * Stores "I have added the link" with the day, or clears it. Nonce first, * then the capability; then back to Setup, which says "Saved.". * * @return void */ public static function handle() { check_admin_referer( self::ACTION ); if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'Permission denied.', 'careback' ) ); } ( new Settings() )->update( array( 'link_added_at' => empty( $_POST['link_added'] ) ? 0 : time() ) ); wp_safe_redirect( add_query_arg( 'careback_link', 'saved', admin_url( 'admin.php?page=careback-setup' ) ) . '#careback-setup-step-3' ); exit; } }