Skip to content

Commit 986b08f

Browse files
committed
enhancement(header): link site name to wp-admin
- Add adminUrl to pressThisData, defaulting to admin_url() - New press_this_site_link_url filter lets the link be customized - Header site name now opens the dashboard; View Site menu item keeps the front-end URL - Guard html() with die() only outside PHPUnit so helper tests can capture output Resolves WordPress#4 Signed-off-by: Faisal Ahammad <faisalahammad24@gmail.com>
1 parent b301568 commit 986b08f

7 files changed

Lines changed: 112 additions & 4 deletions

File tree

‎class-wp-press-this-plugin.php‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,18 @@ public function html() {
15591559
}
15601560
}
15611561

1562+
/**
1563+
* Filters the URL used by the site name link in the Press This header.
1564+
*
1565+
* Defaults to the admin dashboard so users have an easy way back
1566+
* to wp-admin. The "View Site" menu item keeps using the front-end URL.
1567+
*
1568+
* @since 2.1.1
1569+
*
1570+
* @param string $admin_url URL for the header site name link. Default admin URL.
1571+
*/
1572+
$site_link_url = apply_filters( 'press_this_site_link_url', admin_url() );
1573+
15621574
// Comprehensive data object for React app.
15631575
$press_this_data = array(
15641576
// Post data.
@@ -1579,6 +1591,7 @@ public function html() {
15791591
// Decode HTML entities for proper display (e.g., &#8211; to –).
15801592
'siteName' => html_entity_decode( get_bloginfo( 'name', 'display' ), ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
15811593
'siteUrl' => home_url( '/' ),
1594+
'adminUrl' => $site_link_url,
15821595
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
15831596
'restUrl' => rest_url( 'press-this/v1/' ),
15841597
'restNonce' => wp_create_nonce( 'wp_rest' ),
@@ -1752,6 +1765,10 @@ public function html() {
17521765
</html>
17531766
<?php
17541767
// phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
1768+
// Return during unit tests so output can be captured; die in production.
1769+
if ( defined( 'PRESS_THIS_PHPUNIT' ) && PRESS_THIS_PHPUNIT ) {
1770+
return;
1771+
}
17551772
die();
17561773
}
17571774

‎src/App.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ export default function App() {
398398
<Header
399399
siteName={ data.siteName }
400400
siteUrl={ data.siteUrl }
401+
adminUrl={ data.adminUrl }
401402
sourceUrl={ data.sourceUrl }
402403
isLegacyBookmarklet={ data.isLegacyBookmarklet }
403404
hasBookmarkletContent={ !! data.content }

‎src/components/Header.js‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ function isFutureDate( dateString, tz ) {
201201
* @param {Object} props Component props.
202202
* @param {string} props.siteName Site name.
203203
* @param {string} props.siteUrl Site URL.
204+
* @param {string} props.adminUrl Admin URL used by the site name link.
204205
* @param {string} props.sourceUrl Source URL being clipped.
205206
* @param {boolean} props.isLegacyBookmarklet Whether this is a legacy bookmarklet.
206207
* @param {boolean} props.hasBookmarkletContent Whether content was already provided by bookmarklet.
@@ -232,6 +233,7 @@ export {
232233
export default function Header( {
233234
siteName,
234235
siteUrl,
236+
adminUrl,
235237
sourceUrl,
236238
isLegacyBookmarklet,
237239
hasBookmarkletContent = false,
@@ -511,7 +513,7 @@ export default function Header( {
511513
<div className="press-this-header__bar">
512514
<div className="press-this-header__site">
513515
<a
514-
href={ siteUrl }
516+
href={ adminUrl || siteUrl }
515517
target="_blank"
516518
rel="noopener noreferrer"
517519
className="press-this-header__site-link"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Header Site Link Tests
3+
*
4+
* Verifies the site name link in the header points to wp-admin
5+
* (adminUrl) with a front-end fallback, while the View Site menu
6+
* item keeps using the front-end URL. See GitHub issue #4.
7+
*
8+
* @package press-this
9+
*/
10+
11+
const fs = require( 'fs' );
12+
const path = require( 'path' );
13+
14+
describe( 'Header Site Link', () => {
15+
let headerContent;
16+
let appContent;
17+
18+
beforeAll( () => {
19+
const headerPath = path.resolve( __dirname, '../../src/components/Header.js' );
20+
headerContent = fs.readFileSync( headerPath, 'utf8' );
21+
22+
const appPath = path.resolve( __dirname, '../../src/App.js' );
23+
appContent = fs.readFileSync( appPath, 'utf8' );
24+
} );
25+
26+
test( 'Header accepts adminUrl prop', () => {
27+
expect( headerContent ).toContain( 'adminUrl' );
28+
} );
29+
30+
test( 'site name link uses adminUrl with siteUrl fallback', () => {
31+
expect( headerContent ).toContain( 'href={ adminUrl || siteUrl }' );
32+
} );
33+
34+
test( 'View Site menu item still uses siteUrl', () => {
35+
expect( headerContent ).toContain( 'href={ siteUrl }' );
36+
} );
37+
38+
test( 'App passes adminUrl from pressThisData to Header', () => {
39+
expect( appContent ).toContain( 'adminUrl={ data.adminUrl }' );
40+
} );
41+
} );

‎tests/php/bootstrap.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
define( 'DOING_AJAX', true );
2222
}
2323

24+
// Signal unit tests so html() returns instead of die() and output can be captured.
25+
if ( ! defined( 'PRESS_THIS_PHPUNIT' ) ) {
26+
define( 'PRESS_THIS_PHPUNIT', true );
27+
}
28+
2429
// Initialize WorDBless with SQLite database support.
2530
// This enables full database operations including taxonomies and categories.
2631
// Note: First run shows "no such table: wp_options" error during initialization,

‎tests/php/test-integration.php‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ function ( $severity, $message, $file, $line ) {
115115
);
116116
}
117117

118-
// Anchor to </script> to avoid early termination on }; inside JSON strings.
119-
preg_match( '/window\.pressThisData\s*=\s*({.+?})\s*;\s*<\/script>/s', $html, $matches );
118+
// wp_json_encode emits a single line; match that line only (no /s)
119+
// so later script tags cannot bleed into the capture.
120+
preg_match( '/window\.pressThisData\s*=\s*(\{.*\})\s*;/', $html, $matches );
120121

121122
$this->assertNotEmpty( $matches[1], 'pressThisData JSON not found in html() output.' );
122123

@@ -385,4 +386,43 @@ public function test_post_message_and_window_name_modes_independent() {
385386

386387
unset( $_GET['pm'] );
387388
}
389+
390+
/**
391+
* Test: adminUrl defaults to the admin dashboard URL.
392+
*
393+
* The header site name link uses adminUrl so users have an easy
394+
* way back to wp-admin. See GitHub issue #4.
395+
*/
396+
public function test_admin_url_defaults_to_admin_dashboard() {
397+
$data = $this->get_press_this_data_from_html();
398+
399+
$this->assertArrayHasKey( 'adminUrl', $data );
400+
$this->assertSame( admin_url(), $data['adminUrl'] );
401+
}
402+
403+
/**
404+
* Test: press_this_site_link_url filter changes adminUrl.
405+
*/
406+
public function test_site_link_url_filter_works() {
407+
$site_link_filter = function () {
408+
return 'https://example.com/custom-dashboard/';
409+
};
410+
add_filter( 'press_this_site_link_url', $site_link_filter );
411+
412+
try {
413+
$data = $this->get_press_this_data_from_html();
414+
$this->assertSame( 'https://example.com/custom-dashboard/', $data['adminUrl'] );
415+
} finally {
416+
remove_filter( 'press_this_site_link_url', $site_link_filter );
417+
}
418+
}
419+
420+
/**
421+
* Test: siteUrl still points to the front-end for the View Site menu item.
422+
*/
423+
public function test_site_url_still_points_to_front_end() {
424+
$data = $this->get_press_this_data_from_html();
425+
426+
$this->assertSame( home_url( '/' ), $data['siteUrl'] );
427+
}
388428
}

‎tests/php/test-press-this-plugin.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,9 @@ private function get_press_this_data_from_html() {
304304
$_SERVER['REQUEST_METHOD'] = $previous_method;
305305
}
306306

307-
preg_match( '/window\.pressThisData\s*=\s*({.+?});/s', $html, $matches );
307+
// wp_json_encode emits a single line; match that line only (no /s)
308+
// so later script tags cannot bleed into the capture.
309+
preg_match( '/window\.pressThisData\s*=\s*(\{.*\})\s*;/', $html, $matches );
308310

309311
// If pressThisData was not found and an exception was caught, surface it
310312
// so test failures point to the actual error, not a generic message.

0 commit comments

Comments
 (0)