[svn r1551] Add delete bookmarks functionality as well as many reusability issues. xbelweb
authorfabien
Fri, 26 Sep 2003 11:04:15 -0400
branchxbelweb
changeset 17 14bec94bbe89
parent 16 07adce9ccccb
child 18 25496f251484
[svn r1551] Add delete bookmarks functionality as well as many reusability issues.
.cvsignore
.htaccess
TODO
add_bk.tmpl
add_confirm.py
add_confirm.tmpl
add_result.py
add_result.tmpl
inc/keywords.tmpl
index.py
index.tmpl
kw_result.py
lib/utils.py
--- a/.cvsignore	Fri Sep 26 11:03:02 2003 -0400
+++ b/.cvsignore	Fri Sep 26 11:04:15 2003 -0400
@@ -1,2 +1,3 @@
 *.pyc
 *.tmplc
+.htpasswd
--- a/.htaccess	Fri Sep 26 11:03:02 2003 -0400
+++ b/.htaccess	Fri Sep 26 11:04:15 2003 -0400
@@ -3,7 +3,7 @@
 DirectoryIndex index.py
 AuthType Basic
 AuthName Fabien@TZoNE
-AuthUserFile /home/fabien/.htpasswd
+AuthUserFile /home/fabien/public_html/xbelweb/.htpasswd
 <Limit GET POST>
   require valid-user
 </Limit>
--- a/TODO	Fri Sep 26 11:03:02 2003 -0400
+++ b/TODO	Fri Sep 26 11:04:15 2003 -0400
@@ -1,9 +1,8 @@
 * Used SQL sequence instead of the db_sequence table.
-* Add delete bookmark.
 * Add CSS.
-* Add better navigational means.
 * Add limit query page
 * Add Search capabilities.
+* Add better navigation.
 * Add default (dynamic?) tree expansion.
 * Add multiuser support.
 * Add XBel Import/Export.
--- a/add_bk.tmpl	Fri Sep 26 11:03:02 2003 -0400
+++ b/add_bk.tmpl	Fri Sep 26 11:04:15 2003 -0400
@@ -1,10 +1,10 @@
 <html>
   <head>
-    <title>Add a bookmark</title>
+    <title>Edit a bookmark</title>
   </head>
   <body>
-    <h1>Add a bookmark</h1>
-    <form action="add_result.py" method="post" name="add">
+    <h1>Edit a bookmark</h1>
+    <form action="add_confirm.py" method="post" name="add">
       <fieldset class="bookmark">
         <input type="hidden" name="id" value="<TMPL_VAR bkid>"/>
         <label>Nom: <input type="text" name="name"
@@ -12,8 +12,10 @@
         <label>URL: <input type="text" name="url"
 	  value="<TMPL_VAR curl>"/></label><br/>
         <label for="desc">Description: </label><br/>
-        <textarea name="desc" rows="5" cols="40"><TMPL_VAR desc></textarea>
-        <button type="submit" name="submit">Soumettre</button>
+        <textarea name="desc" rows="5" cols="40"><TMPL_VAR desc></textarea><br/>
+        <button type="submit" name="action" value="update">Submit</button><br/>
+        <button type="submit" name="action" value="delete">Delete</button>
+        <button type="submit" name="action" value="cancel">Cancel</button>
       </fieldset>
       <fieldset class="keywords">
         <legend>Mots clés:</legend>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/add_confirm.py	Fri Sep 26 11:04:15 2003 -0400
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+import sys
+import traceback
+
+if __name__ == "__main__":
+	sys.path.insert(0, "/home/fabien/lib/python")
+	sys.path.insert(0, "./lib")
+	sys.stderr = sys.stdout
+
+	print "Content-type: text/html; charset=iso-8859-1;"
+	print
+
+# import cgitb; cgitb.enable()
+import cgi
+from htmltmpl import TemplateManager, TemplateProcessor
+import my_db
+import time
+from bkmark import Bookmark
+from index import load_index
+from utils import unique
+
+def get_bk_from_form(form):
+	bk = Bookmark()
+	bk.id = int(form['id'].value)
+	bk.url = form['url'].value
+	if form.has_key('name'):
+		bk.name = form['name'].value
+	if form.has_key('desc'):
+		bk.desc = form['desc'].value
+	return bk
+
+def get_kw_from_form(form):
+	kw = form.getvalue("kw")
+	if not isinstance(kw, type([])):
+		if kw:
+			kw = [kw]
+		else:
+			kw = []
+	kw = map(int, kw)
+	return kw
+
+def get_new_kw_from_form(form, sep =','):
+	if form.has_key('newkw'):
+		kw= map(lambda e: e.strip(),
+			form['newkw'].value.split(','))
+		return filter(lambda x: x != '', kw)
+	else:
+		return []
+
+def get_unique_keywords(form, db):
+      kw = get_kw_from_form(form)
+      kw = map(db.get_keyword, kw)
+      kw = kw + get_new_kw_from_form(form)
+      return unique(kw)
+
+def main(action, bk, kw):
+    tmpl = TemplateManager().prepare("add_confirm.tmpl")
+    tproc = TemplateProcessor()
+    tproc.set("confirm_delete", action == 'delete')
+    tproc.set("confirm_update", action == 'update')
+    tproc.set("action", action)
+    tproc.set("id", bk.id)
+    tproc.set("name", bk.name)
+    tproc.set("url", bk.url)
+    tproc.set("Keywords", map(lambda x: {'keyword': x }, kw))
+    print tproc.process(tmpl)
+
+if (__name__ == "__main__"):
+    form = cgi.FieldStorage()
+    db = my_db.connect()
+    id = int(form['id'].value)
+    action = form['action'].value
+    if action == 'cancel' or (action == 'delete' and id == -1):
+        load_index(db)
+    else:
+	if action == 'update':
+	  bk = get_bk_from_form(form)
+	  kw = get_unique_keywords(form, db)
+	else:
+	  bk = db.get_bookmarks([id])[0]
+	  (ids, kw) = apply(zip,db.get_keywords(id))
+	main(action, bk, kw[1:])
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/add_confirm.tmpl	Fri Sep 26 11:04:15 2003 -0400
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-15">
+    <title>Fabien's XBelWeb</title>
+  </head>
+  <body>
+    <h1>Fabien's XBelWeb</h1>
+    <h2>Confirmation</h2>
+    <form name="confirmation" method="post" action="add_result.py">
+      <fieldset>
+        <legend>Bookmark</legend>
+	<input type="hidden" name="id" value="<TMPL_VAR id>"/>
+	<input type="hidden" name="name" value="<TMPL_VAR name>"/>
+	<input type="hidden" name="url" value="<TMPL_VAR url>"/>
+	<input type="hidden" name="newkw"
+	  value="<TMPL_LOOP Keywords><TMPL_VAR keyword>,</TMPL_LOOP>"/>
+	<strong><a href="<TMPL_VAR url>"><TMPL_VAR name></a></strong>
+	<p><TMPL_VAR desc></p>
+	<p>Keywords:
+	  <TMPL_LOOP Keywords>
+	    <TMPL_VAR keyword>;
+	  </TMPL_LOOP>
+	</p>
+	<TMPL_IF confirm_delete>
+	  <button name="action" type="submit"
+	        value="<TMPL_VAR action>">Delete</button>
+	</TMPL_IF>
+	<TMPL_IF confirm_update>
+	  <button name="action" type="submit"
+	        value="<TMPL_VAR action>">Update</button>
+	</TMPL_IF>
+	<button name="action" type="submit" value="cancel">Cancel</button>
+    </form>
+  </body>
+</html>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-namecase-general:t
+sgml-general-insert-case:lower
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:2
+sgml-indent-data:nil
+sgml-parent-document:nil
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vi: syntax=html
+-->
--- a/add_result.py	Fri Sep 26 11:03:02 2003 -0400
+++ b/add_result.py	Fri Sep 26 11:04:15 2003 -0400
@@ -12,36 +12,23 @@
 # import cgitb; cgitb.enable()
 import cgi
 from htmltmpl import TemplateManager, TemplateProcessor
+import time
+from utils import unique
 import my_db
-import time
-from bkmark import Bookmark
-
-def get_bk_from_form(form):
-	bk = Bookmark()
-	bk.id = int(form['id'].value)
-	bk.url = form['url'].value
-	if form.has_key('name'):
-		bk.name = form['name'].value
-	if form.has_key('desc'):
-		bk.desc = form['desc'].value
-	return bk
+from add_confirm import get_bk_from_form, get_new_kw_from_form
+from index import load_index
 
-def get_kw_from_form(form):
-	kw = form.getvalue("kw")
-	if not isinstance(kw, type([])):
-		if kw:
-			kw = [kw]
-		else:
-			kw = []
-	kw = map(int, kw)
-	return kw
-
-def get_new_kw_from_form(form):
-	if form.has_key('newkw'):
-		return map(lambda e: e.strip(),
-			form['newkw'].value.split(','))
+def add_new_keywords(db, newkw):
+    kw = [0] # Default keyword, so that every bookmarks appears in
+             # the bookmarks_keywords db.
+    (ids, kws, cnt) = map(list,apply(zip, db.get_all_keywords()))
+    for elem in unique(newkw):
+    	if elem in kws:
+		kw.append(ids[kws.index(elem)])
 	else:
-		return []
+		id = db.add_keyword(elem)
+		kw.append(id)
+    return kw
 
 def main(bk, kw):
     tmpl = TemplateManager().prepare("add_result.tmpl")
@@ -56,21 +43,21 @@
 if (__name__ == "__main__"):
     form = cgi.FieldStorage()
     db = my_db.connect()
-    bk = get_bk_from_form(form)
-    kw = get_kw_from_form(form)
-    newkw = get_new_kw_from_form(form)
-    (ids, kws, cnt) = map(list,apply(zip, db.get_all_keywords()))
-    for elem in newkw:
-    	if elem in kws:
-		kw.append(ids[kws.index(elem)])
-	else:
-		id = db.add_keyword(elem)
-		kw.append(id)
-    if bk.id == -1:
+    action = form['action'].value
+    if action == 'cancel':
+      load_index(db)
+    elif action == 'delete':
+      db.remove_bookmark(int(form['id'].value))
+      load_index(db)
+    else:
+      bk = get_bk_from_form(form)
+      newkw = get_new_kw_from_form(form)
+      kw = add_new_keywords(db, newkw)
+      if bk.id == -1:
 	id = db.add_bookmark(bk)
-    else:
-    	db.update_bookmark(bk)
+      else:
+	db.update_bookmark(bk)
 	id = bk.id
-    db.update_keywords(id, kw)
-    kw = map(lambda e: { 'keyword': e[1] }, db.get_keywords(id)[1:])
-    main(bk, kw)
+      db.update_keywords(id, kw)
+      kw = map(lambda e: { 'keyword': e[1] }, db.get_keywords(id)[1:])
+      main(bk, kw)
--- a/add_result.tmpl	Fri Sep 26 11:03:02 2003 -0400
+++ b/add_result.tmpl	Fri Sep 26 11:04:15 2003 -0400
@@ -9,8 +9,10 @@
     <p>Bookmark successfully added:</p>
     <p><a href="<TMPL_VAR url>"><TMPL_VAR name></a><p>
     <p><TMPL_VAR desc></p>
-    <p>Keywords:</p>
-    <TMPL_INCLUDE NAME="keywords.tmpl">
+    <p>Keywords:
+    <TMPL_LOOP Keywords>
+      <TMPL_VAR keyword>;
+    </TMPL_LOOP></p>
     <hr>
     <p>Retour à la <a href="index.py">page principale</a></p>
   </body>
--- a/inc/keywords.tmpl	Fri Sep 26 11:03:02 2003 -0400
+++ b/inc/keywords.tmpl	Fri Sep 26 11:04:15 2003 -0400
@@ -1,6 +1,6 @@
-<ul>
+<p>
 <TMPL_LOOP Keywords>
-  <li><TMPL_VAR keyword></li>
+  <TMPL_VAR keyword>;
 </TMPL_LOOP>
-</ul>
+</p>
 
--- a/index.py	Fri Sep 26 11:03:02 2003 -0400
+++ b/index.py	Fri Sep 26 11:04:15 2003 -0400
@@ -40,6 +40,19 @@
 def get_curl():
 	return urljoin( 'http://' + environ["HTTP_HOST"] + environ["REQUEST_URI"], 'add.py')
 
+def load_index(db):
+    	kw = db.get_all_keywords()
+	kw = set_selection(kw, [])
+    	total = kw[0]['count']
+    	kw = kw[1:]
+    	exc = map(lambda e: int(e['id']), kw)
+    	bookmarks = db.select_bookmarks([0], exc)
+	if len(bookmarks)>0:
+    		bookmarks = db.get_bookmarks(map(lambda x: x[0], bookmarks))
+    	bookmarks = map(lambda bk: bk.dict(), bookmarks)
+    	curl = get_curl();
+    	main(bookmarks, kw, curl, total)
+
 def main(bk, kw, curl, total):
     tmpl = TemplateManager().prepare("index.tmpl")
     tproc = TemplateProcessor()
--- a/index.tmpl	Fri Sep 26 11:03:02 2003 -0400
+++ b/index.tmpl	Fri Sep 26 11:04:15 2003 -0400
@@ -12,21 +12,19 @@
         <legend>Keywords:</legend>
 	<p>
 	<TMPL_LOOP Keywords>
-	  <input type="checkbox" name="kw"
+	  <input type="checkbox" name="kw" onclick="submit()"
 	  <TMPL_IF checked>checked</TMPL_IF>
 	  value="<TMPL_VAR id>"
 	  /><a href="edit_kw.py?id=<TMPL_VAR id>"><TMPL_VAR keyword></a>(<TMPL_VAR count>);
 	</TMPL_LOOP>
 	</p>
-      <button type="submit">Refresh</button>
       </fieldset>
     </form>
     <dl>
     <TMPL_LOOP Bookmarks>
       <dt>
         <a href="<TMPL_VAR url>"><TMPL_VAR name></a>
-	<a href="edit.py?id=<TMPL_VAR id>">edit<a>
-	<a href="delete.py?id=<TMPL_VAR id>">delete<a>
+	<a href="edit.py?id=<TMPL_VAR id>">edit</a>
       </dt>
       <dd><p><TMPL_VAR desc></p></dd>
     </TMPL_LOOP>
--- a/kw_result.py	Fri Sep 26 11:03:02 2003 -0400
+++ b/kw_result.py	Fri Sep 26 11:04:15 2003 -0400
@@ -15,7 +15,7 @@
 import my_db
 from os import environ
 from urlparse import urljoin
-from index import set_selection, get_curl, main
+from index import load_index
 
 def do_merge(form, db):
 	fromid = int(form['id'].value)	
@@ -41,14 +41,4 @@
 	do_update(form, db)
     elif action == 'delete':
     	do_delete(form, db)
-    keywords = db.get_all_keywords()
-    total = keywords[0][2]
-    keywords = keywords[1:]
-    exc = map(lambda e: int(e[0]), keywords)
-    bookmarks = db.select_bookmarks([0], exc)
-    keywords = set_selection(keywords, [])
-    if len(bookmarks) > 0:
-    	bookmarks = db.get_bookmarks(map(lambda x: x[0], bookmarks))
-    bookmarks = map(lambda bk: bk.dict(), bookmarks)
-    curl = get_curl();
-    main(bookmarks, keywords, curl, total)
+    load_index(db)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/utils.py	Fri Sep 26 11:04:15 2003 -0400
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+
+def unique(s):
+    """Return a list of the elements in s, but without duplicates.
+
+    For example, unique([1,2,3,1,2,3]) is some permutation of [1,2,3],
+    unique("abcabc") some permutation of ["a", "b", "c"], and
+    unique(([1, 2], [2, 3], [1, 2])) some permutation of
+    [[2, 3], [1, 2]].
+
+    For best speed, all sequence elements should be hashable.  Then
+    unique() will usually work in linear time.
+
+    If not possible, the sequence elements should enjoy a total
+    ordering, and if list(s).sort() doesn't raise TypeError it's
+    assumed that they do enjoy a total ordering.  Then unique() will
+    usually work in O(N*log2(N)) time.
+
+    If that's not possible either, the sequence elements must support
+    equality-testing.  Then unique() will usually work in quadratic
+    time.
+    """
+
+    n = len(s)
+    if n == 0:
+        return []
+
+    # Try using a dict first, as that's the fastest and will usually
+    # work.  If it doesn't work, it will usually fail quickly, so it
+    # usually doesn't cost much to *try* it.  It requires that all the
+    # sequence elements be hashable, and support equality comparison.
+    u = {}
+    try:
+        for x in s:
+            u[x] = 1
+    except TypeError:
+        del u  # move on to the next method
+    else:
+        return u.keys()
+
+    # We can't hash all the elements.  Second fastest is to sort,
+    # which brings the equal elements together; then duplicates are
+    # easy to weed out in a single pass.
+    # NOTE:  Python's list.sort() was designed to be efficient in the
+    # presence of many duplicate elements.  This isn't true of all
+    # sort functions in all languages or libraries, so this approach
+    # is more effective in Python than it may be elsewhere.
+    try:
+        t = list(s)
+        t.sort()
+    except TypeError:
+        del t  # move on to the next method
+    else:
+        assert n > 0
+        last = t[0]
+        lasti = i = 1
+        while i < n:
+            if t[i] != last:
+                t[lasti] = last = t[i]
+                lasti += 1
+            i += 1
+        return t[:lasti]
+
+    # Brute force is all that's left.
+    u = []
+    for x in s:
+        if x not in u:
+            u.append(x)
+    return u
+