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-2023 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',
19 from urllib.parse import quote, \
20 splittype, splithost, splituser, splitpasswd, \
23 from urllib import quote, \
24 splittype, splithost, splituser, splitpasswd, \
27 BKMK_FORMAT = os.environ.get("BKMK_FORMAT", "MOZILLA")
34 def __init__(self, add_date=None, comment='', last_modified=None):
35 super(Folder, self).__init__()
36 self.comment = comment
37 self.add_date = add_date
38 self.last_modified = last_modified
40 def walk_depth(self, walker, level=0):
41 if hasattr(self, "header"): # root folder
43 walker.root_folder(self)
45 prune = walker.prune_folder(self)
47 walker.start_folder(self, level)
52 object.walk_depth(walker, level+1)
53 elif object.isBookmark:
54 walker.bookmark(object, level)
56 walker.ruler(object, level)
58 walker.end_folder(self, level)
61 class Bookmark(object):
65 def __init__(self, href, add_date, last_visit=None, last_modified=None,
66 keyword=None, comment='', icon_href=None, icon=None,
67 charset=None, parser_charset=None):
68 protocol, request = splittype(href)
69 user, password, port = None, None, None
70 host, path = splithost(request)
72 user, host = splituser(host)
74 user, password = splitpasswd(user)
75 host, port = splitport(host)
76 if port: port = int(port)
78 if protocol == 'place':
81 href = protocol + "://"
85 href += ':' + quote(password)
88 href += host.decode(parser_charset or 'utf-8').encode('idna')
95 self.add_date = add_date
96 self.last_visit = last_visit
97 self.last_modified = last_modified
98 self.keyword = keyword
99 self.comment = comment
100 self.icon_href = icon_href
102 self.charset = charset
110 class Walker(object):
112 Interface class. Any instance that will be passed to Folder.walk_depth
113 may be derived from this class. It is not mandatory - unlike Java
114 Python does not require interface classes; but it is convenient to have
115 some methods predefined to no-op, in case you do not want to
116 provide end_folder etc.
119 def root_folder(self, r):
122 def start_folder(self, f, level):
125 def end_folder(self, f, level):
128 def bookmark(self, b, level):
131 def ruler(self, r, level):
134 def prune_folder(self, folder):
138 class Writer(Walker):
139 def __init__(self, outfile, prune=None):
140 self.outfile = outfile
143 def prune_folder(self, folder):
144 return self.prune == folder.name
148 def __init__(self, log):
152 pass # Nothing to do on cleanup
155 # Helper class to make inverese links (nodes linked to their parent)
156 class InverseLinker(Walker):
157 def root_folder(self, r):
158 self.parent_stack = [r]
160 def start_folder(self, f, level):
161 f.parent = self.parent_stack[-1]
162 # Push the folder onto the stack of parents
163 self.parent_stack.append(f)
165 def end_folder(self, f, level):
166 del self.parent_stack[-1] # Pop off the stack
168 def bookmark(self, b, level):
169 b.parent = self.parent_stack[-1]
171 def ruler(self, r, level):
172 r.parent = self.parent_stack[-1]
175 # Helper class to make linear represenatation of the tree
176 class Linear(Walker):
177 def root_folder(self, r):
179 self.linear = r.linear
181 def add_object(self, object):
182 self.linear.append(object)
184 def start_folder(self, f, level):
187 def bookmark(self, b, level):
190 def ruler(self, r, level):
194 # Helper - make linked linear represenatation of the tree,
195 # suitable to be stored in sequential storage.
196 def make_linear(root_folder):
197 linker = InverseLinker()
198 root_folder.walk_depth(linker)
201 root_folder.walk_depth(linear)
204 # Helper, opposite of make_linear -
205 # make a tree from the linked linear representation.
206 def make_tree(linear):
207 root_folder = linear[0]
210 for object in linear:
211 object.parent.append(object)
216 def break_tree(linear):
219 for object in linear:
223 def quote_title(title):
224 if BKMK_FORMAT == "MOZILLA":
225 title = title.replace("'", "'")
229 def unquote_title(title):
230 if BKMK_FORMAT == "MOZILLA":
231 from HTMLParser import HTMLParser
232 title = HTMLParser().unescape(
233 title.replace("&", '&').decode('utf-8'))
234 title = title.encode('utf-8').replace("'", "'")
238 def parse_params(param_str):
239 params = param_str.split(':')
240 main_param = params.pop(0)
243 key, value = param.split('=', 1)
244 param_list[key] = value
245 return main_param, param_list
248 def set_params(obj, params):
249 if hasattr(params, "items"):
250 params = params.items()
251 for key, value in params:
252 setattr(obj, key, value)