diff --git a/README.md b/README.md index f2f584d..4044a31 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,45 @@ # Network Template Parts -Share responsibility for the look and feel of websites on a multisite network. +Render template parts in a site or network context. ## Description When managing a brand across many sites, it is often necessary to ship changes to the look and feel with the certainty that they will be applied immediately. -In its current iteration, the full site editor allows individual site administrators to make changes to a theme's templates and template parts. Once a template or a template part has been edited on an individual site, it no longer receives updates from the theme automatically, which may in turn cause unintended consequences. +In its current iteration, the WordPress site editor allows individual site administrators to make changes to a theme's templates and template parts. Once a template or a template part has been edited on an individual site, it no longer receives updates from the theme automatically, which may in turn cause unintended consequences. -This plugin provides a way for site administrators to edit parts of the site in the site without accidentally overwriting pieces that should remain consistent. +This plugin provides a way for site administrators to edit parts of the site without accidentally overwriting pieces that should remain consistent. -Network Template Parts works in tandem with [Restrict Network Templates](https://github.com/happyprime/restrict-network-templates), which restricts the editing of templates and network-level parts on individual sites. +Network Template Parts works in tandem with [Restrict Network Templates](https://github.com/happyprime/restrict-network-templates), which restricts the editing of templates on individual sites. ## Assumptions ### Templates provided by the theme should not be edited. -The core HTML structure of the theme is provided by templates. (e.g. `home.html`) +A theme's core structure is defined by templates. (e.g. `templates/home.html`) It should be possible to set a structure in these templates and assume that they will not be overridden on individual sites. -### Network-level templates and parts are managed by network administrators. +### Network-level template parts are managed by network administrators. Parts of a theme's structure rely on data from the main site on a network. (e.g. global navigation) -It should not be possible to override network-level templates and parts from an individual site. +It should be possible for a template to specify that a section of a page is rendered in the context of the main site. -### Site-level templates and parts are managed by site or network administrators. +### Site-level template parts are managed by site administrators. Parts of a theme's structure rely on data from individual sites on a network. (e.g. site navigation, content) It should be possible to override site-level template parts from an individual site. -## Definitions +## Block -### Template +The Network Template Part block renders a selected template part in either a "site" or "network" context. -A template is a file stored in a theme's `templates/` directory. +Attributes: -### Network Templates - -A network template is a file prefixed as `network-templates-` and stored in the theme's `parts/` directory. (e.g. `parts/network-templates-home.html`) - -The Network Template block renders network templates in the context of the network's main site. These are intended to be used in place of the templates normally found in a theme's `templates/` directory, but with network-level data. - -The template selection interface provided by this block will remove `network-templates-` from the name and allow you to select from a list like: home, index, 404, etc… - -### Site Templates - -A site template is a file prefixed as `site-templates-` and stored in the theme's `parts/` directory. (e.g. `parts/site-templates-home.html`) - -The Site Template block renders a template part in the context of the current site. These are intended to be used in place of the templates normally found in a theme's `templates/` directory. - -The template selection interface provided by this block will remove `site-templates-` from the name and allow you to select from a list like: home, index, 404, etc… - -### Network Template Parts - -A network template part is a file prefixed as `network-parts-` and stored in the theme's `parts/` directory. (e.g. `parts/network-parts-header.html`) - -The Network Template Parts block renders a template part in the context of the network's main site. These are the same as any template part found in a theme's `parts/` directory, but with the intent to render network-level data. - -The template selection interface provided by this block will remove `network-parts-` from the name and allow you to select from a list like: header, header-main, etc… - -### Site Template Parts - -A site template part is a file prefixed as `site-parts-` and stored in the theme's `parts/` directory. (e.g. `parts/site-parts-header-navigation.html`) - -The Site Template Part block renders a template part in the context of the current site. These are the same as any template part found in a theme's `parts/` directory, but with the intent to always render site-level data. - -The template selection interface provided by this block will remove `site-parts-` from the name and allow you to select from a list like: header, header-main, etc… +* `slug` determines which template part to load. Template parts are located in a theme's `parts/` directory. +* `context` determines the context in which the part should render. ## Examples @@ -77,53 +48,46 @@ Often, a network will provide common header and footer areas while leaving the s A `templates/index.html` file may contain: ```html - - - + + + ``` -This loads `parts/network-parts-header.html`, `parts/network-templates-index.html`, and `parts/network-parts-footer.html`. +This loads `parts/header.html` from the main site on the network, `parts/main-index.html` from the current site, and `parts/footer.html` from the main site on the network. -Skipping ahead to the main content, the `parts/network-templates-index.html` file may contain: +The `parts/header.html` file main contain: ```html - -
- -
- + + ``` -This provides a common wrapping container around a site-level template part, `parts/site-templates-index.html`. The site-level part can be left as is or overwritten by an individual site administrator through the full site editor to provide a custom layout while keeping the network's header and footer intact. - -The `parts/network-parts-header.html` file main contain: - -```html - - -``` - -This defines two areas in the header to be managed at the network level. The `parts/network-parts-header-main.html` may contain something like: +This defines two areas in the header to be managed at the network level. The `parts/header-main.html` may contain something like: ```html
- - + +
``` -This provides some common HTML structure, loads a network-level logo, and also provides a site-level navigation in `parts/site-parts-header-main-navigation.html`. +This provides some common HTML structure, loads a network-level logo, and also provides a site-level navigation in `parts/header-main-site-navigation.html`. Now, an individual site administrator can make changes to a navigation menu while also receiving updates from the theme and the network if the look and feel of the broader network changes. ## Changelog +### 1.0.0 + +* Consolidate into one block: Network Template Part. +* Add "context" attribute. + ### 0.1.1 * Provide a select interface for site and network template parts. diff --git a/blocks/network-template-part/block.json b/blocks/network-template-part/block.json index f084d4f..b991e70 100644 --- a/blocks/network-template-part/block.json +++ b/blocks/network-template-part/block.json @@ -5,7 +5,7 @@ "title": "Network Template Part", "category": "widgets", "icon": "location", - "description": "Retrieve a template part from the main site on the network.", + "description": "Render a template part in the context of a site or network.", "version": "0.0.1", "textdomain": "network-template-parts", "editorScript": "file:index.js", @@ -13,6 +13,10 @@ "slug": { "type": "string", "default": "" + }, + "context": { + "type": "string", + "default": "site" } } } diff --git a/blocks/network-template-part/index.js b/blocks/network-template-part/index.js index ea61ddb..24e821d 100644 --- a/blocks/network-template-part/index.js +++ b/blocks/network-template-part/index.js @@ -11,7 +11,7 @@ import metadata from './block.json'; const Edit = ( props ) => { const { - attributes: { slug }, + attributes: { slug, context }, setAttributes, } = props; @@ -25,14 +25,10 @@ const Edit = ( props ) => { ); const partOptions = parts - ? parts - .filter( ( part ) => - part.slug.startsWith( 'network-parts-' ) - ) - .map( ( part ) => ( { - label: part.slug.slice( 14 ), - value: part.slug.slice( 14 ), - } ) ) + ? parts.map( ( part ) => ( { + label: part.slug, + value: part.slug, + } ) ) : []; partOptions.unshift( { @@ -65,6 +61,23 @@ const Edit = ( props ) => { setAttributes( { slug: value } ) } /> + { + setAttributes( { context: value } ); + } } + /> diff --git a/blocks/network-template-part/index.php b/blocks/network-template-part/index.php index dfb3bad..b63e5d9 100644 --- a/blocks/network-template-part/index.php +++ b/blocks/network-template-part/index.php @@ -29,7 +29,8 @@ function register_block() { */ function get_block_html( array $attributes ): string { $ntp_block_defaults = [ - 'slug' => '', + 'slug' => '', + 'context' => 'site', ]; $attributes = wp_parse_args( $attributes, $ntp_block_defaults ); @@ -38,15 +39,23 @@ function get_block_html( array $attributes ): string { return '

Please specify a template part slug.

'; } - if ( is_multisite() ) { + $switched = false; + + if ( 'network' === $attributes['context'] && is_multisite() && ! is_main_site() ) { + $switched = true; switch_to_blog( get_main_site_id() ); + } elseif ( 'site' === $attributes['context'] && is_multisite() && ! empty( $GLOBALS['_wp_switched_stack'] ) ) { + $switched = true; + + // We're already operating in a switched state, switch to the site that made the original request. + switch_to_blog( $GLOBALS['_wp_switched_stack'][ array_key_first( $GLOBALS['_wp_switched_stack'] ) ] ); } ob_start(); - block_template_part( 'network-parts-' . $attributes['slug'] ); + block_template_part( $attributes['slug'] ); $content = ob_get_clean(); - if ( is_multisite() ) { + if ( $switched ) { restore_current_blog(); } diff --git a/blocks/network-template/block.json b/blocks/network-template/block.json deleted file mode 100644 index 8b29abe..0000000 --- a/blocks/network-template/block.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/block.json", - "apiVersion": 2, - "name": "ntp/network-template", - "title": "Network Template", - "category": "widgets", - "icon": "location", - "description": "Retrieve a template from the main site on the network.", - "version": "0.0.1", - "textdomain": "network-template-parts", - "editorScript": "file:index.js", - "attributes": { - "slug": { - "type": "string", - "default": "" - } - } -} diff --git a/blocks/network-template/index.js b/blocks/network-template/index.js deleted file mode 100644 index 34fbcf0..0000000 --- a/blocks/network-template/index.js +++ /dev/null @@ -1,82 +0,0 @@ -// WordPress dependencies. -import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; -import { registerBlockType } from '@wordpress/blocks'; -import { Disabled, PanelBody, SelectControl } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; -import ServerSideRender from '@wordpress/server-side-render'; - -// Internal dependencies. -import metadata from './block.json'; - -const Edit = ( props ) => { - const { - attributes: { slug }, - setAttributes, - } = props; - - const { options } = useSelect( ( select ) => { - const parts = select( 'core' ).getEntityRecords( - 'postType', - 'wp_template_part', - { - per_page: -1, - } - ); - - const partOptions = parts - ? parts - .filter( ( part ) => - part.slug.startsWith( 'network-templates-' ) - ) - .map( ( part ) => ( { - label: part.slug.slice( 18 ), - value: part.slug.slice( 18 ), - } ) ) - : []; - - partOptions.unshift( { - label: __( 'None', 'network-template-parts' ), - value: '', - } ); - - return { - options: partOptions, - }; - }, [] ); - - return ( -
- - - - setAttributes( { slug: value } ) - } - /> - - - - - -
- ); -}; - -registerBlockType( metadata, { - edit: Edit, -} ); diff --git a/blocks/network-template/index.php b/blocks/network-template/index.php deleted file mode 100644 index efa0d7e..0000000 --- a/blocks/network-template/index.php +++ /dev/null @@ -1,54 +0,0 @@ - __NAMESPACE__ . '\get_block_html', - ] - ); -} - -/** - * Retrieve the block rendered as HTML. - * - * @param array $attributes The block attributes. - * @return string The block HTML. - */ -function get_block_html( array $attributes ): string { - $ntp_block_defaults = [ - 'slug' => '', - ]; - - $attributes = wp_parse_args( $attributes, $ntp_block_defaults ); - - if ( '' === $attributes['slug'] ) { - return '

Please specify a template part slug.

'; - } - - if ( is_multisite() ) { - switch_to_blog( get_main_site_id() ); - } - - ob_start(); - block_template_part( 'network-templates-' . $attributes['slug'] ); - $content = ob_get_clean(); - - if ( is_multisite() ) { - restore_current_blog(); - } - - return $content; -} diff --git a/blocks/site-template-part/block.json b/blocks/site-template-part/block.json deleted file mode 100644 index dfcf6eb..0000000 --- a/blocks/site-template-part/block.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/block.json", - "apiVersion": 2, - "name": "ntp/site-template-part", - "title": "Site Template Part", - "category": "widgets", - "icon": "location", - "description": "Retrieve a template part from the site being viewed on the network.", - "version": "0.0.1", - "textdomain": "network-template-parts", - "editorScript": "file:index.js", - "attributes": { - "slug": { - "type": "string", - "default": "" - } - } -} diff --git a/blocks/site-template-part/index.js b/blocks/site-template-part/index.js deleted file mode 100644 index 2eec872..0000000 --- a/blocks/site-template-part/index.js +++ /dev/null @@ -1,80 +0,0 @@ -// WordPress dependencies. -import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; -import { registerBlockType } from '@wordpress/blocks'; -import { Disabled, PanelBody, SelectControl } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; -import ServerSideRender from '@wordpress/server-side-render'; - -// Internal dependencies. -import metadata from './block.json'; - -const Edit = ( props ) => { - const { - attributes: { slug }, - setAttributes, - } = props; - - const { options } = useSelect( ( select ) => { - const parts = select( 'core' ).getEntityRecords( - 'postType', - 'wp_template_part', - { - per_page: -1, - } - ); - - const partOptions = parts - ? parts - .filter( ( part ) => part.slug.startsWith( 'site-parts-' ) ) - .map( ( part ) => ( { - label: part.slug.slice( 11 ), - value: part.slug.slice( 11 ), - } ) ) - : []; - - partOptions.unshift( { - label: __( 'None', 'network-template-parts' ), - value: '', - } ); - - return { - options: partOptions, - }; - }, [] ); - - return ( -
- - - - setAttributes( { slug: value } ) - } - /> - - - - - -
- ); -}; - -registerBlockType( metadata, { - edit: Edit, -} ); diff --git a/blocks/site-template-part/index.php b/blocks/site-template-part/index.php deleted file mode 100644 index 20efaaf..0000000 --- a/blocks/site-template-part/index.php +++ /dev/null @@ -1,58 +0,0 @@ - __NAMESPACE__ . '\get_block_html', - ] - ); -} - -/** - * Retrieve the block rendered as HTML. - * - * @param array $attributes The block attributes. - * @return string The block HTML. - */ -function get_block_html( array $attributes ): string { - $stp_block_defaults = [ - 'slug' => '', - ]; - - $attributes = wp_parse_args( $attributes, $stp_block_defaults ); - $switched = false; - - if ( '' === $attributes['slug'] ) { - return '

Please specify a template part slug.

'; - } - - // If we're operating in a switched state, switch to the site - // that made the original request. - if ( is_multisite() && ! empty( $GLOBALS['_wp_switched_stack'] ) ) { - $switched = true; - switch_to_blog( $GLOBALS['_wp_switched_stack'][ array_key_first( $GLOBALS['_wp_switched_stack'] ) ] ); - } - - ob_start(); - block_template_part( 'site-parts-' . $attributes['slug'] ); - $content = ob_get_clean(); - - if ( $switched ) { - restore_current_blog(); - } - - return $content; -} diff --git a/blocks/site-template/block.json b/blocks/site-template/block.json deleted file mode 100644 index 3eb3089..0000000 --- a/blocks/site-template/block.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/block.json", - "apiVersion": 2, - "name": "ntp/site-template", - "title": "Site Template", - "category": "widgets", - "icon": "location", - "description": "Retrieve a template from the site being viewed on the network.", - "version": "0.0.1", - "textdomain": "network-template-parts", - "editorScript": "file:index.js", - "attributes": { - "slug": { - "type": "string", - "default": "" - } - } -} diff --git a/blocks/site-template/index.js b/blocks/site-template/index.js deleted file mode 100644 index eab66c2..0000000 --- a/blocks/site-template/index.js +++ /dev/null @@ -1,79 +0,0 @@ -// WordPress dependencies. -import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; -import { registerBlockType } from '@wordpress/blocks'; -import { Disabled, PanelBody, SelectControl } from '@wordpress/components'; -import { useSelect } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; -import ServerSideRender from '@wordpress/server-side-render'; - -// Internal dependencies. -import metadata from './block.json'; - -const Edit = ( props ) => { - const { - attributes: { slug }, - setAttributes, - } = props; - - const { options } = useSelect( ( select ) => { - const parts = select( 'core' ).getEntityRecords( - 'postType', - 'wp_template_part', - { - per_page: -1, - } - ); - - const partOptions = parts - ? parts - .filter( ( part ) => - part.slug.startsWith( 'site-templates-' ) - ) - .map( ( part ) => ( { - label: part.slug.slice( 15 ), - value: part.slug.slice( 15 ), - } ) ) - : []; - - partOptions.unshift( { - label: __( 'None', 'network-template-parts' ), - value: '', - } ); - - return { - options: partOptions, - }; - }, [] ); - - return ( -
- - - - setAttributes( { slug: value } ) - } - /> - - - - - -
- ); -}; - -registerBlockType( metadata, { - edit: Edit, -} ); diff --git a/blocks/site-template/index.php b/blocks/site-template/index.php deleted file mode 100644 index 8c71679..0000000 --- a/blocks/site-template/index.php +++ /dev/null @@ -1,58 +0,0 @@ - __NAMESPACE__ . '\get_block_html', - ] - ); -} - -/** - * Retrieve the block rendered as HTML. - * - * @param array $attributes The block attributes. - * @return string The block HTML. - */ -function get_block_html( array $attributes ): string { - $stp_block_defaults = [ - 'slug' => '', - ]; - - $attributes = wp_parse_args( $attributes, $stp_block_defaults ); - $switched = false; - - if ( '' === $attributes['slug'] ) { - return '

Please specify a template part slug.

'; - } - - // If we're operating in a switched state, switch to the site - // that made the original request. - if ( is_multisite() && ! empty( $GLOBALS['_wp_switched_stack'] ) ) { - $switched = true; - switch_to_blog( $GLOBALS['_wp_switched_stack'][ array_key_first( $GLOBALS['_wp_switched_stack'] ) ] ); - } - - ob_start(); - block_template_part( 'site-templates-' . $attributes['slug'] ); - $content = ob_get_clean(); - - if ( $switched ) { - restore_current_blog(); - } - - return $content; -} diff --git a/plugin.php b/plugin.php index 746131f..2dfd1a3 100644 --- a/plugin.php +++ b/plugin.php @@ -1,7 +1,7 @@ - - - - + + + +> -This loads `parts/network-parts-header.html`, `parts/network-templates-index.html`, and `parts/network-parts-footer.html`. +This loads `parts/header.html` from the main site on the network, `parts/main-index.html` from the current site, and `parts/footer.html` from the main site on the network. -Skipping ahead to the main content, the `parts/network-templates-index.html` file may contain: +The `parts/header.html` file main contain: - -
- -
- -
- -This provides a common wrapping container around a site-level template part, `parts/site-templates-index.html`. The site-level part can be left as is or overwritten by an individual site administrator through the full site editor to provide a custom layout while keeping the network's header and footer intact. + + +> -The `parts/network-parts-header.html` file main contain: - - - - - - -This defines two areas in the header to be managed at the network level. The `parts/network-parts-header-main.html` may contain something like: +This defines two areas in the header to be managed at the network level. The `parts/header-main.html` may contain something like:
- - + +
-
+> -This provides some common HTML structure, loads a network-level logo, and also provides a site-level navigation in `parts/site-parts-header-main-navigation.html`. +This provides some common HTML structure, loads a network-level logo, and also provides a site-level navigation in `parts/header-main-site-navigation.html`. Now, an individual site administrator can make changes to a navigation menu while also receiving updates from the theme and the network if the look and feel of the broader network changes. ## Changelog +### 1.0.0 + +* Consolidate into one block: Network Template Part. +* Add "context" attribute. + ### 0.1.1 * Provide a select interface for site and network template parts.