Author SHA1 Message Date
Kelvin Tan 23b8e3f607 DEV: Update to Font Awesome 6 icon names 2024-12-04 08:30:40 +08:00
David Taylor 7685ebf396 DEV: Update to native class syntax (#230) 2024-11-29 10:11:21 +00:00
David Taylor 4506b0b837 DEV: Update linting (#229) 2024-11-28 15:49:06 +01:00
David Taylor 0400165798 DEV: Update linting (#228) 2024-11-20 18:50:41 +01:00
David Taylor 119d331759 DEV: Update linting (#227) 2024-11-20 15:43:19 +01:00
dependabot[bot] e28f97a0f4 Bump cross-spawn from 7.0.3 to 7.0.6 (#226)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 10:37:59 +00:00
David Taylor 1d3baa5519 DEV: Update eslint config (#225) 2024-11-19 11:18:26 +01:00
Discourse Translator Bot 21de3a2b10 Update translations (#224) 2024-10-29 15:31:18 +01:00
dependabot[bot] c91c456187 Bump rexml from 3.3.6 to 3.3.9 (#223)
Bumps [rexml](https://github.com/ruby/rexml) from 3.3.6 to 3.3.9.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.3.6...v3.3.9)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-28 20:43:51 +01:00
Kelv e42b90507e DEV: Switch to use pnpm (#222) 2024-10-14 13:37:11 +02:00
dependabot[bot] 38354f2a2f Bump micromatch from 4.0.5 to 4.0.8 (#221)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-01 12:46:36 +02:00
dependabot[bot] fc109c75c4 Bump rexml from 3.3.3 to 3.3.6 (#219)
Bumps [rexml](https://github.com/ruby/rexml) from 3.3.3 to 3.3.6.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.3.3...v3.3.6)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-01 12:31:59 +02:00
Jarek Radosz 35cc34ab17 DEV: Update ace-editor usage (#220)
AceEditor is now a glimmer component (see: https://github.com/discourse/discourse/pull/28492) and it follows the "data down, actions up" pattern.
2024-08-27 13:53:15 +02:00
Discourse Translator Bot 95993e7d93 Update translations (#218) 2024-08-13 16:30:54 +02:00
dependabot[bot] 10093e0a9f Bump rexml from 3.2.8 to 3.3.3 (#217)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.8 to 3.3.3.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.8...v3.3.3)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 13:16:47 +02:00
Natalie Tay 02f602236a DEV: Pin plugin for Discourse < 3.4.0.beta1-dev (#216) 2024-08-02 17:03:18 +08:00
Discourse Translator Bot da38a4c053 Update translations (#215) 2024-07-31 00:13:50 +02:00
Arpit Jalan ec53a0d6b8 FIX: add a fix for cases where ad record is not found (#214) 2024-07-05 13:00:18 +05:30
Arpit Jalan 9b72130fdf FIX: show non-restricted ads instead of not showing ads at all (#213)
In some cases where there were category restricted house ads we were not
showing ads on reload. This commit filter out all the ads that should
not be shown on current page, leaving only allowed ads. So now we'll
show ads on every reload in all the cases.

Internal ticket: t130920
2024-06-27 22:13:54 +05:30
Discourse Translator Bot a38fbd0935 Update translations (#212) 2024-06-25 15:39:10 +02:00
dependabot[bot] ea9a0def55 Bump braces from 3.0.2 to 3.0.3 (#211)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-16 22:30:23 +02:00
Discourse Translator Bot 81843e8c98 Update translations (#210) 2024-06-04 21:43:55 +02:00
Discourse Translator Bot 114639d2e7 Update translations (#209) 2024-05-31 12:26:24 +02:00
Loïc Guitaut 5f1e07c556 DEV: Update rubocop-discourse to version 3.8.0 (#208) 2024-05-28 11:11:14 +02:00
dependabot[bot] f5c1b6da59 Bump rexml from 3.2.6 to 3.2.8 (#207)
Bumps [rexml](https://github.com/ruby/rexml) from 3.2.6 to 3.2.8.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](https://github.com/ruby/rexml/compare/v3.2.6...v3.2.8)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-16 23:54:19 +02:00
Discourse Translator Bot 6cd52a4966 Update translations (#206) 2024-04-16 17:37:40 +02:00
Isaac Janzen 554f03f3da FEATURE: Add group and category restrictions to house ads (#205)
# Description

This PR adds the ability to apply **group** and **category** restrictions to a **house ad**.

# What is included
- In order to get the group and category selectors to work within `admin/assets/javascripts/discourse/controllers/admin-plugins-house-ads-show.js` I needed to modernize the file. 
- I dropped the `bufferedProperty` implementation in favor of a vanilla ember approach
- I added `category_ids` and `group_ids` to our house ads model
- I added tests for group / category restrictions
- I added a preview button to display the house ad
- `/site.json` would return a object called `house_creatives` and a list of key value pairs that matched the ad name with the html, like so:
```js
{ AD_KEY: ad.html }
```
I need access to the category ids on the client to conditionally render the house ads so the new format will be: 
```js
{ AD_KEY: { html: ad.html, category_ids: ad.category_ids } }
```

# Screenshots
<img width="658" alt="Screenshot 2024-04-08 at 2 39 22 PM" src="https://github.com/discourse/discourse-adplugin/assets/50783505/b44b386d-65a1-4a2a-a487-d735b13357dd">

# Preview Video

https://github.com/discourse/discourse-adplugin/assets/50783505/6d0d8253-afef-4e15-b6fc-c6f696efd169
2024-04-09 11:54:11 -06:00
Loïc Guitaut c4227de1b5 DEV: Fix new Rubocop offenses 2024-03-05 18:12:49 +01:00
Selase Krakani 84d9b1a6da DEV: Pin plugin for Discourse < 3.3.0.beta1-dev (#203) 2024-02-29 13:48:36 -07:00
Discourse Translator Bot 062efc8a54 Update translations (#202) 2024-02-23 09:42:33 +01:00
David Taylor 32f18893ec FEATURE: Improve DFP / Ad-manager Content-Security-Policy compat (#201)
Switching to cross-domain iframes is recommended by google here: https://developers.google.com/publisher-tag/guides/content-security-policy
2024-02-19 12:35:22 +00:00
Blake Erickson b0c95114ea FIX: Calculate no ads for groups server side (#200)
If the selected group to not display ads to had its visibility set to
not be visible then this setting wouldn't work correctly because that
group wouldn't be available client side. The change moves that group
check to be server side so that we can correctly see all the groups that
should not see ads.
2024-02-15 14:52:15 -07:00
David Taylor 1677f7ae39 DEV: Pass post instead of transformedPost to post-bottom-ad (#199)
Changes in 0948d6b19e caused the post-bottom-ad component to start receiving the widget-ised 'transformedPost' instead of the original `post` model. In most cases this difference didn't matter, but it did cause noticable issues with `@model.category` and `@model.topic`.

This commit also deletes the unused `post-bottom/discourse-adplugin.hbs` connector. In the past, the adplugin itself was defining the post-bottom outlet. But now, we use RenderGlimmer to load the `post-bottom-ad` directly.
2024-02-14 13:59:36 +00:00
Discourse Translator Bot 751916b8e3 Update translations (#198) 2024-02-13 16:11:16 +01:00
Blake Erickson 9581367239 FIX: Add exclude groups for each ad platforms (#197)
With the new group system for displaying ads we no longer can check if a
user belongs to a trust level group lower than specified. The other
problem is that ALL users including staff and higher trust levels all
belong to trust level 0. So without this fix if we say that an ad should
be visible to trust level 0 users then it will be shown to all users.

This fix adds a new default setting for each ad platform for excluding
trust level 3, 4, and staff users from being shown ads.

- Make display_groups hidden (they will be removed in a later commit)
- Switch to using only exclude_groups instead of display groups and
  exclude groups
- rename showToDisplayGroups to showXAds for each provider
2024-02-08 19:42:40 -07:00
Blake Erickson 6b5412826f DEV: Update migration to match new site settings (#196)
Forgot to update these values in the previous PR.

Follow up to: https://github.com/discourse/discourse-adplugin/pull/195
2024-01-31 11:17:56 -07:00
Blake EricksonandMartin Brennan 6f8ff34ffa DEV: Convert TL settings to groups (#195)
* DEV: Convert TL settings to groups

This change converts the TL site settings in this plugin to use groups
instead.

See: https://meta.discourse.org/t/283408

Co-authored-by: Martin Brennan <[email protected]>
2024-01-31 11:04:24 -07:00
Discourse Translator Bot 19ac017dd4 Update translations (#194) 2024-01-16 14:54:13 +01:00
Jarek Radosz 429c9644de DEV: Update linting (#191) 2024-01-15 11:24:24 +00:00
David Taylor de7d8ce497 DEV: Resolve more link-to deprecations for ember upgrade (#193) 2024-01-09 20:07:04 +00:00
David Taylor 51af568c92 DEV: Resolve link-to deprecation for Ember upgrade (#192) 2023-12-13 14:59:32 +00:00
Martin BrennanandAlexDev 1d39cd8c80 Update plugin.rb metadata (#183)
Co-authored-by: AlexDev <[email protected]>
2023-11-08 11:16:53 -08:00
David Taylor 36f4ebc64b DEV: Abort placing adsense if Ember component is destroyed (#190)
This should avoid surprising error messages being printed to the console
2023-11-08 10:44:39 +11:00
David Taylor 789be84744 DEV: Log errors from adsense (#189) 2023-11-07 22:44:02 +00:00
David Taylor c88bb59d81 FIX: Fully re-render ads when navigating between pages (#188)
In the past, the ad plugin relied on two side-effects to achieve this behaviour:

1. Components being fully destroyed/rendered when navigating between pages. This stopped working when Discourse core moved to the more efficient 'loading slider' UI

2. The `listLoading` argument. This was an implementation detail of the old discovery routing infrastructure. Core recently overhauled this and removed the `listLoading` argument, because loading is now handled properly by the Ember router.

Instead of these two properties, we can use the `currentRoute` property of Ember's router service to trigger changes when navigating between pages. A common `{{#each` trick is used to fully destroy/re-render components even if the ad network is unchanged.
2023-11-07 21:12:30 +00:00
Discourse Translator Bot 5ad841de0b Update translations (#184) 2023-11-07 21:31:35 +01:00
David Taylor 0948d6b19e DEV: Refactor initializer to use RenderGlimmer and registerWidgetShim 2023-11-07 18:37:39 +00:00
David Taylor 3828578d5d DEV: Restructure file locations
- Colocate components / connectors
- Move admin UI files to `admin/assets` so they are only served to admins
2023-11-07 18:37:39 +00:00
137 changed files with 6194 additions and 4186 deletions
+3 -1
View File
@@ -1,2 +1,4 @@
< 3.4.0.beta2-dev: 7685ebf396c93e8accc5a76a81fcec4384a73fa3
< 3.4.0.beta1-dev: da38a4c053b68d7076db4441cfe86b473979945d
< 3.3.0.beta1-dev: 062efc8a547c19474eaeefae464837d63a5e5fb3
3.1.999: 30c49eb50d6d16fcf82bace3da0e1972b7b220c0
-1
View File
@@ -1 +0,0 @@
module.exports = require("@discourse/lint-configs/eslint");
+2
View File
@@ -0,0 +1,2 @@
engine-strict = true
auto-install-peers = false
+54 -21
View File
@@ -1,54 +1,87 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.3)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
ast (2.4.2)
json (2.6.3)
base64 (0.2.0)
bigdecimal (3.1.8)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
drb (2.2.1)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
json (2.7.2)
language_server-protocol (3.17.0.3)
parallel (1.23.0)
parser (3.2.2.4)
minitest (5.23.1)
mutex_m (0.2.0)
parallel (1.24.0)
parser (3.3.1.0)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.7.3)
racc (1.8.0)
rack (3.0.11)
rainbow (3.1.1)
regexp_parser (2.8.2)
rexml (3.2.6)
rubocop (1.57.2)
regexp_parser (2.9.2)
rexml (3.3.9)
rubocop (1.64.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.4)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-capybara (2.19.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-discourse (3.4.0)
rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0)
rubocop-factory_bot (2.24.0)
rubocop (~> 1.33)
rubocop-rspec (2.25.0)
rubocop-discourse (3.8.0)
activesupport (>= 6.1)
rubocop (>= 1.59.0)
rubocop-capybara (>= 2.0.0)
rubocop-factory_bot (>= 2.0.0)
rubocop-rails (>= 2.25.0)
rubocop-rspec (>= 2.25.0)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-rails (2.25.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (2.29.2)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
rubocop-rspec_rails (~> 2.28)
rubocop-rspec_rails (2.28.3)
rubocop (~> 1.40)
ruby-progressbar (1.13.0)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
PLATFORMS
arm64-darwin-21
x86_64-linux
ruby
DEPENDENCIES
rubocop-discourse
syntax_tree
BUNDLED WITH
2.4.21
2.5.10
@@ -0,0 +1,7 @@
import CategorySelector from "select-kit/components/category-selector";
export default class HouseAdsCategorySelector extends CategorySelector {
get value() {
return this.selectedCategories.map((c) => c.id);
}
}
@@ -1,42 +1,44 @@
import Ember from "ember";
import { action, computed } from "@ember/object";
import { classNames } from "@ember-decorators/component";
import { makeArray } from "discourse-common/lib/helpers";
import MultiSelectComponent from "select-kit/components/multi-select";
const { makeArray } = Ember;
import { computed } from "@ember/object";
export default MultiSelectComponent.extend({
classNames: ["house-ads-chooser"],
filterable: true,
filterPlaceholder: "admin.adplugin.house_ads.filter_placeholder",
tokenSeparator: "|",
allowCreate: false,
allowAny: false,
settingValue: "",
valueAttribute: null,
nameProperty: null,
@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;
value: computed("settingValue", function () {
@computed("settingValue")
get value() {
return this.settingValue
.toString()
.split(this.tokenSeparator)
.filter(Boolean);
}),
}
// TODO: kept for legacy, remove when Discourse is 2.5
mutateValues(values) {
this.set("settingValue", values.join(this.tokenSeparator));
},
}
computeValues() {
return this.settingValue.split(this.tokenSeparator).filter(Boolean);
},
}
content: computed("choices", function () {
@computed("choices")
get content() {
return makeArray(this.choices);
}),
}
actions: {
onChange(value) {
const settingValue = makeArray(value).join(this.tokenSeparator);
this.attrs.onChange && this.attrs.onChange(settingValue);
},
},
});
@action
onChange(value) {
const settingValue = makeArray(value).join(this.tokenSeparator);
this.onChange?.(settingValue);
}
}
@@ -1,13 +1,13 @@
<label for={{name}}>{{title}}</label>
<label for={{this.name}}>{{this.title}}</label>
{{house-ads-chooser
settingValue=adValue
choices=adNames
onChange=(action (mut adValue))
settingValue=this.adValue
choices=this.adNames
onChange=(action (mut this.adValue))
}}
<div class="setting-controls">
{{#if changed}}
{{#if this.changed}}
<DButton class="ok" @action={{action "save"}} @icon="check" />
<DButton class="cancel" @action={{action "cancel"}} @icon="times" />
{{/if}}
</div>
<p class="help">{{help}}</p>
<p class="help">{{this.help}}</p>
@@ -1,7 +1,8 @@
import { mapBy } from "@ember/object/computed";
import { classNames } from "@ember-decorators/component";
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: mapBy("allAds", "name"),
});
@classNames("house-ads-setting house-ads-list-setting")
export default class HouseAdsListSetting extends HouseAdsSetting {
@mapBy("allAds", "name") adNames;
}
@@ -1,9 +1,9 @@
<label for={{name}}>{{title}}</label>
<TextField @value={{adValue}} @classNames="house-ads-text-input" />
<label for={{this.name}}>{{this.title}}</label>
<TextField @value={{this.adValue}} @classNames="house-ads-text-input" />
<div class="setting-controls">
{{#if changed}}
{{#if this.changed}}
<DButton class="ok" @action={{action "save"}} @icon="check" />
<DButton class="cancel" @action={{action "cancel"}} @icon="times" />
{{/if}}
</div>
<p class="help">{{help}}</p>
<p class="help">{{this.help}}</p>
@@ -1,58 +1,58 @@
import Component from "@ember/component";
import { action } from "@ember/object";
import { classNames } from "@ember-decorators/component";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { i18n, propertyNotEqual } from "discourse/lib/computed";
import I18n from "I18n";
export default 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"),
@classNames("house-ads-setting")
export default class HouseAdsSetting extends Component {
adValue = "";
saving = false;
savingStatus = "";
@i18n("name", "admin.adplugin.house_ads.%@.title") title;
@i18n("name", "admin.adplugin.house_ads.%@.description") help;
@propertyNotEqual("adValue", "value") changed;
init() {
this._super(...arguments);
super.init(...arguments);
this.set("adValue", this.get("value"));
},
}
actions: {
save() {
if (!this.get("saving")) {
this.setProperties({
saving: true,
savingStatus: I18n.t("saving"),
});
@action
save() {
if (!this.get("saving")) {
this.setProperties({
saving: true,
savingStatus: I18n.t("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.t("saved"),
});
})
.catch(popupAjaxError)
.finally(() => {
this.setProperties({
saving: false,
savingStatus: "",
});
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.t("saved"),
});
}
},
})
.catch(popupAjaxError)
.finally(() => {
this.setProperties({
saving: false,
savingStatus: "",
});
});
}
}
cancel() {
this.set("adValue", this.get("value"));
},
},
});
@action
cancel() {
this.set("adValue", this.get("value"));
}
}
@@ -0,0 +1,18 @@
import { htmlSafe } from "@ember/template";
import DModal from "discourse/components/d-modal";
import i18n from "discourse-common/helpers/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,8 +1,8 @@
import Controller, { inject as controller } from "@ember/controller";
import { alias } from "@ember/object/computed";
export default Controller.extend({
adminPluginsHouseAds: controller("adminPlugins.houseAds"),
houseAds: alias("adminPluginsHouseAds.model"),
adSettings: alias("adminPluginsHouseAds.houseAdsSettings"),
});
export default class AdminPluginsHouseAdsIndexController extends Controller {
@controller("adminPlugins.houseAds") adminPluginsHouseAds;
@alias("adminPluginsHouseAds.model") houseAds;
@alias("adminPluginsHouseAds.houseAdsSettings") adSettings;
}
@@ -1,113 +1,156 @@
import { tracked } from "@glimmer/tracking";
import Controller, { inject as controller } from "@ember/controller";
import { not, or } from "@ember/object/computed";
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 { propertyNotEqual } from "discourse/lib/computed";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import Category from "discourse/models/category";
import I18n from "I18n";
import Preview from "../components/modal/preview";
export default Controller.extend(bufferedProperty("model"), {
adminPluginsHouseAds: controller("adminPlugins.houseAds"),
export default class adminPluginsHouseAdsShow extends Controller {
@service router;
@service modal;
saving: false,
savingStatus: "",
@controller("adminPlugins.houseAds") houseAdsController;
nameDirty: propertyNotEqual("buffered.name", "model.name"),
htmlDirty: propertyNotEqual("buffered.html", "model.html"),
visibleToAnonsDirty: propertyNotEqual(
"buffered.visible_to_anons",
"model.visible_to_anons"
),
visibleToLoggedInDirty: propertyNotEqual(
"buffered.visible_to_logged_in_users",
"model.visible_to_logged_in_users"
),
dirty: or(
"nameDirty",
"htmlDirty",
"visibleToLoggedInDirty",
"visibleToAnonsDirty"
),
disableSave: not("dirty"),
@tracked selectedCategories = [];
@tracked selectedGroups = [];
@tracked saving = false;
@tracked savingStatus = "";
@tracked buffered;
actions: {
save() {
if (!this.get("saving")) {
this.setProperties({
saving: true,
savingStatus: I18n.t("saving"),
});
@observes("model")
modelChanged() {
this.buffered = new TrackedObject({ ...this.model });
this.selectedCategories = this.model.categories || [];
this.selectedGroups = this.model.group_ids || [];
}
const data = {},
buffered = this.get("buffered"),
newRecord = !buffered.get("id");
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;
}
if (!newRecord) {
data.id = buffered.get("id");
}
data.name = buffered.get("name");
data.html = buffered.get("html");
data.visible_to_logged_in_users = buffered.get(
"visible_to_logged_in_users"
);
data.visible_to_anons = buffered.get("visible_to_anons");
ajax(
@action
async save() {
if (!this.saving) {
this.saving = true;
this.savingStatus = I18n.t("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/${buffered.get("id")}`,
: `/admin/plugins/pluginad/house_creatives/${this.buffered.id}`,
{
type: newRecord ? "POST" : "PUT",
data,
}
);
this.savingStatus = I18n.t("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)
)
.then((ajaxData) => {
this.commitBuffer();
this.set("savingStatus", I18n.t("saved"));
if (newRecord) {
const model = this.get("model");
model.set("id", ajaxData.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/pluginad/house_creatives/${model.get("id")}`, {
type: "DELETE",
})
.then(() => {
houseAds.removeObject(model);
this.transitionToRoute("adminPlugins.houseAds.index");
})
.catch(popupAjaxError);
},
},
});
: [];
}
}
@@ -1,5 +1,5 @@
import Controller from "@ember/controller";
export default Controller.extend({
loadingAds: true,
});
export default class AdminPluginsHouseAdsController extends Controller {
loadingAds = true;
}
@@ -1,9 +1,12 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
export default class AdminPluginsHouseAdsIndex extends DiscourseRoute {
@service router;
@action
moreSettings() {
this.transitionTo("adminSiteSettingsCategory", "ad_plugin");
},
});
this.router.transitionTo("adminSiteSettingsCategory", "ad_plugin");
}
}
@@ -1,21 +1,23 @@
import EmberObject from "@ember/object";
import { TrackedObject } from "@ember-compat/tracked-built-ins";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
export default class AdminPluginsHouseAdsShow extends DiscourseRoute {
model(params) {
if (params.ad_id === "new") {
return EmberObject.create({
return new TrackedObject({
name: I18n.t("admin.adplugin.house_ads.new_name"),
html: "",
visible_to_logged_in_users: true,
visible_to_anons: true,
});
} else {
return this.modelFor("adminPlugins.houseAds").findBy(
"id",
parseInt(params.ad_id, 10)
return new TrackedObject(
this.modelFor("adminPlugins.houseAds").findBy(
"id",
parseInt(params.ad_id, 10)
)
);
}
},
});
}
}
@@ -2,15 +2,15 @@ import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
settings: null,
export default class AdminPluginsHouseAds extends DiscourseRoute {
settings = null;
model() {
return ajax("/admin/plugins/pluginad/house_creatives.json").then((data) => {
this.set("settings", EmberObject.create(data.settings));
return data.house_ads.map((ad) => EmberObject.create(ad));
});
},
}
setupController(controller, model) {
controller.setProperties({
@@ -18,5 +18,5 @@ export default DiscourseRoute.extend({
houseAdsSettings: this.get("settings"),
loadingAds: false,
});
},
});
}
}
@@ -1,51 +1,51 @@
<DSection @class="house-ads-settings content-body">
<section class="house-ads-settings content-body">
<div>{{i18n "admin.adplugin.house_ads.description"}}</div>
{{#if houseAds.length}}
{{#if this.houseAds.length}}
<form class="form-horizontal">
{{house-ads-list-setting
name="topic_list_top"
value=adSettings.topic_list_top
allAds=houseAds
adSettings=adSettings
value=this.adSettings.topic_list_top
allAds=this.houseAds
adSettings=this.adSettings
}}
{{house-ads-list-setting
name="topic_above_post_stream"
value=adSettings.topic_above_post_stream
allAds=houseAds
adSettings=adSettings
value=this.adSettings.topic_above_post_stream
allAds=this.houseAds
adSettings=this.adSettings
}}
{{house-ads-list-setting
name="topic_above_suggested"
value=adSettings.topic_above_suggested
allAds=houseAds
adSettings=adSettings
value=this.adSettings.topic_above_suggested
allAds=this.houseAds
adSettings=this.adSettings
}}
{{house-ads-list-setting
name="post_bottom"
value=adSettings.post_bottom
allAds=houseAds
adSettings=adSettings
value=this.adSettings.post_bottom
allAds=this.houseAds
adSettings=this.adSettings
}}
{{house-ads-list-setting
name="topic_list_between"
value=adSettings.topic_list_between
allAds=houseAds
adSettings=adSettings
value=this.adSettings.topic_list_between
allAds=this.houseAds
adSettings=this.adSettings
}}
<DButton
@label="admin.adplugin.house_ads.more_settings"
@icon="cog"
class="btn-default"
@action={{route-action "moreSettings"}}
class="btn-default"
/>
</form>
{{else}}
<p>
{{#link-to "adminPlugins.houseAds.show" "new"}}
{{#link-to route="adminPlugins.houseAds.show" model="new"}}
{{i18n "admin.adplugin.house_ads.get_started"}}
{{/link-to}}
</p>
{{/if}}
</DSection>
</section>
@@ -1,15 +1,19 @@
<DSection @class="edit-house-ad content-body">
<h1><TextField @class="house-ad-name" @value={{buffered.name}} /></h1>
<section class="edit-house-ad content-body">
<h1><TextField @value={{this.buffered.name}} class="house-ad-name" /></h1>
<div class="controls">
<AceEditor @content={{buffered.html}} @mode="html" />
<AceEditor
@content={{this.buffered.html}}
@onChange={{fn (mut this.buffered.html)}}
@mode="html"
/>
</div>
<div class="controls">
<div class="visibility-settings">
<div>
<Input
class="visible-to-logged-in-checkbox"
@type="checkbox"
@checked={{this.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>
@@ -22,27 +26,53 @@
/>
<span>{{i18n "admin.adplugin.house_ads.show_to_anons"}}</span>
</div>
<HouseAdsCategorySelector
@categories={{this.site.categories}}
@selectedCategories={{this.selectedCategories}}
@onChange={{this.setCategoryIds}}
@options={{hash allowAny=true}}
class="house-ads-categories"
/>
<div class="description">
{{i18n "admin.adplugin.house_ads.category_chooser_description"}}
</div>
<GroupChooser
@content={{this.site.groups}}
@onChange={{this.setGroupIds}}
@value={{this.selectedGroups}}
class="banner-groups"
/>
<div class="description">
{{i18n "admin.adplugin.house_ads.group_chooser_description"}}
</div>
</div>
<DButton
@action={{action "save"}}
@disabled={{disableSave}}
class="btn-primary save-button"
@action={{this.save}}
@disabled={{this.disabledSave}}
@label="admin.adplugin.house_ads.save"
class="btn-primary save-button"
/>
{{#if saving}}
{{savingStatus}}
{{#if this.saving}}
{{this.savingStatus}}
{{else}}
{{#if dirty}}
<a href {{action "cancel"}}>{{i18n "cancel"}}</a>
{{/if}}
{{#unless this.disabledSave}}
<DButton @action={{this.cancel}} @label="cancel" />
{{/unless}}
{{/if}}
<DButton
@action={{action "destroy"}}
class="btn-danger delete-button"
@action={{this.openPreview}}
@label="admin.adplugin.house_ads.preview"
/>
<DButton
@action={{this.destroy}}
@label="admin.adplugin.house_ads.delete"
class="btn-danger delete-button"
/>
</div>
</DSection>
</section>
@@ -1,21 +1,25 @@
<div class="adplugin-mgmt">
<h1>{{i18n "admin.adplugin.house_ads.title"}}</h1>
{{#if model.length}}
{{#if this.model.length}}
<div class="content-list">
<div class="house-ads-actions">
{{#link-to "adminPlugins.houseAds.show" "new" class="btn btn-primary"}}
<LinkTo
@route="adminPlugins.houseAds.show"
@model="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"}}
</LinkTo>
<LinkTo @route="adminPlugins.houseAds.index" class="btn btn-default">
{{d-icon "gear"}}
<span>{{i18n "admin.adplugin.house_ads.settings"}}</span>
{{/link-to}}
</LinkTo>
</div>
<ul class="house-ads-list">
{{#each model as |ad|}}
{{#each this.model as |ad|}}
<li class="house-ads-list-item">
{{#link-to "adminPlugins.houseAds.show" ad.id}}
{{#link-to route="adminPlugins.houseAds.show" model=ad.id}}
{{ad.name}}
{{/link-to}}
</li>
+13
View File
@@ -0,0 +1,13 @@
# 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
+23 -3
View File
@@ -5,11 +5,23 @@ module ::AdPlugin
requires_plugin AdPlugin.plugin_name
def index
render_json_dump(house_ads: HouseAd.all.map(&:to_hash), settings: HouseAdSetting.all)
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,
)
end
def show
render_json_dump(house_ad: HouseAd.find(params[:id])&.to_hash)
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)
end
def create
@@ -41,7 +53,15 @@ module ::AdPlugin
@permitted ||=
begin
permitted =
params.permit(:id, :name, :html, :visible_to_anons, :visible_to_logged_in_users)
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],
)
+40 -9
View File
@@ -4,7 +4,13 @@ module ::AdPlugin
class HouseAd
include ActiveModel::Validations
attr_accessor :id, :name, :html, :visible_to_logged_in_users, :visible_to_anons
attr_accessor :id,
:name,
:html,
:visible_to_logged_in_users,
:visible_to_anons,
:category_ids,
:group_ids
NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i
@@ -22,6 +28,8 @@ module ::AdPlugin
@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)
@@ -29,10 +37,12 @@ module ::AdPlugin
ad.name = h[:name]
ad.html = h[:html]
ad.id = h[:id].to_i if h[:id]
if h.key?(:visible_to_logged_in_users)
ad.visible_to_logged_in_users = h[:visible_to_logged_in_users]
end
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
@@ -72,8 +82,24 @@ module ::AdPlugin
self.all.select(&:visible_to_anons)
end
def self.all_for_logged_in_users
self.all.select(&:visible_to_logged_in_users)
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
@@ -91,10 +117,13 @@ module ::AdPlugin
def update(attrs)
self.name = attrs[:name]
self.html = attrs[:html]
if attrs.key?(:visible_to_logged_in_users)
self.visible_to_logged_in_users = attrs[:visible_to_logged_in_users]
end
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
@@ -105,6 +134,8 @@ module ::AdPlugin
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
+4 -4
View File
@@ -21,14 +21,14 @@ module ::AdPlugin
settings
end
def self.settings_and_ads(for_anons: true)
def self.settings_and_ads(for_anons: true, scope: nil)
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
ads = AdPlugin::HouseAd.all_for_logged_in_users(scope)
end
ads = ads.select { |ad| ad_names.include?(ad.name) }
@@ -41,14 +41,14 @@ module ::AdPlugin
),
creatives:
ads.inject({}) do |h, ad|
h[ad.name] = ad.html
h[ad.name] = { html: ad.html, category_ids: ad.category_ids }
h
end,
}
end
def self.update(setting_name, value)
raise Discourse::NotFound unless DEFAULTS.keys.include?(setting_name.to_sym)
raise Discourse::NotFound if DEFAULTS.keys.exclude?(setting_name.to_sym)
ad_names = value&.split("|") || []
@@ -1,34 +1,36 @@
import Component from "@ember/component";
import { alias, or } from "@ember/object/computed";
import { inject as service } from "@ember/service";
import { service } from "@ember/service";
import discourseComputed from "discourse-common/utils/decorators";
import {
isNthPost,
isNthTopicListItem,
} from "discourse/plugins/discourse-adplugin/discourse/helpers/slot-position";
export default Component.extend({
router: service(),
export default class AdComponent extends Component {
@service router;
currentCategoryId: or(
@or(
"router.currentRoute.attributes.category.id",
"router.currentRoute.parent.attributes.category_id"
),
)
currentCategoryId;
currentCategorySlug: or(
@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.
topicTagsDisableAds: alias(
"router.currentRoute.parent.attributes.tags_disable_ads"
),
@alias("router.currentRoute.parent.attributes.tags_disable_ads")
topicTagsDisableAds;
isRestrictedCategory: or(
@or(
"router.currentRoute.attributes.category.read_restricted",
"router.currentRoute.parent.attributes.category.read_restricted"
),
)
isRestrictedCategory;
@discourseComputed(
"router.currentRoute.attributes.__type",
@@ -38,33 +40,21 @@ export default Component.extend({
if (type === "tag" && tag) {
return tag;
}
},
}
@discourseComputed("router.currentRoute.parent.attributes.archetype")
isPersonalMessage(topicType) {
return topicType === "private_message";
},
}
@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
) {
@discourseComputed
showToGroups() {
if (!this.currentUser) {
return true;
}
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));
},
return this.currentUser.show_to_groups;
}
@discourseComputed(
"currentCategoryId",
@@ -94,13 +84,13 @@ export default Component.extend({
(!isRestrictedCategory ||
!this.siteSettings.no_ads_for_restricted_categories)
);
},
}
isNthPost(n) {
return isNthPost(n, this.get("postNumber"));
},
}
isNthTopicListItem(n) {
return isNthTopicListItem(n, this.get("indexNumber"));
},
});
}
}
@@ -1,12 +1,13 @@
{{#each adComponents as |adComponent|}}
{{component
adComponent
placement=placement
refreshOnChange=refreshOnChange
category=category
listLoading=listLoading
postNumber=postNumber
indexNumber=indexNumber
tagName=childTagName
}}
{{#each this.adComponents as |adComponent|}}
{{! Trick to force full destroy/re-render of component when route changes }}
{{#each (array this.router.currentRoute)}}
{{component
adComponent
placement=this.placement
category=this.category
postNumber=this.postNumber
indexNumber=this.indexNumber
tagName=this.childTagName
}}
{{/each}}
{{/each}}
@@ -1,6 +1,8 @@
import EmberObject from "@ember/object";
import { service } from "@ember/service";
import { isBlank } from "@ember/utils";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { tagName } from "@ember-decorators/component";
import discourseComputed from "discourse-common/utils/decorators";
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
import {
isNthPost,
@@ -172,9 +174,9 @@ export function slotContenders(
return types;
}
export default AdComponent.extend({
needsUpdate: false,
tagName: "",
@tagName("")
export default class AdSlot extends AdComponent {
@service router;
/**
* For a given ad placement and optionally a post number if in between posts,
@@ -189,29 +191,16 @@ export default AdComponent.extend({
indexNumber,
postNumber
);
},
/**
* 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.
*
* Depends on `router.currentRoute` so that we refresh ads when navigating around.
*/
@discourseComputed("placement", "availableAdTypes", "needsUpdate")
@discourseComputed("placement", "availableAdTypes", "router.currentRoute")
adComponents(placement, availableAdTypes) {
if (
!availableAdTypes.includes("house-ad") ||
@@ -250,5 +239,5 @@ export default AdComponent.extend({
}
return networkNames;
},
});
}
}
@@ -1,3 +1,3 @@
{{#if showAd}}
<div id={{divId}} class={{className}}></div>
{{#if this.showAd}}
<div id={{this.divId}} class={{this.className}}></div>
{{/if}}
@@ -2,7 +2,7 @@ import { scheduleOnce } from "@ember/runloop";
import RSVP from "rsvp";
import loadScript from "discourse/lib/load-script";
import { isTesting } from "discourse-common/config/environment";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import AdComponent from "discourse/plugins/discourse-adplugin/discourse/components/ad-component";
let _loaded = false,
@@ -27,9 +27,9 @@ function loadAdbutler(adserverHostname) {
return _promise;
}
export default AdComponent.extend({
divs: null,
publisherId: null,
export default class AdbutlerAd extends AdComponent {
divs = null;
publisherId = null;
init() {
let dimensions = [728, 90];
@@ -70,8 +70,8 @@ export default AdComponent.extend({
dimensions,
});
this._super();
},
super.init();
}
_triggerAds() {
if (isTesting()) {
@@ -104,52 +104,44 @@ export default AdComponent.extend({
}
}.bind(this)
);
},
}
didInsertElement() {
this._super();
super.didInsertElement();
scheduleOnce("afterRender", this, this._triggerAds);
},
}
@observes("listLoading")
waitForLoad() {
if (this.get("adRequested")) {
return;
} // already requested that this ad unit be populated
if (!this.get("listLoading")) {
scheduleOnce("afterRender", this, this._triggerAds);
@discourseComputed
showAdbutlerAds() {
if (!this.currentUser) {
return true;
}
},
@discourseComputed("currentUser.trust_level")
showToTrustLevel(trustLevel) {
return !(
trustLevel && trustLevel > this.siteSettings.adbutler_through_trust_level
);
},
return this.currentUser.show_adbutler_ads;
}
@discourseComputed(
"publisherId",
"showToTrustLevel",
"showAdbutlerAds",
"showToGroups",
"showAfterPost",
"showOnCurrentPage"
)
showAd(
publisherId,
showToTrustLevel,
showAdbutlerAds,
showToGroups,
showAfterPost,
showOnCurrentPage
) {
return (
publisherId &&
showToTrustLevel &&
showAdbutlerAds &&
showToGroups &&
showAfterPost &&
showOnCurrentPage
);
},
}
@discourseComputed("postNumber")
showAfterPost(postNumber) {
@@ -157,5 +149,5 @@ export default AdComponent.extend({
return true;
}
return this.isNthPost(parseInt(this.siteSettings.adbutler_nth_post, 10));
},
});
}
}

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