* Add enclair_db option to UserDB.pm. Allows logging of enclair password
[interchange.git] / code / UI_Tag / user_merge.tag
1 # Copyright 2005-2009 Interchange Development Group and others
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.  See the LICENSE file for details.
7
8 # $Id: user_merge.tag,v 1.4 2009-05-20 23:37:27 pajamian Exp $
9
10 UserTag user-merge Order from to
11 UserTag user-merge addAttr
12 UserTag user-merge Description Merges users based on order number or username
13 UserTag user-merge Routine <<EOR
14 sub {
15         my ($from, $to, $opt) = @_;
16
17 #::logDebug("Called user merge");
18         use vars qw/$Tag $CGI/;
19
20         my $err = sub {
21                 my $msg = errmsg(@_);
22                 logError($msg);
23                 $Tag->error({ name => 'order merge', set => $msg });
24                 return undef;
25         };
26
27         unless($Vend::admin) {
28                 return $err->("Only admin can merge records.");
29         }
30
31         unless($Vend::superuser) {
32                 return $err->("Only admin can merge records.")
33                         unless $Tag->if_mm('advanced', 'merge_users');
34         }
35
36         $from ||= $CGI->{item_id};
37         $to ||= $CGI->{item_radio};
38         my $table = $opt->{table} || $CGI->{mv_data_table};
39
40
41         if($opt->{from_user} or $opt->{from_order}) {
42                 ## We are told what to do
43         }
44         elsif($table eq 'userdb') {
45                 $opt->{from_user} = 1;
46         }
47         elsif ($table eq 'transactions') {
48                 $opt->{from_order} = 1;
49         }
50         else {
51                 return $err->("Unable to determine what to do, no table or from_user...");
52         }
53
54         my $ufield = $opt->{user_field} || 'username';
55         my $ofield = $opt->{order_field} || 'order_number';
56
57         my $utab = $opt->{user_table} || $::Variable->{UI_USER_MERGE_USER_TABLE} || 'userdb';
58         my $ttabs = $opt->{merge_tables} || $::Variable->{UI_USER_MERGE_TABLES} || 'transactions orderline';
59
60         my @ttab = grep /\w/, split /[\s,\0]+/, $ttabs;
61
62         my %kfield;
63         my %sth;
64         my %dbh;
65         my %dbr;
66         my %query;
67
68         for(@ttab) {
69                 my ($t, $f) = split /[=:]+/, $_, 2;
70                 $_ = $t;
71                 $kfield{$t} = $f || $ufield;
72         }
73
74         my $tdb = dbref($ttab[0])
75                 or return $err->("No %s table.", $ttab[0]);
76         my $udb = dbref($utab)
77                 or return $err->("No %s table.", $utab);
78
79         for(@ttab) {
80                 my $db = $dbr{$_} = dbref($_)
81                         or return $err->("Unable to open '%s' table for merge.", $_);
82                 my $dbh = $dbh{$_} = $db->dbh();
83                 $query{$_} = "update $_ set $kfield{$_} = ? where $kfield{$_} = ?"; 
84                 $sth{$_} = $dbh->prepare($query{$_}) 
85                         or return $err->("Unable to prepare statement '%s' for merge.", $query{$_});
86         }
87
88         my $to_user = $to;
89
90         if($opt->{from_order}) {
91                 $to_user = $tdb->field($to, $ufield);
92         }
93
94         my $urec = $udb->row_hash($to_user)
95                 or return $err->("%s does not exist, cannot merge to that user.", $to_user);
96
97         my @from;
98
99         if(ref($from) eq 'ARRAY') {
100                 @from = @$from;
101         }
102         else {
103                 @from = split /\0/, $from;
104         }
105
106         my %from_user;
107
108         if($opt->{from_order}) {
109                 my @to;
110                 for(@from) {
111                         my $okey = $tdb->foreign($_, $ofield);
112                         my $user = $tdb->field($okey, $ufield);
113                         push @to, $user;
114                 }
115                 @from = @to;
116         }
117
118         for(@from) {
119                 next if $_ eq $to_user;
120                 unless($from_user{$_} or $udb->field($_, 'username')) {
121                         $err->("User '%s' does not exist.", $_);
122                         next;
123                 }
124                 $from_user{$_}++;
125         }
126
127         my $cart_hash = string_to_ref($urec->{carts});
128         my $carts_changed;
129
130         my @users = sort keys %from_user;
131
132         my @record;
133         @record = @users;
134
135         my $logfile = $opt->{logfile} || 'logs/merged_users.log';
136         my $done_one;
137         my $save_rec;
138
139         for my $user (@users) {
140                 $Tag->log({ type => 'text', file => $logfile, body => $Tag->time() . "\n" } )
141                         unless $done_one++;
142
143                 my $from_urec = $udb->row_hash($user);
144
145                 # If there's a user_merge specialsub run it here
146                 if (my $subname = $Vend::Cfg->{SpecialSub}{user_merge}) {
147                         my $sub = $Vend::Cfg->{Sub}{$subname} || $Global::GlobalSub->{$subname};
148                         my $status;
149                         eval { $status = $sub->($user, $from_urec, $to_user, $urec, $udb, $tdb) };
150                         if ($@) {
151                                 ::logError("Error running %s subroutine %s: %s", 'user_merge', $subname, $@);
152                         }
153
154                         elsif ($status) {
155                                 # Skip further processing of this user
156                                 next;
157                         }
158
159                         else {
160                                 $save_rec = 1;
161                         }
162                 }
163
164                 for(@ttab) {
165                         $sth{$_}->execute($to_user, $user)
166                                 or $err->("%s update failed: %s", $_, $dbh{$_}->errstr);
167                         my $o = $query{$_};
168                         $o =~ s/\?/$to_user/;
169                         $o =~ s/\?/$user/;
170                         push @record, $o;
171                 }
172
173                 my $chash = string_to_ref($from_urec->{carts});
174                 if(ref $chash) {
175                         for(keys %$chash) {
176                                 if($cart_hash->{$_}) {
177                                         $Tag->log({ type => 'text', file => $logfile, body => "unable to merge cart=$_ (already exists). Contents=$from_urec->{carts}\n"} );
178                                 }
179                                 else {
180                                         $cart_hash->{$_} = $chash->{$_};
181                                         $carts_changed++;
182                                 }
183                         }
184                 }
185                 my $ustring = ::uneval($from_urec);
186                 $Tag->log({ type => 'text', file => $logfile, body => "delete user $user=$ustring\n"} );
187                 $udb->delete_record($user)
188                         unless $opt->{no_delete};
189                 push @record, "delete user $user" unless $opt->{no_delete};
190         }
191
192         if($carts_changed) {
193                 if ($save_rec) {
194                         $urec->{carts} = ::uneval($cart_hash);
195                 }
196
197                 else {
198                         $udb->set_field($to, 'carts', ::uneval($cart_hash));
199                 }
200         }
201
202         if ($save_rec) {
203                 delete $urec->{$udb->[$Vend::Table::DBI::KEY]};
204                 $udb->set_slice($to, $urec);
205         }
206
207         push @record, '';
208
209         $Tag->log({ type => 'text', file => $logfile, body => join("\n", @record)} );
210         ::logDebug(join("\n", @record)) if $opt->{debug};
211         return 1 unless $opt->{hide};
212         return '';
213 }
214 EOR
215