Files
laipower/wp-content/plugins/activitypub/integration/class-classic-editor.php

306 lines
12 KiB
PHP

<?php
/**
* Classic Editor integration file.
*
* @package Activitypub
*/
namespace Activitypub\Integration;
/**
* Classic Editor integration class.
*
* Handles compatibility with the Classic Editor plugin and sites without block editor support.
*/
class Classic_Editor {
/**
* Initialize the class, registering WordPress hooks.
*/
public static function init() {
\add_filter( 'activitypub_attachments_media_markup', array( self::class, 'filter_attachments_media_markup' ), 10, 2 );
\add_filter( 'activitypub_attachment_ids', array( self::class, 'filter_attached_media_ids' ), 10, 2 );
\add_action( 'add_meta_boxes', array( self::class, 'add_meta_box' ) );
\add_action( 'save_post', array( self::class, 'save_meta_data' ) );
if ( \function_exists( 'classicpress_version' ) ) {
\add_filter( 'activitypub_site_supports_blocks', '__return_false' );
}
}
/**
* Filter attachment media markup to use shortcodes instead of blocks.
*
* @param string $markup The custom markup. Empty string by default.
* @param array $attachment_ids Array of attachment IDs.
*
* @return string The generated shortcode markup.
*/
public static function filter_attachments_media_markup( $markup, $attachment_ids ) {
if ( empty( $attachment_ids ) ) {
return $markup;
}
$type = strtok( \get_post_mime_type( $attachment_ids[0] ), '/' );
// Single video or audio file: use media shortcode.
if ( 1 === \count( $attachment_ids ) && ( 'video' === $type || 'audio' === $type ) ) {
return sprintf(
'[%1$s src="%2$s"]',
\esc_attr( $type ),
\esc_url( \wp_get_attachment_url( $attachment_ids[0] ) )
);
}
// Multiple attachments or images: use gallery shortcode.
return '[gallery ids="' . implode( ',', $attachment_ids ) . '" link="none"]';
}
/**
* Filter to add attached media IDs from the post's media library.
*
* Returns additional image attachments from the post's media library,
* respecting the maximum image attachment limit. Only returns new
* attachments that can be added without exceeding the limit.
*
* @param array $attachments The current list of attachments.
* @param \WP_Post $item The post item.
*
* @return array Array of new media IDs to add (as associative arrays with 'id' key).
* Returns empty array if already at or over the limit.
*/
public static function filter_attached_media_ids( $attachments, $item ) {
$max_media = \get_option( 'activitypub_max_image_attachments', \ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS );
$actual_count = \max( 0, $max_media - \count( $attachments ) );
if ( $actual_count <= 0 ) {
return $attachments;
}
$query = new \WP_Query(
array(
'post_parent' => $item->ID,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
'fields' => 'ids',
'posts_per_page' => $actual_count,
)
);
// Transform IDs into associative arrays.
$media_ids = \array_map(
static function ( $id ) {
return array( 'id' => $id );
},
$query->get_posts()
);
return \array_merge( $attachments, $media_ids );
}
/**
* Add ActivityPub meta box to the post editor.
*
* @param string $post_type The post type.
*/
public static function add_meta_box( $post_type ) {
// Only add for post types that support ActivityPub.
if ( ! \post_type_supports( $post_type, 'activitypub' ) ) {
return;
}
\add_meta_box(
'activitypub-settings',
\__( 'Fediverse ⁂', 'activitypub' ),
array( self::class, 'render_meta_box' ),
$post_type,
'side',
'default'
);
}
/**
* Render the ActivityPub meta box.
*
* @param \WP_Post $post The post object.
*/
public static function render_meta_box( $post ) {
// Add nonce for security.
\wp_nonce_field( 'activitypub_meta_box', 'activitypub_meta_box_nonce' );
// Get current values.
$content_warning = \get_post_meta( $post->ID, 'activitypub_content_warning', true );
$max_image_attachments = \get_post_meta( $post->ID, 'activitypub_max_image_attachments', true );
$content_visibility = self::get_default_visibility( $post );
$default_quote_policy = \get_option( 'activitypub_default_quote_policy', ACTIVITYPUB_INTERACTION_POLICY_ANYONE );
$quote_interaction = \get_post_meta( $post->ID, 'activitypub_interaction_policy_quote', true ) ?: $default_quote_policy;
?>
<p>
<label for="activitypub_content_warning">
<strong><?php \esc_html_e( 'Content Warning', 'activitypub' ); ?></strong>
</label><br />
<input type="text" id="activitypub_content_warning" name="activitypub_content_warning" value="<?php echo \esc_attr( $content_warning ); ?>" class="widefat" placeholder="<?php \esc_attr_e( 'Optional content warning', 'activitypub' ); ?>" />
<span class="howto"><?php \esc_html_e( 'Content warnings do not change the content on your site, only in the fediverse.', 'activitypub' ); ?></span>
</p>
<p>
<label for="activitypub_max_image_attachments">
<strong><?php \esc_html_e( 'Maximum Image Attachments', 'activitypub' ); ?></strong>
</label><br />
<input type="number" id="activitypub_max_image_attachments" name="activitypub_max_image_attachments" value="<?php echo \esc_attr( $max_image_attachments ); ?>" min="0" max="10" class="small-text" />
<span class="howto"><?php \esc_html_e( 'Maximum number of image attachments to include when sharing to the fediverse.', 'activitypub' ); ?></span>
</p>
<p>
<strong><?php \esc_html_e( 'Visibility', 'activitypub' ); ?></strong><br />
<label>
<input type="radio" name="activitypub_content_visibility" value="<?php echo \esc_attr( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ); ?>" <?php \checked( $content_visibility, ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ); ?> />
<?php \esc_html_e( 'Public', 'activitypub' ); ?>
</label><br />
<label>
<input type="radio" name="activitypub_content_visibility" value="<?php echo \esc_attr( ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC ); ?>" <?php \checked( $content_visibility, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC ); ?> />
<?php \esc_html_e( 'Quiet public', 'activitypub' ); ?>
</label><br />
<label>
<input type="radio" name="activitypub_content_visibility" value="<?php echo \esc_attr( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ); ?>" <?php \checked( $content_visibility, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ); ?> />
<?php \esc_html_e( 'Do not federate', 'activitypub' ); ?>
</label><br />
<span class="howto"><?php \esc_html_e( 'This adjusts the visibility of a post in the fediverse, but note that it won\'t affect how the post appears on the blog.', 'activitypub' ); ?></span>
</p>
<p>
<label for="activitypub_interaction_policy_quote">
<strong><?php \esc_html_e( 'Who can quote this post?', 'activitypub' ); ?></strong>
</label><br />
<select id="activitypub_interaction_policy_quote" name="activitypub_interaction_policy_quote" class="widefat">
<option value="<?php echo \esc_attr( ACTIVITYPUB_INTERACTION_POLICY_ANYONE ); ?>" <?php \selected( $quote_interaction, ACTIVITYPUB_INTERACTION_POLICY_ANYONE ); ?>><?php \esc_html_e( 'Anyone', 'activitypub' ); ?></option>
<option value="<?php echo \esc_attr( ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS ); ?>" <?php \selected( $quote_interaction, ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS ); ?>><?php \esc_html_e( 'Followers only', 'activitypub' ); ?></option>
<option value="<?php echo \esc_attr( ACTIVITYPUB_INTERACTION_POLICY_ME ); ?>" <?php \selected( $quote_interaction, ACTIVITYPUB_INTERACTION_POLICY_ME ); ?>><?php \esc_html_e( 'Just me', 'activitypub' ); ?></option>
</select>
<span class="howto">
<?php \esc_html_e( 'Quoting allows others to cite your post while adding their own commentary.', 'activitypub' ); ?>
<?php
printf(
/* translators: %s: The current site default quote policy. Note the leading space. */
\esc_html__( ' Site default: %s', 'activitypub' ),
\esc_html( self::get_quote_policy_label( $default_quote_policy ) )
);
?>
</span>
</p>
<?php
}
/**
* Get the label for a quote policy value.
*
* @param string $policy The policy value.
*
* @return string The translated label.
*/
private static function get_quote_policy_label( $policy ) {
$labels = array(
ACTIVITYPUB_INTERACTION_POLICY_ANYONE => \__( 'Anyone', 'activitypub' ),
ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS => \__( 'Followers only', 'activitypub' ),
ACTIVITYPUB_INTERACTION_POLICY_ME => \__( 'Just me', 'activitypub' ),
);
return $labels[ $policy ] ?? $labels[ ACTIVITYPUB_INTERACTION_POLICY_ANYONE ];
}
/**
* Get default visibility based on post age and federation status.
*
* @param \WP_Post $post The post object.
*
* @return string The default visibility value.
*/
private static function get_default_visibility( $post ) {
// If already set, use that value.
$saved_visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true );
if ( $saved_visibility ) {
return $saved_visibility;
}
// If post is federated, use public.
$status = \get_post_meta( $post->ID, 'activitypub_status', true );
if ( ACTIVITYPUB_OBJECT_STATE_FEDERATED === $status ) {
return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
}
// If post is older than 1 month, default to local.
$post_timestamp = \strtotime( $post->post_date );
$one_month_ago = \strtotime( '-30 days' );
if ( $post_timestamp < $one_month_ago ) {
return ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL;
}
// Default to public for new posts.
return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
}
/**
* Save ActivityPub meta data.
*
* @param int $post_id The post ID.
*/
public static function save_meta_data( $post_id ) {
// Check if this is an autosave.
if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// Only process for post types that support ActivityPub.
if ( ! \post_type_supports( \get_post_type( $post_id ), 'activitypub' ) ) {
return;
}
// Check user permissions.
if ( ! \current_user_can( 'edit_post', $post_id ) ) {
return;
}
// Verify nonce is present and valid.
if ( ! isset( $_POST['activitypub_meta_box_nonce'] ) ) {
return;
}
if ( ! \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['activitypub_meta_box_nonce'] ) ), 'activitypub_meta_box' ) ) {
return;
}
// Save content warning.
if ( isset( $_POST['activitypub_content_warning'] ) ) {
$content_warning = \sanitize_text_field( \wp_unslash( $_POST['activitypub_content_warning'] ) );
if ( ! empty( $content_warning ) ) {
\update_post_meta( $post_id, 'activitypub_content_warning', $content_warning );
} else {
\delete_post_meta( $post_id, 'activitypub_content_warning' );
}
}
// Save max image attachments.
if ( isset( $_POST['activitypub_max_image_attachments'] ) ) {
$max_images = \absint( $_POST['activitypub_max_image_attachments'] );
\update_post_meta( $post_id, 'activitypub_max_image_attachments', $max_images );
}
// Save content visibility.
if ( isset( $_POST['activitypub_content_visibility'] ) ) {
$visibility = \sanitize_text_field( \wp_unslash( $_POST['activitypub_content_visibility'] ) );
\update_post_meta( $post_id, 'activitypub_content_visibility', $visibility );
}
// Save quote interaction policy.
if ( isset( $_POST['activitypub_interaction_policy_quote'] ) ) {
$quote_policy = \sanitize_text_field( \wp_unslash( $_POST['activitypub_interaction_policy_quote'] ) );
\update_post_meta( $post_id, 'activitypub_interaction_policy_quote', $quote_policy );
}
}
}