1 """Objects to represent bookmarks.html structure
3 This file is a part of Bookmarks database and Internet robot.
7 __author__ = "Oleg Broytman <phd@phdru.name>"
8 __copyright__ = "Copyright (C) 2000-2024 PhiloSoft Design"
9 __license__ = "GNU GPL"
11 __all__ = ['Folder', 'Bookmark', 'Ruler', 'Walker', 'Writer', 'Robot',
12 'InverseLinker', 'Linear', 'make_linear', 'make_tree', 'break_tree',
13 'quote_title', 'unquote_title', 'parse_params', 'set_params',
17 from urllib.parse import urlsplit, quote, unquote
20 BKMK_FORMAT = os.environ.get("BKMK_FORMAT", "MOZILLA")
27 def __init__(self, add_date=None, comment='', last_modified=None):
28 super(Folder, self).__init__()
29 self.comment = comment
30 self.add_date = add_date
31 self.last_modified = last_modified
33 def walk_depth(self, walker, level=0):
34 if hasattr(self, "header"): # root folder
36 walker.root_folder(self)
38 prune = walker.prune_folder(self)
40 walker.start_folder(self, level)
45 object.walk_depth(walker, level+1)
46 elif object.isBookmark:
47 walker.bookmark(object, level)
49 walker.ruler(object, level)
51 walker.end_folder(self, level)
54 class Bookmark(object):
58 def __init__(self, href, add_date, last_visit=None, last_modified=None,
59 keyword=None, comment='', icon_href=None, icon=None,
60 charset=None, parser_charset=None):
61 split_results = urlsplit(href)
62 protocol, netloc, path, query, tag = split_results
63 user = split_results.username
64 password = split_results.password
65 host = split_results.hostname
66 port = split_results.port
68 if protocol == 'place':
71 href = protocol + "://"
75 href += ':' + quote(password)
78 href += host.encode('idna').decode('ascii')
85 self.add_date = add_date
86 self.last_visit = last_visit
87 self.last_modified = last_modified
88 self.keyword = keyword
89 self.comment = comment
90 self.icon_href = icon_href
92 self.charset = charset
100 class Walker(object):
102 Interface class. Any instance that will be passed to Folder.walk_depth
103 may be derived from this class. It is not mandatory - unlike Java
104 Python does not require interface classes; but it is convenient to have
105 some methods predefined to no-op, in case you do not want to
106 provide end_folder etc.
109 def root_folder(self, r):
112 def start_folder(self, f, level):
115 def end_folder(self, f, level):
118 def bookmark(self, b, level):
121 def ruler(self, r, level):
124 def prune_folder(self, folder):
128 class Writer(Walker):
129 def __init__(self, outfile, prune=None):
130 self.outfile = outfile
133 def prune_folder(self, folder):
134 return self.prune == folder.name
138 def __init__(self, log):
142 pass # Nothing to do on cleanup
145 # Helper class to make inverese links (nodes linked to their parent)
146 class InverseLinker(Walker):
147 def root_folder(self, r):
148 self.parent_stack = [r]
150 def start_folder(self, f, level):
151 f.parent = self.parent_stack[-1]
152 # Push the folder onto the stack of parents
153 self.parent_stack.append(f)
155 def end_folder(self, f, level):
156 del self.parent_stack[-1] # Pop off the stack
158 def bookmark(self, b, level):
159 b.parent = self.parent_stack[-1]
161 def ruler(self, r, level):
162 r.parent = self.parent_stack[-1]
165 # Helper class to make linear represenatation of the tree
166 class Linear(Walker):
167 def root_folder(self, r):
169 self.linear = r.linear
171 def add_object(self, object):
172 self.linear.append(object)
174 def start_folder(self, f, level):
177 def bookmark(self, b, level):
180 def ruler(self, r, level):
184 # Helper - make linked linear represenatation of the tree,
185 # suitable to be stored in sequential storage.
186 def make_linear(root_folder):
187 linker = InverseLinker()
188 root_folder.walk_depth(linker)
191 root_folder.walk_depth(linear)
194 # Helper, opposite of make_linear -
195 # make a tree from the linked linear representation.
196 def make_tree(linear):
197 root_folder = linear[0]
200 for object in linear:
201 object.parent.append(object)
206 def break_tree(linear):
209 for object in linear:
213 def quote_title(title):
214 if BKMK_FORMAT == "MOZILLA":
215 title = title.replace("'", "'")
219 def unquote_title(title):
220 if BKMK_FORMAT == "MOZILLA":
222 from HTMLParser import HTMLParser
224 from html import unescape
226 unescape = HTMLParser().unescape
228 title.replace("&", '&'))
229 title = title.replace("'", "'")
233 def parse_params(param_str):
234 params = param_str.split(':')
235 main_param = params.pop(0)
238 key, value = param.split('=', 1)
239 param_list[key] = unquote(value)
240 return main_param, param_list
243 def set_params(obj, params):
244 if hasattr(params, "items"):
245 params = params.items()
246 for key, value in params:
247 setattr(obj, key, value)