source: subversion/applications/utils/revert/Undo.pm @ 29290

Last change on this file since 29290 was 21142, checked in by frederik, 9 years ago

allow undo module to override subsequent changes on objects

File size: 5.6 KB
RevLine 
[14945]1#!/usr/bin/perl
2
3# Undo.pm
4# -------
5#
6# Implements undo operations
7#
8# Part of the "osmtools" suite of programs
9# Originally written by Frederik Ramm <frederik@remote.org>; public domain
10
11package Undo;
12
13use strict;
14use warnings;
15
16use OsmApi;
17
18# undoes one change by one user (or within one changeset)
19#
20# if the user has multiple changes at the current end of the
21# history, all of them are going to be undone (unless a
22# specific changeset is given). Likewise, if the object
23# has been changed multiple times in the same changeset,
24# then all of these changes will be reverted.
25#
26# fails if the object has been last changed by someone else.
27#
28# parameters:
29#   $what: 'node', 'way', or 'relation'
30#   $id: object id
31#   $undo_user: user whose operation should be undone
[16824]32#      (this may also be a hash reference containing multiple user
33#      names as keys, with any non-null value)
34#      (this may be undef)
[14945]35#   $undo_changeset: changeset whose operation should be undone
[16824]36#      (this may also be a hash reference containing multiple changeset
37#      ids as keys, with any non-null value)
38#      (this may be undef)
[14945]39#   $changeset: id of changeset to use for undo operation
40# return:
[21142]41#   success=1 failure=undef no action necessary=0
[14945]42
43sub undo
44{
45    my ($what, $id, $undo_user, $undo_changeset, $changeset) = @_;
46
47    my ($action, $xml) = 
48        determine_undo_action($what, $id, $undo_user, $undo_changeset, $changeset);
49
[21142]50    return 0 unless defined ($action);
[14945]51
52    if ($action eq "modify")
53    {
54        my $resp = OsmApi::put("$what/$id", "<osm version='0.6'>\n$xml</osm>");
55        if (!$resp->is_success)
56        {
57            print STDERR "$what $id cannot be uploaded: ".$resp->status_line."\n";
58            return undef;
59        }
60    }
61    elsif ($action eq "delete")
62    {
63        my $resp = OsmApi::delete("$what/$id", "<osm version='0.6'>$xml</osm>");
64        if (!$resp->is_success)
65        {
66            print STDERR "$what $id cannot be deleted: ".$resp->status_line."\n";
[21142]67            return ($resp->code == 410) ? 0 : undef;
[14945]68        }
69    }
70    else
71    {
72        die "assertion failed";
73    }
74    return 1;
75}
76
77# the undo workhorse; finds out which XML to upload to the API to
78# make a certain edit undone.
79#
80# Parameters:
81# see sub undo.
82#
83# Returns:
84# undef on error, else a two-element array where the first element is
85# either "modify" or "delete" depending on the action to be taken, and
86# the second element is the bare XML to send to the API. The XML has to
87# be wrapped in <osm>...</osm> or inside a <modify>...</modify> or
88# <delete>...</delete> block in a changeset upload.
89
90sub determine_undo_action
91{
[16824]92    my ($what, $id, $undo_users, $undo_changesets, $changeset) = @_;
[14945]93
[16824]94    # backwards compatibility
95    if (ref($undo_users) ne "HASH" && defined($undo_users))
96    {
97        $undo_users = { $undo_users => 1 };
98    }
99
100    if (ref($undo_changesets) ne "HASH" && defined($undo_changesets))
101    {
102        $undo_changesets = { $undo_changesets => 1 };
103    }
104
[14945]105    my $undo=0; 
106    my $copy=0;
107    my $out = "";
108    my $lastedit;
109    my $lastcs;
110    my $undo_version;
111    my $restore_version;
[21142]112    my $override_version;
113    my $override = 0;
114    my $force = 0; # if this is set to 1, any object touched by the undo userwill be reverted even if there are later modifications by others
[14945]115
116    my $resp = OsmApi::get("$what/$id/history");
117    if (!$resp->is_success)
118    {
119        print STDERR "$what $id cannot be retrieved: ".$resp->status_line."\n";
120        return undef;
121    }
122
123    foreach (split(/\n/, $resp->content()))
124    { 
125        if (/<$what/) 
126        { 
127            /\sid="([^"]+)"/ or die; 
128                die unless $id eq $1; 
129            /\sversion="([^"]+)"/ or die; 
130                my $version = $1;
131            /user="([^"]+)/;
132            my $user=$1;
133            /changeset="(\d+)/;
134            my $cs=$1;
[16824]135            if ((!defined($undo_users) || defined($undo_users->{$user})) && (!defined($undo_changesets) || defined($undo_changesets->{$cs})))
[14945]136            { 
137                $undo=1;
138                $copy=0; 
139                $undo_version = $version;
140            } 
141            else 
142            {
[21142]143                if ($undo && $force)
144                {
145                    $override = 1;
146                    $override_version = $version;
147                }
148                else
149                {
150                    $undo=0; 
151                    $copy=1; 
152                    $out=$_ . "\n"; 
153                    $restore_version = $version;
154                    $lastedit = $user; 
155                    $lastcs = $cs;
156                }
[14945]157            } 
158        } 
159        elsif ($copy) 
160        { 
[16824]161            $out.=$_ . "\n"; 
[14945]162            $copy=0 if (/<\/$what/);
163        } 
164    }; 
165
[21142]166    if ($undo || $override)
[14945]167    {
[21142]168        if ($override)
169        {
170            $undo_version = $override_version unless ($undo_version > $override_version);
171            print STDERR "$what $id: overriding subsequent changes\n";
172        }
[14945]173        if (length($out))
174        {
[16824]175            print STDERR "$what $id last edited as v$undo_version; restoring previous version $restore_version by '$lastedit'\n";
[14945]176            $out =~ s/version="$restore_version"/version="$undo_version"/;
177            $out =~ s/changeset="\d+"/changeset="$changeset"/;
178            return ( "modify", $out );
179        }
180        else
181        {
[16824]182            print STDERR "$what $id was created; deleting\n";
183            return ( "delete", "<$what id='$id' changeset='$changeset' version='$undo_version' lat='0' lon='0' />\n" );
[14945]184        }
185    }
186    else
187    {
[21142]188        print STDERR "$what $id last edited in another changeset/by another user ($lastedit/$lastcs)\n";
[14945]189        return undef;
190    }
191}
192
1931;
Note: See TracBrowser for help on using the repository browser.