source: subversion/sites/namefinder/www/editelement.html @ 17445

Last change on this file since 17445 was 12176, checked in by david, 11 years ago

add edit link and mini tag editor

File size: 18.5 KB
Line 
1<?php
2
3/* This file is a complete mini tag editor for objects in OSM identified by id. */
4
5// $debug = 1;
6session_start();
7$session =& $_SESSION['osmminieditor'];
8$cookiename = 'osmtageditor';
9
10include_once('preamble.php');
11include_once('search.php');
12include_once('options.php');
13
14if (! empty($_SERVER['REQUEST_URI'])) {
15  $db->log("VISITING {$_SERVER['REQUEST_URI']}");
16}
17
18$result = -1;
19$rememberhtml = '';
20$message;
21
22for(;;) {
23
24  if (isset($_GET['node'])) {
25    $id = $_GET['node'];
26    $elemtype = 'node';
27  } else if (isset($_GET['way'])) {
28    $id = $_GET['way'];
29    $elemtype = 'way';
30  } else {
31    $result = 'no_id';
32    $message = 'missing ?elemtype=id in url, or relation (cannot handle relations yet)';
33    break;
34  }
35
36  if (! empty($_POST)) {
37    //print_r($_POST);
38    if (! empty($_POST['login'])) {
39      if (! empty($_POST['user'])) {
40        $session['user'] = $_POST['user'];
41      }
42      if (! empty($_POST['user'])) {
43        $session['password'] = $_POST['password'];
44      }
45      if (! empty($_POST['remember'])) {
46        setlogincookie($session['user'], $session['password']);
47        $rememberhtml = "checked='checked'";
48      } else {
49        clearlogincookie();
50      }
51    } else if (! empty($_POST['logout'])) {
52      unset($session['user']);
53      unset($session['password']);
54      $result = 'need_login';
55      clearlogincookie();
56      break;
57    } else if (isset($_POST['action'])) {
58      $action = $_POST['action'];
59      switch($action) {
60      case 0: // ok
61        if (empty($_POST['values']) || ! is_array($_POST['values'])) {
62          $message = 'missing values (naughty, naughty)';
63          break 2;
64        }
65
66        $newtags = '';
67
68        for($i = 0; $i < count($_POST['tags']); $i++) {
69          if (! is_string($_POST['tags'][$i])) {
70            $message = "non-string tag {$i} (naughty, naughty)";
71            break 2;
72          }
73          if (! isset($_POST['values'][$i])) {
74            $message = "missing value {$i} (naughty, naughty)";
75            break 2;
76          }
77          if (! is_string($_POST['values'][$i])) {
78            $message = "non-string value {$i} (naughty, naughty)";
79            break 2;
80          }
81
82          $uploadtag = trim($_POST['tags'][$i]);
83          if ($uploadtag == '') { continue; }
84          $uploadvalue = $_POST['values'][$i];
85          $newtags .= "<tag k='" . htmlspecialchars($uploadtag, ENT_QUOTES, 'UTF-8') .
86            "' v='" . htmlspecialchars($uploadvalue, ENT_QUOTES, 'UTF-8') . "'/>\n";
87        }
88
89        $uploadxml = preg_replace("~\\<tag .*\\/\\>\\s*\\<\\/{$elemtype}\\>~is",
90          $newtags . "</{$elemtype}>",
91          $session['xml'][$elemtype][$id][count($session['xml'][$elemtype][$id])-1]);
92        $uploadxml = str_replace('generator="OpenStreetMap server"',
93                                 'generator="osm mini tag editor"', $uploadxml);
94        $uploadxml = preg_replace('/(\\<osm .*) timestamp="[^"]*"/i', '${1}', $uploadxml);
95        $uploadxml = preg_replace('/(\\<osm .*) user="[^"]*"/i', '${1}', $uploadxml);
96
97        break;
98      case 1: // cancel
99        // just drop through and get it again
100        break;
101      case 2: // undo
102        if (! isset($session['xml'][$elemtype][$id]) ||
103            count($session['xml'][$elemtype][$id]) < 2)
104        {
105          $message = "nothing to undo";
106          break 2;
107        }
108        array_pop($session['xml'][$elemtype][$id]);
109        $uploadxml = array_pop($session['xml'][$elemtype][$id]);
110        if (count($session['xml'][$elemtype][$id]) == 0) {
111          unset($session['xml'][$elemtype][$id]);
112        }
113        break;
114      default:
115        $message = 'unknown action (naughty, naughty)';
116        $result = 'need_login';
117        break 2;
118      }
119
120      if (isset($uploadxml)) {
121        $url = "http://www.openstreetmap.org/api/0.5/{$elemtype}/{$id}";
122        $fn = "/tmp/osmmi-".session_id();
123        file_put_contents($fn, $uploadxml);
124        $fh = fopen($fn, 'r');
125        $ch = curl_init($url);
126        curl_setopt($ch, CURLOPT_PUT, 1);
127        curl_setopt ($ch, CURLOPT_HTTPHEADER, array("Expect:"));
128          // works round a server problem which generates 417 errors otherwise
129        curl_setopt($ch, CURLOPT_INFILE, $fh);
130        curl_setopt($ch, CURLOPT_INFILESIZE, strlen($uploadxml));
131        curl_setopt($ch, CURLOPT_USERPWD, "{$session['user']}:{$session['password']}");
132        $xml = curl_exec($ch);
133        $uploadresult = curl_getinfo($ch, CURLINFO_HTTP_CODE);
134        curl_close($ch);
135        fclose($fh);
136        unlink($fn);
137
138        switch ($uploadresult) {
139        case 200:
140          $message = "OK";
141          break;
142        default:
143          $message = "upload to server returned {$uploadresult}\n";
144          break 2;
145        }
146      }
147    }
148  }
149
150  if (! isset($session['user'])) {
151    if (! empty($_COOKIE[$cookiename]['un']) &&
152        ! empty($_COOKIE[$cookiename]['up']))
153    {
154       $session['user'] = $_COOKIE[$cookiename]['un'];
155       $session['password'] = $_COOKIE[$cookiename]['up'];
156    } else {
157      $result = 'need_login';
158      break;
159    }
160  }
161
162  if (empty($debug)) {
163    $url = "http://www.openstreetmap.org/api/0.5/{$elemtype}/{$id}";
164    // $url = "http://www.openstreetmap.org/api/0.5/map?bllon=0.1899,bllat=52.1999,trlon=0.1901,trlat=52.001";
165    $ch = curl_init($url);
166    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // give me the output
167    curl_setopt($ch, CURLOPT_USERPWD, "{$session['user']}:{$session['password']}");
168    $xml = curl_exec($ch);
169    $result = curl_getinfo($ch, CURLINFO_HTTP_CODE);
170    curl_close($ch);
171  } else {
172    $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.5" generator="OpenStreetMap server"> <node id="20823872" lat="52.2062694249354" lon="0.134404947388109" user="davidearl" visible="true" timestamp="2006-11-01T22:40:05+00:00"> <tag k="amenity" v="cinema"/> <tag k="name" v="Vue Cinema (multiscreen)"/> <tag k="created_by" v="JOSM"/> </node> </osm>';
173    $result = 200;
174  }
175
176  if ($result != 200) {
177    switch ($result) {
178    case 401:
179      $message = "OSM rejected your email/password";
180      break;
181    case 500:
182      $message = "OSM gave server error 500";
183      break;
184    }
185    break;
186  }
187
188  if (! isset($session['xml'][$elemtype][$id]) ||
189      $session['xml'][$elemtype][$id][count($session['xml'][$elemtype][$id]) - 1] != $xml)
190  {
191    $session['xml'][$elemtype][$id][] = $xml;
192  }
193
194  /*
195    <node lon='-0.148588713362387' uid='110308' lat='51.5278975364617'>
196      <tag k="name" v="cheese"/>
197    </node>
198
199    so $xmlo['lon'], $xmlo['lat'], $xmlo['uid']
200       $xmlo->tag[$i]->tag['k']
201       $xmlo->tag[$i]->tag['v']
202  */
203  $xmlo = simplexml_load_string($xml);
204  if (empty($xmlo->$elemtype)) {
205    $result = -4;
206    break;
207  }
208  $uid = $xmlo->{$elemtype}['id'];
209  if ($uid != $id) {
210    /* problem */
211    $result = -2;
212    break;
213  }
214
215  $tags = array();
216  foreach ($xmlo->{$elemtype}->tag as $tag) {
217    $tags[(string)$tag['k']] = str_replace("\n", "\\n", (string)$tag['v']);
218  }
219
220  break;
221}
222
223function setlogincookie($user, $password) {
224  global $cookiename;
225  $expire = time() + 60*60*24* 30 /* days */;
226  /* the last parameter '/' means cookie available site wide */
227  setcookie("{$cookiename}[un]", $user, $expire, '/');
228  setcookie("{$cookiename}[up]", $password, $expire, '/');
229}
230
231function clearlogincookie() {
232  global $cookiename;
233  $expire = time();
234  setcookie("{$cookiename}[un]", '', $expire, '/'); /* delete cookie */
235  setcookie("{$cookiename}[up]", '', $expire, '/'); /* delete cookie */
236}
237
238?>
239<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
240<html lang='en' xmlns='http://www.w3.org/1999/xhtml'>
241<head>
242<title>OpenStreetMap mini tag editor</title>
243<meta name='description' content='OpenStreetMap mini tag editor' />
244<meta name='MSSmartTagsPreventParsing' content='TRUE' /> 
245<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
246<?php
247$userhtml = isset($session['user']) ? htmlspecialchars($session['user'], ENT_QUOTES, 'UTF-8') : '';
248if ($result == 200) {
249  include_once('mapfeatures.php');
250
251  $mapfeatureoptions = array(''=>'');
252  foreach ($mapfeatures as $category => $features) {
253    foreach($features as $feature) {
254      if ($feature{0} == '(') {
255        $feature = preg_replace('/\\(.*\\)/', '', $feature);
256        $ks = "{$category}-{$feature}";
257        $mapfeatureoptions[$ks] = array($ks, "{$category}={$feature}");
258      } else {
259        $ks = "{$feature}-{$category}";
260        $mapfeatureoptions[$ks] = array($ks, "{$feature} ({$category})");
261      }
262    }
263    $mapfeatureoptions["{$category}-"] = array("-{$category}", "{$category}=...");
264  }
265  ksort($mapfeatureoptions);
266
267  $xmlinhtml = empty($debug) ? '' :
268    "<p class='xml'>" . htmlspecialchars($xml, ENT_QUOTES, 'UTF-8') . "</p>";
269
270  $mapfeatureoptionssize = count($mapfeatureoptions);
271  $arrs = "<script type='text/javascript'>\n";
272  $arrs .= "var mfo = new Array({$mapfeatureoptionssize});\n";
273  foreach($mapfeatureoptions as $mfo) {
274    $arrs .= "mfo['{$mfo[0]}']='{$mfo[1]}';\n";
275  }
276  $tagssize = count($tags);
277  $arrs .= "var tags = new Array({$tagssize});\n";
278  $i = 0;
279  foreach($tags as $k=>$v) {
280    $v = str_replace("'", "\\'", $v);
281    $arrs .= "tags[{$i}] = new Array(2);\ntags[{$i}][0] = '{$k}';\ntags[{$i}][1] = '{$v}';\n";
282    $i++;
283  }
284
285  echo $arrs;
286?>
287
288function addrow(i) {
289  var tbody = document.getElementById("tbody");
290  var trow = document.createElement("TR");
291  var tdselect = document.createElement("TD");
292  var tdkey    = document.createElement("TD");
293  var tdvalue  = document.createElement("TD");
294  var sselect = document.createElement("SELECT");
295  trow.className = 'keyvalue';
296  trow.id = 'tag_' + i;
297  if (tags.length == i) {
298    tags[i] = new Array(2);
299    tags[i][0] = '';
300    tags[i][1] = '';
301  }
302  sselect.className = 'popup';
303  sselect.id = 'select_' + i;
304  // sselect.onchange = eval("function(){skv("+i+")}");
305  sselect.onchange = skv;
306  if (sselect.captureEvents) sselect.captureEvents(Event.CHANGE);
307
308  var j = 0;
309  for (var o in mfo) {
310    sselect.options[j] = new Option(mfo[o], o);
311    j++;
312  }
313  tdselect.appendChild(sselect);
314  tdkey.innerHTML =    "<input class='key' type='text' value='"+tags[i][0]+"' id='key_"+i+"' name='tags[]' onchange='javascript:changekey("+i+")' />";
315  tdvalue.innerHTML =  "<input class='value' type='text' value='"+tags[i][1]+"' id='value_"+i+"' name='values[]' onchange='javascript:changevalue("+i+")' />";
316  trow.appendChild(tdselect);
317  trow.appendChild(tdkey);
318  trow.appendChild(tdvalue);
319  tbody.appendChild(trow);
320}
321
322function checktags(j) {
323  var newkey = document.getElementById("key_"+j).value;
324  if (newkey == '') { return true; }
325  for (var i = 0; true;i++) {
326    var keyo = document.getElementById("key_"+i);
327    if (! keyo) { break; }
328    keyo.style.color = 'black';
329    if (i == j) { continue; }
330    if (newkey == keyo.value) {
331      keyo.style.color = 'red';
332      alert("duplicated tag "+newkey);
333      return false;
334    }
335  }
336  return true;
337}
338
339function changekey(i) {
340  var nexto;
341  if (i+1 == tags.length) { addrow(i+1); }
342  if (checktags(i)) {
343    nexto = document.getElementById("value_"+i);
344  } else {
345    nexto = document.getElementById("key_"+i);
346  }
347  nexto.focus();
348  nexto.select();
349}
350
351function changevalue(i) {
352  var nexto = document.getElementById("key_"+(i+1));
353  if (! nexto) { nexto = document.getElementById("value_"+i); }
354  nexto.focus();
355  nexto.select();
356}
357
358function skv(e) {
359  var select;
360  if (! e) { var e = window.event; }
361  if (e.target) { select = e.target; }
362  else if (e.srcElement) { select = e.srcElement; }
363  if (select.nodeType == 3) { select = select.parentNode; } // defeat Safari bug
364  if (! select) { return; }
365  var i = parseInt(select.id.substr(7)); // after the 'select_' part
366  if (i+1 == tags.length) { addrow(i+1); }
367  var optionvalue = select.options[select.selectedIndex].value;
368  if (optionvalue == '') { return; }
369  select.selectedIndex = 0;
370  var dash = optionvalue.indexOf("-");
371  var valueo = document.getElementById("value_"+i);
372  document.getElementById("key_"+i).value = optionvalue.substr(dash+1);
373  valueo.value = optionvalue.substr(0, dash);
374  if (valueo.value == '') { valueo.focus(); }
375  checktags(i);
376}
377
378function dosubmit(ok) {
379  document.getElementById("action").value = ok;
380  document.theform.submit();
381}
382
383function loadthebody() {
384  var nrows = tags.length; // n.b. tags.length changes as a side effec of addrow
385  for (var i = 0; i <= nrows; i++) { addrow(i); }
386  changevalue(nrows-1);
387}
388
389</script>
390<?php } ?>
391<style>
392body {
393  font-family: Verdana, sans-serif;
394  font-size: 0.7em;
395}
396h1 { font-size: 2em; }
397h2 { font-size: 1.2em; }
398h2.errormessage { color: red; }
399div.logo {
400 float: right;
401 margin-right: 2em; 
402}
403table { width: 95%; }
404td.key {
405}
406td.value {
407 width: 75%;
408}
409input.key {
410}
411input.value {
412 width: 100%;
413}
414div.user {
415  margin-top: 1em;
416}
417input#user,
418input#password {
419 width: 30em;
420}
421td.prompt {
422 width: 10em;
423 font-size: 1em;
424}
425input.button {
426 width: 18em;
427}
428p.hint {
429 width: 50%;
430}
431div#okcancelundo {
432  margin-top: 1em;
433}
434form#loginform {
435 clear: both;
436}
437</style>
438</head>
439<body onload='javascript:loadthebody()'>
440<div class='logo'>
441<img src='http://www.openstreetmap.org/images/osm_logo.png' alt='OpenStreetMap'/>
442</div>
443<h1>OpenStreetMap mini tag editor</h1>
444<?php
445if (! empty($message)) { echo "<h2 class='errormessage'>",$message,"</h2>\n"; }
446
447switch($result) {
448case 401: // authentication failed
449case 'need_login':
450?>
451<h2>please log in</h2>
452<script type='text/javascript'>function loadthebody(){}</script>
453<form action='' method='POST' id='loginform'>
454<table>
455<tr>
456<td class='prompt'>email address:&nbsp;</td>
457<td><input type='text' id='user' name='user' value='<?php echo $userhtml; ?>' /></td>
458</tr><tr>
459<td class='prompt'>OSM&nbsp;password:&nbsp;</td>
460<td><input type='password' id='password' name='password' /></td>
461</tr>
462<tr>
463<td class='prompt'>remember&nbsp;password:&nbsp;</td>
464<td><input type='checkbox' id='remember' name='remember' {$rememberhtml} /></td>
465</tr>
466<tr>
467<td class='prompt'></td>
468<td><input class='submit' type='submit' id='login' name='login'  value='Login' /></td>
469</tr>
470</table>
471<p class='hint'>Your OSM password is needed to collect data from and submit
472data to the OSM server.</p>
473<p class='hint'>If you tick the 'remember password' box your email
474address and password will be stored in a cookie on your PC's disk.
475This means you won't need to re-enter it here again. You can clear it later.</p>
476<p class='hint'>Otherwise your email and password will only be stored on
477the mini-editor server for the duration of your session (that is, until a few
478minutes after you stop using it or you explicitly log out).</p>
479<p class='hint'>Passwords are sent in clear text both between your browser
480and the mini-editor server and from there to the OpenStreetMap server.</p>
481</form>
482
483<?php 
484  break;
485case -1:
486  break;
487case -2:
488  echo "<h2>ids don't match '", $uid, "' '", $id, "'</p>\n";
489  break;
490case 'no_id':
491  echo "<script type='text/javascript'>function loadthebody(){}</script>\n";
492  break;
493case -3:
494  echo "<h2>no node</h2>";
495  break;
496case 500:
497?>
498<form action='' method='POST'>
499<input class='submit' type='submit' id='logout' name='logout'  value='Logout' />
500<p class='hint'>This will forget your email and password both on the server and remove any login cookie.</p>
501</form>
502<?php
503  break;
504default:
505  echo "<h2>server returned error {$result} for url " . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . "</h2>\n";
506  echo htmlspecialchars($xml, ENT_QUOTES, 'UTF-8');
507  break;
508case 200: // normal response
509  $name = empty($tags['name']) ? '' : ' - ' .
510  htmlspecialchars($tags['name'], ENT_QUOTES, 'UTF-8');
511  $timestamp = str_replace('T', ' ', $xmlo->{$elemtype}['timestamp']);
512
513  $s = "<h2>for <a href='http://www.openstreetmap.org/browse/{$elemtype}/{$id}'>{$elemtype} {$id}{$name}</a>:<br />\n";
514
515  if ($elemtype == 'way') {
516    $s .= "bear in mind that streets, buildings etc may be made up of <span style='color: red'>more than one way</span>, so it may not be sufficient just to edit this one<br />\n";
517  }
518
519  if (isset ($xmlo->node['lat'])) {
520    $s .= "at latitude {$xmlo->node['lat']}, longitude {$xmlo->node['lon']}<br />\n";
521    $url = "http://www.openstreetmap.org?index.html?lat={$xmlo->node['lat']}&lon={$xmlo->node['lon']}&zoom=15";
522    $s .= "<a href='{$url}'>" . htmlspecialchars($url, ENT_QUOTES, 'UTF-8') . "</a><br />\n";
523  } else if (isset ($xmlo->way)) {
524    $s .= "nodes: ";
525    if (empty($xmlo->way->nd)) {
526      $s .= "NONE!<br />\n";
527    } else {
528      $prefix = '';
529      foreach($xmlo->way->nd as $node) {
530        $s .= "{$prefix}<a href='?node={$node['ref']}'>{$node['ref']}</a>";
531        $prefix = ', ';
532      }
533      $s .= "<br />\n";
534    }
535  }
536
537  $person = $xmlo->{$elemtype}['user'];
538
539$s .=<<<EOD
540last changed by {$person} at {$timestamp}</h2>
541<div style='clear: both'></div>
542{$xmlinhtml}
543<form action='' method='POST' name='theform'>
544<table>
545<thead>
546<tr>
547<td class='select'></td>
548<td class='key'><strong>key<br/>(delete a key to remove the tag)</strong></td>
549<td class='value'><strong>value</strong></td>
550</tr>
551</thead>
552<tbody id='tbody'><!-- js --></tbody>
553</table>
554<input type='hidden' value='0' id='action' name='action' />
555</form>
556<div id='okcancelundo'>
557<input class='button' type='button' id='submit' name='submit'
558value='Update (commit changes)'
559onclick='javascript:dosubmit(0)' />
560&nbsp;&nbsp;
561<input class='button' type='button' id='cancel' name='cancel'
562value='Cancel (ignore these changes)'
563   onclick='javascript:dosubmit(1)' />
564
565EOD;
566
567 if (isset($session['xml'][$elemtype][$id]) && count($session['xml'][$elemtype][$id]) > 1) {
568   $s .= <<<EOD
569&nbsp;&nbsp;
570<input class='button' type='button' id='undo' name='undo'
571value='Undo (revert database to previous)'
572onclick='javascript:dosubmit(2)' />
573
574EOD;
575 }
576
577 $s .= <<<EOD
578</div>
579<p class='hint'>On Update the database will be changed to reflect the new node
580contents (all being well) and then the node will be fetched again into this form for
581confirmation</p>
582
583<form action='' method='POST'>
584<input class='submit' type='submit' id='logout' name='logout'  value='Logout' />
585<p class='hint'>Logged in as <strong>{$userhtml}</strong>. The Logout button
586will forget your email and
587password on the server and clear any login cookie on your own computer.</p>
588</form>
589
590EOD;
591
592  echo $s;
593  break;
594}
595?>
596</body>
597</html>
Note: See TracBrowser for help on using the repository browser.