PluginProbe
bbPress / 2.6.18
bbPress v2.6.18
2.6.19 2.6.18 2.6.17 trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 All 74 releases
bbpress / includes / admin / converters / Drupal7.php

Drupal7.php in bbPress 2.6.18, at includes/admin/converters/Drupal7.php

848 lines 24.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Drupal7 Converter
5 *
6 * @package bbPress
7 * @subpackage Converters
8 */
9
10 /**
11 * Implementation of Drupal v7.x Forum converter.
12 *
13 * @since 2.5.0 bbPress (r5138)
14 *
15 * @link Codex Docs https://codex.bbpress.org/import-forums/drupal
16 */
17 class Drupal7 extends BBP_Converter_Base {
18
19 /**
20 * Main Constructor
21 */
22 public function __construct() {
23 parent::__construct();
24 }
25
26 /**
27 * Sets up the field mappings
28 */
29 public function setup_globals() {
30
31 /** Forum Section *****************************************************/
32
33 // Old forum id (Stored in postmeta)
34 $this->field_map[] = array(
35 'from_tablename' => 'taxonomy_term_data',
36 'from_fieldname' => 'tid',
37 'to_type' => 'forum',
38 'to_fieldname' => '_bbp_old_forum_id'
39 );
40
41 // Forum parent id (If no parent, then 0, Stored in postmeta)
42 $this->field_map[] = array(
43 'from_tablename' => 'taxonomy_term_hierarchy',
44 'from_fieldname' => 'parent',
45 'join_tablename' => 'taxonomy_term_data',
46 'join_type' => 'INNER',
47 'join_expression' => 'USING (tid)',
48 'from_expression' => 'LEFT JOIN taxonomy_vocabulary AS taxonomy_vocabulary USING (vid) WHERE module = "forum"',
49 'to_type' => 'forum',
50 'to_fieldname' => '_bbp_old_forum_parent_id'
51 );
52
53 // Forum title.
54 $this->field_map[] = array(
55 'from_tablename' => 'taxonomy_term_data',
56 'from_fieldname' => 'name',
57 'to_type' => 'forum',
58 'to_fieldname' => 'post_title'
59 );
60
61 // Forum slug (Clean name to avoid conflicts)
62 $this->field_map[] = array(
63 'from_tablename' => 'taxonomy_term_data',
64 'from_fieldname' => 'name',
65 'to_type' => 'forum',
66 'to_fieldname' => 'post_name',
67 'callback_method' => 'callback_slug'
68 );
69
70 // Forum description.
71 $this->field_map[] = array(
72 'from_tablename' => 'taxonomy_term_data',
73 'from_fieldname' => 'description',
74 'to_type' => 'forum',
75 'to_fieldname' => 'post_content',
76 'callback_method' => 'callback_null'
77 );
78
79 // Forum display order (Starts from 1)
80 $this->field_map[] = array(
81 'from_tablename' => 'taxonomy_term_data',
82 'from_fieldname' => 'weight',
83 'to_type' => 'forum',
84 'to_fieldname' => 'menu_order'
85 );
86
87 // Forum type (Set a default value 'forum', Stored in postmeta)
88 $this->field_map[] = array(
89 'to_type' => 'forum',
90 'to_fieldname' => '_bbp_forum_type',
91 'default' => 'forum'
92 );
93
94 // Forum status (Set a default value 'open', Stored in postmeta)
95 $this->field_map[] = array(
96 'to_type' => 'forum',
97 'to_fieldname' => '_bbp_status',
98 'default' => 'open'
99 );
100
101 // Forum dates.
102 $this->field_map[] = array(
103 'to_type' => 'forum',
104 'to_fieldname' => 'post_date',
105 'default' => date( 'Y-m-d H:i:s' ) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
106 );
107 $this->field_map[] = array(
108 'to_type' => 'forum',
109 'to_fieldname' => 'post_date_gmt',
110 'default' => date( 'Y-m-d H:i:s' ) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
111 );
112 $this->field_map[] = array(
113 'to_type' => 'forum',
114 'to_fieldname' => 'post_modified',
115 'default' => date( 'Y-m-d H:i:s' ) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
116 );
117 $this->field_map[] = array(
118 'to_type' => 'forum',
119 'to_fieldname' => 'post_modified_gmt',
120 'default' => date( 'Y-m-d H:i:s' ) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
121 );
122
123 /** Topic Section *****************************************************/
124
125 // Old topic id (Stored in postmeta)
126 $this->field_map[] = array(
127 'from_tablename' => 'forum_index',
128 'from_fieldname' => 'nid',
129 'to_type' => 'topic',
130 'to_fieldname' => '_bbp_old_topic_id'
131 );
132
133 // Topic reply count (Stored in postmeta)
134 $this->field_map[] = array(
135 'from_tablename' => 'forum_index',
136 'from_fieldname' => 'comment_count',
137 'to_type' => 'topic',
138 'to_fieldname' => '_bbp_reply_count',
139 'callback_method' => 'callback_topic_reply_count'
140 );
141
142 // Topic total reply count (Includes unpublished replies, Stored in postmeta)
143 $this->field_map[] = array(
144 'from_tablename' => 'forum_index',
145 'from_fieldname' => 'comment_count',
146 'to_type' => 'topic',
147 'to_fieldname' => '_bbp_total_reply_count',
148 'callback_method' => 'callback_topic_reply_count'
149 );
150
151 // Topic parent forum id (If no parent, then 0. Stored in postmeta)
152 $this->field_map[] = array(
153 'from_tablename' => 'forum_index',
154 'from_fieldname' => 'tid',
155 'to_type' => 'topic',
156 'to_fieldname' => '_bbp_forum_id',
157 'callback_method' => 'callback_forumid'
158 );
159
160 // Topic author.
161 // Note: We join the 'node' table because 'forum_index' table does not include author id.
162 $this->field_map[] = array(
163 'from_tablename' => 'node',
164 'from_fieldname' => 'uid',
165 'join_tablename' => 'forum_index',
166 'join_type' => 'INNER',
167 'join_expression' => 'ON node.nid = forum_index.nid',
168 'to_type' => 'topic',
169 'to_fieldname' => 'post_author',
170 'callback_method' => 'callback_userid'
171 );
172
173 // Topic author name (Stored in postmeta as _bbp_anonymous_name)
174 $this->field_map[] = array(
175 'to_type' => 'topic',
176 'to_fieldname' => '_bbp_old_topic_author_name_id',
177 'default' => 'Anonymous'
178 );
179
180 // Is the topic anonymous (Stored in postmeta)
181 $this->field_map[] = array(
182 'from_tablename' => 'node',
183 'from_fieldname' => 'uid',
184 'join_tablename' => 'forum_index',
185 'join_type' => 'INNER',
186 'join_expression' => 'ON node.nid = forum_index.nid',
187 'to_type' => 'topic',
188 'to_fieldname' => '_bbp_old_is_topic_anonymous_id',
189 'callback_method' => 'callback_check_anonymous'
190 );
191
192 // Topic content.
193 // Note: We join the 'field_data_body' table because 'node' or 'forum_index' table does not include topic content.
194 $this->field_map[] = array(
195 'from_tablename' => 'field_data_body',
196 'from_fieldname' => 'body_value',
197 'join_tablename' => 'node',
198 'join_type' => 'INNER',
199 'join_expression' => 'ON field_data_body.revision_id = node.vid',
200 'to_type' => 'topic',
201 'to_fieldname' => 'post_content',
202 'callback_method' => 'callback_html'
203 );
204
205 // Topic title.
206 $this->field_map[] = array(
207 'from_tablename' => 'forum_index',
208 'from_fieldname' => 'title',
209 'to_type' => 'topic',
210 'to_fieldname' => 'post_title'
211 );
212
213 // Topic slug (Clean name to avoid conflicts)
214 $this->field_map[] = array(
215 'from_tablename' => 'forum_index',
216 'from_fieldname' => 'title',
217 'to_type' => 'topic',
218 'to_fieldname' => 'post_name',
219 'callback_method' => 'callback_slug'
220 );
221
222 // Topic parent forum id (If no parent, then 0)
223 $this->field_map[] = array(
224 'from_tablename' => 'forum_index',
225 'from_fieldname' => 'tid',
226 'to_type' => 'topic',
227 'to_fieldname' => 'post_parent',
228 'callback_method' => 'callback_forumid'
229 );
230
231 // Topic status (Publish or Unpublished, Drupal v7.x publish = 1, pending = 0)
232 $this->field_map[] = array(
233 'from_tablename' => 'node',
234 'from_fieldname' => 'status',
235 'join_tablename' => 'forum_index',
236 'join_type' => 'INNER',
237 'join_expression' => 'ON node.nid = forum_index.nid',
238 'to_type' => 'topic',
239 'to_fieldname' => 'post_status',
240 'callback_method' => 'callback_status'
241 );
242
243 // Sticky status (Stored in postmeta)
244 $this->field_map[] = array(
245 'from_tablename' => 'forum_index',
246 'from_fieldname' => 'sticky',
247 'to_type' => 'topic',
248 'to_fieldname' => '_bbp_old_sticky_status_id',
249 'callback_method' => 'callback_sticky_status'
250 );
251
252 // Topic dates.
253 $this->field_map[] = array(
254 'from_tablename' => 'forum_index',
255 'from_fieldname' => 'created',
256 'to_type' => 'topic',
257 'to_fieldname' => 'post_date',
258 'callback_method' => 'callback_datetime'
259 );
260 $this->field_map[] = array(
261 'from_tablename' => 'forum_index',
262 'from_fieldname' => 'created',
263 'to_type' => 'topic',
264 'to_fieldname' => 'post_date_gmt',
265 'callback_method' => 'callback_datetime'
266 );
267 $this->field_map[] = array(
268 'from_tablename' => 'forum_index',
269 'from_fieldname' => 'last_comment_timestamp',
270 'to_type' => 'topic',
271 'to_fieldname' => 'post_modified',
272 'callback_method' => 'callback_datetime'
273 );
274 $this->field_map[] = array(
275 'from_tablename' => 'forum_index',
276 'from_fieldname' => 'last_comment_timestamp',
277 'to_type' => 'topic',
278 'to_fieldname' => 'post_modified_gmt',
279 'callback_method' => 'callback_datetime'
280 );
281 $this->field_map[] = array(
282 'from_tablename' => 'forum_index',
283 'from_fieldname' => 'last_comment_timestamp',
284 'to_type' => 'topic',
285 'to_fieldname' => '_bbp_last_active_time',
286 'callback_method' => 'callback_datetime'
287 );
288
289 // Topic status (Drupal v7.x Comments Enabled no = 0, closed = 1 & open = 2)
290 $this->field_map[] = array(
291 'from_tablename' => 'node',
292 'from_fieldname' => 'comment',
293 'join_tablename' => 'forum_index',
294 'join_type' => 'INNER',
295 'join_expression' => 'ON node.nid = forum_index.nid',
296 'to_type' => 'topic',
297 'to_fieldname' => '_bbp_old_closed_status_id',
298 'callback_method' => 'callback_topic_status'
299 );
300
301 /** Tags Section ******************************************************/
302
303 // Topic id.
304 $this->field_map[] = array(
305 'from_tablename' => 'field_data_field_tags',
306 'from_fieldname' => 'entity_id',
307 'to_type' => 'tags',
308 'to_fieldname' => 'objectid',
309 'callback_method' => 'callback_topicid'
310 );
311
312 // Taxonomy ID.
313 $this->field_map[] = array(
314 'from_tablename' => 'field_data_field_tags',
315 'from_fieldname' => 'field_tags_tid',
316 'to_type' => 'tags',
317 'to_fieldname' => 'taxonomy'
318 );
319
320 // Term name.
321 $this->field_map[] = array(
322 'from_tablename' => 'taxonomy_term_data',
323 'from_fieldname' => 'name',
324 'join_tablename' => 'field_data_field_tags',
325 'join_type' => 'INNER',
326 'join_expression' => 'ON field_tags_tid = taxonomy_term_data.tid',
327 'to_type' => 'tags',
328 'to_fieldname' => 'name'
329 );
330
331 // Term slug.
332 $this->field_map[] = array(
333 'from_tablename' => 'taxonomy_term_data',
334 'from_fieldname' => 'name',
335 'join_tablename' => 'field_data_field_tags',
336 'join_type' => 'INNER',
337 'join_expression' => 'ON field_tags_tid = taxonomy_term_data.tid',
338 'to_type' => 'tags',
339 'to_fieldname' => 'slug',
340 'callback_method' => 'callback_slug'
341 );
342
343 // Term description.
344 $this->field_map[] = array(
345 'from_tablename' => 'taxonomy_term_data',
346 'from_fieldname' => 'description',
347 'join_tablename' => 'field_data_field_tags',
348 'join_type' => 'INNER',
349 'join_expression' => 'ON field_tags_tid = taxonomy_term_data.tid',
350 'to_type' => 'tags',
351 'to_fieldname' => 'description'
352 );
353
354 /** Reply Section *****************************************************/
355
356 // Old reply id (Stored in postmeta)
357 $this->field_map[] = array(
358 'from_tablename' => 'comment',
359 'from_fieldname' => 'cid',
360 'to_type' => 'reply',
361 'to_fieldname' => '_bbp_old_reply_id'
362 );
363
364 // Reply parent forum id (If no parent, then 0. Stored in postmeta)
365 $this->field_map[] = array(
366 'from_tablename' => 'comment',
367 'from_fieldname' => 'nid',
368 'to_type' => 'reply',
369 'to_fieldname' => '_bbp_forum_id',
370 'callback_method' => 'callback_topicid_to_forumid'
371 );
372
373 // Reply parent topic id (If no parent, then 0. Stored in postmeta)
374 $this->field_map[] = array(
375 'from_tablename' => 'comment',
376 'from_fieldname' => 'nid',
377 'to_type' => 'reply',
378 'to_fieldname' => '_bbp_topic_id',
379 'callback_method' => 'callback_topicid'
380 );
381
382 // Reply parent reply id (If no parent, then 0. Stored in postmeta)
383 $this->field_map[] = array(
384 'from_tablename' => 'comment',
385 'from_fieldname' => 'pid',
386 'to_type' => 'reply',
387 'to_fieldname' => '_bbp_old_reply_to_id'
388 );
389
390 // Reply author ip (Stored in postmeta)
391 $this->field_map[] = array(
392 'from_tablename' => 'comment',
393 'from_fieldname' => 'hostname',
394 'to_type' => 'reply',
395 'to_fieldname' => '_bbp_author_ip'
396 );
397
398 // Reply author.
399 $this->field_map[] = array(
400 'from_tablename' => 'comment',
401 'from_fieldname' => 'uid',
402 'to_type' => 'reply',
403 'to_fieldname' => 'post_author',
404 'callback_method' => 'callback_userid'
405 );
406
407 // Reply status (Publish or Unpublished, Drupal v7.x publish = 1, pending = 0)
408 $this->field_map[] = array(
409 'from_tablename' => 'comment',
410 'from_fieldname' => 'status',
411 'to_type' => 'reply',
412 'to_fieldname' => 'post_status',
413 'callback_method' => 'callback_status'
414 );
415
416 // Reply author name (Stored in postmeta as _bbp_anonymous_name)
417 $this->field_map[] = array(
418 'from_tablename' => 'comment',
419 'from_fieldname' => 'name',
420 'to_type' => 'reply',
421 'to_fieldname' => '_bbp_old_reply_author_name_id'
422 );
423
424 // Is the reply anonymous (Stored in postmeta)
425 $this->field_map[] = array(
426 'from_tablename' => 'comment',
427 'from_fieldname' => 'uid',
428 'to_type' => 'reply',
429 'to_fieldname' => '_bbp_old_is_reply_anonymous_id',
430 'callback_method' => 'callback_check_anonymous'
431 );
432
433 // Reply content.
434 // Note: We join the 'field_data_comment_body' table because 'comment' table does not include reply content.
435 $this->field_map[] = array(
436 'from_tablename' => 'field_data_comment_body',
437 'from_fieldname' => 'comment_body_value',
438 'join_tablename' => 'comment',
439 'join_type' => 'INNER',
440 'join_expression' => 'ON field_data_comment_body.entity_id = comment.cid',
441 'to_type' => 'reply',
442 'to_fieldname' => 'post_content',
443 'callback_method' => 'callback_html'
444 );
445
446 // Reply parent topic id (If no parent, then 0)
447 $this->field_map[] = array(
448 'from_tablename' => 'comment',
449 'from_fieldname' => 'nid',
450 'to_type' => 'reply',
451 'to_fieldname' => 'post_parent',
452 'callback_method' => 'callback_topicid'
453 );
454
455 // Reply dates.
456 $this->field_map[] = array(
457 'from_tablename' => 'comment',
458 'from_fieldname' => 'created',
459 'to_type' => 'reply',
460 'to_fieldname' => 'post_date',
461 'callback_method' => 'callback_datetime'
462 );
463 $this->field_map[] = array(
464 'from_tablename' => 'comment',
465 'from_fieldname' => 'created',
466 'to_type' => 'reply',
467 'to_fieldname' => 'post_date_gmt',
468 'callback_method' => 'callback_datetime'
469 );
470 $this->field_map[] = array(
471 'from_tablename' => 'comment',
472 'from_fieldname' => 'changed',
473 'to_type' => 'reply',
474 'to_fieldname' => 'post_modified',
475 'callback_method' => 'callback_datetime'
476 );
477 $this->field_map[] = array(
478 'from_tablename' => 'comment',
479 'from_fieldname' => 'changed',
480 'to_type' => 'reply',
481 'to_fieldname' => 'post_modified_gmt',
482 'callback_method' => 'callback_datetime'
483 );
484
485 /** User Section ******************************************************/
486
487 // Store old user id (Stored in usermeta)
488 // Don't import user uid = 0, this is Drupal 7's guest user
489 $this->field_map[] = array(
490 'from_tablename' => 'users',
491 'from_fieldname' => 'uid',
492 'from_expression' => 'WHERE uid != 0',
493 'to_type' => 'user',
494 'to_fieldname' => '_bbp_old_user_id'
495 );
496
497 // Store old user password (Stored in usermeta as a serialized array)
498 $this->field_map[] = array(
499 'from_tablename' => 'users',
500 'from_fieldname' => 'pass',
501 'to_type' => 'user',
502 'to_fieldname' => '_bbp_password',
503 'callback_method' => 'callback_savepass'
504 );
505
506 // User password verify class (Stored in usermeta for verifying password)
507 $this->field_map[] = array(
508 'to_type' => 'user',
509 'to_fieldname' => '_bbp_class',
510 'default' => 'Drupal7'
511 );
512
513 // User name.
514 $this->field_map[] = array(
515 'from_tablename' => 'users',
516 'from_fieldname' => 'name',
517 'to_type' => 'user',
518 'to_fieldname' => 'user_login'
519 );
520
521 // User nice name.
522 $this->field_map[] = array(
523 'from_tablename' => 'users',
524 'from_fieldname' => 'name',
525 'to_type' => 'user',
526 'to_fieldname' => 'user_nicename'
527 );
528
529 // User email.
530 $this->field_map[] = array(
531 'from_tablename' => 'users',
532 'from_fieldname' => 'mail',
533 'to_type' => 'user',
534 'to_fieldname' => 'user_email'
535 );
536
537 // User registered.
538 $this->field_map[] = array(
539 'from_tablename' => 'users',
540 'from_fieldname' => 'created',
541 'to_type' => 'user',
542 'to_fieldname' => 'user_registered',
543 'callback_method' => 'callback_datetime'
544 );
545
546 // Store Signature (Stored in usermeta)
547 $this->field_map[] = array(
548 'from_tablename' => 'users',
549 'from_fieldname' => 'signature',
550 'to_fieldname' => '_bbp_drupal7_user_sig',
551 'to_type' => 'user',
552 'callback_method' => 'callback_html'
553 );
554 }
555
556 /**
557 * This method allows us to indicates what is or is not converted for each
558 * converter.
559 */
560 public function info() {
561 return '';
562 }
563
564 /**
565 * Store the Drupal password hash in a serialized array.
566 *
567 * Array values are automatically serialized by WordPress.
568 */
569 public function callback_savepass( $field, $row ) {
570 return array( 'hash' => $field );
571 }
572
573 /**
574 * Upgrade password metadata written by earlier Drupal 7 imports.
575 *
576 * Earlier imports stored the hash without serializing it. New imports use
577 * the parent callback and the serialized format from callback_savepass().
578 *
579 * @param string $username WordPress user login.
580 * @param string $password Unslashed password for Drupal.
581 * @param string|null $wp_password Optional slashed password for WordPress.
582 */
583 public function callback_pass( $username = '', $password = '', $wp_password = null ) {
584 $user = get_user_by( 'login', $username );
585
586 if ( ! empty( $user ) && '' === $user->user_pass ) {
587 $stored_hash = get_user_meta( $user->ID, '_bbp_password', true );
588
589 if ( is_string( $stored_hash ) && $this->is_drupal_hash( $stored_hash ) ) {
590 if ( $this->authenticate_pass( $password, serialize( array( 'hash' => $stored_hash ) ) ) ) {
591 $this->upgrade_user_pass( $user->ID, is_null( $wp_password ) ? $password : $wp_password );
592 }
593
594 return;
595 }
596 }
597
598 parent::callback_pass( $username, $password, $wp_password );
599 }
600
601 /**
602 * Upgrade a Drupal hash copied directly into wp_users by an old import.
603 *
604 * @param WP_User $user WordPress user.
605 * @param string $password Unslashed password for Drupal.
606 * @param string|null $wp_password Optional slashed password for WordPress.
607 */
608 public function callback_user_pass( $user, $password, $wp_password = null ) {
609 $wp_password = is_null( $wp_password ) ? $password : $wp_password;
610
611 // Do not replace a password that WordPress or a password plugin already
612 // recognizes, regardless of the stored hash prefix.
613 if ( ! ( $user instanceof WP_User ) || wp_check_password( $wp_password, $user->user_pass, $user->ID ) || ! $this->is_drupal_hash( $user->user_pass, false ) ) {
614 return;
615 }
616
617 if ( $this->authenticate_pass( $password, serialize( array( 'hash' => $user->user_pass ) ) ) ) {
618 $this->upgrade_user_pass( $user->ID, $wp_password );
619 }
620 }
621
622 /**
623 * This method is to take the pass out of the database and compare
624 * to a pass the user has typed in.
625 */
626 public function authenticate_pass( $password, $serialized_pass ) {
627
628 // Unserialize the password, with safeguards
629 $pass_array = $this->unserialize_pass( $serialized_pass );
630
631 // Bail if missing or invalid values
632 if ( ! is_string( $password ) || ! is_array( $pass_array ) || ! isset( $pass_array['hash'] ) || ! is_string( $pass_array['hash'] ) ) {
633 return false;
634 }
635
636 $stored_hash = $pass_array['hash'];
637
638 // Drupal 6 passwords upgraded to Drupal 7 use an MD5 pre-hash
639 if ( 0 === strpos( $stored_hash, 'U$' ) ) {
640 $stored_hash = substr( $stored_hash, 1 );
641 $password = md5( $password );
642 }
643
644 switch ( substr( $stored_hash, 0, 3 ) ) {
645 case '$S$' :
646 $hash = $this->hash_password( 'sha512', $password, $stored_hash );
647 break;
648
649 case '$H$' :
650 case '$P$' :
651 $hash = $this->hash_password( 'md5', $password, $stored_hash );
652 break;
653
654 default :
655 return false;
656 }
657
658 return is_string( $hash ) && hash_equals( $stored_hash, $hash );
659 }
660
661 /**
662 * Hash a password using Drupal 7's portable password algorithm.
663 *
664 * @param string $algorithm Hash algorithm.
665 * @param string $password Plain-text password.
666 * @param string $setting Stored hash or hash setting.
667 * @return string|bool Password hash on success, false on failure.
668 */
669 private function hash_password( $algorithm, $password, $setting ) {
670 if ( strlen( $password ) > 512 ) {
671 return false;
672 }
673
674 $setting = substr( $setting, 0, 12 );
675 if ( 12 !== strlen( $setting ) || '$' !== $setting[0] || '$' !== $setting[2] ) {
676 return false;
677 }
678
679 $characters = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
680 $count_log2 = strpos( $characters, $setting[3] );
681 $salt = substr( $setting, 4, 8 );
682
683 if ( false === $count_log2 || $count_log2 < 7 || $count_log2 > 30 || 8 !== strlen( $salt ) ) {
684 return false;
685 }
686
687 $count = 1 << $count_log2;
688 $hash = hash( $algorithm, $salt . $password, true );
689
690 do {
691 $hash = hash( $algorithm, $hash . $password, true );
692 } while ( --$count );
693
694 $output = $setting . $this->base64_encode_password( $hash, strlen( $hash ) );
695 $expected = 12 + ceil( ( 8 * strlen( $hash ) ) / 6 );
696
697 return ( strlen( $output ) === (int) $expected )
698 ? substr( $output, 0, 55 )
699 : false;
700 }
701
702 /**
703 * Encode bytes using Drupal 7's password base64 alphabet.
704 *
705 * @param string $input Bytes to encode.
706 * @param int $count Number of bytes to encode.
707 * @return string Encoded bytes.
708 */
709 private function base64_encode_password( $input, $count ) {
710 $output = '';
711 $index = 0;
712 $characters = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
713
714 do {
715 $value = ord( $input[ $index++ ] );
716 $output .= $characters[ $value & 0x3f ];
717 if ( $index < $count ) {
718 $value |= ord( $input[ $index ] ) << 8;
719 }
720 $output .= $characters[ ( $value >> 6 ) & 0x3f ];
721 if ( $index++ >= $count ) {
722 break;
723 }
724 if ( $index < $count ) {
725 $value |= ord( $input[ $index ] ) << 16;
726 }
727 $output .= $characters[ ( $value >> 12 ) & 0x3f ];
728 if ( $index++ >= $count ) {
729 break;
730 }
731 $output .= $characters[ ( $value >> 18 ) & 0x3f ];
732 } while ( $index < $count );
733
734 return $output;
735 }
736
737 /**
738 * Determine whether a stored value is a Drupal password hash.
739 *
740 * @param string $hash Stored password hash.
741 * @param bool $allow_phpass Whether to include WordPress-compatible $P$.
742 * @return bool True when the value has a supported Drupal hash prefix.
743 */
744 private function is_drupal_hash( $hash, $allow_phpass = true ) {
745 if ( ! is_string( $hash ) ) {
746 return false;
747 }
748
749 // A $P$ hash in metadata is unambiguously from Drupal, while one copied
750 // into user_pass is also a WordPress-compatible password hash.
751 $prefixes = $allow_phpass
752 ? array( '$S$', '$H$', '$P$', 'U$' )
753 : array( '$S$', '$H$', 'U$' );
754
755 foreach ( $prefixes as $prefix ) {
756 if ( 0 === strpos( $hash, $prefix ) ) {
757 return true;
758 }
759 }
760
761 return false;
762 }
763
764 /**
765 * Replace a verified Drupal hash with a WordPress password.
766 *
767 * @param int $user_id WordPress user ID.
768 * @param string $password Password to hash for WordPress.
769 */
770 private function upgrade_user_pass( $user_id, $password ) {
771 wp_set_password( $password, $user_id );
772 delete_user_meta( $user_id, '_bbp_password' );
773 delete_user_meta( $user_id, '_bbp_class' );
774 }
775
776 /**
777 * Translate the post status from Drupal v7.x numerics to WordPress's
778 * strings.
779 *
780 * @param int $status Drupal v7.x numeric post status
781 * @return string WordPress safe
782 */
783 public function callback_status( $status = 1 ) {
784 switch ( $status ) {
785 case 0 :
786 $status = 'pending'; // bbp_get_pending_status_id()
787 break;
788
789 case 1 :
790 default :
791 $status = 'publish'; // bbp_get_public_status_id()
792 break;
793 }
794 return $status;
795 }
796
797 /**
798 * Translate the post status from Drupal v7.x numerics to WordPress's strings.
799 *
800 * @param int $status Drupal v7.x numeric topic status
801 * @return string WordPress safe
802 */
803 public function callback_topic_status( $status = 2 ) {
804 switch ( $status ) {
805 case 1 :
806 $status = 'closed';
807 break;
808
809 case 2 :
810 default :
811 $status = 'publish';
812 break;
813 }
814 return $status;
815 }
816
817 /**
818 * Translate the topic sticky status type from Drupal v7.x numerics to WordPress's strings.
819 *
820 * @param int $status Drupal v7.x numeric forum type
821 * @return string WordPress safe
822 */
823 public function callback_sticky_status( $status = 0 ) {
824 switch ( $status ) {
825 case 1 :
826 $status = 'sticky'; // Drupal Sticky 'topic_sticky = 1'
827 break;
828
829 case 0 :
830 default :
831 $status = 'normal'; // Drupal Normal Topic 'sticky = 0'
832 break;
833 }
834 return $status;
835 }
836
837 /**
838 * Verify the topic/reply count.
839 *
840 * @param int $count Drupal v7.x topic/reply counts
841 * @return string WordPress safe
842 */
843 public function callback_topic_reply_count( $count = 1 ) {
844 $count = absint( (int) $count - 1 );
845 return $count;
846 }
847 }
848