| 1 | #!/usr/bin/perl |
|---|
| 2 | # |
|---|
| 3 | # This program deals with inserting/extracting text/language data |
|---|
| 4 | # from the database. |
|---|
| 5 | # |
|---|
| 6 | |
|---|
| 7 | use strict; |
|---|
| 8 | use Getopt::Long; |
|---|
| 9 | |
|---|
| 10 | my $opt_help = 0; |
|---|
| 11 | my $opt_local_lang; |
|---|
| 12 | exit 1 unless |
|---|
| 13 | GetOptions( |
|---|
| 14 | "help" => \$opt_help, |
|---|
| 15 | "local-lang=s" => \$opt_local_lang, |
|---|
| 16 | ); |
|---|
| 17 | |
|---|
| 18 | my $mode = shift @ARGV; |
|---|
| 19 | |
|---|
| 20 | help() if $opt_help or not defined $mode; |
|---|
| 21 | |
|---|
| 22 | sub help |
|---|
| 23 | { |
|---|
| 24 | die "Usage: texttool.pl <command> |
|---|
| 25 | |
|---|
| 26 | Where 'command' is one of: |
|---|
| 27 | popstruct Populate lang data from text[-local].dat into db |
|---|
| 28 | poptext Populate text from en.dat, etc into database |
|---|
| 29 | dumptext Dump lang text based on text[-local].dat information |
|---|
| 30 | check Check validity of text[-local].dat files |
|---|
| 31 | wipedb Remove all language/text data from database. |
|---|
| 32 | newitems Search files in htdocs, cgi-bin, & bin and insert |
|---|
| 33 | necessary text item codes in database. |
|---|
| 34 | |
|---|
| 35 | Optionally: |
|---|
| 36 | --local-lang=.. If given, works on local site files too |
|---|
| 37 | |
|---|
| 38 | "; |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | ## make sure $LJHOME is set so we can load & run everything |
|---|
| 42 | unless (-d $ENV{'LJHOME'}) { |
|---|
| 43 | die "LJHOME environment variable is not set, or is not a directory.\n". |
|---|
| 44 | "You must fix this before you can run this database update script."; |
|---|
| 45 | } |
|---|
| 46 | require "$ENV{'LJHOME'}/cgi-bin/ljlib.pl"; |
|---|
| 47 | |
|---|
| 48 | my %dom_id; # number -> {} |
|---|
| 49 | my %dom_code; # name -> {} |
|---|
| 50 | my %lang_id; # number -> {} |
|---|
| 51 | my %lang_code; # name -> {} |
|---|
| 52 | my @lang_domains; |
|---|
| 53 | |
|---|
| 54 | my $set = sub { |
|---|
| 55 | my ($hash, $key, $val, $errmsg) = @_; |
|---|
| 56 | die "$errmsg$key\n" if exists $hash->{$key}; |
|---|
| 57 | $hash->{$key} = $val; |
|---|
| 58 | }; |
|---|
| 59 | |
|---|
| 60 | foreach my $scope ("general", "local") |
|---|
| 61 | { |
|---|
| 62 | my $file = $scope eq "general" ? "text.dat" : "text-local.dat"; |
|---|
| 63 | my $ffile = "$ENV{'LJHOME'}/bin/upgrading/$file"; |
|---|
| 64 | unless (-e $ffile) { |
|---|
| 65 | next if $scope eq "local"; |
|---|
| 66 | die "$file file not found; odd: did you delete it?\n"; |
|---|
| 67 | } |
|---|
| 68 | open (F, $ffile) or die "Can't open file: $file: $!\n"; |
|---|
| 69 | while (<F>) { |
|---|
| 70 | s/\s+$//; s/^\#.+//; |
|---|
| 71 | next unless /\S/; |
|---|
| 72 | my @vals = split(/:/, $_); |
|---|
| 73 | my $what = shift @vals; |
|---|
| 74 | |
|---|
| 75 | # language declaration |
|---|
| 76 | if ($what eq "lang") { |
|---|
| 77 | my $lang = { |
|---|
| 78 | 'scope' => $scope, |
|---|
| 79 | 'lnid' => $vals[0], |
|---|
| 80 | 'lncode' => $vals[1], |
|---|
| 81 | 'lnname' => $vals[2], |
|---|
| 82 | 'parentlnid' => 0, # default. changed later. |
|---|
| 83 | 'parenttype' => 'diff', |
|---|
| 84 | }; |
|---|
| 85 | $lang->{'parenttype'} = $vals[3] if defined $vals[3]; |
|---|
| 86 | if (defined $vals[4]) { |
|---|
| 87 | unless (exists $lang_code{$vals[4]}) { |
|---|
| 88 | die "Can't declare language $lang->{'lncode'} with missing parent language $vals[4].\n"; |
|---|
| 89 | } |
|---|
| 90 | $lang->{'parentlnid'} = $lang_code{$vals[4]}->{'lnid'}; |
|---|
| 91 | } |
|---|
| 92 | $set->(\%lang_id, $lang->{'lnid'}, $lang, "Language already defined with ID: "); |
|---|
| 93 | $set->(\%lang_code, $lang->{'lncode'}, $lang, "Language already defined with code: "); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | # domain declaration |
|---|
| 97 | if ($what eq "domain") { |
|---|
| 98 | my $dcode = $vals[1]; |
|---|
| 99 | my ($type, $args) = split(m!/!, $dcode); |
|---|
| 100 | my $dom = { |
|---|
| 101 | 'scope' => $scope, |
|---|
| 102 | 'dmid' => $vals[0], |
|---|
| 103 | 'type' => $type, |
|---|
| 104 | 'args' => $args || "", |
|---|
| 105 | }; |
|---|
| 106 | $set->(\%dom_id, $dom->{'dmid'}, $dom, "Domain already defined with ID: "); |
|---|
| 107 | $set->(\%dom_code, $dcode, $dom, "Domain already defined with parameters: "); |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | # langdomain declaration |
|---|
| 111 | if ($what eq "langdomain") { |
|---|
| 112 | my $ld = { |
|---|
| 113 | 'lnid' => |
|---|
| 114 | (exists $lang_code{$vals[0]} ? $lang_code{$vals[0]}->{'lnid'} : |
|---|
| 115 | die "Undefined language: $vals[0]\n"), |
|---|
| 116 | 'dmid' => |
|---|
| 117 | (exists $dom_code{$vals[1]} ? $dom_code{$vals[1]}->{'dmid'} : |
|---|
| 118 | die "Undefined domain: $vals[1]\n"), |
|---|
| 119 | 'dmmaster' => $vals[2] ? "1" : "0", |
|---|
| 120 | }; |
|---|
| 121 | push @lang_domains, $ld; |
|---|
| 122 | } |
|---|
| 123 | } |
|---|
| 124 | close F; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | if ($mode eq "check") { |
|---|
| 128 | print "all good.\n"; |
|---|
| 129 | exit 0; |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | ## make sure we can connect |
|---|
| 133 | my $dbh = LJ::get_dbh("master"); |
|---|
| 134 | my $sth; |
|---|
| 135 | unless ($dbh) { |
|---|
| 136 | die "Can't connect to the database.\n"; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | popstruct() if $mode eq "popstruct"; |
|---|
| 140 | poptext() if $mode eq "poptext"; |
|---|
| 141 | dumptext() if $mode eq "dumptext"; |
|---|
| 142 | popstruct() if $mode eq "dump"; |
|---|
| 143 | newitems() if $mode eq "newitems"; |
|---|
| 144 | wipedb() if $mode eq "wipedb"; |
|---|
| 145 | help(); |
|---|
| 146 | |
|---|
| 147 | sub wipedb |
|---|
| 148 | { |
|---|
| 149 | $dbh->do("DELETE FROM ml_$_") |
|---|
| 150 | foreach (qw(domains items langdomains langs latest text)); |
|---|
| 151 | exit 0; |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | sub popstruct |
|---|
| 155 | { |
|---|
| 156 | foreach my $l (values %lang_id) { |
|---|
| 157 | print "Inserting language: $l->{'lnname'}\n"; |
|---|
| 158 | $dbh->do("INSERT INTO ml_langs (lnid, lncode, lnname, parenttype, parentlnid) ". |
|---|
| 159 | "VALUES (" . join(",", map { $dbh->quote($l->{$_}) } qw(lnid lncode lnname parenttype parentlnid)) . ")"); |
|---|
| 160 | die "Error: " . $dbh->errstr if $dbh->err; |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | foreach my $d (values %dom_id) { |
|---|
| 164 | print "Inserting domain: $d->{'type'}\[$d->{'args'}\]\n"; |
|---|
| 165 | $dbh->do("INSERT INTO ml_domains (dmid, type, args) ". |
|---|
| 166 | "VALUES (" . join(",", map { $dbh->quote($d->{$_}) } qw(dmid type args)) . ")"); |
|---|
| 167 | die "Error: " . $dbh->errstr if $dbh->err; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | print "Inserting language domains ...\n"; |
|---|
| 171 | foreach my $ld (@lang_domains) { |
|---|
| 172 | $dbh->do("INSERT IGNORE INTO ml_langdomains (lnid, dmid, dmmaster) VALUES ". |
|---|
| 173 | "(" . join(",", map { $dbh->quote($ld->{$_}) } qw(lnid dmid dmmaster)) . ")"); |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | print "All done.\n"; |
|---|
| 177 | exit 0; |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | sub poptext |
|---|
| 181 | { |
|---|
| 182 | foreach my $lang (keys %lang_code) |
|---|
| 183 | { |
|---|
| 184 | print "$lang\n"; |
|---|
| 185 | my $l = $lang_code{$lang}; |
|---|
| 186 | open (D, "$ENV{'LJHOME'}/bin/upgrading/${lang}.dat") |
|---|
| 187 | or die "Can't find $lang.dat\n"; |
|---|
| 188 | my $lnum = 0; |
|---|
| 189 | my ($code, $text); |
|---|
| 190 | while (my $line = <D>) { |
|---|
| 191 | $lnum++; |
|---|
| 192 | if ($line =~ /^(\S+?)=(.*)/) { |
|---|
| 193 | ($code, $text) = ($1, $2); |
|---|
| 194 | } elsif ($line =~ /^(\S+?)\<\<\s*$/) { |
|---|
| 195 | ($code, $text) = ($1, ""); |
|---|
| 196 | while (<D>) { |
|---|
| 197 | last if $_ eq ".\n"; |
|---|
| 198 | s/^\.//; |
|---|
| 199 | $text .= $_; |
|---|
| 200 | } |
|---|
| 201 | } elsif ($line =~ /\S/) { |
|---|
| 202 | die "$lang.dat:$lnum: Bogus format.\n"; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | my $qcode = $dbh->quote($code); |
|---|
| 206 | my $exists = $dbh->selectrow_array("SELECT COUNT(*) FROM ml_latest l, ml_items i ". |
|---|
| 207 | "WHERE l.dmid=1 AND i.dmid AND i.itcode=$qcode AND ". |
|---|
| 208 | "i.itid=l.itid AND l.lnid=$l->{'lnid'}"); |
|---|
| 209 | if (! $exists) { |
|---|
| 210 | print " adding: $code = "; |
|---|
| 211 | my $res = LJ::Lang::set_text($dbh, 1, $lang, $code, $text); |
|---|
| 212 | print "$res\n"; |
|---|
| 213 | } |
|---|
| 214 | } |
|---|
| 215 | close D; |
|---|
| 216 | } |
|---|
| 217 | exit 0; |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | sub dumptext |
|---|
| 221 | { |
|---|
| 222 | foreach my $lang (keys %lang_code) |
|---|
| 223 | { |
|---|
| 224 | print "$lang\n"; |
|---|
| 225 | my $l = $lang_code{$lang}; |
|---|
| 226 | open (D, ">$ENV{'LJHOME'}/bin/upgrading/${lang}.dat") |
|---|
| 227 | or die "Can't open $lang.dat\n"; |
|---|
| 228 | my $sth = $dbh->prepare("SELECT i.itcode, t.text FROM ". |
|---|
| 229 | "ml_items i, ml_latest l, ml_text t ". |
|---|
| 230 | "WHERE l.lnid=$l->{'lnid'} AND l.dmid=1 ". |
|---|
| 231 | "AND i.dmid=1 AND l.itid=i.itid AND ". |
|---|
| 232 | "t.dmid=1 AND t.txtid=l.txtid AND ". |
|---|
| 233 | # only export mappings that aren't inherited: |
|---|
| 234 | "t.lnid=$l->{'lnid'} ". |
|---|
| 235 | "ORDER BY i.itcode"); |
|---|
| 236 | $sth->execute; |
|---|
| 237 | while (my ($itcode, $text) = $sth->fetchrow_array) { |
|---|
| 238 | print "$itcode: $text\n"; |
|---|
| 239 | } |
|---|
| 240 | close D; |
|---|
| 241 | } |
|---|
| 242 | exit 1; |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | sub newitems |
|---|
| 246 | { |
|---|
| 247 | my $top = $ENV{'LJHOME'}; |
|---|
| 248 | my @files; |
|---|
| 249 | push @files, qw(htdocs cgi-bin bin); |
|---|
| 250 | my %items; # $scope -> $key -> 1; |
|---|
| 251 | print "Searching htdocs/cgi-bin/bin for referenced text codes...\n"; |
|---|
| 252 | while (@files) |
|---|
| 253 | { |
|---|
| 254 | my $file = shift @files; |
|---|
| 255 | my $ffile = "$top/$file"; |
|---|
| 256 | next unless -e $ffile; |
|---|
| 257 | if (-d $ffile) { |
|---|
| 258 | opendir (MD, $ffile) or die "Can't open $file"; |
|---|
| 259 | while (my $f = readdir(MD)) { |
|---|
| 260 | next if $f eq "." || $f eq ".." || |
|---|
| 261 | $f =~ /^\.\#/ || $f =~ /(\.png|\.gif|~|\#)$/; |
|---|
| 262 | unshift @files, "$file/$f"; |
|---|
| 263 | } |
|---|
| 264 | closedir MD; |
|---|
| 265 | } |
|---|
| 266 | if (-f $ffile) { |
|---|
| 267 | my $scope = "local"; |
|---|
| 268 | $scope = "general" if -e "$top/cvs/livejournal/$file"; |
|---|
| 269 | |
|---|
| 270 | open (F, $ffile) or die "Can't open $file"; |
|---|
| 271 | my $line = 0; |
|---|
| 272 | while (<F>) { |
|---|
| 273 | $line++; |
|---|
| 274 | while (/BML::ml\([\"\'](.+?)[\"\']/g) { |
|---|
| 275 | $items{$scope}->{$1} = 1; |
|---|
| 276 | } |
|---|
| 277 | while (/\(=_ML\s+(.+?)\s+_ML=\)/g) { |
|---|
| 278 | my $code = $1; |
|---|
| 279 | if ($code =~ /^\./ && $file =~ m!^htdocs/!) { |
|---|
| 280 | $code = "$file$code"; |
|---|
| 281 | $code =~ s!^htdocs!!; |
|---|
| 282 | } |
|---|
| 283 | $items{$scope}->{$code} = 1; |
|---|
| 284 | } |
|---|
| 285 | } |
|---|
| 286 | close F; |
|---|
| 287 | } |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | printf(" %d general and %d local found.\n", |
|---|
| 291 | scalar keys %{$items{'general'}}, |
|---|
| 292 | scalar keys %{$items{'local'}}); |
|---|
| 293 | |
|---|
| 294 | my $register_code = sub |
|---|
| 295 | { |
|---|
| 296 | my $it = shift; |
|---|
| 297 | my $qcode = $dbh->quote($it); |
|---|
| 298 | $dbh->do("INSERT INTO ml_items (dmid, itid, itcode) ". |
|---|
| 299 | "VALUES (1, NULL, $qcode)"); |
|---|
| 300 | my $itid; |
|---|
| 301 | if ($dbh->err) { |
|---|
| 302 | $itid = $dbh->selectrow_array("SELECT itid FROM ml_items WHERE ". |
|---|
| 303 | "dmid=1 AND itcode=$qcode"); |
|---|
| 304 | } else { |
|---|
| 305 | $itid = $dbh->{'mysql_insertid'}; |
|---|
| 306 | } |
|---|
| 307 | unless ($itid) { die "Couldn't register code: $it\n"; } |
|---|
| 308 | return $itid; |
|---|
| 309 | }; |
|---|
| 310 | |
|---|
| 311 | my $set_text = sub |
|---|
| 312 | { |
|---|
| 313 | my ($itid, $lnid, $text) = @_; |
|---|
| 314 | my $qbogustext = $dbh->quote($text); |
|---|
| 315 | $dbh->do("INSERT INTO ml_text (dmid, txtid, lnid, itid, text, userid) VALUES ". |
|---|
| 316 | "(1, NULL, $lnid, $itid, $qbogustext, 0)"); |
|---|
| 317 | my $txtid = $dbh->{'mysql_insertid'}; |
|---|
| 318 | unless ($txtid) { die "Couldn't register bogus text: $text\n"; } |
|---|
| 319 | $dbh->do("INSERT INTO ml_latest (lnid, dmid, itid, chgtime, txtid, version) VALUES ". |
|---|
| 320 | "($lnid, 1, $itid, '0000-00-00', $txtid, 0)"); |
|---|
| 321 | }; |
|---|
| 322 | |
|---|
| 323 | # [ General ] |
|---|
| 324 | my %e_general; # code -> 1 |
|---|
| 325 | print "Checking which general items already exist in database...\n"; |
|---|
| 326 | my $sth = $dbh->prepare("SELECT i.itcode FROM ml_items i, ml_latest l WHERE ". |
|---|
| 327 | "l.dmid=1 AND l.lnid=1 AND i.dmid=1 AND i.itid=l.itid"); |
|---|
| 328 | $sth->execute; |
|---|
| 329 | while (my $it = $sth->fetchrow_array) { $e_general{$it} = 1; } |
|---|
| 330 | printf(" %d found\n", scalar keys %e_general); |
|---|
| 331 | foreach my $it (keys %{$items{'general'}}) { |
|---|
| 332 | next if exists $e_general{$it}; |
|---|
| 333 | my $itid = $register_code->($it); |
|---|
| 334 | print "Added general: $it ($itid)\n"; |
|---|
| 335 | $set_text->($itid, 1, "[no text: $it]"); |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | if ($opt_local_lang) { |
|---|
| 339 | my $ll = $lang_code{$opt_local_lang}; |
|---|
| 340 | die "Bogus --local-lang argument\n" unless $ll; |
|---|
| 341 | die "Local-lang '$ll->{'lncode'}' parent isn't 'en'\n" |
|---|
| 342 | unless $ll->{'parentlnid'} == 1; |
|---|
| 343 | print "Checking which local items already exist in database...\n"; |
|---|
| 344 | |
|---|
| 345 | my %e_local; |
|---|
| 346 | $sth = $dbh->prepare("SELECT i.itcode FROM ml_items i, ml_latest l WHERE ". |
|---|
| 347 | "l.dmid=1 AND l.lnid=$ll->{'lnid'} AND i.dmid=1 AND i.itid=l.itid"); |
|---|
| 348 | $sth->execute; |
|---|
| 349 | while (my $it = $sth->fetchrow_array) { $e_local{$it} = 1; } |
|---|
| 350 | printf(" %d found\n", scalar keys %e_local); |
|---|
| 351 | foreach my $it (keys %{$items{'local'}}) { |
|---|
| 352 | next if exists $e_general{$it}; |
|---|
| 353 | next if exists $e_local{$it}; |
|---|
| 354 | my $itid = $register_code->($it); |
|---|
| 355 | print "Added local: $it ($itid)\n"; |
|---|
| 356 | $set_text->($itid, $ll->{'lnid'}, "[no text: $it]"); |
|---|
| 357 | } |
|---|
| 358 | } |
|---|
| 359 | |
|---|
| 360 | #use Data::Dumper; |
|---|
| 361 | #print Dumper(\%items); |
|---|
| 362 | |
|---|
| 363 | exit 0; |
|---|
| 364 | } |
|---|