FEATURE: house ads can be configured to alternate with other ads

Use the new "house ads frequency" site setting to configure how often
house ads should show, as a percentage. This only applies in ad
placements where other ad networks are configured to appear.
This commit is contained in:
Neil Lalonde 2019-05-07 19:52:17 -04:00
parent cdc14398ba
commit 4bd2035866
5 changed files with 149 additions and 41 deletions

View File

@ -24,7 +24,10 @@ module ::AdPlugin
ad_names = settings.values.map { |v| v.split('|') }.flatten.uniq
ads = AdPlugin::HouseAd.all.select { |ad| ad_names.include?(ad.name) }
{
settings: settings.merge(after_nth_post: SiteSetting.house_ads_after_nth_post),
settings: settings.merge(
after_nth_post: SiteSetting.house_ads_after_nth_post,
house_ads_frequency: SiteSetting.house_ads_frequency
),
creatives: ads.inject({}) { |h, ad| h[ad.name] = ad.html; h }
}
end

View File

@ -1,14 +1,24 @@
import computed from "ember-addons/ember-computed-decorators";
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import {
default as computed,
observes
} from "ember-addons/ember-computed-decorators";
const adConfig = Ember.Object.create({
"google-adsense": {
settingPrefix: "adsense" // settings follow naming convention
settingPrefix: "adsense", // settings follow naming convention
enabledSetting: "adsense_publisher_code",
nthPost: "adsense_nth_post_code"
},
"google-dfp-ad": {
settingPrefix: "dfp" // settings follow naming convention
settingPrefix: "dfp", // settings follow naming convention
enabledSetting: "dfp_publisher_id",
nthPost: "dfp_nth_post_code"
},
"amazon-product-links": {
settingPrefix: "amazon",
enabledSetting: false,
nthPost: "amazon_nth_post_code",
desktop: {
"topic-list-top": "amazon_topic_list_top_src_code",
"post-bottom": "amazon_post_bottom_src_code",
@ -25,6 +35,8 @@ const adConfig = Ember.Object.create({
},
"codefund-ad": {
settingPrefix: "codefund",
enabledSetting: "codefund_property_id",
nthPost: "codefund_nth_post",
desktop: {
"topic-list-top": "codefund_top_of_topic_list_enabled",
"post-bottom": "codefund_below_post_enabled",
@ -34,6 +46,7 @@ const adConfig = Ember.Object.create({
},
"carbonads-ad": {
settingPrefix: "carbonads",
enabledSetting: "carbonads_serve_id",
desktop: {
"topic-list-top": "carbonads_topic_list_top_enabled",
"post-bottom": false,
@ -43,47 +56,133 @@ const adConfig = Ember.Object.create({
}
});
export default Ember.Component.extend({
@computed("placement")
adComponents(placement) {
// Check house ads first
const houseAds = this.site.get("house_creatives");
if (!houseAds || !houseAds.settings) {
return [];
const displayCounts = {
houseAds: 0,
allAds: 0
};
export default AdComponent.extend({
needsUpdate: false,
/**
* For a given ad placement and optionally a post number if in between posts,
* list all ad network names that are configured to show there.
*/
@computed("placement", "postNumber")
availableAdTypes(placement, postNumber) {
let types = [];
const houseAds = this.site.get("house_creatives"),
placeUnderscored = placement.replace(/-/g, "_");
if (houseAds && houseAds.settings) {
const adsForSlot = houseAds.settings[placeUnderscored];
if (
Object.keys(houseAds.creatives).length > 0 &&
!Ember.isBlank(adsForSlot) &&
(!postNumber ||
this.isNthPost(parseInt(houseAds.settings.after_nth_post, 10)))
) {
types.push("house-ad");
}
}
const adsForSlot = houseAds.settings[placement.replace(/-/g, "_")];
if (
Object.keys(houseAds.creatives).length > 0 &&
!Ember.isBlank(adsForSlot)
) {
return ["house-ad"];
}
return Object.keys(adConfig).filter(adNetwork => {
Object.keys(adConfig).forEach(adNetwork => {
const config = adConfig[adNetwork];
let settingNames = null,
name;
if (this.site.mobileView) {
settingNames = config.mobile || config.desktop;
} else {
settingNames = config.desktop;
}
if (
config.enabledSetting &&
!Ember.isBlank(this.siteSettings[config.enabledSetting]) &&
(!postNumber ||
!config.nthPost ||
this.isNthPost(parseInt(this.siteSettings[config.nthPost], 10)))
) {
if (this.site.mobileView) {
settingNames = config.mobile || config.desktop;
} else {
settingNames = config.desktop;
}
if (settingNames) {
name = settingNames[placement];
}
if (settingNames) {
name = settingNames[placement];
}
if (name === undefined) {
// follows naming convention: prefix_(mobile_)_{placement}_code
name = `${config.settingPrefix}_${
this.site.mobileView ? "mobile_" : ""
}${placement.replace(/-/g, "_")}_code`;
}
if (name === undefined) {
// follows naming convention: prefix_(mobile_)_{placement}_code
name = `${config.settingPrefix}_${
this.site.mobileView ? "mobile_" : ""
}${placeUnderscored}_code`;
}
return name !== false && !Ember.isBlank(this.siteSettings[name]);
if (name !== false && !Ember.isBlank(this.siteSettings[name])) {
types.push(adNetwork);
}
}
});
return types;
},
/**
* When house ads are configured to alternate with other ad networks, we
* need to trigger an update of which ad component is shown after
* navigating between topic lists or topics.
*/
@observes("refreshOnChange")
changed() {
if (this.get("listLoading")) {
return;
}
// force adComponents to be recomputed
this.notifyPropertyChange("needsUpdate");
},
/**
* Returns a list of the names of ad components that should be rendered
* in the given ad placement. It handles alternating between house ads
* and other ad networks.
*/
@computed("placement", "availableAdTypes", "needsUpdate")
adComponents(placement, availableAdTypes) {
if (
!availableAdTypes.includes("house-ad") ||
availableAdTypes.length === 1
) {
// Current behaviour is to allow multiple ads from different networks
// to show in the same place. We could change this to choose one somehow.
return availableAdTypes;
}
const houseAds = this.site.get("house_creatives");
let houseAdsSkipped = false;
if (houseAds.settings.house_ads_frequency === 100) {
// house always wins
return ["house-ad"];
} else if (houseAds.settings.house_ads_frequency > 0) {
// show house ads the given percent of the time
if (
displayCounts.allAds === 0 ||
(100 * displayCounts.houseAds) / displayCounts.allAds <
houseAds.settings.house_ads_frequency
) {
displayCounts.houseAds += 1;
displayCounts.allAds += 1;
return ["house-ad"];
} else {
houseAdsSkipped = true;
}
}
const networkNames = availableAdTypes.filter(x => x !== "house-ad");
if (houseAdsSkipped) {
displayCounts.allAds += networkNames.length;
}
return networkNames;
}
});

View File

@ -1,8 +1,8 @@
{{#each adComponents as |adComponent|}}
{{component adComponent
placement=placement
refreshOnChange=refreshOnChange
category=category
listLoading=listLoading
postNumber=postNumber}}
placement=placement
refreshOnChange=refreshOnChange
category=category
listLoading=listLoading
postNumber=postNumber}}
{{/each}}

View File

@ -2,6 +2,7 @@ en:
site_settings:
no_ads_for_groups: "Don't show ads to users in these groups."
house_ads_after_nth_post: 'If "Between posts" house ads are defined, show an ad after every N posts, where N is this value.'
house_ads_frequency: "If other ad networks are configured to show in an ad slot, how often should house ads be shown, as a percentage."
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."

View File

@ -8,6 +8,11 @@ ad_plugin:
default: 20
min: 1
max: 10000
house_ads_frequency:
client: true
default: 100
min: 0
max: 100
adsense_plugin:
adsense_publisher_code: