"""
Classes to store bookmarks and dump them to XBEL.
"""
import sys,string,types
from xml.sax.saxutils import escape
# --- Class for bookmark container
class Bookmarks:
def __init__(self, info=None, id=None, title=None):
self.folders=[]
self.folder_stack=[]
self.desc = "No description"
self.info = info
self.id = id
self.title = title
def add_folder(self, name, added=None):
nf=Folder(name, added)
if self.folder_stack==[]:
self.folders.append(nf)
else:
self.folder_stack[-1].add_child(nf)
self.folder_stack.append(nf)
return nf
def add_bookmark(self,name=None,
added=None, visited=None, modified=None,
href=None, desc = None):
nb=Bookmark(name,added,visited,modified,href, desc = desc)
if self.folder_stack!=[]:
self.folder_stack[-1].add_child(nb)
else:
self.folders.append(nb)
return nb
def add_separator(self):
s = Separator()
if self.folder_stack!=[]:
self.folder_stack[-1].add_child(s)
else:
self.folders.append(s)
return s
def leave_folder(self):
if self.folder_stack!=[]:
del self.folder_stack[-1]
def update_ids(self):
ids = {}
aliases = []
for folder in self.folders:
folder.update_ids(ids, aliases)
for alias in aliases:
alias.update_link(ids)
def dump_xbel(self,out=sys.stdout):
if self.id:
ID = ' id="%s"' % self.id
else:
ID = ""
out.write('\n'
'\n'
'\n'
% ID
)
if self.title:
out.write(" %s\n" % esc_enc(self.title))
if self.info:
out.write(" %s\n" % esc_enc(self.info))
out.write(" %s\n" % (esc_enc(self.desc),) )
for folder in self.folders:
folder.dump_xbel(out)
out.write("\n")
def dump_adr(self,out=sys.stdout):
out.write("Opera Hotlist version 2.0\n\n")
for folder in self.folders:
folder.dump_adr(out)
def dump_netscape(self,out=sys.stdout):
out.write("\n")
out.write("\n")
# Mozilla recognizes the content-type declaration; let's hope
# Netscape 4 is not bothered by it
out.write('\n')
if self.title:
out.write("
" + esc_enc(self.title) + "\n")
else:
out.write("Bookmarks\n")
out.write("" + esc_enc(self.desc) + "
\n\n")
out.write("\n")
for folder in self.folders:
folder.dump_netscape(out)
out.write("
\n")
# Lynx uses multiple bookmark files; each folder will be written to a
# different file.
def dump_lynx(self, path):
import os
for folder in self.folders:
# First, figure out a reasonable filename for this folder
filename = string.replace(folder.title, ' ', '_') + '.html'
# Open a file for the top-level folders
output = open( os.path.join(path, filename), 'w')
print 'folder title:', folder.title, filename
output.write('
\n%s\n\n'
% (folder.title,) )
output.write('\n
\n')
folder.dump_lynx(output)
output.close()
# --- Superclass for folder and bookmarks
class Node:
def __init__(self, name, added=None,
visited=None, modified=None, id=None, desc=None):
self.title = name
self.added = added
self.visited = visited
self.modified = modified
self.id = id
self.desc = desc
def update_ids(self, ids, aliases):
if self.id:
while ids.has_key(self.id):
# Duplicate ID
self.id = self.id + '0'
ids[self.id] = self
def gen_id(self, ids):
self.id = 'X'+str(id(self))
self.update_ids(ids, [])
# --- Class for folders
class Folder(Node):
def __init__(self, name, added = None, info = None, id=None,
folded = 'yes', icon = None, toolbar = 'no', desc=None):
Node.__init__(self, name, added=added, id=None,desc=desc)
self.children=[]
self.info = None
self.folded = None
self.icon = None
self.toolbar = None
def add_child(self,child):
self.children.append(child)
def update_ids(self, ids, aliases):
Node.update_ids(self, ids, aliases)
for node in self.children:
node.update_ids(ids, aliases)
def is_folded(self):
# folded defaults to yes
return self.folded is None or self.folded == 'yes'
def dump_xbel(self,out):
if self.id:
ID = ' id="%s"' % self.id
else:
ID = ""
if self.added:
added = ' added="%s"' % self.added
else:
added = ""
if self.folded is not None:
folded = ' folded="%s"' % self.folded
else:
folded = ""
if self.icon is not None:
icon = ' icon="%s"' % self.icon
else:
icon = ""
if self.toolbar is not None:
toolbar = ' toolbar="%s"' % self.toolbar
else:
toolbar = ""
out.write(" \n" % (ID, added, folded, icon, toolbar))
out.write(" %s\n" % esc_enc(self.title) )
if self.info:
out.write(" %s\n" % esc_enc(self.info))
if self.desc:
out.write(" %s\n" % esc_enc(self.desc))
for child in self.children:
child.dump_xbel(out)
out.write(" \n")
def dump_adr(self,out):
out.write("#FOLDER\n")
out.write("\tNAME=%s\n" % self.title)
out.write("\tADDED=%s\n" % "0 (?)")
out.write("\tVISITED=%s\n" % "0 (?)")
out.write("\tORDER=-1\n")
out.write("\n")
for child in self.children:
child.dump_adr(out)
out.write("\n")
out.write("-\n")
def dump_netscape(self,out):
if self.id:
ID = ' ID="%s"' % self.id
else:
ID = ""
if self.is_folded():
folded = " FOLDED"
else:
folded = ""
if self.added:
added = ' ADD_DATE="%s"' % self.added
else:
added = ""
out.write(" %s
\n" %
(folded,added,ID,esc_enc(self.title)))
if self.desc:
out.write(" - %s\n" %
(esc_enc(self.desc)))
out.write("
\n")
for child in self.children:
child.dump_netscape(out)
out.write("
\n")
def dump_lynx(self, out):
out.write("
%s
\n" % self.title)
out.write(" \n")
for child in self.children:
child.dump_lynx(out)
# Mustn't write the closing
, because Lynx will add it
# when it reads the bookmark file.
##out.write("
\n")
# --- Class for bookmarks
class Bookmark(Node):
def __init__(self, name, added=None, visited=None,
modified=None, href=None, info=None, id = None, desc = None):
Node.__init__(self,name,added,visited,modified, id=id, desc=desc)
self.href = href
self.info = info
def dump_xbel(self,out):
if self.id:
ID = ' id="%s"' % self.id
else:
ID = ""
if self.visited!=None:
visited = ' visited="%s"' % escape(self.visited)
else:
visited = ""
if self.added!=None:
added = ' added="%s"' % escape(self.added)
else:
added = ""
if self.modified!=None:
modified = ' modified="%s"' % escape(self.modified)
else:
modified = ""
out.write(' \n' %
( esc_enc(self.href), ID, added, visited, modified) )
out.write(" %s\n" % esc_enc(self.title) )
if self.info:
out.write(" %s\n" % esc_enc(self.info))
if self.desc:
out.write(" %s\n" % esc_enc(self.desc))
out.write(" \n")
def dump_adr(self,out):
out.write("#URL\n")
out.write("\tNAME=%s\n" % self.title)
out.write("\tURL=%s\n" % self.href)
out.write("\tCREATED=%s\n" % "0 (?)")
out.write("\tVISITED=%s\n" % "0 (?)")
out.write("\tORDER=-1\n")
out.write("\n")
def dump_netscape(self,out):
added = visited = modified = ""
if self.added:
added = ' ADD_DATE="%s"' % encode(self.added)
if self.visited:
visited = ' LAST_VISIT="%s"' % encode(self.visited)
if self.modified:
modified = ' LAST_MODIFIED="%s"' % encode(self.modified)
out.write(" %s\n" %
(encode(self.href),added,visited,modified,
esc_enc(self.title)))
if self.desc:
out.write(" %s\n" %(esc_enc(self.desc)))
def dump_lynx(self, out):
out.write("%s\n" % (self.href, esc_enc(self.title)) )
# --- Class for separators
class Separator(Node):
def __init__(self):
Node.__init__(self, None)
def dump_xbel(self, out):
out.write(' \n')
def dump_netscape(self, out):
out.write("
\n")
# --- Class for separators
class InvalidReference(Exception):
pass
class Alias(Node):
def __init__(self, aliased_to):
Node.__init__(self, None)
if isinstance(aliased_to, Node):
self.aliased_to = aliased_to
self.ref = None
else:
self.aliased_to = None
self.ref = aliased_to
def update_ids(self, ids, aliases):
aliases.append(self)
def update_link(self, ids):
if self.aliased_to:
if self.aliased_to.id is None:
self.aliased_to.gen_id(ids)
self.ref = self.aliased_to.id
else:
try:
self.aliased_to = ids[self.ref]
except KeyError:
raise InvalidReference, self.ref
def dump_xbel(self, out):
out.write(' \n' % self.ref)
# --- helper functions
try:
types.UnicodeType
except AttributeError:
def encode(str, encoding = "utf-8"):
# Can't do proper recoding in Python 1.5
return str
else:
def encode(str, encoding = "utf-8"):
if type(str) == types.UnicodeType:
return str.encode(encoding)
return str
def esc_enc(str):
return encode(escape(str))