174 changed files with 4432 additions and 7428 deletions
-7
View File
@@ -1,7 +0,0 @@
< 3.5.0.beta8-dev: e51aad3bfd41cf2cb9557e025e833122a9f6c892
< 3.5.0.beta5-dev: bd9af0b9a577bfd2c01cc8b81a782a4e8b81b9c5
< 3.5.0.beta1-dev: 6ceba62e985223c269782c4bc9a1bdc4a5adba28
< 3.4.0.beta2-dev: 7685ebf396c93e8accc5a76a81fcec4384a73fa3
< 3.4.0.beta1-dev: da38a4c053b68d7076db4441cfe86b473979945d
< 3.3.0.beta1-dev: 062efc8a547c19474eaeefae464837d63a5e5fb3
3.1.999: 30c49eb50d6d16fcf82bace3da0e1972b7b220c0
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "eslint-config-discourse"
}
-2
View File
@@ -1,2 +0,0 @@
engine-strict = true
auto-install-peers = false
+1
View File
@@ -0,0 +1 @@
{}
-1
View File
@@ -1 +0,0 @@
module.exports = require("@discourse/lint-configs/prettier");
-3
View File
@@ -1,5 +1,2 @@
inherit_gem: inherit_gem:
rubocop-discourse: stree-compat.yml rubocop-discourse: stree-compat.yml
RSpec/NamedSubject:
Enabled: false
+1 -1
View File
@@ -1,2 +1,2 @@
--print-width=100 --print-width=100
--plugins=plugin/trailing_comma,plugin/disable_auto_ternary --plugins=plugin/trailing_comma
-1
View File
@@ -1 +0,0 @@
module.exports = require("@discourse/lint-configs/template-lint");
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
plugins: ["ember-template-lint-plugin-discourse"],
extends: "discourse:recommended",
};
+24 -83
View File
@@ -1,95 +1,36 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activesupport (8.0.2) ast (2.4.2)
base64 json (2.6.2)
benchmark (>= 0.3) parallel (1.22.1)
bigdecimal parser (3.1.2.1)
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
ast (2.4.3)
base64 (0.3.0)
benchmark (0.4.1)
bigdecimal (3.2.0)
concurrent-ruby (1.3.5)
connection_pool (2.5.3)
drb (2.2.3)
i18n (1.14.7)
concurrent-ruby (~> 1.0)
json (2.12.2)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
minitest (5.25.5)
parallel (1.27.0)
parser (3.3.8.0)
ast (~> 2.4.1) ast (~> 2.4.1)
racc prettier_print (1.2.0)
prettier_print (1.2.1)
prism (1.4.0)
racc (1.8.1)
rack (3.1.16)
rainbow (3.1.1) rainbow (3.1.1)
regexp_parser (2.10.0) regexp_parser (2.6.0)
rubocop (1.75.8) rexml (3.2.5)
rubocop (1.36.0)
json (~> 2.3) json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.3.0.2) parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0) regexp_parser (>= 1.8, < 3.0)
rubocop-ast (>= 1.44.0, < 2.0) rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.20.1, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0) unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.44.1) rubocop-ast (1.21.0)
parser (>= 3.3.7.2) parser (>= 3.1.1.0)
prism (~> 1.4) rubocop-discourse (3.0)
rubocop-capybara (2.22.1) rubocop (>= 1.1.0)
lint_roller (~> 1.1) rubocop-rspec (>= 2.0.0)
rubocop (~> 1.72, >= 1.72.1) rubocop-rspec (2.13.2)
rubocop-discourse (3.12.1) rubocop (~> 1.33)
activesupport (>= 6.1) ruby-progressbar (1.11.0)
lint_roller (>= 1.1.0) syntax_tree (5.1.0)
rubocop (>= 1.73.2)
rubocop-capybara (>= 2.22.0)
rubocop-factory_bot (>= 2.27.0)
rubocop-rails (>= 2.30.3)
rubocop-rspec (>= 3.0.1)
rubocop-rspec_rails (>= 2.31.0)
rubocop-factory_bot (2.27.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.32.0)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rspec (3.6.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec_rails (2.31.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5)
ruby-progressbar (1.13.0)
securerandom (0.4.1)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0) prettier_print (>= 1.2.0)
tzinfo (2.0.6) unicode-display_width (2.3.0)
concurrent-ruby (~> 1.0)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
PLATFORMS PLATFORMS
ruby ruby
@@ -99,4 +40,4 @@ DEPENDENCIES
syntax_tree syntax_tree
BUNDLED WITH BUNDLED WITH
2.6.9 2.1.4
+2 -3
View File
@@ -1,8 +1,5 @@
# Official Discourse Advertising Plugin # Official Discourse Advertising Plugin
> [!IMPORTANT]
> This plugin has now been bundled into Discourse core. See: https://meta.discourse.org/t/bundling-more-popular-plugins-with-discourse-core/373574
Official Plugin Topic & Documentation: https://meta.discourse.org/t/official-advertising-ad-plugin-for-discourse/33734 Official Plugin Topic & Documentation: https://meta.discourse.org/t/official-advertising-ad-plugin-for-discourse/33734
This is the official Discourse advertising plugin. It allows advertisements to be served by supported advertising platforms for users with a Discourse forum. This is the official Discourse advertising plugin. It allows advertisements to be served by supported advertising platforms for users with a Discourse forum.
@@ -112,8 +109,10 @@ Only for Product Link and Banner Ads.
##### Carbon Ads Script Tag to Discourse's Site Settings ##### Carbon Ads Script Tag to Discourse's Site Settings
![adpluginexample](https://user-images.githubusercontent.com/5862206/214489455-234a3812-3606-47bf-9e49-cdaa3d1e3b9c.png) ![adpluginexample](https://user-images.githubusercontent.com/5862206/214489455-234a3812-3606-47bf-9e49-cdaa3d1e3b9c.png)
##### AdButler Ads Zone URL to Discourse's Site Settings ##### AdButler Ads Zone URL to Discourse's Site Settings
This plugin only support AdButler "Standard Zones". Text and VAST are not supported. This plugin only support AdButler "Standard Zones". Text and VAST are not supported.
@@ -1,7 +0,0 @@
import CategorySelector from "select-kit/components/category-selector";
export default class HouseAdsCategorySelector extends CategorySelector {
get value() {
return this.selectedCategories.map((c) => c.id);
}
}
@@ -1,33 +0,0 @@
import { computed } from "@ember/object";
import { classNames } from "@ember-decorators/component";
import { makeArray } from "discourse/lib/helpers";
import MultiSelectComponent from "select-kit/components/multi-select";
@classNames("house-ads-chooser")
export default class HouseAdsChooser extends MultiSelectComponent {
filterable = true;
filterPlaceholder = "admin.adplugin.house_ads.filter_placeholder";
tokenSeparator = "|";
allowCreate = false;
allowAny = false;
settingValue = "";
valueAttribute = null;
nameProperty = null;
@computed("settingValue")
get value() {
return this.settingValue
.toString()
.split(this.tokenSeparator)
.filter(Boolean);
}
computeValues() {
return this.settingValue.split(this.tokenSeparator).filter(Boolean);
}
@computed("choices")
get content() {
return makeArray(this.choices);
}
}
@@ -1,34 +0,0 @@
import { action } from "@ember/object";
import { mapBy } from "@ember/object/computed";
import { classNames } from "@ember-decorators/component";
import DButton from "discourse/components/d-button";
import { makeArray } from "discourse/lib/helpers";
import HouseAdsChooser from "./house-ads-chooser";
import HouseAdsSetting from "./house-ads-setting";
@classNames("house-ads-setting house-ads-list-setting")
export default class HouseAdsListSetting extends HouseAdsSetting {
@mapBy("allAds", "name") adNames;
@action
changeAdValue(value) {
const settingValue = makeArray(value).join("|");
this.set("adValue", settingValue);
}
<template>
<label for={{this.name}}>{{this.title}}</label>
<HouseAdsChooser
@settingValue={{this.adValue}}
@choices={{this.adNames}}
@onChange={{this.changeAdValue}}
/>
<div class="setting-controls">
{{#if this.changed}}
<DButton class="ok" @action={{action "save"}} @icon="check" />
<DButton class="cancel" @action={{action "cancel"}} @icon="xmark" />
{{/if}}
</div>
<p class="help">{{this.help}}</p>
</template>
}
@@ -1,72 +0,0 @@
import Component from "@ember/component";
import { action } from "@ember/object";
import { classNames } from "@ember-decorators/component";
import DButton from "discourse/components/d-button";
import TextField from "discourse/components/text-field";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { i18n as computedI18n, propertyNotEqual } from "discourse/lib/computed";
import { i18n } from "discourse-i18n";
@classNames("house-ads-setting")
export default class HouseAdsSetting extends Component {
adValue = "";
saving = false;
savingStatus = "";
@computedI18n("name", "admin.adplugin.house_ads.%@.title") title;
@computedI18n("name", "admin.adplugin.house_ads.%@.description") help;
@propertyNotEqual("adValue", "value") changed;
init() {
super.init(...arguments);
this.set("adValue", this.get("value"));
}
@action
save() {
if (!this.get("saving")) {
this.setProperties({
saving: true,
savingStatus: i18n("saving"),
});
ajax(`/admin/plugins/pluginad/house_settings/${this.get("name")}.json`, {
type: "PUT",
data: { value: this.get("adValue") },
})
.then(() => {
const adSettings = this.get("adSettings");
adSettings.set(this.get("name"), this.get("adValue"));
this.setProperties({
value: this.get("adValue"),
savingStatus: i18n("saved"),
});
})
.catch(popupAjaxError)
.finally(() => {
this.setProperties({
saving: false,
savingStatus: "",
});
});
}
}
@action
cancel() {
this.set("adValue", this.get("value"));
}
<template>
<label for={{this.name}}>{{this.title}}</label>
<TextField @value={{this.adValue}} @classNames="house-ads-text-input" />
<div class="setting-controls">
{{#if this.changed}}
<DButton class="ok" @action={{action "save"}} @icon="check" />
<DButton class="cancel" @action={{action "cancel"}} @icon="xmark" />
{{/if}}
</div>
<p class="help">{{this.help}}</p>
</template>
}
@@ -1,18 +0,0 @@
import { htmlSafe } from "@ember/template";
import DModal from "discourse/components/d-modal";
import { i18n } from "discourse-i18n";
const Preview = <template>
<DModal
@closeModal={{@closeModal}}
@title={{i18n "admin.adplugin.house_ads.preview"}}
>
<:body>
<div class="house-ad-preview">
{{htmlSafe @model.html}}
</div>
</:body>
</DModal>
</template>;
export default Preview;
@@ -1,9 +0,0 @@
import Controller, { inject as controller } from "@ember/controller";
import { alias } from "@ember/object/computed";
export default class AdminPluginsHouseAdsIndexController extends Controller {
@controller("adminPlugins.houseAds") adminPluginsHouseAds;
@alias("adminPluginsHouseAds.model") houseAds;
@alias("adminPluginsHouseAds.houseAdsSettings") adSettings;
}
@@ -1,156 +0,0 @@
import { tracked } from "@glimmer/tracking";
import Controller, { inject as controller } from "@ember/controller";
import EmberObject, { action } from "@ember/object";
import { service } from "@ember/service";
import { TrackedObject } from "@ember-compat/tracked-built-ins";
import { observes } from "@ember-decorators/object";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import Category from "discourse/models/category";
import { i18n } from "discourse-i18n";
import Preview from "../components/modal/preview";
export default class adminPluginsHouseAdsShow extends Controller {
@service router;
@service modal;
@controller("adminPlugins.houseAds") houseAdsController;
@tracked selectedCategories = [];
@tracked selectedGroups = [];
@tracked saving = false;
@tracked savingStatus = "";
@tracked buffered;
@observes("model")
modelChanged() {
this.buffered = new TrackedObject({ ...this.model });
this.selectedCategories = this.model.categories || [];
this.selectedGroups = this.model.group_ids || [];
}
get disabledSave() {
for (const key in this.buffered) {
// we don't want to compare the categories array
if (key !== "categories" && this.buffered[key] !== this.model[key]) {
return false;
}
}
return true;
}
@action
async save() {
if (!this.saving) {
this.saving = true;
this.savingStatus = i18n("saving");
const data = {};
const newRecord = !this.buffered.id;
if (!newRecord) {
data.id = this.buffered.id;
}
data.name = this.buffered.name;
data.html = this.buffered.html;
data.visible_to_logged_in_users =
this.buffered.visible_to_logged_in_users;
data.visible_to_anons = this.buffered.visible_to_anons;
data.category_ids = this.buffered.category_ids;
data.group_ids = this.buffered.group_ids;
try {
const ajaxData = await ajax(
newRecord
? `/admin/plugins/pluginad/house_creatives`
: `/admin/plugins/pluginad/house_creatives/${this.buffered.id}`,
{
type: newRecord ? "POST" : "PUT",
data,
}
);
this.savingStatus = i18n("saved");
const houseAds = this.houseAdsController.model;
if (newRecord) {
this.buffered.id = ajaxData.house_ad.id;
if (!houseAds.includes(this.buffered)) {
houseAds.pushObject(EmberObject.create(this.buffered));
}
this.router.transitionTo(
"adminPlugins.houseAds.show",
this.buffered.id
);
} else {
houseAds
.find((ad) => ad.id === this.buffered.id)
.setProperties(this.buffered);
}
} catch (error) {
popupAjaxError(error);
} finally {
this.set("model", this.buffered);
this.saving = false;
this.savingStatus = "";
}
}
}
@action
setCategoryIds(categoryArray) {
this.selectedCategories = categoryArray;
this.buffered.category_ids = categoryArray.map((c) => c.id);
this.setCategoriesForBuffered();
}
@action
setGroupIds(groupIds) {
this.selectedGroups = groupIds;
this.buffered.group_ids = groupIds.map((id) => id);
}
@action
cancel() {
this.buffered = new TrackedObject({ ...this.model });
this.selectedCategories = this.model.categories || [];
this.selectedGroups = this.model.group_ids || [];
this.setCategoriesForBuffered();
}
@action
async destroy() {
if (!this.buffered.id) {
this.router.transitionTo("adminPlugins.houseAds.index");
return;
}
try {
await ajax(
`/admin/plugins/pluginad/house_creatives/${this.buffered.id}`,
{
type: "DELETE",
}
);
this.houseAdsController.model.removeObject(
this.houseAdsController.model.findBy("id", this.buffered.id)
);
this.router.transitionTo("adminPlugins.houseAds.index");
} catch (error) {
popupAjaxError(error);
}
}
@action
openPreview() {
this.modal.show(Preview, {
model: {
html: this.buffered.html,
},
});
}
setCategoriesForBuffered() {
// we need to fetch the categories because the serializer is not being used
// to attach the category object to the house ads
this.buffered.categories = this.buffered.category_ids
? this.buffered.category_ids.map((categoryId) =>
Category.findById(categoryId)
)
: [];
}
}
@@ -1,5 +0,0 @@
import Controller from "@ember/controller";
export default class AdminPluginsHouseAdsController extends Controller {
loadingAds = true;
}
@@ -1,12 +0,0 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
export default class AdminPluginsHouseAdsIndex extends DiscourseRoute {
@service router;
@action
moreSettings() {
this.router.transitionTo("adminSiteSettingsCategory", "ad_plugin");
}
}
@@ -1,23 +0,0 @@
import { TrackedObject } from "@ember-compat/tracked-built-ins";
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminPluginsHouseAdsShow extends DiscourseRoute {
model(params) {
if (params.ad_id === "new") {
return new TrackedObject({
name: i18n("admin.adplugin.house_ads.new_name"),
html: "",
visible_to_logged_in_users: true,
visible_to_anons: true,
});
} else {
return new TrackedObject(
this.modelFor("adminPlugins.houseAds").findBy(
"id",
parseInt(params.ad_id, 10)
)
);
}
}
}
@@ -1,62 +0,0 @@
import { LinkTo } from "@ember/routing";
import RouteTemplate from "ember-route-template";
import DButton from "discourse/components/d-button";
import routeAction from "discourse/helpers/route-action";
import { i18n } from "discourse-i18n";
import HouseAdsListSetting from "../components/house-ads-list-setting";
export default RouteTemplate(
<template>
<section class="house-ads-settings content-body">
<div>{{i18n "admin.adplugin.house_ads.description"}}</div>
{{#if @controller.houseAds.length}}
<form class="form-horizontal">
<HouseAdsListSetting
@name="topic_list_top"
@value={{@controller.adSettings.topic_list_top}}
@allAds={{@controller.houseAds}}
@adSettings={{@controller.adSettings}}
/>
<HouseAdsListSetting
@name="topic_above_post_stream"
@value={{@controller.adSettings.topic_above_post_stream}}
@allAds={{@controller.houseAds}}
@adSettings={{@controller.adSettings}}
/>
<HouseAdsListSetting
@name="topic_above_suggested"
@value={{@controller.adSettings.topic_above_suggested}}
@allAds={{@controller.houseAds}}
@adSettings={{@controller.adSettings}}
/>
<HouseAdsListSetting
@name="post_bottom"
@value={{@controller.adSettings.post_bottom}}
@allAds={{@controller.houseAds}}
@adSettings={{@controller.adSettings}}
/>
<HouseAdsListSetting
@name="topic_list_between"
@value={{@controller.adSettings.topic_list_between}}
@allAds={{@controller.houseAds}}
@adSettings={{@controller.adSettings}}
/>
<DButton
@label="admin.adplugin.house_ads.more_settings"
@icon="gear"
@action={{routeAction "moreSettings"}}
class="btn-default"
/>
</form>
{{else}}
<p>
{{#LinkTo route="adminPlugins.houseAds.show" model="new"}}
{{i18n "admin.adplugin.house_ads.get_started"}}
{{/LinkTo}}
</p>
{{/if}}
</section>
</template>
);
@@ -1,97 +0,0 @@
import { Input } from "@ember/component";
import { fn, hash } from "@ember/helper";
import RouteTemplate from "ember-route-template";
import AceEditor from "discourse/components/ace-editor";
import DButton from "discourse/components/d-button";
import TextField from "discourse/components/text-field";
import { i18n } from "discourse-i18n";
import GroupChooser from "select-kit/components/group-chooser";
import HouseAdsCategorySelector from "../components/house-ads-category-selector";
export default RouteTemplate(
<template>
<section class="edit-house-ad content-body">
<h1><TextField
@value={{@controller.buffered.name}}
class="house-ad-name"
/></h1>
<div class="controls">
<AceEditor
@content={{@controller.buffered.html}}
@onChange={{fn (mut @controller.buffered.html)}}
@mode="html"
/>
</div>
<div class="controls">
<div class="visibility-settings">
<div>
<Input
@type="checkbox"
@checked={{@controller.buffered.visible_to_logged_in_users}}
class="visible-to-logged-in-checkbox"
/>
<span>{{i18n
"admin.adplugin.house_ads.show_to_logged_in_users"
}}</span>
</div>
<div>
<Input
class="visible-to-anonymous-checkbox"
@type="checkbox"
@checked={{@controller.buffered.visible_to_anons}}
/>
<span>{{i18n "admin.adplugin.house_ads.show_to_anons"}}</span>
</div>
<HouseAdsCategorySelector
@categories={{@controller.site.categories}}
@selectedCategories={{@controller.selectedCategories}}
@onChange={{@controller.setCategoryIds}}
@options={{hash allowAny=true}}
class="house-ads-categories"
/>
<div class="description">
{{i18n "admin.adplugin.house_ads.category_chooser_description"}}
</div>
<GroupChooser
@content={{@controller.site.groups}}
@onChange={{@controller.setGroupIds}}
@value={{@controller.selectedGroups}}
class="banner-groups"
/>
<div class="description">
{{i18n "admin.adplugin.house_ads.group_chooser_description"}}
</div>
</div>
<DButton
@action={{@controller.save}}
@disabled={{@controller.disabledSave}}
@label="admin.adplugin.house_ads.save"
class="btn-primary save-button"
/>
{{#if @controller.saving}}
{{@controller.savingStatus}}
{{else}}
{{#unless @controller.disabledSave}}
<DButton @action={{@controller.cancel}} @label="cancel" />
{{/unless}}
{{/if}}
<DButton
@action={{@controller.openPreview}}
@label="admin.adplugin.house_ads.preview"
/>
<DButton
@action={{@controller.destroy}}
@label="admin.adplugin.house_ads.delete"
class="btn-danger delete-button"
/>
</div>
</section>
</template>
);
@@ -1,43 +0,0 @@
import { LinkTo } from "@ember/routing";
import RouteTemplate from "ember-route-template";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
export default RouteTemplate(
<template>
<div class="adplugin-mgmt">
<h1>{{i18n "admin.adplugin.house_ads.title"}}</h1>
{{#if @controller.model.length}}
<div class="content-list">
<div class="house-ads-actions">
<LinkTo
@route="adminPlugins.houseAds.show"
@model="new"
class="btn btn-primary"
>
{{icon "plus"}}
<span>{{i18n "admin.adplugin.house_ads.new"}}</span>
</LinkTo>
<LinkTo
@route="adminPlugins.houseAds.index"
class="btn btn-default"
>
{{icon "gear"}}
<span>{{i18n "admin.adplugin.house_ads.settings"}}</span>
</LinkTo>
</div>
<ul class="house-ads-list">
{{#each @controller.model as |ad|}}
<li class="house-ads-list-item">
{{#LinkTo route="adminPlugins.houseAds.show" model=ad.id}}
{{ad.name}}
{{/LinkTo}}
</li>
{{/each}}
</ul>
</div>
{{/if}}
{{outlet}}
</div>
</template>
);
-13
View File
@@ -1,13 +0,0 @@
# frozen_string_literal: true
#
class AdstxtController < ::ApplicationController
requires_plugin AdPlugin.plugin_name
skip_before_action :preload_json, :check_xhr, :redirect_to_login_if_required
def index
raise Discourse::NotFound if SiteSetting.ads_txt.blank?
render plain: SiteSetting.ads_txt
end
end
+3 -34
View File
@@ -5,23 +5,11 @@ module ::AdPlugin
requires_plugin AdPlugin.plugin_name requires_plugin AdPlugin.plugin_name
def index def index
render_json_dump( render_json_dump(house_ads: HouseAd.all.map(&:to_hash), settings: HouseAdSetting.all)
house_ads:
HouseAd.all.map do |ad|
ad.to_hash.merge!(categories: Category.secured(@guardian).where(id: ad.category_ids))
end,
settings: HouseAdSetting.all,
)
end end
def show def show
house_ad_hash = HouseAd.find(params[:id])&.to_hash render_json_dump(house_ad: HouseAd.find(params[:id])&.to_hash)
if house_ad_hash
house_ad_hash.merge!(
categories: Category.secured(@guardian).where(id: house_ad_hash[:category_ids]),
)
end
render_json_dump(house_ad: house_ad_hash)
end end
def create def create
@@ -50,26 +38,7 @@ module ::AdPlugin
private private
def house_ad_params def house_ad_params
@permitted ||= params.permit(:id, :name, :html)
begin
permitted =
params.permit(
:id,
:name,
:html,
:visible_to_anons,
:visible_to_logged_in_users,
group_ids: [],
category_ids: [],
)
permitted[:visible_to_logged_in_users] = ActiveModel::Type::Boolean.new.cast(
permitted[:visible_to_logged_in_users],
)
permitted[:visible_to_anons] = ActiveModel::Type::Boolean.new.cast(
permitted[:visible_to_anons],
)
permitted
end
end end
end end
end end
+2 -59
View File
@@ -4,13 +4,7 @@ module ::AdPlugin
class HouseAd class HouseAd
include ActiveModel::Validations include ActiveModel::Validations
attr_accessor :id, attr_accessor :id, :name, :html
:name,
:html,
:visible_to_logged_in_users,
:visible_to_anons,
:category_ids,
:group_ids
NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i
@@ -26,10 +20,6 @@ module ::AdPlugin
def initialize def initialize
@name = "New Ad" @name = "New Ad"
@html = "<div class='house-ad'>New Ad</div>" @html = "<div class='house-ad'>New Ad</div>"
@visible_to_logged_in_users = true
@visible_to_anons = true
@group_ids = []
@category_ids = []
end end
def self.from_hash(h) def self.from_hash(h)
@@ -37,12 +27,6 @@ module ::AdPlugin
ad.name = h[:name] ad.name = h[:name]
ad.html = h[:html] ad.html = h[:html]
ad.id = h[:id].to_i if h[:id] ad.id = h[:id].to_i if h[:id]
ad.visible_to_logged_in_users = h[:visible_to_logged_in_users] if h.key?(
:visible_to_logged_in_users,
)
ad.visible_to_anons = h[:visible_to_anons] if h.key?(:visible_to_anons)
ad.group_ids = h[:group_ids].map(&:to_i) if h.key?(:group_ids)
ad.category_ids = h[:category_ids].map(&:to_i) if h.key?(:category_ids)
ad ad
end end
@@ -78,35 +62,10 @@ module ::AdPlugin
.sort_by { |ad| ad.id } .sort_by { |ad| ad.id }
end end
def self.all_for_anons
self.all.select(&:visible_to_anons)
end
def self.all_for_logged_in_users(scope)
if scope.nil?
# this is for our admin page, so we don't need to filter by group
self.all.select(&:visible_to_logged_in_users)
else
# otherwise, filter by group and visible categories
self.all.select do |ad|
(
ad.group_ids.any? { |group_id| scope.user.groups.pluck(:id).include?(group_id) } ||
ad.group_ids.include?(Group::AUTO_GROUPS[:everyone]) || ad.group_ids.empty?
) && ad.visible_to_logged_in_users &&
(
ad.category_ids.any? do |category_id|
Category.secured(scope).pluck(:id).include?(category_id)
end || true
)
end
end
end
def save def save
if self.valid? if self.valid?
self.id = self.class.alloc_id if self.id.to_i <= 0 self.id = self.class.alloc_id if self.id.to_i <= 0
AdPlugin.pstore_set("ad:#{id}", to_hash) AdPlugin.pstore_set("ad:#{id}", to_hash)
Site.clear_anon_cache!
self.class.publish_if_ads_enabled self.class.publish_if_ads_enabled
true true
else else
@@ -117,31 +76,15 @@ module ::AdPlugin
def update(attrs) def update(attrs)
self.name = attrs[:name] self.name = attrs[:name]
self.html = attrs[:html] self.html = attrs[:html]
self.visible_to_logged_in_users = attrs[:visible_to_logged_in_users] if attrs.key?(
:visible_to_logged_in_users,
)
self.visible_to_anons = attrs[:visible_to_anons] if attrs.key?(:visible_to_anons)
# ensure that group_ids and category_ids can be set to an empty array
self.group_ids = attrs.key?(:group_ids) ? attrs[:group_ids].map(&:to_i) : []
self.category_ids = attrs.key?(:category_ids) ? attrs[:category_ids].map(&:to_i) : []
self.save self.save
end end
def to_hash def to_hash
{ { id: @id, name: @name, html: @html }
id: @id,
name: @name,
html: @html,
visible_to_logged_in_users: @visible_to_logged_in_users,
visible_to_anons: @visible_to_anons,
group_ids: @group_ids,
category_ids: @category_ids,
}
end end
def destroy def destroy
AdPlugin.pstore_delete("ad:#{id}") AdPlugin.pstore_delete("ad:#{id}")
Site.clear_anon_cache!
self.class.publish_if_ads_enabled self.class.publish_if_ads_enabled
end end
+5 -18
View File
@@ -21,17 +21,10 @@ module ::AdPlugin
settings settings
end end
def self.settings_and_ads(for_anons: true, scope: nil) def self.settings_and_ads
settings = AdPlugin::HouseAdSetting.all settings = AdPlugin::HouseAdSetting.all
ad_names = settings.values.map { |v| v.split("|") }.flatten.uniq ad_names = settings.values.map { |v| v.split("|") }.flatten.uniq
ads = AdPlugin::HouseAd.all.select { |ad| ad_names.include?(ad.name) }
if for_anons
ads = AdPlugin::HouseAd.all_for_anons
else
ads = AdPlugin::HouseAd.all_for_logged_in_users(scope)
end
ads = ads.select { |ad| ad_names.include?(ad.name) }
{ {
settings: settings:
settings.merge( settings.merge(
@@ -41,14 +34,14 @@ module ::AdPlugin
), ),
creatives: creatives:
ads.inject({}) do |h, ad| ads.inject({}) do |h, ad|
h[ad.name] = { html: ad.html, category_ids: ad.category_ids } h[ad.name] = ad.html
h h
end, end,
} }
end end
def self.update(setting_name, value) def self.update(setting_name, value)
raise Discourse::NotFound if DEFAULTS.keys.exclude?(setting_name.to_sym) raise Discourse::NotFound unless DEFAULTS.keys.include?(setting_name.to_sym)
ad_names = value&.split("|") || [] ad_names = value&.split("|") || []
@@ -63,18 +56,12 @@ module ::AdPlugin
else else
AdPlugin.pstore_set("ad-setting:#{setting_name}", new_value) AdPlugin.pstore_set("ad-setting:#{setting_name}", new_value)
end end
Site.clear_anon_cache!
publish_settings publish_settings
end end
def self.publish_settings def self.publish_settings
MessageBus.publish("/site/house-creatives/anonymous", settings_and_ads(for_anons: true)) MessageBus.publish("/site/house-creatives", settings_and_ads)
MessageBus.publish(
"/site/house-creatives/logged-in",
settings_and_ads(for_anons: false),
group_ids: [Group::AUTO_GROUPS[:trust_level_0]],
)
end end
end end
end end
@@ -1,36 +1,34 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { inject as service } from "@ember/service";
import { alias, or } from "@ember/object/computed"; import { alias, or } from "@ember/object/computed";
import { service } from "@ember/service"; import discourseComputed from "discourse-common/utils/decorators";
import discourseComputed from "discourse/lib/decorators";
import { import {
isNthPost, isNthPost,
isNthTopicListItem, isNthTopicListItem,
} from "discourse/plugins/discourse-adplugin/discourse/helpers/slot-position"; } from "discourse/plugins/discourse-adplugin/discourse/helpers/slot-position";
export default class AdComponent extends Component { export default Component.extend({
@service router; router: service(),
@or( currentCategoryId: or(
"router.currentRoute.attributes.category.id", "router.currentRoute.attributes.category.id",
"router.currentRoute.parent.attributes.category_id" "router.currentRoute.parent.attributes.category_id"
) ),
currentCategoryId;
@or( currentCategorySlug: or(
"router.currentRoute.attributes.category.slug", "router.currentRoute.attributes.category.slug",
"router.currentRoute.parent.attributes.category.slug" "router.currentRoute.parent.attributes.category.slug"
) ),
currentCategorySlug;
// Server needs to compute this in case hidden tags are being used. // Server needs to compute this in case hidden tags are being used.
@alias("router.currentRoute.parent.attributes.tags_disable_ads") topicTagsDisableAds: alias(
topicTagsDisableAds; "router.currentRoute.parent.attributes.tags_disable_ads"
),
@or( isRestrictedCategory: or(
"router.currentRoute.attributes.category.read_restricted", "router.currentRoute.attributes.category.read_restricted",
"router.currentRoute.parent.attributes.category.read_restricted" "router.currentRoute.parent.attributes.category.read_restricted"
) ),
isRestrictedCategory;
@discourseComputed( @discourseComputed(
"router.currentRoute.attributes.__type", "router.currentRoute.attributes.__type",
@@ -40,21 +38,33 @@ export default class AdComponent extends Component {
if (type === "tag" && tag) { if (type === "tag" && tag) {
return tag; return tag;
} }
} },
@discourseComputed("router.currentRoute.parent.attributes.archetype") @discourseComputed("router.currentRoute.parent.attributes.archetype")
isPersonalMessage(topicType) { isPersonalMessage(topicType) {
return topicType === "private_message"; return topicType === "private_message";
} },
@discourseComputed @discourseComputed("currentUser.groups")
showToGroups() { showToGroups(groups) {
if (!this.currentUser) { const currentUser = this.currentUser;
if (
!currentUser ||
!groups ||
!this.siteSettings.no_ads_for_groups ||
this.siteSettings.no_ads_for_groups.length === 0
) {
return true; return true;
} }
return this.currentUser.show_to_groups; let noAdsGroups = this.siteSettings.no_ads_for_groups
} .split("|")
.filter(Boolean);
let currentGroups = groups.map((g) => g.id.toString());
return !currentGroups.any((g) => noAdsGroups.includes(g));
},
@discourseComputed( @discourseComputed(
"currentCategoryId", "currentCategoryId",
@@ -84,13 +94,13 @@ export default class AdComponent extends Component {
(!isRestrictedCategory || (!isRestrictedCategory ||
!this.siteSettings.no_ads_for_restricted_categories) !this.siteSettings.no_ads_for_restricted_categories)
); );
} },
isNthPost(n) { isNthPost(n) {
return isNthPost(n, this.get("postNumber")); return isNthPost(n, this.get("postNumber"));
} },
isNthTopicListItem(n) { isNthTopicListItem(n) {
return isNthTopicListItem(n, this.get("indexNumber")); return isNthTopicListItem(n, this.get("indexNumber"));
} },
} });

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