[svn r1551] Add delete bookmarks functionality as well as many reusability issues.
--- 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
+