Skip to content

Commit a143ce0

Browse files
committed
first commit
0 parents  commit a143ce0

14 files changed

+21102
-0
lines changed

.eslintrc.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
root: true
2+
env:
3+
es6: true
4+
browser: true
5+
jest: true
6+
extends:
7+
- plugin:@wordpress/eslint-plugin/recommended
8+
rules:
9+
'@wordpress/no-unsafe-wp-apis': off
10+
'import/no-extraneous-dependencies': off

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
debug.log
3+

.prettierrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require( '@wordpress/prettier-config' );

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Data Types with post_content and post meta
2+
3+
`npm install` and `npm run dev-server`
4+
5+
### TODO
6+
7+
- [ ] Fix TODOs
8+
- [ ] Default pattern for created CPT
9+
- [ ] Lock all content except bound blocks
10+
- [ ] Document stuff better.

data-types.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
/**
3+
* Plugin Name: Data Types
4+
* Description: Create data types in wp-admin.
5+
* Version: 1.0
6+
*
7+
* @package data-types
8+
*/
9+
10+
declare( strict_types = 1 );
11+
12+
require_once __DIR__ . '/inc/content-area-binder.php';
13+
require_once __DIR__ . '/inc/data-type-management.php';
14+
require_once __DIR__ . '/inc/data-entry.php';
15+
require_once __DIR__ . '/inc/templating.php';

dev.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "https://playground.wordpress.net/blueprint-schema.json",
3+
"steps": [
4+
{
5+
"step": "defineWpConfigConsts",
6+
"consts": {
7+
"WP_DEBUG": true,
8+
"WP_DEBUG_LOG": "/var/www/html/wp-content/plugins/cf24-cpts/debug.log",
9+
"WP_PD_DEV": true
10+
}
11+
}
12+
]
13+
}

inc/content-area-binder.js

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
function registerContentAreaInspector( wp ) {
2+
const loadedPagePostType = wp.data
3+
.select( 'core/editor' )
4+
.getCurrentPostType();
5+
6+
const createHigherOrderComponent = wp.compose.createHigherOrderComponent;
7+
const { Fragment } = wp.element;
8+
const { InspectorControls } = wp.blockEditor;
9+
const { TextControl } = wp.components;
10+
const PanelBody = wp.components.PanelBody;
11+
12+
const withCustomInspectorControl = createHigherOrderComponent(
13+
( BlockEdit ) => {
14+
return ( props ) => {
15+
const { attributes, setAttributes, name } = props;
16+
17+
if ( name !== 'core/group' ) {
18+
return wp.element.createElement( BlockEdit, props );
19+
}
20+
21+
return wp.element.createElement( Fragment, null, [
22+
wp.element.createElement( InspectorControls, null, [
23+
wp.element.createElement(
24+
PanelBody,
25+
{
26+
title: 'Content Area',
27+
initialOpen: true,
28+
},
29+
[
30+
wp.element.createElement( TextControl, {
31+
key: 'location',
32+
label: 'location',
33+
readOnly:
34+
'data_types' !== loadedPagePostType,
35+
value: attributes.metadata?.[
36+
'data-types/binding'
37+
],
38+
onChange: ( value ) => {
39+
setAttributes( {
40+
...attributes,
41+
metadata: {
42+
...( attributes.metadata ??
43+
{} ),
44+
'data-types/binding': value,
45+
},
46+
} );
47+
},
48+
} ),
49+
]
50+
),
51+
] ),
52+
wp.element.createElement( BlockEdit, props ),
53+
] );
54+
};
55+
},
56+
'withCustomInspectorControl'
57+
);
58+
59+
wp.hooks.addFilter(
60+
'editor.BlockEdit',
61+
'data-types/content-area-binder',
62+
withCustomInspectorControl
63+
);
64+
}
65+
66+
setTimeout( registerContentAreaInspector, 800, window.wp );

inc/content-area-binder.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
/**
3+
* Exposes the content area binder within Gutenberg.
4+
*
5+
* @package data-types
6+
*/
7+
8+
add_action(
9+
'enqueue_block_editor_assets',
10+
function () {
11+
wp_enqueue_script(
12+
'data-types/content-area-binder',
13+
plugin_dir_url( __FILE__ ) . '/content-area-binder.js',
14+
array( 'wp-blocks', 'wp-dom-ready', 'wp-edit-post' ),
15+
'v1',
16+
true
17+
);
18+
}
19+
);

inc/data-entry.php

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
/**
3+
* Adds data entry capabilities.
4+
*
5+
* @package data-types
6+
*/
7+
8+
add_action( 'save_post', 'redirect_content_areas', 99, 2 );
9+
10+
/**
11+
* Redirects content areas to the appropriate place after saving the post.
12+
*
13+
* @param int $post_id The post ID.
14+
* @param WP_Post $post The post.
15+
*/
16+
function redirect_content_areas( $post_id, $post ) {
17+
// TODO: Include all data types.
18+
if ( 'movie' !== $post->post_type || 'publish' !== $post->post_status ) {
19+
return;
20+
}
21+
22+
if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
23+
return;
24+
}
25+
26+
remove_action( 'save_post', 'redirect_content_areas', 99 );
27+
28+
$blocks = parse_blocks( wp_unslash( $post->post_content ) );
29+
30+
$data = extract_content_from_blocks( $blocks );
31+
32+
wp_update_post(
33+
array(
34+
'ID' => $post_id,
35+
'post_content' => $data['post_content'],
36+
'meta_input' => $data['meta_fields'],
37+
)
38+
);
39+
40+
add_action( 'save_post', 'redirect_content_areas', 99, 2 );
41+
}
42+
43+
/**
44+
* Extract contents from the blocks.
45+
*
46+
* TODO: Fix recursion.
47+
*
48+
* @param array $blocks The blocks from the post.
49+
*/
50+
function extract_content_from_blocks( $blocks ) {
51+
$data = array(
52+
'post_content' => '',
53+
'meta_fields' => array(),
54+
);
55+
56+
foreach ( $blocks as $block ) {
57+
$binding = $block['attrs']['metadata']['data-types/binding'] ?? null;
58+
59+
if ( is_null( $binding ) ) {
60+
continue;
61+
}
62+
63+
if ( 'post_content' === $binding ) {
64+
$data['post_content'] = serialize_blocks( $block['innerBlocks'] );
65+
continue;
66+
}
67+
68+
$data['meta_fields'][ $binding ] = serialize_blocks( $block['innerBlocks'] );
69+
}
70+
71+
return $data;
72+
}
73+
74+
add_action( 'the_post', 'hydrate_data_with_content' );
75+
76+
/**
77+
* In the editor, hydrate the template with the actual data and display it.
78+
*
79+
* @param WP_Post $post The current post.
80+
*/
81+
function hydrate_data_with_content( $post ) {
82+
// TODO: Include all data types.
83+
if ( 'movie' !== $post->post_type ) {
84+
return;
85+
}
86+
87+
// TODO: Get the template from the post type.
88+
$template = get_post( 80 );
89+
$template = parse_blocks( $template->post_content );
90+
91+
// TODO: Fix recursion.
92+
foreach ( $template as $key => $block ) {
93+
$binding = $block['attrs']['metadata']['data-types/binding'] ?? null;
94+
95+
if ( is_null( $binding ) ) {
96+
continue;
97+
}
98+
99+
if ( 'post_content' === $binding ) {
100+
$content = $post->post_content;
101+
} else {
102+
$content = get_post_meta( get_the_ID(), $binding, true );
103+
}
104+
105+
$template[ $key ]['innerBlocks'] = parse_blocks( $content );
106+
107+
$new_content = backfill_html( $block['innerHTML'], $content );
108+
109+
$template[ $key ]['innerHTML'] = $new_content;
110+
$template[ $key ]['innerContent'] = array( $new_content );
111+
}
112+
113+
$post->post_content = serialize_blocks( $template );
114+
}
115+
116+
/**
117+
* Backfill the HTML.
118+
*
119+
* @param string $inner_html The markup.
120+
* @param string $block_content The content of the block.
121+
*/
122+
function backfill_html( $inner_html, $block_content ) {
123+
$p = new WP_HTML_Tag_Processor( $inner_html );
124+
$p->next_tag();
125+
$p2 = new WP_HTML_Tag_Processor( '<div>' );
126+
$p2->next_tag();
127+
foreach ( $p->get_attribute_names_with_prefix( '' ) ?? array() as $attribute ) {
128+
$p2->set_attribute( $attribute, $p->get_attribute( $attribute ) );
129+
}
130+
131+
return $p2->get_updated_html() . $block_content . '</div>';
132+
}

inc/data-type-management.php

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
/**
3+
* Adds data type registration capabilities.
4+
*
5+
* @package data-types
6+
*/
7+
8+
/**
9+
* Register the Data Types Manager in the sidebar, as well as the existing data types.
10+
*/
11+
add_action(
12+
'init',
13+
function () {
14+
register_post_type(
15+
'data_types',
16+
array(
17+
'label' => 'Data Types',
18+
'public' => true,
19+
'show_in_menu' => true,
20+
'show_in_rest' => true,
21+
)
22+
);
23+
24+
$data_types = new WP_Query( array( 'post_type' => 'data_types' ) );
25+
26+
while ( $data_types->have_posts() ) {
27+
$data_types->the_post();
28+
$data_type = get_post();
29+
$cpt_slug = strtolower( $data_type->post_title );
30+
31+
$blocks = parse_blocks( $data_type->post_content );
32+
33+
register_post_type(
34+
$cpt_slug,
35+
array(
36+
'label' => $data_type->post_title,
37+
'public' => true,
38+
'show_in_menu' => true,
39+
'show_in_rest' => true,
40+
'icon' => 'dashicons-admin-site',
41+
'template' => _convert_parsed_blocks_for_js( $blocks ),
42+
)
43+
);
44+
45+
$meta_fields = _get_meta_fields( $blocks );
46+
47+
foreach ( $meta_fields as $meta_field ) {
48+
register_post_meta(
49+
$cpt_slug,
50+
$meta_field,
51+
array(
52+
'show_in_rest' => true,
53+
'single' => true,
54+
'type' => 'string',
55+
'default' => $meta_field,
56+
)
57+
);
58+
}
59+
}
60+
},
61+
0
62+
);
63+
64+
/**
65+
* Converts parsed blocks to a format Gutenberg can understand.
66+
*
67+
* @param array $blocks A list of blocks.
68+
*/
69+
function _convert_parsed_blocks_for_js( $blocks ) {
70+
$template = array();
71+
foreach ( $blocks as $block ) {
72+
if ( null === $block['blockName'] && empty( trim( $block['innerHTML'] ) ) ) {
73+
continue;
74+
}
75+
76+
$entry = array( $block['blockName'], $block['attrs'] );
77+
if ( isset( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
78+
$entry[] = _convert_parsed_blocks_for_js( $block['innerBlocks'] );
79+
}
80+
$template[] = $entry;
81+
}
82+
return $template;
83+
}
84+
85+
/**
86+
* Parse the blocks looking for bound attributes.
87+
*
88+
* TODO: Fix recursion.
89+
*
90+
* @param array $blocks The blocks from the CPT template.
91+
*/
92+
function _get_meta_fields( $blocks ) {
93+
$meta_fields = array();
94+
95+
foreach ( $blocks as $block ) {
96+
$binding = $block['attrs']['metadata']['data-types/binding'] ?? null;
97+
98+
if ( is_null( $binding ) || 'post_content' === $binding ) {
99+
continue;
100+
}
101+
102+
$meta_fields[] = $binding;
103+
}
104+
105+
return $meta_fields;
106+
}

0 commit comments

Comments
 (0)