View Issue Details

IDProjectCategoryView StatusLast Update
2137Composrbannerspublic2021-11-01 20:04
ReporterChris Graham Assigned ToGuest  
PrioritynormalSeverityfeature 
Status newResolutionopen 
Summary2137: Detect ad blockers
DescriptionIf an ad blocker is in action, detect this, and either (based on configuration):
1) show a "do not block ads" message at the top of the site [default]
2) block the whole page view
Iff the user is not in a group that has privilege to avoid the adblock detection (i.e. people can be given the privilege to get away with adblocking, which could, for example, be sold with a usergroup subscription - probably along with the avoid banners privilege).

By default say that you should provide a donation if you want to get rid of ads (to cover website costs), or add the site to the ads whitelist - and that you promise to not show bad kinds of ads (I think there's a standard policy for this that can be referenced). Make this be controlled via a config option, the text customisable easily.

Use a clean JavaScript function to detect the blocked ads that can be overridden in a theme.
The detection of banner blocking would be quite hard. Perhaps the best way to do it is with a false-positive invisible banner, and see if that becomes even more invisible (or removed) via some external actor. Here's a technique for external ads: https://davidwalsh.name/detect-ad-blocker
TagsHas Patch, Roadmap: Over the horizon
Attach Tags
Attached Files
Time estimation (hours)2
Sponsorship open

Sponsor

Date Added Member Amount Sponsored

Relationships

has duplicate 1989 ClosedChris Graham Composr non-bundled addons Adblocker detector 

Activities

Adam Edington

2016-01-17 00:29

administrator   ~3275

I think a simple detection and message to donate in order to hide ads to the user would be sufficient. Doesn't have to detect if banners are hidden or shown or invisible (or does it). Just needs to detect (in most cases) the adblocker. Sites like Channel 4 detect adblocker, because it can block the adverts on their TV programmes. Rather than trying to force the adverts, it just doesn't allow you to watch anything until you disable adblocker for their website. I would say that was an extreme case of dealing with the issue. Was just proposing a simple message to ask the user to disable adblock, but I think a link to the donate page would be useful too. I said it because if members and external clients are paying for advertising space on your website, it makes sense to do our best to ensure they get seen. As this is an 'if' case, then I agree with the config option.

Chris Graham

2020-10-08 20:56

administrator   ~6749

Attached is a patch for v9 that implements this functionality. Would need updating for whatever version this might get merged into.
adblocking-patch.diff (14,496 bytes)   
diff --git a/data/adblocker_blocked.php b/data/adblocker_blocked.php
new file mode 100644
index 000000000..a1e9d7eb7
--- /dev/null
+++ b/data/adblocker_blocked.php
@@ -0,0 +1,39 @@
+<?php /*
+
+ ocPortal
+ Copyright (c) ocProducts, 2004-2012
+
+ See text/EN/licence.txt for full licencing information.
+
+*/
+
+/**
+ * @license		http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
+ * @copyright	ocProducts Ltd
+ * @package		core
+ */
+
+// Find ocPortal base directory, and chdir into it
+global $FILE_BASE,$RELATIVE_PATH;
+$FILE_BASE=(strpos(__FILE__,'./')===false)?__FILE__:realpath(__FILE__);
+$FILE_BASE=dirname($FILE_BASE);
+if (!is_file($FILE_BASE.'/sources/global.php')) // Need to navigate up a level further perhaps?
+{
+	$RELATIVE_PATH=basename($FILE_BASE);
+	$FILE_BASE=dirname($FILE_BASE);
+} else
+{
+	$RELATIVE_PATH='';
+}
+@chdir($FILE_BASE);
+
+global $NON_PAGE_SCRIPT;
+$NON_PAGE_SCRIPT=1;
+global $FORCE_INVISIBLE_GUEST;
+$FORCE_INVISIBLE_GUEST=0;
+if (!is_file($FILE_BASE.'/sources/global.php')) exit('<!DOCTYPE html>'.chr(10).'<html lang="EN"><head><title>Critical startup error</title></head><body><h1>ocPortal startup error</h1><p>The second most basic ocPortal startup file, sources/global.php, could not be located. This is almost always due to an incomplete upload of the ocPortal system, so please check all files are uploaded correctly.</p><p>Once all ocPortal files are in place, ocPortal must actually be installed by running the installer. You must be seeing this message either because your system has become corrupt since installation, or because you have uploaded some but not all files from our manual installer package: the quick installer is easier, so you might consider using that instead.</p><p>ocProducts maintains full documentation for all procedures and tools, especially those for installation. These may be found on the <a href="http://ocportal.com">ocPortal website</a>. If you are unable to easily solve this problem, we may be contacted from our website and can help resolve it for you.</p><hr /><p style="font-size: 0.8em">ocPortal is a website engine created by ocProducts.</p></body></html>'); require($FILE_BASE.'/sources/global.php');
+
+require_code('banners');
+adblocker_blocked_script();
+
+
diff --git a/data/files.dat b/data/files.dat
index f60f292e9..bee7e876d 100644
Binary files a/data/files.dat and b/data/files.dat differ
diff --git a/data/modules/banners/ads/dfp.js b/data/modules/banners/ads/dfp.js
new file mode 100644
index 000000000..3918c74e4
--- /dev/null
+++ b/data/modules/banners/ads/dfp.js
@@ -0,0 +1 @@
+"use strict";
diff --git a/data/modules/banners/ads/index.html b/data/modules/banners/ads/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/modules/banners/index.html b/data/modules/banners/index.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/data_custom/execute_temp.php b/data_custom/execute_temp.php
index c50c44910..c976d6fa6 100644
--- a/data_custom/execute_temp.php
+++ b/data_custom/execute_temp.php
@@ -55,5 +55,7 @@ if (!headers_sent())
  */
 function execute_temp()
 {
-	echo http_download_file('https://example.com/');
+	add_config_option('ADBLOCKER_RESPONSE_ACTION','adblocker_response_action','list','return 0;','FEATURE','BANNERS',0,'0|1|2');
+	add_config_option('ADBLOCKER_RESPONSE_TEXT','adblocker_response_text','transtext','return do_lang(\'banners:ADBLOCKER_RESPONSE_TEXT_DEFAULT\');','FEATURE','BANNERS');
+	add_specific_permission('BANNERS', 'allow_adblocker');
 }
diff --git a/docs/pages/comcode_custom/EN/tut_banners.txt b/docs/pages/comcode_custom/EN/tut_banners.txt
index fbd5def52..64dde1214 100644
--- a/docs/pages/comcode_custom/EN/tut_banners.txt
+++ b/docs/pages/comcode_custom/EN/tut_banners.txt
@@ -127,8 +127,61 @@ If banners won't show check:
 7) The banner type has permissions for the viewer's usergroup.
 8) The "Community advert chance" configuration option is not set to zero.
 
-
-
+[title="2"]Adblocker detection[/title]
+
+We can detect all broad adblockers (at least, the ones we tested). We use two techniques:
+1) A suspicious looking JavaScript file that may be outright blocked
+2) A suspicious looking HTML element, for the few cases missed by the above
+
+We tested the following adblockers:
+ - Firefox:
+  - AdBlocker Lite &dagger;&dagger;&dagger;
+  - Adblocker Ultimate
+  - AdBlock for Firefox
+  - Adblock Plus
+  - AdGuard AdBlocker
+  - AdNauseam &dagger;
+  - Ghostery
+  - uBlock Origin
+ - Chrome (and Edge and Opera):
+  - Adaware Ad Block
+  - Adbear
+  - Adblock
+  - AdBlock — best ad blocker
+  - Ad-Blocker
+  - Adblocker Genius PRO
+  - AdBlocker Ultimate
+  - Adblocker-X
+  - AdBlock Max - ad blocker
+  - Adblock Plus - free ad blocker
+  - AdBlock - Stop Ad on every Site
+  - Adburner
+  - AdGuard AdBlocker
+  - Adkill
+  - AdLock - adblock & privacy protection
+  - ADT-Blocker
+  - Easy Ad Blocker
+  - Free AdBlocker - block ads, browse safe
+  - Ghostery – Privacy Ad Blocker
+  - GreenBoost - Boost & Clean & AdBlock
+  - JustBlock Security &dagger;&dagger;&dagger;
+  - StopAds
+  - uBlocker - Ad Block Tool for Chrome
+  - uBlock Origin
+ - Safari:
+  - 1blocker &dagger;&dagger;&dagger;
+  - AdBlock for Safari
+  - AdBlock Max &dagger;&dagger;
+  - AdGuard for Safari &dagger;&dagger;
+  - Magic Lasoo
+ - Opera:
+  - Inbuilt ad blocker
+
+You can configure to prevent users with an adblocker from accessing site, or just to warn them about it. You can configure the message to ask them to disable the adblocker, to donate, to subscribe, etc. You can also give usergroups privilege to be allowed to use an adblocker.
+
+&dagger; When testing make sure to enable the adblocking capability
+&dagger;&dagger; When testing it needs to be on a domain name not an IP address
+&dagger;&dagger;&dagger; Uses element hiding technique (either because URL blocking rules are site-specific, or blocking only works based on CSS)
 
 [concepts
  1_key="Default banner"       1_value="A banner that is displayable at any time (i.e. both when campaign and permanent banners would be chosen for viewing), but is intended to have a low importance level such that it is shown when the system is low on banners"
diff --git a/lang/EN/banners.ini b/lang/EN/banners.ini
index e57b4d2d6..839b8c752 100644
--- a/lang/EN/banners.ini
+++ b/lang/EN/banners.ini
@@ -115,3 +115,12 @@ BANNER_HTML_NOT_RUN=Banner HTML has not been run, due to the submitter's permiss
 BANNER_DIRECT_CODE=Direct code
 DESCRIPTION_BANNER_DIRECT_CODE=The HTML/PHP code that was directly supplied to you for display a certain third party banner/banner rotation.
 DESCRIPTION_BANNER_TYPE=The banner type codename (no spaces, punctuation, or capital letters).<br />The banner type codename used in the default global template is blank.
+PT_allow_adblocker=Permit adblockers
+ADBLOCKER_RESPONSE_ACTION=Adblocker response action
+CONFIG_OPTION_adblocker_response_action=What to do if an adblocker is detected (assuming the user does not have the &ldquo;Permit adblockers&rdquo; privilege).
+CONFIG_OPTION_adblocker_response_action_VALUE_0=No action
+CONFIG_OPTION_adblocker_response_action_VALUE_1=Put out a message
+CONFIG_OPTION_adblocker_response_action_VALUE_2=Put out a fatal error
+ADBLOCKER_RESPONSE_TEXT=Adblocker response text
+CONFIG_OPTION_adblocker_response_text=Text to show if an adblocker is detected. The default text is convervative, but you could consider trying to meet the <a href="https://acceptableads.com/standard/" target="_blank" title="Acceptable Ads (this link will open in a new window)">Acceptable Ads standard</a> and quoting that here. If you support a paid usergroup that doesn't have ads and/or allows adblockers, you could promote that here also.
+ADBLOCKER_RESPONSE_TEXT_DEFAULT=You appear to be using an adblocker. {$SITE_NAME*} depends on ad revenue to meet costs so please configure an exception to your adblocker for our website.
diff --git a/sources/banners.php b/sources/banners.php
index 9511baa29..1a84834a8 100755
--- a/sources/banners.php
+++ b/sources/banners.php
@@ -28,6 +28,18 @@ function init__banners()
 	define('BANNER_DEFAULT',2);
 }
 
+/**
+ * Fail with a message about an adblocker being present.
+ * JavaScript redirects to this script if an adblocker is detected, depending on configuration.
+ */
+function adblocker_blocked_script()
+{
+	header('X-Robots-Tag: noindex');
+
+	$msg = comcode_to_tempcode(get_option('adblocker_response_text'), null, true);
+	warn_exit($msg);
+}
+
 /**
  * Show a banner according to GET parameter specification.
  *
diff --git a/sources/hooks/systems/addon_registry/banners.php b/sources/hooks/systems/addon_registry/banners.php
index 2a9f52d10..6e13e1bbe 100644
--- a/sources/hooks/systems/addon_registry/banners.php
+++ b/sources/hooks/systems/addon_registry/banners.php
@@ -121,7 +121,10 @@ class Hook_addon_registry_banners
 			'data/images/advertise_here.png',
 			'data/images/donate.png',
 			'site/pages/comcode/EN/advertise.txt',
-			'site/pages/comcode/EN/donate.txt'
+			'site/pages/comcode/EN/donate.txt',
+			'data/modules/banners/index.html',
+			'data/modules/banners/ads/dfp.js',
+			'data/modules/banners/ads/index.html',
 		);
 	}
 
diff --git a/sources_custom/miniblocks/ocpcom_featuretray.php b/sources_custom/miniblocks/ocpcom_featuretray.php
index 6f96c5bbf..ef0c915a0 100644
--- a/sources_custom/miniblocks/ocpcom_featuretray.php
+++ b/sources_custom/miniblocks/ocpcom_featuretray.php
@@ -103,6 +103,7 @@ $featuretree=array(
 				array('Smart banners','Integrate text-banners into your content via keyword detection'),
 				array('Wide support','Accepts image banners, flash banners, external banner rotations, and text banners'),
 				NULL, // divider
+				array('Adblocker detection with configurable handling'),
 				array('Determine which banners display most often'),
 				array('Run a cross-site banner network'),
 				array('Hit-balancing support','A site on a banner network gets as many inbound hits as it provides outbound clicks'),
diff --git a/themes/default/css/global.css b/themes/default/css/global.css
index 662897901..68d211eb5 100644
--- a/themes/default/css/global.css
+++ b/themes/default/css/global.css
@@ -790,10 +790,10 @@ body.popup_spacer h2 {
 	margin-{!en_left}: 10px;
 }
 
-.global_messages {
+.global_messages:not(:empty) {
 	margin: 1em;
 }
-.global_middle_faux .global_messages {
+.global_middle_faux .global_messages:not(:empty) {
 	margin: 1em 0; /* Probably inside a frame, hence already indented outside said frame */
 }
 
diff --git a/themes/default/templates/GLOBAL_HTML_WRAP.tpl b/themes/default/templates/GLOBAL_HTML_WRAP.tpl
index f3a1f11a8..1439aca6b 100644
--- a/themes/default/templates/GLOBAL_HTML_WRAP.tpl
+++ b/themes/default/templates/GLOBAL_HTML_WRAP.tpl
@@ -69,11 +69,7 @@ Powered by {$BRAND_NAME*} version {$VERSION_NUMBER*}, (c) ocProducts Ltd
 			{+END}
 
 			{$,ocPortal may show little messages for you as it runs relating to what you are doing or the state the site is in}
-			{+START,IF_NON_EMPTY,{$MESSAGES_TOP}}
-				<div class="global_messages">
-					{$MESSAGES_TOP}
-				</div>
-			{+END}
+			<div id="global_messages" class="global_messages">{$MESSAGES_TOP}</div>
 
 			{$,The main panels and content; float_surrounder contains the layout into a rendering box so that the footer etc can sit underneath}
 			<div class="global_middle_outer float_surrounder">
diff --git a/themes/default/templates/HTML_HEAD.tpl b/themes/default/templates/HTML_HEAD.tpl
index 8ce32209d..fe03a7bd4 100644
--- a/themes/default/templates/HTML_HEAD.tpl
+++ b/themes/default/templates/HTML_HEAD.tpl
@@ -146,6 +146,10 @@
 	{+END}
 	{+START,IF,{$NOT,{$BROWSER_MATCHES,ie}}}{+START,IF,{$HAS_PRIVILEGE,sees_javascript_error_alerts}}window.take_errors=true;{+END}{+END}
 	var {+START,IF,{$CONFIG_OPTION,is_on_timezone_detection}}server_timestamp={$FROM_TIMESTAMP%},{+END}ocp_lang='{$LANG;/}',ocp_theme='{$THEME;/}',ocp_username='{$USERNAME;/}'{+START,IF,{$IS_STAFF}},ocp_is_staff=true{+END};
+
+	{+START,IF,{$AND,{$RUNNING_SCRIPT,index},{$ADDON_INSTALLED,banners},{$NEQ,{$CONFIG_OPTION,adblocker_response_action},0}}}
+		var dab = {$?,{$HAS_PRIVILEGE,allow_adblocker},false,true};
+	{+END}
 //]]></script>
 
 {$,Javascript includes from ocPortal page}
diff --git a/themes/default/templates/JAVASCRIPT.tpl b/themes/default/templates/JAVASCRIPT.tpl
index 3951fb0b9..7034c25bd 100644
--- a/themes/default/templates/JAVASCRIPT.tpl
+++ b/themes/default/templates/JAVASCRIPT.tpl
@@ -160,8 +160,57 @@ function script_load_stuff()
 	} );
 
 	if ((typeof window.ocp_is_staff!='undefined') && (window.ocp_is_staff) && (typeof window.script_load_stuff_staff!='undefined')) script_load_stuff_staff();
+
+	{+START,IF,{$AND,{$ADDON_INSTALLED,banners},{$NEQ,{$CONFIG_OPTION,adblocker_response_action},0}}}
+		if ((typeof dab != 'undefined') && (window.dab)) check_adblocking();
+	{+END}
 }
 
+{+START,IF,{$AND,{$ADDON_INSTALLED,banners},{$NEQ,{$CONFIG_OPTION,adblocker_response_action},0}}}
+	function check_adblocking()
+	{
+		// '/dfp.js' will trigger EasyList rule, and some other rules due to '/ads/' etc
+		var request = new XMLHttpRequest();
+		request.onreadystatechange = function() {
+			if ((request.readyState == 4) && (request.status == 0)) {
+				adblocker_response();
+			}
+		};
+		request.open('GET', get_base_url() + '/data/modules/banners/ads/dfp.js?ads&adunit=foobar');
+		request.send();
+
+		// Also see if an element gets hidden
+		var e = document.createElement('div');
+		e.className = 'zaba-advertising';
+		document.body.appendChild(e);
+		window.setTimeout(function() {
+			if (window.getComputedStyle(e).display == 'none') {
+				adblocker_response();
+			}
+			document.body.removeChild(e);
+		}, 500);
+	}
+
+	function adblocker_response()
+	{
+		{+START,IF,{$EQ,{$CONFIG_OPTION,adblocker_response_action,1},1}}
+			var messages_div = document.getElementById('global_messages');
+			{+START,SET,adblock_response_text}
+				{+START,INCLUDE,MESSAGE}
+					TYPE=notice
+					MESSAGE={$COMCODE^,{$CONFIG_OPTION,adblocker_response_text}}
+				{+END}
+			{+END}
+			var message_div = document.createElement('div');
+			set_inner_html(message_div, '{$GET;^,adblock_response_text}');
+			messages_div.appendChild(message_div);
+		{+END}
+		{+START,IF,{$EQ,{$CONFIG_OPTION,adblocker_response_action,1},2}}
+			window.location.href = '{$FIND_SCRIPT;,adblocker_blocked}';
+		{+END}
+	}
+{+END}
+
 function set_font_size(size)
 {
 	var old_size=read_cookie('font_size');
adblocking-patch.diff (14,496 bytes)   

Add Note

View Status
Note
Upload Files
Maximum size: 32,768 KiB

Attach files by dragging & dropping, selecting or pasting them.
You are not logged in You are not logged in. This means you will not get any e-mail notifications. And if you reply, we will not know for sure you are the original poster of the issue.

Issue History

Date Modified Username Field Change
2020-08-02 20:13 Chris Graham Description Updated
2020-08-02 20:13 Chris Graham Additional Information Updated
2020-08-02 20:13 Chris Graham Time estimation (hours) 3 => 2
2020-08-02 20:13 Chris Graham Relationship added has duplicate 1989
2020-10-08 20:56 Chris Graham File Added: adblocking-patch.diff
2020-10-08 20:56 Chris Graham Note Added: 0006749
2021-03-15 03:54 Chris Graham Tag Attached: Roadmap: v12
2021-11-01 20:04 Chris Graham Tag Attached: Has Patch
2024-03-26 00:58 PDStig Tag Renamed Roadmap: v12 => Roadmap: Over the horizon