real api and clearer separation between server and client
authorarno <arenevier@fdn.fr>
Thu, 16 Jul 2009 14:23:31 +0000 (16:23 +0200)
committerarno <arenevier@fdn.fr>
Thu, 16 Jul 2009 18:08:28 +0000 (20:08 +0200)
23 files changed:
admin.php
api.php [new file with mode: 0644]
auth.php [deleted file]
build.sh
changes.php [deleted file]
devdoc/api.txt [new file with mode: 0644]
inc/db/anydb.php
inc/db/mysql.php
inc/errors.php [deleted file]
inc/settings.php
inc/utils.php
index.php
items.php
js/admin.js
js/syp.js
logout.php [new file with mode: 0644]
media/admin.css
openlayers/build/OpenLayers.js
openlayers/tools/jsmin.pyc
openlayers/tools/mergejs.pyc
openlayers/tools/toposort.pyc
syp.cfg
wizard.php

index 02a56453acac3186e2d1eb2d6f6a75d6f85a04bd..b69cc9d0348ca1ecb660ee321ee8657dfec14607 100644 (file)
--- a/admin.php
+++ b/admin.php
@@ -2,8 +2,9 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
    license. */
 
-require ("./inc/settings.php");
-require ("./inc/db/mysql.php");
+require_once ("./inc/settings.php");
+require_once ("./inc/db/mysql.php");
+require_once ("./inc/utils.php");
 
 $error = false;
 try {
@@ -59,7 +60,7 @@ if (!$usrtblexists || !$itemstblexists) {
 
     <noscript>
     <style type="text/css">
-        #map, #editor, #newimage, #global_admin, #login_area {
+        #map, #editor, #admin, #login_area {
             display: none;
         }
     </style>
@@ -77,81 +78,76 @@ if (!$usrtblexists || !$itemstblexists) {
 
     <div id="map"></div>
 
-    <p id="logout"><a href="auth.php?logout=true">Déconnexion</a></p>
+    <div id="admin" class="center">
+        <input id="newfeature_button" type="button" value="ajouter un emplacement">
+        <p id="server_comm"></p>
+        <p id="instructions"></p>
+    </div>
 
-    <!-- we need to set some random content to give some height to
-         #features_success -->
-    <p class="success center" id="features_success">.</p>
+    <p id="logout"><a href="logout.php">Déconnexion</a></p>
 
     <div id="editor" class="center">
-        <label for="title_input">titre&nbsp;:</label><br>
-        <input id="title_input" class="input" size="50"><br>
-        <label for="desc_input">description&nbsp;:</label><br>
-        <textarea id="desc_input" class="input" cols="45" rows="4"></textarea><br>
-        <img id="img"><br>
-        <input id="deletephoto_button" type="button" class="center" value="supprimer cette photo">
+        <input id="editor_close" type="image" src="openlayers/theme/default/img/close.gif"
+             title="fermer sans enregistrer" alt="fermer">
+        <form id="feature_update" method="post" enctype="multipart/form-data">
+            <label for="title">titre&nbsp;:</label><br>
+            <input id="title" name="title"><br>
+            <label for="description">description&nbsp;:</label><br>
+            <textarea id="description" name="description" rows="4"></textarea><br>
+            <div><img id="img"></div>
+            <input id="image_delete" type="button" value="supprimer l'image">
+            <div>
+                <label for="image_file">ajouter une image&nbsp;:</label>
+                <input id="image_file" type="file" name="image_file">
+            </div>
+            <br>
+            <div class="center">
+            <input id="validate_editor" type="submit" value="Valider les changements">
+            </div>
+            <input type="hidden" name="request">
+            <input type="hidden" name="lon">
+            <input type="hidden" name="lat">
+            <input type="hidden" name="fid">
+            <input type="hidden" name="keep_img">
+        </form>
+        <form id="feature_delete" method="post">
+            <input id="delete" type="submit" value="Supprimer la fiche">
+            <input type="hidden" name="request" value="del">
+            <input type="hidden" name="fid">
+        </form>
     </div>
 
-    <div id="newimage">
-        <img id="newimage_close" src="openlayers/theme/default/img/close.gif" alt="fermer">
-        <form  id="file_form"
-               action="changes.php"
-               method="POST"
-               enctype="multipart/form-data"
-               target="fileframe"
-               accept="image/gif image/jpeg image/jpg image/png"
-             >
-           <label for="newimage_input">sélectionnez une image</label>
-           <input id="newimage_input" name="newimage_input" type="file">
-        </form>
-        <p id="newimage_throbber" class="throbber center">
-                Upload en cours
-                <img src="media/newimage_throbber.gif" alt="throbber">
-        </p>
-        <img id="newimage_preview" src="">
-        <p class="warn center" id="newimage_warn"></p>
-        <p class="error center" id="newimage_error"></p>
-      </div>
-
-      <div id="global_admin" class="center">
-        <p id="modify_howto">Pour modifier les données d'une photo, sélectionnez le marqueur correspondant</p>
-        <p id="dragdrop_howto">Vous pouvez déplacer le marqueur en effectuant un glisser-déposer</p>
-        <input id="addphoto_button" type="button" class="center" value="ajouter une image">
-        <p class="error" id="features_connect_error"></p>
-      </div>
-
-
-      <div id="login_area"<?php 
-        $cookie_name = sprintf ("%sauth", DBPREFIX);
-        if (isset ($_COOKIE [$cookie_name])) {
-            if ($connection->checkpwdmd5 ("admin", $_COOKIE [$cookie_name])) {
-                echo ' class="hidden"';
-            }
+      <div id="login_area"
+        <?php 
+        if ($connection->checkpwdmd5 ("admin", 
+                               $_COOKIE [sprintf ("%sauth", DBPREFIX)])) {
+            echo ' class="hidden"';
         }
       ?>>
      <div id="login_transparency"></div>
      <div id="login_padding"></div>
      <div id="login_content">
-        <form id="login_form" method="post" action="auth.php">
+        <form id="login_form" method="post">
             <table>
                 <tr>
-                    <td><label for="user_pwd">mot de passe</label></td>
-                    <td style="width: 100%"><input id="user_pwd" name="user_pwd" type="password"></td>
+                    <td><label for="password">mot de passe</label></td>
+                    <td style="width: 100%"><input id="password" name="password" type="password"></td>
                 </tr>
             </table>
             <p class="center">
-                <input id="login_submit" type="submit">
+                <input id="login_submit" type="submit" value="Connexion">
+                <input type="hidden" name="request" value="auth">
             </p>
             <p id="pwd_throbber" class="throbber center">
                 Connexion en cours
                 <img src="media/pwd_throbber.gif" alt="throbber">
             </p>
-            <p class="error center" id="login_connect_error"></p>
-            <p class="error center" id="login_password_error">Le mot de passe n'est pas correct</p>
+            <p class="error center" id="login_error"></p>
         </form>
      </div>
      </div>
 
-     <iframe id="fileframe" name="fileframe" src="" frameborder="0" width="0" height="0"></iframe>
+     <iframe id="api_frame" name="api_frame" src="" frameborder="0" width="0" height="0"></iframe>
+
 </body>
 </html>
diff --git a/api.php b/api.php
new file mode 100644 (file)
index 0000000..93f27bd
--- /dev/null
+++ b/api.php
@@ -0,0 +1,287 @@
+<?php
+/* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
+   license. */
+
+require_once ("./inc/settings.php");
+require_once ("./inc/db/mysql.php");
+require_once ("./inc/utils.php");
+
+function exit_document ($body) {
+    exit ("<html><head></head><body>$body</body></html>");
+}
+
+function success_auth () {
+    success ("auth");
+}
+
+function success_feature ($feature, $request) {
+    $res = "<success request=\"$request\"><feature>";
+    $res .= "<id>" .  $feature->id .  "</id>";
+
+    $res .= "<imgurl>" .
+             ($feature->imgpath ? 
+                    full_url_from_filename ($feature->imgpath)
+                    : "") .
+             "</imgurl>";
+
+    $res .= "<description>" .
+                 htmlspecialchars ($feature->description) .
+                 "</description>";
+
+    // XXX: we do not use <title> because that would be interpreted and
+    // altered by browers html parser
+    $res .= "<heading>" . 
+            htmlspecialchars ($feature->title) .
+            "</heading>";
+
+    $res .= "<lon>" . $feature->lon . "</lon>";
+    $res .= "<lat>" . $feature->lat . "</lat>";
+    $res .= "</feature></success>";
+    exit_document ($res);
+}
+
+function success_delete_feature ($feature) {
+    $res = "<success request=\"del\"><feature>";
+    $res .= "<id>" .  $feature->id .  "</id>";
+    $res .= "</feature></success>";
+    exit_document ($res);
+}
+
+function success ($reason) {
+    exit_document ("<success request=\"$reason\"></success>");
+}
+
+function error ($reason) {
+    exit_document ("<error reason=\"$reason\"></error>");
+}
+
+function error_feature ($id, $reason) {
+    $res = "<error reason=\"$reason\"><feature>";
+    $res .= "<id>" .  $id .  "</id>";
+    $res .= "</feature></error>";
+    exit_document ($res);
+}
+
+function nochange_error ($id) {
+    error_feature ($id, "nochange");
+}
+function unreferenced_error ($id) {
+    error_feature ($id, "unreferenced");
+}
+
+function server_error () {
+    error ("server");
+}
+
+function unauthorized_error () {
+    error ("unauthorized");
+}
+
+function request_error () {
+    error ("request");
+}
+
+function file_too_big_error () {
+    error ("toobig");
+}
+
+function notanimage_error () {
+    error ("notimage");
+}
+
+function save_uploaded_file ($file, $con) {
+    $dest = "";
+    if (isset ($file) && ($file ["error"] != UPLOAD_ERR_NO_FILE)) {
+        img_check_upload ($file);
+        $dest = unique_file (UPLOADDIR, $file ["name"], $con);
+        if (!isset ($dest) || 
+                (!move_uploaded_file ($file ["tmp_name"], $dest))) {
+            server_error ();
+        }
+    }
+    return basename($dest);
+}
+
+function img_check_upload ($file) {
+    if (!is_uploaded_file ($file ["tmp_name"])) {
+        if ($file ["error"] ==  UPLOAD_ERR_INI_SIZE) {
+            file_too_big_error ();
+        } else {
+            server_error ();
+        }
+    }
+    if (!getimagesize ($file ["tmp_name"])) {
+        notanimage_error ();
+    }
+}
+
+function delete_image_if_unused ($imgpath, $con) {
+    if ($con->imgpath_exists ($imgpath)) {
+        return false;
+    }
+    $path = UPLOADDIR . "/" . $imgpath;
+    if (file_exists($path)) {
+        unlink ($path);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+function unique_file ($dirname, $relpath, $con) {
+   $relpath = str_replace ('/', '', $relpath); // strip slashes from path
+   $relpath = str_replace ('\\', '', $relpath); // strip antislashes from path
+   $filename = $dirname . '/' . $relpath;
+   $counter = 1;
+
+   $dotpos = strrpos ($relpath, '.');
+   if ($dotpos) {
+       $base = substr ($relpath, 0, $dotpos);
+       $ext = substr ($relpath, $dotpos + 1);
+   } else {
+       $base = $relpath;
+       $ext = "";
+   }
+
+   while ($counter < 1000) {
+       if (!file_exists ($filename) && 
+           !($con->imgpath_exists (basename ($filename)))) {
+           return $filename;
+       } else {
+            $counter++;
+            $filename = $dirname . '/' . $base . '_' . $counter . '.' . $ext;
+       }
+   }
+   // we tried to find an unused filename 1000 times. Give up now.
+   return null;
+}
+
+function main ($con) {
+    if (!isset ($_POST ["request"])) {
+        request_error ();
+    }
+    if ($_POST ["request"] == "auth") {
+        $pwd = unquote ($_POST["password"]);
+        $user = "admin";
+        if ($con->checkpwdmd5 ($user, md5 ($pwd))) {
+            // cookie will be valid for 2 weeks. I've chosen that value
+            // arbitrarily, and it may change in the future.
+            $time = time () + 14 * 60 * 24 * 60;
+            $cookie_name = sprintf ("%sauth", DBPREFIX);
+            setcookie ($cookie_name, md5 ($pwd), $time, "" , "", false, true);
+            success_auth ();
+        } else {
+            unauthorized_error ();
+        }
+    }
+    if (!($con->checkpwdmd5 ("admin",
+                             $_COOKIE [sprintf ("%sauth", DBPREFIX)]))) {
+        unauthorized_error ();
+    }
+
+    switch ($_POST ["request"]) {
+        case "update":
+            $id = $_POST ["fid"];
+            $feature = $con->getfeature ($id);
+            if (!isset ($feature)) {
+                unreferenced_error ($id);
+            }
+
+            // no file uploaded, but editor currently has an image: it means
+            // image was not changed
+            if ($_POST ["keep_img"] == "yes") {
+                $imgpath = $feature->imgpath;
+            } else {
+                $imgpath = save_uploaded_file ($_FILES ["image_file"], $con);
+            }
+
+            $lon = $_POST ["lon"];
+            $lat = $_POST ["lat"];
+            $title = unquote ($_POST ["title"]);
+            $description = unquote ($_POST ["description"]);
+
+            try {
+                $new_feature = new feature ($id, $lon, $lat, $imgpath, $title, $description);
+            } catch (Exception $e) {
+                request_error ();
+            }
+
+            if (($new_feature->lon == $feature->lon) &&
+                ($new_feature->lat == $feature->lat) &&
+                ($new_feature->title == $feature->title) &&
+                ($new_feature->imgpath == $feature->imgpath) &&
+                ($new_feature->description == $feature->description)) {
+                nochange_error ($feature->id);
+            }
+
+            $old_imgpath = "";
+            if ($feature->imgpath && ($feature->imgpath != $new_feature->imgpath)) {
+                $old_imgpath = $feature->imgpath;
+            }
+
+            try {
+                $con->save_feature ($new_feature);
+            } catch (Exception $e) {
+                server_error ();
+            }
+            if ($old_imgpath) {
+                try {
+                    delete_image_if_unused ($old_imgpath, $con); 
+                } catch (Exception $e) {}
+            }
+            success_feature ($new_feature, "update");
+        break;
+        case "add":
+            $imgpath = save_uploaded_file ($_FILES ["image_file"], $con);
+
+            $lon = $_POST ["lon"];
+            $lat = $_POST ["lat"];
+            $title = unquote ($_POST ["title"]);
+            $description = unquote ($_POST ["description"]);
+            try {
+                $feature = new feature (null, $lon, $lat, $imgpath, $title, $description);
+            } catch (Exception $e) {
+                request_error ();
+            }
+            try {
+                $feature = $con->save_feature ($feature);
+            } catch (Exception $e) {
+                server_error ();
+            }
+            success_feature ($feature, "add");
+        break;
+        case "del":
+            $id = $_POST ["fid"];
+            $feature = $con->getfeature ($id);
+            if (!isset ($feature)) {
+                unreferenced_error ($id);
+            }
+            $imgpath = $feature->imgpath;
+
+            try {
+                $con->delete_feature ($feature);
+            } catch (Exception $e) {
+                server_error ();
+            }
+
+            try {
+                delete_image_if_unused ($imgpath, $con);
+            } catch (Exception $e) {}
+
+            success_delete_feature ($feature);
+        default:
+            request_error();
+        break;
+    }
+
+    server_error ();
+}
+
+try {
+    $connection->connect (DBHOST, DBUSER, DBPWD, DBNAME, DBPREFIX);
+} catch (Exception $e) {
+    server_error ();
+}
+
+main ($connection);
+?>
diff --git a/auth.php b/auth.php
deleted file mode 100644 (file)
index 4a43a22..0000000
--- a/auth.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-/* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
-   license. */
-
-require ("./inc/errors.php");
-require ("./inc/settings.php");
-require ("./inc/db/mysql.php");
-require ("./inc/utils.php");
-
-function logout () {
-    $cookie_name = sprintf ("%sauth", DBPREFIX);
-    if (isset ($_COOKIE [$cookie_name])) {
-        setcookie ($cookie_name, "", time () - 3600, "" , "",false, true);
-        header ('Location: index.php');
-    }
-    exit();
-}
-
-function main ($con) {
-    $pwd = unquote ($_POST["user_pwd"]);
-    if (!isset ($pwd)) {
-        access_denied ();
-        return;
-    }
-    $con->connect (DBHOST, DBUSER, DBPWD, DBNAME, DBPREFIX);
-    if ($con->checkpwdmd5 ("admin", md5 ($pwd))) {
-        // cookie will be valid for 2 weeks. I've chosen that value
-        // arbitrarily, and it may change in the future.
-        $time = time () + 14 * 60 * 24 * 60;
-        $cookie_name = sprintf ("%sauth", DBPREFIX);
-        setcookie ($cookie_name, md5 ($pwd), $time, "" , "", false, true);
-        access_allowed ();
-    } else {
-        access_denied ();
-    }
-}
-
-if ($_GET ["logout"] == "true" || $_GET ["logout"] == "1") {
-    logout ();
-}
-
-try {
-    main ($connection);
-} catch (Exception $e) {
-    access_denied ();
-}
-?>
index 92873f5c42bf98cf87b758e781df60e00ba2653e..fbbd7ffbc1619611a5882cb402ca87f7a144ad22 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -15,7 +15,7 @@ mkdir -p $DESTDIR
 cp -RLp inc/ $DESTDIR/
 
 # other php files
-cp -p admin.php auth.php changes.php index.php items.php wizard.php $DESTDIR/
+cp -p admin.php api.php index.php items.php logout.php wizard.php $DESTDIR/
 
 # media
 cp -RLp media/ $DESTDIR/
diff --git a/changes.php b/changes.php
deleted file mode 100644 (file)
index 9c157ae..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-/* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
-   license. */
-
-require ("./inc/settings.php");
-require ("./inc/errors.php");
-require ("./inc/db/mysql.php");
-require ("./inc/utils.php");
-
-function unique_file ($dirname, $relpath, $con) {
-   $relpath = str_replace ('/', '', $relpath); // strip slashes from path
-   $relpath = str_replace ('\\', '', $relpath); // strip antislashes from path
-   $filename = $dirname . '/' . $relpath;
-   $counter = 1;
-
-   $dotpos = strrpos ($relpath, '.');
-   if ($dotpos) {
-       $base = substr ($relpath, 0, $dotpos);
-       $ext = substr ($relpath, $dotpos + 1);
-   } else {
-       $base = $relpath;
-       $ext = "";
-   }
-
-   while ($counter < 1000) {
-       if (!file_exists ($filename) && !$con->imgurl_exists ($filename)) {
-           return $filename;
-       } else {
-            $counter++;
-            $filename = $dirname . '/' . $base . '_' . $counter . '.' . $ext;
-       }
-   }
-   // we tried to find an unused filename 1000 times. Give up now.
-   return null;
-}
-
-function checkimgupload ($file) {
-    if (!is_uploaded_file ($file ["tmp_name"])) {
-        if ($file ["error"] ==  UPLOAD_ERR_INI_SIZE) {
-            file_too_big_error ();
-        } else {
-            request_error ();
-        }
-    }
-    if (!getimagesize ($file ["tmp_name"])) {
-        notanimage_error ();
-    }
-}
-
-function main ($con) {
-    if (isset ($_FILES ["newimage_input"])) {
-        $file = $_FILES ["newimage_input"];
-        checkimgupload ($file);
-
-        $dest = unique_file (UPLOADDIR, $file ["name"], $con);
-        if (!isset ($dest) || 
-             (!move_uploaded_file ($file ["tmp_name"], $dest))) {
-            server_error ();
-        }
-
-        exit (sprintf ("<p class=\"res\">request accepted</p>
-                        <p class=\"infos\"><span class=\"imgurl\">%s</p></span></p>", 
-                        rawurlencode ($dest)));
-    } else if (isset ($_POST ["feature_imgurl"])) {
-        $imgurl = rawurldecode (unquote ($_POST ["feature_imgurl"]));
-        $title = unquote ($_POST ["feature_title"]);
-        $description = unquote ($_POST ["feature_description"]);
-        $lon = $_POST ["feature_lon"];
-        $lat = $_POST ["feature_lat"];
-
-        try {
-            $feature = new feature ($imgurl, $title, $description, $lon, $lat);
-        } catch (Exception $e) {
-            switch ($e->getMessage ()) {
-               case $feature->err_lonlat_invalid:
-                   request_error ();
-               default:
-                   server_error ();
-            }
-        }
-
-        try {
-            if (!$con->save_feature ($feature)) {
-                feature_unavailable ();
-            }
-        } catch (Exception $e) {
-            server_error ();
-        }
-
-        request_success ();
-    } else if (isset ($_POST ["feature_delete"])) {
-        $imgurl = rawurldecode (unquote ($_POST ["feature_delete"]));
-        $feature = $con->getfeature ($imgurl);
-        if (!isset ($feature)) {
-            feature_unavailable ();
-        }
-
-        try {
-            $con->delete_feature ($feature);
-        } catch (Exception $e) {
-            server_error ();
-        }
-
-        request_success ();
-    } else if (isset ($_POST ["imgurl_delete_0"])) {
-        $idx = 0;
-        while (isset ($_POST ["imgurl_delete_" . $idx])) {
-            $imgurl = rawurldecode (unquote ($_POST ["imgurl_delete_" .  $idx]));
-            if (!$con->imgurl_exists ($imgurl)) {
-                if (file_exists ($imgurl)) {
-                    unlink ($imgurl);
-                } else {
-                    $fname = relative_path ($imgurl);
-                    if (file_exists ($fname)) {
-                        unlink ($fname);
-                    }
-                }
-            }
-            $idx++;
-        }
-    } else {
-        request_error ();
-    }
-}
-
-try {
-    $connection->connect (DBHOST, DBUSER, DBPWD, DBNAME, DBPREFIX);
-} catch (Exception $e) {
-    server_error ();
-}
-$cookie_name = sprintf ("%sauth", DBPREFIX);
-if (!isset ($_COOKIE [$cookie_name]) ||
-   !$connection->checkpwdmd5 ("admin", $_COOKIE [$cookie_name])) {
-       access_denied ();
-}
-
-main ($connection)
-?>
diff --git a/devdoc/api.txt b/devdoc/api.txt
new file mode 100644 (file)
index 0000000..cc1413e
--- /dev/null
@@ -0,0 +1,109 @@
+# feature definition
+
+A feature represents a location. It has index _fid_. It has two dimensions:
+_lagitude_ and _longitude_. It may have an image represented by it's url.
+This field is called _imgurl_; it may also have a _title_ and/or a _description_.
+
+# client to server communication:
+
+Client submits a classic html form to server. 
+
+**note**: In this documentation, php notation is used (`_POST` and `_FILES`),
+but server may be written in any language.
+
+`_POST["request"]` is either:
+
+## auth
+ asks for authentication
+
+ * ` _POST["password"]` must contains user password
+
+## add
+ adds a new feature
+
+ * `_POST["lon"]` must contain feature longitude
+ * `_POST["lat"]` must contain feature latitude
+ * `_POST["title"]` may contain feature title
+ * `_POST["description"]` may contain feature description
+ * `_FILES["image_file"]` may contain an uploaded image
+
+## del
+ to delete an existing feature
+
+ * `_POST["fid"]` must contain feature id
+
+## update
+ to modify an existing feature
+
+ * `_POST["fid"]` must contain feature id
+ * `_POST["lon"]` must contain feature longitude
+ * `_POST["lat"]` must contain feature latitude
+ * `_POST["title"]` may contain feature title
+ * `_POST["description"]` may contain feature description
+
+ if `_POST["keep_img"]` is "yes", feature image will not be modified.
+ Otherwise, `_FILES["photo_file"]` is checked for an uploaded file. If
+ `_FILES["photo_file"]` is empty, existing feature image will be deleted.
+ Otherwise, uploaded file will replace current feature image.
+
+# server reply to client:
+Server replies with xml content. In an ideal world, reply would be just xml
+content. But due to technical issues, syp must wrap xml content in the body of a
+html document. So for example, instead of sending 
+
+`<error reason="unauthorized"></error>`
+
+as _text/xml_, syp sends
+
+`<html><head></head><body><error reason="unauthorized"></error></body></html>`
+
+as _text/html_
+
+## error handling:
+
+ reply is something like `<error reason="?reason?"></error>` with _?reason?_ can be either:
+
+ * `unauthorized`: user is not authorized to execute request, or authentication failed
+ * `server`: an error occured on server side (such as database problem)
+ * `request`: server could not understand request
+ * `toobig`: uploaded file was too big
+ * `notation`: uploaded file was not an image
+ * `nochange`: when trying to update a feature, there is nothing to update (ie: no field of the feature has changed)
+
+## success handling:
+
+ * `<success request="auth"></success>`:
+     authentication was successfull
+
+ * `<success request="del">
+     <feature>
+        <id>?id?</id>
+     </feature>
+    </success>`:
+     deletion was successfull. _?id?_ was property of deleted feature
+
+*  `<success request="add">
+     <feature>
+       <id>?id?</id>
+       <imgurl>?imgurl?</imgurl>
+       <description>?description?</description>
+       <heading>?heading?</heading>
+       <lon>?lon?</lon>
+       <lat>?lat?</lat>
+     </feature>
+   </success>`:
+     addition was successfull. _?id?_, _?imgurl?_, _?description?_,
+     _?heading?_, _?lon?_, _?lat?_ are properties of added feature;
+
+*  `<success request="update">
+      <feature>
+        <id>?id?</id>
+        <imgurl>?imgurl?</imgurl>
+        <description>?description?</description>
+        <heading>?heading?</heading>
+        <lon>?lon?</lon>
+        <lat>?lat?</lat>
+     </feature>
+   </success>`:
+     update was successfull. _?id?_, _?imgurl?_, _?description?_, _?heading?_,
+     _?lon?_, _?lat?_ are properties of updated feature;
index 7706506420d5615de4d7a98b5cdff4a8c4d1d855..c88367a46277fb081309b020288e58eed88a6647 100644 (file)
@@ -2,53 +2,22 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
    license. */
 
-// XXX: put that function somewhere else. It's used in changes.php
-function relative_path ($url_str) {
-    $url = parse_url ($url_str);
-    if ($url ['host'] != $_SERVER ['HTTP_HOST']) {
-        return null;
-    }
-
-    // first strip common directory names
-    $url_path = split ('/', $url ['path']);
-    $script_path = split ('/', $_SERVER ['SCRIPT_NAME']);
-    $len = min (count ($url_path), count ($script_path));
-    while ($url_path [0] == $script_path [0]) {
-        array_shift ($url_path);
-        array_shift ($script_path);
-    }
-
-    // $url_path contains $script_path; abort
-    if (count ($script_path) == 0) { 
-        return null;
-    }
-
-    // now, create relative path
-    $relpath = "";
-    for ($i = 0; $i < (count ($script_path) - 1); $i++) {
-        $relpath .= "../";
-    }
-    $relpath .= join ("/", $url_path);
-    return $relpath;
-}
-
 class feature {
-    private $imgurl = null;
-    private $title = null;
-    private $description = null;
+    private $id = null;
     private $lon = null;
     private $lat = null;
+    private $imgpath = null;
+    private $title = null;
+    private $description = null;
 
     const err_lonlat_invalid = 1;
 
-    function __construct ($imgurl, $title, $description, $lon, $lat) {
-        // imgurl
-        // tries to transform full url in relative path
-        $relpath = relative_path ($imgurl);
-        if (isset ($relpath)) {
-            $this->imgurl = $relpath;
-        } else {
-            $this->imgurl = $imgurl;
+    function __construct ($id, $lon, $lat, $imgpath, $title, $description) {
+        $this->imgpath = $imgpath;
+
+        // id
+        if (isset ($id)) {
+            $this->id = $id;
         }
 
         // title
@@ -81,25 +50,6 @@ class feature {
         throw new Exception ('properties can only be set in constructor');
     }
 
-    public function imgurl_exists () {
-        if (file_exists ($this->imgurl)) {
-            return true;
-        }
-        if (!function_exists ("curl_init")) {
-            return false;
-        }
-        $handle   = curl_init ($this->imgurl);
-        if ($handle === false)
-        {
-            return false;
-        }
-        curl_setopt ($handle, CURLOPT_NOBODY, true);
-        curl_setopt ($handle, CURLOPT_CONNECTTIMEOUT, 10);
-        curl_setopt ($handle, CURLOPT_TIMEOUT, 10);
-        $connectable = curl_exec ($handle);
-        curl_close ($handle);  
-        return $connectable;
-    }
 }
 
 interface anydbConnection {
@@ -147,8 +97,8 @@ interface anydbConnection {
     public function checkpwdmd5($usrname, $pwd_md5);
 
     /*
-     * saves feature in database. Returns false if $feature does not exist and
-     * if $feature->imgurl is not accessible; returns true otherwise.
+     * saves feature in database. If feature has an id, feature will be updated
+     * in database; otherwise it will be created. Returns saved feature
      */
     public function save_feature($feature);
 
@@ -159,9 +109,9 @@ interface anydbConnection {
     public function delete_feature($feature);
 
     /*
-     * Returns feature with given imgurl. If none exists, returns null.
+     * Returns feature with given id. If none exists, returns null.
      */
-    public function getfeature($imgurl);
+    public function getfeature($id);
 
     /*
      * returns an array of available features
@@ -169,9 +119,9 @@ interface anydbConnection {
     public function listfeatures();
 
     /*
-     * returns true if a feature with imgurl exists
+     * returns true if a feature with imgpath exists
      */
-    public function imgurl_exists($imgurl);
+    public function imgpath_exists($imgpath);
 
     /*
      * returns Minimum Bounding Rectangle containing all feature locations.
index 1e20c34e526fde9ff2a18134c35bfec519bb75a0..e4c61d2c8e629a6fa2ccd6ddbbe639c07a1f2d1e 100644 (file)
@@ -2,7 +2,7 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
    license. */
 
-require ("./inc/db/anydb.php");
+require_once ("./inc/db/anydb.php");
 
 class mysqlConnection implements anydbConnection {
     var $link = null;
@@ -44,12 +44,14 @@ class mysqlConnection implements anydbConnection {
         $query = sprintf ("CREATE TABLE " .
                            ($error_if_exists ? " " : "IF NOT EXISTS ") .
                             "%sitems (
-                                imgurl VARCHAR(255),
+                                id MEDIUMINT NOT NULL AUTO_INCREMENT,
+                                location POINT,
                                 title VARCHAR(127),
                                 description TEXT,
-                                location POINT,
+                                imgpath VARCHAR(255),
                                 date DATETIME,
-                                PRIMARY KEY (imgurl)
+                                user VARCHAR(255),
+                                PRIMARY KEY (id)
                             );", $this->dbprefix);
         $this->_execute_query ($query);
     }
@@ -84,65 +86,67 @@ class mysqlConnection implements anydbConnection {
     }
 
     public function save_feature ($feature) {
-        $query = sprintf ("SELECT imgurl FROM %sitems WHERE imgurl = '%s'", 
-                           $this->dbprefix, 
-                           mysql_real_escape_string ($feature->imgurl)); 
-        $this->_execute_query ($query);
-        if (mysql_affected_rows ($this->link) == 0) {
-            if ($feature->imgurl_exists ()) {
+        try {
+            $id = $feature->id;
+        } catch (Exception $e) {}
+        if (isset ($id)) {
+            $query = sprintf ("UPDATE %sitems SET
+                                    imgpath='%s', 
+                                    title='%s', 
+                                    description='%s', 
+                                    location=GeomFromText('POINT(%s %s)')
+                            WHERE id = '%s';",
+                            $this->dbprefix,
+                            mysql_real_escape_string ($feature->imgpath),
+                            mysql_real_escape_string ($feature->title),
+                            mysql_real_escape_string ($feature->description),
+                            $feature->lon,
+                            $feature->lat,
+                            $id);
+                $this->_execute_query ($query);
+                return $feature;
+        } else {
               $query = sprintf ("INSERT INTO %sitems
-                                   (imgurl, title, description, location, date)
-                                   VALUES ('%s', '%s', '%s', 
-                                   GeomFromText('POINT(%s %s)'), NOW())", 
+                              (imgpath, title, description, location, date, user)
+                                VALUES ('%s', '%s', '%s', 
+                               GeomFromText('POINT(%s %s)'), NOW(), 'admin')", 
                               $this->dbprefix,
-                              mysql_real_escape_string ($feature->imgurl),
+                              mysql_real_escape_string ($feature->imgpath),
                               mysql_real_escape_string ($feature->title),
                               mysql_real_escape_string ($feature->description),
                               $feature->lon,
                               $feature->lat
                     );
+
                 $this->_execute_query ($query);
-                return true;
-            } else {
-                return false;
-            }
-        } else {
-            $query = sprintf ("UPDATE %sitems SET title='%s', description='%s',
-                               location=GeomFromText('POINT(%s %s)')
-                               where imgurl = '%s'",
-                             $this->dbprefix,
-                             mysql_real_escape_string ($feature->title),
-                             mysql_real_escape_string ($feature->description),
-                             $feature->lon,
-                             $feature->lat,
-                             mysql_real_escape_string ($feature->imgurl)
-                );
-            $this->_execute_query ($query);
-            return true;
+                $id = mysql_insert_id ();
+                return new feature ($id, $feature->lon, $feature->lat,
+                                    $feature->imgpath, $feature->title,
+                                    $feature->description);
         }
     }
 
     public function delete_feature ($feature) {
-        $query = sprintf ("DELETE from %sitems WHERE imgurl like '%s'",
+        $query = sprintf ("DELETE from %sitems WHERE id = '%s'",
                         $this->dbprefix,
-                        mysql_real_escape_string ($feature->imgurl));
+                        mysql_real_escape_string ($feature->id));
         $this->_execute_query ($query);
         return true;
     }
 
-    public function getfeature ($imgurl) {
-        $query = sprintf ("SELECT imgurl, title, description, AsText(location)
-                           AS location FROM %sitems WHERE imgurl like '%s';", 
-                        $this->dbprefix, mysql_real_escape_string ($imgurl));
+    public function getfeature ($id) {
+        $query = sprintf ("SELECT id, imgpath, title, description, AsText(location)
+                           AS location FROM %sitems WHERE id = '%s';", 
+                        $this->dbprefix, mysql_real_escape_string ($id));
         $row = mysql_fetch_assoc ($this->_execute_query ($query));
-        if (!isset ($row)) {
+        if ($row === false) {
             return null;
         }
         return $this->_feature_frow_row ($row);
     }
 
     public function listfeatures () {
-        $query = sprintf ("SELECT imgurl, title, description, AsText(location)
+        $query = sprintf ("SELECT id, imgpath, title, description, AsText(location)
                             AS location FROM %sitems;",
                           $this->dbprefix);
 
@@ -157,9 +161,9 @@ class mysqlConnection implements anydbConnection {
         return $features;
     }
 
-    public function imgurl_exists ($imgurl) {
-        $query = sprintf ("SELECT COUNT(*) FROM %sitems WHERE imgurl LIKE '%s';",
-                           $this->dbprefix, mysql_real_escape_string ($imgurl));
+    public function imgpath_exists ($imgpath) {
+        $query = sprintf ("SELECT COUNT(*) FROM %sitems WHERE imgpath LIKE '%s';",
+                           $this->dbprefix, mysql_real_escape_string ($imgpath));
         $res = mysql_fetch_array  ($this->_execute_query ($query), MYSQL_NUM);
         return ($res [0] >= 1) ? true : false;
     }
@@ -223,8 +227,8 @@ class mysqlConnection implements anydbConnection {
         $lon = $matches [1];
         $lat = $matches [2];
         try {
-            $feature = new feature ($row ["imgurl"], $row ["title"], $row
-                                    ["description"], $lon, $lat);
+            $feature = new feature ($row ["id"], $lon, $lat, $row ["imgpath"],
+                                    $row ["title"], $row ["description"]);
         } catch (Exception $e) {
             return null;
         }
diff --git a/inc/errors.php b/inc/errors.php
deleted file mode 100644 (file)
index 0eef29b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-/* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
-   license. */
-
-function access_denied() {
-   exit("access denied");
-}
-function request_error() {
-   exit("request error");
-}
-function server_error() {
-    exit("server error");
-}
-function feature_unavailable() {
-   exit("feature unavailable");
-}
-function request_success() {
-   exit("request accepted");
-}
-function file_too_big_error() {
-   exit("file too big");
-}
-function notanimage_error() {
-   exit("not an image");
-}
-function access_allowed() {
-    exit("access allowed");
-}
-?>
index 33a358f027b3c5df81caf543642abb06c1b05e0a..8ec2d4ba62055e6a23b0bcd03314c2e0d1c02529 100644 (file)
@@ -6,7 +6,7 @@
 define ("DBHOST", "localhost");
 
 // database user
-define ("DBUSER", "root");
+define ("DBUSER", "syp");
 
 // database password
 define ("DBPWD", "");
@@ -29,4 +29,10 @@ define ("UPLOADDIR", "upload");
 
 // title of your website
 define ("SITETITLE", "SYP");
+
+// url of images directory. If empty, syp will try to guess it from syp admin
+// location and UPLOADDIR. So, you need to set that variable if host is
+// different between syp admin and images directory. Or if protocol is
+// different (for example, if syp admin is https and images directory is http)
+define ("IMGSDIRURL", "");
 ?>
index 50eb995f29f484cb9fa59920bde03a1d0375da95..a0824c8dea4293d218717c16714a3dfecef26edf 100644 (file)
@@ -2,6 +2,8 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
    license. */
 
+require_once ("inc/settings.php");
+
 function unquote($gpc_str) {
    if (!isset ($gpc_str)) {
        return $gpc_str;
@@ -12,4 +14,52 @@ function unquote($gpc_str) {
        return $gpc_str;
    }
 }
+
+function full_url_from_filename ($filename) {
+    if (defined ("IMGSDIRURL") && (strlen (IMGSDIRURL) != 0)) {
+        return rtrim (IMGSDIRURL, '/') . "/" . rawurlencode ($filename);
+    }
+
+    $rel_path = UPLOADDIR . "/" . rawurlencode ($filename);
+
+    while (substr($rel_path, 0, 2) == "./") { // strips ./
+        $rel_path = substr ($rel_path, 2);
+    }
+
+    if ($rel_path [0] == "/") {
+        $path = $rel_path;
+    } else {
+        $script_dir = dirname ($_SERVER ["SCRIPT_NAME"]);
+        while ((substr ($rel_path, 0, 3) == "../") &&
+                (strlen($script_dir) != 0)) {
+            $rel_path = substr ($rel_path, 3);
+            while (substr($rel_path, 0, 2) == "./") {
+                $rel_path = substr ($rel_path, 2);
+            }
+             $script_dir = substr ($script_dir, 0, strrpos ($script_dir, "/"));
+        }
+        if ((strlen ($script_dir) == 0) && (substr ($rel_path, 0, 3) == "../")) {
+            return null;
+        }
+        $path = "$script_dir/$rel_path";
+    }
+
+    $host = $_SERVER ["HTTP_HOST"];
+    $port = $_SERVER ["SERVER_PORT"];
+    if ($_SERVER ["HTTPS"] == "on") {
+        $proto = "https";
+    } else {
+        $proto = "http";
+    }
+
+    if (($port == "80" && $proto == "http") ||
+        ($port == "443" && $proto == "https")) {
+        $port = "";
+    } else {
+        $port = ":$port";
+    }
+
+    return "$proto://$host$port$path";
+}
+
 ?>
index cd32d0ec1aaab102a369147032deb855c68d7ab5..4b640a3f3bb1d657e0c9a21df3809b1f7916d9b9 100644 (file)
--- a/index.php
+++ b/index.php
@@ -2,8 +2,8 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
    license. */
 
-require ("./inc/settings.php");
-require ("./inc/db/mysql.php");
+require_once ("./inc/settings.php");
+require_once ("./inc/db/mysql.php");
 
 try {
     $connection->connect (DBHOST, DBUSER, DBPWD, DBNAME, DBPREFIX);
index f816cfbeb25f1f8b060779ea105bef8afed77095..bcf5f1fc9757c50b33097c72bec649a0c9f738f4 100644 (file)
--- a/items.php
+++ b/items.php
@@ -2,9 +2,9 @@
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
    license. */
 
-require ("./inc/settings.php");
-require ("./inc/errors.php");
-require ("./inc/db/mysql.php");
+require_once ("./inc/settings.php");
+require_once ("./inc/utils.php");
+require_once ("./inc/db/mysql.php");
 
 function main ($features) {
 
@@ -17,34 +17,42 @@ function main ($features) {
         printf ('    <name>%s</name>', SITETITLE);
     }
     foreach ($features as $feature) {
+        $id = $feature->id;
         $title = htmlspecialchars ($feature->title, ENT_QUOTES);
         $description = htmlspecialchars ($feature->description, ENT_QUOTES);
-        if (strpos ($feature->imgurl, "http://") === 0) {
-            $imgurl = "http://" . rawurlencode (substr($feature->imgurl, 7));
-        } else if (strpos ($feature->imgurl, "https://") === 0) {
-            $imgurl = "https://" . rawurlencode (substr ($feature->imgurl, 8));
-        } else {
-            $imgurl = rawurlencode ($feature->imgurl);
-        }
-        $imgurl = str_replace ('%2F', '/', $imgurl);
+        $imgurl = ($feature->imgpath ? 
+                    full_url_from_filename ($feature->imgpath)
+                    : "");
         $lon = $feature->lon;
         $lat = $feature->lat;
         $alt = (strlen ($title) > 60) ?
                     (substr ($title, 0, 57) . '...') :
                     $title;
 
+        if ($imgurl) {
+            $imgurlHTML = sprintf ('<img alt="%s" src="%s">', $alt, $imgurl);
+        } else {
+            $imgurlHTML = "";
+        }
+
+        if ($description) {
+            $descriptionHTML = sprintf ('<p>%s</p>', $description) ;
+        } else {
+            $descriptionHTML = "";
+        }
+
         printf ('
-        <Placemark>
+        <Placemark id="%s">
             <name>%s</name>
             <description><![CDATA[
-                <p>%s</p>
-                <img alt="%s" src="%s">
+                %s
+                %s
             ]]></description>
             <Point>
                 <coordinates>%s,%s</coordinates>
             </Point>
         </Placemark>
-', $title, $description, $alt, $imgurl, $lon, $lat);
+', $id, $title, $descriptionHTML, $imgurlHTML, $lon, $lat);
     }
 
     echo' </Document>
@@ -55,7 +63,7 @@ try {
     $connection->connect (DBHOST, DBUSER, DBPWD, DBNAME, DBPREFIX);
     $features = $connection->listfeatures ();
 } catch (Exception $e) {
-    server_error ();
+    exit ("server error");
 }
 header ("Content-type: application/vnd.google-earth.kml+xml");
 main ($features);
index 0709878efef7476b55ea339c6e93b5166df4bdd3..c7be3dbb00abedaf53790952a4459e23bee1ea39 100644 (file)
 /* Copyright (c) 2009 Arnaud Renevier, Inc, published under the modified BSD
  * license. */
 
-OpenLayers.Control.SelectDragFeature = 
-    OpenLayers.Class (OpenLayers.Control.SelectFeature, {
-
-    lastPixel: null,
-    dragFeature: null,
-
-    startPixel : null,
+// drag feature with tolerance
+OpenLayers.Control.SypDragFeature = OpenLayers.Class (OpenLayers.Control.DragFeature, {
+    startPixel: null,
+    dragStart: null,
     pixelTolerance : 0,
     timeTolerance: 300,
-    dragStart: null, 
-
-    onComplete: function (feature, pixel) {},
-    onCancel: function (feature, pixel) {},
-
-    initialize: function (layers, options) {
-        var callbacks = {
-            over: this.overFeature,
-            out: this.outFeature
-        };
-        this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
-
-        OpenLayers.Control.SelectFeature.prototype.initialize.apply(this,
-                                                                    arguments);
-
-        var handlers = {
-            drag: new OpenLayers.Handler.Drag(
-                this, OpenLayers.Util.extend({
-                    down: this.downFeature,
-                    move: this.moveFeature,
-                    up: this.upFeature,
-                    out: this.cancel,
-                    done: this.doneDragging
-                })
-            )
-        };
-        this.handlers = OpenLayers.Util.extend(handlers, this.handlers);
-    },
-
-    doneDragging: function (pixel) {
-        var passesTimeTolerance = 
-            (new Date ()).getTime () > this.dragStart + this.timeTolerance; 
-        var xDiff = this.startPixel.x - pixel.x;
-        var yDiff = this.startPixel.y - pixel.y;
-        var passesPixelTolerance = Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2))
-                                     > this.pixelTolerance;
-        if(passesTimeTolerance && passesPixelTolerance){
-            this.onComplete(this.dragFeature, pixel);
-        } else {
-            var res = this.map.getResolution ();
-            this.dragFeature.geometry.move(res * (this.startPixel.x - pixel.x),
-                                           res * (pixel.y - this.startPixel.y));
-            this.onCancel(this.dragFeature,pixel);
-        }
 
-        this.layer.drawFeature(this.dragFeature, "select");
+    downFeature: function(pixel) {
+        OpenLayers.Control.DragFeature.prototype.downFeature.apply(this, arguments);
+        this.dragStart = (new Date()).getTime(); 
+        this.startPixel = pixel; 
     },
 
-    cancel: function () {
-        this.handlers.drag.deactivate ();
-        this.over = false;
-    },
+    doneDragging: function(pixel) {
+        OpenLayers.Control.DragFeature.prototype.doneDragging.apply(this, arguments);
+        // Check tolerance. 
+        var passesTimeTolerance =  
+                    (new Date()).getTime() > this.dragStart + this.timeTolerance; 
 
-    clickFeature: function (feature) {
-        OpenLayers.Control.SelectFeature.prototype.clickFeature.apply(this,
-                                                                      arguments);
-        if (Admin.Utils.indexOf(this.layer.selectedFeatures, feature) == -1) {
-            this.dragDisable ();
-        }
-    },
+        var xDiff = this.startPixel.x - pixel.x; 
+        var yDiff = this.startPixel.y - pixel.y; 
 
-    upFeature: function (pixel) {
-    },
+        var passesPixelTolerance =  
+        Math.sqrt(Math.pow(xDiff,2) + Math.pow(yDiff,2)) > this.pixelTolerance; 
 
-    moveFeature: function (pixel) {
-        if (Admin.Utils.indexOf(this.layer.selectedFeatures,
-                                this.dragFeature) != -1) {
-            var res = this.map.getResolution ();
-            this.dragFeature.geometry.move(res * (pixel.x - this.lastPixel.x),
-                                   res * (this.lastPixel.y - pixel.y));
-            this.layer.drawFeature(this.dragFeature, "temporary");
-            this.lastPixel = pixel;
+        if(passesTimeTolerance && passesPixelTolerance){ 
+            this.onComplete(this.feature, pixel);    
+        } else { 
+            var feature = this.feature; 
+            var res = this.map.getResolution(); 
+            this.feature.geometry.move(res * (this.startPixel.x - this.lastPixel.x), 
+                    res * (this.lastPixel.y - this.startPixel.y)); 
+            this.layer.drawFeature(this.feature); 
         }
+        this.layer.drawFeature(this.feature, "select");
     },
 
-    downFeature: function (pixel) {
-        this.handlers.feature.down = pixel;
-
-        if (Admin.Utils.indexOf(this.layer.selectedFeatures,
-                                this.dragFeature) != -1) {
-            this.dragStart = (new Date ()).getTime ();
-            this.startPixel = pixel;
-            this.lastPixel = pixel;
-        } 
-    },
-
-    deactivate: function () {
-        this.dragDisable ();
-        return OpenLayers.Control.SelectFeature.prototype.deactivate.apply(
-            this, arguments
-        );
+    moveFeature: function(pixel) {
+        OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments);
+        this.layer.drawFeature(this.feature, "temporary");
     },
 
     overFeature: function (feature) {
-        if (Admin.Utils.indexOf(this.layer.selectedFeatures, feature) != -1) {
-            this.dragEnable(feature);
-        }
-        OpenLayers.Control.SelectFeature.prototype.overFeature.apply(this,
-                                                                     arguments);
-    },
-
-    outFeature: function (feature) {
-        this.dragDisable ();
-        OpenLayers.Control.SelectFeature.prototype.outFeature.apply(this,
-                                                                    arguments);
-    },
-
-    select: function (feature) {
-        this.dragEnable(feature);
-        OpenLayers.Control.SelectFeature.prototype.select.apply(this,
-                                                                arguments);
-    },
-
-    dragDisable: function () {
-        this.over = false;
-        if(!this.handlers.drag.dragging) {
-            this.handlers.drag.deactivate ();
-            OpenLayers.Element.removeClass(
-                this.map.viewPortDiv, this.displayClass + "Over"
-            );
-            this.dragFeature = null;
-        }
-    },
-
-    dragEnable: function (feature) {
-        if(!this.handlers.drag.dragging) {
-            this.dragFeature = feature;
-            this.handlers.drag.activate ();
-            this.over = true;
-            OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over");
-        } else {
-            if(this.dragFeature.id == feature.id) {
-                this.over = true;
-            } else {
-                this.over = false;
-            }
+        // can only drag and drop currently selected feature
+        if (feature != Admin.currentFeature) {
+            return;
         }
+        OpenLayers.Control.DragFeature.prototype.overFeature.apply(this, arguments);
     },
 
-    setMap: function (map) {
-        this.handlers.drag.setMap(map);
-        OpenLayers.Control.SelectFeature.prototype.setMap.apply(this, arguments);
-    },
-
-    CLASS_NAME: "OpenLayers.Control.SelectDragFeature"
+    CLASS_NAME: "OpenLayers.Control.SypDragFeature"
 });
 
 var Admin = {
@@ -170,8 +67,12 @@ var Admin = {
     map: null,
     baseLayer: null,
     dataLayer: null,
-    selectControl: null,
-    clickControl: null,
+    selFeatureControl: null,
+    moveFeatureControl: null,
+    addFeatureControl: null,
+
+    currentFeature: null,
+    currentFeatureLocation: null,
 
     init: function () {
         this.map = new OpenLayers.Map ("map", {
@@ -187,14 +88,16 @@ var Admin = {
          this.dataLayer = this.createDataLayer ();
          this.map.addLayers([this.baseLayer, this.dataLayer]);
 
-         this.selectControl = this.createSelectDragControl ();
-         this.map.addControl(this.selectControl);
-         this.selectControl.activate ();
+         // controls
+         this.selFeatureControl = this.createSelectFeatureControl();
+         this.map.addControl(this.selFeatureControl);
+         this.moveFeatureControl = this.createMoveFeatureControl();
+         this.map.addControl(this.moveFeatureControl);
+         this.addFeatureControl = this.createNewfeatureControl();
+         this.map.addControl(this.addFeatureControl);
 
-         this.clickControl = this.createClickControl ();
-         this.map.addControl(this.clickControl);
-
-         var centerBounds = new OpenLayers.Bounds ();
+         // position
+         var centerBounds = new OpenLayers.Bounds();
 
          var mapProj = this.map.getProjectionObject();
          var sypOrigProj = new OpenLayers.Projection("EPSG:4326");
@@ -206,7 +109,24 @@ var Admin = {
 
          centerBounds.extend(bottomLeft);
          centerBounds.extend(topRight);
+
+         // at that moment, ie does not know size of the map, we need to update
+         // manually
+         this.map.updateSize();
          this.map.zoomToExtent(centerBounds);
+
+         this.reset();
+    },
+
+    reset: function() {
+        this.addFeatureControl.deactivate();
+        this.moveFeatureControl.deactivate();
+        this.selFeatureControl.activate();
+        this.checkForFeatures();
+        $("#newfeature_button").show().val("ajouter un emplacement");
+        $("#newfeature_button").unbind("click").click(function () {
+            Admin.addNewFeature();
+        });
     },
 
     createBaseLayer: function () {
@@ -237,332 +157,148 @@ var Admin = {
             format: OpenLayers.Format.KML, 
             projection: this.map.displayProjection,
             eventListeners: { scope: this,
-                loadend: this.checkForFeatures,
-                featureremoved: this.checkForFeatures,
-                featureadded: this.checkForFeatures
+                loadend: this.checkForFeatures
             }
        });
 
         return layer;
     },
 
-    createSelectDragControl: function () {
-        var control = new OpenLayers.Control.SelectDragFeature(
+    createMoveFeatureControl: function () {
+        var control = new OpenLayers.Control.SypDragFeature(
                 this.dataLayer, {
-                        onSelect: this.onFeatureSelect,
-                        onUnselect: this.onFeatureUnselect,
-                        onComplete: OpenLayers.Function.bind(this.onDragComplete.action,
-                                                             this.onDragComplete),
-                        toggle: true,
-                        clickout: false
-                               });
+                         });
         return control;
     },
 
-    checkForFeatures: function () {
-        var features = this.dataLayer.features;
-        if (features.length == 0) {
-            $("#modify_howto").css("visibility", "hidden");
-        } else {
-            $("#modify_howto").css("visibility", "visible");
-        }
+    createSelectFeatureControl: function () {
+        var control = new OpenLayers.Control.SelectFeature(
+                this.dataLayer, {
+                        onSelect: OpenLayers.Function.bind(this.onFeatureSelect, this)
+                         });
+        return control;
     },
 
-    createClickControl: function () {
+    createNewfeatureControl: function () {
         var control = new OpenLayers.Control ();
-        var handler = new OpenLayers.Handler.Click(control, {});
+        var handler = new OpenLayers.Handler.Click(control, {
+                'click': OpenLayers.Function.bind(FeatureMgr.add, FeatureMgr)
+            });
         control.handler = handler;
         return control;
     },
 
-    addMarkerNewImage: function (imgurl) {
-        return function (evt) {
-            FeatureMgr.itemDeleter.lock(imgurl);
-            var pos = this.map.getLonLatFromViewPortPx(evt.xy);
-            var point = new OpenLayers.Geometry.Point(pos.lon, pos.lat);
-            var desc = '<img src="' + imgurl + '">';
-            var feature = new OpenLayers.Feature.Vector(point, {
-                                                               name: '', 
-                                                               description: desc
-                                                               });
-            Admin.dataLayer.addFeatures([feature]);
-            Admin.selectControl.activate ();
-
-            // When adding a feature and then selecting it, we set render style
-            // to "default" and right after, we set it to "select". When
-            // rendering backend is SVG, that triggers a modification of href
-            // attribute right after having inserted image with a different
-            // href. Unfortunately, webkit does not like that (see
-            // webkit#26392). That's why we need to wrap selection in a
-            // timeout.
-            var renderer = Admin.dataLayer.renderer;
-            if (renderer && renderer.CLASS_NAME == "OpenLayers.Renderer.SVG") {
-                window.setTimeout(function () { 
-                    Admin.selectControl.select(feature);
-                    Admin.saveFeature(feature);
-                } ,0);
-            } else {
-                Admin.selectControl.select(feature);
-                Admin.saveFeature(feature);
-            }
-            $("#addphoto_button").val("ajouter une autre image");
-        }
-    },
-
     onFeatureSelect: function (feature) {
-        Admin.closeNewimage();
-
-        $("#img").attr('src', '');
-        $("#editor").show();
-        $("#features_success").css("visibility", "hidden");
-
-        // we use the real onclick method otherwise: jquery method would not
-        // execute because of input.change method
-        $("#deletephoto_button").get(0).onclick = function () {
-            Admin.clearChangeTimeouts();
-            var imgurl = $("#img").attr("src");
-            Admin.deleteFeature(feature, imgurl);
-        };
-
-        $("#title_input").val(feature.attributes.name);
-
-        var fullDesc = $(feature.attributes.description).parent();
-        $("#desc_input").val(fullDesc.find('p').text());
-        $("#img").attr('src', fullDesc.find('img').attr('src'));
-
-        $(".input").each(function () {
-            this.curVal = this.value;
-        });
-
-         // Change event happens if an input has change if we leave that field.
-         // But we want data to be saved even if user does not blur input field
-         // (for example, he types something in the box and then does not touch
-         // it's computer). So, every 60 seconds, we check if value of input
-         // has changed. More precisely, every 60 seconds, we wait 3 seconds to
-         // see if input value is still changing and if not, it probably means
-         // user is not modifying data anymore. In that case, we save.
-
-        $(".input").focus(function (evt) {
-            var self = this;
-            this.curVal = this.value;
-            this.checkTimer = window.setInterval(function () {
-                 if (self.value != self.curVal) {
-                    $("#features_success").css("visibility", "hidden");
-                    var newVal = self.value;
-                     this.saveTimeout = window.setTimeout(function () {
-                         if (self.value == newVal) {
-                            self.curVal = self.value;
-                            Admin.saveFeature(feature);
-                         }
-                     }, 3 * 1000);
-                 }
-            }, 6 * 1000);
-        })
-
-        $(".input").blur(function (evt) {
-            if (this.checkTimer) {
-                window.clearInterval(this.checkTimer);
-                this.checkTimer = null;
-            }
-            if (this.saveTimeout) {
-                window.clearTimeout(this.saveTimeout);
-                this.saveTimeout = null;
-            }
-        });
-
-        $(".input").change(function (evt) {
-            if (this.curVal != this.value) {
-                $("#features_success").css("visibility", "hidden");
-                this.curVal = this.value;
-                Admin.saveFeature(feature);
-            }
-        });
-
-        $("#title_input").blur()
-        $("#title_input").focus()
-        $("#title_input").select()
-
-        $("#dragdrop_howto").css("visibility", "visible");
+        this.showEditor(feature);
+        FeatureMgr.reset();
+        this.selFeatureControl.deactivate();
+        this.moveFeatureControl.activate();
     },
 
-    onFeatureUnselect: function (feature) {
-        Admin.closeEditor();
-        $("#features_connect_error").hide();
-        $("#deletephoto_button").get(0).onclick = null;
-
-        // if user unselects feature, save modifications without waiting
-        var needsSaving = Admin.onDragComplete.timeout ? true: false;
-        $(".input").each(function () {
-            if (this.value != this.curVal) {
-                needsSaving = true;
-            }
-            if (this.checkTimer) {
-                window.clearInterval(this.checkTimer);
-                this.checkTimer = null;
-            }
-            if (this.saveTimeout) {
-                window.clearTimeout(this.saveTimeout);
-                this.saveTimeout = null;
-            }
-        });
-
-        if (needsSaving) {
-            $("#features_success").css("visibility", "hidden");
-            Admin.saveFeature(feature);
+    closeEditor: function() {
+        if (this.currentFeature && this.currentFeature.layer) {
+            this.selFeatureControl.unselect(this.currentFeature);
         }
-
-        window.clearTimeout(Admin.onDragComplete.timeout);
-        Admin.onDragComplete.timeout = null;
-    },
-
-    addNewFeature: function () {
-        this.selectControl.unselectAll();
-        $("#features_success").css("visibility", "hidden");
-        $("#features_connect_error").hide();
-        $("#addphoto_button").attr("disabled", "disabled");
-        $("#newimage").show();
-        $("#file_form").show();
-        $("#file_form").get(0).reset();
-        $("#newimage_input").change(function () {
-            $("#newimage_error").hide();
-            if (OpenLayers.Util.getBrowserName() == "msie") {
-                if ($("#file_form").find('input[type="submit"]').length == 0) {
-                    $("#file_form").append(
-                        '<div class="center" style="margin-top: 15px">' + 
-                        '<input type="submit" class="center"><div>');
-                    $('#file_form > div > input[type="submit"]').focus();
-                    $("#file_form").one("submit", function () {
-                        $("#file_form").find('input[type="submit"]').
-                                        parent().remove();
-                        $("#newimage_throbber").css("visibility", "visible");
-                    });
-                }
-            } else {
-                $("#file_form").submit();
-                $("#newimage_throbber").css("visibility", "visible");
+        this.currentFeature = null;
+        this.currentFeatureLocation = null;
+        $("#img").removeAttr('src');
+        $("#img").parent().html($("#img").parent().html());
+        $("#img").parent().show();
+        $("#title, #description").val("");
+        $("#editor").hide();
+        // do it once before hidding and once after hidding to work in all cases
+        $("#title, #description").val(""); 
+        $("#image_file").parent().html($("#image_file").parent().html());
+        $(document).unbind("keydown");
+        this.checkForFeatures();
+        this.reset();
+    },
+
+    showEditor: function (feature) {
+        $("#newfeature_button").hide();
+        if (feature.fid) {
+            $("#delete").show();
+        } else {
+            $("#delete").hide();
+        }
+        $(document).unbind("keydown").keydown(function(e) { 
+            if (e.keyCode == 27) {
+                Admin.cancelCurrentFeature()
+                e.preventDefault();
             }
-            $("#fileframe").one("load", FeatureMgr.fileFrameLoad);
         });
-        // works in webkit and in ie
-        // XXX: we want to call
-        // the real click method of newimage_input, not jquery click method
-        // because jquery click method prevents change listener to be called
-        // click event is running.
-        if (OpenLayers.Util.getBrowserName() != "msie")  {
-            // XXX: in ie, it prevents submiting form
-            $("#newimage_input").get(0).click();
+        this.currentFeature = feature;
+        this.currentFeatureLocation = new OpenLayers.Pixel(feature.geometry.x, feature.geometry.y);
+        $("#editor").show();
+        $("#instructions").text("Vous pouvez déplacer le marqueur en effectuant un glisser-déposer.");
+        $("#title").val(feature.attributes.name);
+        var fullDesc = $(feature.attributes.description).parent();
+        $("#description").val(fullDesc.find('p').text());
+        var src = fullDesc.find('img').attr('src');
+        if (src) {
+            $("#img").parent().show();
+            $("#img").attr('src', src);
+            $("#image_file").parent().hide();
+            $("#image_delete").show();
+        } else {
+            $("#img").parent().hide();
+            $("#image_file").parent().show();
+            $("#image_delete").hide();
         }
-        // works in opera
-        $("#newimage_input").focus(); 
+        $("#title").select().focus(); 
     },
 
-    closeNewimage: function () {
-        if ($("#newimage").css("display") == "none") {
-            return;
+    checkForFeatures: function () {
+        if (this.dataLayer.features.length != 0) {
+            $("#instructions").text("Pour modifier les données d'une image, sélectionnez le marqueur correspondant.");
         }
-        $("#newimage_input").unbind('change');
-        $("#fileframe").unbind('load');
-        $("#addphoto_button").removeAttr("disabled");
-        $("#newimage_error").hide();
-        $("#newimage_throbber").css("visibility", "hidden").show();
-        $("#newimage_input").val('');
-        FeatureMgr.itemDeleter.add($("#newimage_preview").attr("src"));
-        $("#newimage").hide();
-        $("#newimage_preview").removeAttr("src");
-        $("#newimage_preview").hide();
-        $("#modify_howto").css("visibility", "visible");
-        $("#newimage_warn").hide();
-        this.clickControl.handler.callbacks.click = null;
-        this.clickControl.deactivate();
-        this.selectControl.activate();
-    },
-
-    closeEditor: function () {
-        $("#editor").hide();
-        $(".input").unbind('change');
-        $(".input").unbind('focus');
-        $(".input").unbind('blur');
-        $("#dragdrop_howto").css("visibility", "hidden");
     },
 
-    clearChangeTimeouts: function () {
-        $(".input").each(function (){
-            if (this.checkTimer) {
-                window.clearInterval(this.checkTimer);
-                this.checkTimer = null;
-            }
-            if (this.saveTimeout) {
-                window.clearTimeout(this.saveTimeout);
-                this.saveTimeout = null;
+    addNewFeature: function () {
+        function cancel() {
+            $(document).unbind("keydown");
+            Admin.reset()
+        }
+        $(document).unbind("keydown").keydown(function(e) { 
+            if (e.keyCode == 27) {
+                e.preventDefault();
+                cancel();
             }
         });
-    },
-
-    onDragComplete: {
-        timeout: null,
-        // we wait 3 seconds before saving in case user drags marker again
-        action: function (feature, pixel) {
-            if (this.timeout) {
-                window.clearTimeout(this.timeout);
-            }
-            var self = this;
-            this.timeout = window.setTimeout(function () {
-                self.timeout = null;
-                Admin.saveFeature(feature);
-            }, 3000);
-        }
-    },
-
-    saveFeature: function (feature) {
-        var imgurl = $("#img").attr("src");
-        var title = $("#title_input").val();
-        var description = $("#desc_input").val();
-
-        feature.attributes.name = this.Utils.escapeHTML(title);
-        feature.attributes.description = "<p>" + 
-                                          this.Utils.escapeHTML(description) +
-                                          "</p>" +
-                                          "<img class=\"" +
-                                          $("#img").attr("class") +
-                                          "\"" +
-                                          " src=\"" + imgurl + "\">";
 
-        var x = feature.geometry.x;
-        var y = feature.geometry.y;
+        $("#newfeature_button").val("annuler");
+        $("#newfeature_button").unbind("click").click(cancel);
 
-        var mapProj = feature.layer.map.getProjectionObject();
-        var lonlat = new OpenLayers.LonLat(x, y).
-                             transform(mapProj,
-                                       new OpenLayers.Projection("EPSG:4326"));
-
-        FeatureMgr.itemDeleter.unlock(imgurl);
-        FeatureMgr.saveFeature(feature, imgurl, title, description, lonlat);
+        $("#instructions").text("Cliquez sur la carte pour ajouter un marqueur.");
+        this.selFeatureControl.deactivate();
+        this.addFeatureControl.activate();
+        FeatureMgr.reset();
     },
 
-    deleteFeature: function (feature, imgurl) {
-        Admin.dataLayer.destroyFeatures([feature]);
-        FeatureMgr.deleteFeature(imgurl);
+    cancelCurrentFeature: function() {
+        if (AjaxMgr.running) {
+            return;
+        }
+        var feature = this.currentFeature;
+        if (feature.fid) {
+            FeatureMgr.move (feature, this.currentFeatureLocation);
+        } else {
+            this.dataLayer.removeFeatures([feature]);
+        }
+        this.closeEditor();
     },
 
     reloadLayer: function (layer) {
         layer.destroyFeatures();
         layer.loaded = false;
         layer.loadGML();
-        this.closeEditor();
-    },
-
-    connectErrorMsg: function (textStatus) {
-        if (textStatus == undefined) {
-            textStatus = "inconnue";
-        }
-        return "Une erreur de type " +
-                textStatus + 
-                " est survenue lors de la connexion au serveur." + 
-                " Veuillez réessayer ou contacter l'administrateur du site.";
     },
 
     Utils: {
         escapeHTML: function (str) {
+            if (!str) {
+                return "";
+            }
             return str.
              replace(/&/gm, '&amp;').
              replace(/'/gm, '&#39;').
@@ -585,250 +321,217 @@ var Admin = {
     }
 }
 
-var pwdMgr = {
-
-    init: function () {
-        $("#login_form").submit(this.submit);
-        $("#user_pwd").focus().select();
+var FeatureMgr = {
+    reset: function() {
+        this.commError("");
     },
 
-    submit: function () {
-        // removes focus from #user_pwd before disabling it. Otherwise, opera
-        // prevents re-focusing it after re-enabling it.
-        $("#user_pwd").blur(); 
-        $("#login_submit, #user_pwd").attr("disabled", "disabled");
-        $("#login_connect_error, #login_password_error").hide();
-        $("#pwd_throbber").css("visibility", "visible");
-        
-        var req = {
-            type: this.method,
-            url: this.action,
-            data:  { user_pwd: this.user_pwd.value },
-            success: pwdMgr.postSuccessCallback,
-            error: pwdMgr.postErrorCallback,
-            timeout: 10000
-        };
-
-        AjaxMgr.add(req);
-        return false;
+    add: function(evt) {
+        var map = Admin.map;
+        var pos = map.getLonLatFromViewPortPx(evt.xy);
+        feature = this.update (null, pos, "", "", "");
+        Admin.addFeatureControl.deactivate();
+        Admin.selFeatureControl.select(feature);
     },
 
-    postErrorCallback: function (data, textStatus) {
-        $("#pwd_throbber").css("visibility", "hidden");
-        $("#login_submit, #user_pwd").removeAttr("disabled");
-        var errorText = Admin.connectErrorMsg(textStatus);
-        $("#login_connect_error").text(errorText).show();
-        $("#user_pwd").focus().select();
+    move: function (feature, aLocation) {
+        if (!feature || !aLocation) {
+            return;
+        }
+        var curLoc = feature.geometry;
+        feature.geometry.move(aLocation.x - curLoc.x, aLocation.y - curLoc.y);
+        feature.layer.drawFeature(feature); 
     },
 
-    postSuccessCallback: function (data) {
-        $("#pwd_throbber").css("visibility", "hidden");
-        $("#login_submit, #user_pwd").removeAttr("disabled");
-        if (data == "access allowed") {
-            $("#login_area").hide();
+    update: function(feature, lonlat, imgurl, title, description) {
+        var point = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+        if (!feature) {
+            feature = new OpenLayers.Feature.Vector(point);
+            Admin.dataLayer.addFeatures([feature]);
         } else {
-            $("#login_password_error").show();
-            $("#user_pwd").focus().select();
+            this.move (feature, point);
         }
-    }
-}
-
-var FeatureMgr = {
-    saveFeature: function (feature, imgurl, title, description, lonlat) {
-        $("#features_success").text("La sauvegarde a été réalisée avec succès");
-        var req = {
-            type: "post",
-            url: "changes.php",
-            data:  { feature_imgurl: imgurl,
-                     feature_title: title,
-                     feature_description: description,
-                     feature_lon: lonlat.lon,
-                     feature_lat: lonlat.lat
-                    },
-            success: this.featureSuccessCallback,
-            error: this.featureErrorCallback,
-            timeout: 10000
-        };
-        AjaxMgr.add(req);
-    },
-
-    deleteFeature: function (imgurl) {
-        $("#features_success").text("La suppression a été réalisée avec succès");
-        var self = this;
-
-        var req = {
-            type: "post",
-            url: "changes.php",
-            data:  { feature_delete: imgurl },
-            success: function (data) { 
-                if (data == "request accepted") {
-                    Admin.closeEditor();
-                    self.itemDeleter.add(imgurl);
-                }
-                self.featureSuccessCallback(data);
-            },
-            error: this.featureErrorCallback,
-            timeout: 10000
-        };
-        AjaxMgr.add(req);
+        feature.attributes.name = title;
+        feature.attributes.description = "<p>" + Admin.Utils.escapeHTML(description) + "</p>"
+                                + "<img src=\"" + imgurl + "\">"
+        return feature;
+    },
+
+    del: function (feature) {
+        var form = $("#feature_delete");
+        form.find('input[name="fid"]').val(feature.fid);
+        AjaxMgr.add({
+            form: form,
+            oncomplete: OpenLayers.Function.bind(this.ajaxReply, this)
+        });
     },
 
-    featureSuccessCallback: function (data) {
-        switch (data) {
-            case "request accepted": // do nothing: everything went fine
-                    $("#features_success").css("visibility", "visible");
-                return;
-
-            case "access denied":
-                $("#login_area, #login_password_error").show();
-                break;
-
-            case "request error":
-                alert("Le serveur a été victime d'une erreur de requête. Il s'agit probablement d'un bug dans SYP.");
-                break;
+    save: function (feature) {
+        var x = feature.geometry.x;
+        var y = feature.geometry.y;
 
-            case "feature unavailable":
-                alert("La photo n'était pas référencée sur le serveur. Il est possible qu'elle ait été supprimée");
-                break;
+        var mapProj = feature.layer.map.getProjectionObject();
+        var lonlat = new OpenLayers.LonLat(x, y).
+                                    transform(mapProj,
+                                              new OpenLayers.Projection("EPSG:4326"));
+        var form = $("#feature_update");
+        form.find('input[name="lon"]').val(lonlat.lon);
+        form.find('input[name="lat"]').val(lonlat.lat);
+        form.find('input[name="fid"]').val(feature.fid);
+        form.find('input[name="keep_img"]').val(
+            $("#img").attr("src") ? "yes": "no"
+        );
 
-                case "server error":
-            default:
-                var text = Admin.connectErrorMsg();
-                $("#features_connect_error").text(text).show();
-                break;
+        if (feature.fid) {
+            form.find('input[name="request"]').val("update");
+        } else {
+            form.find('input[name="request"]').val("add");
         }
-        Admin.reloadLayer(Admin.dataLayer);
-    },
-
-    featureErrorCallback: function (data, textStatus) {
-        var text = Admin.connectErrorMsg(textStatus);
-        $("#features_connect_error").text(text).show();
-        Admin.reloadLayer(Admin.dataLayer);
+        AjaxMgr.add({
+            form: form,
+            oncomplete: OpenLayers.Function.bind(this.ajaxReply, this)
+        });
     },
 
-    fileFrameLoad: function () {
-        $("#newimage_throbber").hide();
-        $("#newimage_input").val('');
-
-        var doc;
-        if (this.contentDocument) {
-            var doc = this.contentDocument;
-        } else if (this.contentWindow) {
-            var doc = this.contentWindow.document;
-        } else {
-            var doc = document.frames[this.id].document;
+    ajaxReply: function (data) {
+        if (!data) {
+            this.commError("Il s'est produit une erreur serveur.");
+            return;
         }
-        var body = $(doc.body);
-
-        if (body.children().length <= 1) { // error are signaled with a simple
-                                           // string message
-            var resp = body.html();
-            if (resp == "access denied") {
-                $("#login_area, #login_password_error").show();
-                Admin.closeNewimage();
-            } else if (resp == "file too big") {
-                var text = "L'image était trop grande et n'a pas été acceptée " +
-                            "par le serveur. Veuillez réduire sa taille avant " +
-                            "de l'envoyer.";
-                $("#newimage_error").text(text).show();
-                $("#newimage_input").focus(); 
-            } else if (resp == "not an image") {
-                var text = "Le fichier ne semble pas être une image.";
-                $("#newimage_error").text(text).show();
-                $("#newimage_input").focus(); 
-            } else {
-                var text = Admin.connectErrorMsg();
-                $("#newimage_error").text(text).show();
-            }
 
-        } else { // when image is successfully uploaded, informations are
-                 // passed back in document body
-            var res = body.find('.res');
-            if (res.text() == "request accepted") {
-                $("#newimage_input").unbind('change');
-                $("#fileframe").unbind('load');
-                $("#file_form").hide();
-
-                var imgurl = body.find('.infos > .imgurl').text();
-                $("#newimage_preview").attr("src", imgurl);
-                $("#newimage_preview").css("display", "block");
-                var text = "Pour valider l'ajout de cette image, vous devez la " +
-                           " positionner sur la carte. Cliquez sur la carte pour " +
-                           "positionner le marqueur.";
-                $("#newimage_warn").text(text).show();
-
-                var clickControl = Admin.clickControl;
-                clickControl.handler.callbacks.click = 
-                                            Admin.addMarkerNewImage(imgurl);
-                clickControl.activate();
-
-                Admin.selectControl.deactivate();
-                $("#modify_howto").css("visibility", "hidden");
-            } else {
-                var text = Admin.connectErrorMsg();
-                $("#newimage_error").text(text).show();
-            }
+        var xml = new OpenLayers.Format.XML().read(data);
+
+        switch (xml.documentElement.nodeName.toLowerCase()) {
+            case "error":
+                switch (xml.documentElement.getAttribute("reason")) {
+                    case "unauthorized":
+                        $("#login_area").show();
+                        this.reset();
+                        Admin.reset();
+                    break;
+                    case "server":
+                        this.commError("Il s'est produit une erreur serveur.");
+                        $("title").focus();
+                    break;
+                    case "unreferenced":
+                        this.commError("La fiche n'était pas référencée sur le serveur.");
+                        Admin.reloadLayer(Admin.dataLayer);
+                        Admin.closeEditor();
+                    break;
+                    case "nochange":
+                        this.commError("Aucun changement n'a été effectué.");
+                        Admin.closeEditor();
+                    break;
+                    case "request":
+                        this.commError("Le serveur n'a pas compris la requête. Il s'agit probablement d'un bug dans SYP.");
+                        $("title").focus();
+                    break;
+                    case "toobig":
+                        this.commError("L'image est trop grande et n'a pas été acceptée par le serveur.");
+                        $("#image_file").parent().html($("#image_file").parent().html());
+                        $("#image_file").focus();
+                    break;
+                    case "notimage":
+                        this.commError("Le fichier ne semble pas être une image.");
+                        $("#image_file").parent().html($("#image_file").parent().html());
+                        $("#image_file").focus();
+                    break;
+                    default:
+                        this.commError("Il s'est produit une erreur inconnue.");
+                        $("title").focus();
+                    break;
+                }
+            break;
+            case "success":
+                switch (xml.documentElement.getAttribute("request")) {
+                    case "del":
+                        this.commSuccess("La suppression s'est déroulée correctement.");
+                        var someFeature = false;
+                        var self = this;
+                        $.each($(xml).find("FEATURE,feature"), function () {
+                             someFeature = true;
+                             var id = parseFloat($(this).find("ID:first,id:first").text());
+                             if ((id === null) || isNaN (id)) {
+                                return;;
+                             }
+                             var features = Admin.dataLayer.features;
+                             for (var idx = 0; idx < features.length; idx++) {
+                                 if (features[idx].fid == id) {
+                                     Admin.dataLayer.removeFeatures([features[idx]]);
+                                 }
+                             }
+                        });
+                        if (someFeature == false) {
+                            this.commError("Le serveur a fait une réponse incohérente.");
+                        } else {
+                            Admin.closeEditor();
+                        }
+                    break;
+                    case "update":
+                    case "add":
+                        var someFeature = false;
+                        var self = this;
+                        $.each($(xml).find("FEATURE,feature"), function () {
+                                someFeature = true;
+                                var id = parseFloat($(this).find("ID:first,id:first").text());
+                                if ((id === null) || isNaN (id)) {
+                                    return;;
+                                }
+
+                                var lon = parseFloat($(this).find("LON:first,lon:first").text());
+                                if ((typeof (lon) != "number") || isNaN (lon) ||
+                                        (lon < -180) || (lon > 180)) {
+                                    return;;
+                                }
+
+                                var lat = parseFloat($(this).find("LAT:first,lat:first").text());
+                                if ((typeof (lat) != "number") || isNaN (lat) ||
+                                        (lat < -90) || (lat > 90)) {
+                                    return;;
+                                }
+
+                                var mapProj = Admin.map.getProjectionObject();
+                                var lonlat = new OpenLayers.LonLat (lon, lat).
+                                                transform( new OpenLayers.Projection("EPSG:4326"), mapProj);
+
+                                var imgurl = $(this).find("IMGURL:first,imgurl:first").text();
+                                var title = $(this).find("HEADING:first,heading:first").text();
+                                var description = $(this).find("DESCRIPTION:first,description:first").text();
+
+                                feature = self.update (Admin.currentFeature, lonlat, imgurl, title, description); 
+                                feature.fid = id;
+                        });
+
+                        if (someFeature == false) {
+                            this.commError("Le serveur a fait une réponse incohérente.");
+                        } else {
+                            this.commSuccess("La sauvegarde s'est déroulée correctement.");
+                            Admin.closeEditor();
+                        }
+
+                    break;
+                    default:
+                        this.commError("Le serveur a fait une réponse incohérente.");
+                   break;
+                }
+            break;
+            default:
+                this.commError("Le serveur a fait une réponse incohérente.");
+            break;
         }
     },
 
-    itemDeleter: {
-        _items: [],
-        _locks: [],
-
-        _pushUnique: function (arr, item) {
-            if (Admin.Utils.indexOf(arr, item) == -1) {
-                arr.push(item);
-            }
-        },
-
-        add: function (imgurl) {
-            if (!imgurl) {
-                return;
-            }
-            if (Admin.Utils.indexOf(this._locks, imgurl) == -1) {
-                this._pushUnique(this._items, imgurl);
-                var brName = OpenLayers.Util.getBrowserName();
-                // unload event does not work in opera, and webkit does not
-                // support xmlHttpRequests in unload, so we just don't buffer
-                // those requests.
-                if (brName == "opera" || brName == "safari") { 
-                    this.flush();
-                }
-            }
-        },
-
-        lock: function (imgurl) {
-            this._pushUnique(this._locks, imgurl);
-        },
-
-        unlock: function (imgurl) {
-            var idx = Admin.Utils.indexOf(this._locks, imgurl);
-            while (idx != -1) {
-                this._locks.splice(idx, 1);
-                idx = Admin.Utils.indexOf(this._locks, imgurl);
-            }
-        },
+    commSuccess: function (message) {
+        $("#server_comm").text(message);
+        $("#server_comm").removeClass().addClass("success");
+    },
 
-        flush: function () {
-            if (this._items.length == 0) {
-                return;
-            }
-            var i = 0;
-            var data = {};
-            for (var i = 0; i < this._items.length; i++) {
-                data["imgurl_delete_" + i] = this._items[i];
-            }
-            var req = {
-                type: "post",
-                url: "changes.php",
-                data:  data,
-                success: function (data) { 
-                },
-                error: function (data) {
-                }
-            };
-            AjaxMgr.add(req);
-            this._items = [];
+    commError: function (message) {
+        $("#server_comm").text(message);
+        $("#server_comm").removeClass().addClass("error");
+        if (message.length) {
+      //      this.move(Admin.currentFeature, Admin.currentFeatureLocation);
+    //        Admin.reset();
         }
     }
 }
@@ -838,6 +541,8 @@ var FeatureMgr = {
 var AjaxMgr = {
     _queue: [],
 
+    running: false,
+
     add: function(query) {
         this._queue.push(query);
         if (this._queue.length > 1) {
@@ -849,20 +554,35 @@ var AjaxMgr = {
 
     _runQuery: function(query) {
         var self = this;
-        $.ajax({
-            type: query.type,
-            url: query.url,
-            data: query.data,
-            success: function(data) {
-                self._reqEnd();
-                query.success.call(query, data);
-            },
-            error: function(data, textStatus) {
-                self._reqEnd();
-                query.error.call(query, data, textStatus);
-            },
-            timeout: query.timeout
+        $('#api_frame').one("load", function() {
+            self.running = false;
+            self._reqEnd();
+            if (typeof (query.oncomplete) == "function") {
+                var body = null;
+                try {
+                    if (this.contentDocument) {
+                        body = this.contentDocument.body;
+                    } else if (this.contentWindow) {
+                        body = this.contentWindow.document.body;
+                    } else {
+                        body = document.frames[this.id].document.body;
+                    }
+                } catch (e) {}
+                    if (body) {
+                        query.oncomplete(body.innerHTML);
+                    } else {
+                        query.oncomplete(null);
+                    }
+            }
         });
+        query.form.attr("action", "api.php");
+        query.form.attr("target", "api_frame");
+        query.form.attr("method", "post");
+        this.running = true;
+        query.form.get(0).submit();
+        if (typeof (query.onsend) == "function") {
+            query.onsend();
+        }
     },
 
     _reqEnd: function() {
@@ -873,19 +593,131 @@ var AjaxMgr = {
     }
 }
 
+var pwdMgr = {
+
+    init: function () {
+        $("#login_form").submit(this.submit);
+        $("#password").focus().select();
+    },
+
+    reset: function() {
+        this.commError ("");
+    },
+
+    submit: function () {
+        try {
+            pwdMgr.commError("");
+            var req = {
+                form:  $("#login_form"),
+                onsend: function() {
+                    $("#pwd_throbber").css("visibility", "visible");
+                    $("#login_error").hide();
+
+                    // we need a timeout; otherwise those fields will not be submitted
+                    window.setTimeout(function() { 
+                            // removes focus from #password before disabling it. Otherwise, opera
+                            // prevents re-focusing it after re-enabling it.
+                            $("#password").blur(); 
+                            $("#login_submit, #password").attr("disabled", "disabled");
+                    }, 0)
+                },
+                oncomplete: OpenLayers.Function.bind(pwdMgr.ajaxReply, pwdMgr)
+            };
+            AjaxMgr.add(req);
+        } catch(e) {}
+        return false;
+    },
+
+    ajaxReply: function (data) {
+
+        $("#pwd_throbber").css("visibility", "hidden");
+        // here, we need a timeout because onsend timeout sometimes has not been triggered yet
+        window.setTimeout(function() {
+            $("#login_submit, #password").removeAttr("disabled");
+        }, 0);
+
+        if (!data) {
+            this.commError("Il s'est produit une erreur serveur.");
+            $("#login_error").show();
+            window.setTimeout(function() {
+                    $("#password").focus().select();
+            }, 0);
+            return;
+        }
+        var xml = new OpenLayers.Format.XML().read(data);
+
+        switch (xml.documentElement.nodeName.toLowerCase()) {
+            case "error":
+                switch (xml.documentElement.getAttribute("reason")) {
+                    case "server":
+                        this.commError("Il s'est produit une erreur serveur.");
+                    break;
+                    case "unauthorized":
+                        this.commError("Le mot de passe n'est pas correct.");
+                    break;
+                    case "request":
+                        this.commError("Le serveur n'a pas compris la requête. Il s'agit probablement d'un bug dans SYP.");
+                    break;
+                    default:
+                        this.commError("Il s'est produit une erreur inconnue.");
+                    break;
+                }
+                $("#login_error").show();
+                window.setTimeout(function() {
+                        $("#password").focus().select();
+                }, 0);
+            break;
+            case "success":
+                this.reset();
+                $("#login_area").hide();
+            break;
+            default:
+                this.commError("Le serveur a fait une réponse incohérente.");
+            break;
+        }
+    },
+
+    commError: function (message) {
+        $("#login_error").text(message);
+        if (message) {
+            $("#login_error").show();
+        } else {
+            $("#login_error").hide();
+        }
+    }
+}
+
 $(window).load(function () {
     // if using .ready, ie triggers an error when trying to access
     // document.namespaces
     pwdMgr.init();
-    $("#newimage_close").click(function () {
-        Admin.closeNewimage();
-    });
-    $("#addphoto_button").click(function () {
+    $("#newfeature_button").click(function () {
         Admin.addNewFeature();
     });
+    $("#editor_close").click(function () {
+        Admin.cancelCurrentFeature()
+    });
+    $("#feature_update").submit(function() {
+        try {
+            FeatureMgr.save(Admin.currentFeature);
+        } catch(e) {}
+        return false;
+    });
+    $("#feature_delete").submit(function() {
+        try {
+            FeatureMgr.del(Admin.currentFeature);
+        } catch(e) {}
+        return false;
+    });
+    $("#image_delete").click(function() {
+            $("#img").removeAttr('src');
+            // needs to rebuild element otherwise some browsers still
+            // display image.
+            $("#img").parent().html($("#img").parent().html());
+            $("#img").parent().hide();
+            $("#image_delete").hide();
+            $("#image_file").parent().show();
+    });
+
     Admin.init();
 });
-$(window).unload(function () {
-    FeatureMgr.itemDeleter.add($("#newimage_preview").attr("src"));
-    FeatureMgr.itemDeleter.flush();
-});
index 7e60b4e858df70821f0cd2f44b154d99ac558efd..c9f637aa78f615f4fed291c6e4daa11305dfa721 100644 (file)
--- a/js/syp.js
+++ b/js/syp.js
@@ -96,7 +96,7 @@ var SYP = {
     checkForFeatures: function() {
         var features = this.dataLayer.features;
         if (features.length == 0) {
-            var message = "Il n'y a aucune photo enregistrée sur le site.";
+            var message = "Il n'y a aucune image enregistrée sur le site.";
             this.Utils.displayUserMessage(message, "warn");
         }
     },
diff --git a/logout.php b/logout.php
new file mode 100644 (file)
index 0000000..c2cd62c
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+require_once ("./inc/settings.php");
+
+$cookie_name = sprintf ("%sauth", DBPREFIX);
+setcookie ($cookie_name, "", time () - 3600, "" , "",false, true);
+header ('Location: index.php');
+
+?>
index 0822ece217923a1375df4568540d203d9d52a195..9cbbf7b833de210855c2c258b877d470f5f0ee32 100644 (file)
@@ -3,6 +3,16 @@
 
 @import url(common.css);
 
+
+/*
+ * misc
+ */
+
+#logout {
+    float: right;
+    margin: 0px 10px 0px 0px;
+}
+
 /*
  * map
  */
  * feature editor
  */
 #editor {
+    position: absolute;
+    width: 44%;
+    top: 2em;
+    left: 55%;
     display: none;
+    border: 1px solid black;
+    padding-bottom: 18px;
+}
+
+#editor_close {
     float: right;
-    clear: right;
-    margin-right: 12px;
+}
+
+#title, #description {
+    width: 90%;
 }
 
 #img {
     max-height: 400px;
     max-width: 400px;
+
 }
 html>/**/body #img { /* hide from ie7 */
     display: block;
     margin: 1em auto 0 auto;
 }
 
-#deletephoto_button {
-    margin-top: 12px;
+#validate_editor {
+    font-weight: bold;
+}
+
+#feature_delete {
+    margin-top: 2.5em;
+}
+#delete {
+    display: none;
+    font-weight: bold;
 }
 
 /*
  * global admin area
  */
 
-#global_admin {
+#admin {
     position: fixed;
-    top: 50%;
+    top: 53%;
     width: 50%;
 }
 
-#modify_howto,
-#dragdrop_howto {
-    visibility: hidden;
-}
-
-#features_connect_error {
-    display: none;
-}
-
 /*
  * password manager
  */
@@ -104,66 +125,15 @@ html>/**/body #img { /* hide from ie7 */
     background-color: white;
 }
 
-#user_pwd {
+#password {
     display: block;
     width: 100%;
 }
 
-#login_connect_error, 
-#login_password_error {
+#login_error {
     display: none;
 }
 
 /*
  * add feature
  */
-#newimage {
-    display: none;
-    float: right;
-    clear: right;
-    width: 45%;
-    border: 1px solid black;
-    padding: 15px 3px 8px 2px;
-}
-
-#newimage_error,
-#newimage_warn {
-    display: none;
-}
-
-#newimage_close {
-    float: right;
-    right: 8px;
-    margin: -14px -2px 0px 0px;
-}
-
-#newimage_preview {
-    display: none;
-    max-height: 400px;
-    max-width: 400px;
-
-    /* for IE */
-    _height: expression((this.offsetHeight>this.offsetWidth) ? 
-             Math.min(parseInt(this.offsetHeight), 400 ) : true);
-    _width: expression((this.offsetWidth>=this.offsetHeight) ? 
-            Math.min(parseInt(this.offsetWidth), 400 ) : true); 
-
-    margin-left: auto;
-    margin-right: auto;
-}
-
-/*
- * misc
- */
-
-#logout {
-    float: right;
-    margin: 0px 10px 0px 0px;
-}
-
-#features_success {
-    visibility: hidden;
-    clear: right;
-    text-align: center;
-    margin: 0px 0px 12px 50%;
-}
index 709a39b0d67dc4b86e811f66d2c9087f19ede84f..ce215b70a3d71dab8d59a2a7b528565ea3dbc58f 100644 (file)
@@ -199,29 +199,7 @@ if(!parentHasPositionAbsolute){container.style.position="absolute";}
 if(!w){w=parseInt(content.scrollWidth);container.style.width=w+"px";}
 if(!h){h=parseInt(content.scrollHeight);}
 container.removeChild(content);containerElement.removeChild(container);return new OpenLayers.Size(w,h);};OpenLayers.Util.getScrollbarWidth=function(){var scrollbarWidth=OpenLayers.Util._scrollbarWidth;if(scrollbarWidth==null){var scr=null;var inn=null;var wNoScroll=0;var wScroll=0;scr=document.createElement('div');scr.style.position='absolute';scr.style.top='-1000px';scr.style.left='-1000px';scr.style.width='100px';scr.style.height='50px';scr.style.overflow='hidden';inn=document.createElement('div');inn.style.width='100%';inn.style.height='200px';scr.appendChild(inn);document.body.appendChild(scr);wNoScroll=inn.offsetWidth;scr.style.overflow='scroll';wScroll=inn.offsetWidth;document.body.removeChild(document.body.lastChild);OpenLayers.Util._scrollbarWidth=(wNoScroll-wScroll);scrollbarWidth=OpenLayers.Util._scrollbarWidth;}
-return scrollbarWidth;};OpenLayers.Rico=new Object();OpenLayers.Rico.Corner={round:function(e,options){e=OpenLayers.Util.getElement(e);this._setOptions(options);var color=this.options.color;if(this.options.color=="fromElement"){color=this._background(e);}
-var bgColor=this.options.bgColor;if(this.options.bgColor=="fromParent"){bgColor=this._background(e.offsetParent);}
-this._roundCornersImpl(e,color,bgColor);},changeColor:function(theDiv,newColor){theDiv.style.backgroundColor=newColor;var spanElements=theDiv.parentNode.getElementsByTagName("span");for(var currIdx=0;currIdx<spanElements.length;currIdx++){spanElements[currIdx].style.backgroundColor=newColor;}},changeOpacity:function(theDiv,newOpacity){var mozillaOpacity=newOpacity;var ieOpacity='alpha(opacity='+newOpacity*100+')';theDiv.style.opacity=mozillaOpacity;theDiv.style.filter=ieOpacity;var spanElements=theDiv.parentNode.getElementsByTagName("span");for(var currIdx=0;currIdx<spanElements.length;currIdx++){spanElements[currIdx].style.opacity=mozillaOpacity;spanElements[currIdx].style.filter=ieOpacity;}},reRound:function(theDiv,options){var topRico=theDiv.parentNode.childNodes[0];var bottomRico=theDiv.parentNode.childNodes[2];theDiv.parentNode.removeChild(topRico);theDiv.parentNode.removeChild(bottomRico);this.round(theDiv.parentNode,options);},_roundCornersImpl:function(e,color,bgColor){if(this.options.border){this._renderBorder(e,bgColor);}
-if(this._isTopRounded()){this._roundTopCorners(e,color,bgColor);}
-if(this._isBottomRounded()){this._roundBottomCorners(e,color,bgColor);}},_renderBorder:function(el,bgColor){var borderValue="1px solid "+this._borderColor(bgColor);var borderL="border-left: "+borderValue;var borderR="border-right: "+borderValue;var style="style='"+borderL+";"+borderR+"'";el.innerHTML="<div "+style+">"+el.innerHTML+"</div>";},_roundTopCorners:function(el,color,bgColor){var corner=this._createCorner(bgColor);for(var i=0;i<this.options.numSlices;i++){corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));}
-el.style.paddingTop=0;el.insertBefore(corner,el.firstChild);},_roundBottomCorners:function(el,color,bgColor){var corner=this._createCorner(bgColor);for(var i=(this.options.numSlices-1);i>=0;i--){corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));}
-el.style.paddingBottom=0;el.appendChild(corner);},_createCorner:function(bgColor){var corner=document.createElement("div");corner.style.backgroundColor=(this._isTransparent()?"transparent":bgColor);return corner;},_createCornerSlice:function(color,bgColor,n,position){var slice=document.createElement("span");var inStyle=slice.style;inStyle.backgroundColor=color;inStyle.display="block";inStyle.height="1px";inStyle.overflow="hidden";inStyle.fontSize="1px";var borderColor=this._borderColor(color,bgColor);if(this.options.border&&n==0){inStyle.borderTopStyle="solid";inStyle.borderTopWidth="1px";inStyle.borderLeftWidth="0px";inStyle.borderRightWidth="0px";inStyle.borderBottomWidth="0px";inStyle.height="0px";inStyle.borderColor=borderColor;}
-else if(borderColor){inStyle.borderColor=borderColor;inStyle.borderStyle="solid";inStyle.borderWidth="0px 1px";}
-if(!this.options.compact&&(n==(this.options.numSlices-1))){inStyle.height="2px";}
-this._setMargin(slice,n,position);this._setBorder(slice,n,position);return slice;},_setOptions:function(options){this.options={corners:"all",color:"fromElement",bgColor:"fromParent",blend:true,border:false,compact:false};OpenLayers.Util.extend(this.options,options||{});this.options.numSlices=this.options.compact?2:4;if(this._isTransparent()){this.options.blend=false;}},_whichSideTop:function(){if(this._hasString(this.options.corners,"all","top")){return"";}
-if(this.options.corners.indexOf("tl")>=0&&this.options.corners.indexOf("tr")>=0){return"";}
-if(this.options.corners.indexOf("tl")>=0){return"left";}else if(this.options.corners.indexOf("tr")>=0){return"right";}
-return"";},_whichSideBottom:function(){if(this._hasString(this.options.corners,"all","bottom")){return"";}
-if(this.options.corners.indexOf("bl")>=0&&this.options.corners.indexOf("br")>=0){return"";}
-if(this.options.corners.indexOf("bl")>=0){return"left";}else if(this.options.corners.indexOf("br")>=0){return"right";}
-return"";},_borderColor:function(color,bgColor){if(color=="transparent"){return bgColor;}else if(this.options.border){return this.options.border;}else if(this.options.blend){return this._blend(bgColor,color);}else{return"";}},_setMargin:function(el,n,corners){var marginSize=this._marginSize(n);var whichSide=corners=="top"?this._whichSideTop():this._whichSideBottom();if(whichSide=="left"){el.style.marginLeft=marginSize+"px";el.style.marginRight="0px";}
-else if(whichSide=="right"){el.style.marginRight=marginSize+"px";el.style.marginLeft="0px";}
-else{el.style.marginLeft=marginSize+"px";el.style.marginRight=marginSize+"px";}},_setBorder:function(el,n,corners){var borderSize=this._borderSize(n);var whichSide=corners=="top"?this._whichSideTop():this._whichSideBottom();if(whichSide=="left"){el.style.borderLeftWidth=borderSize+"px";el.style.borderRightWidth="0px";}
-else if(whichSide=="right"){el.style.borderRightWidth=borderSize+"px";el.style.borderLeftWidth="0px";}
-else{el.style.borderLeftWidth=borderSize+"px";el.style.borderRightWidth=borderSize+"px";}
-if(this.options.border!=false){el.style.borderLeftWidth=borderSize+"px";el.style.borderRightWidth=borderSize+"px";}},_marginSize:function(n){if(this._isTransparent()){return 0;}
-var marginSizes=[5,3,2,1];var blendedMarginSizes=[3,2,1,0];var compactMarginSizes=[2,1];var smBlendedMarginSizes=[1,0];if(this.options.compact&&this.options.blend){return smBlendedMarginSizes[n];}else if(this.options.compact){return compactMarginSizes[n];}else if(this.options.blend){return blendedMarginSizes[n];}else{return marginSizes[n];}},_borderSize:function(n){var transparentBorderSizes=[5,3,2,1];var blendedBorderSizes=[2,1,1,1];var compactBorderSizes=[1,0];var actualBorderSizes=[0,2,0,0];if(this.options.compact&&(this.options.blend||this._isTransparent())){return 1;}else if(this.options.compact){return compactBorderSizes[n];}else if(this.options.blend){return blendedBorderSizes[n];}else if(this.options.border){return actualBorderSizes[n];}else if(this._isTransparent()){return transparentBorderSizes[n];}
-return 0;},_hasString:function(str){for(var i=1;i<arguments.length;i++)if(str.indexOf(arguments[i])>=0){return true;}return false;},_blend:function(c1,c2){var cc1=OpenLayers.Rico.Color.createFromHex(c1);cc1.blend(OpenLayers.Rico.Color.createFromHex(c2));return cc1;},_background:function(el){try{return OpenLayers.Rico.Color.createColorFromBackground(el).asHex();}catch(err){return"#ffffff";}},_isTransparent:function(){return this.options.color=="transparent";},_isTopRounded:function(){return this._hasString(this.options.corners,"all","top","tl","tr");},_isBottomRounded:function(){return this._hasString(this.options.corners,"all","bottom","bl","br");},_hasSingleTextChild:function(el){return el.childNodes.length==1&&el.childNodes[0].nodeType==3;}};OpenLayers.Element={visible:function(element){return OpenLayers.Util.getElement(element).style.display!='none';},toggle:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);var display=OpenLayers.Element.visible(element)?'hide':'show';OpenLayers.Element[display](element);}},hide:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='none';}},show:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='';}},remove:function(element){element=OpenLayers.Util.getElement(element);element.parentNode.removeChild(element);},getHeight:function(element){element=OpenLayers.Util.getElement(element);return element.offsetHeight;},getDimensions:function(element){element=OpenLayers.Util.getElement(element);if(OpenLayers.Element.getStyle(element,'display')!='none'){return{width:element.offsetWidth,height:element.offsetHeight};}
+return scrollbarWidth;};OpenLayers.Element={visible:function(element){return OpenLayers.Util.getElement(element).style.display!='none';},toggle:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);var display=OpenLayers.Element.visible(element)?'hide':'show';OpenLayers.Element[display](element);}},hide:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='none';}},show:function(){for(var i=0,len=arguments.length;i<len;i++){var element=OpenLayers.Util.getElement(arguments[i]);element.style.display='';}},remove:function(element){element=OpenLayers.Util.getElement(element);element.parentNode.removeChild(element);},getHeight:function(element){element=OpenLayers.Util.getElement(element);return element.offsetHeight;},getDimensions:function(element){element=OpenLayers.Util.getElement(element);if(OpenLayers.Element.getStyle(element,'display')!='none'){return{width:element.offsetWidth,height:element.offsetHeight};}
 var els=element.style;var originalVisibility=els.visibility;var originalPosition=els.position;els.visibility='hidden';els.position='absolute';els.display='';var originalWidth=element.clientWidth;var originalHeight=element.clientHeight;els.display='none';els.position=originalPosition;els.visibility=originalVisibility;return{width:originalWidth,height:originalHeight};},hasClass:function(element,name){var names=element.className;return(!!names&&new RegExp("(^|\\s)"+name+"(\\s|$)").test(names));},addClass:function(element,name){if(!OpenLayers.Element.hasClass(element,name)){element.className+=(element.className?" ":"")+name;}
 return element;},removeClass:function(element,name){var names=element.className;if(names){element.className=OpenLayers.String.trim(names.replace(new RegExp("(^|\\s+)"+name+"(\\s+|$)")," "));}
 return element;},toggleClass:function(element,name){if(OpenLayers.Element.hasClass(element,name)){OpenLayers.Element.removeClass(element,name);}else{OpenLayers.Element.addClass(element,name);}
@@ -797,7 +775,7 @@ return null;return oDocument;};function fSynchronizeValues(oRequest){try{oReques
 try{oRequest.responseXML=fGetDocument(oRequest._object);}catch(e){}
 try{oRequest.status=oRequest._object.status;}catch(e){}
 try{oRequest.statusText=oRequest._object.statusText;}catch(e){}};function fCleanTransport(oRequest){oRequest._object.onreadystatechange=new window.Function;delete oRequest._headers;};if(!window.Function.prototype.apply){window.Function.prototype.apply=function(oRequest,oArguments){if(!oArguments)
-oArguments=[];oRequest.__func=this;oRequest.__func(oArguments[0],oArguments[1],oArguments[2],oArguments[3],oArguments[4]);delete oRequest.__func;};};OpenLayers.Request.XMLHttpRequest=cXMLHttpRequest;})();OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:false,interval:25,draw:function(){this.handler=new OpenLayers.Handler.Drag(this,{"move":this.panMap,"done":this.panMapDone},{interval:this.interval});},panMap:function(xy){this.panned=true;this.map.pan(this.handler.last.x-xy.x,this.handler.last.y-xy.y,{dragging:this.handler.dragging,animate:false});},panMapDone:function(xy){if(this.panned){this.panMap(xy);this.panned=false;}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.State={UNKNOWN:'Unknown',INSERT:'Insert',UPDATE:'Update',DELETE:'Delete'};OpenLayers.Feature.Vector=OpenLayers.Class(OpenLayers.Feature,{fid:null,geometry:null,attributes:null,bounds:null,state:null,style:null,renderIntent:"default",initialize:function(geometry,attributes,style){OpenLayers.Feature.prototype.initialize.apply(this,[null,null,attributes]);this.lonlat=null;this.geometry=geometry?geometry:null;this.state=null;this.attributes={};if(attributes){this.attributes=OpenLayers.Util.extend(this.attributes,attributes);}
+oArguments=[];oRequest.__func=this;oRequest.__func(oArguments[0],oArguments[1],oArguments[2],oArguments[3],oArguments[4]);delete oRequest.__func;};};OpenLayers.Request.XMLHttpRequest=cXMLHttpRequest;})();OpenLayers.Control.DragFeature=OpenLayers.Class(OpenLayers.Control,{geometryTypes:null,onStart:function(feature,pixel){},onDrag:function(feature,pixel){},onComplete:function(feature,pixel){},layer:null,feature:null,dragCallbacks:{},featureCallbacks:{},lastPixel:null,initialize:function(layer,options){OpenLayers.Control.prototype.initialize.apply(this,[options]);this.layer=layer;this.handlers={drag:new OpenLayers.Handler.Drag(this,OpenLayers.Util.extend({down:this.downFeature,move:this.moveFeature,up:this.upFeature,out:this.cancel,done:this.doneDragging},this.dragCallbacks)),feature:new OpenLayers.Handler.Feature(this,this.layer,OpenLayers.Util.extend({over:this.overFeature,out:this.outFeature},this.featureCallbacks),{geometryTypes:this.geometryTypes})};},destroy:function(){this.layer=null;OpenLayers.Control.prototype.destroy.apply(this,[]);},activate:function(){return(this.handlers.feature.activate()&&OpenLayers.Control.prototype.activate.apply(this,arguments));},deactivate:function(){this.handlers.drag.deactivate();this.handlers.feature.deactivate();this.feature=null;this.dragging=false;this.lastPixel=null;OpenLayers.Element.removeClass(this.map.viewPortDiv,this.displayClass+"Over");return OpenLayers.Control.prototype.deactivate.apply(this,arguments);},overFeature:function(feature){if(!this.handlers.drag.dragging){this.feature=feature;this.handlers.drag.activate();this.over=true;OpenLayers.Element.addClass(this.map.viewPortDiv,this.displayClass+"Over");}else{if(this.feature.id==feature.id){this.over=true;}else{this.over=false;}}},downFeature:function(pixel){this.lastPixel=pixel;this.onStart(this.feature,pixel);},moveFeature:function(pixel){var res=this.map.getResolution();this.feature.geometry.move(res*(pixel.x-this.lastPixel.x),res*(this.lastPixel.y-pixel.y));this.layer.drawFeature(this.feature);this.lastPixel=pixel;this.onDrag(this.feature,pixel);},upFeature:function(pixel){if(!this.over){this.handlers.drag.deactivate();}},doneDragging:function(pixel){this.onComplete(this.feature,pixel);},outFeature:function(feature){if(!this.handlers.drag.dragging){this.over=false;this.handlers.drag.deactivate();OpenLayers.Element.removeClass(this.map.viewPortDiv,this.displayClass+"Over");this.feature=null;}else{if(this.feature.id==feature.id){this.over=false;}}},cancel:function(){this.handlers.drag.deactivate();this.over=false;},setMap:function(map){this.handlers.drag.setMap(map);this.handlers.feature.setMap(map);OpenLayers.Control.prototype.setMap.apply(this,arguments);},CLASS_NAME:"OpenLayers.Control.DragFeature"});OpenLayers.Control.DragPan=OpenLayers.Class(OpenLayers.Control,{type:OpenLayers.Control.TYPE_TOOL,panned:false,interval:25,draw:function(){this.handler=new OpenLayers.Handler.Drag(this,{"move":this.panMap,"done":this.panMapDone},{interval:this.interval});},panMap:function(xy){this.panned=true;this.map.pan(this.handler.last.x-xy.x,this.handler.last.y-xy.y,{dragging:this.handler.dragging,animate:false});},panMapDone:function(xy){if(this.panned){this.panMap(xy);this.panned=false;}},CLASS_NAME:"OpenLayers.Control.DragPan"});OpenLayers.State={UNKNOWN:'Unknown',INSERT:'Insert',UPDATE:'Update',DELETE:'Delete'};OpenLayers.Feature.Vector=OpenLayers.Class(OpenLayers.Feature,{fid:null,geometry:null,attributes:null,bounds:null,state:null,style:null,renderIntent:"default",initialize:function(geometry,attributes,style){OpenLayers.Feature.prototype.initialize.apply(this,[null,null,attributes]);this.lonlat=null;this.geometry=geometry?geometry:null;this.state=null;this.attributes={};if(attributes){this.attributes=OpenLayers.Util.extend(this.attributes,attributes);}
 this.style=style?style:null;},destroy:function(){if(this.layer){this.layer.removeFeatures(this);this.layer=null;}
 this.geometry=null;OpenLayers.Feature.prototype.destroy.apply(this,arguments);},clone:function(){return new OpenLayers.Feature.Vector(this.geometry?this.geometry.clone():null,this.attributes,this.style);},onScreen:function(boundsOnly){var onScreen=false;if(this.layer&&this.layer.map){var screenBounds=this.layer.map.getExtent();if(boundsOnly){var featureBounds=this.geometry.getBounds();onScreen=screenBounds.intersectsBounds(featureBounds);}else{var screenPoly=screenBounds.toGeometry();onScreen=screenPoly.intersects(this.geometry);}}
 return onScreen;},createMarker:function(){return null;},destroyMarker:function(){},createPopup:function(){return null;},atPoint:function(lonlat,toleranceLon,toleranceLat){var atPoint=false;if(this.geometry){atPoint=this.geometry.atPoint(lonlat,toleranceLon,toleranceLat);}
index 1f624d36c6e2f0247e3cd9421282b1897a065931..292aac4db77274a044bc06a8b9ec049e9cbdc01f 100644 (file)
Binary files a/openlayers/tools/jsmin.pyc and b/openlayers/tools/jsmin.pyc differ
index 5a2d92087fdc9144108aea33830a044dd2ec48da..74e99e015e07240c31d91eb895ae56db2caded9b 100644 (file)
Binary files a/openlayers/tools/mergejs.pyc and b/openlayers/tools/mergejs.pyc differ
index d853ac51e61444a1b835cd5e7ad7b6f657f0e7f6..67f01e0959334a41fe6fee3f32712bc2c8c91b0d 100644 (file)
Binary files a/openlayers/tools/toposort.pyc and b/openlayers/tools/toposort.pyc differ
diff --git a/syp.cfg b/syp.cfg
index 8d5895d197fa24e7080055c3108ebb063ab75128..cf9767979eac9f0b581d700d028c12e8788f27eb 100644 (file)
--- a/syp.cfg
+++ b/syp.cfg
@@ -6,7 +6,6 @@ OpenLayers.js
 OpenLayers/BaseTypes.js
 OpenLayers/BaseTypes/Class.js
 OpenLayers/Util.js
-Rico/Corner.js
 
 [last]
 
@@ -17,6 +16,7 @@ OpenLayers/Control/PanZoom.js
 OpenLayers/Control/Permalink.js
 OpenLayers/Control/Attribution.js
 OpenLayers/Control/SelectFeature.js
+OpenLayers/Control/DragFeature.js
 OpenLayers/Layer/XYZ.js
 OpenLayers/Format/KML.js
 OpenLayers/Layer/GML.js
index a6859ef9be0720ec25cbbcfc95c91c09d3618a5d..9710d8fbb97db0157e3f847f1f436d4efb37014e 100644 (file)
@@ -31,8 +31,8 @@
         wiz_warn ("Cette version de php ne supporte pas la lecture des données exif");
     }
 
-    require("./inc/settings.php");
-    require("./inc/db/mysql.php");
+    require_once ("./inc/settings.php");
+    require_once ("./inc/db/mysql.php");
 
     try {
         $connection->connect (DBHOST, DBUSER, DBPWD, DBNAME, DBPREFIX);