Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c902770
build: add webpack build pipeline with newspack-scripts
rbcorrales Mar 31, 2026
d54ddb7
refactor: shared modal flag and remove inline onclick from copy button
rbcorrales Mar 31, 2026
30606cf
feat(block): add block.json metadata and base styles
rbcorrales Mar 31, 2026
a7a3530
feat(block): add editor component with block registration
rbcorrales Mar 31, 2026
f9feea1
feat(block): add PHP block class with server-side rendering
rbcorrales Mar 31, 2026
395099b
feat(block): add frontend modal JS for block trigger
rbcorrales Mar 31, 2026
162cbb9
chore: raise minimum WP version to 6.0 for block support
rbcorrales Mar 31, 2026
262254e
fix(block): address review findings from 12 parallel code reviewers
rbcorrales Mar 31, 2026
1aace67
fix(block): address remaining review findings
rbcorrales Mar 31, 2026
73620aa
fix(block): apply wrapper attributes to outer div for correct anchor …
rbcorrales Mar 31, 2026
1da6b7f
fix: resolve PHPCS, eslint plugin, and test failures
rbcorrales Mar 31, 2026
529045f
fix(block): address Copilot review feedback
rbcorrales Mar 31, 2026
642d9b8
fix: resolve JS lint errors in edit.js and view.js
rbcorrales Mar 31, 2026
f228d76
fix: update widget test assertion and register block in test setup
rbcorrales Mar 31, 2026
1e18f1c
fix: resolve test failures and load composer autoloader in bootstrap
rbcorrales Mar 31, 2026
e900d80
fix: remove 'page' as option and leave only modal
laurelfulford May 12, 2026
8283ae4
Merge branch 'trunk' into feat/republish-button-block
laurelfulford May 12, 2026
b0cf32d
feat(block): introduce republish-section pattern + license block
laurelfulford May 13, 2026
12852b5
refactor(block): consolidate republish block layout, icons, gating
laurelfulford May 13, 2026
8857acd
fix: remove separate license block
laurelfulford May 18, 2026
d24fadc
chore: update minimum WP version for the switch to API3, and update R…
laurelfulford May 18, 2026
618e031
fix: escape translated output
laurelfulford May 18, 2026
ba68fb6
fix: add 'classic theme only' comment to one of the plugin options
laurelfulford May 18, 2026
2609cf5
fix: make sure we're not trapping focus on hidden elements
laurelfulford May 18, 2026
5a0d858
Merge branch 'trunk' into feat/republish-button-block
laurelfulford May 26, 2026
4a1926a
Merge branch 'trunk' into feat/republish-button-block
laurelfulford Jun 4, 2026
4ac5abf
chore: revert readme.txt and README.md changes to avoid sync clash
laurelfulford Jun 4, 2026
72c3c27
fix: guard composer autoloader in test bootstrap
laurelfulford Jun 4, 2026
fc55996
fix(block): guard pre-5.5 fatals, fix i18n double-escape + supports
rbcorrales Jul 10, 2026
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
8 changes: 8 additions & 0 deletions assets/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ function show_modal( $modal, $close ) {

initTabSwitching();

// Bind copy button via data attribute (replaces inline onclick).
var $copyBtn = $modal.find( '[data-copy-active]' );
$copyBtn.off( 'click.republish' ).on( 'click.republish', function() {
if ( window.ClipboardUtils ) {
ClipboardUtils.copyFromElement( getActiveTextarea(), this );
}
} );

trapFocus( $modal );
$close.focus();
}
Expand Down
85 changes: 85 additions & 0 deletions includes/class-republish-pattern.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
/**
* Republish block pattern registration.
*
* @package Republication_Tracker_Tool
*/

defined( 'ABSPATH' ) || exit;

/**
* Registers the "Republish Section" pattern and its category.
*/
final class Republication_Tracker_Tool_Republish_Pattern {

/**
* Pattern name.
*/
public const PATTERN_NAME = 'republication-tracker-tool/republish-section';

/**
* Pattern category slug.
*/
public const CATEGORY_SLUG = 'republication-tracker-tool';

/**
* Hook up registration.
*/
public static function init() {
add_action( 'init', [ __CLASS__, 'register' ] );
}

/**
* Register the pattern category and pattern.
*/
public static function register() {
// Block pattern APIs are only available on WP 5.5+. Bail gracefully on
// older installs instead of fataling on init.
if ( ! function_exists( 'register_block_pattern' ) || ! function_exists( 'register_block_pattern_category' ) ) {
return;
}

register_block_pattern_category(
self::CATEGORY_SLUG,
[ 'label' => esc_html__( 'Republication', 'republication-tracker-tool' ) ]
);

$description = esc_html__( 'Republish our articles for free, online or in print, under a Creative Commons license.', 'republication-tracker-tool' );

// Plain __() (not esc_html__): buttonText is JSON-encoded into a block
// attribute and escaped again by the block's render_callback, so escaping
// here would double-encode entities in translations (apostrophes, etc.).
$button_text = __( 'Republish This Story', 'republication-tracker-tool' );
$button_text_enc = wp_json_encode( $button_text );

$content = <<<HTML
<!-- wp:group {"className":"republication-tracker-tool-republish-section"} -->
<div class="wp-block-group republication-tracker-tool-republish-section">
<!-- wp:paragraph -->
<p>{$description}</p>
<!-- /wp:paragraph -->

<!-- wp:republication-tracker-tool/republish-button {"buttonText":{$button_text_enc}} /-->
</div>
<!-- /wp:group -->
HTML;

// Hide the pattern from the inserter on classic themes (matches the
// republish-button block's gating). Stays registered so existing
// instances and inter-block references keep working.
$inserter = ! function_exists( 'wp_is_block_theme' ) || wp_is_block_theme();

register_block_pattern(
self::PATTERN_NAME,
[
'title' => esc_html__( 'Republish Section', 'republication-tracker-tool' ),
'description' => esc_html__( 'A paragraph, republish button, and Creative Commons license badge grouped together.', 'republication-tracker-tool' ),
'categories' => [ self::CATEGORY_SLUG ],
'content' => $content,
'inserter' => $inserter,
]
);
}
}

Republication_Tracker_Tool_Republish_Pattern::init();
2 changes: 1 addition & 1 deletion includes/class-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ public function republication_tracker_tool_display_attribution_callback() {
checked
<?php endif; ?>
/>
<p><em><?php echo esc_html__( 'If checked, an attribution statement will be appended to the copied content.', 'republication-tracker-tool' ); ?></em></p>
<p><em><?php echo esc_html__( 'If checked, an attribution statement will be appended to the copied content (classic theme only).', 'republication-tracker-tool' ); ?></em></p>
<?php
}

Expand Down
21 changes: 8 additions & 13 deletions includes/class-widget.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@
*/
class Republication_Tracker_Tool_Widget extends WP_Widget {

/**
* Whether the widget has been instantiated.
*
* @var bool
*/
public $has_instance = false;

/**
* Sets up the widgets name etc.
*/
Expand Down Expand Up @@ -108,25 +101,27 @@ public function widget( $args, $instance ) {

echo wp_kses_post( $args['after_widget'] );

// if has_instance is false, we can continue with displaying the modal.
if ( isset( $this->has_instance ) && false === $this->has_instance ) {
// If the modal has not been rendered yet, include it.
if ( ! Republication_Tracker_Tool::$modal_rendered ) {

// update has_instance so the next time the widget is created on the same page, it does not create a second modal.
$this->has_instance = true;
// Mark the modal as rendered.
Republication_Tracker_Tool::$modal_rendered = true;

// define our path to grab file content from.
$modal_content_path = plugin_dir_path( __FILE__ ) . 'shareable-content.php';

$is_amp = self::is_amp();

if ( $is_amp ) {
?>
<amp-lightbox id="republication-tracker-tool-modal" layout="nodisplay" role="dialog" aria-modal="true" aria-labelledby="republish-modal-label">
<?php echo esc_html( include_once $modal_content_path ); ?>
<?php include $modal_content_path; ?>
</amp-lightbox>
<?php
} else {
?>
<div id="republication-tracker-tool-modal" style="display:none;" data-postid="<?php echo esc_attr( $post->ID ); ?>" data-pluginsdir="<?php echo esc_attr( plugins_url() ); ?>" role="dialog" aria-modal="true" aria-labelledby="republish-modal-label">
<?php echo esc_html( include_once $modal_content_path ); ?>
<?php include $modal_content_path; ?>
</div>
<?php
}
Expand Down
2 changes: 1 addition & 1 deletion includes/shareable-content.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class="plain-text-field__input"
<?php
if ( ! $is_amp ) {
?>
<button onclick="ClipboardUtils.copyFromElement( getActiveTextarea(), this )" class="republication-tracker-tool__copy-button republication-tracker-tool__copy-button--main show-for-html"><?php echo esc_html__( 'Copy to Clipboard', 'republication-tracker-tool' ); ?></button>
<button data-copy-active class="republication-tracker-tool__copy-button republication-tracker-tool__copy-button--main show-for-html"><?php echo esc_html__( 'Copy to Clipboard', 'republication-tracker-tool' ); ?></button>
<?php
}

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
"main": "Gruntfile.js",
"author": "Automattic",
"scripts": {
"start": "npm ci",
"start": "npm ci && npm run watch",
"cm": "newspack-scripts commit",
"build": "newspack-scripts wp-scripts build",
"i18n": "grunt i18n",
"lint:js": "newspack-scripts wp-scripts lint-js src",
"readme": "grunt readme",
"semantic-release": "newspack-scripts release --files=republication-tracker-tool.php",
"release:archive": "rm -rf release && mkdir -p release && rsync -r . ./release/republication-tracker-tool --exclude-from='./.distignore' && cd release && zip -r republication-tracker-tool.zip republication-tracker-tool",
"release:archive": "npm run build && rm -rf release && mkdir -p release && rsync -r . ./release/republication-tracker-tool --exclude-from='./.distignore' && cd release && zip -r republication-tracker-tool.zip republication-tracker-tool",
"release": "npm run semantic-release"
},
"repository": {
Expand Down
9 changes: 9 additions & 0 deletions republication-tracker-tool.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ function_exists( 'get_plugin_data' ) || require_once ABSPATH . 'wp-admin/include
require plugin_dir_path( __FILE__ ) . 'includes/class-widget.php';
require plugin_dir_path( __FILE__ ) . 'includes/compatibility-co-authors-plus.php';
require plugin_dir_path( __FILE__ ) . 'includes/class-republication-rewrite.php';
require plugin_dir_path( __FILE__ ) . 'src/blocks/republish-button/class-republish-button-block.php';
require plugin_dir_path( __FILE__ ) . 'includes/class-republish-pattern.php';

/**
* Main initiation class.
Expand Down Expand Up @@ -72,6 +74,13 @@ final class Republication_Tracker_Tool {
*/
protected static $single_instance = null;

/**
* Whether the modal has been rendered on this page.
*
* @var bool
*/
public static $modal_rendered = false;

/**
* Instance of Republication_Tracker_Tool_Settings
*
Expand Down
66 changes: 66 additions & 0 deletions src/blocks/republish-button/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
Comment thread
laurelfulford marked this conversation as resolved.
Comment thread
laurelfulford marked this conversation as resolved.
"name": "republication-tracker-tool/republish-button",
"title": "Republish Button",
"category": "widgets",
"description": "A button that allows readers to republish your content under a Creative Commons license.",
"keywords": [ "republish", "creative commons", "syndication" ],
"textdomain": "republication-tracker-tool",
"attributes": {
"buttonText": {
"type": "string",
"default": "Republish This Story"
},
"showLicense": {
"type": "boolean",
"default": true
}
Comment thread
laurelfulford marked this conversation as resolved.
},
"supports": {
"anchor": true,
"align": false,
"color": {
"background": true,
"text": true,
"__experimentalDefaultControls": {
"background": true,
"text": true
}
},
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalFontFamily": true,
"__experimentalFontWeight": true,
"__experimentalFontStyle": true,
"__experimentalTextTransform": true,
"__experimentalTextDecoration": true,
"__experimentalLetterSpacing": true,
"__experimentalDefaultControls": {
"fontSize": true
}
},
"spacing": {
"padding": true,
"__experimentalDefaultControls": {
"padding": true
}
},
"__experimentalBorder": {
"radius": true,
"color": true,
"style": true,
"width": true,
"__experimentalDefaultControls": {}
},
"shadow": true
},
"editorScript": "file:../../../dist/republish-button.js",
"style": "file:../../../dist/republish-button.css",
"example": {
"attributes": {
"buttonText": "Republish This Story"
}
}
}
Loading
Loading