PluginProbe
ActivityPub / 7.8.4
ActivityPub v7.8.4
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
activitypub / integration / class-classic-editor.php

class-classic-editor.php in ActivityPub 7.8.4, at integration/class-classic-editor.php

276 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Classic Editor integration file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Integration;
9
10 /**
11 * Classic Editor integration class.
12 *
13 * Handles compatibility with the Classic Editor plugin and sites without block editor support.
14 */
15 class Classic_Editor {
16
17 /**
18 * Initialize the class, registering WordPress hooks.
19 */
20 public static function init() {
21 \add_filter( 'activitypub_attachments_media_markup', array( self::class, 'filter_attachments_media_markup' ), 10, 2 );
22 \add_filter( 'activitypub_attachment_ids', array( self::class, 'filter_attached_media_ids' ), 10, 2 );
23 \add_filter( 'activitypub_site_supports_blocks', '__return_false' );
24 \add_action( 'add_meta_boxes', array( self::class, 'add_meta_box' ) );
25 \add_action( 'save_post', array( self::class, 'save_meta_data' ) );
26 }
27
28 /**
29 * Filter attachment media markup to use shortcodes instead of blocks.
30 *
31 * @param string $markup The custom markup. Empty string by default.
32 * @param array $attachment_ids Array of attachment IDs.
33 *
34 * @return string The generated shortcode markup.
35 */
36 public static function filter_attachments_media_markup( $markup, $attachment_ids ) {
37 if ( empty( $attachment_ids ) ) {
38 return $markup;
39 }
40
41 $type = strtok( \get_post_mime_type( $attachment_ids[0] ), '/' );
42
43 // Single video or audio file: use media shortcode.
44 if ( 1 === \count( $attachment_ids ) && ( 'video' === $type || 'audio' === $type ) ) {
45 return sprintf(
46 '[%1$s src="%2$s"]',
47 \esc_attr( $type ),
48 \esc_url( \wp_get_attachment_url( $attachment_ids[0] ) )
49 );
50 }
51
52 // Multiple attachments or images: use gallery shortcode.
53 return '[gallery ids="' . implode( ',', $attachment_ids ) . '" link="none"]';
54 }
55
56 /**
57 * Filter to add attached media IDs from the post's media library.
58 *
59 * Returns additional image attachments from the post's media library,
60 * respecting the maximum image attachment limit. Only returns new
61 * attachments that can be added without exceeding the limit.
62 *
63 * @param array $attachments The current list of attachments.
64 * @param \WP_Post $item The post item.
65 *
66 * @return array Array of new media IDs to add (as associative arrays with 'id' key).
67 * Returns empty array if already at or over the limit.
68 */
69 public static function filter_attached_media_ids( $attachments, $item ) {
70 $max_media = \get_option( 'activitypub_max_image_attachments', \ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS );
71 $actual_count = \max( 0, $max_media - \count( $attachments ) );
72
73 if ( $actual_count <= 0 ) {
74 return $attachments;
75 }
76
77 $query = new \WP_Query(
78 array(
79 'post_parent' => $item->ID,
80 'post_status' => 'inherit',
81 'post_type' => 'attachment',
82 'post_mime_type' => 'image',
83 'order' => 'ASC',
84 'orderby' => 'menu_order ID',
85 'fields' => 'ids',
86 'posts_per_page' => $actual_count,
87 )
88 );
89
90 // Transform IDs into associative arrays.
91 $media_ids = \array_map(
92 static function ( $id ) {
93 return array( 'id' => $id );
94 },
95 $query->get_posts()
96 );
97
98 return \array_merge( $attachments, $media_ids );
99 }
100
101 /**
102 * Add ActivityPub meta box to the post editor.
103 *
104 * @param string $post_type The post type.
105 */
106 public static function add_meta_box( $post_type ) {
107 // Only add for post types that support ActivityPub.
108 if ( ! \post_type_supports( $post_type, 'activitypub' ) ) {
109 return;
110 }
111
112 \add_meta_box(
113 'activitypub-settings',
114 \__( 'Fediverse ⁂', 'activitypub' ),
115 array( self::class, 'render_meta_box' ),
116 $post_type,
117 'side',
118 'default'
119 );
120 }
121
122 /**
123 * Render the ActivityPub meta box.
124 *
125 * @param \WP_Post $post The post object.
126 */
127 public static function render_meta_box( $post ) {
128 // Add nonce for security.
129 \wp_nonce_field( 'activitypub_meta_box', 'activitypub_meta_box_nonce' );
130
131 // Get current values.
132 $content_warning = \get_post_meta( $post->ID, 'activitypub_content_warning', true );
133 $max_image_attachments = \get_post_meta( $post->ID, 'activitypub_max_image_attachments', true );
134 $content_visibility = self::get_default_visibility( $post );
135 $quote_interaction = \get_post_meta( $post->ID, 'activitypub_interaction_policy_quote', true ) ?: ACTIVITYPUB_INTERACTION_POLICY_ANYONE;
136
137 ?>
138 <p>
139 <label for="activitypub_content_warning">
140 <strong><?php \esc_html_e( 'Content Warning', 'activitypub' ); ?></strong>
141 </label><br />
142 <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' ); ?>" />
143 <span class="howto"><?php \esc_html_e( 'Content warnings do not change the content on your site, only in the fediverse.', 'activitypub' ); ?></span>
144 </p>
145
146 <p>
147 <label for="activitypub_max_image_attachments">
148 <strong><?php \esc_html_e( 'Maximum Image Attachments', 'activitypub' ); ?></strong>
149 </label><br />
150 <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" />
151 <span class="howto"><?php \esc_html_e( 'Maximum number of image attachments to include when sharing to the fediverse.', 'activitypub' ); ?></span>
152 </p>
153
154 <p>
155 <strong><?php \esc_html_e( 'Visibility', 'activitypub' ); ?></strong><br />
156 <label>
157 <input type="radio" name="activitypub_content_visibility" value="<?php echo \esc_attr( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ); ?>" <?php \checked( $content_visibility, ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ); ?> />
158 <?php \esc_html_e( 'Public', 'activitypub' ); ?>
159 </label><br />
160 <label>
161 <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 ); ?> />
162 <?php \esc_html_e( 'Quiet public', 'activitypub' ); ?>
163 </label><br />
164 <label>
165 <input type="radio" name="activitypub_content_visibility" value="<?php echo \esc_attr( ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ); ?>" <?php \checked( $content_visibility, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ); ?> />
166 <?php \esc_html_e( 'Do not federate', 'activitypub' ); ?>
167 </label><br />
168 <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>
169 </p>
170
171 <p>
172 <label for="activitypub_interaction_policy_quote">
173 <strong><?php \esc_html_e( 'Who can quote this post?', 'activitypub' ); ?></strong>
174 </label><br />
175 <select id="activitypub_interaction_policy_quote" name="activitypub_interaction_policy_quote" class="widefat">
176 <option value="anyone" <?php \selected( $quote_interaction, ACTIVITYPUB_INTERACTION_POLICY_ANYONE ); ?>><?php \esc_html_e( 'Anyone', 'activitypub' ); ?></option>
177 <option value="followers" <?php \selected( $quote_interaction, ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS ); ?>><?php \esc_html_e( 'Followers only', 'activitypub' ); ?></option>
178 <option value="me" <?php \selected( $quote_interaction, ACTIVITYPUB_INTERACTION_POLICY_ME ); ?>><?php \esc_html_e( 'Just me', 'activitypub' ); ?></option>
179 </select>
180 <span class="howto"><?php \esc_html_e( 'Quoting allows others to cite your post while adding their own commentary.', 'activitypub' ); ?></span>
181 </p>
182 <?php
183 }
184
185 /**
186 * Get default visibility based on post age and federation status.
187 *
188 * @param \WP_Post $post The post object.
189 *
190 * @return string The default visibility value.
191 */
192 private static function get_default_visibility( $post ) {
193 // If already set, use that value.
194 $saved_visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true );
195 if ( $saved_visibility ) {
196 return $saved_visibility;
197 }
198
199 // If post is federated, use public.
200 $status = \get_post_meta( $post->ID, 'activitypub_status', true );
201 if ( 'federated' === $status ) {
202 return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
203 }
204
205 // If post is older than 1 month, default to local.
206 $post_timestamp = \strtotime( $post->post_date );
207 $one_month_ago = \strtotime( '-30 days' );
208
209 if ( $post_timestamp < $one_month_ago ) {
210 return ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL;
211 }
212
213 // Default to public for new posts.
214 return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
215 }
216
217 /**
218 * Save ActivityPub meta data.
219 *
220 * @param int $post_id The post ID.
221 */
222 public static function save_meta_data( $post_id ) {
223 // Check if this is an autosave.
224 if ( \defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
225 return;
226 }
227
228 // Only process for post types that support ActivityPub.
229 if ( ! \post_type_supports( \get_post_type( $post_id ), 'activitypub' ) ) {
230 return;
231 }
232
233 // Check user permissions.
234 if ( ! \current_user_can( 'edit_post', $post_id ) ) {
235 return;
236 }
237
238 // Verify nonce is present and valid.
239 if ( ! isset( $_POST['activitypub_meta_box_nonce'] ) ) {
240 return;
241 }
242
243 if ( ! \wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_POST['activitypub_meta_box_nonce'] ) ), 'activitypub_meta_box' ) ) {
244 return;
245 }
246
247 // Save content warning.
248 if ( isset( $_POST['activitypub_content_warning'] ) ) {
249 $content_warning = \sanitize_text_field( \wp_unslash( $_POST['activitypub_content_warning'] ) );
250 if ( ! empty( $content_warning ) ) {
251 \update_post_meta( $post_id, 'activitypub_content_warning', $content_warning );
252 } else {
253 \delete_post_meta( $post_id, 'activitypub_content_warning' );
254 }
255 }
256
257 // Save max image attachments.
258 if ( isset( $_POST['activitypub_max_image_attachments'] ) ) {
259 $max_images = \absint( $_POST['activitypub_max_image_attachments'] );
260 \update_post_meta( $post_id, 'activitypub_max_image_attachments', $max_images );
261 }
262
263 // Save content visibility.
264 if ( isset( $_POST['activitypub_content_visibility'] ) ) {
265 $visibility = \sanitize_text_field( \wp_unslash( $_POST['activitypub_content_visibility'] ) );
266 \update_post_meta( $post_id, 'activitypub_content_visibility', $visibility );
267 }
268
269 // Save quote interaction policy.
270 if ( isset( $_POST['activitypub_interaction_policy_quote'] ) ) {
271 $quote_policy = \sanitize_text_field( \wp_unslash( $_POST['activitypub_interaction_policy_quote'] ) );
272 \update_post_meta( $post_id, 'activitypub_interaction_policy_quote', $quote_policy );
273 }
274 }
275 }
276