Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New block: Chapter List #650

Merged
merged 2 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions mu-plugins/blocks/chapter-list/class-chapter-walker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/**
* Custom walker for chapter block page list.
*
* Identical to `Walker_Page` except with a `walk()` that ignores orphaned
* pages, which are pages with an ancestor that is not published.
*/

namespace WordPressdotorg\MU_Plugins\Chapter_List;

class Chapter_Walker extends \Walker_Page {

/**
* Display array of elements hierarchically.
*
* Does not assume any existing order of elements.
*
* $max_depth = -1 means flatly display every element.
* $max_depth = 0 means display all levels.
* $max_depth > 0 specifies the number of display levels.
*
* NOTE: This is identical to `Walker::walk()` except that it ignores orphaned
* pages, which are essentially pages whose ancestor is not published.
*
* @param array $elements An array of elements.
* @param int $max_depth The maximum hierarchical depth.
* @param mixed ...$args Optional additional arguments.
* @return string The hierarchical item output.
*/
public function walk( $elements, $max_depth, ...$args ) {
$output = '';

// invalid parameter or nothing to walk.
if ( $max_depth < -1 || empty( $elements ) ) {
return $output;
}

$parent_field = $this->db_fields['parent'];

// flat display.
if ( -1 === $max_depth ) {
$empty_array = array();
foreach ( $elements as $e ) {
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
}
return $output;
}

/*
* Need to display in hierarchical order.
* Separate elements into two buckets: top level and children elements.
* Children_elements is two dimensional array, eg.
* Children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e ) {
if ( empty( $e->$parent_field ) ) {
$top_level_elements[] = $e;
} else {
$children_elements[ $e->$parent_field ][] = $e;
}
}

/*
* When none of the elements is top level.
* Assume the first one must be root of the sub elements.
*/
if ( empty( $top_level_elements ) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];

$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e ) {
if ( $root->$parent_field === $e->$parent_field ) {
$top_level_elements[] = $e;
} else {
$children_elements[ $e->$parent_field ][] = $e;
}
}
}

foreach ( $top_level_elements as $e ) {
$this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
}

/*
* Here is where it differs from the original `walk()`. The original would
* automatically display orphans.
*/

return $output;
}
}
80 changes: 80 additions & 0 deletions mu-plugins/blocks/chapter-list/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
namespace WordPressdotorg\MU_Plugins\Chapter_List;

defined( 'WPINC' ) || die();

require_once __DIR__ . '/class-chapter-walker.php';

add_action( 'init', __NAMESPACE__ . '\init' );

/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function init() {
register_block_type(
__DIR__ . '/build',
array(
'render_callback' => __NAMESPACE__ . '\render',
)
);
}

/**
* Render the block content.
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
*
* @return string Returns the block markup.
*/
function render( $attributes, $content, $block ) {
if ( ! isset( $block->context['postId'] ) ) {
return '';
}

$post_id = $block->context['postId'];
$post_type = get_post_type( $post_id );

$args = array(
'title_li' => '',
'echo' => 0,
'sort_column' => 'menu_order, title',
'post_type' => $post_type,

// Use custom walker that excludes display of orphaned pages (an ancestor
// of such a page is likely not published and thus this is not accessible).
'walker' => new Chapter_Walker(),
);

$post_type_obj = get_post_type_object( $post_type );

if ( $post_type_obj && current_user_can( $post_type_obj->cap->read_private_posts ) ) {
$args['post_status'] = array( 'publish', 'private' );
}

$content = wp_list_pages( $args );

$header = '<div class="wporg-chapter-list__header">';
$header .= do_blocks(
'<!-- wp:heading {"style":{"typography":{"fontStyle":"normal","fontWeight":"400"}},"fontSize":"normal","fontFamily":"inter"} -->
<h2 class="wp-block-heading has-inter-font-family has-normal-font-size" style="font-style:normal;font-weight:400">' . esc_html__( 'Chapters', 'wporg' ) . '</h2>
<!-- /wp:heading -->'
);
$header .= '<button type="button" class="wporg-chapter-list__toggle" aria-expanded="false">';
$header .= '<span class="screen-reader-text">' . esc_html__( 'Chapter list', 'wporg' ) . '</span>';
$header .= '</button>';
$header .= '</div>';

$wrapper_attributes = get_block_wrapper_attributes();
return sprintf(
'<aside %1$s><nav>%2$s<ul class="wporg-chapter-list__list">%3$s</ul></nav></aside>',
$wrapper_attributes,
$header,
$content
);
}
35 changes: 35 additions & 0 deletions mu-plugins/blocks/chapter-list/src/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "wporg/chapter-list",
"version": "0.1.0",
"title": "Chapter Navigation",
"category": "widgets",
"icon": "smiley",
"description": "",
"usesContext": [ "postId" ],
"attributes": {
"postType": {
"type": "string"
}
},
"supports": {
"html": false,
"spacing": {
"margin": [
"top",
"bottom"
],
"padding": true,
"blockGap": true
},
"typography": {
"fontSize": true,
"lineHeight": true
}
},
"textdomain": "wporg",
"editorScript": "file:./index.js",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
}
25 changes: 25 additions & 0 deletions mu-plugins/blocks/chapter-list/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import ServerSideRender from '@wordpress/server-side-render';

/**
* Internal dependencies
*/
import metadata from './block.json';
import './style.scss';

const Edit = ( { attributes, name } ) => {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<ServerSideRender block={ name } attributes={ attributes } skipBlockSupportAttributes />
</div>
);
};

registerBlockType( metadata.name, {
edit: Edit,
} );
Loading
Loading