From 72a727c63e4afe8567bb9d6eef804a85c9a735c3 Mon Sep 17 00:00:00 2001 From: Gerhard Gonter <ggonter@gmail.com> Date: Sun, 6 Apr 2014 09:10:49 +0200 Subject: [PATCH] syncing project members takes form --- lib/Redmine/DB/CTX.pm | 286 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 lib/Redmine/DB/CTX.pm diff --git a/lib/Redmine/DB/CTX.pm b/lib/Redmine/DB/CTX.pm new file mode 100644 index 0000000..ef9ae6a --- /dev/null +++ b/lib/Redmine/DB/CTX.pm @@ -0,0 +1,286 @@ + +package Redmine::DB::CTX; + +use strict; +use parent 'Redmine::DB'; + +sub sync_project +{ + my $ctx= shift; + my $sp_id= shift; + my $dp_id= shift; + + $ctx->sync_project_users ($sp_id, $dp_id); +} + +=head2 $context->translate ($table_name, $src_id) + +=cut + +sub translate +{ + my $ctx= shift; + my $table_name= shift; + my $src_id= shift; + + unless (defined ($src_id)) + { + print "TRANSLATE: table_name=[$table_name] src_id=[undef] tlt=[undef]\n"; + return undef; + } + + # fetch all known translations, they are stored in the destionation's database + my $t; + unless (defined ($t= $ctx->{'tlt'})) + { + $t= $ctx->{'tlt'}= {}; + my $d= $ctx->{'dst'}->get_all_x ('syncs', [ 'sync_context_id=?', $ctx->{'ctx_id'} ] ); + # print "d: ", main::Dumper ($d); + + foreach my $id (keys %$d) + { + my $dd= $d->{$id}; + my ($tn, $si, $di, $st, $sd)= map { $dd->{$_} } qw(table_name src_id dst_id status sync_date); + $t->{$tn}->{$si}= [ $di, $st, $sd ]; + } + } + + if (exists ($t->{$table_name}->{$src_id})) + { + my $x= $t->{$table_name}->{$src_id}; + print "TRANSLATE: table_name=[$table_name] src_id=[$src_id] tlt=[",join(',',@$x),"]\n"; + return (wantarray) ? @$x : $x->[0]; + } + + return undef; +} + +sub store_translate +{ + my $ctx= shift; + my $table_name= shift; + my $src_id= shift; + my $dst_id= shift; + + my $dbh= $ctx->{'dst'}->connect(); + return undef unless (defined ($dbh)); + + my $ssi= "INSERT INTO syncs (sync_context_id, table_name, src_id, dst_id, sync_date, status) VALUES (?,?,?,?,now(),2)"; + print "ssi=[$ssi]\n"; + my $sth= $dbh->prepare($ssi); + my @vals= ($ctx->{'ctx_id'}, $table_name, $src_id, $dst_id); + print "vals: ", join (',', @vals), "\n"; + $sth->execute(@vals); + $sth->finish(); +} + +sub sync_project_users +{ + my $ctx= shift; + my $sp_id= shift; + my $dp_id= shift; + + my ($ctx_id, $src, $dst)= map { $ctx->{$_} } qw(ctx_id src dst); + + # pcx means something like "project context"; TODO: change that name if a better one comes up... + my $s_pcx= $src->pcx_members ($sp_id); + my $d_pcx= $dst->pcx_members ($dp_id); + # print "keys src: ", join (' ', keys %$src), "\n"; + # print "pcx: ", main::Dumper ($pcx); + + my ($s_members, $s_users)= map { $s_pcx->{$_} } qw(members users); + my ($d_members, $d_users)= map { $d_pcx->{$_} } qw(members users); + + # verbose Redmine::DB::MySQL (1); + + my @s_member_ids= (keys %$s_members); + foreach my $s_member_id (@s_member_ids) + { + my $s_member= $s_members->{$s_member_id}; + my $s_user_id= $s_member->{'user_id'}; + my $s_user= $s_users->{$s_user_id}; +# next unless ($s_user->{'type'} eq 'Group'); + + print "s_member: ", main::Dumper ($s_member); + my $d_user_id= $ctx->sync_user ($s_user_id, $s_user); + + my ($d_member_id, $d_status, $d_sync_date)= $ctx->translate ('members', $s_member_id); + print "s_member_id=[$s_member_id] d_member=[$d_member_id] d_status=[$d_status] d_sync_date=[$d_sync_date]\n"; + + unless (defined ($d_member_id)) + { + my $d_member= + { + 'user_id' => $d_user_id, + 'project_id' => $dp_id, + 'created_on' => $s_member->{'created_on'}, + 'mail_notification' => $s_member->{'mail_notification'}, + }; + + $d_member_id= $dst->insert ('members', $d_member); + $ctx->store_translate('members', $s_member_id, $d_member_id); + } + } + +=begin comment + +sync member_roles + +mysql> desc member_roles; ++----------------+---------+------+-----+---------+----------------+ +| Field | Type | Null | Key | Default | Extra | ++----------------+---------+------+-----+---------+----------------+ +| id | int(11) | NO | PRI | NULL | auto_increment | +| member_id | int(11) | NO | MUL | NULL | | +| role_id | int(11) | NO | MUL | NULL | | +| inherited_from | int(11) | YES | | NULL | | ++----------------+---------+------+-----+---------+----------------+ + +inherited_from points back to member_roles.id and that record might not be synced when the current record is processed. + +=end comment +=cut + + my $in= 'member_id IN ('. join(',', map { '?' } @s_member_ids) . ')'; + + my $s_mr_hash= $src->get_all_x ('member_roles', [ $in, @s_member_ids ]); + # print "s_mr_hash: ", main::Dumper ($s_mr_hash); + my @s_mr_ids= sort { $a <=> $b } keys %$s_mr_hash; # maybe ordering helps + print "s_mr_ids: [", join (',', @s_mr_ids), "]\n"; + print "\n\n", '='x72, "MEMBER_ROLE processing\n", '-'x72, "\n"; + MEMBER_ROLE: while (@s_mr_ids) + { + my $s_mr_id= shift @s_mr_ids; + my $s_mr= $s_mr_hash->{$s_mr_id}; + print "member_role: ", main::Dumper ($s_mr); + my $d_mr_id= $ctx->translate('member_roles', $s_mr_id); + + if (defined ($d_mr_id)) + { # this member_role was already synced, so we can skip it. + print "member_role already synced: $s_mr_id -> $d_mr_id\n"; + next MEMBER_ROLE; + } + + # if the role is not yet know, we need to pull it over + my $s_role_id= $s_mr->{'role_id'}; + my $d_role_id= $ctx->translate ('roles', $s_role_id); + $d_role_id= $ctx->sync_role ($s_role_id) unless (defined ($d_role_id)); + + # users can inherit their roles from a group; + # inherited_from is that group's id from the member_roles-table + my $s_inh_from= $s_mr->{'inherited_from'}; + print "s_inh_from=[$s_inh_from] s_mr: ", main::Dumper ($s_mr); + + my $d_inh_from; + if (defined ($s_inh_from)) + { + $d_inh_from= $ctx->translate ('member_roles', $s_inh_from); + unless (defined ($d_inh_from)) + { + unshift (@s_mr_ids, $s_inh_from); + print "QQQ: inherited_from member_record [$s_inh_from] not yet know, inserted at the head of the queue!\n"; + next MEMBER_ROLE; + } + } + + my $d_member_id= $ctx->translate ('members', $s_mr->{'member_id'}); + unless (defined ($d_member_id)) + { + print "ATTN: member not yet synced! member_id=[", $s_mr->{'member_id'}, "]; skipping member_role!\n"; + next MEMBER_ROLE; + } + + my %d_mr= + ( + 'member_id' => $d_member_id, + 'role_id' => $d_role_id, + ); + $d_mr{'inherited_from'}= $d_inh_from if (defined ($d_inh_from)); + + print "new member_role record: ", main::Dumper (\%d_mr); + + $d_mr_id= $ctx->{'dst'}->insert ('member_roles', \%d_mr); + $ctx->store_translate('member_roles', $s_mr_id, $d_mr_id); + } + +} + +sub sync_role +{ + my $ctx= shift; + my $s_role_id= shift; + + my $res= $ctx->{'src'}->get_all_x ('roles', [ 'id=?', $s_role_id ]); + return undef unless (defined ($res)); + print "sync_role: s_role_id=[$s_role_id] res: ", main::Dumper ($res); + + my $s_role= $res->{$s_role_id}; + my %d_role= %$s_role; + delete ($d_role{'id'}); + + my $d_role_id= $ctx->{'dst'}->insert ('roles', \%d_role); + $ctx->store_translate('roles', $s_role_id, $d_role_id); + + $d_role_id; +} + +sub sync_user +{ + my $ctx= shift; + my $s_user_id= shift; + my $s_user= shift; + + unless (defined ($s_user)) + { + my $res= $ctx->{'src'}->get_all_x ('users', [ 'id=?', $s_user_id ]); + return undef unless (defined ($res)); + + $s_user= $res->{$s_user_id}; + } + + print "s_user: ", main::Dumper ($s_user); + + my ($d_user_id, $d_status, $d_sync_date)= $ctx->translate ('users', $s_user_id); + print "s_user_id=[$s_user_id] d_user_id=[$d_user_id] d_status=[$d_status] d_sync_date=[$d_sync_date]\n"; + + unless (defined ($d_user_id)) + { + my $d_user= $ctx->clone_user ($s_user); + print "cloned_user: ", main::Dumper ($d_user); + + $d_user_id= $ctx->{'dst'}->insert ('users', $d_user); + $ctx->store_translate('users', $s_user_id, $d_user_id); + } + + $d_user_id; +} + +sub clone_user +{ + my $ctx= shift; + my $src= shift; + + my %user= %$src; + $user{'auth_source_id'}= $ctx->translate ('auth_sources', $src->{'auth_source_id'}); + delete($user{'id'}); + \%user; +} + +sub sync_wiki +{ + +=begin comment + +sync wiki + # my $pcx= $src->pcx_wiki ($proj_id); + + # print "pcx: ", main::Dumper ($pcx); + # print "src: ", main::Dumper ($src); + +=end comment +=cut + +} + +1; + -- GitLab