Skip to content

Commit 1860b93

Browse files
enhancement(header): link site name to wp-admin (WordPress#151)
* 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> * test: drop PRESS_THIS_PHPUNIT constant made redundant by trunk * fix: validate press_this_site_link_url result before use --------- Signed-off-by: Faisal Ahammad <faisalahammad24@gmail.com> Co-authored-by: Brandon Kraft <public@brandonkraft.com>
1 parent c11d17b commit 1860b93

6 files changed

Lines changed: 124 additions & 2 deletions

File tree

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,23 @@ 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 $site_link_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+
$site_link_url = is_string( $site_link_url ) ? esc_url_raw( $site_link_url ) : '';
1574+
1575+
if ( '' === $site_link_url ) {
1576+
$site_link_url = admin_url();
1577+
}
1578+
15621579
// Comprehensive data object for React app.
15631580
$press_this_data = array(
15641581
// Post data.
@@ -1579,6 +1596,7 @@ public function html() {
15791596
// Decode HTML entities for proper display (e.g., &#8211; to –).
15801597
'siteName' => html_entity_decode( get_bloginfo( 'name', 'display' ), ENT_QUOTES | ENT_HTML5, 'UTF-8' ),
15811598
'siteUrl' => home_url( '/' ),
1599+
'adminUrl' => $site_link_url,
15821600
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
15831601
'restUrl' => rest_url( 'press-this/v1/' ),
15841602
'restNonce' => wp_create_nonce( 'wp_rest' ),

‎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
@@ -199,6 +199,7 @@ function isFutureDate( dateString, tz ) {
199199
* @param {Object} props Component props.
200200
* @param {string} props.siteName Site name.
201201
* @param {string} props.siteUrl Site URL.
202+
* @param {string} props.adminUrl Admin URL used by the site name link.
202203
* @param {string} props.sourceUrl Source URL being clipped.
203204
* @param {boolean} props.isLegacyBookmarklet Whether this is a legacy bookmarklet.
204205
* @param {boolean} props.hasBookmarkletContent Whether content was already provided by bookmarklet.
@@ -230,6 +231,7 @@ export {
230231
export default function Header( {
231232
siteName,
232233
siteUrl,
234+
adminUrl,
233235
sourceUrl,
234236
isLegacyBookmarklet,
235237
hasBookmarkletContent = false,
@@ -509,7 +511,7 @@ export default function Header( {
509511
<div className="press-this-header__bar">
510512
<div className="press-this-header__site">
511513
<a
512-
href={ siteUrl }
514+
href={ adminUrl || siteUrl }
513515
target="_blank"
514516
rel="noopener noreferrer"
515517
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/test-integration.php‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,62 @@ public function test_post_message_and_window_name_modes_independent() {
386386

387387
unset( $_GET['pm'] );
388388
}
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: an empty, non-string or unsafe filter result falls back to the admin URL.
422+
*/
423+
public function test_site_link_url_filter_falls_back_on_invalid_values() {
424+
foreach ( array( '', null, array( 'https://example.com/' ), 'javascript:alert(1)' ) as $value ) {
425+
$site_link_filter = function () use ( $value ) {
426+
return $value;
427+
};
428+
add_filter( 'press_this_site_link_url', $site_link_filter );
429+
430+
try {
431+
$data = $this->get_press_this_data_from_html();
432+
$this->assertSame( admin_url(), $data['adminUrl'], 'Filter value: ' . wp_json_encode( $value ) );
433+
} finally {
434+
remove_filter( 'press_this_site_link_url', $site_link_filter );
435+
}
436+
}
437+
}
438+
439+
/**
440+
* Test: siteUrl still points to the front-end for the View Site menu item.
441+
*/
442+
public function test_site_url_still_points_to_front_end() {
443+
$data = $this->get_press_this_data_from_html();
444+
445+
$this->assertSame( home_url( '/' ), $data['siteUrl'] );
446+
}
389447
}

‎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() prints the object on one line, so match to the last }; on that line.
308+
// This keeps a }; inside a JSON string from ending the match early.
309+
preg_match( '/window\.pressThisData\s*=\s*(\{.*\});[ \t]*$/m', $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)