From f53c934605052ea00253283dead0e1c0137c000a Mon Sep 17 00:00:00 2001 From: Fabian Todt Date: Wed, 17 May 2023 14:01:30 +0200 Subject: [PATCH 1/2] #6945: add a props block to sync mentions to wp.org --- mu-plugins/blocks/props/index.php | 106 +++++++++++++++++++++++++ mu-plugins/blocks/props/src/block.json | 60 ++++++++++++++ mu-plugins/blocks/props/src/edit.js | 48 +++++++++++ mu-plugins/blocks/props/src/index.js | 16 ++++ mu-plugins/blocks/props/src/save.js | 23 ++++++ mu-plugins/loader.php | 1 + 6 files changed, 254 insertions(+) create mode 100644 mu-plugins/blocks/props/index.php create mode 100644 mu-plugins/blocks/props/src/block.json create mode 100644 mu-plugins/blocks/props/src/edit.js create mode 100644 mu-plugins/blocks/props/src/index.js create mode 100644 mu-plugins/blocks/props/src/save.js diff --git a/mu-plugins/blocks/props/index.php b/mu-plugins/blocks/props/index.php new file mode 100644 index 000000000..89c14d748 --- /dev/null +++ b/mu-plugins/blocks/props/index.php @@ -0,0 +1,106 @@ +post_status ) { + return; + } + + if ( ! has_block( 'wporg/props', $post ) ) { + return; + } + + $existing_handled_props = get_post_meta( $post_id, '_wporg_props', true ); + if ( empty( $existing_handled_props ) ) { + $existing_handled_props = array(); + } + + $props_blocks = search_props_block( parse_blocks( $post->post_content ) ); + + $props_to_handle = array(); + foreach ( $props_blocks as $block ) { + $props = parse_props( $block['innerHTML'] ); + $props_to_handle = array_merge( $props_to_handle, $props ); + } + + // TODO: does not remove props. + $new_props = array_diff( $props_to_handle, $existing_handled_props ); + $post_link = get_permalink( $post_id ); + $post_title = get_the_title( $post_id ); + foreach ( $new_props as $prop_user ) { + WordPressdotorg\Profiles\add_activity( + /* translators: link to post the user has received props in */ + sprintf( __( 'Received props in %s', 'wporg' ), '' . esc_html( $post_title ) . '' ), + '', // TODO: which type? + $prop_user + ); + } + + $handled_props = array_unique( array_merge( $existing_handled_props, $new_props ) ); + update_post_meta( $post_id, '_wporg_props', $handled_props ); +} + +/** + * Search for top-level and nested props blocks in an array of blocks + * + * @param mixed $blocks An array of blocks to search in (eg from parse_blocks or nested innerBlocks). + * @return array All found blocks of the props blockType. + */ +function search_props_block( $blocks ) { + $props_blocks = array(); + foreach ( $blocks as $block ) { + if ( 'wporg/props' === $block['blockName'] ) { + $props_blocks[] = $block; + continue; + } + if ( ! empty( $block['innerBlocks'] ) ) { + $nested_blocks = search_props_block( $block['innerBlocks'] ); + $props_blocks = array_merge( $props_blocks, $nested_blocks ); + } + } + + return $props_blocks; +} + +/** + * Parses props (=user mentions) from the HTML block content + * + * Regex for parsing taken from bbPress: bbp_find_mentions + * + * @param string $block_content The innerHTML from the block. + * @return string[] + */ +function parse_props( $block_content ) { + if ( function_exists( 'bbp_find_mentions' ) ) { + return bbp_find_mentions( $block_content ); + } + $pattern = '/[@]+([A-Za-z0-9-_\.@]+)\b/'; + preg_match_all( $pattern, $block_content, $usernames ); + $usernames = array_unique( $usernames[1] ); + return $usernames; +} diff --git a/mu-plugins/blocks/props/src/block.json b/mu-plugins/blocks/props/src/block.json new file mode 100644 index 000000000..6ba5445a1 --- /dev/null +++ b/mu-plugins/blocks/props/src/block.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/props", + "version": "0.1.0", + "title": "Props Block", + "category": "design", + "icon": "awards", + "description": "Tag wp.org users so they receive props for helping.", + "supports": { + "anchor": true, + "className": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalTextDecoration": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "__experimentalSelector": "p", + "__unstablePasteTextInline": true + }, + "attributes": { + "align": { + "type": "string" + }, + "content": { + "type": "string", + "source": "html", + "selector": "p", + "default": "", + "__experimentalRole": "content" + } + }, + "example": { + "attributes": { + "content": "

Props to @exampleuser

" + } + }, + "textdomain": "wporg", + "editorScript": "file:./index.js" +} diff --git a/mu-plugins/blocks/props/src/edit.js b/mu-plugins/blocks/props/src/edit.js new file mode 100644 index 000000000..81dfb8e9d --- /dev/null +++ b/mu-plugins/blocks/props/src/edit.js @@ -0,0 +1,48 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { + AlignmentControl, + BlockControls, + RichText, + useBlockProps, +} from '@wordpress/block-editor'; + +export default function Edit( { attributes, setAttributes } ) { + const { content, align } = attributes; + + const blockProps = useBlockProps( { + className: classnames( { + [ `has-text-align-${ align }` ]: align, + } ), + } ); + + return ( + <> + + + setAttributes( { + align: newAlign, + } ) + } + /> + + + setAttributes( { content: newContent } ) + } + value={ content } + /> + + ); +} diff --git a/mu-plugins/blocks/props/src/index.js b/mu-plugins/blocks/props/src/index.js new file mode 100644 index 000000000..953d2df39 --- /dev/null +++ b/mu-plugins/blocks/props/src/index.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import Edit from './edit'; +import save from './save'; +import metadata from './block.json'; + +registerBlockType( metadata.name, { + edit: Edit, + save, +} ); diff --git a/mu-plugins/blocks/props/src/save.js b/mu-plugins/blocks/props/src/save.js new file mode 100644 index 000000000..786329256 --- /dev/null +++ b/mu-plugins/blocks/props/src/save.js @@ -0,0 +1,23 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { RichText, useBlockProps } from '@wordpress/block-editor'; + +export default function save( { attributes } ) { + const { content, align } = attributes; + + const className = classnames( { + [ `has-text-align-${ align }` ]: align, + } ); + + return ( +

+ +

+ ); +} diff --git a/mu-plugins/loader.php b/mu-plugins/loader.php index c5531b979..a789e3140 100644 --- a/mu-plugins/loader.php +++ b/mu-plugins/loader.php @@ -22,6 +22,7 @@ require_once __DIR__ . '/blocks/latest-news/latest-news.php'; require_once __DIR__ . '/blocks/link-wrapper/index.php'; require_once __DIR__ . '/blocks/notice/index.php'; +require_once __DIR__ . '/blocks/props/index.php'; require_once __DIR__ . '/blocks/sidebar-container/index.php'; require_once __DIR__ . '/blocks/screenshot-preview/block.php'; require_once __DIR__ . '/blocks/site-breadcrumbs/index.php'; From 6cb87ff652e52c6d381d41e62c3f64bd6ce879a1 Mon Sep 17 00:00:00 2001 From: Fabian Todt Date: Wed, 17 May 2023 14:16:06 +0200 Subject: [PATCH 2/2] add classnames dependency --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ca2130e9..1f24efbe0 100644 --- a/package.json +++ b/package.json @@ -64,5 +64,7 @@ "selector-class-pattern": null } }, - "dependencies": {} + "dependencies": { + "classnames": "^2.3.1" + } }