Skip to content

Commit ff95bd8

Browse files
Copilotsybrew
andauthored
Add primary category selection to quick-edit and bulk-edit interfaces (#731)
Closes #512. Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Sybre Waaijer <sybrew@users.noreply.github.com>
1 parent 29a5596 commit ff95bd8

File tree

20 files changed

+832
-92
lines changed

20 files changed

+832
-92
lines changed

autodescription.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Plugin Name: The SEO Framework
44
* Plugin URI: https://theseoframework.com/
55
* Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for your WordPress website.
6-
* Version: 5.1.3-dev-20
6+
* Version: 5.1.3-dev-22
77
* Author: Sybre Waaijer
88
* Author URI: https://theseoframework.com/
99
* Troy: https://repo.theseoframework.com/

inc/classes/admin/script/loader.class.php

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ public static function init() {
101101
$scripts[] = static::get_description_scripts();
102102
$scripts[] = static::get_canonical_scripts();
103103

104+
if ( Query::is_singular_admin() )
105+
$scripts[] = static::get_primaryterm_scripts();
106+
104107
if ( Data\Plugin::get_option( 'display_pixel_counter' ) || Data\Plugin::get_option( 'display_character_counter' ) )
105108
$scripts[] = static::get_counter_scripts();
106109
} elseif ( Query::is_seo_settings_page() ) {
@@ -331,7 +334,7 @@ public static function get_list_edit_scripts() {
331334
[
332335
'id' => 'tsf-le',
333336
'type' => 'js',
334-
'deps' => [ 'tsf-title', 'tsf-description', 'tsf-canonical', 'tsf-postslugs', 'tsf-termslugs', 'tsf-authorslugs', 'tsf', 'tsf-tt', 'tsf-utils' ],
337+
'deps' => [ 'tsf-pt-le', 'tsf-title', 'tsf-description', 'tsf-canonical', 'tsf-postslugs', 'tsf-termslugs', 'tsf-authorslugs', 'tsf', 'tsf-tt', 'tsf-utils' ],
335338
'autoload' => true,
336339
'name' => 'le',
337340
'base' => \THE_SEO_FRAMEWORK_DIR_URL . 'lib/js/',
@@ -813,12 +816,13 @@ public static function get_canonical_scripts() {
813816
* @since 4.0.0
814817
* @since 4.1.0 Now filters out unsupported taxonomies.
815818
* @since 5.1.0 Changed the dependencies for pt, because we now use a select field.
819+
* @since 5.1.3 Added list edit support.
816820
*
817821
* @return array The script params.
818822
*/
819823
public static function get_primaryterm_scripts() {
820824

821-
$post_id = Query::get_the_real_admin_id();
825+
$is_list_edit = Query::is_wp_lists_edit();
822826

823827
$post_type = Query::get_admin_post_type();
824828
$_taxonomies = $post_type ? Taxonomy::get_hierarchical( 'names', $post_type ) : [];
@@ -828,7 +832,7 @@ public static function get_primaryterm_scripts() {
828832
if ( ! Taxonomy::is_supported( $tax ) ) continue;
829833

830834
$singular_name = Taxonomy::get_label( $tax );
831-
$primary_term_id = Data\Plugin\Post::get_primary_term_id( $post_id, $tax );
835+
$primary_term_id = Data\Plugin\Post::get_primary_term_id( Query::get_the_real_admin_id(), $tax );
832836

833837
$taxonomies[ $tax ] = [
834838
'name' => $tax,
@@ -840,20 +844,33 @@ public static function get_primaryterm_scripts() {
840844
];
841845
}
842846

843-
if ( Query::is_block_editor() ) {
847+
if ( $is_list_edit ) {
844848
$vars = [
845-
'id' => 'tsf-pt-gb',
846-
'name' => 'pt-gb',
849+
'id' => 'tsf-pt-le',
850+
'name' => 'pt-le',
847851
];
848-
$deps = [ 'tsf', 'tsf-ays', 'wp-hooks', 'wp-element', 'wp-components', 'wp-data', 'wp-util' ];
852+
$deps = [ 'tsf', 'wp-util' ];
849853
} else {
850-
$vars = [
851-
'id' => 'tsf-pt',
852-
'name' => 'pt',
853-
];
854-
$deps = [ 'tsf', 'tsf-ays', 'wp-util' ];
854+
// If not list edit, we're in the post editor.
855+
if ( Query::is_block_editor() ) {
856+
$vars = [
857+
'id' => 'tsf-pt-gb',
858+
'name' => 'pt-gb',
859+
];
860+
$deps = [ 'tsf', 'tsf-ays', 'wp-hooks', 'wp-element', 'wp-components', 'wp-data', 'wp-util' ];
861+
} else {
862+
$vars = [
863+
'id' => 'tsf-pt',
864+
'name' => 'pt',
865+
];
866+
$deps = [ 'tsf', 'tsf-ays', 'wp-util' ];
867+
}
855868
}
856869

870+
$tmpl_file = $is_list_edit
871+
? Template::get_view_location( 'templates/list/primary-term-selector' )
872+
: Template::get_view_location( 'templates/inpost/primary-term-selector' );
873+
857874
return [
858875
[
859876
'id' => 'tsf-pt',
@@ -879,7 +896,7 @@ public static function get_primaryterm_scripts() {
879896
],
880897
],
881898
'tmpl' => [
882-
'file' => Template::get_view_location( 'templates/inpost/primary-term-selector' ),
899+
'file' => $tmpl_file,
883900
],
884901
],
885902
];

inc/classes/admin/settings/listedit.class.php

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ public function output_column_contents_for_post( $column_name, $post_id ) {
256256
HTML::make_data_attributes( [ 'le' => $data ] )
257257
);
258258

259+
$primary_terms = [];
260+
259261
if ( $is_homepage ) {
260262
// When the homepage title is set, we can safely get the custom field.
261263
$_has_home_title = (bool) \strlen( Data\Plugin::get_option( 'homepage_title' ) );
@@ -281,6 +283,8 @@ public function output_column_contents_for_post( $column_name, $post_id ) {
281283

282284
$permastruct = Meta\URI\Utils::get_url_permastruct( $generator_args );
283285
$is_post_type_hierarchical = false; // Homepage cannot have a parent page.
286+
287+
$primary_terms = []; // Homepage cannot have terms -- at least... why would anyone.
284288
} else {
285289
static $memo = [];
286290

@@ -298,7 +302,7 @@ public function output_column_contents_for_post( $column_name, $post_id ) {
298302
$is_canonical_ref_locked = false;
299303
$default_canonical = Meta\URI::get_generated_url( $generator_args );
300304

301-
$memo['post_type'] ??= Query::get_admin_post_type();
305+
$memo['post_type'] ??= Query::get_post_type_real_id( $post_id );
302306
$memo['permastruct'] ??= Meta\URI\Utils::get_url_permastruct( $generator_args );
303307
$memo['is_post_type_hierarchical'] ??= \is_post_type_hierarchical( $memo['post_type'] );
304308

@@ -321,25 +325,30 @@ public function output_column_contents_for_post( $column_name, $post_id ) {
321325
}
322326

323327
// Only hierarchical taxonomies can be used in the URL.
324-
// TODO filter post_tag here.
325-
$memo['taxonomies'] ??= $post_type ? Taxonomy::get_hierarchical( 'names', $post_type ) : [];
328+
$memo['taxonomies'] ??= array_diff(
329+
$post_type ? Taxonomy::get_hierarchical( 'names', $post_type ) : [],
330+
// post_tag isn't hierarchical by default, but it can be filtered to be.
331+
// It's broken in Core when used in the permastruct. Nobody should be using %post_tag%.
332+
[ 'post_tag' ],
333+
);
326334

327335
$taxonomies = $memo['taxonomies'];
328336
$parent_term_slugs_by_tax = [];
337+
$primary_term_ids = [];
338+
339+
// Store primary term IDs for later use.
340+
foreach ( $taxonomies as $taxonomy )
341+
$primary_term_ids[ $taxonomy ] = Data\Plugin\Post::get_primary_term_id( $post_id, $taxonomy );
329342

330343
// Yes, on its surface, this is a very expensive procedure.
331344
// However, WordPress needs to walk all the terms already to create the post links.
332345
// Hence, it ought to net to zero impact.
333346
foreach ( $taxonomies as $taxonomy ) {
334347
if ( str_contains( $permastruct, "%$taxonomy%" ) ) {
335-
// Broken in Core. Skip writing cache. We may reach this line 200 times, but nobody should be using %post_tag% anyway.
336-
if ( 'post_tag' === $taxonomy ) continue;
337-
338-
$parent_term_slugs_by_tax[ $taxonomy ] = [];
339348
// There's no need to test for hierarchy, because we want the full structure anyway (third parameter).
340349
foreach (
341350
Data\Term::get_term_parents(
342-
Data\Plugin\Post::get_primary_term_id( $post_id, $taxonomy ),
351+
$primary_term_ids[ $taxonomy ],
343352
$taxonomy,
344353
true,
345354
)
@@ -367,6 +376,14 @@ public function output_column_contents_for_post( $column_name, $post_id ) {
367376
];
368377
}
369378
}
379+
380+
foreach ( $taxonomies as $taxonomy ) {
381+
$primary_terms[ "primary_term_{$taxonomy}" ] = [
382+
'value' => $primary_term_ids[ $taxonomy ] ?? 0,
383+
'isSelect' => true,
384+
'taxonomy' => $taxonomy,
385+
];
386+
}
370387
}
371388

372389
printf(
@@ -376,7 +393,8 @@ public function output_column_contents_for_post( $column_name, $post_id ) {
376393
// phpcs:disable WordPress.Security.EscapeOutput -- make_data_attributes escapes.
377394
HTML::make_data_attributes( [
378395
'lePostData' => [
379-
'isFront' => Query::is_static_front_page( $generator_args['id'] ),
396+
'isFront' => Query::is_static_front_page( $generator_args['id'] ),
397+
'primaryTerms' => $primary_terms,
380398
],
381399
] ),
382400
// phpcs:enable WordPress.Security.EscapeOutput

inc/classes/data/admin/post.class.php

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,14 @@ public static function update_meta( $post_id ) {
9090
* 2. Now allows updating during `WP_AJAX`.
9191
* @since 5.0.0 1. Moved from `\The_SEO_Framework\Load`.
9292
* 2. Renamed from `_save_inpost_primary_term`.
93+
* @since 5.1.3 Now supports quick-edit and bulk-edit.
9394
*
9495
* @param int $post_id The post ID.
9596
* @return void
9697
*/
9798
public static function update_primary_term( $post_id ) {
9899

99-
// The 'autodescription' index should only be used when using the editor.
100-
// Quick and bulk-edit should be halted here.
101-
if ( empty( $_POST['autodescription'] ) ) return;
102-
100+
// This resolves a quirk, since wp_insert_post() has no proper guard.
103101
$post_id = \get_post( $post_id )->ID ?? null;
104102

105103
if ( empty( $post_id ) ) return;
@@ -113,25 +111,70 @@ public static function update_primary_term( $post_id ) {
113111
*/
114112
if ( \wp_is_post_autosave( $post_id ) || \wp_is_post_revision( $post_id ) ) return;
115113

116-
// Check that the user is allowed to edit the post. Nonce checks are done in bulk later.
117114
if ( ! \current_user_can( 'edit_post', $post_id ) ) return;
118115

119-
$post_type = \get_post_type( $post_id ) ?: false;
116+
$post_type = \get_post_type( $post_id );
120117
// Can this even fail?
121118
if ( empty( $post_type ) ) return;
122119

123-
foreach ( Taxonomy::get_hierarchical( 'names', $post_type ) as $taxonomy ) {
124-
// Redundant. Fortified.
125-
if ( ! \wp_verify_nonce(
126-
$_POST[ static::$nonce_name . "_pt_{$taxonomy}" ] ?? '', // If empty, wp_verify_nonce will return false.
127-
static::$nonce_action . '_pt',
128-
) ) continue;
129-
130-
Data\Plugin\Post::update_primary_term_id(
131-
$post_id,
132-
$taxonomy,
133-
\absint( $_POST['autodescription'][ "_primary_term_{$taxonomy}" ] ?? 0 ),
134-
);
120+
// Determine edit type: post-edit, quick-edit, or bulk-edit
121+
if ( ! empty( $_POST['autodescription'] ) ) {
122+
// Post-edit
123+
foreach ( Taxonomy::get_hierarchical( 'names', $post_type ) as $taxonomy ) {
124+
if ( ! \wp_verify_nonce(
125+
$_POST[ static::$nonce_name . "_pt_{$taxonomy}" ] ?? '',
126+
static::$nonce_action . '_pt',
127+
) ) continue;
128+
129+
Data\Plugin\Post::update_primary_term_id(
130+
$post_id,
131+
$taxonomy,
132+
\absint( $_POST['autodescription'][ "_primary_term_{$taxonomy}" ] ?? 0 ),
133+
);
134+
}
135+
} elseif ( ! empty( $_POST['autodescription-quick'] ) ) {
136+
// Quick-edit
137+
if ( ! \check_ajax_referer( 'inlineeditnonce', '_inline_edit', false ) ) return;
138+
139+
foreach ( Taxonomy::get_hierarchical( 'names', $post_type ) as $taxonomy ) {
140+
if ( ! isset( $_POST['autodescription-quick'][ "primary_term_{$taxonomy}" ] ) ) continue;
141+
142+
$term_id = \absint( \wp_unslash( $_POST['autodescription-quick'][ "primary_term_{$taxonomy}" ] ) );
143+
144+
if ( $term_id > 0 )
145+
Data\Plugin\Post::update_primary_term_id( $post_id, $taxonomy, $term_id );
146+
}
147+
} elseif ( ! empty( $_REQUEST['autodescription-bulk'] ) ) {
148+
// Bulk-edit
149+
static $verified_bulk_referer = false;
150+
151+
if ( ! $verified_bulk_referer ) {
152+
\check_admin_referer( 'bulk-posts' );
153+
$verified_bulk_referer = true;
154+
}
155+
156+
foreach ( Taxonomy::get_hierarchical( 'names', $post_type ) as $taxonomy ) {
157+
if ( ! isset( $_REQUEST['autodescription-bulk'][ "primary_term_{$taxonomy}" ] ) ) continue;
158+
159+
$value = $_REQUEST['autodescription-bulk'][ "primary_term_{$taxonomy}" ];
160+
161+
if ( 'nochange' === $value ) continue;
162+
163+
$term_id = \absint( $value );
164+
165+
if ( $term_id > 0 ) {
166+
$terms = \get_the_terms( $post_id, $taxonomy );
167+
168+
if ( $terms && ! \is_wp_error( $terms ) ) {
169+
$valid_term_ids = \array_column( $terms, 'term_id' );
170+
171+
if ( \in_array( $term_id, $valid_term_ids, true ) )
172+
Data\Plugin\Post::update_primary_term_id( $post_id, $taxonomy, $term_id );
173+
}
174+
} else {
175+
Data\Plugin\Post::update_primary_term_id( $post_id, $taxonomy, 0 );
176+
}
177+
}
135178
}
136179
}
137180

@@ -221,6 +264,7 @@ private static function update_via_quick_edit( $post_id ) {
221264

222265
case 'canonical':
223266
$new_data['_genesis_canonical_uri'] = $value;
267+
break;
224268
}
225269
}
226270

inc/classes/helper/query.class.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ public static function is_post_edit() {
419419
*
420420
* @since 2.6.0
421421
* @since 5.0.0 Moved from `\The_SEO_Framework\Load`.
422+
* @todo should be renamed to is_list_edit()
422423
* @global \WP_Screen $current_screen
423424
*
424425
* @return bool We're on the edit screen.

inc/classes/meta/robots/factory.class.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ class Factory {
7373
* @return Factory $this
7474
*/
7575
public function set( $args = null, $options = 0 ) {
76+
7677
static::$args = $args;
7778
static::$options = $options;
79+
7880
return $this;
7981
}
8082

@@ -86,6 +88,7 @@ public function set( $args = null, $options = 0 ) {
8688
* @generator
8789
*/
8890
public static function generator() {
91+
8992
// phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.Found -- Shhh. It's OK.
9093
while ( true ) switch ( $sender = yield static::START ) {
9194
case 'noindex':

inc/classes/meta/robots/front.class.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ protected static function assert_no( $type ) {
9898
}
9999
}
100100

101-
globals:
101+
globals: {
102102
yield 'globals_site' => (bool) Data\Plugin::get_option( "site_$type" );
103103

104104
if ( Query::is_real_front_page() ) {
@@ -134,6 +134,7 @@ protected static function assert_no( $type ) {
134134
} elseif ( Query::is_singular() ) {
135135
yield 'globals_post_type' => Robots::is_post_type_robots_set( $type, Query::get_current_post_type() );
136136
}
137+
}
137138

138139
// We assert options here for a jump to index_protection might be unaware.
139140
index_protection: if ( $asserting_noindex && ! ( static::$options & ROBOTS_IGNORE_PROTECTION ) ) {
@@ -170,6 +171,7 @@ protected static function assert_no( $type ) {
170171
* @param string $pass The passage to assert.
171172
*/
172173
private static function assert_noindex_query_pass( $pass ) {
174+
173175
switch ( $pass ) {
174176
case 'paged_home':
175177
yield 'paged_home' =>

inc/classes/meta/robots/main.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public function get( $get = null ) {
141141
$start = $factory::START;
142142
$generator = $factory->set(
143143
$this->args,
144-
$options
144+
$options,
145145
)::generator();
146146

147147
$results = [];

inc/views/post/settings.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,15 +561,16 @@
561561
}
562562

563563
// Only hierarchical taxonomies can be used in the URL.
564-
// TODO filter post_tag here.
565-
$taxonomies = $post_type ? Taxonomy::get_hierarchical( 'names', $post_type ) : [];
564+
$taxonomies = array_diff(
565+
$post_type ? Taxonomy::get_hierarchical( 'names', $post_type ) : [],
566+
// post_tag isn't hierarchical by default, but it can be filtered to be.
567+
// It's broken in Core when used in the permastruct. Nobody should be using %post_tag%.
568+
[ 'post_tag' ],
569+
);
566570
$parent_term_slugs_by_tax = [];
567571

568572
foreach ( $taxonomies as $taxonomy ) {
569573
if ( str_contains( $permastruct, "%$taxonomy%" ) ) {
570-
// Broken in Core. Skip.
571-
if ( 'post_tag' === $taxonomy ) continue;
572-
573574
// There's no need to test for hierarchy, because we want the full structure anyway (third parameter).
574575
foreach (
575576
Data\Term::get_term_parents(

inc/views/templates/list/index.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
/**
3+
* Art is the elimination of the unnecessary.
4+
*
5+
* - Pablo Ruiz Picasso
6+
*/

0 commit comments

Comments
 (0)