PluginProbe
ActivityPub / 7.8.2
ActivityPub v7.8.2
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.2, at integration/class-classic-editor.php

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