FEATURE: use groups to control who sees ads

A new setting has been added in Admin > Settings > Ad Plugin called
"no ads for groups". Add group names to this list. If a user belongs
to any of the groups, they will not see any ads. This is an alternative
to using trust level settings like "adsense through trust level".
This commit is contained in:
Neil Lalonde 2019-04-16 16:57:16 -04:00
parent 6097e6f097
commit 4fd7caffd6
8 changed files with 183 additions and 43 deletions

View File

@ -0,0 +1,23 @@
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
@computed()
showToGroups: function() {
const currentUser = Discourse.User.current();
if (
!currentUser ||
!currentUser.get("groups") ||
!this.siteSettings.no_ads_for_groups ||
this.siteSettings.no_ads_for_groups.length === 0
) {
return true;
}
const noAdsGroupNames = this.siteSettings.no_ads_for_groups.split("|");
return !currentUser
.get("groups")
.any(group => noAdsGroupNames.includes(group.name));
}
});

View File

@ -1,3 +1,4 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
import { import {
default as computed, default as computed,
observes observes
@ -146,7 +147,7 @@ if (Discourse.SiteSettings.adsense_publisher_code) {
} }
} }
export default Ember.Component.extend({ export default AdComponent.extend({
classNameBindings: [ classNameBindings: [
":google-adsense", ":google-adsense",
"classForSlot", "classForSlot",
@ -193,7 +194,7 @@ export default Ember.Component.extend({
}, },
@observes("listLoading") @observes("listLoading")
waitForLoad: function() { waitForLoad() {
if (this.get("adRequested")) { if (this.get("adRequested")) {
return; return;
} // already requested that this ad unit be populated } // already requested that this ad unit be populated
@ -203,34 +204,34 @@ export default Ember.Component.extend({
}, },
@computed("ad_width") @computed("ad_width")
isResponsive: function(adWidth) { isResponsive(adWidth) {
return adWidth === "auto"; return adWidth === "auto";
}, },
@computed("placement", "checkTrustLevels") @computed("placement", "showAd")
classForSlot: function(placement, shown) { classForSlot(placement, showAd) {
return shown ? `adsense-${placement}`.htmlSafe() : ""; return showAd ? `adsense-${placement}`.htmlSafe() : "";
}, },
@computed("isResponsive") @computed("isResponsive")
autoAdFormat: function(isResponsive) { autoAdFormat(isResponsive) {
return isResponsive ? "auto".htmlSafe() : false; return isResponsive ? "auto".htmlSafe() : false;
}, },
@computed("ad_width", "ad_height", "isResponsive") @computed("ad_width", "ad_height", "isResponsive")
adWrapperStyle: function(w, h, isResponsive) { adWrapperStyle(w, h, isResponsive) {
return (isResponsive ? "" : `width: ${w}; height: ${h};`).htmlSafe(); return (isResponsive ? "" : `width: ${w}; height: ${h};`).htmlSafe();
}, },
@computed("adWrapperStyle", "isResponsive") @computed("adWrapperStyle", "isResponsive")
adInsStyle: function(adWrapperStyle, isResponsive) { adInsStyle(adWrapperStyle, isResponsive) {
return `display: ${ return `display: ${
isResponsive ? "block" : "inline-block" isResponsive ? "block" : "inline-block"
}; ${adWrapperStyle}`.htmlSafe(); }; ${adWrapperStyle}`.htmlSafe();
}, },
@computed() @computed()
checkTrustLevels: function() { showToTrustLevel() {
return !( return !(
currentUser && currentUser &&
currentUser.get("trust_level") > currentUser.get("trust_level") >
@ -238,8 +239,12 @@ export default Ember.Component.extend({
); );
}, },
@computed("checkTrustLevels") @computed("showToTrustLevel", "showToGroups")
showAd: function(shown) { showAd(showToTrustLevel, showToGroups) {
return shown && Discourse.SiteSettings.adsense_publisher_code; return (
showToTrustLevel &&
showToGroups &&
Discourse.SiteSettings.adsense_publisher_code
);
} }
}); });

View File

@ -1,3 +1,9 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
import {
default as computed,
observes,
on
} from "ember-addons/ember-computed-decorators";
import loadScript from "discourse/lib/load-script"; import loadScript from "discourse/lib/load-script";
var currentUser = Discourse.User.current(), var currentUser = Discourse.User.current(),
@ -198,51 +204,56 @@ function loadGoogle() {
return _promise; return _promise;
} }
export default Ember.Component.extend({ export default AdComponent.extend({
classNameBindings: ["adUnitClass"], classNameBindings: ["adUnitClass"],
classNames: ["google-dfp-ad"], classNames: ["google-dfp-ad"],
loadedGoogletag: false, loadedGoogletag: false,
refreshOnChange: null, refreshOnChange: null,
divId: function() { @computed("placement", "postNumber")
if (this.get("postNumber")) { divId(placement, postNumber) {
return ( if (postNumber) {
"div-gpt-ad-" + this.get("placement") + "-" + this.get("postNumber") return `div-gpt-ad-${placement}-${postNumber}`;
);
} else { } else {
return "div-gpt-ad-" + this.get("placement"); return `div-gpt-ad-${placement}`;
} }
}.property("placement", "postNumber"), },
adUnitClass: function() { @computed("placement", "showAd")
return "dfp-ad-" + this.get("placement"); adUnitClass(placement, showAd) {
}.property("placement"), return showAd ? `dfp-ad-${placement}` : "";
},
adWrapperStyle: function() { @computed("width", "height")
return `width: ${this.get("width")}px; height: ${this.get( adWrapperStyle(w, h) {
"height" return `width: ${w}px; height: ${h}px;`.htmlSafe();
)}px;`.htmlSafe(); },
}.property("width", "height"),
adTitleStyleMobile: function() { @computed("width")
return `width: ${this.get("width")}px;`.htmlSafe(); adTitleStyleMobile(w) {
}.property("width"), return `width: ${w}px;`.htmlSafe();
},
showAd: function() { @computed("showToTrustLevel", "showToGroups")
showAd(showToTrustLevel, showToGroups) {
return ( return (
Discourse.SiteSettings.dfp_publisher_id && this.get("checkTrustLevels") Discourse.SiteSettings.dfp_publisher_id &&
showToTrustLevel &&
showToGroups
); );
}.property("checkTrustLevels"), },
checkTrustLevels: function() { @computed()
showToTrustLevel() {
return !( return !(
currentUser && currentUser &&
currentUser.get("trust_level") > currentUser.get("trust_level") >
Discourse.SiteSettings.dfp_through_trust_level Discourse.SiteSettings.dfp_through_trust_level
); );
}.property("trust_level"), },
refreshAd: function() { @observes("refreshOnChange")
refreshAd() {
var slot = ads[this.get("divId")]; var slot = ads[this.get("divId")];
if (!(slot && slot.ad)) { if (!(slot && slot.ad)) {
return; return;
@ -260,9 +271,10 @@ export default Ember.Component.extend({
window.googletag.pubads().refresh([ad]); window.googletag.pubads().refresh([ad]);
}); });
} }
}.observes("refreshOnChange"), },
_initGoogleDFP: function() { @on("didInsertElement")
_initGoogleDFP() {
if (!this.get("showAd")) { if (!this.get("showAd")) {
return; return;
} }
@ -287,7 +299,7 @@ export default Ember.Component.extend({
} }
}); });
}); });
}.on("didInsertElement"), },
willRender() { willRender() {
this._super(...arguments); this._super(...arguments);
@ -300,7 +312,8 @@ export default Ember.Component.extend({
this.set("height", size.height); this.set("height", size.height);
}, },
cleanup: function() { @on("willDestroyElement")
cleanup() {
destroySlot(this.get("divId")); destroySlot(this.get("divId"));
}.on("willDestroyElement") }
}); });

View File

@ -6,6 +6,7 @@ en:
admin: admin:
site_settings: site_settings:
categories: categories:
ad_plugin: 'Ad Plugin'
dfp_plugin: 'DFP/Ad Manager' dfp_plugin: 'DFP/Ad Manager'
adsense_plugin: 'AdSense' adsense_plugin: 'AdSense'
amazon_plugin: 'Amazon' amazon_plugin: 'Amazon'

View File

@ -1,5 +1,7 @@
en: en:
site_settings: site_settings:
no_ads_for_groups: "Don't show ads to users in these groups."
dfp_publisher_id: "Input your Google Ad Manager (formerly called DFP) network code, which is found in your network settings." dfp_publisher_id: "Input your Google Ad Manager (formerly called DFP) network code, which is found in your network settings."
dfp_through_trust_level: "Show your ads to users based on trust levels. Users with trust level higher than this value will not see ads." dfp_through_trust_level: "Show your ads to users based on trust levels. Users with trust level higher than this value will not see ads."

View File

@ -1,3 +1,11 @@
ad_plugin:
no_ads_for_groups:
client: true
default: ""
type: list
choices: "Group.pluck(:name)"
list_type: compact
adsense_plugin: adsense_plugin:
adsense_publisher_code: adsense_publisher_code:
client: true client: true

View File

@ -1,8 +1,10 @@
import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers"; import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers";
import groupFixtures from "fixtures/group-fixtures";
acceptance("AdSense", { acceptance("AdSense", {
loggedIn: true, loggedIn: true,
settings: { settings: {
no_ads_for_groups: "discourse",
adsense_publisher_code: "MYADSENSEID", adsense_publisher_code: "MYADSENSEID",
adsense_through_trust_level: 2, adsense_through_trust_level: 2,
adsense_topic_list_top_code: "list_top_ad_unit", adsense_topic_list_top_code: "list_top_ad_unit",
@ -54,3 +56,17 @@ test("no ads for trust level 3", async assert => {
"it should render 0 ads" "it should render 0 ads"
); );
}); });
QUnit.only("can omit ads based on groups", async assert => {
replaceCurrentUser({
staff: false,
trust_level: 1,
groups: [groupFixtures["/groups/discourse.json"].group]
});
await visit("/t/280");
assert.equal(
find(".google-adsense.adsense-post-bottom").length,
0,
"it should render 0 ads"
);
});

View File

@ -0,0 +1,72 @@
import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers";
import groupFixtures from "fixtures/group-fixtures";
acceptance("DFP Ads", {
loggedIn: true,
settings: {
no_ads_for_groups: "discourse",
dfp_publisher_id: "MYdfpID",
dfp_through_trust_level: 2,
dfp_topic_list_top_code: "list_top_ad_unit",
dfp_topic_list_top_ad_sizes: "728*90 - leaderboard",
dfp_mobile_topic_list_top_code: "mobile_list_top_ad_unit",
dfp_mobile_topic_list_top_ad_size: "300*250 - medium rectangle",
dfp_post_bottom_code: "post_bottom_ad_unit",
dfp_post_bottom_ad_sizes: "728*90 - leaderboard",
dfp_mobile_post_bottom_code: "mobile_post_bottom_ad_unit",
dfp_mobile_post_bottom_ad_size: "300*250 - medium rectangle",
dfp_nth_post_code: 6
}
});
test("correct number of ads should show", async assert => {
replaceCurrentUser({ staff: false, trust_level: 1 });
await visit("/t/280"); // 20 posts
const ads = find(".google-dfp-ad.dfp-ad-post-bottom");
assert.equal(ads.length, 3, "it should render 3 ads");
assert.equal(
find("#post_6 + .widget-connector").find(
".google-dfp-ad.dfp-ad-post-bottom"
).length,
1,
"ad after 6th post"
);
assert.equal(
find("#post_12 + .widget-connector").find(
".google-dfp-ad.dfp-ad-post-bottom"
).length,
1,
"ad after 12th post"
);
assert.equal(
find("#post_18 + .widget-connector").find(
".google-dfp-ad.dfp-ad-post-bottom"
).length,
1,
"ad after 18th post"
);
});
test("no ads for trust level 3", async assert => {
replaceCurrentUser({ staff: false, trust_level: 3 });
await visit("/t/280");
assert.equal(
find(".google-dfp-ad.dfp-ad-post-bottom").length,
0,
"it should render 0 ads"
);
});
test("can omit ads based on groups", async assert => {
replaceCurrentUser({
staff: false,
trust_level: 1,
groups: [groupFixtures["/groups/discourse.json"].group]
});
await visit("/t/280");
assert.equal(
find(".google-dfp-ad.dfp-ad-post-bottom").length,
0,
"it should render 0 ads"
);
});