eric6/Helpviewer/AdBlock/AdBlockManager.py

branch
maintenance
changeset 7286
7eb04391adf7
parent 7226
babe80d84a3e
parent 7285
1ff497f33f31
child 7287
1c17f2191bdd
equal deleted inserted replaced
7226:babe80d84a3e 7286:7eb04391adf7
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the AdBlock manager.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import pyqtSignal, QObject, QUrl, QFile
15
16 from .AdBlockSubscription import AdBlockSubscription
17
18 from Utilities.AutoSaver import AutoSaver
19 import Utilities
20 import Preferences
21
22
23 class AdBlockManager(QObject):
24 """
25 Class implementing the AdBlock manager.
26
27 @signal rulesChanged() emitted after some rule has changed
28 @signal requiredSubscriptionLoaded(subscription) emitted to indicate
29 loading of a required subscription is finished (AdBlockSubscription)
30 """
31 rulesChanged = pyqtSignal()
32 requiredSubscriptionLoaded = pyqtSignal(AdBlockSubscription)
33
34 def __init__(self, parent=None):
35 """
36 Constructor
37
38 @param parent reference to the parent object (QObject)
39 """
40 super(AdBlockManager, self).__init__(parent)
41
42 self.__loaded = False
43 self.__subscriptionsLoaded = False
44 self.__enabled = False
45 self.__adBlockDialog = None
46 self.__adBlockExceptionsDialog = None
47 self.__adBlockNetwork = None
48 self.__adBlockPage = None
49 self.__subscriptions = []
50 self.__exceptedHosts = Preferences.getHelp("AdBlockExceptions")
51 self.__saveTimer = AutoSaver(self, self.save)
52
53 self.__defaultSubscriptionUrlString = \
54 "abp:subscribe?location=" \
55 "https://easylist-downloads.adblockplus.org/easylist.txt&"\
56 "title=EasyList"
57 self.__customSubscriptionUrlString = \
58 bytes(self.__customSubscriptionUrl().toEncoded()).decode()
59
60 self.rulesChanged.connect(self.__saveTimer.changeOccurred)
61
62 def close(self):
63 """
64 Public method to close the open search engines manager.
65 """
66 self.__adBlockDialog and self.__adBlockDialog.close()
67 self.__adBlockExceptionsDialog and \
68 self.__adBlockExceptionsDialog.close()
69
70 self.__saveTimer.saveIfNeccessary()
71
72 def isEnabled(self):
73 """
74 Public method to check, if blocking ads is enabled.
75
76 @return flag indicating the enabled state (boolean)
77 """
78 if not self.__loaded:
79 self.load()
80
81 return self.__enabled
82
83 def setEnabled(self, enabled):
84 """
85 Public slot to set the enabled state.
86
87 @param enabled flag indicating the enabled state (boolean)
88 """
89 if self.isEnabled() == enabled:
90 return
91
92 import Helpviewer.HelpWindow
93 self.__enabled = enabled
94 for mainWindow in Helpviewer.HelpWindow.HelpWindow.mainWindows():
95 mainWindow.adBlockIcon().setEnabled(enabled)
96 if enabled:
97 self.__loadSubscriptions()
98 self.rulesChanged.emit()
99
100 def network(self):
101 """
102 Public method to get a reference to the network block object.
103
104 @return reference to the network block object (AdBlockNetwork)
105 """
106 if self.__adBlockNetwork is None:
107 from .AdBlockNetwork import AdBlockNetwork
108 self.__adBlockNetwork = AdBlockNetwork(self)
109 return self.__adBlockNetwork
110
111 def page(self):
112 """
113 Public method to get a reference to the page block object.
114
115 @return reference to the page block object (AdBlockPage)
116 """
117 if self.__adBlockPage is None:
118 from .AdBlockPage import AdBlockPage
119 self.__adBlockPage = AdBlockPage(self)
120 return self.__adBlockPage
121
122 def __customSubscriptionLocation(self):
123 """
124 Private method to generate the path for custom subscriptions.
125
126 @return URL for custom subscriptions (QUrl)
127 """
128 dataDir = os.path.join(Utilities.getConfigDir(), "browser",
129 "subscriptions")
130 if not os.path.exists(dataDir):
131 os.makedirs(dataDir)
132 fileName = os.path.join(dataDir, "adblock_subscription_custom")
133 return QUrl.fromLocalFile(fileName)
134
135 def __customSubscriptionUrl(self):
136 """
137 Private method to generate the URL for custom subscriptions.
138
139 @return URL for custom subscriptions (QUrl)
140 """
141 location = self.__customSubscriptionLocation()
142 encodedUrl = bytes(location.toEncoded()).decode()
143 url = QUrl("abp:subscribe?location={0}&title={1}".format(
144 encodedUrl, self.tr("Custom Rules")))
145 return url
146
147 def customRules(self):
148 """
149 Public method to get a subscription for custom rules.
150
151 @return subscription object for custom rules (AdBlockSubscription)
152 """
153 location = self.__customSubscriptionLocation()
154 for subscription in self.__subscriptions:
155 if subscription.location() == location:
156 return subscription
157
158 url = self.__customSubscriptionUrl()
159 customAdBlockSubscription = AdBlockSubscription(url, True, self)
160 self.addSubscription(customAdBlockSubscription)
161 return customAdBlockSubscription
162
163 def subscriptions(self):
164 """
165 Public method to get all subscriptions.
166
167 @return list of subscriptions (list of AdBlockSubscription)
168 """
169 if not self.__loaded:
170 self.load()
171
172 return self.__subscriptions[:]
173
174 def subscription(self, location):
175 """
176 Public method to get a subscription based on its location.
177
178 @param location location of the subscription to search for (string)
179 @return subscription or None (AdBlockSubscription)
180 """
181 if location != "":
182 for subscription in self.__subscriptions:
183 if subscription.location().toString() == location:
184 return subscription
185
186 return None
187
188 def updateAllSubscriptions(self):
189 """
190 Public method to update all subscriptions.
191 """
192 for subscription in self.__subscriptions:
193 subscription.updateNow()
194
195 def removeSubscription(self, subscription, emitSignal=True):
196 """
197 Public method to remove an AdBlock subscription.
198
199 @param subscription AdBlock subscription to be removed
200 (AdBlockSubscription)
201 @param emitSignal flag indicating to send a signal (boolean)
202 """
203 if subscription is None:
204 return
205
206 if subscription.url().toString().startswith(
207 (self.__defaultSubscriptionUrlString,
208 self.__customSubscriptionUrlString)):
209 return
210
211 try:
212 self.__subscriptions.remove(subscription)
213 rulesFileName = subscription.rulesFileName()
214 QFile.remove(rulesFileName)
215 requiresSubscriptions = self.getRequiresSubscriptions(subscription)
216 for requiresSubscription in requiresSubscriptions:
217 self.removeSubscription(requiresSubscription, False)
218 if emitSignal:
219 self.rulesChanged.emit()
220 except ValueError:
221 pass
222
223 def addSubscription(self, subscription):
224 """
225 Public method to add an AdBlock subscription.
226
227 @param subscription AdBlock subscription to be added
228 (AdBlockSubscription)
229 """
230 if subscription is None:
231 return
232
233 self.__subscriptions.insert(-1, subscription)
234
235 subscription.rulesChanged.connect(self.rulesChanged)
236 subscription.changed.connect(self.rulesChanged)
237 subscription.enabledChanged.connect(self.rulesChanged)
238
239 self.rulesChanged.emit()
240
241 def save(self):
242 """
243 Public method to save the AdBlock subscriptions.
244 """
245 if not self.__loaded:
246 return
247
248 Preferences.setHelp("AdBlockEnabled", self.__enabled)
249 if self.__subscriptionsLoaded:
250 subscriptions = []
251 requiresSubscriptions = []
252 # intermediate store for subscription requiring others
253 for subscription in self.__subscriptions:
254 if subscription is None:
255 continue
256 urlString = bytes(subscription.url().toEncoded()).decode()
257 if "requiresLocation" in urlString:
258 requiresSubscriptions.append(urlString)
259 else:
260 subscriptions.append(urlString)
261 subscription.saveRules()
262 for subscription in requiresSubscriptions:
263 subscriptions.insert(-1, subscription) # custom should be last
264 Preferences.setHelp("AdBlockSubscriptions", subscriptions)
265
266 def load(self):
267 """
268 Public method to load the AdBlock subscriptions.
269 """
270 if self.__loaded:
271 return
272
273 self.__loaded = True
274
275 self.__enabled = Preferences.getHelp("AdBlockEnabled")
276 if self.__enabled:
277 self.__loadSubscriptions()
278
279 def __loadSubscriptions(self):
280 """
281 Private method to load the set of subscriptions.
282 """
283 if self.__subscriptionsLoaded:
284 return
285
286 subscriptions = Preferences.getHelp("AdBlockSubscriptions")
287 if subscriptions:
288 for subscription in subscriptions:
289 if subscription.startswith(
290 self.__defaultSubscriptionUrlString):
291 break
292 else:
293 subscriptions.insert(0, self.__defaultSubscriptionUrlString)
294 for subscription in subscriptions:
295 if subscription.startswith(self.__customSubscriptionUrlString):
296 break
297 else:
298 subscriptions.append(self.__customSubscriptionUrlString)
299 else:
300 subscriptions = [self.__defaultSubscriptionUrlString,
301 self.__customSubscriptionUrlString]
302 for subscription in subscriptions:
303 url = QUrl.fromEncoded(subscription.encode("utf-8"))
304 adBlockSubscription = AdBlockSubscription(
305 url,
306 subscription.startswith(self.__customSubscriptionUrlString),
307 self,
308 subscription.startswith(self.__defaultSubscriptionUrlString))
309 adBlockSubscription.rulesChanged.connect(self.rulesChanged)
310 adBlockSubscription.changed.connect(self.rulesChanged)
311 adBlockSubscription.enabledChanged.connect(self.rulesChanged)
312 self.__subscriptions.append(adBlockSubscription)
313
314 self.__subscriptionsLoaded = True
315
316 def loadRequiredSubscription(self, location, title):
317 """
318 Public method to load a subscription required by another one.
319
320 @param location location of the required subscription (string)
321 @param title title of the required subscription (string)
322 """
323 # Step 1: check, if the subscription is in the list of subscriptions
324 urlString = "abp:subscribe?location={0}&title={1}".format(
325 location, title)
326 for subscription in self.__subscriptions:
327 if subscription.url().toString().startswith(urlString):
328 # We found it!
329 return
330
331 # Step 2: if it is not, get it
332 url = QUrl.fromEncoded(urlString.encode("utf-8"))
333 adBlockSubscription = AdBlockSubscription(url, False, self)
334 self.addSubscription(adBlockSubscription)
335 self.requiredSubscriptionLoaded.emit(adBlockSubscription)
336
337 def getRequiresSubscriptions(self, subscription):
338 """
339 Public method to get a list of subscriptions, that require the given
340 one.
341
342 @param subscription subscription to check for (AdBlockSubscription)
343 @return list of subscription requiring the given one (list of
344 AdBlockSubscription)
345 """
346 subscriptions = []
347 location = subscription.location().toString()
348 for subscription in self.__subscriptions:
349 if subscription.requiresLocation() == location:
350 subscriptions.append(subscription)
351
352 return subscriptions
353
354 def showDialog(self):
355 """
356 Public slot to show the AdBlock subscription management dialog.
357
358 @return reference to the dialog (AdBlockDialog)
359 """
360 if self.__adBlockDialog is None:
361 from .AdBlockDialog import AdBlockDialog
362 self.__adBlockDialog = AdBlockDialog()
363
364 self.__adBlockDialog.show()
365 return self.__adBlockDialog
366
367 def showRule(self, act):
368 """
369 Public slot to show an AdBlock rule.
370
371 @param act reference to the action
372 @type QAction
373 """
374 rule = act.data()
375 if rule:
376 self.showDialog().showRule(rule)
377
378 def elementHidingRules(self):
379 """
380 Public method to get the element hiding rules.
381
382 @return element hiding rules (string)
383 """
384 if not self.__enabled:
385 return ""
386
387 rules = ""
388
389 for subscription in self.__subscriptions:
390 rules += subscription.elementHidingRules()
391
392 if rules:
393 # remove last ",
394 rules = rules[:-1]
395
396 return rules
397
398 def elementHidingRulesForDomain(self, url):
399 """
400 Public method to get the element hiding rules for a domain.
401
402 @param url URL to get hiding rules for (QUrl)
403 @return element hiding rules (string)
404 """
405 if not self.__enabled:
406 return ""
407
408 rules = ""
409
410 for subscription in self.__subscriptions:
411 if subscription.elemHideDisabledForUrl(url):
412 return ""
413
414 rules += subscription.elementHidingRulesForDomain(url.host())
415
416 if rules:
417 # remove last ",
418 rules = rules[:-1]
419
420 return rules
421
422 def exceptions(self):
423 """
424 Public method to get a list of excepted hosts.
425
426 @return list of excepted hosts (list of string)
427 """
428 return self.__exceptedHosts
429
430 def setExceptions(self, hosts):
431 """
432 Public method to set the list of excepted hosts.
433
434 @param hosts list of excepted hosts (list of string)
435 """
436 self.__exceptedHosts = hosts[:]
437 Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts)
438
439 def addException(self, host):
440 """
441 Public method to add an exception.
442
443 @param host to be excepted (string)
444 """
445 if host and host not in self.__exceptedHosts:
446 self.__exceptedHosts.append(host)
447 Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts)
448
449 def removeException(self, host):
450 """
451 Public method to remove an exception.
452
453 @param host to be removed from the list of exceptions (string)
454 """
455 if host in self.__exceptedHosts:
456 self.__exceptedHosts.remove(host)
457 Preferences.setHelp("AdBlockExceptions", self.__exceptedHosts)
458
459 def isHostExcepted(self, host):
460 """
461 Public slot to check, if a host is excepted.
462
463 @param host host to check (string)
464 @return flag indicating an exception (boolean)
465 """
466 return host in self.__exceptedHosts
467
468 def showExceptionsDialog(self):
469 """
470 Public method to show the AdBlock Exceptions dialog.
471
472 @return reference to the exceptions dialog (AdBlockExceptionsDialog)
473 """
474 if self.__adBlockExceptionsDialog is None:
475 from .AdBlockExceptionsDialog import AdBlockExceptionsDialog
476 self.__adBlockExceptionsDialog = AdBlockExceptionsDialog()
477
478 self.__adBlockExceptionsDialog.load(self.__exceptedHosts)
479 self.__adBlockExceptionsDialog.show()
480 return self.__adBlockExceptionsDialog

eric ide

mercurial