18 Commits
Author SHA1 Message Date
Penar Musaraj d6af1d2e20 FEAT: ads.txt support
Adds a plugin setting that holds text content served on a new /ads.txt route.
2019-02-04 16:15:26 -05:00
Neil Lalonde 79d5f144a6 Merge branch 'master' into stable 2019-02-01 14:04:22 -05:00
Neil Lalonde 408b043046 Merge branch 'master' into stable 2019-02-01 11:56:46 -05:00
Penar Musaraj e628bc859b Revert "Add support for ads.txt redirect"
This reverts commit f6c7c2d1af.

Ads.txt redirection can be done directly in core, in admin/customize/permalinks.
2019-01-29 16:02:02 -05:00
Penar Musaraj f6c7c2d1af Add support for ads.txt redirect 2019-01-29 11:06:42 -05:00
Eric Berry a669381a71 Remove need to only allow UUID as CodeFund Property ID (#71)
* Remove need to only allow UUID as CodeFund Property ID

* Do not apply format check
2019-01-29 11:06:26 -05:00
Joffrey JAFFEUX 75a56891d2 Reverts Ember.String.prototype.w monkey patching causing exception
The git blame of the commit is unclear on the reasons leading to this, and how to reproduce a failure.
2019-01-09 21:18:06 +01:00
Gerhard Schlager 0d4e6bb4fa Inherit rubocop config from discourse repo 2019-01-09 21:17:55 +01:00
Neil Lalonde 5eed122002 Merge branch 'master' into stable 2018-11-29 11:05:04 -05:00
Neil Lalonde 4c45a27a72 Merge branch 'master' into stable 2018-09-10 13:48:57 -04:00
Neil Lalonde c838f10467 Merge branch 'master' into stable 2018-07-19 13:50:15 -04:00
Joshua Rosenfeld 8b404b9ffb Add URL to plugin.rb on stable branch 2018-06-19 22:39:51 -04:00
Jeff Wong 97f56a95bd FIX: resolve collision on ember's builtin w string function 2018-05-16 13:58:33 -07:00
Gerhard Schlager 73d010ef49 Merge branch 'master' into stable 2017-12-14 12:26:44 +01:00
Neil Lalonde 8e1041ec75 Merge branch 'master' into stable 2017-12-05 15:19:21 -05:00
Neil Lalonde 40c0a64bb9 Merge branch 'master' into stable 2017-11-13 16:27:57 -05:00
Neil Lalonde 3f71c80e89 Merge branch 'master' into stable 2017-11-08 13:39:37 -05:00
Guo Xiang Tan 392fd76679 Merge pull request #48 from discourse/merge_master_changes
Merge master changes
2016-12-09 02:19:47 +01:00
64 changed files with 575 additions and 2548 deletions
-22
View File
@@ -1,22 +0,0 @@
Copyright (c) 2019 Discourse
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+3 -28
View File
@@ -3,22 +3,15 @@
This is the official Discourse advertising plugin. It allows advertisements to be served by supported advertising platforms for users with a Discourse forum.
**Authors**: [Sarah Ni](https://github.com/cyberkoi) & [Vi Nguyen](https://github.com/ladydanger)
**Version**: 1.2.0
**Contributors**: See [credits section](#credits) below
**Version**: 1.0.1
**Contributors**: See credits section below
**License**: MIT License
**Supported Discourse Version**: 1.4
**Supported Ad Platforms**:
* House Ads: Your own ads served from Discourse
* [Google Adsense](http://www.google.com.au/adsense/start/why-adsense.html)
* [Google Double Click for Publishers](https://www.google.com/dfp)
* [Amazon Affiliates](http://affiliate-program.amazon.com) - Banner and Product Link Ads
* [CodeFund](https://codefund.io) - Ethical Ad Platform for Developers
* [Carbon Ads](https://www.carbonads.net/)
## Quick Start in 3 Steps
@@ -57,18 +50,6 @@ git pull
There are 2 easy steps for configuring your Discourse settings to enable advertisements to display in your Discourse forum.
#### House Ads
If you don't want to use an external ad platform, but want to show your own ads, then House Ads are for you!
Define your ads by going to the Admin section of your Discourse forum, and go to the Plugins section.
On the left, you should see a link called "House Ads".
Begin by creating your ads. Give each a short descriptive name and enter the html for each.
Style them using a custom theme (Admin > Customize > Themes).
Lastly, click the Settings button in the House Ads UI and choose which of your ads to show in each of
the ad slots. The ads will start showing as soon as you add them to slots.
#### Step 2(a) - Choose Your Advertisement Platform
<ul>
@@ -76,11 +57,9 @@ the ad slots. The ads will start showing as soon as you add them to slots.
<li>Click on Settings and a left vertical navigation bar should appear.</li>
<li>Choose your advertisement platform.</li>
<ul>
<li>House Ads - if you want to create and serve ads from your Discourse instance.</li>
<li>Adsense - if using Adsense as your advertisement platform.</li>
<li>DFP - if using the DoubleClick for Publishers advertisement platform.</li>
<li>CodeFund - if using the CodeFund ethical advertisement platform.</li>
<li>Carbon Ads - if using the Carbon Ads advertisement platform.</li>
</ul>
</ul>
@@ -109,10 +88,6 @@ Only for Product Link and Banner Ads.
![CodeFund Instructions](https://s3-us-west-2.amazonaws.com/codesponsor/discourse-codefund-instructions.png)
##### Carbon Ads Script Tag to Discourse's Site Settings
![Carbon Ads](https://d11a6trkgmumsb.cloudfront.net/original/3X/3/a/3acc7488db2b53733cdd427d3cb1b76361c786e1.png)
### Step 3 - See Your Ad
Once you've configured your settings and your advertising platform has ads that are ready to serve, navigate to the page where you've inputted for the location and you should see ads.
@@ -223,4 +198,4 @@ Open an Issue on this repository to start a chat.
**Our Coaches**: Very special thank you to our coaches and honorary coach - [@georg](https://github.com/georg), [@betaass](https://github.com/betaass), [@adelsmee](https://github.com/adelsmee), [@davich](https://github.com/davich), [@link664](https://github.com/link664), [@tomjadams](https://github.com/tomjadams), [@compactcode](https://github.com/compactcode), [@joffotron](https://github.com/joffotron), [@jocranford](https://github.com/jocranford), [@saramic](https://github.com/saramic), [@madpilot](https://github.com/madpilot), [@catkins](https://github.com/catkins)
**Rails Girls**: Thanks [@sareg0](https://github.com/sareg0) and the Rails Girls Team for the opportunity to participate in Rails Girls Summer of Code 2015.
<p>To create this plugin we referenced the <a href="https://github.com/discourse/discourse-google-dfp">original dfp plugin</a> (created by <a href="https://github.com/nlalonde">@nlalonde</a>) and the <a href="https://meta.discourse.org/t/google-adsense-plugin/11763/133">adsense plugin</a>.</p>
<p>To create this plugin we referenced the <a href="https://github.com/discourse/discourse-google-dfp">original dfp plugin</a> (created by <a href="https://github.com/search?q=neil+lalonde&ref=opensearch&type=Users">nlalonde</a>) and the <a href="https://meta.discourse.org/t/google-adsense-plugin/11763/133">adsense plugin</a>.</p>
@@ -1,10 +0,0 @@
module ::AdPlugin
class HouseAdSettingsController < ::ApplicationController
requires_plugin AdPlugin.plugin_name
def update
HouseAdSetting.update(params[:id], params[:value])
render json: success_json
end
end
end
-53
View File
@@ -1,53 +0,0 @@
module ::AdPlugin
class HouseAdsController < ::ApplicationController
requires_plugin AdPlugin.plugin_name
def index
render_json_dump(
house_ads: HouseAd.all.map(&:to_hash),
settings: HouseAdSetting.all
)
end
def show
render_json_dump(house_ad: HouseAd.find(params[:id])&.to_hash)
end
def create
ad = HouseAd.create(house_ad_params)
if ad.valid?
render_json_dump(house_ad: ad.to_hash)
else
render_json_error(ad)
end
end
def update
if ad = HouseAd.find(house_ad_params[:id])
ad.update(house_ad_params)
else
ad = HouseAd.create(house_ad_params.except(:id))
end
if ad.valid?
render_json_dump(house_ad: ad.to_hash)
else
render_json_error(ad)
end
end
def destroy
if ad = HouseAd.find(house_ad_params[:id])
ad.destroy
else
render_json_error(I18n.t('not_found'), status: 404)
end
end
private
def house_ad_params
params.permit(:id, :name, :html)
end
end
end
-100
View File
@@ -1,100 +0,0 @@
module ::AdPlugin
class HouseAd
include ActiveModel::Validations
attr_accessor :id, :name, :html
NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i
validates :name, presence: true, format: { with: NAME_REGEX }
validates :html, presence: true
validate do
if self.class.all.any? { |ad| ad.id != self.id && ad.name.downcase == self.name.downcase }
errors.add(:name, :taken) # unique name
end
end
def initialize
@name = "New Ad"
@html = "<div class='house-ad'>New Ad</div>"
end
def self.from_hash(h)
ad = self.new
ad.name = h[:name]
ad.html = h[:html]
ad.id = h[:id].to_i if h[:id]
ad
end
def self.create(attrs)
ad = from_hash(attrs)
ad.save
ad
end
def self.alloc_id
DistributedMutex.synchronize('adplugin-house-ad-id') do
max_id = AdPlugin.pstore_get("ad:_id")
max_id = 1 unless max_id
AdPlugin.pstore_set("ad:_id", max_id + 1)
max_id
end
end
def self.find(id)
if r = AdPlugin::pstore_get("ad:#{id}")
from_hash(r)
else
nil
end
end
def self.all
PluginStoreRow.where(plugin_name: AdPlugin.plugin_name)
.where("key LIKE 'ad:%'")
.where("key != 'ad:_id'")
.map do |psr|
from_hash(PluginStore.cast_value(psr.type_name, psr.value))
end.sort_by { |ad| ad.id }
end
def save
if self.valid?
self.id = self.class.alloc_id if self.id.to_i <= 0
AdPlugin::pstore_set("ad:#{id}", to_hash)
self.class.publish_if_ads_enabled
true
else
false
end
end
def update(attrs)
self.name = attrs[:name]
self.html = attrs[:html]
self.save
end
def to_hash
{
id: @id,
name: @name,
html: @html
}
end
def destroy
AdPlugin::pstore_delete("ad:#{id}")
self.class.publish_if_ads_enabled
end
def self.publish_if_ads_enabled
if AdPlugin::HouseAdSetting.all.any? { |_, adsToShow| !adsToShow.blank? }
AdPlugin::HouseAdSetting.publish_settings
end
end
end
end
-65
View File
@@ -1,65 +0,0 @@
module ::AdPlugin
class HouseAdSetting
DEFAULTS = {
topic_list_top: '',
topic_above_post_stream: '',
topic_above_suggested: '',
post_bottom: ''
}
def self.all
settings = DEFAULTS.dup
PluginStoreRow.where(plugin_name: AdPlugin.plugin_name)
.where("key LIKE 'ad-setting:%'")
.each do |psr|
settings[psr.key[11..-1].to_sym] = psr.value
end
settings
end
def self.settings_and_ads
settings = AdPlugin::HouseAdSetting.all
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,
house_ads_frequency: SiteSetting.house_ads_frequency
),
creatives: ads.inject({}) { |h, ad| h[ad.name] = ad.html; h }
}
end
def self.update(setting_name, value)
unless DEFAULTS.keys.include?(setting_name.to_sym)
raise Discourse::NotFound
end
ad_names = value&.split('|') || []
if value && ad_names.any? { |v| v !~ HouseAd::NAME_REGEX }
raise Discourse::InvalidParameters
end
unless ad_names.empty?
ad_names = (HouseAd.all.map(&:name) & ad_names)
end
new_value = ad_names.join('|')
if value.nil? || new_value == DEFAULTS[setting_name.to_sym]
AdPlugin::pstore_delete("ad-setting:#{setting_name}")
else
AdPlugin::pstore_set("ad-setting:#{setting_name}", new_value)
end
publish_settings
end
def self.publish_settings
MessageBus.publish('/site/house-creatives', settings_and_ads)
end
end
end
BIN
View File
Binary file not shown.
@@ -1,10 +0,0 @@
export default {
resource: "admin.adminPlugins",
path: "/plugins",
map() {
this.route("houseAds", { path: "/adplugin/house_ads" }, function() {
this.route("index", { path: "/" });
this.route("show", { path: "/:ad_id" });
});
}
};
@@ -1,32 +0,0 @@
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
@computed("currentUser.groups")
showToGroups(groups) {
const currentUser = Discourse.User.current();
if (
!currentUser ||
!groups ||
!this.siteSettings.no_ads_for_groups ||
this.siteSettings.no_ads_for_groups.length === 0
) {
return true;
}
const groupNames = groups.map(g => g.name.toLowerCase());
const noAdsGroupNames = this.siteSettings.no_ads_for_groups
.split("|")
.map(g => g.toLowerCase());
return !groupNames.any(g => noAdsGroupNames.includes(g));
},
isNthPost(n) {
if (n && n > 0) {
return this.get("postNumber") % n === 0;
} else {
return false;
}
}
});
@@ -1,188 +0,0 @@
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
enabledSetting: "adsense_publisher_code",
nthPost: "adsense_nth_post_code"
},
"google-dfp-ad": {
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",
"topic-above-post-stream": "amazon_topic_above_post_stream_src_code",
"topic-above-suggested": "amazon_topic_above_suggested_src_code"
},
mobile: {
"topic-list-top": "amazon_mobile_topic_list_top_src_code",
"post-bottom": "amazon_mobile_post_bottom_src_code",
"topic-above-post-stream":
"amazon_mobile_topic_above_post_stream_src_code",
"topic-above-suggested": "amazon_mobile_topic_above_suggested_src_code"
}
},
"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",
"topic-above-post-stream": "codefund_above_post_stream_enabled",
"topic-above-suggested": "codefund_above_suggested_enabled"
}
},
"carbonads-ad": {
settingPrefix: "carbonads",
enabledSetting: "carbonads_serve_id",
desktop: {
"topic-list-top": "carbonads_topic_list_top_enabled",
"post-bottom": false,
"topic-above-post-stream": "carbonads_above_post_stream_enabled",
"topic-above-suggested": false
}
}
});
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");
}
}
Object.keys(adConfig).forEach(adNetwork => {
const config = adConfig[adNetwork];
let settingNames = null,
name;
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 (name === undefined) {
// follows naming convention: prefix_(mobile_)_{placement}_code
name = `${config.settingPrefix}_${
this.site.mobileView ? "mobile_" : ""
}${placeUnderscored}_code`;
}
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;
}
});
@@ -1,9 +1,6 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import computed from "ember-addons/ember-computed-decorators";
var currentUser = Discourse.User.current();
const currentUser = Discourse.User.current();
const data = {
var data = {
"topic-list-top": {},
"topic-above-post-stream": {},
"topic-above-suggested": {},
@@ -122,16 +119,10 @@ if (
);
}
export default AdComponent.extend({
export default Ember.Component.extend({
classNames: ["amazon-product-links"],
showAd: Ember.computed.and(
"showToTrustLevel",
"showToGroups",
"showAfterPost"
),
init() {
init: function() {
let placement = this.get("placement");
this.set("user_input", data[placement]["user_input"]);
this.set("amazon_width", data[placement]["amazon_width"]);
@@ -142,45 +133,35 @@ export default AdComponent.extend({
this._super();
},
@computed("amazon_width", "amazon_height")
adWrapperStyle(w, h) {
return `width: ${w}px; height: ${h}px;`.htmlSafe();
},
adWrapperStyle: function() {
return `width: ${this.get("amazon_width")}px; height: ${this.get(
"amazon_height"
)}px;`.htmlSafe();
}.property("amazon_width", "amazon_height"),
@computed("mobile_amazon_width", "mobile_amazon_height")
adWrapperStyleMobile(w, h) {
return `width: ${w}px; height: ${h}px;`.htmlSafe();
},
adWrapperStyleMobile: function() {
return `width: ${this.get("mobile_amazon_width")}px; height: ${this.get(
"mobile_amazon_height"
)}px;`.htmlSafe();
}.property("mobile_amazon_width", "mobile_amazon_height"),
@computed("mobile_amazon_width")
adTitleStyleMobile(w) {
return `width: ${w}px;`.htmlSafe();
},
adTitleStyleMobile: function() {
return `width: ${this.get("mobile_amazon_width")}px;`.htmlSafe();
}.property("mobile_amazon_width"),
@computed("user_input")
userInput(userInput) {
return `${userInput}`.htmlSafe();
},
userInput: function() {
return `${this.get("user_input")}`.htmlSafe();
}.property("user_input"),
@computed("user_input_mobile")
userInputMobile(userInput) {
return `${userInput}`.htmlSafe();
},
userInputMobile: function() {
return `${this.get("user_input_mobile")}`.htmlSafe();
}.property("user_input_mobile"),
@computed("currentUser.trust_level")
showToTrustLevel(trustLevel) {
checkTrustLevels: function() {
return !(
trustLevel &&
trustLevel > Discourse.SiteSettings.amazon_through_trust_level
currentUser &&
currentUser.get("trust_level") >
Discourse.SiteSettings.amazon_through_trust_level
);
},
@computed("postNumber")
showAfterPost(postNumber) {
if (!postNumber) {
return true;
}
return this.isNthPost(parseInt(this.siteSettings.amazon_nth_post_code));
}
}.property("trust_level")
});
@@ -1,35 +0,0 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import {
default as computed,
observes
} from "ember-addons/ember-computed-decorators";
const currentUser = Discourse.User.current(),
serve_id = Discourse.SiteSettings.carbonads_serve_id,
placement = Discourse.SiteSettings.carbonads_placement;
export default AdComponent.extend({
init() {
this.set("serve_id", serve_id);
this.set("placement", placement);
this._super();
},
@computed("serve_id", "placement")
url(serveId, placement) {
return `//cdn.carbonads.com/carbon.js?serve=${serveId}&placement=${placement}`.htmlSafe();
},
@computed("currentUser.trust_level")
showToTrustLevel(trustLevel) {
return !(
trustLevel &&
trustLevel > Discourse.SiteSettings.carbonads_through_trust_level
);
},
@computed("showToTrustLevel", "showToGroups")
showAd(showToTrustLevel, showToGroups) {
return placement && serve_id && showToTrustLevel && showToGroups;
}
});
@@ -1,13 +1,11 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import {
default as computed,
observes
} from "ember-addons/ember-computed-decorators";
let _loaded = false,
_promise = null;
const currentUser = Discourse.User.current(),
var _loaded = false,
_promise = null,
currentUser = Discourse.User.current(),
propertyId = Discourse.SiteSettings.codefund_property_id;
function loadCodeFund() {
@@ -50,23 +48,16 @@ function loadCodeFund() {
return _promise;
}
export default AdComponent.extend({
classNameBindings: [":codefund-ad"],
export default Ember.Component.extend({
classNameBindings: [
":codefund-ad",
"classForSlot",
"isResponsive:codefund-responsive"
],
propertyId: propertyId,
adRequested: false,
adDetails: {},
displayPostBottom: Ember.computed.equal("placement", "post-bottom"),
displayTopicAbovePostStream: Ember.computed.equal(
"placement",
"topic-above-post-stream"
),
displayTopicAboveSuggested: Ember.computed.equal(
"placement",
"topic-above-suggested"
),
displayTopicListTop: Ember.computed.equal("placement", "topic-list-top"),
_triggerAds() {
if (!propertyId) return;
@@ -96,7 +87,7 @@ export default AdComponent.extend({
},
@observes("listLoading")
waitForLoad() {
waitForLoad: function() {
if (this.get("adRequested")) {
return;
} // already requested that this ad unit be populated
@@ -105,29 +96,36 @@ export default AdComponent.extend({
}
},
@computed("currentUser.trust_level")
showToTrustLevel(trustLevel) {
checkTrustLevels: function() {
return !(
trustLevel && trustLevel > this.siteSettings.codefund_through_trust_level
currentUser &&
currentUser.get("trust_level") >
Discourse.SiteSettings.codefund_through_trust_level
);
}.property("trust_level"),
@computed("checkTrustLevels")
showAd: function(checkTrustLevels) {
return Discourse.SiteSettings.codefund_property_id && checkTrustLevels;
},
@computed("showToTrustLevel", "showToGroups", "showAfterPost")
showAd(showToTrustLevel, showToGroups, showAfterPost) {
return (
this.siteSettings.codefund_property_id &&
showToTrustLevel &&
showToGroups &&
showAfterPost
);
@computed("placement")
displayPostBottom: function(placement) {
return placement === "post-bottom";
},
@computed("postNumber")
showAfterPost(postNumber) {
if (!postNumber) {
return true;
}
@computed("placement")
displayTopicAbovePostStream: function() {
return this.get("placement") === "topic-above-post-stream";
},
return this.isNthPost(parseInt(this.siteSettings.codefund_nth_post));
@computed("placement")
displayTopicAboveSuggested: function() {
return this.get("placement") === "topic-above-suggested";
},
@computed("placement")
displayTopicListTop: function() {
return this.get("placement") === "topic-list-top";
}
});
@@ -1,11 +1,6 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import {
default as computed,
observes
} from "ember-addons/ember-computed-decorators";
import loadScript from "discourse/lib/load-script";
let _loaded = false,
var _loaded = false,
_promise = null,
currentUser = Discourse.User.current(),
publisher_id = Discourse.SiteSettings.adsense_publisher_code;
@@ -35,7 +30,7 @@ function loadAdsense() {
return _promise;
}
const adsenseSrc =
var adsenseSrc =
("https:" === document.location.protocol ? "https:" : "http:") +
"//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js";
_promise = loadScript(adsenseSrc, { scriptTag: true }).then(function() {
@@ -45,7 +40,7 @@ function loadAdsense() {
return _promise;
}
let data = {
var data = {
"topic-list-top": {},
"topic-above-post-stream": {},
"topic-above-suggested": {},
@@ -147,7 +142,7 @@ if (Discourse.SiteSettings.adsense_publisher_code) {
}
}
export default AdComponent.extend({
export default Ember.Component.extend({
classNameBindings: [
":google-adsense",
"classForSlot",
@@ -193,66 +188,52 @@ export default AdComponent.extend({
Ember.run.scheduleOnce("afterRender", this, this._triggerAds);
},
@observes("listLoading")
waitForLoad() {
waitForLoad: function() {
if (this.get("adRequested")) {
return;
} // already requested that this ad unit be populated
if (!this.get("listLoading")) {
Ember.run.scheduleOnce("afterRender", this, this._triggerAds);
}
},
}.observes("listLoading"),
@computed("ad_width")
isResponsive(adWidth) {
return adWidth === "auto";
},
isResponsive: function() {
return this.get("ad_width") === "auto";
}.property("ad_width"),
@computed("placement", "showAd")
classForSlot(placement, showAd) {
return showAd ? `adsense-${placement}`.htmlSafe() : "";
},
classForSlot: function() {
return `adsense-${this.get("placement")}`.htmlSafe();
}.property("placement"),
@computed("isResponsive")
autoAdFormat(isResponsive) {
return isResponsive ? "auto".htmlSafe() : false;
},
autoAdFormat: function() {
return this.get("isResponsive") ? "auto".htmlSafe() : false;
}.property("isResponsive"),
@computed("ad_width", "ad_height", "isResponsive")
adWrapperStyle(w, h, isResponsive) {
return (isResponsive ? "" : `width: ${w}; height: ${h};`).htmlSafe();
},
adWrapperStyle: function() {
return (this.get("isResponsive")
? ""
: `width: ${this.get("ad_width")}; height: ${this.get("ad_height")};`
).htmlSafe();
}.property("ad_width", "ad_height"),
@computed("adWrapperStyle", "isResponsive")
adInsStyle(adWrapperStyle, isResponsive) {
adInsStyle: function() {
return `display: ${
isResponsive ? "block" : "inline-block"
}; ${adWrapperStyle}`.htmlSafe();
},
this.get("isResponsive") ? "block" : "inline-block"
}; ${this.get("adWrapperStyle")}`.htmlSafe();
}.property("adWrapperStyle", "isResponsive"),
@computed("currentUser.trust_level")
showToTrustLevel(trustLevel) {
checkTrustLevels: function() {
return !(
trustLevel && trustLevel > this.siteSettings.adsense_through_trust_level
currentUser &&
currentUser.get("trust_level") >
Discourse.SiteSettings.adsense_through_trust_level
);
},
}.property("trust_level"),
@computed("showToTrustLevel", "showToGroups", "showAfterPost")
showAd(showToTrustLevel, showToGroups, showAfterPost) {
showAd: function() {
return (
this.siteSettings.adsense_publisher_code &&
showToTrustLevel &&
showToGroups &&
showAfterPost
Discourse.SiteSettings.adsense_publisher_code &&
this.get("checkTrustLevels")
);
},
@computed("postNumber")
showAfterPost(postNumber) {
if (!postNumber) {
return true;
}
return this.isNthPost(parseInt(this.siteSettings.adsense_nth_post_code));
}
}.property("checkTrustLevels")
});
File diff suppressed because it is too large Load Diff
@@ -1,107 +0,0 @@
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import {
default as computed,
observes,
on
} from "ember-addons/ember-computed-decorators";
const adIndex = {
topic_list_top: null,
topic_above_post_stream: null,
topic_above_suggested: null,
post_bottom: null
};
export default AdComponent.extend({
classNames: ["house-creative"],
classNameBindings: ["adUnitClass"],
adHtml: "",
@computed("placement", "showAd")
adUnitClass(placement, showAd) {
return showAd ? `house-${placement}` : "";
},
@computed("showToGroups", "showAfterPost")
showAd(showToGroups, showAfterPost) {
return showToGroups && showAfterPost;
},
@computed("postNumber")
showAfterPost(postNumber) {
if (!postNumber) {
return true;
}
return this.isNthPost(
parseInt(this.site.get("house_creatives.settings.after_nth_post"))
);
},
chooseAdHtml() {
const houseAds = this.site.get("house_creatives"),
placement = this.get("placement").replace(/-/g, "_"),
adNames = this.adsNamesForSlot(placement);
if (adNames.length > 0) {
if (!adIndex[placement]) {
adIndex[placement] = 0;
}
let ad = houseAds.creatives[adNames[adIndex[placement]]] || "";
adIndex[placement] = (adIndex[placement] + 1) % adNames.length;
return ad;
} else {
return "";
}
},
adsNamesForSlot(placement) {
const houseAds = this.site.get("house_creatives");
if (!houseAds || !houseAds.settings) {
return [];
}
const adsForSlot = houseAds.settings[placement];
if (
Object.keys(houseAds.creatives).length > 0 &&
!Ember.isBlank(adsForSlot)
) {
return adsForSlot.split("|");
} else {
return [];
}
},
@observes("refreshOnChange")
refreshAd() {
if (this.get("listLoading")) {
return;
}
this.set("adHtml", this.chooseAdHtml());
},
didInsertElement() {
this._super(...arguments);
if (!this.get("showAd")) {
return;
}
if (this.get("listLoading")) {
return;
}
if (adIndex.topic_list_top === null) {
// start at a random spot in the ad inventory
Object.keys(adIndex).forEach(placement => {
const adNames = this.adsNamesForSlot(placement);
adIndex[placement] = Math.floor(Math.random() * adNames.length);
});
}
this.refreshAd();
}
});
@@ -1,29 +0,0 @@
import MultiSelectComponent from "select-kit/components/multi-select";
import computed from "ember-addons/ember-computed-decorators";
const { makeArray } = Ember;
export default MultiSelectComponent.extend({
classNames: "house-ads-chooser",
filterable: true,
filterPlaceholder: "admin.adplugin.house_ads.filter_placeholder",
tokenSeparator: "|",
allowCreate: false,
allowAny: false,
settingValue: "",
computeContent() {
return makeArray(this.get("choices"));
},
// called after a selection is made
mutateValues(values) {
this.set("settingValue", values.join(this.get("tokenSeparator")));
},
// called when first rendered
computeValues() {
return this.get("settingValue")
.split(this.get("tokenSeparator"))
.filter(c => c);
}
});
@@ -1,9 +0,0 @@
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import computed from "ember-addons/ember-computed-decorators";
import HouseAdsSetting from "discourse/plugins/discourse-adplugin/discourse/components/house-ads-setting";
export default HouseAdsSetting.extend({
classNames: "house-ads-setting house-ads-list-setting",
adNames: Ember.computed.mapBy("allAds", "name")
});
@@ -1,57 +0,0 @@
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { i18n, propertyNotEqual } from "discourse/lib/computed";
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
classNames: "house-ads-setting",
adValue: "",
saving: false,
savingStatus: "",
title: i18n("name", "admin.adplugin.house_ads.%@.title"),
help: i18n("name", "admin.adplugin.house_ads.%@.description"),
changed: propertyNotEqual("adValue", "value"),
init() {
this._super(...arguments);
this.set("adValue", this.get("value"));
},
actions: {
save() {
if (!this.get("saving")) {
this.setProperties({
saving: true,
savingStatus: I18n.t("saving")
});
ajax(
`/admin/plugins/adplugin/house_ad_settings/${this.get("name")}.json`,
{
type: "PUT",
data: { value: this.get("adValue") }
}
)
.then(data => {
const adSettings = this.get("adSettings");
adSettings.set(this.get("name"), this.get("adValue"));
this.setProperties({
value: this.get("adValue"),
savingStatus: I18n.t("saved")
});
})
.catch(popupAjaxError)
.finally(() => {
this.setProperties({
saving: false,
savingStatus: ""
});
});
}
},
cancel() {
this.set("adValue", this.get("value"));
}
}
});
@@ -1,5 +0,0 @@
export default Ember.Controller.extend({
adminPluginsHouseAds: Ember.inject.controller("adminPlugins.houseAds"),
houseAds: Ember.computed.alias("adminPluginsHouseAds.model"),
adSettings: Ember.computed.alias("adminPluginsHouseAds.houseAdsSettings")
});
@@ -1,93 +0,0 @@
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { propertyNotEqual } from "discourse/lib/computed";
import { bufferedProperty } from "discourse/mixins/buffered-content";
export default Ember.Controller.extend(bufferedProperty("model"), {
adminPluginsHouseAds: Ember.inject.controller("adminPlugins.houseAds"),
saving: false,
savingStatus: "",
nameDirty: propertyNotEqual("buffered.name", "model.name"),
htmlDirty: propertyNotEqual("buffered.html", "model.html"),
dirty: Ember.computed.or("nameDirty", "htmlDirty"),
disableSave: Ember.computed.not("dirty"),
actions: {
save() {
if (!this.get("saving")) {
this.setProperties({
saving: true,
savingStatus: I18n.t("saving")
});
const data = {},
buffered = this.get("buffered"),
newRecord = !buffered.get("id");
if (!newRecord) {
data.id = buffered.get("id");
}
data.name = buffered.get("name");
data.html = buffered.get("html");
ajax(
newRecord
? `/admin/plugins/adplugin/house_ads`
: `/admin/plugins/adplugin/house_ads/${buffered.get("id")}`,
{
type: newRecord ? "POST" : "PUT",
data
}
)
.then(data => {
this.commitBuffer();
this.set("savingStatus", I18n.t("saved"));
if (newRecord) {
const model = this.get("model");
model.set("id", data.house_ad.id);
const houseAds = this.get("adminPluginsHouseAds.model");
if (!houseAds.includes(model)) {
houseAds.pushObject(model);
}
this.transitionToRoute(
"adminPlugins.houseAds.show",
model.get("id")
);
}
})
.catch(popupAjaxError)
.finally(() => {
this.setProperties({
saving: false,
savingStatus: ""
});
});
}
},
cancel() {
this.rollbackBuffer();
},
destroy() {
const houseAds = this.get("adminPluginsHouseAds.model");
const model = this.get("model");
if (!model.get("id")) {
this.transitionToRoute("adminPlugins.houseAds.index");
return;
}
ajax(`/admin/plugins/adplugin/house_ads/${model.get("id")}`, {
type: "DELETE"
})
.then(() => {
houseAds.removeObject(model);
this.transitionToRoute("adminPlugins.houseAds.index");
})
.catch(() => bootbox.alert(I18n.t("generic_error")));
}
}
});
@@ -1,3 +0,0 @@
export default Ember.Controller.extend({
loadingAds: true
});
@@ -1,7 +0,0 @@
export default Discourse.Route.extend({
actions: {
moreSettings() {
this.transitionTo("adminSiteSettingsCategory", "ad_plugin");
}
}
});
@@ -1,15 +0,0 @@
export default Discourse.Route.extend({
model(params) {
if (params.ad_id === "new") {
return Ember.Object.create({
name: I18n.t("admin.adplugin.house_ads.new_name"),
html: ""
});
} else {
return this.modelFor("adminPlugins.houseAds").findBy(
"id",
parseInt(params.ad_id, 10)
);
}
}
});
@@ -1,20 +0,0 @@
import { ajax } from "discourse/lib/ajax";
export default Discourse.Route.extend({
settings: null,
model(params) {
return ajax("/admin/plugins/adplugin/house_ads.json").then(data => {
this.set("settings", Ember.Object.create(data.settings));
return data.house_ads.map(ad => Ember.Object.create(ad));
});
},
setupController(controller, model) {
controller.setProperties({
model,
houseAdsSettings: this.get("settings"),
loadingAds: false
});
}
});
@@ -1,23 +0,0 @@
{{#d-section class="house-ads-settings content-body"}}
<div>{{i18n 'admin.adplugin.house_ads.description'}}</div>
{{#unless houseAds.length}}
<p>
{{#link-to 'adminPlugins.houseAds.show' 'new'}}
{{i18n 'admin.adplugin.house_ads.get_started'}}
{{/link-to}}
</p>
{{else}}
<form class="form-horizontal">
{{house-ads-list-setting name="topic_list_top" value=adSettings.topic_list_top allAds=houseAds adSettings=adSettings}}
{{house-ads-list-setting name="topic_above_post_stream" value=adSettings.topic_above_post_stream allAds=houseAds adSettings=adSettings}}
{{house-ads-list-setting name="topic_above_suggested" value=adSettings.topic_above_suggested allAds=houseAds adSettings=adSettings}}
{{house-ads-list-setting name="post_bottom" value=adSettings.post_bottom allAds=houseAds adSettings=adSettings}}
{{d-button label="admin.adplugin.house_ads.more_settings"
icon="cog"
class="btn-default"
action=(route-action "moreSettings")}}
</form>
{{/unless}}
{{/d-section}}
@@ -1,26 +0,0 @@
{{#d-section class="edit-house-ad content-body"}}
<h1>{{text-field class="house-ad-name" value=buffered.name}}</h1>
<div class="controls">
{{ace-editor content=buffered.html mode="html"}}
</div>
<div class="controls">
{{d-button
action=(action "save")
disabled=disableSave
class="btn-primary"
label="admin.adplugin.house_ads.save"}}
{{#if saving}}
{{savingStatus}}
{{else}}
{{#if dirty}}
<a href {{action "cancel"}}>{{i18n 'cancel'}}</a>
{{/if}}
{{/if}}
{{d-button
action=(action "destroy")
class="btn-danger delete-button"
label="admin.adplugin.house_ads.delete"}}
</div>
{{/d-section}}
@@ -1,27 +0,0 @@
<div class="adplugin-mgmt">
<h1>{{i18n 'admin.adplugin.house_ads.title'}}</h1>
{{#if model.length}}
<div class="content-list">
<div class="house-ads-actions">
{{#link-to 'adminPlugins.houseAds.show' 'new' class="btn btn-primary"}}
{{d-icon "plus"}}
<span>{{i18n 'admin.adplugin.house_ads.new'}}</span>
{{/link-to}}
{{#link-to 'adminPlugins.houseAds.index' class="btn btn-default"}}
{{d-icon "cog"}}
<span>{{i18n 'admin.adplugin.house_ads.settings'}}</span>
{{/link-to}}
</div>
<ul class="house-ads-list">
{{#each model as |ad|}}
<li class="house-ads-list-item">
{{#link-to 'adminPlugins.houseAds.show' ad.id}}
{{ad.name}}
{{/link-to}}
</li>
{{/each}}
</ul>
</div>
{{/if}}
{{outlet}}
</div>
@@ -1,8 +0,0 @@
{{#each adComponents as |adComponent|}}
{{component adComponent
placement=placement
refreshOnChange=refreshOnChange
category=category
listLoading=listLoading
postNumber=postNumber}}
{{/each}}
@@ -0,0 +1,43 @@
{{#if site.mobileView}}
{{#if model.postSpecificCountAdsense}}
{{#if siteSettings.adsense_mobile_post_bottom_code}}
{{google-adsense placement="post-bottom" postNumber=model.post_number}}
{{/if}}
{{/if}}
{{#if model.postSpecificCountDFP}}
{{#if siteSettings.dfp_mobile_post_bottom_code}}
{{google-dfp-ad placement="post-bottom" category=model.topic.category.slug postNumber=model.post_number}}
{{/if}}
{{/if}}
{{#if model.postSpecificCountAmazon}}
{{#if siteSettings.amazon_mobile_post_bottom_src_code}}
{{amazon-product-links placement="post-bottom" postNumber=model.post_number}}
{{/if}}
{{/if}}
{{#if model.postSpecificCountCodeFund}}
{{#if siteSettings.codefund_below_post_enabled}}
{{codefund-ad placement="post-bottom" postNumber=model.post_number}}
{{/if}}
{{/if}}
{{else}}
{{#if model.postSpecificCountAdsense}}
{{#if siteSettings.adsense_post_bottom_code}}
{{google-adsense placement="post-bottom" postNumber=model.post_number}}
{{/if}}
{{/if}}
{{#if model.postSpecificCountDFP}}
{{#if siteSettings.dfp_post_bottom_code}}
{{google-dfp-ad placement="post-bottom" category=model.topic.category.slug postNumber=model.post_number}}
{{/if}}
{{/if}}
{{#if model.postSpecificCountAmazon}}
{{#if siteSettings.amazon_post_bottom_src_code}}
{{amazon-product-links placement="post-bottom" postNumber=model.post_number}}
{{/if}}
{{/if}}
{{#if model.postSpecificCountCodeFund}}
{{#if siteSettings.codefund_below_post_enabled}}
{{codefund-ad placement="post-bottom" postNumber=model.post_number}}
{{/if}}
{{/if}}
{{/if}}

Some files were not shown because too many files have changed in this diff Show More