App-WordPressTools

 view release on metacpan or  search on metacpan

script/wp-tools  view on Meta::CPAN

            }
        };

        my @paths = map { -e "$nice_path/$_" ? shell_quote("$nice_path/$_") : () } @backup_whitelist;
        my $paths = join(' ', @paths);
        my $size = eval { `du -msc $paths` } || '';
        $size =~ s/.*?(\d+)\s+total.*/$1/s;
        if (!$size || $size !~ /^\d+$/) {
            die 'Cannot determine size of WordPress installation';
        }
        if ($args->{max_size} < $size) {
            # try to reduce content that is included in the backup
            my $backupsize = $size;
            for my $type (sort { $skippable->{$b} <=> $skippable->{$a} } keys %$skippable) {
                my $skip_path   = "$args->{'path'}/wp-content/$type";
                my $skip_pathq  = shell_quote($skip_path);
                next if !-d $skip_path;
                $skips->{$type} = $skippable->{$type} || eval { `du -ms $skip_pathq` } || 0;
                $skips->{$type} =~ s/^(\d+).*/$1/s;
                $backupsize -= $skips->{$type};
                last if $backupsize <= $args->{max_size};
            }
            if ($args->{max_size} < $backupsize) {
                die "Cannot backup this WordPress installation because it is too large ($size/$backupsize)";
            }
        }
    }

    ### backup database
    my $result = '';
    my $dump_command = '';

    # we cannot check the database without wp-config.php (credential storage location)
    if (!-f "$nice_path/wp-config.php") {
        $args->{'__skip_database'} = 1;
    }
    else {
        #acceptable failure states for databaseless accounts
        my $no_db_regex = qr/(?:Access denied for user|Unknown MySQL server host|Unknown database.*when selecting the database|is marked as crashed and last \(automatic\?\) repair failed when using LOCK TABLES|Got error: 130: Incorrect file format|Go...
        if ($plus3713) {
            $dump_command = "db export $db_file --add-drop-table 2>&1";
            $result = _run_wpcli($nice_path, $dump_command);
        }
        if ($result =~ $no_db_regex) {
            $args->{'__skip_database'} = 1;
        }
        elsif ($result !~ /^Success/) {
            # wp-cli backup can fail for wp <3.7.13, so try mysqldump instead
            my $wp_configq = shell_quote("$args->{'path'}/wp-config.php");
            my $username = `grep DB_USER     <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
            my $password = `grep DB_PASSWORD <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
            my $database = `grep DB_NAME     <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
            #disallow starting whitespace
            $username =~ s/^[\r\n]+//;
            $password =~ s/^[\r\n]+//;
            $database =~ s/^[\r\n]+//;
            chomp $username;
            chomp $password;
            chomp $database;
            if ($username =~ /[\r\n]/ || $password =~ /[\r\n]/ || $database =~ /[\r\n]/) {
                die "Multiple credentials found in $args->{'path'}/wp-config.php.  Cannot determine which to use.  Backup operation halted.";
            }
            open (my $fh, '>', $defaults_file) or die "Cannot write to $defaults_file: $!";
            close $fh;
            chmod(0600, $defaults_file) or die "Cannot chmod $defaults_file: $!";
            write_text($defaults_file,"[client]\nuser=$username\npassword=$password");
            my $databaseq = shell_quote($database);
            $dump_command = "mysqldump --defaults-file=$defaults_file $databaseq 2>&1 >$db_file";
            my $result = `$dump_command`;
            #acceptable failure states for databaseless accounts
            if ($result =~ $no_db_regex) {
                $args->{'__skip_database'} = 1;
                $db_file = '';
            }
            elsif ($?) {
                die "mysqldump command ($dump_command) failed (exit: $?): $result";
            }
        }
        if (!$args->{'__skip_database'}) {
            if (!-f $db_file || -s $db_file < 1024) {
                die "Aborting because there doesn't seem to be any data to back up (command: $dump_command)";
            }
            open my $db_test, '<', $db_file;
            my $first_line = <$db_test>;
            close $db_test;
            if ($first_line =~ /Usage: mysqldump/) {
                die "Database dump looks like it failed because it contains usage (command: $dump_command)";
            }
        }
    }

    ### backup filesystem
    my $backup_filename = "$args->{backup_dir}/$backup_file";
    mkpath($args->{backup_dir}, {mode => 0750}) if !-d $args->{backup_dir};
    my $exclusions = '';
    for my $type (keys %$skips) {
        if (!$skips->{$type}) {
            delete $skips->{$type};
            next;
        }
        my $spath = "$wpdir/wp-content/$type";
        $spath = "./$spath" if $spath =~ /^-/;
        my $skip_path = shell_quote($spath);
        $exclusions .= " --exclude $skip_path";
    }
    my $inclusions = '';
    for my $file (@backup_whitelist) {
        next if !-e "$args->{'path'}/$file";
        my $apath = "$wpdir/$file";
        $apath = "./$apath" if $apath =~ /^-/;
        my $add_path = shell_quote($apath);
        $inclusions .= " $add_path";
    }
    open(my $fh, '>', $manifest_file) or die "Cannot write to $manifest_file: $!";
    print $fh "version:1\n";
    print $fh "path:$nice_path\n";
    print $fh "time:$time\n";
    print $fh "nodb:1\n" if $args->{'__skip_database'};
    if (scalar keys %$skips) {
        print $fh "skipped:".join(',', keys %$skips)."\n";
    }

script/wp-tools  view on Meta::CPAN


    our $saved_path = $path;
    $saved_path =~ s!/+$!!;
    $saved_path = "${saved_path}.restore";

    if (-d $path) {
        my $saved_pathq = shell_quote($saved_path);
        my $err_out = `cp -al $pathq $saved_pathq 2>&1`;
        if ($?) {
            die "Could not copy $path to $saved_path: $err_out";
        }
    }

    eval {
        my $del  = _find_sed_delimiter($wpdir, $path);
        my $bre  = _bre_quote($wpdir);
        my $repl = _bre_quote($path, '\&');
        if (!$del) {
            die 'No available delimiter found to use with tar -x --transform';
        }
        my $transform = shell_quote("--transform=s${del}^$bre${del}$repl${del}");
        my $restore_cmd = "tar -xzf $backup_fileq --recursive-unlink $transform $wpdirq 2>&1";
        my $restore = `$restore_cmd`;
        if ($?) {
            die "File restore command ($restore_cmd) exited non-zero ($?): $restore";
        }
        if (!-d $path) {
            die "File restore command ($restore_cmd) failed to actually restore files";
        }

        if (!$manifest{'nodb'}) {
            my $transform = "--transform='s/^wp_backup${time}\\.sql\$/$db_file/'";
            my $db_restore = `tar -xzf $backup_fileq $transform wp_backup${time}.sql 2>&1`;
            if ($? || !-f $db_file) {
                die "Cannot extract database restore file ($db_file): $db_restore";
            }
            $wp_cli_path  = "$args->{'wp_cli'} --path=".shell_quote($path);
            $version      = _run_wpcli($path, 'core version');
            chomp $version;
            $canonversion = _canon_ver($version);
            $plus3713     = !!($canonversion ge $canon3713);
            if ($plus3713) {
                my $dbfile = _run_wpcli($path, "db import $db_file --skip-plugins --skip-themes");
            }
            if (!$plus3713 || $?) {
                my $wp_configq = shell_quote("$path/wp-config.php");
                if (!-f "$path/wp-config.php") {
                    die "Missing wp-config.php";
                }
                my $username = `grep DB_USER      <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
                my $password = `grep DB_PASSWORD  <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
                my $database = `grep DB_NAME      <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
                #disallow starting whitespace
                $username =~ s/^[\r\n]+//;
                $password =~ s/^[\r\n]+//;
                $database =~ s/^[\r\n]+//;
                chomp $username;
                chomp $password;
                chomp $database;
                if ($username =~ /[\r\n]/ || $password =~ /[\r\n]/ || $database =~ /[\r\n]/) {
                    die "Multiple credentials found in $args->{'path'}/wp-config.php.  Cannot determine which to use.  Restoration operation halted.";
                }
                open (my $fh, '>', $defaults_file) or die "Cannot write to $defaults_file: $!";
                close $fh;
                chmod(0600, $defaults_file) or die "Cannot chmod $defaults_file: $!";
                write_text($defaults_file,"[client]\nuser=$username\npassword=$password");
                my $databaseq = shell_quote($database);
                `mysql --defaults-file=$defaults_file $databaseq <$db_file`;
                if ($?) {
                    die "Failed to restore database";
                }
            }
        }

        # if we skipped large directories in the backup procedure
        # we must copy them from the installation we are replacing
        my @skipped = split(/,/, $manifest{'skipped'} || '');
        for my $dir (@skipped) {
            my $src = "${saved_path}/wp-content/$dir";
            my $dst = "${path}/wp-content/$dir";
            if (-d $src) {
                rmtree($dst) if -e $dst;
                my $srcq = shell_quote($src);
                my $dstq = shell_quote($dst);
                my $err_out = `cp -al $srcq $dstq 2>&1`;
                if ($?) {
                    die "Could not copy $path to $saved_path: $err_out";
                }
            }
            else {
                # TODO - should this condition be fatal?
                warn "Expected to restore using $dir in installation being replaced, but it is missing";
            }
        }
    };
    if ($@) {
        my $err = $@;
        if (-d $saved_path) {
            my $defunct_path = $path;
            $defunct_path =~ s!/+$!!;
            $defunct_path .= ".defunct_" . time;
            if (rename($path, $defunct_path)) {
                if (!rename($saved_path, $path)) {
                    # this is the sad case
                    $err .= "; also could not move $saved_path back to $path: $!";
                    undef $saved_path;  # do not wipe out if we couldn't fully fix it
                    rename($defunct_path, $path);
                }

                if (-d $defunct_path) {
                    rmtree($defunct_path);
                }
            }
        }
        die $err;
    }

    ### cleanup
    END {
        unlink($db_file)        if $db_file && -e $db_file;
        unlink($defaults_file)  if $defaults_file && -e $defaults_file;

script/wp-tools  view on Meta::CPAN

        print "No updates available.\n";
        exit;
    }

    ### if not explicitly refused, make a backup first
    my $backup = !$args->{'skip_backup'} ? backup() : undef;
    die "Unable to make backup." if !$args->{'skip_backup'} && (!$backup || !ref $backup || !$backup->{'success'});
    if ($backup && ref $backup && $backup->{'success'}) {
        $args->{'backup_file'} = $backup->{'backup_file'};
    }
    if (!$args->{'skip_backup'} && (length($args->{'backup_file'}) == 0 || !-e $args->{'backup_file'} || !-s $args->{'backup_file'})) {
        die "Unable to read backup file - upgrade stopped.";
    }

    #find all non-executable directories and make the executable (for listing)
    my $fix_directories = `find $pathq -type d ! -perm /u+x -exec chmod u+x {} \\; ;`;
    #find all non-writable files and make them user writable
    my $set_permissions = `find $pathq ! -perm /u+w -exec chmod u+w {} \\; ;`;

    #if a backup is made, disable maintenance mode temporarily
    if (-e '.maintenance') {
        $maintenance = '.not.maintenance';
        rename('.maintenance',$maintenance);
    }

    my $current_status;
    my $http = HTTP::Tiny->new(timeout => 30);
    if ($plus3713) {
        ### default wp-cli status check
        $current_status->{'wpcli_std'} = _run_wpcli($nice_path, q{eval 'echo "ok";'});
        chomp $current_status->{'wpcli_std'} if $current_status->{'wpcli_std'};
        ### npt "safe mode" status check
        $current_status->{'wpcli_npt'} = _run_wpcli($nice_path, q{--skip-plugins --skip-themes eval 'echo "ok";'});
        chomp $current_status->{'wpcli_npt'} if $current_status->{'wpcli_npt'};
        ### get url and current default page size/status code
        $current_status->{'siteurl'}  = _run_wpcli($nice_path, q{option get siteurl});;
        $current_status->{'adminurl'} = "$current_status->{'siteurl'}/wp-admin";
        my $resp = $http->head($current_status->{'siteurl'});
        $current_status->{'code'} = $resp->{'status'};
        $current_status->{'size'} = $resp->{'headers'}{'content-length'} || 1;
        my $aresp = $http->head($current_status->{'adminurl'});
        $current_status->{'admincode'} = $aresp->{'status'};
        $current_status->{'adminsize'} = $aresp->{'headers'}{'content-length'} || 1;
    }
    elsif (-f "$args->{'path'}/wp-config.php") {
        ### get url and current default page size/status code
        my $wp_configq = shell_quote("$args->{'path'}/wp-config.php");
        my $username = `grep DB_USER      <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
        my $password = `grep DB_PASSWORD  <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
        my $database = `grep DB_NAME      <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 4`;
        my $prefix   = `grep table_prefix <$wp_configq | grep -v '$definition_check_string' | cut -d \\' -f 2` || 'wp_';
        $prefix      =~ s/[^\w_-]//g;
        #disallow starting whitespace
        $username =~ s/^[\r\n]+//;
        $password =~ s/^[\r\n]+//;
        $database =~ s/^[\r\n]+//;
        chomp $username;
        chomp $password;
        chomp $database;
        if ($username =~ /[\r\n]/ || $password =~ /[\r\n]/ || $database =~ /[\r\n]/) {
            die "Multiple credentials found in $args->{'path'}/wp-config.php.  Cannot determine which to use.  Upgrade operation halted.";
        }
        if ($prefix =~ /[\r\n]/) {
            die "Multiple database prefixes found in $args->{'path'}/wp-config.php.  Cannot determine which to use.  Upgrade operation halted.";
        }
        open (my $fh, '>', $defaults_file) or die "Cannot write to $defaults_file: $!";
        close $fh;
        chmod(0600, $defaults_file) or die "Cannot chmod $defaults_file: $!";
        write_text($defaults_file,"[client]\nuser=$username\npassword=$password");
        my $table = "${prefix}options";
        my $databaseq = shell_quote($database);
        my $siteurl_sql = qq{mysql -N -B -e --defaults-file=$defaults_file $databaseq "SELECT option_value FROM $table WHERE option_name = 'siteurl'"};
        $current_status->{'siteurl'} = `$siteurl_sql`;
        $current_status->{'adminurl'} = "$current_status->{'siteurl'}/wp-admin";
        my $resp = $http->head($current_status->{'siteurl'});
        $current_status->{'code'} = $resp->{'status'};
        $current_status->{'size'} = $resp->{'headers'}{'content-length'} || 1;
        my $aresp = $http->head($current_status->{'adminurl'});
        $current_status->{'admincode'} = $aresp->{'status'};
        $current_status->{'adminsize'} = $aresp->{'headers'}{'content-length'} || 1;
    }

    eval {
        if ($components{'core'}) {
            my $update = _run_wpcli($nice_path, ($args->{'force'} || !$plus3713) ? "core download --force 2>&1" : "core update 2>&1");
            #try forcing errors
            $update = _run_wpcli($nice_path, q{core download --force 2>&1}) if $?;
            die "Upgrading WordPress core files exited with $? ($update)" if $?;
            if (!$args->{'__skip_database'}) {
                my $updatedb = _run_wpcli($nice_path, q{core update-db 2>&1});
                #try harder
                if ($?) {
                    $updatedb = _run_wpcli($nice_path, q{core update-db  --skip-plugins --skip-themes 2>&1});
                }
                #allowable database failures
                if ($updatedb !~ /(?:The site you have requested is not installed.)/) {
                    die "Upgrading WordPress core database exited with $? ($updatedb)" if $?;
                }
            }
        }
        my $pt_ok = qr/(?:Warning: Update package not available.|Error establishing a database connection)/;
        if ($components{'plugin'}) {
            my $plugins  = _run_wpcli($nice_path, q{plugin update --all 2>&1});
            if ($? && $plugins !~ /$pt_ok/gsm) {
                die "Upgrading plugins exited with $? ($plugins)";
            }
        }
        if ($components{'theme'}) {
            my $themes   = _run_wpcli($nice_path, q{theme update --all 2>&1});
            if ($? && $themes !~ /$pt_ok/gsm) {
                die "Upgrading themes exited with $? ($themes)" if $?;
            }
        }
    };
    if ($@) {
        $args->{'failed'} = $@;
    }
    if (!$args->{'failed'} && $plus3713) {
        ### default wp-cli status check
        if ($current_status->{'wpcli_std'} eq 'ok') {
            my $test = _run_wpcli($nice_path, q{eval 'echo "ok";'});



( run in 0.323 second using v1.01-cache-2.11-cpan-eab888a1d7d )