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:
rubocop-discourse: stree-compat.yml
RSpec/NamedSubject:
Enabled: false
+1 -1
View File
@@ -1,2 +1,2 @@
--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
remote: https://rubygems.org/
specs:
activesupport (8.0.2)
base64
benchmark (>= 0.3)
bigdecimal
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.2)
json (2.6.2)
parallel (1.22.1)
parser (3.1.2.1)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
prism (1.4.0)
racc (1.8.1)
rack (3.1.16)
prettier_print (1.2.0)
rainbow (3.1.1)
regexp_parser (2.10.0)
rubocop (1.75.8)
regexp_parser (2.6.0)
rexml (3.2.5)
rubocop (1.36.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.44.0, < 2.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.20.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.44.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-discourse (3.12.1)
activesupport (>= 6.1)
lint_roller (>= 1.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)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.21.0)
parser (>= 3.1.1.0)
rubocop-discourse (3.0)
rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0)
rubocop-rspec (2.13.2)
rubocop (~> 1.33)
ruby-progressbar (1.11.0)
syntax_tree (5.1.0)
prettier_print (>= 1.2.0)
tzinfo (2.0.6)
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)
unicode-display_width (2.3.0)
PLATFORMS
ruby
@@ -99,4 +40,4 @@ DEPENDENCIES
syntax_tree
BUNDLED WITH
2.6.9
2.1.4
+2 -3
View File
@@ -1,8 +1,5 @@
# 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
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
![adpluginexample](https://user-images.githubusercontent.com/5862206/214489455-234a3812-3606-47bf-9e49-cdaa3d1e3b9c.png)
##### AdButler Ads Zone URL to Discourse's Site Settings
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
def index
render_json_dump(
house_ads:
HouseAd.all.map do |ad|
ad.to_hash.merge!(categories: Category.secured(@guardian).where(id: ad.category_ids))
end,
settings: HouseAdSetting.all,
)
render_json_dump(house_ads: HouseAd.all.map(&:to_hash), settings: HouseAdSetting.all)
end
def show
house_ad_hash = 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)
render_json_dump(house_ad: HouseAd.find(params[:id])&.to_hash)
end
def create
@@ -50,26 +38,7 @@ module ::AdPlugin
private
def house_ad_params
@permitted ||=
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
params.permit(:id, :name, :html)
end
end
end
+2 -59
View File
@@ -4,13 +4,7 @@ module ::AdPlugin
class HouseAd
include ActiveModel::Validations
attr_accessor :id,
:name,
:html,
:visible_to_logged_in_users,
:visible_to_anons,
:category_ids,
:group_ids
attr_accessor :id, :name, :html
NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i
@@ -26,10 +20,6 @@ module ::AdPlugin
def initialize
@name = "New Ad"
@html = "<div class='house-ad'>New Ad</div>"
@visible_to_logged_in_users = true
@visible_to_anons = true
@group_ids = []
@category_ids = []
end
def self.from_hash(h)
@@ -37,12 +27,6 @@ module ::AdPlugin
ad.name = h[:name]
ad.html = h[:html]
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
end
@@ -78,35 +62,10 @@ module ::AdPlugin
.sort_by { |ad| ad.id }
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
if self.valid?
self.id = self.class.alloc_id if self.id.to_i <= 0
AdPlugin.pstore_set("ad:#{id}", to_hash)
Site.clear_anon_cache!
self.class.publish_if_ads_enabled
true
else
@@ -117,31 +76,15 @@ module ::AdPlugin
def update(attrs)
self.name = attrs[:name]
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
end
def to_hash
{
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,
}
{ id: @id, name: @name, html: @html }
end
def destroy
AdPlugin.pstore_delete("ad:#{id}")
Site.clear_anon_cache!
self.class.publish_if_ads_enabled
end
+5 -18
View File
@@ -21,17 +21,10 @@ module ::AdPlugin
settings
end
def self.settings_and_ads(for_anons: true, scope: nil)
def self.settings_and_ads
settings = AdPlugin::HouseAdSetting.all
ad_names = settings.values.map { |v| v.split("|") }.flatten.uniq
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) }
ads = AdPlugin::HouseAd.all.select { |ad| ad_names.include?(ad.name) }
{
settings:
settings.merge(
@@ -41,14 +34,14 @@ module ::AdPlugin
),
creatives:
ads.inject({}) do |h, ad|
h[ad.name] = { html: ad.html, category_ids: ad.category_ids }
h[ad.name] = ad.html
h
end,
}
end
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("|") || []
@@ -63,18 +56,12 @@ module ::AdPlugin
else
AdPlugin.pstore_set("ad-setting:#{setting_name}", new_value)
end
Site.clear_anon_cache!
publish_settings
end
def self.publish_settings
MessageBus.publish("/site/house-creatives/anonymous", settings_and_ads(for_anons: true))
MessageBus.publish(
"/site/house-creatives/logged-in",
settings_and_ads(for_anons: false),
group_ids: [Group::AUTO_GROUPS[:trust_level_0]],
)
MessageBus.publish("/site/house-creatives", settings_and_ads)
end
end
end
@@ -1,36 +1,34 @@
import Component from "@ember/component";
import { inject as service } from "@ember/service";
import { alias, or } from "@ember/object/computed";
import { service } from "@ember/service";
import discourseComputed from "discourse/lib/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import {
isNthPost,
isNthTopicListItem,
} from "discourse/plugins/discourse-adplugin/discourse/helpers/slot-position";
export default class AdComponent extends Component {
@service router;
export default Component.extend({
router: service(),
@or(
currentCategoryId: or(
"router.currentRoute.attributes.category.id",
"router.currentRoute.parent.attributes.category_id"
)
currentCategoryId;
),
@or(
currentCategorySlug: or(
"router.currentRoute.attributes.category.slug",
"router.currentRoute.parent.attributes.category.slug"
)
currentCategorySlug;
),
// Server needs to compute this in case hidden tags are being used.
@alias("router.currentRoute.parent.attributes.tags_disable_ads")
topicTagsDisableAds;
topicTagsDisableAds: alias(
"router.currentRoute.parent.attributes.tags_disable_ads"
),
@or(
isRestrictedCategory: or(
"router.currentRoute.attributes.category.read_restricted",
"router.currentRoute.parent.attributes.category.read_restricted"
)
isRestrictedCategory;
),
@discourseComputed(
"router.currentRoute.attributes.__type",
@@ -40,21 +38,33 @@ export default class AdComponent extends Component {
if (type === "tag" && tag) {
return tag;
}
}
},
@discourseComputed("router.currentRoute.parent.attributes.archetype")
isPersonalMessage(topicType) {
return topicType === "private_message";
}
},
@discourseComputed
showToGroups() {
if (!this.currentUser) {
@discourseComputed("currentUser.groups")
showToGroups(groups) {
const currentUser = this.currentUser;
if (
!currentUser ||
!groups ||
!this.siteSettings.no_ads_for_groups ||
this.siteSettings.no_ads_for_groups.length === 0
) {
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(
"currentCategoryId",
@@ -84,13 +94,13 @@ export default class AdComponent extends Component {
(!isRestrictedCategory ||
!this.siteSettings.no_ads_for_restricted_categories)
);
}
},
isNthPost(n) {
return isNthPost(n, this.get("postNumber"));
}
},
isNthTopicListItem(n) {
return isNthTopicListItem(n, this.get("indexNumber"));
}
}
},
});

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