PluginProbe
ActivityPub / 4.0.2
ActivityPub v4.0.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 / includes / class-migration.php

class-migration.php in ActivityPub 4.0.2, at includes/class-migration.php

411 lines 10.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Migration class file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub;
9
10 use Activitypub\Collection\Users;
11 use Activitypub\Collection\Followers;
12
13 /**
14 * ActivityPub Migration Class
15 *
16 * @author Matthias Pfefferle
17 */
18 class Migration {
19 /**
20 * Initialize the class, registering WordPress hooks.
21 */
22 public static function init() {
23 \add_action( 'activitypub_migrate', array( self::class, 'async_migration' ) );
24
25 self::maybe_migrate();
26 }
27
28 /**
29 * Get the target version.
30 *
31 * This is the version that the database structure will be updated to.
32 * It is the same as the plugin version.
33 *
34 * @return string The target version.
35 */
36 public static function get_target_version() {
37 return get_plugin_version();
38 }
39
40 /**
41 * The current version of the database structure.
42 *
43 * @return string The current version.
44 */
45 public static function get_version() {
46 return get_option( 'activitypub_db_version', 0 );
47 }
48
49 /**
50 * Locks the database migration process to prevent simultaneous migrations.
51 */
52 public static function lock() {
53 \update_option( 'activitypub_migration_lock', \time() );
54 }
55
56 /**
57 * Unlocks the database migration process.
58 */
59 public static function unlock() {
60 \delete_option( 'activitypub_migration_lock' );
61 }
62
63 /**
64 * Whether the database migration process is locked.
65 *
66 * @return boolean
67 */
68 public static function is_locked() {
69 $lock = \get_option( 'activitypub_migration_lock' );
70
71 if ( ! $lock ) {
72 return false;
73 }
74
75 $lock = (int) $lock;
76
77 if ( $lock < \time() - 1800 ) {
78 self::unlock();
79 return false;
80 }
81
82 return true;
83 }
84
85 /**
86 * Whether the database structure is up to date.
87 *
88 * @return bool True if the database structure is up to date, false otherwise.
89 */
90 public static function is_latest_version() {
91 return (bool) \version_compare(
92 self::get_version(),
93 self::get_target_version(),
94 '=='
95 );
96 }
97
98 /**
99 * Updates the database structure if necessary.
100 */
101 public static function maybe_migrate() {
102 if ( self::is_latest_version() ) {
103 return;
104 }
105
106 if ( self::is_locked() ) {
107 return;
108 }
109
110 self::lock();
111
112 $version_from_db = self::get_version();
113
114 // Check for inital migration.
115 if ( ! $version_from_db ) {
116 self::add_default_settings();
117 $version_from_db = self::get_target_version();
118 }
119
120 // Schedule the async migration.
121 if ( ! \wp_next_scheduled( 'activitypub_migrate', $version_from_db ) ) {
122 \wp_schedule_single_event( \time(), 'activitypub_migrate', array( $version_from_db ) );
123 }
124 if ( \version_compare( $version_from_db, '0.17.0', '<' ) ) {
125 self::migrate_from_0_16();
126 }
127 if ( \version_compare( $version_from_db, '1.3.0', '<' ) ) {
128 self::migrate_from_1_2_0();
129 }
130 if ( \version_compare( $version_from_db, '2.1.0', '<' ) ) {
131 self::migrate_from_2_0_0();
132 }
133 if ( \version_compare( $version_from_db, '2.3.0', '<' ) ) {
134 self::migrate_from_2_2_0();
135 }
136 if ( \version_compare( $version_from_db, '3.0.0', '<' ) ) {
137 self::migrate_from_2_6_0();
138 }
139 if ( \version_compare( $version_from_db, '4.0.0', '<' ) ) {
140 self::migrate_to_4_0_0();
141 }
142
143 /**
144 * Fires when the system has to be migrated.
145 *
146 * @param string $version_from_db The version from which to migrate.
147 * @param string $target_version The target version to migrate to.
148 */
149 \do_action( 'activitypub_migrate', $version_from_db, self::get_target_version() );
150
151 \update_option( 'activitypub_db_version', self::get_target_version() );
152
153 self::unlock();
154 }
155
156 /**
157 * Asynchronously migrates the database structure.
158 *
159 * @param string $version_from_db The version from which to migrate.
160 */
161 public static function async_migration( $version_from_db ) {
162 if ( \version_compare( $version_from_db, '1.0.0', '<' ) ) {
163 self::migrate_from_0_17();
164 }
165 }
166
167 /**
168 * Updates the custom template to use shortcodes instead of the deprecated templates.
169 */
170 private static function migrate_from_0_16() {
171 // Get the custom template.
172 $old_content = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
173
174 /*
175 * If the old content exists but is a blank string, we're going to need a flag to updated it even
176 * after setting it to the default contents.
177 */
178 $need_update = false;
179
180 // If the old contents is blank, use the defaults.
181 if ( '' === $old_content ) {
182 $old_content = ACTIVITYPUB_CUSTOM_POST_CONTENT;
183 $need_update = true;
184 }
185
186 // Set the new content to be the old content.
187 $content = $old_content;
188
189 // Convert old templates to shortcodes.
190 $content = \str_replace( '%title%', '[ap_title]', $content );
191 $content = \str_replace( '%excerpt%', '[ap_excerpt]', $content );
192 $content = \str_replace( '%content%', '[ap_content]', $content );
193 $content = \str_replace( '%permalink%', '[ap_permalink type="html"]', $content );
194 $content = \str_replace( '%shortlink%', '[ap_shortlink type="html"]', $content );
195 $content = \str_replace( '%hashtags%', '[ap_hashtags]', $content );
196 $content = \str_replace( '%tags%', '[ap_hashtags]', $content );
197
198 // Store the new template if required.
199 if ( $content !== $old_content || $need_update ) {
200 \update_option( 'activitypub_custom_post_content', $content );
201 }
202 }
203
204 /**
205 * Updates the DB-schema of the followers-list.
206 */
207 public static function migrate_from_0_17() {
208 // Migrate followers.
209 foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
210 $followers = get_user_meta( $user_id, 'activitypub_followers', true );
211
212 if ( $followers ) {
213 foreach ( $followers as $actor ) {
214 Followers::add_follower( $user_id, $actor );
215 }
216 }
217 }
218
219 Activitypub::flush_rewrite_rules();
220 }
221
222 /**
223 * Clear the cache after updating to 1.3.0.
224 */
225 private static function migrate_from_1_2_0() {
226 $user_ids = \get_users(
227 array(
228 'fields' => 'ID',
229 'capability__in' => array( 'publish_posts' ),
230 )
231 );
232
233 foreach ( $user_ids as $user_id ) {
234 wp_cache_delete( sprintf( Followers::CACHE_KEY_INBOXES, $user_id ), 'activitypub' );
235 }
236 }
237
238 /**
239 * Unschedule Hooks after updating to 2.0.0.
240 */
241 private static function migrate_from_2_0_0() {
242 wp_clear_scheduled_hook( 'activitypub_send_post_activity' );
243 wp_clear_scheduled_hook( 'activitypub_send_update_activity' );
244 wp_clear_scheduled_hook( 'activitypub_send_delete_activity' );
245
246 wp_unschedule_hook( 'activitypub_send_post_activity' );
247 wp_unschedule_hook( 'activitypub_send_update_activity' );
248 wp_unschedule_hook( 'activitypub_send_delete_activity' );
249
250 $object_type = \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE );
251 if ( 'article' === $object_type ) {
252 \update_option( 'activitypub_object_type', 'wordpress-post-format' );
253 }
254 }
255
256 /**
257 * Add the ActivityPub capability to all users that can publish posts
258 * Delete old meta to store followers.
259 */
260 private static function migrate_from_2_2_0() {
261 // Add the ActivityPub capability to all users that can publish posts.
262 self::add_activitypub_capability();
263 }
264
265 /**
266 * Rename DB fields.
267 */
268 private static function migrate_from_2_6_0() {
269 wp_cache_flush();
270
271 self::update_usermeta_key( 'activitypub_user_description', 'activitypub_description' );
272
273 self::update_options_key( 'activitypub_blog_user_description', 'activitypub_blog_description' );
274 self::update_options_key( 'activitypub_blog_user_identifier', 'activitypub_blog_identifier' );
275 }
276
277 /**
278 * * Update actor-mode settings.
279 * * Get the ID of the latest blog post and save it to the options table.
280 */
281 private static function migrate_to_4_0_0() {
282 $latest_post_id = 0;
283
284 // Get the ID of the latest blog post and save it to the options table.
285 $latest_post = get_posts(
286 array(
287 'numberposts' => 1,
288 'orderby' => 'ID',
289 'order' => 'DESC',
290 'post_type' => 'any',
291 'post_status' => 'publish',
292 )
293 );
294
295 if ( $latest_post ) {
296 $latest_post_id = $latest_post[0]->ID;
297 }
298
299 \update_option( 'activitypub_last_post_with_permalink_as_id', $latest_post_id );
300
301 $users = \get_users(
302 array(
303 'capability__in' => array( 'activitypub' ),
304 )
305 );
306
307 foreach ( $users as $user ) {
308 $followers = Followers::get_followers( $user->ID );
309
310 if ( $followers ) {
311 \update_user_option( $user->ID, 'activitypub_use_permalink_as_id', '1' );
312 }
313 }
314
315 $followers = Followers::get_followers( Users::BLOG_USER_ID );
316
317 if ( $followers ) {
318 \update_option( 'activitypub_use_permalink_as_id_for_blog', '1' );
319 }
320
321 self::migrate_actor_mode();
322 }
323
324 /**
325 * Set the defaults needed for the plugin to work.
326 *
327 * Add the ActivityPub capability to all users that can publish posts.
328 */
329 public static function add_default_settings() {
330 self::add_activitypub_capability();
331 }
332
333 /**
334 * Add the ActivityPub capability to all users that can publish posts.
335 */
336 private static function add_activitypub_capability() {
337 // Get all WP_User objects that can publish posts.
338 $users = \get_users(
339 array(
340 'capability__in' => array( 'publish_posts' ),
341 )
342 );
343
344 // Add ActivityPub capability to all users that can publish posts.
345 foreach ( $users as $user ) {
346 $user->add_cap( 'activitypub' );
347 }
348 }
349
350 /**
351 * Rename meta keys.
352 *
353 * @param string $old_key The old comment meta key.
354 * @param string $new_key The new comment meta key.
355 */
356 private static function update_usermeta_key( $old_key, $new_key ) {
357 global $wpdb;
358
359 $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
360 $wpdb->usermeta,
361 array( 'meta_key' => $new_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
362 array( 'meta_key' => $old_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
363 array( '%s' ),
364 array( '%s' )
365 );
366 }
367
368 /**
369 * Rename option keys.
370 *
371 * @param string $old_key The old option key.
372 * @param string $new_key The new option key.
373 */
374 private static function update_options_key( $old_key, $new_key ) {
375 global $wpdb;
376
377 $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
378 $wpdb->options,
379 array( 'option_name' => $new_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
380 array( 'option_name' => $old_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
381 array( '%s' ),
382 array( '%s' )
383 );
384 }
385
386 /**
387 * Migrate the actor mode settings.
388 */
389 public static function migrate_actor_mode() {
390 $blog_profile = \get_option( 'activitypub_enable_blog_user', '0' );
391 $author_profiles = \get_option( 'activitypub_enable_users', '1' );
392
393 if (
394 '1' === $blog_profile &&
395 '1' === $author_profiles
396 ) {
397 \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE );
398 } elseif (
399 '1' === $blog_profile &&
400 '1' !== $author_profiles
401 ) {
402 \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE );
403 } elseif (
404 '1' !== $blog_profile &&
405 '1' === $author_profiles
406 ) {
407 \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE );
408 }
409 }
410 }
411