├── BlocksLogo.png ├── README.md ├── acf-block-field.php ├── advanced-custom-blocks.php └── assets ├── 44556941-25c55100-a70a-11e8-8ff5-b51c312be386.png ├── 44556962-49889700-a70a-11e8-89de-b8c6f9693cd8.png ├── 44556981-6755fc00-a70a-11e8-9a69-47a9aad03f3d.png ├── 44557063-d4699180-a70a-11e8-8f55-3b8528852361.png └── BlocksLogo.png /BlocksLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rchipka/advanced-custom-blocks/c6b80345ea9ebcf10464480ce043ef662a9f8b0f/BlocksLogo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced Custom Blocks 2 | 3 | Create custom blocks for the new Wordpress [Gutenberg editor](https://github.com/WordPress/gutenberg) using [Advanced Custom Fields](https://github.com/elliotcondon/acf). 4 | 5 | ![](assets/BlocksLogo.png) 6 | 7 | 8 | 9 | 10 | 11 |

12 | Key Features • 13 | Installation • 14 | How to make a block • 15 | How To Use The Block • 16 | Credits • 17 | Related • 18 | License 19 | 20 |

21 | 22 | ## Motivation 23 | Make an easy straightforward way for Wordpress developers to create custom Gutenberg blocks using a pipeline they are already familiar with. 24 | 25 | ## Installation 26 | 27 | Download the zipped folder from Github, and then [install manually to Wordpress via upload](https://themetry.com/docs/install-wordpress-plugin-zip-upload/) in the admin interface. 28 | 29 | ## How to make a block 30 | 1. First create a custom field group in ACF and set the location rules to target the block. 31 | 32 | ![screen shot 2018-08-23 at 7 23 20 pm](assets/44556941-25c55100-a70a-11e8-8ff5-b51c312be386.png) 33 | 34 | 2. Next, at the bottom of the field group page you can set the block name, and the icon that will appear in the Gutenberg block menu. 35 | 36 | ![screen shot 2018-08-23 at 7 23 33 pm](assets/44556962-49889700-a70a-11e8-89de-b8c6f9693cd8.png) 37 | 38 | 3. Create `testimonial.php` inside of the `blocks/acf/` directory within your theme 39 | 40 | ``` 41 |
42 | 43 |

44 | 45 |
46 | ``` 47 | 48 | 49 | ## How to use the block 50 | 51 | It's really simple! Just add the block when from the normal Gutenberg insert block menu when writing a post. 52 | 53 | ![screen shot 2018-08-23 at 7 13 59 pm](assets/44556981-6755fc00-a70a-11e8-9a69-47a9aad03f3d.png) 54 | 55 | And you can add multiple blocks of the same kind, similar to the repeater field. 56 | 57 | ![screen shot 2018-08-23 at 7 21 38 pm](assets/44557063-d4699180-a70a-11e8-8f55-3b8528852361.png) 58 | 59 | 60 | ## Rendering block content 61 | 62 | Here are the ways in which Advanced Custom Blocks will load block content: 63 | 64 | ```php 65 | # Prepend 66 | do_action("acf/before_render_block", $attributes); 67 | do_action("acf/before_render_block/name=$block_name", $attributes); 68 | 69 | 70 | # Block content actions 71 | do_action("acf/render_block", $attributes); 72 | do_action("acf/render_block/name=$block_name", $attributes); 73 | 74 | 75 | # Block content templates 76 | include(get_template_directory() . "/blocks/acf/$block_name.php"); 77 | include(get_template_directory() . "/blocks/acf-$block_name.php"); 78 | 79 | 80 | # Append 81 | do_action("acf/after_render_block", $attributes); 82 | do_action("acf/after_render_block/name=$block_name", $attributes); 83 | ``` 84 | 85 | 86 | ## Contribute 87 | 88 | Please contribute! Just fork this repo and make a pull request. 89 | 90 | 91 | ## TODOs: 92 | 93 | * Create a "Block" field type so that we can have inner blocks as fields 94 | * Location rules for block placement and quantity restrictions 95 | * Sidebar positioning for block field groups 96 | 97 | ## Credits 98 | 99 | See [this thread](https://github.com/elliotcondon/acf/issues/622) for original project discussion. 100 | 101 | Project by [Robbie Chipka](https://github.com/rchipka) 102 | 103 | Logo by [David Aslan French](https://github.com/thedonquixotic) (forked from [Darius Dan](https://www.flaticon.com/authors/darius-dan) ) 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /acf-block-field.php: -------------------------------------------------------------------------------- 1 | name = 'block'; 36 | 37 | 38 | /* 39 | * label (string) Multiple words, can include spaces, visible when selecting a field type 40 | */ 41 | 42 | $this->label = __('Block', 'TEXTDOMAIN'); 43 | 44 | 45 | /* 46 | * category (string) basic | content | choice | relational | jquery | layout | CUSTOM GROUP NAME 47 | */ 48 | 49 | $this->category = 'layout'; 50 | 51 | 52 | /* 53 | * defaults (array) Array of default settings which are merged into the field object. These are used later in settings 54 | */ 55 | 56 | $this->defaults = array( 57 | // 'font_size' => 14, 58 | ); 59 | 60 | 61 | /* 62 | * l10n (array) Array of strings that are used in JavaScript. This allows JS strings to be translated in PHP and loaded via: 63 | * var message = acf._e('block', 'error'); 64 | */ 65 | 66 | $this->l10n = array( 67 | 'error' => __('Error! Please enter a higher value', 'TEXTDOMAIN'), 68 | ); 69 | 70 | 71 | /* 72 | * settings (array) Store plugin settings (url, path, version) as a reference for later use with assets 73 | */ 74 | 75 | $this->settings = $settings; 76 | 77 | 78 | // do not delete! 79 | parent::__construct(); 80 | 81 | } 82 | 83 | 84 | /* 85 | * render_field_settings() 86 | * 87 | * Create extra settings for your field. These are visible when editing a field 88 | * 89 | * @type action 90 | * @since 3.6 91 | * @date 23/01/13 92 | * 93 | * @param $field (array) the $field being edited 94 | * @return n/a 95 | */ 96 | 97 | function render_field_settings( $field ) { 98 | 99 | /* 100 | * acf_render_field_setting 101 | * 102 | * This function will create a setting for your field. Simply pass the $field parameter and an array of field settings. 103 | * The array of settings does not require a `value` or `prefix`; These settings are found from the $field array. 104 | * 105 | * More than one setting can be added by copy/paste the above code. 106 | * Please note that you must also have a matching $defaults value for the field name (font_size) 107 | */ 108 | 109 | // acf_render_field_setting( $field, array( 110 | // 'label' => __('Font Size','TEXTDOMAIN'), 111 | // 'instructions' => __('Customise the input font size','TEXTDOMAIN'), 112 | // 'type' => 'number', 113 | // 'name' => 'font_size', 114 | // 'prepend' => 'px', 115 | // )); 116 | 117 | } 118 | 119 | 120 | 121 | /* 122 | * render_field() 123 | * 124 | * Create the HTML interface for your field 125 | * 126 | * @param $field (array) the $field being rendered 127 | * 128 | * @type action 129 | * @since 3.6 130 | * @date 23/01/13 131 | * 132 | * @param $field (array) the $field being edited 133 | * @return n/a 134 | */ 135 | 136 | function render_field( $field ) { 137 | 138 | 139 | /* 140 | * Review the data of $field. 141 | * This will show what data is available 142 | */ 143 | 144 | $block_id = acb_current_block('block_id'); 145 | $parent_post_id = acb_current_block('post_id'); 146 | 147 | if (!$block_id) { 148 | echo '
';
149 |         print_r( $field );
150 |       echo '
'; 151 | 152 | return; 153 | } 154 | 155 | $field_id = $field['id']; 156 | $post_name = implode('-', ['acf-child-block', $parent_post_id, $block_id, $field_id]); 157 | echo '
';
158 |         print_r( $post_name );
159 |         print_r( $field );
160 |       echo '
'; 161 | 162 | if (!isset($field['value']) || !$field['value'] || !(get_post($field['value']))) { 163 | $field['value'] = intval(wp_insert_post([ 164 | 'post_type' => 'acf-child-block', 165 | 'post_parent' => $parent_post_id, 166 | 'post_name' => $post_name, 167 | ])); 168 | 169 | update_post_meta($parent_post_id, $field_id, $field['value']); 170 | } 171 | 172 | $post = get_post($field['value']); 173 | $editor_settings = acb_get_editor_settings($post); 174 | $post_name .= '-' . rand(0, 99999); 175 | ?> 176 | 177 |
178 | content 179 | post_content); ?> 180 |
181 | settings['url']; 205 | $version = $this->settings['version']; 206 | 207 | 208 | // register & include JS 209 | wp_register_script('TEXTDOMAIN', "{$url}assets/js/input.js", array('acf-input'), $version); 210 | wp_enqueue_script('TEXTDOMAIN'); 211 | 212 | 213 | // register & include CSS 214 | wp_register_style('TEXTDOMAIN', "{$url}assets/css/input.css", array('acf-input'), $version); 215 | wp_enqueue_style('TEXTDOMAIN'); 216 | 217 | } 218 | 219 | */ 220 | 221 | 222 | /* 223 | * input_admin_head() 224 | * 225 | * This action is called in the admin_head action on the edit screen where your field is created. 226 | * Use this action to add CSS and JavaScript to assist your render_field() action. 227 | * 228 | * @type action (admin_head) 229 | * @since 3.6 230 | * @date 23/01/13 231 | * 232 | * @param n/a 233 | * @return n/a 234 | */ 235 | 236 | /* 237 | 238 | function input_admin_head() { 239 | 240 | 241 | 242 | } 243 | 244 | */ 245 | 246 | 247 | /* 248 | * input_form_data() 249 | * 250 | * This function is called once on the 'input' page between the head and footer 251 | * There are 2 situations where ACF did not load during the 'acf/input_admin_enqueue_scripts' and 252 | * 'acf/input_admin_head' actions because ACF did not know it was going to be used. These situations are 253 | * seen on comments / user edit forms on the front end. This function will always be called, and includes 254 | * $args that related to the current screen such as $args['post_id'] 255 | * 256 | * @type function 257 | * @date 6/03/2014 258 | * @since 5.0.0 259 | * 260 | * @param $args (array) 261 | * @return n/a 262 | */ 263 | 264 | /* 265 | 266 | function input_form_data( $args ) { 267 | 268 | 269 | 270 | } 271 | 272 | */ 273 | 274 | 275 | /* 276 | * input_admin_footer() 277 | * 278 | * This action is called in the admin_footer action on the edit screen where your field is created. 279 | * Use this action to add CSS and JavaScript to assist your render_field() action. 280 | * 281 | * @type action (admin_footer) 282 | * @since 3.6 283 | * @date 23/01/13 284 | * 285 | * @param n/a 286 | * @return n/a 287 | */ 288 | 289 | /* 290 | 291 | function input_admin_footer() { 292 | 293 | 294 | 295 | } 296 | 297 | */ 298 | 299 | 300 | /* 301 | * field_group_admin_enqueue_scripts() 302 | * 303 | * This action is called in the admin_enqueue_scripts action on the edit screen where your field is edited. 304 | * Use this action to add CSS + JavaScript to assist your render_field_options() action. 305 | * 306 | * @type action (admin_enqueue_scripts) 307 | * @since 3.6 308 | * @date 23/01/13 309 | * 310 | * @param n/a 311 | * @return n/a 312 | */ 313 | 314 | /* 315 | 316 | function field_group_admin_enqueue_scripts() { 317 | 318 | } 319 | 320 | */ 321 | 322 | 323 | /* 324 | * field_group_admin_head() 325 | * 326 | * This action is called in the admin_head action on the edit screen where your field is edited. 327 | * Use this action to add CSS and JavaScript to assist your render_field_options() action. 328 | * 329 | * @type action (admin_head) 330 | * @since 3.6 331 | * @date 23/01/13 332 | * 333 | * @param n/a 334 | * @return n/a 335 | */ 336 | 337 | /* 338 | 339 | function field_group_admin_head() { 340 | 341 | } 342 | 343 | */ 344 | 345 | 346 | /* 347 | * load_value() 348 | * 349 | * This filter is applied to the $value after it is loaded from the db 350 | * 351 | * @type filter 352 | * @since 3.6 353 | * @date 23/01/13 354 | * 355 | * @param $value (mixed) the value found in the database 356 | * @param $post_id (mixed) the $post_id from which the value was loaded 357 | * @param $field (array) the field array holding all the field options 358 | * @return $value 359 | */ 360 | 361 | /* 362 | 363 | function load_value( $value, $post_id, $field ) { 364 | 365 | return $value; 366 | 367 | } 368 | 369 | */ 370 | 371 | 372 | /* 373 | * update_value() 374 | * 375 | * This filter is applied to the $value before it is saved in the db 376 | * 377 | * @type filter 378 | * @since 3.6 379 | * @date 23/01/13 380 | * 381 | * @param $value (mixed) the value found in the database 382 | * @param $post_id (mixed) the $post_id from which the value was loaded 383 | * @param $field (array) the field array holding all the field options 384 | * @return $value 385 | */ 386 | 387 | /* 388 | 389 | function update_value( $value, $post_id, $field ) { 390 | 391 | return $value; 392 | 393 | } 394 | 395 | */ 396 | 397 | 398 | /* 399 | * format_value() 400 | * 401 | * This filter is appied to the $value after it is loaded from the db and before it is returned to the template 402 | * 403 | * @type filter 404 | * @since 3.6 405 | * @date 23/01/13 406 | * 407 | * @param $value (mixed) the value which was loaded from the database 408 | * @param $post_id (mixed) the $post_id from which the value was loaded 409 | * @param $field (array) the field array holding all the field options 410 | * 411 | * @return $value (mixed) the modified value 412 | */ 413 | 414 | /* 415 | 416 | function format_value( $value, $post_id, $field ) { 417 | 418 | // bail early if no value 419 | if( empty($value) ) { 420 | 421 | return $value; 422 | 423 | } 424 | 425 | 426 | // apply setting 427 | if( $field['font_size'] > 12 ) { 428 | 429 | // format the value 430 | // $value = 'something'; 431 | 432 | } 433 | 434 | 435 | // return 436 | return $value; 437 | } 438 | 439 | */ 440 | 441 | 442 | /* 443 | * validate_value() 444 | * 445 | * This filter is used to perform validation on the value prior to saving. 446 | * All values are validated regardless of the field's required setting. This allows you to validate and return 447 | * messages to the user if the value is not correct 448 | * 449 | * @type filter 450 | * @date 11/02/2014 451 | * @since 5.0.0 452 | * 453 | * @param $valid (boolean) validation status based on the value and the field's required setting 454 | * @param $value (mixed) the $_POST value 455 | * @param $field (array) the field array holding all the field options 456 | * @param $input (string) the corresponding input name for $_POST value 457 | * @return $valid 458 | */ 459 | 460 | /* 461 | 462 | function validate_value( $valid, $value, $field, $input ){ 463 | 464 | // Basic usage 465 | if( $value < $field['custom_minimum_setting'] ) 466 | { 467 | $valid = false; 468 | } 469 | 470 | 471 | // Advanced usage 472 | if( $value < $field['custom_minimum_setting'] ) 473 | { 474 | $valid = __('The value is too little!','TEXTDOMAIN'), 475 | } 476 | 477 | 478 | // return 479 | return $valid; 480 | 481 | } 482 | 483 | */ 484 | 485 | 486 | /* 487 | * delete_value() 488 | * 489 | * This action is fired after a value has been deleted from the db. 490 | * Please note that saving a blank value is treated as an update, not a delete 491 | * 492 | * @type action 493 | * @date 6/03/2014 494 | * @since 5.0.0 495 | * 496 | * @param $post_id (mixed) the $post_id from which the value was deleted 497 | * @param $key (string) the $meta_key which the value was deleted 498 | * @return n/a 499 | */ 500 | 501 | /* 502 | 503 | function delete_value( $post_id, $key ) { 504 | 505 | 506 | 507 | } 508 | 509 | */ 510 | 511 | 512 | /* 513 | * load_field() 514 | * 515 | * This filter is applied to the $field after it is loaded from the database 516 | * 517 | * @type filter 518 | * @date 23/01/2013 519 | * @since 3.6.0 520 | * 521 | * @param $field (array) the field array holding all the field options 522 | * @return $field 523 | */ 524 | 525 | /* 526 | 527 | function load_field( $field ) { 528 | 529 | return $field; 530 | 531 | } 532 | 533 | */ 534 | 535 | 536 | /* 537 | * update_field() 538 | * 539 | * This filter is applied to the $field before it is saved to the database 540 | * 541 | * @type filter 542 | * @date 23/01/2013 543 | * @since 3.6.0 544 | * 545 | * @param $field (array) the field array holding all the field options 546 | * @return $field 547 | */ 548 | 549 | /* 550 | 551 | function update_field( $field ) { 552 | 553 | return $field; 554 | 555 | } 556 | 557 | */ 558 | 559 | 560 | /* 561 | * delete_field() 562 | * 563 | * This action is fired after a field is deleted from the database 564 | * 565 | * @type action 566 | * @date 11/02/2014 567 | * @since 5.0.0 568 | * 569 | * @param $field (array) the field array holding all the field options 570 | * @return n/a 571 | */ 572 | 573 | /* 574 | 575 | function delete_field( $field ) { 576 | 577 | 578 | 579 | } 580 | 581 | */ 582 | 583 | 584 | } 585 | 586 | function acb_get_editor_settings($post) { 587 | 588 | $available_templates = wp_get_theme()->get_page_templates( get_post( $post->ID ) ); 589 | $available_templates = ! empty( $available_templates ) ? array_merge( 590 | array( 591 | '' => apply_filters( 'default_page_template_title', __( 'Default template', 'gutenberg' ), 'rest-api' ), 592 | ), 593 | $available_templates 594 | ) : $available_templates; 595 | 596 | $gutenberg_theme_support = get_theme_support( 'gutenberg' ); 597 | $align_wide = get_theme_support( 'align-wide' ); 598 | $color_palette = current( (array) get_theme_support( 'editor-color-palette' ) ); 599 | $font_sizes = current( (array) get_theme_support( 'editor-font-sizes' ) ); 600 | $max_upload_size = wp_max_upload_size(); 601 | if ( ! $max_upload_size ) { 602 | $max_upload_size = 0; 603 | } 604 | global $editor_styles; 605 | $styles = array( 606 | array( 607 | 'css' => file_get_contents( 608 | gutenberg_dir_path() . 'build/editor/editor-styles.css' 609 | ), 610 | ), 611 | ); 612 | if ( $editor_styles && current_theme_supports( 'editor-styles' ) ) { 613 | foreach ( $editor_styles as $style ) { 614 | if ( filter_var( $style, FILTER_VALIDATE_URL ) ) { 615 | $styles[] = array( 616 | 'css' => file_get_contents( $style ), 617 | ); 618 | } else { 619 | $file = get_theme_file_path( $style ); 620 | $styles[] = array( 621 | 'css' => file_get_contents( get_theme_file_path( $style ) ), 622 | 'baseURL' => get_theme_file_uri( $style ), 623 | ); 624 | } 625 | } 626 | } 627 | 628 | // Lock settings. 629 | $user_id = wp_check_post_lock( $post->ID ); 630 | if ( $user_id ) { 631 | /** 632 | * Filters whether to show the post locked dialog. 633 | * 634 | * Returning a falsey value to the filter will short-circuit displaying the dialog. 635 | * 636 | * @since 3.6.0 637 | * 638 | * @param bool $display Whether to display the dialog. Default true. 639 | * @param WP_Post $post Post object. 640 | * @param WP_User|bool $user The user id currently editing the post. 641 | */ 642 | if ( apply_filters( 'show_post_locked_dialog', true, $post, $user_id ) ) { 643 | $locked = true; 644 | } 645 | 646 | $user_details = null; 647 | if ( $locked ) { 648 | $user = get_userdata( $user_id ); 649 | $user_details = array( 650 | 'name' => $user->display_name, 651 | ); 652 | $avatar = get_avatar( $user_id, 64 ); 653 | if ( $avatar ) { 654 | if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) { 655 | $user_details['avatar'] = $matches[1]; 656 | } 657 | } 658 | } 659 | 660 | $lock_details = array( 661 | 'isLocked' => $locked, 662 | 'user' => $user_details, 663 | ); 664 | } else { 665 | 666 | // Lock the post. 667 | $active_post_lock = wp_set_post_lock( $post->ID ); 668 | $lock_details = array( 669 | 'isLocked' => false, 670 | 'activePostLock' => esc_attr( implode( ':', $active_post_lock ) ), 671 | ); 672 | } 673 | 674 | $editor_settings = array( 675 | 'alignWide' => $align_wide || ! empty( $gutenberg_theme_support[0]['wide-images'] ), 676 | 'availableTemplates' => $available_templates, 677 | 'allowedBlockTypes' => apply_filters( 'allowed_block_types', true, $post ), 678 | 'disableCustomColors' => get_theme_support( 'disable-custom-colors' ), 679 | 'disableCustomFontSizes' => get_theme_support( 'disable-custom-font-sizes' ), 680 | 'disablePostFormats' => ! current_theme_supports( 'post-formats' ), 681 | 'titlePlaceholder' => apply_filters( 'enter_title_here', __( 'Add title', 'gutenberg' ), $post ), 682 | 'bodyPlaceholder' => apply_filters( 'write_your_story', __( 'Write your story', 'gutenberg' ), $post ), 683 | 'isRTL' => is_rtl(), 684 | 'autosaveInterval' => 10, 685 | 'maxUploadFileSize' => $max_upload_size, 686 | 'allowedMimeTypes' => get_allowed_mime_types(), 687 | 'styles' => $styles, 688 | 'postLock' => $lock_details, 689 | 'postLockUtils' => array( 690 | 'nonce' => wp_create_nonce( 'lock-post_' . $post->ID ), 691 | 'unlockNonce' => wp_create_nonce( 'update-post_' . $post->ID ), 692 | 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 693 | ), 694 | ); 695 | 696 | return $editor_settings; 697 | } 698 | 699 | 700 | // initialize 701 | new ACB_acf_field_block( [] ); 702 | 703 | 704 | // class_exists check 705 | endif; 706 | 707 | ?> 708 | -------------------------------------------------------------------------------- /advanced-custom-blocks.php: -------------------------------------------------------------------------------- 1 | '; 55 | }, 10, 2); 56 | 57 | add_filter('acf/location/rule_match/block_name', function ( $match, $rule, $options ) { 58 | if (empty($rule['value'])) { 59 | return true; 60 | } 61 | 62 | return ($rule['operator'] === '==') === (acb_current_block('name') == $rule['value']); 63 | }, 10, 3); 64 | 65 | add_filter('acf/location/screen', function ($screen, $field_group) { 66 | if (!isset($field_group['block_name']) || !$field_group['block_name']) { 67 | return $screen; 68 | } 69 | 70 | if (!isset($screen['post_id']) || !$screen['post_id']) { 71 | $screen['post_id'] = $_REQUEST['post'] ?: $_REQUEST['post_id'] ?: $_REQUEST['attributes']['post_id']; 72 | } 73 | 74 | return $screen; 75 | }, 1, 2); 76 | 77 | add_filter('get_post_metadata', function ($orig_value, $post_id, $meta_key, $single) { 78 | $block_meta = acb_current_block('block_meta'); 79 | 80 | if (!$block_meta || !isset($block_meta[$meta_key])) { 81 | return $orig_value; 82 | } 83 | 84 | $meta_value = $block_meta[$meta_key]; 85 | 86 | if ($single && is_array($meta_value)) { 87 | $meta_value = $meta_value[0]; 88 | } 89 | 90 | if (!$single && !is_array($meta_value)) { 91 | $meta_value = [$meta_value]; 92 | } 93 | 94 | // $type = 'single'; 95 | 96 | // if (!$single) { 97 | // $type = 'array'; 98 | // } 99 | 100 | // error_log('Block #' . acb_current_block('block_id') . ' - overriding ' . $type . ' value for ' . json_encode($meta_key) . ' from ' . json_encode($orig_value) . ' to ' . json_encode($meta_value)); 101 | 102 | return $meta_value; 103 | }, 0, 4); 104 | 105 | add_action('acf/render_field_group_settings', function ($field_group) { 106 | acf_render_field_wrap(array( 107 | 'label' => 'Block Name', 108 | 'instructions' => '', 109 | 'type' => 'text', 110 | 'name' => 'block_name', 111 | 'prefix' => 'acf_field_group', 112 | 'value' => $field_group['block_name'], 113 | )); 114 | 115 | acf_render_field_wrap(array( 116 | 'label' => 'Block Icon', 117 | 'instructions' => '', 118 | 'type' => 'text', 119 | 'name' => 'block_icon', 120 | 'placeholder' => 'Dashicons class name', 121 | 'prefix' => 'acf_field_group', 122 | 'value' => $field_group['block_icon'], 123 | )); 124 | 125 | acf_render_field_wrap(array( 126 | 'label' => 'Block Category', 127 | 'instructions' => '', 128 | 'type' => 'text', 129 | 'name' => 'block_category', 130 | 'prefix' => 'acf_field_group', 131 | 'placeholder' => 'Widgets', 132 | 'value' => $field_group['block_category'], 133 | )); 134 | }, 10, 1); 135 | 136 | function my_plugin_block_categories( $categories, $post ) { 137 | if ( $post->post_type !== 'post' ) { 138 | return $categories; 139 | } 140 | return array_merge( 141 | $categories, 142 | array( 143 | array( 144 | 'slug' => 'my-category', 145 | 'title' => __( 'My category', 'my-plugin' ), 146 | ), 147 | ) 148 | ); 149 | } 150 | 151 | add_filter( 'block_categories', function ($block_categories, $post) { 152 | $block_category_slugs = wp_list_pluck($block_categories, 'slug'); 153 | 154 | setup_postdata($post); 155 | 156 | foreach (wp_list_pluck(acf_gb_get_block_field_groups(), 'block_category') as $category) { 157 | $slug = sanitize_title($category); 158 | 159 | if (!in_array($slug, $block_category_slugs)) { 160 | $block_category_slugs[] = $slug; 161 | $block_categories[] = [ 162 | 'slug' => $slug, 163 | 'title' => $category 164 | ]; 165 | } 166 | } 167 | 168 | wp_reset_postdata(); 169 | 170 | return $block_categories; 171 | }, 5, 2 ); 172 | 173 | 174 | add_action( 'init', function () { 175 | register_post_type('acf-child-block', [ 176 | 'label' => 'ACF Block', 177 | 'labels' => [], 178 | 'public' => true, 179 | 'show_in_menu' => false, 180 | 'capability_type' => 'page', 181 | 'supports' => array( 'title', 'editor', 'comments', 'thumbnail', 'custom-fields' ), 182 | ]); 183 | 184 | if (!function_exists('register_block_type')) { 185 | return; 186 | } 187 | 188 | foreach (acf_gb_get_block_field_groups() as $group) { 189 | register_block_type( 'acf/' . $group['block_name'], array( 190 | 'attributes' => array( 191 | 'post_id' => array( 192 | 'type' => 'number', 193 | 'default' => 0, 194 | ), 195 | 'block_id' => array( 196 | 'type' => 'string', 197 | 'default' => '', 198 | ), 199 | 'block_name' => array( 200 | 'type' => 'string', 201 | 'default' => $group['block_name'], 202 | ), 203 | 'acf_fields' => array( 204 | 'type' => 'object', 205 | 'default' => [], 206 | ), 207 | 'acf_field_group' => array( 208 | 'type' => 'number', 209 | 'default' => 0, 210 | ), 211 | ), 212 | 'render_callback' => function ($attributes) { 213 | global $post; 214 | global $acb_block; 215 | 216 | $output = ''; 217 | 218 | if ($post instanceof WP_Post && $post->ID) { 219 | $post_id = $attributes['post_id'] = $post->ID; 220 | } else if ($attributes['post_id']) { 221 | $post_id = intval($attributes['post_id']); 222 | setup_postdata($post = get_post($post_id)); 223 | } 224 | 225 | $cache_active = acf_is_cache_active(); 226 | 227 | if ($cache_active) { 228 | acf_disable_cache(); 229 | } 230 | 231 | $attributes['block_meta'] = get_post_meta($post_id, '__block_' . $attributes['block_id'], true); 232 | 233 | if (!is_array($attributes['block_meta'])) { 234 | $attributes['block_meta'] = []; 235 | } 236 | 237 | if (isset($_REQUEST['attributes']) && isset($_REQUEST['attributes']['acf_fields'])) { 238 | $post_id = $_REQUEST['attributes']['post_id']; 239 | $block_id = $_REQUEST['attributes']['block_id']; 240 | $acf_fields = $attributes['acf_fields'] = $_REQUEST['attributes']['acf_fields']; 241 | 242 | $fields = array_map(function ($field_key) { 243 | return get_field_object($field_key); 244 | }, array_keys($acf_fields)); 245 | 246 | $filter_keys = []; 247 | 248 | foreach ($fields as $field) { 249 | if (!$field['name']) { 250 | continue; 251 | } 252 | 253 | $filter_keys[] = '_' . $field['name']; 254 | $filter_keys[] = $field['name']; 255 | } 256 | 257 | acf_save_post( $post_id, $acf_fields ); 258 | 259 | if (sizeof($filter_keys) > 0) { 260 | $attributes['block_meta'] = []; 261 | $regex = '/^(' . implode($filter_keys, '|') . ')/'; 262 | 263 | foreach (get_post_meta($post_id) as $meta_key => $meta_values) { 264 | if (!preg_match($regex, $meta_key)) { 265 | continue; 266 | } 267 | 268 | $attributes['block_meta'][$meta_key] = $meta_values[0]; 269 | } 270 | } 271 | 272 | // foreach ($fields as $field) { 273 | // $meta_value = $attributes['block_meta'][$field_name]; 274 | // add_post_meta($post_id, $field['name'], $meta_value, false); 275 | // } 276 | 277 | update_post_meta($post_id, '__block_' . $block_id, $attributes['block_meta']); 278 | } 279 | 280 | $acb_block = $attributes; 281 | 282 | if ($_GET['context'] === 'edit') { 283 | ob_start(); 284 | $fields = acf_get_fields($attributes['acf_field_group']); 285 | acf_render_fields($fields, $attributes['post_id']); 286 | $output .= trim(ob_get_contents()); 287 | ob_end_clean(); 288 | 289 | if (!$output) { 290 | $output = '
'; 291 | } 292 | } else { 293 | ob_start(); 294 | 295 | do_action('acf/before_render_block', $attributes); 296 | do_action('acf/before_render_block/name=' . $attributes['block_name'], $attributes); 297 | 298 | do_action('acf/render_block', $attributes); 299 | do_action('acf/render_block/name=' . $attributes['block_name'], $attributes); 300 | 301 | $file_name = $attributes['block_name'] . '.php'; 302 | 303 | foreach (['/blocks/acf/' . $file_name, '/blocks/acf-' . $file_name] as $path) { 304 | if (file_exists(get_stylesheet_directory() . $path)) { 305 | include(get_stylesheet_directory() . $path); 306 | } 307 | } 308 | 309 | do_action('acf/after_render_block', $attributes); 310 | do_action('acf/after_render_block/name=' . $attributes['block_name'], $attributes); 311 | 312 | $output .= ob_get_contents(); 313 | ob_end_clean(); 314 | } 315 | 316 | reset_rows(); 317 | wp_reset_postdata(); 318 | $acb_block = null; 319 | 320 | if ($cache_active) { 321 | acf_enable_cache(); 322 | } 323 | 324 | return $output; 325 | }, 326 | ) ); 327 | } 328 | }); 329 | 330 | function acf_gb_get_block_field_groups() { 331 | global $acb_block; 332 | 333 | $groups = []; 334 | 335 | if (isset($_REQUEST['post']) && $_REQUEST['post']) { 336 | setup_postdata($post = get_post($_REQUEST['post'])); 337 | } 338 | 339 | foreach (acf_get_field_groups() as $group) { 340 | $acb_block = [ 341 | 'block_name' => ($is_block = (isset($group['block_name']) ?: $group['block_name'])), 342 | ]; 343 | 344 | if ($is_block) { 345 | if (!isset($group['block_category']) || !$group['block_category']) { 346 | $group['block_category'] = 'widgets'; 347 | } 348 | 349 | if (isset($group['block_category'])) { 350 | $group['block_category_slug'] = sanitize_title($group['block_category']); 351 | } 352 | 353 | if (acf_get_field_group_visibility($group)) { 354 | $groups[] = $group; 355 | } 356 | } 357 | 358 | $acb_block = null; 359 | } 360 | 361 | if ($_REQUEST['post']) { 362 | wp_reset_postdata(); 363 | } 364 | 365 | return $groups; 366 | } 367 | 368 | add_action('admin_notices', function () { 369 | ?> 370 | 567 | 589 |