230 lines
9.1 KiB
PHP
230 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\MigrationUserPlan;
|
|
use App\Services\XenforoApiService;
|
|
use Illuminate\Console\Attributes\Description;
|
|
use Illuminate\Console\Attributes\Signature;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Str;
|
|
|
|
#[Signature('migrate:users:execute {--limit=} {--wp-uploads-path=} {--xf-data-path=}')]
|
|
#[Description("Migrate all users in migration table to XenForo.")]
|
|
class MigrateUsersExecute extends Command
|
|
{
|
|
|
|
private function extractWpRole(?string $serialized): string
|
|
{
|
|
$caps = $serialized ? @unserialize($serialized) : null;
|
|
return is_array($caps) ? (array_key_first($caps) ?: 'contributor') : 'contributor';
|
|
}
|
|
|
|
private function extractWpAvatarPath(?string $serialized, string $wpUploadsPath ): ?string
|
|
{
|
|
$data = $serialized ? @unserialize($serialized) : null;
|
|
$mediaId = $data['media_id'] ?? null;
|
|
if( !$mediaId )
|
|
return null;
|
|
|
|
$relative = DB::connection('old_wp')
|
|
->table('postmeta')
|
|
->where('post_id', $mediaId)
|
|
->where('meta_key', '_wp_attached_file')
|
|
->value('meta_value');
|
|
|
|
if( !$relative )
|
|
return null;
|
|
|
|
$path = rtrim($wpUploadsPath, '/') . '/' . $relative;
|
|
return is_file($path) ? $path : null;
|
|
}
|
|
|
|
private function extractXfAvatarPath(int $userId, string $xfDataPath, string $size = 'o' ): ?string
|
|
{
|
|
$path = sprintf('%s/avatars/%s/%d/%d.jpg',
|
|
rtrim($xfDataPath, '/'), $size, (int) floor($userId / 1000), $userId
|
|
);
|
|
return is_file($path) ? $path : null;
|
|
}
|
|
|
|
private function extractXfBannerPath(int $userId, string $xfDataPath, string $size = 'l' ): ?string
|
|
{
|
|
$path = sprintf('%s/profile_banners/%s/%d/%d.jpg',
|
|
rtrim($xfDataPath, '/'), $size, (int) floor($userId / 1000), $userId
|
|
);
|
|
return is_file($path) ? $path : null;
|
|
}
|
|
|
|
private function buildUserInfos( MigrationUserPlan $plan, array $roleMap, array $groupMap, string $wpUploadsPath, string $xfDataPath ): array
|
|
{
|
|
$infos = [
|
|
'username' => $plan->xf_username ?: $plan->wp_username,
|
|
'email' => $plan->email,
|
|
'user_group_id' => null,
|
|
'password' => Str::uuid(), // Used for API verifications.
|
|
'xf_user_id' => 0,
|
|
'avatar_path' => null,
|
|
'banner_path' => null,
|
|
'register_date' => null,
|
|
'profile' => [
|
|
'about' => null,
|
|
'website' => null,
|
|
],
|
|
'source_real_password' => null,
|
|
'wp_password' => null,
|
|
'xf_scheme_class' => null,
|
|
'xf_password_data' => null,
|
|
];
|
|
|
|
if( $plan->wp_user_id )
|
|
{
|
|
$wp = DB::connection('old_wp')
|
|
->table('users')
|
|
->leftJoin('usermeta as m1', fn( $j ) => $j->on('users.ID', '=', 'm1.user_id')->where('m1.meta_key', '=', 'description') )
|
|
->leftJoin('usermeta as m2', fn( $j ) => $j->on('users.ID', '=', 'm2.user_id')->where('m2.meta_key', '=', 'wp_capabilities') )
|
|
->leftJoin('usermeta as m3', fn( $j ) => $j->on('users.ID', '=', 'm3.user_id')->where('m3.meta_key', '=', 'simple_local_avatar') )
|
|
->where('users.ID', '=', $plan->wp_user_id )
|
|
->select('m1.meta_value as description', 'm2.meta_value as capabilities', 'm3.meta_value as avatar_meta', 'users.user_url as website', 'users.user_pass as password', 'users.user_registred' )
|
|
->first();
|
|
|
|
$infos['register_date'] = $wp->user_registred ? strtotime($wp->user_registred) : null;
|
|
$infos['profile']['about'] = $wp->description;
|
|
$infos['profile']['website'] = $wp->website;
|
|
$role = $this->extractWpRole($wp->capabilities);
|
|
$infos['user_group_id'] = $roleMap[$role] ?? $roleMap['contributor'];
|
|
|
|
if( $url = $this->extractWpAvatarPath($wp->avatar_meta, $wpUploadsPath)){
|
|
$infos['avatar_path'] = $url;
|
|
}
|
|
|
|
$infos['source_real_password'] = 'wp';
|
|
$infos['wp_password'] = $wp->password;
|
|
}
|
|
|
|
if( $plan->xf_user_id )
|
|
{
|
|
$xf = DB::connection('old_xf')
|
|
->table('user')
|
|
->leftJoin('user_profile', 'user.user_id', '=', 'user_profile.user_id')
|
|
->leftJoin('user_authenticate', 'user.user_id', '=', 'user_authenticate.user_id')
|
|
->where('user.user_id', '=', $plan->xf_user_id)
|
|
->select('user.avatar_date', 'user.user_group_id', 'user.register_date', 'user_profile.about', 'user_profile.website', 'user_profile.banner_date', 'user_authenticate.scheme_class', 'user_authenticate.data')
|
|
->first();
|
|
|
|
if( !$infos['register_date'] && $xf )
|
|
$infos['register_date'] = $xf->register_date ?: null;
|
|
if( !$infos['profile']['about'] && $xf )
|
|
$infos['profile']['about'] = $xf->about ?: null;
|
|
if( !$infos['profile']['website'] && $xf )
|
|
$infos['profile']['website'] = $xf->website ?: null;
|
|
|
|
if( !$plan->wp_user_id ){
|
|
$infos['user_group_id'] = $groupMap[$xf->user_group_id] ?? reset($groupMap);
|
|
}
|
|
$infos['xf_user_id'] = $plan->xf_user_id;
|
|
|
|
if( $infos['avatar_path'] === null && (int) $xf->avatar_date > 0){
|
|
if( $path = $this->extractXfAvatarPath($plan->xf_user_id, $xfDataPath)){
|
|
$infos['avatar_path'] = $path;
|
|
}
|
|
}
|
|
|
|
if( $infos['banner_path'] === null && (int) $xf->banner_date > 0 ){
|
|
if( $path = $this->extractXfBannerPath($plan->xf_user_id, $xfDataPath)){
|
|
$infos['banner_path'] = $path;
|
|
}
|
|
}
|
|
|
|
if( $infos['source_real_password'] === null && $xf->scheme_class && $xf->data ){
|
|
$infos['source_real_password'] = 'xf';
|
|
$infos['xf_scheme_class'] = $xf->scheme_class;
|
|
$infos['xf_password_data'] = $xf->data;
|
|
}
|
|
}
|
|
|
|
return $infos;
|
|
}
|
|
|
|
private function logMap( string $sourceSystem, string $sourceTable, int $sourceId, int $targetId )
|
|
{
|
|
DB::table('migrations_logs')->insert([
|
|
'source_system' => $sourceSystem,
|
|
'source_table' => $sourceTable,
|
|
'source_id' => $sourceId,
|
|
'target_table' => 'xf_user',
|
|
'target_id' => $targetId,
|
|
'status' => 'done',
|
|
'migrated_at' => now(),
|
|
'created_at' => now(),
|
|
'updated_at' => now()
|
|
]);
|
|
}
|
|
|
|
public function handle()
|
|
{
|
|
$wpUploadsPath = $this->option('wp-uploads-path');
|
|
$xfDataPath = $this->option('xf-data-path');
|
|
|
|
if( !$wpUploadsPath || !is_dir($wpUploadsPath) ){
|
|
$this->error('Missing WP Uploads Path');
|
|
return self::FAILURE;
|
|
}
|
|
|
|
if( !$xfDataPath || !is_dir($xfDataPath) ){
|
|
$this->error('Missing XF Data Path');
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$roleMap = json_decode(DB::table('migration_settings')->where('key', 'wp_role_to_xf_group')->value('value'), true);
|
|
$groupMap = json_decode(DB::table('migration_settings')->where('key', 'old_xf_group_to_xf_group')->value('value'), true);
|
|
|
|
if( !$roleMap || !$groupMap ) {
|
|
$this->error('Role map and group map are required.');
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$query = MigrationUserPlan::where('status', 'approved')->whereNull('user_id');
|
|
if( $limit = $this->option('limit') ) {
|
|
$query->limit((int) $limit);
|
|
}
|
|
|
|
$rows = $query->get();
|
|
$this->info("{$rows->count()} accounts will be created on XenForo database !!!.");
|
|
$ok = $this->ask("Write 'ok' if you want to start the migration. Everything else to quit the migration.");
|
|
if( $ok !== 'ok' ) {
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$service = app(XenforoApiService::class);
|
|
|
|
$this->withProgressBar($rows, function($plan) use( $roleMap, $groupMap, $wpUploadsPath, $xfDataPath, $service ) {
|
|
try {
|
|
$infos = $this->buildUserInfos( $plan, $roleMap, $groupMap, $wpUploadsPath, $xfDataPath );
|
|
|
|
[ $userId, $passwordSet ] = $service->_migrateUser( $infos );
|
|
if( !$userId ){
|
|
throw new \RuntimeException("Error when user creation.");
|
|
}
|
|
|
|
MigrationUserPlan::where('id', $plan->id )->update([ 'user_id' => $userId ]);
|
|
|
|
$this->logMap( $plan->wp_user_id ? 'wp' : 'xf', $plan->wp_user_id ? 'wp_users' : 'xf_user', $plan->wp_user_id ?? $plan->xf_user_id, $userId );
|
|
if( $plan->wp_user_id && $plan->xf_user_id ){
|
|
$this->logMap('xf', 'xf_user', $plan->xf_user_id, $userId );
|
|
}
|
|
|
|
|
|
} catch ( \Throwable $e ) {
|
|
Log::error("Unable to create Plan#{$plan->id} user : {$e->getMessage()}");
|
|
}
|
|
});
|
|
|
|
$this->newLine(2);
|
|
$this->info("Process finished.");
|
|
return self::SUCCESS;
|
|
}
|
|
}
|