FEATURE: House Ads
Allows creating ads within Discourse admin at Plugins > House Ads. Write the ads in html and style them with CSS in themes.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
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 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
|
||||
@@ -0,0 +1,100 @@
|
||||
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
|
||||
@@ -0,0 +1,62 @@
|
||||
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),
|
||||
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
|
||||
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
resource: "admin.adminPlugins",
|
||||
path: "/plugins",
|
||||
map() {
|
||||
this.route("houseAds", { path: "/house-ads" }, function() {
|
||||
this.route("index", { path: "/" });
|
||||
this.route("show", { path: "/:ad_id" });
|
||||
});
|
||||
}
|
||||
};
|
||||
+10
-2
@@ -1,8 +1,8 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
@computed('currentUser.groups')
|
||||
showToGroups: function(groups) {
|
||||
@computed("currentUser.groups")
|
||||
showToGroups(groups) {
|
||||
const currentUser = Discourse.User.current();
|
||||
|
||||
if (
|
||||
@@ -17,5 +17,13 @@ export default Ember.Component.extend({
|
||||
const noAdsGroupNames = this.siteSettings.no_ads_for_groups.split("|");
|
||||
|
||||
return !groups.any(group => noAdsGroupNames.includes(group.name));
|
||||
},
|
||||
|
||||
isNthPost(n) {
|
||||
if (n && n > 0) {
|
||||
return this.get("postNumber") % n === 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const adConfig = Ember.Object.create({
|
||||
"google-adsense": {
|
||||
settingPrefix: "adsense" // settings follow naming convention
|
||||
},
|
||||
"google-dfp-ad": {
|
||||
settingPrefix: "dfp" // settings follow naming convention
|
||||
},
|
||||
"amazon-product-links": {
|
||||
settingPrefix: "amazon",
|
||||
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",
|
||||
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",
|
||||
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
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default Ember.Component.extend({
|
||||
@computed("placement")
|
||||
adComponents(placement) {
|
||||
// Check house ads first
|
||||
const houseAds = this.site.get("house_creatives"),
|
||||
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 => {
|
||||
const config = adConfig[adNetwork];
|
||||
let settingNames = null,
|
||||
name;
|
||||
|
||||
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_" : ""
|
||||
}${placement.replace(/-/g, "_")}_code`;
|
||||
}
|
||||
|
||||
return name !== false && !Ember.isBlank(this.siteSettings[name]);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const currentUser = Discourse.User.current();
|
||||
@@ -125,7 +125,11 @@ if (
|
||||
export default AdComponent.extend({
|
||||
classNames: ["amazon-product-links"],
|
||||
|
||||
showAd: Ember.computed.and("showToTrustLevel", "showToGroups"),
|
||||
showAd: Ember.computed.and(
|
||||
"showToTrustLevel",
|
||||
"showToGroups",
|
||||
"showAfterPost"
|
||||
),
|
||||
|
||||
init() {
|
||||
let placement = this.get("placement");
|
||||
@@ -169,5 +173,14 @@ export default AdComponent.extend({
|
||||
trustLevel &&
|
||||
trustLevel > Discourse.SiteSettings.amazon_through_trust_level
|
||||
);
|
||||
},
|
||||
|
||||
@computed("postNumber")
|
||||
showAfterPost(postNumber) {
|
||||
if (!postNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isNthPost(parseInt(this.siteSettings.amazon_nth_post_code));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
@@ -108,17 +108,26 @@ export default AdComponent.extend({
|
||||
@computed("currentUser.trust_level")
|
||||
showToTrustLevel(trustLevel) {
|
||||
return !(
|
||||
trustLevel &&
|
||||
trustLevel > Discourse.SiteSettings.codefund_through_trust_level
|
||||
trustLevel && trustLevel > this.siteSettings.codefund_through_trust_level
|
||||
);
|
||||
},
|
||||
|
||||
@computed("showToTrustLevel", "showToGroups")
|
||||
showAd(showToTrustLevel, showToGroups) {
|
||||
@computed("showToTrustLevel", "showToGroups", "showAfterPost")
|
||||
showAd(showToTrustLevel, showToGroups, showAfterPost) {
|
||||
return (
|
||||
Discourse.SiteSettings.codefund_property_id &&
|
||||
this.siteSettings.codefund_property_id &&
|
||||
showToTrustLevel &&
|
||||
showToGroups
|
||||
showToGroups &&
|
||||
showAfterPost
|
||||
);
|
||||
},
|
||||
|
||||
@computed("postNumber")
|
||||
showAfterPost(postNumber) {
|
||||
if (!postNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isNthPost(parseInt(this.siteSettings.codefund_nth_post));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
@@ -233,17 +233,26 @@ export default AdComponent.extend({
|
||||
@computed("currentUser.trust_level")
|
||||
showToTrustLevel(trustLevel) {
|
||||
return !(
|
||||
trustLevel &&
|
||||
trustLevel > Discourse.SiteSettings.adsense_through_trust_level
|
||||
trustLevel && trustLevel > this.siteSettings.adsense_through_trust_level
|
||||
);
|
||||
},
|
||||
|
||||
@computed("showToTrustLevel", "showToGroups")
|
||||
showAd(showToTrustLevel, showToGroups) {
|
||||
@computed("showToTrustLevel", "showToGroups", "showAfterPost")
|
||||
showAd(showToTrustLevel, showToGroups, showAfterPost) {
|
||||
return (
|
||||
this.siteSettings.adsense_publisher_code &&
|
||||
showToTrustLevel &&
|
||||
showToGroups &&
|
||||
Discourse.SiteSettings.adsense_publisher_code
|
||||
showAfterPost
|
||||
);
|
||||
},
|
||||
|
||||
@computed("postNumber")
|
||||
showAfterPost(postNumber) {
|
||||
if (!postNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isNthPost(parseInt(this.siteSettings.adsense_nth_post_code));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad_component";
|
||||
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
|
||||
import {
|
||||
default as computed,
|
||||
observes,
|
||||
@@ -234,23 +234,32 @@ export default AdComponent.extend({
|
||||
return `width: ${w}px;`.htmlSafe();
|
||||
},
|
||||
|
||||
@computed("showToTrustLevel", "showToGroups")
|
||||
showAd(showToTrustLevel, showToGroups) {
|
||||
@computed("showToTrustLevel", "showToGroups", "showAfterPost")
|
||||
showAd(showToTrustLevel, showToGroups, showAfterPost) {
|
||||
return (
|
||||
Discourse.SiteSettings.dfp_publisher_id &&
|
||||
this.siteSettings.dfp_publisher_id &&
|
||||
showToTrustLevel &&
|
||||
showToGroups
|
||||
showToGroups &&
|
||||
showAfterPost
|
||||
);
|
||||
},
|
||||
|
||||
@computed("currentUser.trust_level")
|
||||
showToTrustLevel(trustLevel) {
|
||||
return !(
|
||||
trustLevel &&
|
||||
trustLevel > Discourse.SiteSettings.dfp_through_trust_level
|
||||
trustLevel && trustLevel > this.siteSettings.dfp_through_trust_level
|
||||
);
|
||||
},
|
||||
|
||||
@computed("postNumber")
|
||||
showAfterPost(postNumber) {
|
||||
if (!postNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.isNthPost(parseInt(this.siteSettings.dfp_nth_post_code));
|
||||
},
|
||||
|
||||
@observes("refreshOnChange")
|
||||
refreshAd() {
|
||||
var slot = ads[this.get("divId")];
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
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"),
|
||||
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"]) {
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
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")
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
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"));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
export default Ember.Controller.extend({
|
||||
adminPluginsHouseAds: Ember.inject.controller("adminPlugins.houseAds"),
|
||||
houseAds: Ember.computed.alias("adminPluginsHouseAds.model"),
|
||||
adSettings: Ember.computed.alias("adminPluginsHouseAds.houseAdsSettings")
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
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")));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export default Ember.Controller.extend({
|
||||
loadingAds: true
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
export default Discourse.Route.extend({
|
||||
actions: {
|
||||
moreSettings() {
|
||||
this.transitionTo("adminSiteSettingsCategory", "ad_plugin");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
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
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
{{#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}}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{#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}}
|
||||
@@ -0,0 +1,27 @@
|
||||
<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>
|
||||
@@ -0,0 +1,8 @@
|
||||
{{#each adComponents as |adComponent|}}
|
||||
{{component adComponent
|
||||
placement=placement
|
||||
refreshOnChange=refreshOnChange
|
||||
category=category
|
||||
listLoading=listLoading
|
||||
postNumber=postNumber}}
|
||||
{{/each}}
|
||||
@@ -1,43 +0,0 @@
|
||||
{{#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}}
|
||||
@@ -0,0 +1,3 @@
|
||||
{{#if showAd}}
|
||||
{{{adHtml}}}
|
||||
{{/if}}
|
||||
@@ -0,0 +1,9 @@
|
||||
<label for="{{name}}">{{title}}</label>
|
||||
{{house-ads-chooser settingValue=adValue choices=adNames}}
|
||||
<div class='setting-controls'>
|
||||
{{#if changed}}
|
||||
{{d-button class="ok" action=(action "save") icon="check"}}
|
||||
{{d-button class="cancel" action=(action "cancel") icon="times"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<p class='help'>{{help}}</p>
|
||||
@@ -0,0 +1,9 @@
|
||||
<label for="{{name}}">{{title}}</label>
|
||||
{{text-field value=adValue classNames="house-ads-text-input"}}
|
||||
<div class='setting-controls'>
|
||||
{{#if changed}}
|
||||
{{d-button class="ok" action=(action "save") icon="check"}}
|
||||
{{d-button class="cancel" action=(action "cancel") icon="times"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<p class='help'>{{help}}</p>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user