PluginProbe
Forumax – AI Powered Advanced Community Forum Plugin / 2.4.2
Forumax – AI Powered Advanced Community Forum Plugin v2.4.2
2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 trunk 1.0.8 1.1.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 2.0.0 2.1.0 All 29 releases
bbp-core / lib / bbpress / includes / admin / classes / class-bbp-converter-base.php

class-bbp-converter-base.php in Forumax – AI Powered Advanced Community Forum Plugin 2.4.2, at lib/bbpress/includes/admin/classes/class-bbp-converter-base.php

1,244 lines 38.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Converter Base Class
5 *
6 * Based on the hard work of Adam Ellis
7 *
8 * @package bbPress
9 * @subpackage Administration
10 */
11
12 // Exit if accessed directly
13 defined( 'ABSPATH' ) || exit;
14
15 if ( ! class_exists( 'BBP_Converter_Base' ) ) :
16 /**
17 * Base class to be extended by specific individual importers
18 *
19 * @since 2.1.0 bbPress (r3813)
20 */
21 abstract class BBP_Converter_Base {
22
23 /**
24 * @var array() This is the field mapping array to process.
25 */
26 protected $field_map = array();
27
28 /**
29 * @var object This is the connection to the WordPress database.
30 */
31 protected $wpdb;
32
33 /**
34 * @var object This is the connection to the other platforms database.
35 */
36 protected $opdb;
37
38 /**
39 * @var int Maximum number of rows to convert at 1 time. Default 100.
40 */
41 protected $max_rows = 100;
42
43 /**
44 * @var array() Map of topic to forum. It is for optimization.
45 */
46 protected $map_topicid_to_forumid = array();
47
48 /**
49 * @var array() Map of from old forum ids to new forum ids. It is for optimization.
50 */
51 protected $map_forumid = array();
52
53 /**
54 * @var array() Map of from old topic ids to new topic ids. It is for optimization.
55 */
56 protected $map_topicid = array();
57
58 /**
59 * @var array() Map of from old reply_to ids to new reply_to ids. It is for optimization.
60 */
61 protected $map_reply_to = array();
62
63 /**
64 * @var array() Map of from old user ids to new user ids. It is for optimization.
65 */
66 protected $map_userid = array();
67
68 /**
69 * @var str This is the charset for your wp database.
70 */
71 public $charset = '';
72
73 /**
74 * @var boolean Sync table available.
75 */
76 public $sync_table = false;
77
78 /**
79 * @var str Sync table name.
80 */
81 public $sync_table_name = '';
82
83 /**
84 * @var bool Whether users should be converted or not. Default false.
85 */
86 public $convert_users = false;
87
88 /**
89 * @var bool Whether to clean up any old converter data. Default false.
90 */
91 public $clean = false;
92
93 /**
94 * @var array Custom BBCode class properties in a key => value format
95 */
96 public $bbcode_parser_properties = array();
97
98 /** Methods ***************************************************************/
99
100 /**
101 * This is the constructor and it connects to the platform databases.
102 */
103 public function __construct() {
104 $this->init();
105 $this->setup_globals();
106 }
107
108 /**
109 * Initialize the converter
110 *
111 * @since 2.1.0
112 */
113 private function init() {
114
115 /** BBCode Parse Properties *******************************************/
116
117 // Setup smiley URL & path
118 $this->bbcode_parser_properties = array(
119 'smiley_url' => includes_url( 'images/smilies' ),
120 'smiley_dir' => '/' . WPINC . '/images/smilies'
121 );
122
123 /** Sanitize Options **************************************************/
124
125 $this->clean = ! empty( $_POST['_bbp_converter_clean'] );
126 $this->convert_users = (bool) get_option( '_bbp_converter_convert_users', false );
127 $this->halt = (bool) get_option( '_bbp_converter_halt', 0 );
128 $this->max_rows = (int) get_option( '_bbp_converter_rows', 100 );
129
130 /** Sanitize Connection ***********************************************/
131
132 $db_user = get_option( '_bbp_converter_db_user', DB_USER );
133 $db_pass = get_option( '_bbp_converter_db_pass', DB_PASSWORD );
134 $db_name = get_option( '_bbp_converter_db_name', DB_NAME );
135 $db_host = get_option( '_bbp_converter_db_server', DB_HOST );
136 $db_port = get_option( '_bbp_converter_db_port', '' );
137 $db_prefix = get_option( '_bbp_converter_db_prefix', '' );
138
139 // Maybe add port to server
140 if ( ! empty( $db_port ) && ! empty( $db_host ) && ! strstr( $db_host, ':' ) ) {
141 $db_host = "{$db_host}:{$db_port}";
142 }
143
144 /** Get database connections ******************************************/
145
146 // Setup WordPress Database
147 $this->wpdb = bbp_db();
148
149 // Setup old forum Database
150 $this->opdb = new BBP_Converter_DB( $db_user, $db_pass, $db_name, $db_host );
151
152 // Connection failed
153 if ( ! $this->opdb->db_connect( false ) ) {
154 $error = new WP_Error( 'bbp_converter_db_connection_failed', esc_html__( 'Database connection failed.', 'bbpress' ) );
155 wp_send_json_error( $error );
156 }
157
158 // Maybe setup the database prefix
159 $this->opdb->prefix = $db_prefix;
160
161 /**
162 * Don't wp_die() uncontrollably
163 */
164 $this->wpdb->show_errors( false );
165 $this->opdb->show_errors( false );
166
167 /**
168 * Syncing
169 */
170 $this->sync_table_name = $this->wpdb->prefix . 'bbp_converter_translator';
171 $this->sync_table = $this->sync_table_name === $this->wpdb->get_var( "SHOW TABLES LIKE '{$this->sync_table_name}'" )
172 ? true
173 : false;
174
175 /**
176 * Character set
177 */
178 $this->charset = ! empty( $this->wpdb->charset )
179 ? $this->wpdb->charset
180 : 'UTF8';
181
182 /**
183 * Default mapping.
184 */
185
186 /** Forum Section *****************************************************/
187
188 $this->field_map[] = array(
189 'to_type' => 'forum',
190 'to_fieldname' => 'post_status',
191 'default' => 'publish'
192 );
193 $this->field_map[] = array(
194 'to_type' => 'forum',
195 'to_fieldname' => 'comment_status',
196 'default' => 'closed'
197 );
198 $this->field_map[] = array(
199 'to_type' => 'forum',
200 'to_fieldname' => 'ping_status',
201 'default' => 'closed'
202 );
203 $this->field_map[] = array(
204 'to_type' => 'forum',
205 'to_fieldname' => 'post_type',
206 'default' => 'forum'
207 );
208
209 /** Topic Section *****************************************************/
210
211 $this->field_map[] = array(
212 'to_type' => 'topic',
213 'to_fieldname' => 'post_status',
214 'default' => 'publish'
215 );
216 $this->field_map[] = array(
217 'to_type' => 'topic',
218 'to_fieldname' => 'comment_status',
219 'default' => 'closed'
220 );
221 $this->field_map[] = array(
222 'to_type' => 'topic',
223 'to_fieldname' => 'ping_status',
224 'default' => 'closed'
225 );
226 $this->field_map[] = array(
227 'to_type' => 'topic',
228 'to_fieldname' => 'post_type',
229 'default' => 'topic'
230 );
231
232 /** Post Section ******************************************************/
233
234 $this->field_map[] = array(
235 'to_type' => 'reply',
236 'to_fieldname' => 'post_status',
237 'default' => 'publish'
238 );
239 $this->field_map[] = array(
240 'to_type' => 'reply',
241 'to_fieldname' => 'comment_status',
242 'default' => 'closed'
243 );
244 $this->field_map[] = array(
245 'to_type' => 'reply',
246 'to_fieldname' => 'ping_status',
247 'default' => 'closed'
248 );
249 $this->field_map[] = array(
250 'to_type' => 'reply',
251 'to_fieldname' => 'post_type',
252 'default' => 'reply'
253 );
254
255 /** User Section ******************************************************/
256
257 $this->field_map[] = array(
258 'to_type' => 'user',
259 'to_fieldname' => 'role',
260 'default' => get_option( 'default_role' )
261 );
262 }
263
264 /**
265 * Setup global values
266 */
267 public function setup_globals() {}
268
269 /**
270 * Convert Forums
271 */
272 public function convert_forums( $start = 1 ) {
273 return $this->convert_table( 'forum', $start );
274 }
275
276 /**
277 * Convert Topics / Threads
278 */
279 public function convert_topics( $start = 1 ) {
280 return $this->convert_table( 'topic', $start );
281 }
282
283 /**
284 * Convert Posts
285 */
286 public function convert_replies( $start = 1 ) {
287 return $this->convert_table( 'reply', $start );
288 }
289
290 /**
291 * Convert Users
292 */
293 public function convert_users( $start = 1 ) {
294 return $this->convert_table( 'user', $start );
295 }
296
297 /**
298 * Convert Topic Tags
299 */
300 public function convert_tags( $start = 1 ) {
301 return $this->convert_table( 'tags', $start );
302 }
303
304 /**
305 * Convert Forum Subscriptions
306 */
307 public function convert_forum_subscriptions( $start = 1 ) {
308 return $this->convert_table( 'forum_subscriptions', $start );
309 }
310
311 /**
312 * Convert Topic Subscriptions
313 */
314 public function convert_topic_subscriptions( $start = 1 ) {
315 return $this->convert_table( 'topic_subscriptions', $start );
316 }
317
318 /**
319 * Convert Favorites
320 */
321 public function convert_favorites( $start = 1 ) {
322 return $this->convert_table( 'favorites', $start );
323 }
324
325 /**
326 * Convert Table
327 *
328 * @param string to type
329 * @param int Start row
330 */
331 public function convert_table( $to_type, $start ) {
332
333 // Set some defaults
334 $has_insert = false;
335 $from_tablename = '';
336 $field_list = $from_tables = $tablefield_array = array();
337
338 // Toggle Table Name based on $to_type (destination)
339 switch ( $to_type ) {
340 case 'user' :
341 $tablename = $this->wpdb->users;
342 break;
343
344 case 'tags' :
345 $tablename = '';
346 break;
347
348 case 'forum_subscriptions' :
349 $tablename = $this->wpdb->postmeta;
350 break;
351
352 case 'topic_subscriptions' :
353 $tablename = $this->wpdb->postmeta;
354 break;
355
356 case 'favorites' :
357 $tablename = $this->wpdb->postmeta;
358 break;
359
360 default :
361 $tablename = $this->wpdb->posts;
362 }
363
364 // Get the fields from the destination table
365 if ( ! empty( $tablename ) ) {
366 $tablefield_array = $this->get_fields( $tablename );
367 }
368
369 /** Step 1 ************************************************************/
370
371 // Loop through the field maps, and look for to_type matches
372 foreach ( $this->field_map as $item ) {
373
374 // Yay a match, and we have a from table, too
375 if ( ( $item['to_type'] === $to_type ) && ! empty( $item['from_tablename'] ) ) {
376
377 // $from_tablename was set from a previous loop iteration
378 if ( ! empty( $from_tablename ) ) {
379
380 // Doing some joining
381 if ( ! in_array( $item['from_tablename'], $from_tables, true ) && in_array( $item['join_tablename'], $from_tables, true ) ) {
382 $from_tablename .= ' ' . $item['join_type'] . ' JOIN ' . $this->opdb->prefix . $item['from_tablename'] . ' AS ' . $item['from_tablename'] . ' ' . $item['join_expression'];
383 }
384
385 // $from_tablename needs to be set
386 } else {
387 $from_tablename = $item['from_tablename'] . ' AS ' . $item['from_tablename'];
388 }
389
390 // Specific FROM expression data used
391 if ( ! empty( $item['from_expression'] ) ) {
392
393 // No 'WHERE' in expression
394 if ( stripos( $from_tablename, "WHERE" ) === false ) {
395 $from_tablename .= ' ' . $item['from_expression'];
396
397 // 'WHERE' in expression, so replace with 'AND'
398 } else {
399 $from_tablename .= ' ' . str_replace( "WHERE", "AND", $item['from_expression'] );
400 }
401 }
402
403 // Add tablename and fieldname to arrays, formatted for querying
404 $from_tables[] = $item['from_tablename'];
405 $field_list[] = 'convert(' . $item['from_tablename'] . '.' . $item['from_fieldname'] . ' USING "' . $this->charset . '") AS ' . $item['from_fieldname'];
406 }
407 }
408
409 /** Step 2 ************************************************************/
410
411 // We have a $from_tablename, so we want to get some data to convert
412 if ( ! empty( $from_tablename ) ) {
413
414 // Update rows
415 $this->count_rows_by_table( "{$this->opdb->prefix}{$from_tablename}" );
416
417 // Get some data from the old forums
418 $field_list = array_unique( $field_list );
419 $fields = implode( ',', $field_list );
420 $forum_query = "SELECT {$fields} FROM {$this->opdb->prefix}{$from_tablename} LIMIT {$start}, {$this->max_rows}";
421
422 // Set this query as the last one ran
423 $this->update_query( $forum_query );
424
425 // Get results as an array
426 $forum_array = $this->opdb->get_results( $forum_query, ARRAY_A );
427
428 // Query returned some results
429 if ( ! empty( $forum_array ) ) {
430
431 // Loop through results
432 foreach ( (array) $forum_array as $forum ) {
433
434 // Reset some defaults
435 $insert_post = $insert_postmeta = $insert_data = array();
436
437 // Loop through field map, again...
438 foreach ( $this->field_map as $row ) {
439
440 // Types match and to_fieldname is present. This means
441 // we have some work to do here.
442 if ( ( $row['to_type'] === $to_type ) && isset( $row['to_fieldname'] ) ) {
443
444 // This row has a destination that matches one of the
445 // columns in this table.
446 if ( in_array( $row['to_fieldname'], $tablefield_array, true ) ) {
447
448 // Allows us to set default fields.
449 if ( isset( $row['default'] ) ) {
450 $insert_post[ $row['to_fieldname'] ] = $row['default'];
451
452 // Translates a field from the old forum.
453 } elseif ( isset( $row['callback_method'] ) ) {
454 if ( ( 'callback_userid' === $row['callback_method'] ) && ( false === $this->convert_users ) ) {
455 $insert_post[ $row['to_fieldname'] ] = $forum[ $row['from_fieldname'] ];
456 } else {
457 $insert_post[ $row['to_fieldname'] ] = call_user_func_array( array( $this, $row['callback_method'] ), array( $forum[ $row['from_fieldname'] ], $forum ) );
458 }
459
460 // Maps the field from the old forum.
461 } else {
462 $insert_post[ $row['to_fieldname'] ] = $forum[ $row['from_fieldname'] ];
463 }
464
465 // Destination field is not empty, so we might need
466 // to do some extra work or set a default.
467 } elseif ( ! empty( $row['to_fieldname'] ) ) {
468
469 // Allows us to set default fields.
470 if ( isset( $row['default'] ) ) {
471 $insert_postmeta[ $row['to_fieldname'] ] = $row['default'];
472
473 // Translates a field from the old forum.
474 } elseif ( isset( $row['callback_method'] ) ) {
475 if ( ( $row['callback_method'] === 'callback_userid' ) && ( false === $this->convert_users ) ) {
476 $insert_postmeta[ $row['to_fieldname'] ] = $forum[ $row['from_fieldname'] ];
477 } else {
478 $insert_postmeta[ $row['to_fieldname'] ] = call_user_func_array( array( $this, $row['callback_method'] ), array( $forum[ $row['from_fieldname'] ], $forum ) );
479 }
480
481 // Maps the field from the old forum.
482 } else {
483 $insert_postmeta[ $row['to_fieldname'] ] = $forum[ $row['from_fieldname'] ];
484 }
485 }
486 }
487 }
488
489 /** Step 3 ************************************************/
490
491 // Something to insert into the destination field
492 if ( count( $insert_post ) > 0 || ( $to_type == 'tags' && count( $insert_postmeta ) > 0 ) ) {
493
494 switch ( $to_type ) {
495
496 /** New user **************************************/
497
498 case 'user' :
499 if ( username_exists( $insert_post['user_login'] ) ) {
500 $insert_post['user_login'] = "imported_{$insert_post['user_login']}";
501 }
502
503 if ( email_exists( $insert_post['user_email'] ) ) {
504 $insert_post['user_email'] = "imported_{$insert_post['user_email']}";
505 }
506
507 if ( empty( $insert_post['user_pass'] ) ) {
508 $insert_post['user_pass'] = '';
509 }
510
511 // Internally re-calls _exists() checks above.
512 // Also checks for existing nicename.
513 $post_id = wp_insert_user( $insert_post );
514
515 if ( is_numeric( $post_id ) ) {
516 foreach ( $insert_postmeta as $key => $value ) {
517 add_user_meta( $post_id, $key, $value, true );
518
519 if ( '_id' == substr( $key, -3 ) && ( true === $this->sync_table ) ) {
520 $this->wpdb->insert( $this->sync_table_name, array(
521 'value_type' => 'user',
522 'value_id' => $post_id,
523 'meta_key' => $key,
524 'meta_value' => $value
525 ) );
526 }
527 }
528 }
529 break;
530
531 /** New Topic-Tag *********************************/
532
533 case 'tags' :
534 $post_id = wp_set_object_terms( $insert_postmeta['objectid'], $insert_postmeta['name'], 'topic-tag', true );
535 $term = get_term_by( 'name', $insert_postmeta['name'], 'topic-tag');
536 if ( false !== $term ) {
537 wp_update_term( $term->term_id, 'topic-tag', array(
538 'description' => $insert_postmeta['description'],
539 'slug' => $insert_postmeta['slug']
540 ) );
541 }
542 break;
543
544 /** Forum Subscriptions ***************************/
545
546 case 'forum_subscriptions' :
547 $user_id = $insert_post['user_id'];
548 $items = wp_list_pluck( $insert_postmeta, '_bbp_forum_subscriptions' );
549 if ( is_numeric( $user_id ) && ! empty( $items ) ) {
550 foreach ( $items as $value ) {
551
552 // Maybe string with commas
553 $value = is_string( $value )
554 ? explode( ',', $value )
555 : (array) $value;
556
557 // Add user ID to forums subscribed users
558 foreach ( $value as $fav ) {
559 bbp_add_user_subscription( $user_id, $this->callback_forumid( $fav ) );
560 }
561 }
562 }
563 break;
564
565 /** Subscriptions *********************************/
566
567 case 'topic_subscriptions' :
568 $user_id = $insert_post['user_id'];
569 $items = wp_list_pluck( $insert_postmeta, '_bbp_subscriptions' );
570 if ( is_numeric( $user_id ) && ! empty( $items ) ) {
571 foreach ( $items as $value ) {
572
573 // Maybe string with commas
574 $value = is_string( $value )
575 ? explode( ',', $value )
576 : (array) $value;
577
578 // Add user ID to topics subscribed users
579 foreach ( $value as $fav ) {
580 bbp_add_user_subscription( $user_id, $this->callback_topicid( $fav ) );
581 }
582 }
583 }
584 break;
585
586 /** Favorites *************************************/
587
588 case 'favorites' :
589 $user_id = $insert_post['user_id'];
590 $items = wp_list_pluck( $insert_postmeta, '_bbp_favorites' );
591 if ( is_numeric( $user_id ) && ! empty( $items ) ) {
592 foreach ( $items as $value ) {
593
594 // Maybe string with commas
595 $value = is_string( $value )
596 ? explode( ',', $value )
597 : (array) $value;
598
599 // Add user ID to topics favorited users
600 foreach ( $value as $fav ) {
601 bbp_add_user_favorite( $user_id, $this->callback_topicid( $fav ) );
602 }
603 }
604 }
605 break;
606
607 /** Forum, Topic, Reply ***************************/
608
609 default :
610 $post_id = wp_insert_post( $insert_post, true );
611
612 if ( is_numeric( $post_id ) ) {
613 foreach ( $insert_postmeta as $key => $value ) {
614 add_post_meta( $post_id, $key, $value, true );
615
616 /**
617 * If we are using the sync_table add
618 * the meta '_id' keys to the table
619 *
620 * Forums: _bbp_old_forum_id // The old forum ID
621 * _bbp_old_forum_parent_id // The old forum parent ID
622 *
623 * Topics: _bbp_forum_id // The new forum ID
624 * _bbp_old_topic_id // The old topic ID
625 * _bbp_old_closed_status_id // The old topic open/closed status
626 * _bbp_old_sticky_status_id // The old topic sticky status
627 *
628 * Replies: _bbp_forum_id // The new forum ID
629 * _bbp_topic_id // The new topic ID
630 * _bbp_old_reply_id // The old reply ID
631 * _bbp_old_reply_to_id // The old reply to ID
632 */
633 if ( '_id' === substr( $key, -3 ) && ( true === $this->sync_table ) ) {
634 $this->wpdb->insert( $this->sync_table_name, array(
635 'value_type' => 'post',
636 'value_id' => $post_id,
637 'meta_key' => $key,
638 'meta_value' => $value
639 ) );
640 }
641
642 /**
643 * Replies need to save their old reply_to ID for
644 * hierarchical replies association. Later we update
645 * the _bbp_reply_to value with the new bbPress
646 * value using convert_reply_to_parents()
647 */
648 if ( ( 'reply' === $to_type ) && ( '_bbp_old_reply_to_id' === $key ) ) {
649 add_post_meta( $post_id, '_bbp_reply_to', $value );
650 }
651 }
652 }
653 break;
654 }
655 $has_insert = true;
656 }
657 }
658 }
659 }
660
661 return ! $has_insert;
662 }
663
664 /**
665 * This method converts old forum hierarchy to new bbPress hierarchy.
666 */
667 public function convert_forum_parents( $start = 1 ) {
668 $has_update = false;
669 $query = ! empty( $this->sync_table )
670 ? $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value > 0 LIMIT {$start}, {$this->max_rows}", '_bbp_old_forum_parent_id' )
671 : $this->wpdb->prepare( "SELECT post_id AS value_id, meta_value FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value > 0 LIMIT {$start}, {$this->max_rows}", '_bbp_old_forum_parent_id' );
672
673 foreach ( $this->count_rows_by_results( $query ) as $row ) {
674 $parent_id = $this->callback_forumid( $row->meta_value );
675 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->posts} SET post_parent = %d WHERE ID = %d LIMIT 1", $parent_id, $row->value_id ) );
676 $has_update = true;
677 }
678
679 return ! $has_update;
680 }
681
682 /**
683 * This method converts old topic stickies to new bbPress stickies.
684 *
685 * @since 2.5.0 bbPress (r5170)
686 */
687 public function convert_topic_stickies( $start = 1 ) {
688 $has_update = false;
689 $query = ! empty( $this->sync_table )
690 ? $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT {$start}, {$this->max_rows}", '_bbp_old_sticky_status_id', 'sticky' )
691 : $this->wpdb->prepare( "SELECT post_id AS value_id, meta_value FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT {$start}, {$this->max_rows}", '_bbp_old_sticky_status_id', 'sticky' );
692
693 foreach ( $this->count_rows_by_results( $query ) as $row ) {
694 bbp_stick_topic( $row->value_id );
695 $has_update = true;
696 }
697
698 return ! $has_update;
699 }
700
701 /**
702 * This method converts old topic super stickies to new bbPress super stickies.
703 *
704 * @since 2.5.0 bbPress (r5170)
705 */
706 public function convert_topic_super_stickies( $start = 1 ) {
707 $has_update = false;
708 $query = ! empty( $this->sync_table )
709 ? $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT {$start}, {$this->max_rows}", '_bbp_old_sticky_status_id', 'super-sticky' )
710 : $this->wpdb->prepare( "SELECT post_id AS value_id, meta_value FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT {$start}, {$this->max_rows}", '_bbp_old_sticky_status_id', 'super-sticky' );
711
712 foreach ( $this->count_rows_by_results( $query ) as $row ) {
713 $super = true;
714 bbp_stick_topic( $row->value_id, $super );
715 $has_update = true;
716 }
717
718 return ! $has_update;
719 }
720
721 /**
722 * This method converts old closed topics to bbPress closed topics.
723 *
724 * @since 2.6.0 bbPress (r5425)
725 */
726 public function convert_topic_closed_topics( $start = 1 ) {
727 $has_update = false;
728 $query = ! empty( $this->sync_table )
729 ? $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT {$start}, {$this->max_rows}", '_bbp_old_closed_status_id', 'closed' )
730 : $this->wpdb->prepare( "SELECT post_id AS value_id, meta_value FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT {$start}, {$this->max_rows}", '_bbp_old_closed_status_id', 'closed' );
731
732 foreach ( $this->count_rows_by_results( $query ) as $row ) {
733 bbp_close_topic( $row->value_id );
734 $has_update = true;
735 }
736
737 return ! $has_update;
738 }
739
740 /**
741 * This method converts old reply_to post id to new bbPress reply_to post id.
742 *
743 * @since 2.4.0 bbPress (r5093)
744 */
745 public function convert_reply_to_parents( $start = 1 ) {
746 $has_update = false;
747 $query = ! empty( $this->sync_table )
748 ? $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value > 0 LIMIT {$start}, {$this->max_rows}", '_bbp_old_reply_to_id' )
749 : $this->wpdb->prepare( "SELECT post_id AS value_id, meta_value FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value > 0 LIMIT {$start}, {$this->max_rows}", '_bbp_old_reply_to_id' );
750
751 foreach ( $this->count_rows_by_results( $query ) as $row ) {
752 $reply_to = $this->callback_reply_to( $row->meta_value );
753 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->postmeta} SET meta_value = %s WHERE meta_key = %s AND post_id = %d LIMIT 1", $reply_to, '_bbp_reply_to', $row->value_id ) );
754 $has_update = true;
755 }
756
757 return ! $has_update;
758 }
759
760 /**
761 * This method converts anonymous topics.
762 *
763 * @since 2.6.0 bbPress (r5538)
764 */
765 public function convert_anonymous_topic_authors( $start = 1 ) {
766
767 $has_update = false;
768
769 if ( ! empty( $this->sync_table ) ) {
770 $query = $this->wpdb->prepare( "SELECT sync_table1.value_id AS topic_id, sync_table1.meta_value AS topic_is_anonymous, sync_table2.meta_value AS topic_author
771 FROM {$this->sync_table_name} AS sync_table1
772 INNER JOIN {$this->sync_table_name} AS sync_table2
773 ON ( sync_table1.value_id = sync_table2.value_id )
774 WHERE sync_table1.meta_value = %s
775 AND sync_table2.meta_key = %s
776 LIMIT {$start}, {$this->max_rows}", 'true', '_bbp_old_topic_author_name_id' );
777 } else {
778 $query = $this->wpdb->prepare( "SELECT wp_postmeta1.post_id AS topic_id, wp_postmeta1.meta_value AS topic_is_anonymous, wp_postmeta2.meta_value AS topic_author
779 FROM {$this->wpdb->postmeta} AS wp_postmeta1
780 INNER JOIN {$this->wpdb->postmeta} AS wp_postmeta2
781 ON ( wp_postmeta1.post_id = wp_postmeta2.post_id )
782 WHERE wp_postmeta1.meta_value = %s
783 AND wp_postmeta2.meta_key = %s
784 LIMIT {$start}, {$this->max_rows}", 'true', '_bbp_old_topic_author_name_id' );
785 }
786
787 foreach ( $this->count_rows_by_results( $query ) as $row ) {
788 $anonymous_topic_author_id = 0;
789 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->posts} SET post_author = %d WHERE ID = %d LIMIT 1", $anonymous_topic_author_id, $row->topic_id ) );
790
791 add_post_meta( $row->topic_id, '_bbp_anonymous_name', $row->topic_author );
792
793 $has_update = true;
794 }
795
796 return ! $has_update;
797 }
798
799 /**
800 * This method converts anonymous replies.
801 *
802 * @since 2.6.0 bbPress (r5538)
803 */
804 public function convert_anonymous_reply_authors( $start = 1 ) {
805
806 $has_update = false;
807
808 if ( ! empty( $this->sync_table ) ) {
809 $query = $this->wpdb->prepare( "SELECT sync_table1.value_id AS reply_id, sync_table1.meta_value AS reply_is_anonymous, sync_table2.meta_value AS reply_author
810 FROM {$this->sync_table_name} AS sync_table1
811 INNER JOIN {$this->sync_table_name} AS sync_table2
812 ON ( sync_table1.value_id = sync_table2.value_id )
813 WHERE sync_table1.meta_value = %s
814 AND sync_table2.meta_key = %s
815 LIMIT {$start}, {$this->max_rows}", 'true', '_bbp_old_reply_author_name_id' );
816 } else {
817 $query = $this->wpdb->prepare( "SELECT wp_postmeta1.post_id AS reply_id, wp_postmeta1.meta_value AS reply_is_anonymous, wp_postmeta2.meta_value AS reply_author
818 FROM {$this->wpdb->postmeta} AS wp_postmeta1
819 INNER JOIN {$this->wpdb->postmeta} AS wp_postmeta2
820 ON ( wp_postmeta1.post_id = wp_postmeta2.post_id )
821 WHERE wp_postmeta1.meta_value = %s
822 AND wp_postmeta2.meta_key = %s
823 LIMIT {$start}, {$this->max_rows}", 'true', '_bbp_old_reply_author_name_id' );
824 }
825
826 foreach ( $this->count_rows_by_results( $query ) as $row ) {
827 $anonymous_reply_author_id = 0;
828 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->posts} SET post_author = %d WHERE ID = %d LIMIT 1", $anonymous_reply_author_id, $row->reply_id ) );
829
830 add_post_meta( $row->reply_id, '_bbp_anonymous_name', $row->reply_author );
831
832 $has_update = true;
833 }
834
835 return ! $has_update;
836 }
837
838 /**
839 * This method deletes data from the wp database.
840 *
841 * @since 2.6.0 bbPress (r6456)
842 */
843 public function clean() {
844
845 // Defaults
846 $has_delete = false;
847
848 /** Delete topics/forums/posts ****************************************/
849
850 $esc_like = $this->wpdb->esc_like( '_bbp_' ) . '%';
851 $query = ! empty( $this->sync_table )
852 ? $this->wpdb->prepare( "SELECT value_id FROM {$this->sync_table_name} INNER JOIN {$this->wpdb->posts} ON(value_id = ID) WHERE meta_key LIKE %s AND value_type = %s GROUP BY value_id ORDER BY value_id DESC LIMIT {$this->max_rows}", $esc_like, 'post' )
853 : $this->wpdb->prepare( "SELECT post_id AS value_id FROM {$this->wpdb->postmeta} WHERE meta_key LIKE %s GROUP BY post_id ORDER BY post_id DESC LIMIT {$this->max_rows}", $esc_like );
854
855 $posts = $this->get_results( $query, ARRAY_A );
856
857 if ( isset( $posts[0] ) && ! empty( $posts[0]['value_id'] ) ) {
858 foreach ( (array) $posts as $value ) {
859 $deleted = wp_delete_post( $value['value_id'], true );
860
861 // Only flag if not empty or error
862 if ( ( false === $has_delete ) && ! empty( $deleted ) && ! is_wp_error( $deleted ) ) {
863 $has_delete = true;
864 }
865 }
866 }
867
868 /** Delete users ******************************************************/
869
870 $query = ! empty( $this->sync_table )
871 ? $this->wpdb->prepare( "SELECT value_id FROM {$this->sync_table_name} INNER JOIN {$this->wpdb->users} ON(value_id = ID) WHERE meta_key = %s AND value_type = %s LIMIT {$this->max_rows}", '_bbp_old_user_id', 'user' )
872 : $this->wpdb->prepare( "SELECT user_id AS value_id FROM {$this->wpdb->usermeta} WHERE meta_key = %s LIMIT {$this->max_rows}", '_bbp_old_user_id' );
873
874 $users = $this->get_results( $query, ARRAY_A );
875
876 if ( ! empty( $users ) ) {
877 foreach ( $users as $value ) {
878 $deleted = wp_delete_user( $value['value_id'] );
879
880 // Only flag if not empty or error
881 if ( ( false === $has_delete ) && ! empty( $deleted ) && ! is_wp_error( $deleted ) ) {
882 $has_delete = true;
883 }
884 }
885 }
886
887 unset( $posts );
888 unset( $users );
889
890 return ! $has_delete;
891 }
892
893 /**
894 * This method deletes passwords from the wp database.
895 *
896 * @param int Start row
897 */
898 public function clean_passwords( $start = 1 ) {
899 $has_delete = false;
900 $query = $this->wpdb->prepare( "SELECT user_id, meta_value FROM {$this->wpdb->usermeta} WHERE meta_key = %s LIMIT {$start}, {$this->max_rows}", '_bbp_password' );
901 $converted = $this->get_results( $query, ARRAY_A );
902
903 if ( ! empty( $converted ) ) {
904 foreach ( $converted as $value ) {
905 if ( is_serialized( $value['meta_value'] ) ) {
906 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->users} SET user_pass = '' WHERE ID = %d", $value['user_id'] ) );
907 } else {
908 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->users} SET user_pass = %s WHERE ID = %d", $value['meta_value'], $value['user_id'] ) );
909 $this->query( $this->wpdb->prepare( "DELETE FROM {$this->wpdb->usermeta} WHERE meta_key = %s AND user_id = %d", '_bbp_password', $value['user_id'] ) );
910 }
911 }
912 $has_delete = true;
913 }
914
915 return ! $has_delete;
916 }
917
918 /**
919 * This method implements the authentication for the different forums.
920 *
921 * @param string Un-encoded password.
922 */
923 abstract protected function authenticate_pass( $password, $hash );
924
925 /**
926 * Info
927 */
928 abstract protected function info();
929
930 /**
931 * This method grabs appropriate fields from the table specified
932 *
933 * @param string The table name to grab fields from
934 */
935 private function get_fields( $tablename = '' ) {
936 $retval = array();
937 $field_array = $this->get_results( "DESCRIBE {$tablename}", ARRAY_A );
938
939 // Bail if no fields
940 if ( empty( $field_array ) ) {
941 return $retval;
942 }
943
944 // Add fields to array
945 foreach ( $field_array as $field ) {
946 if ( ! empty( $field['Field'] ) ) {
947 $retval[] = $field['Field'];
948 }
949 }
950
951 // Add social fields for users table
952 if ( $tablename === $this->wpdb->users ) {
953 $retval[] = 'role';
954 $retval[] = 'yim';
955 $retval[] = 'aim';
956 $retval[] = 'jabber';
957 }
958
959 return $retval;
960 }
961
962 /** Database Wrappers *****************************************************/
963
964 /**
965 * Update the last query option and return results
966 *
967 * @param string $query
968 * @param string $output
969 */
970 private function get_row( $query = '' ) {
971 $this->update_query( $query );
972
973 return $this->wpdb->get_row( $query );
974 }
975
976 /**
977 * Update the last query option and return results
978 *
979 * @param string $query
980 * @param string $output
981 */
982 private function get_results( $query = '', $output = OBJECT ) {
983 $this->update_query( $query );
984
985 return (array) $this->wpdb->get_results( $query, $output );
986 }
987
988 /**
989 * Update the last query option and do a general query
990 *
991 * @param string $query
992 */
993 private function query( $query = '' ) {
994 $this->update_query( $query );
995
996 return $this->wpdb->query( $query );
997 }
998
999 /**
1000 * Update the last query ran
1001 *
1002 * @since 2.6.0 bbPress (r6637)
1003 *
1004 * @param string $query The literal MySQL query
1005 * @return bool
1006 */
1007 private function update_query( $query = '' ) {
1008 return update_option( '_bbp_converter_query', $query );
1009 }
1010
1011 /**
1012 * Update the number of rows in the current step
1013 *
1014 * @since 2.6.0 bbPress (r6637)
1015 *
1016 * @param string $query The literal MySQL query
1017 * @return bool
1018 */
1019 private function count_rows_by_results( $query = '' ) {
1020 $results = $this->get_results( $query );
1021
1022 update_option( '_bbp_converter_rows_in_step', count( $results ) );
1023
1024 return $results;
1025 }
1026
1027 /**
1028 * Update the number of rows in the current step
1029 *
1030 * @since 2.6.0 bbPress (r6637)
1031 *
1032 * @param string $table_name The literal MySQL query
1033 * @return bool
1034 */
1035 private function count_rows_by_table( $table_name = '' ) {
1036 $count = (int) $this->opdb->get_var( "SELECT COUNT(*) FROM {$table_name}" );
1037
1038 return update_option( '_bbp_converter_rows_in_step', $count );
1039 }
1040
1041 /** Callbacks *************************************************************/
1042
1043 /**
1044 * Run password through wp_hash_password()
1045 *
1046 * @param string $username
1047 * @param string $password
1048 */
1049 public function callback_pass( $username = '', $password = '' ) {
1050
1051 // Get user – Bail if not found
1052 $user = $this->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->users} WHERE user_login = %s AND user_pass = '' LIMIT 1", $username ) );
1053 if ( empty( $user ) ) {
1054 return;
1055 }
1056
1057 // Get usermeta – Bail if not found
1058 $usermeta = $this->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->usermeta} WHERE meta_key = %s AND user_id = %d LIMIT 1", '_bbp_password', $user->ID ) );
1059 if ( empty( $usermeta ) ) {
1060 return;
1061 }
1062
1063 // Bail if auth fails
1064 if ( ! $this->authenticate_pass( $password, $usermeta->meta_value ) ) {
1065 return;
1066 }
1067
1068 // Hash the password
1069 $new_pass = wp_hash_password( $password );
1070
1071 // Update
1072 $this->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->users} SET user_pass = %s WHERE ID = %d", $new_pass, $user->ID ) );
1073
1074 // Clean up
1075 unset( $new_pass );
1076 $this->query( $this->wpdb->prepare( "DELETE FROM {$this->wpdb->usermeta} WHERE meta_key = %s AND user_id = %d", '_bbp_password', $user->ID ) );
1077 $this->query( $this->wpdb->prepare( "DELETE FROM {$this->wpdb->usermeta} WHERE meta_key = %s AND user_id = %d", '_bbp_class', $user->ID ) );
1078
1079 // Clean the cache for this user since their password was
1080 // upgraded from the old platform to the new.
1081 clean_user_cache( $user->ID );
1082 }
1083
1084 /**
1085 * A mini cache system to reduce database calls to forum ID's
1086 *
1087 * @param string $field
1088 * @return string
1089 */
1090 private function callback_forumid( $field ) {
1091 if ( ! isset( $this->map_forumid[ $field ] ) ) {
1092 $row = ! empty( $this->sync_table )
1093 ? $this->get_row( $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_forum_id', $field ) )
1094 : $this->get_row( $this->wpdb->prepare( "SELECT post_id AS value_id FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_forum_id', $field ) );
1095
1096 $this->map_forumid[ $field ] = ! is_null( $row )
1097 ? $row->value_id
1098 : 0;
1099 }
1100
1101 return $this->map_forumid[ $field ];
1102 }
1103
1104 /**
1105 * A mini cache system to reduce database calls to topic ID's
1106 *
1107 * @param string $field
1108 * @return string
1109 */
1110 private function callback_topicid( $field ) {
1111 if ( ! isset( $this->map_topicid[ $field ] ) ) {
1112 $row = ! empty( $this->sync_table )
1113 ? $this->get_row( $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_topic_id', $field ) )
1114 : $this->get_row( $this->wpdb->prepare( "SELECT post_id AS value_id FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_topic_id', $field ) );
1115
1116 $this->map_topicid[ $field ] = ! is_null( $row )
1117 ? $row->value_id
1118 : 0;
1119 }
1120
1121 return $this->map_topicid[ $field ];
1122 }
1123
1124 /**
1125 * A mini cache system to reduce database calls to reply_to post id.
1126 *
1127 * @since 2.4.0 bbPress (r5093)
1128 *
1129 * @param string $field
1130 * @return string
1131 */
1132 private function callback_reply_to( $field ) {
1133 if ( ! isset( $this->map_reply_to[ $field ] ) ) {
1134 $row = ! empty( $this->sync_table )
1135 ? $this->get_row( $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_reply_id', $field ) )
1136 : $this->get_row( $this->wpdb->prepare( "SELECT post_id AS value_id FROM {$this->wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_reply_id', $field ) );
1137
1138 $this->map_reply_to[ $field ] = ! is_null( $row )
1139 ? $row->value_id
1140 : 0;
1141 }
1142
1143 return $this->map_reply_to[ $field ];
1144 }
1145
1146 /**
1147 * A mini cache system to reduce database calls to user ID's
1148 *
1149 * @param string $field
1150 * @return string
1151 */
1152 private function callback_userid( $field ) {
1153 if ( ! isset( $this->map_userid[ $field ] ) ) {
1154 $row = ! empty( $this->sync_table )
1155 ? $this->get_row( $this->wpdb->prepare( "SELECT value_id, meta_value FROM {$this->sync_table_name} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_user_id', $field ) )
1156 : $this->get_row( $this->wpdb->prepare( "SELECT user_id AS value_id FROM {$this->wpdb->usermeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_bbp_old_user_id', $field ) );
1157
1158 if ( ! is_null( $row ) ) {
1159 $this->map_userid[ $field ] = $row->value_id;
1160 } else {
1161 $this->map_userid[ $field ] = ( true === $this->convert_users )
1162 ? 0
1163 : $field;
1164 }
1165 }
1166
1167 return $this->map_userid[ $field ];
1168 }
1169
1170 /**
1171 * Check if the topic or reply author is anonymous
1172 *
1173 * @since 2.6.0 bbPress (r5544)
1174 *
1175 * @param string $field
1176 * @return string
1177 */
1178 private function callback_check_anonymous( $field ) {
1179 $field = ( $this->callback_userid( $field ) == 0 )
1180 ? 'true'
1181 : 'false';
1182
1183 return $field;
1184 }
1185
1186 /**
1187 * A mini cache system to reduce database calls map topics ID's to forum ID's
1188 *
1189 * @param string $field
1190 * @return string
1191 */
1192 private function callback_topicid_to_forumid( $field ) {
1193 $topicid = $this->callback_topicid( $field );
1194 if ( empty( $topicid ) ) {
1195 $this->map_topicid_to_forumid[ $topicid ] = 0;
1196 } elseif ( ! isset( $this->map_topicid_to_forumid[ $topicid ] ) ) {
1197 $row = $this->get_row( $this->wpdb->prepare( "SELECT post_parent FROM {$this->wpdb->posts} WHERE ID = %d LIMIT 1", $topicid ) );
1198
1199 $this->map_topicid_to_forumid[ $topicid ] = ! is_null( $row )
1200 ? $row->post_parent
1201 : 0;
1202 }
1203
1204 return $this->map_topicid_to_forumid[ $topicid ];
1205 }
1206
1207 protected function callback_slug( $field ) {
1208 return sanitize_title( $field );
1209 }
1210
1211 protected function callback_negative( $field ) {
1212 return ( $field < 0 )
1213 ? 0
1214 : $field;
1215 }
1216
1217 protected function callback_html( $field ) {
1218 require_once bbp_admin()->admin_dir . 'parser.php';
1219
1220 // Setup the BBCode parser
1221 $bbcode = BBCode::getInstance();
1222
1223 // Pass BBCode properties to the parser
1224 foreach ( $this->bbcode_parser_properties as $prop => $value ) {
1225 $bbcode->{$prop} = $value;
1226 }
1227
1228 return html_entity_decode( $bbcode->Parse( $field ) );
1229 }
1230
1231 protected function callback_null( $field ) {
1232 return is_null( $field )
1233 ? ''
1234 : $field;
1235 }
1236
1237 protected function callback_datetime( $field ) {
1238 return is_numeric( $field )
1239 ? date( 'Y-m-d H:i:s', $field )
1240 : date( 'Y-m-d H:i:s', strtotime( $field ) );
1241 }
1242 }
1243 endif;
1244