Skip to content

Commit 0167fb7

Browse files
authored
Merge pull request #48 from Automattic/add/permastructs
2 parents b6f18dc + 866b065 commit 0167fb7

File tree

1 file changed

+303
-5
lines changed

1 file changed

+303
-5
lines changed

src/class-rewrite-rules-inspector.php

Lines changed: 303 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ public function action_init() {
9292
* @since 1.0.0
9393
*/
9494
public function action_admin_menu() {
95-
add_submenu_page( $this->parent_slug, __( 'Rewrite Rules Inspector', 'rewrite-rules-inspector' ), __( 'Rewrite Rules', 'rewrite-rules-inspector' ), $this->view_cap, $this->page_slug, array( $this, 'view_rules' ) );
95+
$hook = add_submenu_page( $this->parent_slug, __( 'Rewrite Rules Inspector', 'rewrite-rules-inspector' ), __( 'Rewrite Rules', 'rewrite-rules-inspector' ), $this->view_cap, $this->page_slug, array( $this, 'view_rules' ) );
96+
97+
// Add screen help.
98+
add_action( 'load-' . $hook, array( $this, 'add_screen_help' ) );
9699
}
97100

98101
/**
@@ -104,6 +107,198 @@ public function action_admin_notices() {
104107
echo '<div class="message updated"><p>' . esc_html__( 'Rewrite rules flushed.', 'rewrite-rules-inspector' ) . '</p></div>';
105108
}
106109

110+
/**
111+
* Add screen help tabs to explain rewrite rules and permastructs.
112+
*
113+
* @since 1.5.0
114+
*/
115+
public function add_screen_help() {
116+
$screen = get_current_screen();
117+
118+
// Overview tab.
119+
$screen->add_help_tab(
120+
array(
121+
'id' => 'overview',
122+
'title' => __( 'Overview', 'rewrite-rules-inspector' ),
123+
'content' => '<p>' . __( 'The Rewrite Rules Inspector helps you understand and debug your WordPress site\'s URL structure. It shows you all the rewrite rules and permastructs that WordPress uses to handle URLs.', 'rewrite-rules-inspector' ) . '</p>',
124+
)
125+
);
126+
127+
// Rewrite Rules tab.
128+
$screen->add_help_tab(
129+
array(
130+
'id' => 'rewrite-rules',
131+
'title' => __( 'Rewrite Rules', 'rewrite-rules-inspector' ),
132+
'content' => '<p>' . __( '<strong>Rewrite Rules</strong> are the actual URL patterns that WordPress uses to match incoming requests and determine what content to display.', 'rewrite-rules-inspector' ) . '</p>' .
133+
'<p>' . __( 'Each rule consists of:', 'rewrite-rules-inspector' ) . '</p>' .
134+
'<ul>' .
135+
'<li>' . __( '<strong>Rule:</strong> A regular expression pattern that matches URLs (e.g., <code>^category/([^/]+)/?$</code>)', 'rewrite-rules-inspector' ) . '</li>' .
136+
'<li>' . __( '<strong>Rewrite:</strong> The internal WordPress query that gets executed (e.g., <code>index.php?category_name=$matches[1]</code>)', 'rewrite-rules-inspector' ) . '</li>' .
137+
'<li>' . __( '<strong>Source:</strong> Where the rule comes from (e.g., category, post, custom permastruct)', 'rewrite-rules-inspector' ) . '</li>' .
138+
'</ul>' .
139+
'<p>' . __( 'When someone visits a URL, WordPress checks these rules in order until it finds a match, then executes the corresponding rewrite to determine what content to show.', 'rewrite-rules-inspector' ) . '</p>',
140+
)
141+
);
142+
143+
// Permastructs tab.
144+
$screen->add_help_tab(
145+
array(
146+
'id' => 'permastructs',
147+
'title' => __( 'Permastructs', 'rewrite-rules-inspector' ),
148+
'content' => '<p>' . __( '<strong>Permastructs</strong> are the URL structure templates that define how different types of content should be accessed via URLs.', 'rewrite-rules-inspector' ) . '</p>' .
149+
'<p>' . __( 'For example:', 'rewrite-rules-inspector' ) . '</p>' .
150+
'<ul>' .
151+
'<li>' . __( '<strong>Post Permalink:</strong> <code>/%year%/%monthnum%/%day%/%postname%/</code> - defines how individual posts are accessed', 'rewrite-rules-inspector' ) . '</li>' .
152+
'<li>' . __( '<strong>Category Archive:</strong> <code>/category/%category%</code> - defines how category pages are accessed', 'rewrite-rules-inspector' ) . '</li>' .
153+
'<li>' . __( '<strong>Tag Archive:</strong> <code>/tag/%post_tag%</code> - defines how tag pages are accessed', 'rewrite-rules-inspector' ) . '</li>' .
154+
'</ul>' .
155+
'<p>' . __( 'WordPress uses these permastructs to generate the actual rewrite rules. The permastructs are like blueprints, while the rewrite rules are the specific patterns that get created from those blueprints.', 'rewrite-rules-inspector' ) . '</p>' .
156+
'<p>' . __( 'You can customize permastructs through WordPress settings (Settings → Permalinks) or by using WordPress functions like <code>add_permastruct()</code> in your theme or plugin.', 'rewrite-rules-inspector' ) . '</p>',
157+
)
158+
);
159+
160+
// Troubleshooting tab.
161+
$screen->add_help_tab(
162+
array(
163+
'id' => 'troubleshooting',
164+
'title' => __( 'Troubleshooting', 'rewrite-rules-inspector' ),
165+
'content' => '<p>' . __( '<strong>Common Issues:</strong>', 'rewrite-rules-inspector' ) . '</p>' .
166+
'<ul>' .
167+
'<li>' . __( '<strong>Missing Rules:</strong> If you see rules marked as "missing", try clicking the "Flush Rules" button to regenerate them.', 'rewrite-rules-inspector' ) . '</li>' .
168+
'<li>' . __( '<strong>404 Errors:</strong> Check if the URL pattern exists in the rewrite rules. Use the "Match URL" filter to test specific URLs.', 'rewrite-rules-inspector' ) . '</li>' .
169+
'<li>' . __( '<strong>Custom URLs Not Working:</strong> Verify that your custom permastruct is properly registered and that rewrite rules have been flushed.', 'rewrite-rules-inspector' ) . '</li>' .
170+
'<li>' . __( '<strong>Plugin Conflicts:</strong> Some plugins may modify rewrite rules. Check the "Source" column to see which rules come from which sources.', 'rewrite-rules-inspector' ) . '</li>' .
171+
'</ul>' .
172+
'<p>' . __( '<strong>Tips:</strong>', 'rewrite-rules-inspector' ) . '</p>' .
173+
'<ul>' .
174+
'<li>' . __( 'Use the "Rule Source" filter to focus on specific types of rules.', 'rewrite-rules-inspector' ) . '</li>' .
175+
'<li>' . __( 'Download the rules as a text file for offline analysis.', 'rewrite-rules-inspector' ) . '</li>' .
176+
'<li>' . __( 'Check the permastructs section to understand the URL structure templates.', 'rewrite-rules-inspector' ) . '</li>' .
177+
'</ul>',
178+
)
179+
);
180+
181+
// Help sidebar.
182+
$screen->set_help_sidebar(
183+
'<p><strong>' . __( 'For more information:', 'rewrite-rules-inspector' ) . '</strong></p>' .
184+
'<p><a href="https://wordpress.org/support/article/using-permalinks/" target="_blank">' . __( 'WordPress Permalinks Documentation', 'rewrite-rules-inspector' ) . '</a></p>' .
185+
'<p><a href="https://developer.wordpress.org/reference/functions/add_rewrite_rule/" target="_blank">' . __( 'WordPress Rewrite API', 'rewrite-rules-inspector' ) . '</a></p>' .
186+
'<p><a href="https://github.com/Automattic/Rewrite-Rules-Inspector" target="_blank">' . __( 'Plugin on GitHub', 'rewrite-rules-inspector' ) . '</a></p>'
187+
);
188+
}
189+
190+
/**
191+
* Get all permastructs that WordPress is aware of.
192+
*
193+
* @since 1.5.0
194+
* @return array Array of permastructs with their names and structures.
195+
*/
196+
public function get_permastructs() {
197+
global $wp_rewrite;
198+
199+
$permastructs = array();
200+
201+
// Core permastructs.
202+
$permastructs['post'] = array(
203+
'name' => __( 'Post Permalink', 'rewrite-rules-inspector' ),
204+
'structure' => $wp_rewrite->permalink_structure,
205+
'description' => __( 'The permalink structure for posts', 'rewrite-rules-inspector' ),
206+
);
207+
208+
$permastructs['date'] = array(
209+
'name' => __( 'Date Archive', 'rewrite-rules-inspector' ),
210+
'structure' => $wp_rewrite->get_date_permastruct(),
211+
'description' => __( 'The permalink structure for date archives', 'rewrite-rules-inspector' ),
212+
);
213+
214+
$permastructs['search'] = array(
215+
'name' => __( 'Search Results', 'rewrite-rules-inspector' ),
216+
'structure' => $wp_rewrite->get_search_permastruct(),
217+
'description' => __( 'The permalink structure for search results', 'rewrite-rules-inspector' ),
218+
);
219+
220+
$permastructs['author'] = array(
221+
'name' => __( 'Author Archive', 'rewrite-rules-inspector' ),
222+
'structure' => $wp_rewrite->get_author_permastruct(),
223+
'description' => __( 'The permalink structure for author archives', 'rewrite-rules-inspector' ),
224+
);
225+
226+
$permastructs['comments'] = array(
227+
'name' => __( 'Comments', 'rewrite-rules-inspector' ),
228+
'structure' => $wp_rewrite->root . $wp_rewrite->comments_base,
229+
'description' => __( 'The permalink structure for comments', 'rewrite-rules-inspector' ),
230+
);
231+
232+
$permastructs['root'] = array(
233+
'name' => __( 'Root', 'rewrite-rules-inspector' ),
234+
'structure' => $wp_rewrite->root . '/',
235+
'description' => __( 'The root permalink structure', 'rewrite-rules-inspector' ),
236+
);
237+
238+
// Extra permastructs including tags, categories, etc.
239+
foreach ( $wp_rewrite->extra_permastructs as $permastructname => $permastruct ) {
240+
$structure = '';
241+
if ( is_array( $permastruct ) ) {
242+
// Pre 3.4 compat.
243+
if ( count( $permastruct ) === 2 ) {
244+
$structure = $permastruct[0];
245+
} else {
246+
$structure = $permastruct['struct'] ?? '';
247+
}
248+
} else {
249+
$structure = $permastruct;
250+
}
251+
252+
// Generate human-readable names and descriptions.
253+
$name = ucwords( str_replace( array( '_', '-' ), ' ', $permastructname ) );
254+
/* translators: %s: permastruct name */
255+
$description = sprintf( __( 'The permalink structure for %s', 'rewrite-rules-inspector' ), strtolower( $name ) );
256+
257+
// Special cases for common permastructs.
258+
switch ( $permastructname ) {
259+
case 'category':
260+
$name = __( 'Category Archive', 'rewrite-rules-inspector' );
261+
$description = __( 'The permalink structure for category archives', 'rewrite-rules-inspector' );
262+
break;
263+
case 'post_tag':
264+
$name = __( 'Tag Archive', 'rewrite-rules-inspector' );
265+
$description = __( 'The permalink structure for tag archives', 'rewrite-rules-inspector' );
266+
break;
267+
case 'post_format':
268+
$name = __( 'Post Format Archive', 'rewrite-rules-inspector' );
269+
$description = __( 'The permalink structure for post format archives', 'rewrite-rules-inspector' );
270+
break;
271+
case 'test_custom':
272+
$name = __( 'Test Custom (Demo)', 'rewrite-rules-inspector' );
273+
$description = __( 'A custom permastruct added for testing the permastructs display feature', 'rewrite-rules-inspector' );
274+
break;
275+
case 'demo_archive':
276+
$name = __( 'Demo Archive (Test)', 'rewrite-rules-inspector' );
277+
$description = __( 'A demo archive permastruct with date-based structure for testing', 'rewrite-rules-inspector' );
278+
break;
279+
}
280+
281+
$permastructs[ $permastructname ] = array(
282+
'name' => $name,
283+
'structure' => $structure,
284+
'description' => $description,
285+
);
286+
}
287+
288+
// Filter out empty structures.
289+
$permastructs = array_filter(
290+
$permastructs,
291+
function ( $permastruct ) {
292+
return ! empty( $permastruct['structure'] );
293+
}
294+
);
295+
296+
// Allow filtering of permastructs.
297+
$permastructs = apply_filters( 'rri_permastructs', $permastructs );
298+
299+
return $permastructs;
300+
}
301+
107302
/**
108303
* Get the rewrite rules for the current view.
109304
*
@@ -250,15 +445,38 @@ public function view_rules() {
250445
border-top-color: #fecfd0;
251446
border-bottom-color: #f99b9d;
252447
}
448+
#permastructs-section {
449+
margin-top: 30px;
450+
}
451+
.permastruct-structure {
452+
font-family: monospace;
453+
background: #f6f7f7;
454+
padding: 2px 6px;
455+
border-radius: 3px;
456+
font-size: 13px;
457+
}
253458
</style>
254459
<div class="wrap">
255460
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
256461

462+
<?php
463+
// Get permastructs for jump link.
464+
$permastructs = $this->get_permastructs();
465+
?>
466+
467+
<h2 id="rewrite-rules-section"><?php esc_html_e( 'Rewrite Rules', 'rewrite-rules-inspector' ); ?></h2>
468+
469+
<?php if ( ! empty( $permastructs ) ) : ?>
470+
<p>
471+
<a href="#permastructs-section"><?php esc_html_e( 'Jump to Permastructs', 'rewrite-rules-inspector' ); ?></a>
472+
</p>
473+
<?php endif; ?>
474+
257475
<?php
258476
$missing_count = 0;
259477
foreach ( $rules as $rule ) {
260478
if ( 'missing' === $rule['source'] ) {
261-
$missing_count++;
479+
++$missing_count;
262480
}
263481
}
264482

@@ -286,10 +504,90 @@ public function view_rules() {
286504
printf( esc_html__( 'A listing of all %1$s rewrite rules for this site.', 'rewrite-rules-inspector' ), count( $wp_list_table->items ) );
287505
?>
288506
</p>
289-
<?php endif;
507+
<?php endif; ?>
290508

291-
$wp_list_table->display();
292-
?>
509+
<?php $wp_list_table->display(); ?>
510+
511+
<?php if ( ! empty( $permastructs ) ) : ?>
512+
<h2 id="permastructs-section"><?php esc_html_e( 'Permastructs', 'rewrite-rules-inspector' ); ?></h2>
513+
514+
<p>
515+
<?php
516+
/* translators: %d: Count of permastructs */
517+
printf( esc_html__( 'A listing of all %d permastructs that WordPress is aware of.', 'rewrite-rules-inspector' ), count( $permastructs ) );
518+
?>
519+
<a href="#rewrite-rules-section"><?php esc_html_e( 'Jump to Rewrite Rules', 'rewrite-rules-inspector' ); ?></a>
520+
</p>
521+
522+
<?php
523+
// Create a simple list table for permastructs.
524+
$permastructs_table = new WP_List_Table(
525+
array(
526+
'singular' => 'Permastruct',
527+
'plural' => 'Permastructs',
528+
)
529+
);
530+
531+
// Set up the columns.
532+
$permastructs_table->_column_headers = array(
533+
array(
534+
'name' => __( 'Name', 'rewrite-rules-inspector' ),
535+
'structure' => __( 'Structure', 'rewrite-rules-inspector' ),
536+
'description' => __( 'Description', 'rewrite-rules-inspector' ),
537+
),
538+
array(),
539+
array(),
540+
);
541+
542+
// Set the items.
543+
$permastructs_table->items = $permastructs;
544+
545+
// Display the table.
546+
?>
547+
<table class="wp-list-table widefat fixed striped">
548+
<thead>
549+
<tr>
550+
<th scope="col" class="manage-column column-name column-primary">
551+
<?php esc_html_e( 'Name', 'rewrite-rules-inspector' ); ?>
552+
</th>
553+
<th scope="col" class="manage-column column-structure">
554+
<?php esc_html_e( 'Structure', 'rewrite-rules-inspector' ); ?>
555+
</th>
556+
<th scope="col" class="manage-column column-description">
557+
<?php esc_html_e( 'Description', 'rewrite-rules-inspector' ); ?>
558+
</th>
559+
</tr>
560+
</thead>
561+
<tbody id="the-list">
562+
<?php foreach ( $permastructs as $permastruct ) : ?>
563+
<tr>
564+
<td class="name column-name column-primary">
565+
<strong><?php echo esc_html( $permastruct['name'] ); ?></strong>
566+
</td>
567+
<td class="structure column-structure">
568+
<code class="permastruct-structure"><?php echo esc_html( $permastruct['structure'] ); ?></code>
569+
</td>
570+
<td class="description column-description">
571+
<?php echo esc_html( $permastruct['description'] ); ?>
572+
</td>
573+
</tr>
574+
<?php endforeach; ?>
575+
</tbody>
576+
<tfoot>
577+
<tr>
578+
<th scope="col" class="manage-column column-name column-primary">
579+
<?php esc_html_e( 'Name', 'rewrite-rules-inspector' ); ?>
580+
</th>
581+
<th scope="col" class="manage-column column-structure">
582+
<?php esc_html_e( 'Structure', 'rewrite-rules-inspector' ); ?>
583+
</th>
584+
<th scope="col" class="manage-column column-description">
585+
<?php esc_html_e( 'Description', 'rewrite-rules-inspector' ); ?>
586+
</th>
587+
</tr>
588+
</tfoot>
589+
</table>
590+
<?php endif; ?>
293591
</div>
294592
<?php
295593
}

0 commit comments

Comments
 (0)