*/ function demos() { $demos = array( 'build' => array( 'title' => __( 'Build – construction company', 'blockmason-core' ), 'description' => __( 'Adds sample services, projects, team members, testimonials, news posts, the About, Prices and Contact pages, a menu and the images they use. Your existing content is not changed.', 'blockmason-core' ), 'path' => DIR . 'demo/build/', ), ); /** * Filters the importable demos. * * Each demo needs a title, a description and the path of a folder with a * content.php file and an images/ folder. Images missing from that folder * are taken from the Build demo. * * @param array $demos Demos keyed by slug. */ return (array) apply_filters( 'blockmason_core_demos', $demos ); } /** * Folder of a demo, with a trailing slash, or an empty string. * * @param string $demo Demo slug. * @return string */ function demo_dir( $demo ) { $demos = demos(); $demo = sanitize_key( $demo ); return isset( $demos[ $demo ]['path'] ) ? trailingslashit( $demos[ $demo ]['path'] ) : ''; } /** * Adds the demo page under Appearance. */ function demo_admin_menu() { add_theme_page( __( 'Blockmason demo', 'blockmason-core' ), __( 'Blockmason demo', 'blockmason-core' ), 'manage_options', 'blockmason-demo', __NAMESPACE__ . '\demo_page' ); } /** * Renders the demo page. */ function demo_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $state = get_option( DEMO_OPTION ); $result = isset( $_GET['blockmason-demo'] ) ? sanitize_key( wp_unslash( $_GET['blockmason-demo'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only. ?>

$info ) : ?>

array_values( get_post_types() ), 'post_status' => array_values( get_post_stati() ), 'posts_per_page' => -1, 'fields' => 'ids', 'meta_key' => '_blockmason_demo', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Admin only, run on demand. ) ); return array_values( array_unique( array_map( 'intval', $ids ) ) ); } /** * Counts the demo items still on the site. * * @return int */ function demo_count() { static $count = null; if ( null === $count ) { $count = count( demo_items() ); } return $count; } /** * Deletes the demo content and restores the reading settings. * * @return int Number of deleted items. */ function demo_remove() { $ids = demo_items(); $deleted = 0; foreach ( array( 'page_on_front', 'page_for_posts' ) as $option ) { if ( in_array( (int) get_option( $option ), $ids, true ) ) { update_option( $option, 0 ); } } if ( ! get_option( 'page_on_front' ) ) { update_option( 'show_on_front', 'posts' ); } foreach ( $ids as $id ) { $done = 'attachment' === get_post_type( $id ) ? wp_delete_attachment( $id, true ) : wp_delete_post( $id, true ); if ( $done ) { ++$deleted; } } // Terms the importer created, if nothing else uses them now. $state = get_option( DEMO_OPTION ); $terms = $state['terms'] ?? demo_term_ids( $state['demo'] ?? 'build' ); foreach ( (array) $terms as $taxonomy => $term_ids ) { foreach ( (array) $term_ids as $term_id ) { $term = get_term( (int) $term_id, $taxonomy ); if ( $term instanceof \WP_Term && 0 === (int) $term->count ) { wp_delete_term( $term->term_id, $taxonomy ); } } } if ( ! empty( $state['title'] ) ) { update_option( 'blogname', $state['title'] ); } /** * Fires after the demo content has been removed. * * @param string $demo Slug of the demo that was imported. */ do_action( 'blockmason_core_demo_removed', (string) ( $state['demo'] ?? 'build' ) ); delete_option( DEMO_OPTION ); flush_rewrite_rules(); return $deleted; } /** * Looks up the demo terms by slug (for imports made before 0.4.0, which did * not record the terms they created). * * @param string $demo Demo slug. * @return array */ function demo_term_ids( $demo ) { $file = demo_dir( $demo ) . 'content.php'; $data = demo_dir( $demo ) && file_exists( $file ) ? include $file : array(); $ids = array(); foreach ( (array) ( $data['terms'] ?? array() ) as $taxonomy => $items ) { foreach ( array_keys( $items ) as $slug ) { $term = get_term_by( 'slug', $slug, $taxonomy ); if ( $term ) { $ids[ $taxonomy ][] = (int) $term->term_id; } } } return $ids; } /** * Replaces {{img:key}} tokens in demo content with the URL of the imported image. * * @param string $content Content. * @param callable $image Returns the attachment ID for an image key. * @return string */ function demo_image_tokens( $content, $image ) { return (string) preg_replace_callback( '/\{\{img:([a-z0-9-]+)\}\}/', static function ( $m ) use ( $image ) { return esc_url( (string) wp_get_attachment_image_url( $image( $m[1] ), 'large' ) ); }, $content ); } /** * Imports a demo. * * @param string $demo Demo slug (folder in demo/). * @return bool */ function demo_import( $demo ) { $state = get_option( DEMO_OPTION ); if ( ! empty( $state['imported'] ) ) { return true; } $dir = demo_dir( $demo ); if ( ! $dir || ! file_exists( $dir . 'content.php' ) ) { return false; } $file = $dir . 'content.php'; $data = include $file; if ( ! is_array( $data ) ) { return false; } require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; wp_raise_memory_limit( 'admin' ); if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 300 ); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Image processing can take a while. } $mark = static function ( $post_id ) use ( $demo ) { if ( $post_id && ! is_wp_error( $post_id ) ) { update_post_meta( $post_id, '_blockmason_demo', $demo ); } return $post_id; }; // Images. $images = array(); foreach ( $data['images'] as $key => $filename ) { $path = $dir . 'images/' . $filename; if ( ! file_exists( $path ) ) { $path = DIR . 'demo/build/images/' . $filename; } $images[ $key ] = demo_import_image( $path, $demo ); } $image = static function ( $key ) use ( $images ) { return isset( $images[ $key ] ) ? (int) $images[ $key ] : 0; }; // Terms. $terms = array(); $created_terms = array(); foreach ( $data['terms'] as $taxonomy => $items ) { foreach ( $items as $slug => $name ) { $term = term_exists( $slug, $taxonomy ); if ( ! $term ) { $term = wp_insert_term( $name, $taxonomy, array( 'slug' => $slug ) ); if ( ! is_wp_error( $term ) ) { $created_terms[ $taxonomy ][] = (int) $term['term_id']; } } if ( ! is_wp_error( $term ) ) { $terms[ $taxonomy ][ $slug ] = (int) $term['term_id']; } } } $author = get_current_user_id(); if ( ! $author ) { $admins = get_users( array( 'role' => 'administrator', 'number' => 1, 'fields' => 'ID', ) ); $author = $admins ? (int) $admins[0] : 0; } $insert = static function ( $type, $item, $order = 0 ) use ( $mark, $image, $author ) { $post_id = wp_insert_post( array( 'post_type' => $type, 'post_status' => 'publish', 'post_title' => wp_slash( $item['title'] ), 'post_content' => wp_slash( demo_image_tokens( $item['content'] ?? '', $image ) ), 'post_excerpt' => wp_slash( $item['excerpt'] ?? '' ), 'menu_order' => $order, 'post_author' => $author, ), true ); if ( is_wp_error( $post_id ) ) { return 0; } $mark( $post_id ); if ( ! empty( $item['image'] ) && $image( $item['image'] ) ) { set_post_thumbnail( $post_id, $image( $item['image'] ) ); } foreach ( $item['meta'] ?? array() as $key => $value ) { update_post_meta( $post_id, $key, $value ); } return (int) $post_id; }; // Services. $services = array(); $order = 0; foreach ( $data['services'] as $slug => $item ) { $item['meta'] = array( 'bm_icon' => $item['icon'] ); $services[ $slug ] = $insert( 'bm_service', $item, ++$order ); } // Projects. $projects = array(); foreach ( array_reverse( $data['projects'], true ) as $slug => $item ) { if ( ! empty( $item['before'] ) ) { $item['meta']['bm_before_image'] = $image( $item['before'] ); $item['meta']['bm_after_image'] = $image( $item['after'] ); } $post_id = $insert( 'bm_project', $item ); if ( ! $post_id ) { continue; } $projects[ $slug ] = $post_id; if ( isset( $terms['bm_project_category'][ $item['category'] ] ) ) { wp_set_object_terms( $post_id, $terms['bm_project_category'][ $item['category'] ], 'bm_project_category' ); } foreach ( $item['services'] as $service ) { if ( ! empty( $services[ $service ] ) ) { add_post_meta( $post_id, 'bm_services', $services[ $service ] ); } } } // Team. $order = 0; foreach ( $data['team'] as $item ) { $post_id = $insert( 'bm_team_member', $item, ++$order ); if ( $post_id && isset( $terms['bm_department'][ $item['department'] ] ) ) { wp_set_object_terms( $post_id, $terms['bm_department'][ $item['department'] ], 'bm_department' ); } } // Testimonials. $order = 0; foreach ( $data['testimonials'] as $item ) { $item['meta']['bm_project'] = $projects[ $item['project'] ] ?? 0; $insert( 'bm_testimonial', $item, ++$order ); } // News. foreach ( array_reverse( $data['posts'] ) as $item ) { $post_id = $insert( 'post', $item ); if ( $post_id && ! empty( $item['category'] ) && isset( $terms['category'][ $item['category'] ] ) ) { wp_set_post_categories( $post_id, array( $terms['category'][ $item['category'] ] ) ); } if ( $post_id && ! empty( $item['tags'] ) ) { wp_set_post_tags( $post_id, $item['tags'] ); } } // Pages. $pages = array(); foreach ( $data['pages'] as $slug => $item ) { if ( 'contact' === $slug ) { $item['content'] = demo_contact_content( $item['content'], $mark, (string) wp_get_attachment_image_url( $image( 'apartments' ), 'full' ) ); } $pages[ $slug ] = $insert( 'page', $item ); if ( $pages[ $slug ] && ! empty( $item['template'] ) ) { update_post_meta( $pages[ $slug ], '_wp_page_template', $item['template'] ); } } if ( ! empty( $pages['home'] ) && ! empty( $pages['news'] ) ) { update_option( 'show_on_front', 'page' ); update_option( 'page_on_front', $pages['home'] ); update_option( 'page_for_posts', $pages['news'] ); } // Menu (used by the Navigation block in the header and footer). $links = ''; foreach ( $data['menu'] as $link ) { $links .= sprintf( '', esc_attr( $link[0] ), esc_url_raw( home_url( $link[1] ) ) ); } $mark( wp_insert_post( array( 'post_type' => 'wp_navigation', 'post_status' => 'publish', 'post_title' => __( 'Main menu', 'blockmason-core' ), 'post_content' => wp_slash( $links ), ) ) ); $old_title = get_option( 'blogname' ); if ( ! empty( $data['site_title'] ) ) { update_option( 'blogname', $data['site_title'] ); } flush_rewrite_rules(); update_option( DEMO_OPTION, array( 'demo' => $demo, 'imported' => time(), 'terms' => $created_terms, 'title' => $old_title, ), false ); /** * Fires after a demo has been imported. * * @param string $demo Demo slug. * @param array $data The demo's content.php data. */ do_action( 'blockmason_core_demo_imported', $demo, $data ); return true; } /** * Adds a Contact Form 7 quote form to the Contact page, when the plugin is active. * * The form sits in a white card on a full-width photo. * * @param string $content Page content. * @param callable $mark Marks a post as demo content. * @param string $image URL of the background photo. * @return string */ function demo_contact_content( $content, $mark, $image = '' ) { if ( ! class_exists( 'WPCF7_ContactForm' ) || ! method_exists( 'WPCF7_ContactForm', 'get_template' ) ) { return $content; } $form = \WPCF7_ContactForm::get_template( array( 'title' => __( 'Quote request', 'blockmason-core' ), ) ); $field = static function ( $label, $tag ) { return ''; }; $privacy = get_privacy_policy_url(); $consent = $privacy ? sprintf( /* translators: %s: link to the privacy policy. */ __( 'I agree that my data is stored to answer my request (%s).', 'blockmason-core' ), '' . esc_html__( 'privacy policy', 'blockmason-core' ) . '' ) : __( 'I agree that my data is stored to answer my request.', 'blockmason-core' ); $form->set_properties( array( 'form' => implode( "\n", array( $field( __( 'Your name', 'blockmason-core' ), '[text* your-name autocomplete:name placeholder "' . esc_attr__( 'Your name', 'blockmason-core' ) . '"]' ), $field( __( 'Email', 'blockmason-core' ), '[email* your-email autocomplete:email placeholder "' . esc_attr__( 'Email', 'blockmason-core' ) . '"]' ), $field( __( 'Phone', 'blockmason-core' ), '[tel your-phone autocomplete:tel placeholder "' . esc_attr__( 'Phone', 'blockmason-core' ) . '"]' ), $field( __( 'Type of work', 'blockmason-core' ), '[select your-work first_as_label "' . __( 'Type of work', 'blockmason-core' ) . '" "' . __( 'New build', 'blockmason-core' ) . '" "' . __( 'Renovation', 'blockmason-core' ) . '" "' . __( 'Roofing', 'blockmason-core' ) . '" "' . __( 'Bathroom or kitchen', 'blockmason-core' ) . '" "' . __( 'Other', 'blockmason-core' ) . '"]' ), $field( __( 'Tell us about the project', 'blockmason-core' ), '[textarea your-message placeholder "' . esc_attr__( 'Tell us about the project', 'blockmason-core' ) . '"]' ), '[acceptance your-consent] ' . $consent . ' [/acceptance]', '[submit "' . __( 'Request a quote', 'blockmason-core' ) . '"]', ) ), ) ); $form_id = $form->save(); if ( ! $form_id ) { return $content; } $mark( $form_id ); $cover = $image ? sprintf( '{"url":"%1$s","dimRatio":20,', esc_url_raw( $image ) ) : '{"dimRatio":100,'; $img = $image ? sprintf( '', esc_url( $image ) ) : ''; $section = sprintf( '
%3$s

%4$s

[contact-form-7 id="%5$d" title="%6$s"]
', $cover, $image ? 20 : 100, $img, esc_html__( 'Have a project in mind? Get in touch!', 'blockmason-core' ), (int) $form_id, esc_attr__( 'Quote request', 'blockmason-core' ) ); // Place the form right after the contacts section. $anchor = ''; return str_contains( $content, $anchor ) ? str_replace( $anchor, $anchor . $section, $content ) : $content . $section; } /** * Copies a bundled image into the media library. * * @param string $path Absolute path of the bundled file. * @param string $demo Demo slug. * @return int Attachment ID, or 0. */ function demo_import_image( $path, $demo ) { if ( ! file_exists( $path ) ) { return 0; } $tmp = wp_tempnam( basename( $path ) ); if ( ! $tmp || ! copy( $path, $tmp ) ) { return 0; } $id = media_handle_sideload( array( 'name' => basename( $path ), 'tmp_name' => $tmp, ), 0 ); if ( is_wp_error( $id ) ) { wp_delete_file( $tmp ); return 0; } update_post_meta( $id, '_blockmason_demo', $demo ); return (int) $id; }