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

Last change on this file since 16824 was 16824, checked in by frederik, 10 years ago

allow undo for multiple users/changesets

File size: 4.9 KB
Line 
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
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)
35#   $undo_changeset: changeset whose operation should be undone
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)
39#   $changeset: id of changeset to use for undo operation
40# return:
41#   success=1 failure=undef
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
50    return undef unless defined ($action);
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";
67            return undef;
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{
92    my ($what, $id, $undo_users, $undo_changesets, $changeset) = @_;
93
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
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;
112
113    my $resp = OsmApi::get("$what/$id/history");
114    if (!$resp->is_success)
115    {
116        print STDERR "$what $id cannot be retrieved: ".$resp->status_line."\n";
117        return undef;
118    }
119
120    foreach (split(/\n/, $resp->content()))
121    { 
122        if (/<$what/) 
123        { 
124            /\sid="([^"]+)"/ or die; 
125                die unless $id eq $1; 
126            /\sversion="([^"]+)"/ or die; 
127                my $version = $1;
128            /user="([^"]+)/;
129            my $user=$1;
130            /changeset="(\d+)/;
131            my $cs=$1;
132            if ((!defined($undo_users) || defined($undo_users->{$user})) && (!defined($undo_changesets) || defined($undo_changesets->{$cs})))
133            { 
134                $undo=1;
135                $copy=0; 
136                $undo_version = $version;
137            } 
138            else 
139            {
140                $lastedit = $user; 
141                $lastcs = $cs;
142                $undo=0; 
143                $copy=1; 
144                $out=$_ . "\n"; 
145                $restore_version = $version;
146            } 
147        } 
148        elsif ($copy) 
149        { 
150            $out.=$_ . "\n"; 
151            $copy=0 if (/<\/$what/);
152        } 
153    }; 
154
155    if ($undo)
156    {
157        if (length($out))
158        {
159            print STDERR "$what $id last edited as v$undo_version; restoring previous version $restore_version by '$lastedit'\n";
160            $out =~ s/version="$restore_version"/version="$undo_version"/;
161            $out =~ s/changeset="\d+"/changeset="$changeset"/;
162            return ( "modify", $out );
163        }
164        else
165        {
166            print STDERR "$what $id was created; deleting\n";
167            return ( "delete", "<$what id='$id' changeset='$changeset' version='$undo_version' lat='0' lon='0' />\n" );
168        }
169    }
170    else
171    {
172        print STDERR "$what $id last edited in another changeset/by another user\n";
173        return undef;
174    }
175}
176
1771;
Note: See TracBrowser for help on using the repository browser.