PluginProbe
Stream – Activity Log & Audit Trail / 3.0.0
Stream – Activity Log & Audit Trail v3.0.0
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
stream / classes / class-migrate.php

class-migrate.php in Stream – Activity Log & Audit Trail 3.0.0, at classes/class-migrate.php

354 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WP_Stream;
3
4 class Migrate {
5 /**
6 * Hold Plugin class
7 * @var Plugin
8 */
9 public $plugin;
10
11 /**
12 * Hold site API Key
13 *
14 * @var string
15 */
16 private $api_key;
17
18 /**
19 * Hold site UUID
20 *
21 * @var string
22 */
23 private $site_uuid;
24
25 /**
26 * Hold the total number of legacy records found in the cloud
27 *
28 * @var int
29 */
30 public $record_count;
31
32 /**
33 * Limit payload chunks to a certain number of records
34 *
35 * @var int
36 */
37 public $limit;
38
39 /**
40 * Class constructor.
41 *
42 * @param Plugin $plugin The main Plugin class.
43 */
44 public function __construct( $plugin ) {
45 $this->plugin = $plugin;
46
47 $this->api_key = get_option( 'wp_stream_site_api_key' );
48 $this->site_uuid = get_option( 'wp_stream_site_uuid' );
49
50 // Exit early if disconnected
51 if ( ! $this->is_connected() ) {
52 return;
53 }
54
55 $this->limit = absint( apply_filters( 'wp_stream_migrate_chunk_size', 100 ) );
56
57 $this->record_count = $this->get_record_count();
58
59 // Display admin notice
60 add_action( 'admin_notices', array( $this, 'migrate_notice' ), 9 );
61
62 // AJAX callback for migrate action
63 add_action( 'wp_ajax_wp_stream_migrate_action', array( $this, 'migrate_action_callback' ) );
64 }
65
66 /**
67 * Are we currently connected to WP Stream?
68 *
69 * @return bool
70 */
71 private function is_connected() {
72 return ( ! empty( $this->api_key ) && ! empty( $this->site_uuid ) );
73 }
74
75 /**
76 * Disconnect from WP Stream
77 */
78 private function disconnect() {
79 delete_option( 'wp_stream_site_api_key' );
80 delete_option( 'wp_stream_site_uuid' );
81 delete_option( 'wp_stream_delay_migration' );
82 delete_option( 'wp_stream_site_restricted' );
83
84 $this->api_key = false;
85 $this->site_uuid = false;
86 }
87
88 /**
89 * Search for records
90 *
91 * @param array $query
92 *
93 * @return mixed Response body on success, or FALSE on failure
94 */
95 private function search( $query = array() ) {
96 if ( ! $this->is_connected() ) {
97 return false;
98 }
99
100 $defaults = array(
101 'sort' => array(
102 array(
103 'created' => array(
104 'order' => 'asc',
105 ),
106 ),
107 ),
108 );
109
110 $last = get_option( 'wp_stream_last_migrated' );
111
112 if ( $last ) {
113 $defaults['filter'] = array(
114 'and' => array(
115 array(
116 'range' => array(
117 'created' => array(
118 'gt' => $last,
119 ),
120 ),
121 ),
122 ),
123 );
124 }
125
126 $body['sites'] = array( $this->site_uuid );
127 $body['query'] = array_merge( $defaults, (array) $query );
128
129 $args = array(
130 'headers' => array(
131 'Stream-Site-API-Key' => $this->api_key,
132 'Content-Type' => 'application/json',
133 ),
134 'method' => 'POST',
135 'body' => wp_stream_json_encode( $body ),
136 'sslverify' => true,
137 );
138
139 $response = wp_safe_remote_request( 'https://api.wp-stream.com/search', $args );
140
141 if ( is_wp_error( $response ) ) {
142 return false;
143 }
144
145 return json_decode( wp_remote_retrieve_body( $response ) );
146 }
147
148 /**
149 * Get the total number of records found
150 *
151 * @return int
152 */
153 private function get_record_count() {
154 $response = $this->search( array( 'size' => 0 ) );
155
156 if ( empty( $response->meta->total ) ) {
157 return 0;
158 }
159
160 return absint( $response->meta->total );
161 }
162
163 /**
164 * Get a chunk of records
165 *
166 * @param int $limit (optional)
167 * @param int $offset (optional)
168 *
169 * @return array|bool An array of record arrays, or FALSE if no records were found
170 */
171 private function get_records( $limit = 100, $offset = 0 ) {
172 $limit = is_int( $limit ) ? $limit : $this->limit;
173
174 $query = array(
175 'size' => absint( $limit ),
176 'from' => absint( $offset ),
177 );
178
179 $response = $this->search( $query );
180
181 if ( empty( $response->records ) ) {
182 return false;
183 }
184
185 return $response->records;
186 }
187
188 /**
189 * Determine where and when the migrate notice should be displayed
190 *
191 * @see Admin->admin_enqueue_scripts()
192 *
193 * @return bool
194 */
195 public function show_migrate_notice() {
196 if (
197 ! isset( $_GET['migrate_action'] ) // input var okay
198 &&
199 $this->is_connected()
200 &&
201 ! empty( $this->record_count )
202 &&
203 false === get_transient( 'wp_stream_delay_migration' )
204 ) {
205 return true;
206 }
207
208 return false;
209 }
210
211 /**
212 * Give the user options for how to handle their records
213 *
214 * @action admin_notices
215 */
216 public function migrate_notice() {
217 if ( ! $this->show_migrate_notice() ) {
218 return;
219 }
220
221 $notice = sprintf(
222 '<h3>%s</h3><strong id="stream-migrate-title">%s</strong></p><p id="stream-migrate-blog-link"><a href="https://wp-stream.com/introducing-stream-3/" target="_blank">%s</a></p><p id="stream-migrate-message">%s</p><div id="stream-migrate-progress"><progress value="0" max="100"></progress> <strong>0&#37;</strong> <em></em> <button id="stream-migrate-actions-close" class="button button-secondary">%s</button><div class="clear"></div></div><p id="stream-migrate-actions"><button id="stream-start-migrate" class="button button-primary">%s</button> <button id="stream-migrate-reminder" class="button button-secondary">%s</button> <a href="#" id="stream-ignore-migrate" class="delete">%s</a>',
223 __( 'Stream Records Update' ),
224 __( 'Our cloud storage services will be shutting down permanently on September 1, 2015', 'stream' ),
225 __( 'Read the announcement post', 'stream' ),
226 sprintf( esc_html__( 'We found %s activity records in the cloud that need to be migrated to your local database.', 'stream' ), number_format( $this->record_count ) ),
227 __( 'Close', 'stream' ),
228 __( 'Start Migration Now', 'stream' ),
229 __( 'Remind Me Later', 'stream' ),
230 __( "No thanks, I don't want to migrate", 'stream' )
231 );
232
233 $this->plugin->admin->notice( $notice, true );
234 }
235
236 /**
237 * Ajax callback for processing migrate actions
238 *
239 * Break down the total number of records found into reasonably-sized
240 * chunks and save records from each of those chunks to the local DB.
241 *
242 * Disconnects from WP Stream once the migration is complete.
243 *
244 * @action wp_ajax_wp_stream_migrate_action
245 */
246 public function migrate_action_callback() {
247 $action = wp_stream_filter_input( INPUT_POST, 'migrate_action' );
248 $nonce = wp_stream_filter_input( INPUT_POST, 'nonce' );
249
250 if ( ! wp_verify_nonce( $nonce, 'wp_stream_migrate-' . absint( get_current_blog_id() ) . absint( get_current_user_id() ) ) ) {
251 return;
252 }
253
254 set_time_limit( 0 ); // Just in case, this could take a while for some
255
256 switch ( $action ) {
257 case 'migrate':
258 case 'continue':
259 $this->migrate();
260 break;
261 case 'delay':
262 $this->delay();
263 break;
264 case 'ignore':
265 $this->ignore();
266 break;
267 }
268
269 die();
270 }
271
272 /**
273 * Migrate a chunk of records
274 *
275 * @return string JSON data
276 */
277 private function migrate() {
278 $records = $this->get_records( $this->limit );
279
280 // Disconnect when there are no records left
281 if ( ! $records ) {
282 $this->disconnect();
283
284 wp_send_json_success( esc_html__( 'Migration complete!', 'stream' ) );
285 }
286
287 $records_saved = $this->save_records( $records );
288
289 if ( true !== $records_saved ) {
290 wp_send_json_error( esc_html__( 'An unknown error occurred during migration. Please try again later or contact support.', 'stream' ) );
291 }
292
293 wp_send_json_success( 'continue' );
294 }
295
296 /**
297 * Delay the migration of records for 3 hours
298 *
299 * @return string JSON data
300 */
301 private function delay() {
302 set_transient( 'wp_stream_delay_migration', "Don't nag me, bro", HOUR_IN_SECONDS * 3 );
303
304 wp_send_json_success( esc_html__( "OK, we'll remind you again in a few hours.", 'stream' ) );
305 }
306
307 /**
308 * Don't migrate any records
309 *
310 * @return string JSON data
311 */
312 private function ignore() {
313 $this->disconnect();
314
315 wp_send_json_success( esc_html__( 'All new activity will be stored in the local database.', 'stream' ) );
316 }
317
318 /**
319 * Save records to the database
320 *
321 * @param array $records
322 *
323 * @return bool
324 */
325 private function save_records( $records ) {
326 foreach ( $records as $record ) {
327 // Remove existing meta field
328 unset( $record->meta );
329
330 // Map fields to the newer data model
331 $record->user_id = $record->author;
332 $record->user_role = $record->author_role;
333 $record->meta = $record->stream_meta;
334 $record->meta->user_meta = $record->author_meta;
335
336 // Convert the record object to a record array
337 // @codingStandardsIgnoreStart
338 $record = json_decode( json_encode( $record ), true );
339 // @codingStandardsIgnoreEnd
340
341 // Save the record
342 $inserted = $this->plugin->db->insert( $record );
343
344 // Save the date of the last known migrated record
345 if ( false !== $inserted ) {
346 update_option( 'wp_stream_last_migrated', $record['created'] );
347 }
348 }
349
350 return true;
351 }
352
353 }
354