272 lines
7.5 KiB
PHP
272 lines
7.5 KiB
PHP
<?php
|
|
namespace AIOSEO\Plugin\Common\Ai;
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Handles AI bulk actions for post types and the Media Library.
|
|
*
|
|
* @since 4.9.6
|
|
*/
|
|
class BulkActions {
|
|
/**
|
|
* The required capability to use bulk generation.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @var string
|
|
*/
|
|
const REQUIRED_CAPABILITY = 'aioseo_page_ai_content_settings';
|
|
|
|
/**
|
|
* Class constructor.
|
|
*
|
|
* @since 4.9.6
|
|
*/
|
|
public function __construct() {
|
|
add_action( 'admin_init', [ $this, 'registerBulkActions' ] );
|
|
}
|
|
|
|
/**
|
|
* Register bulk actions for all public post types.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @return void
|
|
*/
|
|
public function registerBulkActions() {
|
|
$postTypes = aioseo()->helpers->getPublicPostTypes( false, false, true );
|
|
|
|
foreach ( $postTypes as $postType ) {
|
|
if ( 'attachment' === $postType['name'] ) {
|
|
continue;
|
|
}
|
|
|
|
add_filter( 'bulk_actions-edit-' . $postType['name'], [ $this, 'addBulkAction' ] );
|
|
add_filter( 'handle_bulk_actions-edit-' . $postType['name'], [ $this, 'handleBulkAction' ], 10, 3 );
|
|
}
|
|
|
|
// Media Library bulk actions.
|
|
add_filter( 'bulk_actions-upload', [ $this, 'addMediaBulkAction' ] );
|
|
add_filter( 'handle_bulk_actions-upload', [ $this, 'handleMediaBulkAction' ], 10, 3 );
|
|
|
|
// Row action is registered only when the AIOSEO Details column is active,
|
|
// since it emits an event that the column's Vue component handles.
|
|
add_action( 'aioseo_details_column_activated', [ $this, 'registerMediaRowAction' ] );
|
|
}
|
|
|
|
/**
|
|
* Registers the media row action when the AIOSEO Details column is activated for attachments.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param string $postType The post type the column was activated for.
|
|
* @return void
|
|
*/
|
|
public function registerMediaRowAction( $postType ) {
|
|
if ( 'attachment' === $postType ) {
|
|
add_filter( 'media_row_actions', [ $this, 'addMediaRowAction' ], 10, 2 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add the AI bulk actions to a post type list.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param array $bulkActions The existing bulk actions.
|
|
* @return array The modified bulk actions.
|
|
*/
|
|
public function addBulkAction( $bulkActions ) {
|
|
if ( ! current_user_can( self::REQUIRED_CAPABILITY ) || $this->isTrashView() ) {
|
|
return $bulkActions;
|
|
}
|
|
|
|
$bulkActions[ AIOSEO_PLUGIN_SHORT_NAME ] = array_map( function ( $config ) {
|
|
return $config['label'];
|
|
}, $this->getPostListActions() );
|
|
|
|
return $bulkActions;
|
|
}
|
|
|
|
/**
|
|
* Add the AI bulk actions to the Media Library.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param array $bulkActions The existing bulk actions.
|
|
* @return array The modified bulk actions.
|
|
*/
|
|
public function addMediaBulkAction( $bulkActions ) {
|
|
if ( ! current_user_can( self::REQUIRED_CAPABILITY ) || $this->isTrashView() ) {
|
|
return $bulkActions;
|
|
}
|
|
|
|
$label = AIOSEO_PLUGIN_SHORT_NAME;
|
|
$actions = array_map( function ( $config ) {
|
|
return $config['label'];
|
|
}, $this->getMediaActions() );
|
|
|
|
$bulkActions[ $label ] = isset( $bulkActions[ $label ] )
|
|
? array_merge( $bulkActions[ $label ], $actions )
|
|
: $actions;
|
|
|
|
return $bulkActions;
|
|
}
|
|
|
|
/**
|
|
* Handle a post list bulk action when triggered.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param string $redirectTo The redirect URL.
|
|
* @param string $doAction The action being taken.
|
|
* @param array $postIds The array of post IDs.
|
|
* @return string The redirect URL.
|
|
*/
|
|
public function handleBulkAction( $redirectTo, $doAction, $postIds ) {
|
|
if ( ! current_user_can( self::REQUIRED_CAPABILITY ) ) {
|
|
return $redirectTo;
|
|
}
|
|
|
|
$actions = $this->getPostListActions();
|
|
if ( ! isset( $actions[ $doAction ] ) || empty( $postIds ) ) {
|
|
return $redirectTo;
|
|
}
|
|
|
|
return $this->buildRedirectUrl( $postIds, $actions[ $doAction ]['type'] );
|
|
}
|
|
|
|
/**
|
|
* Handle the Media Library bulk action when triggered.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param string $redirectTo The redirect URL.
|
|
* @param string $doAction The action being taken.
|
|
* @param array $postIds The array of post IDs.
|
|
* @return string The redirect URL.
|
|
*/
|
|
public function handleMediaBulkAction( $redirectTo, $doAction, $postIds ) {
|
|
if ( ! current_user_can( self::REQUIRED_CAPABILITY ) ) {
|
|
return $redirectTo;
|
|
}
|
|
|
|
$actions = $this->getMediaActions();
|
|
if ( ! isset( $actions[ $doAction ] ) || empty( $postIds ) ) {
|
|
return $redirectTo;
|
|
}
|
|
|
|
return $this->buildRedirectUrl( $postIds, $actions[ $doAction ]['type'] );
|
|
}
|
|
|
|
/**
|
|
* Add a row action to generate alt text with AI for individual images in the Media Library.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param array $actions The existing row actions.
|
|
* @param \WP_Post $post The current attachment post object.
|
|
* @return array The modified row actions.
|
|
*/
|
|
public function addMediaRowAction( $actions, $post ) {
|
|
if (
|
|
! current_user_can( self::REQUIRED_CAPABILITY ) ||
|
|
! current_user_can( 'edit_post', $post->ID ) ||
|
|
! aioseo()->helpers->attachmentIs( 'image', $post->ID ) ||
|
|
! aioseo()->addons->getLoadedAddon( 'imageSeo' )
|
|
) {
|
|
return $actions;
|
|
}
|
|
|
|
$onclick = sprintf(
|
|
'event.preventDefault(); window.aioseoBus && window.aioseoBus.$emit(\'generateAltInline%d\')',
|
|
$post->ID
|
|
);
|
|
|
|
$actions['aioseo_generate_alt'] = sprintf(
|
|
'<a href="#" onclick="%1$s">%2$s</a>',
|
|
esc_attr( $onclick ),
|
|
esc_html__( 'Generate Alt Text with AI', 'all-in-one-seo-pack' )
|
|
);
|
|
|
|
return $actions;
|
|
}
|
|
|
|
/**
|
|
* Whether the current list view is filtered to trashed posts.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function isTrashView() {
|
|
// Mirrors how WordPress core determines the trash view in WP_Posts_List_Table and WP_Media_List_Table.
|
|
// phpcs:disable HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
|
$postStatus = sanitize_text_field( wp_unslash( $_REQUEST['post_status'] ?? '' ) );
|
|
$attachmentFilter = sanitize_text_field( wp_unslash( $_REQUEST['attachment-filter'] ?? '' ) );
|
|
// phpcs:enable HM.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Recommended
|
|
|
|
return 'trash' === $postStatus || 'trash' === $attachmentFilter;
|
|
}
|
|
|
|
/**
|
|
* Returns the post list bulk action definitions.
|
|
* Single source of truth for post list action keys, labels, and types.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @return array[] Keyed by action slug, each with 'label' and 'type'.
|
|
*/
|
|
private function getPostListActions() {
|
|
return [
|
|
'aioseo_generate_ai_titles' => [
|
|
'label' => __( 'Generate SEO Titles with AI', 'all-in-one-seo-pack' ),
|
|
'type' => 'title'
|
|
],
|
|
'aioseo_generate_ai_descriptions' => [
|
|
'label' => __( 'Generate Meta Descriptions with AI', 'all-in-one-seo-pack' ),
|
|
'type' => 'description'
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Returns the Media Library bulk action definitions.
|
|
* Single source of truth for media action keys, labels, and types.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @return array[] Keyed by action slug, each with 'label' and 'type'.
|
|
*/
|
|
private function getMediaActions() {
|
|
return [
|
|
'aioseo_generate_ai_alt_text' => [
|
|
'label' => __( 'Generate Alt Text for Images with AI', 'all-in-one-seo-pack' ),
|
|
'type' => 'alt'
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Build the redirect URL for the AI bulk generate page.
|
|
*
|
|
* @since 4.9.6
|
|
*
|
|
* @param array $postIds The array of post IDs.
|
|
* @param string $type The generation type (title, description, alt).
|
|
* @return string The redirect URL.
|
|
*/
|
|
private function buildRedirectUrl( $postIds, $type ) {
|
|
return add_query_arg(
|
|
[
|
|
'ids' => implode( ',', array_map( 'intval', $postIds ) ),
|
|
'type' => $type
|
|
],
|
|
admin_url( 'admin.php?page=aioseo-ai-bulk-generate' )
|
|
);
|
|
}
|
|
} |