# JBC Block Theme Based on [_s theme](https://github.com/Automattic/_s) with Custom ACF Block MVC ## Theme Requirements * PHP 8.2+ * [ACF Pro](https://www.advancedcustomfields.com/pro/) * [ACF Extended](https://www.acf-extended.com/) (optional) ## Development Requirements ## - [Node.js v12](https://nodejs.org/) - [Composer](https://getcomposer.org/) ### Quick Start Clone or download this repository, remname, and then you'll need to do a six-step find and replace on the name in all the templates. 1. Search for `'atlas'` (inside single quotations) to capture the text domain and replace with: `'theme-name'`. 2. Search for `atlas_` to capture all the functions names and replace with: `theme_name_`. 3. Search for `Text Domain: atlas-theme` in `style.css` and replace with: `Text Domain: theme-name`. 4. Search for <code>&nbsp;Atlas</code> (with a space before it) to capture DocBlocks and replace with: <code>&nbsp;Theme_Name</code>. 5. Search for `atlas-` to capture prefixed handles and replace with: `theme-name-`. 6. Search for `ATLAS_` (in uppercase) to capture constants and replace with: `THEME_NAME_`. ### Setup To start using all the tools that come with `atlas` you need to install the necessary Node.js and Composer dependencies : ```sh $ composer install $ npm install ``` ### Available CLI commands - `composer lint:wpcs` : checks all PHP files against [PHP Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/php/). - `composer lint:php` : checks all PHP files for syntax errors. - `composer make-pot` : generates a .pot file in the `languages/` directory. - `npm run compile:css` : compiles SASS files to css. - `npm run compile:rtl` : generates an RTL stylesheet. - `npm run watch` : watches all SASS files and recompiles them to css when they change. - `npm run lint:scss` : checks all SASS files against [CSS Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/css/). - `npm run lint:js` : checks all JavaScript files against [JavaScript Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/). - `npm run bundle` : generates a .zip archive for distribution, excluding development and system files. --- ## Field Types Follow this [LINK](https://hackmd.io/kRXbmm2IQ96Bo-kqCMSnqA#Basic) to find example implanmentations. You can find a full reference of available settings on the [official ACF documentation](https://www.advancedcustomfields.com/resources/register-fields-via-php/#field-type%20settings). ### Blocks *** #### File Structure Create directory in the `blocks` directory found in the root directory and set up the below file Structure ``` WordPress/ ├── wp-content/ │ ├── themes/ │ │ ├── theme-name/ │ │ │ └── blocks/ │ │ │ ├── example-block/ │ │ │ │ ├── view/ │ │ │ │ │ └── render.php │ │ │ │ └── example-block.php │ │ │ └── register-blocks.php │ │ └── ... │ └── ... └── ... ``` - `render.php`: Contains the skeleton of the block view on the frontend - `example-block.php`: This is the block class that contains the functionality and ACF Field of the Block #### Register Block To register a block add the class to `$classArray` in the `register-blocks.php` found in `theme-name/blocks`. :::spoiler {state="open"} Code ```php= <?php $classArray = [ BlockNameBlock::class ]; ``` ::: --- ## Register ACF Fields to block In the Block class with in the 'registerFields' function ### Simple Example Below is a Simple implemention of use case ___ :::spoiler {state="open"} Code ```php= <?php $block = new FieldsBuilder('Block Name'); $block->setLocation('block', '==', 'acf/block-name-block'); $block->addGroup('block_name_block') ->addText('text_field') ->instructions('Instructions') ->addRepeater('repeater') ->addImage('image') ->endRepeater() ->endGroup(); return $block->build(); ``` ::: :::spoiler {state="open"} Output ```php= <?php return = [ 'key' => 'block_name', 'title' => 'Block Name', 'fields' => [ [ 'key' => 'block_name_block', 'label' => 'Block Name Block', 'name' => 'block_name_block', 'type' => 'group', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => [ 'width' => '', 'class' => '', 'id' => '', ], 'layout' => 'block', 'sub_fields' => [ [ 'key' => 'text_field', 'label' => 'Text Field', 'name' => 'text_field', 'type' => 'text', 'instructions' => 'Instructions', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => [ 'width' => '', 'class' => '', 'id' => '', ], 'default_value' => '', 'placeholder' => '', 'prepend' => '', 'append' => '', 'maxlength' => '', ], [ 'key' => 'repeater', 'label' => 'Repeater', 'name' => 'repeater', 'type' => 'repeater', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => [ 'width' => '', 'class' => '', 'id' => '', ], 'collapsed' => '', 'min' => 0, 'max' => 0, 'layout' => 'table', 'button_label' => '', 'sub_fields' => [ [ 'key' => 'image', 'label' => 'Image', 'name' => 'image', 'type' => 'image', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => [ 'width' => '', 'class' => '', 'id' => '', ], 'return_format' => 'array', 'preview_size' => 'medium', 'library' => 'all', 'min_width' => '', 'min_height' => '', 'min_size' => '', 'max_width' => '', 'max_height' => '', 'max_size' => '', 'mime_types' => '', ], ], ], ], ], ], 'location' => [ [ [ 'param' => 'post_type', 'operator' => '==', 'value' => 'post', ], ], ], 'menu_order' => 0, 'position' => 'normal', 'style' => 'default', 'label_placement' => 'top', 'instruction_placement' => 'label', 'hide_on_screen' => '', 'active' => TRUE, 'description' => '', 'show_in_rest' => 0, ]; ``` ::: --- #### Reusable Examples Below is an example of the reusable case scenario --- :::spoiler {state="open"} Code ```php= <?php $background = new FieldsBuilder('background'); $background ->addTab('Background') ->addImage('background_image') ->addTrueFalse('fixed') ->instructions("Check to add a parallax effect where the background image doesn't move when scrolling") ->addColorPicker('background_color'); $banner = new FieldsBuilder('banner'); $banner ->addTab('Content') ->addText('title') ->addWysiwyg('content') ->addFields($background) ->setLocation('post_type', '==', 'page'); $section = new FieldsBuilder('section'); $section ->addTab('Content') ->addText('section_title') ->addRepeater('columns', ['min' => 1, 'layout' => 'block']) ->addTab('Content') ->addText('title') ->addWysiwyg('content') ->addFields($background) ->endRepeater() ->addFields($background) ->setLocation('post_type', '==', 'page'); ``` ::: --- ## CPT (Custom Post Type) ### File Location Create a class in the `cpt` directory found in the `include` directory. ``` WordPress/ ├── wp-content/ │ ├── themes/ │ │ ├── include/ │ │ │ └── cpt/ │ │ │ └── example-cpt.php │ │ └── ... │ └── ... └── ... ``` #### CPT Class Skeleton In the CPT class file the below snippet is the basic structure :::spoiler {state="open"} Code ```php= <?php namespace JBC\Include\CPT; /** * Functions which enhance the theme by adding Custom Post Type (CPT) into WordPress * * @package Example_Theme */ class ExampleCPT { public function register() { $this->example_cpt(); $this->example_cpt_categories(); $this->example_cpt_template(); $this->example_settings_menu(); acf_add_local_field_group( $this->example_settings_acf() ); acf_add_local_field_group( $this->example_categories_acf() ); } public function example_cpt() { ... } private function example_cpt_categories() { ... } private function example_cpt_template() { ... } private function example_settings_menu() { ... } private function example_settings_acf() { ... } private function example_categories_acf() { ... } } ``` ::: #### example_cpt() Code example: :::spoiler {state="open"} Code ```php= <?php public function example_cpt() { $supports = [ 'title', // post title 'editor', // post content 'author', // post author 'thumbnail', // featured images 'excerpt', // post excerpt 'custom-fields', // custom fields 'comments', // post comments 'revisions', // post revisions 'post-formats', // post formats ]; $labels = [ 'name' => _x( 'Examples', 'plural' ), 'singular_name' => _x( 'Example', 'singular' ), 'menu_name' => _x( 'Example', 'admin menu' ), 'name_admin_bar' => _x( 'Example', 'admin bar' ), 'add_new' => _x( 'Add New', 'add new' ), 'add_new_item' => __( 'Add New Example' ), 'new_item' => __( 'New Example' ), 'edit_item' => __( 'Edit Example' ), 'view_item' => __( 'View Example' ), 'all_items' => __( 'All Examples' ), 'search_items' => __( 'Search Examples' ), 'not_found' => __( 'No Example found.' ), ]; $args = [ 'supports' => $supports, 'labels' => $labels, 'public' => TRUE, 'query_var' => TRUE, 'rewrite' => [ 'slug' => 'example' ], 'has_archive' => TRUE, 'hierarchical' => FALSE, ]; register_post_type( 'example', $args ); } ``` ::: #### example_cpt_categories() Code example: :::spoiler {state="open"} Code ```php= <?php private function example_cpt_categories() { $labels = [ 'name' => _x( 'Example Categories', 'taxonomy general name', 'example-theme' ), 'singular_name' => _x( 'Example Category', 'taxonomy singular name', 'example-theme' ), 'search_items' => __( 'Search Example Categories', 'example-theme' ), 'all_items' => __( 'All Example Categories', 'example-theme' ), 'parent_item' => __( 'Parent Example Category', 'example-theme' ), 'parent_item_colon' => __( 'Parent Example Category:', 'example-theme' ), 'edit_item' => __( 'Edit Example Category', 'example-theme' ), 'update_item' => __( 'Update Example Category', 'example-theme' ), 'add_new_item' => __( 'Add New Example Category', 'example-theme' ), 'new_item_name' => __( 'New Example Category Name', 'example-theme' ), 'menu_name' => __( 'Example Category', 'example-theme' ), ]; $args = [ 'hierarchical' => TRUE, 'labels' => $labels, 'show_ui' => TRUE, 'show_admin_column' => TRUE, 'query_var' => TRUE, 'show_in_rest' => TRUE, ]; register_taxonomy( 'examples_categories', [ 'examples' ], $args ); } ``` ::: #### example_cpt_template() Code example: :::spoiler {state="open"} Code ```php=12 <?php private function example_cpt_template() { $page_type_object = get_post_type_object( 'examples' ); $page_type_object->template = [ [ 'acf/examples-block', [ 'data' => [ 'examples-block_title' => 'Related examples', '"_examples-block_title' => 'field_examples_block_examples-block_title', 'examples-block_sub_title' => '', '_examples-block_sub_title' => 'field_examples_block_examples-block_sub_title', 'examples-block_link' => [ "title" => 'All examples', "url" => '/examples', "target" => '', ], '_examples-block_link' => 'field_examples_block_examples-block_link', 'examples-block' => '', '_examples-block' => 'field_examples_block_examples-block', ], ], ], ]; } ``` ::: #### example_settings_menu() Code example: :::spoiler {state="open"} Code ```php= <?php private function example_settings_menu() { if ( function_exists( 'acf_add_options_page' ) ) { acf_add_options_page( [ 'page_title' => 'Examples Settings', 'menu_slug' => 'examples-settings', 'menu_title' => 'Setting', 'parent_slug' => 'edit.php?post_type=examples', ] ); } } ``` ::: #### example_settings_acf() Set up the same as the [Register ACF Fields to block](#Register-ACF-Fields-to-block) changing the location to the setting page. Code example: :::spoiler {state="open"} Code ```php= <?php private function example_settings_acf() { ... $block->setLocation( 'options_page', '==', 'examples-settings' ); ... } ``` #### example_categories_acf() Set up the same as the [Register ACF Fields to block](#Register-ACF-Fields-to-block) changing the location to the categories. Code example: :::spoiler {state="open"} Code ```php= <?php private function example_categories_acf() { ... $block ->setLocation( 'taxonomy', '==', 'examples_categories' ); ... } ``` #### Register CPT To register a CPT add the class to `getCptList()` return array in the `register-cpt.php` found in `theme-name/include`. :::spoiler {state="open"} Code ```php= <?php function getCptList(): array { return [ ExampleCPT::class ]; } ``` ::: ---