1 Commits
Author SHA1 Message Date
Neil Lalonde d3025a0e56 test widget in post-bottom placement 2019-05-10 15:08:28 -04:00
207 changed files with 3423 additions and 9542 deletions
-2
View File
@@ -1,2 +0,0 @@
3.1.999: 30c49eb50d6d16fcf82bace3da0e1972b7b220c0
+105
View File
@@ -0,0 +1,105 @@
{
"env": {
"jasmine": true,
"node": true,
"mocha": true,
"browser": true,
"builtin": true
},
"parserOptions": {
"ecmaVersion": 7,
"sourceType": "module"
},
"globals":
{"Ember":true,
"jQuery":true,
"$":true,
"RSVP":true,
"Discourse":true,
"Em":true,
"PreloadStore":true,
"Handlebars":true,
"I18n":true,
"bootbox":true,
"module":true,
"moduleFor":true,
"moduleForComponent":true,
"Pretender":true,
"sandbox":true,
"controllerFor":true,
"test":true,
"ok":true,
"not":true,
"expect":true,
"equal":true,
"visit":true,
"andThen":true,
"click":true,
"currentPath":true,
"currentRouteName":true,
"currentURL":true,
"fillIn":true,
"keyEvent":true,
"triggerEvent":true,
"count":true,
"exists":true,
"visible":true,
"invisible":true,
"asyncRender":true,
"selectDropdown":true,
"asyncTestDiscourse":true,
"fixture":true,
"find":true,
"sinon":true,
"moment":true,
"start":true,
"_":true,
"alert":true,
"containsInstance":true,
"deepEqual":true,
"notEqual":true,
"define":true,
"require":true,
"requirejs":true,
"hasModule":true,
"Blob":true,
"File":true},
"rules": {
"block-scoped-var": 2,
"dot-notation": 0,
"eqeqeq": [
2,
"allow-null"
],
"guard-for-in": 2,
"no-bitwise": 2,
"no-caller": 2,
"no-cond-assign": 0,
"no-debugger": 2,
"no-empty": 0,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-parens": 0,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-loop-func": 2,
"no-multi-str": 2,
"no-new": 2,
"no-plusplus": 0,
"no-proto": 2,
"no-script-url": 2,
"no-sequences": 2,
"no-shadow": 2,
"no-undef": 2,
"no-unused-vars": 2,
"no-with": 2,
"semi": 2,
"strict": 0,
"valid-typeof": 2,
"wrap-iife": [
2,
"inside"
]
},
"parser": "babel-eslint"
}
-1
View File
@@ -1 +0,0 @@
module.exports = require("@discourse/lint-configs/eslint");
-11
View File
@@ -1,11 +0,0 @@
name: Discourse Plugin
on:
push:
branches:
- main
pull_request:
jobs:
ci:
uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1
+2 -3
View File
@@ -2,8 +2,7 @@
log/*.log log/*.log
pkg/ pkg/
auto_generated auto_generated
Gemfile.lock
.DS_Store .DS_Store
*.swp *.swp
node_modules .rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml
yarn-error.log
.rubocop-https---raw-githubusercontent-com-discourse-*
-1
View File
@@ -1 +0,0 @@
module.exports = require("@discourse/lint-configs/prettier");
+1 -5
View File
@@ -1,5 +1 @@
inherit_gem: inherit_from: https://raw.githubusercontent.com/discourse/discourse/master/.rubocop.yml
rubocop-discourse: stree-compat.yml
RSpec/NamedSubject:
Enabled: false
-2
View File
@@ -1,2 +0,0 @@
--print-width=100
--plugins=plugin/trailing_comma,plugin/disable_auto_ternary
-1
View File
@@ -1 +0,0 @@
module.exports = require("@discourse/lint-configs/template-lint");
+15
View File
@@ -0,0 +1,15 @@
[main]
host = https://www.transifex.com
lang_map = el_GR: el, es_ES: es, fr_FR: fr, hu_HU: hu, ko_KR: ko, pt_PT: pt, sk_SK: sk, vi_VN: vi
[discourse-org.discourse-adpluginclientenyml]
file_filter = config/locales/client.<lang>.yml
source_file = config/locales/client.en.yml
source_lang = en
type = YML
[discourse-org.discourse-adpluginserverenyml]
file_filter = config/locales/server.<lang>.yml
source_file = config/locales/server.en.yml
source_lang = en
type = YML
+2 -5
View File
@@ -1,8 +1,5 @@
# frozen_string_literal: true source 'https://rubygems.org'
source "https://rubygems.org"
group :development do group :development do
gem "rubocop-discourse" gem 'translations-manager', git: 'https://github.com/discourse/translations-manager.git'
gem "syntax_tree"
end end
-54
View File
@@ -1,54 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)
json (2.6.3)
language_server-protocol (3.17.0.3)
parallel (1.23.0)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
prettier_print (1.2.1)
racc (1.7.3)
rainbow (3.1.1)
regexp_parser (2.8.2)
rexml (3.2.6)
rubocop (1.57.2)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.4)
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)
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 (~> 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 (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
unicode-display_width (2.5.0)
PLATFORMS
arm64-darwin-21
x86_64-linux
DEPENDENCIES
rubocop-discourse
syntax_tree
BUNDLED WITH
2.4.21
+39 -56
View File
@@ -1,12 +1,10 @@
# Official Discourse Advertising Plugin # Official Discourse Advertising Plugin
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.
**Authors**: [Sarah Ni](https://github.com/cyberkoi) & [Vi Nguyen](https://github.com/ladydanger) **Authors**: [Sarah Ni](https://github.com/cyberkoi) & [Vi Nguyen](https://github.com/ladydanger)
**Version**: 1.2.5 **Version**: 1.2.0
**Contributors**: See [credits section](#credits) below **Contributors**: See [credits section](#credits) below
@@ -19,8 +17,8 @@ This is the official Discourse advertising plugin. It allows advertisements to
* [Google Adsense](http://www.google.com.au/adsense/start/why-adsense.html) * [Google Adsense](http://www.google.com.au/adsense/start/why-adsense.html)
* [Google Double Click for Publishers](https://www.google.com/dfp) * [Google Double Click for Publishers](https://www.google.com/dfp)
* [Amazon Affiliates](http://affiliate-program.amazon.com) - Banner and Product Link Ads * [Amazon Affiliates](http://affiliate-program.amazon.com) - Banner and Product Link Ads
* [CodeFund](https://codefund.io) - Ethical Ad Platform for Developers
* [Carbon Ads](https://www.carbonads.net/) * [Carbon Ads](https://www.carbonads.net/)
* [AdButler](https://www.adbutler.com/)
## Quick Start in 3 Steps ## Quick Start in 3 Steps
@@ -36,7 +34,7 @@ For non-docker or local development installation (those with programming experie
### Step 1 - Install the Official Discourse Advertising Plugin ### Step 1 - Install the Official Discourse Advertising Plugin
As seen in a [how-to on meta.discourse.org](https://meta.discourse.org/t/install-plugins-in-discourse/19157), simply **add the plugin's repository url to your container's app.yml file**: As seen in a [how-to on meta.discourse.org](https://meta.discourse.org/t/advanced-troubleshooting-with-docker/15927#Example:%20Install%20a%20plugin), simply **add the plugin's repository url to your container's app.yml file**:
```yml ```yml
hooks: hooks:
@@ -81,8 +79,8 @@ the ad slots. The ads will start showing as soon as you add them to slots.
<li>House Ads - if you want to create and serve ads from your Discourse instance.</li> <li>House Ads - if you want to create and serve ads from your Discourse instance.</li>
<li>Adsense - if using Adsense as your advertisement platform.</li> <li>Adsense - if using Adsense as your advertisement platform.</li>
<li>DFP - if using the DoubleClick for Publishers advertisement platform.</li> <li>DFP - if using the DoubleClick for Publishers advertisement platform.</li>
<li>CodeFund - if using the CodeFund ethical advertisement platform.</li>
<li>Carbon Ads - if using the Carbon Ads advertisement platform.</li> <li>Carbon Ads - if using the Carbon Ads advertisement platform.</li>
<li>AdButler - if using the AdButler advertisement platform.</li>
</ul> </ul>
</ul> </ul>
@@ -96,47 +94,24 @@ the ad slots. The ads will start showing as soon as you add them to slots.
##### Adsense Advertisement Tag to Discourse's Site Settings ##### Adsense Advertisement Tag to Discourse's Site Settings
![image](https://user-images.githubusercontent.com/5862206/214489271-32ce230f-9f79-441e-9d7a-c5488ebb8c8a.png) ![](https://www.dropbox.com/sc/pguxq17zo2rovyd/AAD--LTH_IIgVhgczoaY1Ljva?dl=1)
##### DoubleClick for Publishers' Advertisement Tag to Discourse's Site Settings ##### DoubleClick for Publishers' Advertisement Tag to Discourse's Site Settings
![image](https://user-images.githubusercontent.com/5862206/214489324-8554a996-1876-432b-a686-e1add3b96ec4.png) ![](https://www.dropbox.com/sc/0inoc1iduux0gsf/AADi8tfKX9S6Tx9S8RndcUE8a?dl=1)
##### Amazon Affiliates' Advertisement Tag to Discourse's Site Settings ##### Amazon Affiliates' Advertisement Tag to Discourse's Site Settings
Only for Product Link and Banner Ads. Only for Product Link and Banner Ads.
![image](https://user-images.githubusercontent.com/5862206/214489360-8829291b-8571-4deb-a867-139e823ebdea.png) ![](https://www.dropbox.com/sc/l67fb5c3tl8bq3d/AAAAMmccMW3kkIeBR7cBdWoFa?dl=1)
##### CodeFund Embed Tag to Discourse's Site Settings
![CodeFund Instructions](https://s3-us-west-2.amazonaws.com/codesponsor/discourse-codefund-instructions.png)
##### Carbon Ads Script Tag to Discourse's Site Settings ##### Carbon Ads Script Tag to Discourse's Site Settings
![adpluginexample](https://user-images.githubusercontent.com/5862206/214489455-234a3812-3606-47bf-9e49-cdaa3d1e3b9c.png) ![Carbon Ads](https://d11a6trkgmumsb.cloudfront.net/original/3X/3/a/3acc7488db2b53733cdd427d3cb1b76361c786e1.png)
##### AdButler Ads Zone URL to Discourse's Site Settings
This plugin only support AdButler "Standard Zones". Text and VAST are not supported.
If you browse to a zone in the AdButler admin, then you can find the Publisher ID (PPPPPP) and the Zone ID (ZZZZZZ) in the URL of your browser's address bar:
`https://admin.adbutler.com/?ID=PPPPPP&p=textadzone.view&zoneID=ZZZZZZ`
Configure the ads in Admin > Settings > AdButler.
Enter the publisher id in the "adbutler publisher id" setting, and enter the Zone IDs in the different
zone id settings as desired.
By default, ads are assumed to be size 728 x 90, or 320 x 50 in mobile view.
To use different size ads, customize using CSS in your site's theme. Override the following CSS:
```css
.adbutler-ad {
width: 728px;
height: 90px;
}
.adbutler-mobile-ad {
width: 320px;
height: 50px;
}
```
### Step 3 - See Your Ad ### Step 3 - See Your Ad
@@ -147,11 +122,9 @@ Once you've configured your settings and your advertising platform has ads that
In this section, we go into more detail on: In this section, we go into more detail on:
* Available Locations for Ad Display * Available Locations for Ad Display
* Ad Sizes Supported
* Trust Levels * Trust Levels
* Personal messages * Languages Supported
* Groups
* Categories
* Tags
### Available Locations for Ad Display ### Available Locations for Ad Display
@@ -164,7 +137,26 @@ The following are available locations along with a description and an image show
| Topic Above Suggested | Ad will appear in the footer above suggested topics of all Discourse forum topics | | Topic Above Suggested | Ad will appear in the footer above suggested topics of all Discourse forum topics |
| Post Bottom & Nth Post | Ad will appear on the stipulated nth post within a topic. So if you have 5 posts in a topic and you want the ad to display after on the 2nd post, put 2 in ```ad_platform_nth_post_code```. | | Post Bottom & Nth Post | Ad will appear on the stipulated nth post within a topic. So if you have 5 posts in a topic and you want the ad to display after on the 2nd post, put 2 in ```ad_platform_nth_post_code```. |
![adplugin2](https://user-images.githubusercontent.com/5862206/214489128-2e0a9520-ffa5-47a9-81f8-27609b2c9975.jpeg)
![](https://www.dropbox.com/sc/rm5bcn8c85niul1/AAAUVW-hn56XtCl_XTNlE19Ra?dl=1)
### Advertisement Sizes Supported
This plugin supports the following ad sizes for the following locations.
All locations except post bottom | Post bottom location | Mobile
--- | --- | ---
728 x 90 | 728 x 90 | 320 x 50
336 x 280 | 336 x 280 |
300 x 250 | 300 x 250 |
970 x 90 | 970 x 90 |
468 x 60 | 468 x 60 |
234 x 60 | 234 x 60 |
125 x 125 | |
180 x 150 | |
200 x 200 | |
250 x 250 | |
### Trust Levels ### Trust Levels
@@ -178,21 +170,12 @@ You can use the ```ad_platform_through_trust_level``` dropdown to disable ads fo
To find more about trust levels in Discourse, refer to [Discourse's posts on trust levels](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924) To find more about trust levels in Discourse, refer to [Discourse's posts on trust levels](https://meta.discourse.org/t/what-do-user-trust-levels-do/4924)
### Personal messages ### Languages Supported
By default, ads won't be shown in personal messages. To enable ads in personal messages, use the "no ads for personal messages" setting. * Chinese (Simplified)
* English
### Groups * French
* Spanish
To give some users an ad-free experience, put the users in groups and add those groups to the "no ads for groups" setting.
### Categories
To disable ads in certain categories, add them to the "no ads for categories" setting. Also consider using the "no ads for restricted categories" to disable ads in all categories that have read access restrictions.
### Tags
Individual topics can have ads disabled by using tags, and entering those tags in the "no ads for tags" setting. This is useful if some topics violate ad network policies.
## Other Installation ## Other Installation
@@ -1,42 +0,0 @@
import Ember from "ember";
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,
value: computed("settingValue", function () {
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 () {
return makeArray(this.choices);
}),
actions: {
onChange(value) {
const settingValue = makeArray(value).join(this.tokenSeparator);
this.attrs.onChange && this.attrs.onChange(settingValue);
},
},
});
@@ -1,13 +0,0 @@
<label for={{name}}>{{title}}</label>
{{house-ads-chooser
settingValue=adValue
choices=adNames
onChange=(action (mut adValue))
}}
<div class="setting-controls">
{{#if changed}}
<DButton class="ok" @action={{action "save"}} @icon="check" />
<DButton class="cancel" @action={{action "cancel"}} @icon="times" />
{{/if}}
</div>
<p class="help">{{help}}</p>
@@ -1,7 +0,0 @@
import { mapBy } from "@ember/object/computed";
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"),
});
@@ -1,9 +0,0 @@
<label for={{name}}>{{title}}</label>
<TextField @value={{adValue}} @classNames="house-ads-text-input" />
<div class="setting-controls">
{{#if changed}}
<DButton class="ok" @action={{action "save"}} @icon="check" />
<DButton class="cancel" @action={{action "cancel"}} @icon="times" />
{{/if}}
</div>
<p class="help">{{help}}</p>
@@ -1,8 +0,0 @@
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"),
});
@@ -1,5 +0,0 @@
import Controller from "@ember/controller";
export default Controller.extend({
loadingAds: true,
});
@@ -1,9 +0,0 @@
import { action } from "@ember/object";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
@action
moreSettings() {
this.transitionTo("adminSiteSettingsCategory", "ad_plugin");
},
});
@@ -1,21 +0,0 @@
import EmberObject from "@ember/object";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
export default DiscourseRoute.extend({
model(params) {
if (params.ad_id === "new") {
return EmberObject.create({
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)
);
}
},
});
@@ -1,22 +0,0 @@
import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
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({
model,
houseAdsSettings: this.get("settings"),
loadingAds: false,
});
},
});
@@ -1,51 +0,0 @@
<DSection @class="house-ads-settings content-body">
<div>{{i18n "admin.adplugin.house_ads.description"}}</div>
{{#if houseAds.length}}
<form class="form-horizontal">
{{house-ads-list-setting
name="topic_list_top"
value=adSettings.topic_list_top
allAds=houseAds
adSettings=adSettings
}}
{{house-ads-list-setting
name="topic_above_post_stream"
value=adSettings.topic_above_post_stream
allAds=houseAds
adSettings=adSettings
}}
{{house-ads-list-setting
name="topic_above_suggested"
value=adSettings.topic_above_suggested
allAds=houseAds
adSettings=adSettings
}}
{{house-ads-list-setting
name="post_bottom"
value=adSettings.post_bottom
allAds=houseAds
adSettings=adSettings
}}
{{house-ads-list-setting
name="topic_list_between"
value=adSettings.topic_list_between
allAds=houseAds
adSettings=adSettings
}}
<DButton
@label="admin.adplugin.house_ads.more_settings"
@icon="cog"
class="btn-default"
@action={{route-action "moreSettings"}}
/>
</form>
{{else}}
<p>
{{#link-to "adminPlugins.houseAds.show" "new"}}
{{i18n "admin.adplugin.house_ads.get_started"}}
{{/link-to}}
</p>
{{/if}}
</DSection>
@@ -1,48 +0,0 @@
<DSection @class="edit-house-ad content-body">
<h1><TextField @class="house-ad-name" @value={{buffered.name}} /></h1>
<div class="controls">
<AceEditor @content={{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}}
/>
<span>{{i18n "admin.adplugin.house_ads.show_to_logged_in_users"}}</span>
</div>
<div>
<Input
class="visible-to-anonymous-checkbox"
@type="checkbox"
@checked={{this.buffered.visible_to_anons}}
/>
<span>{{i18n "admin.adplugin.house_ads.show_to_anons"}}</span>
</div>
</div>
<DButton
@action={{action "save"}}
@disabled={{disableSave}}
class="btn-primary save-button"
@label="admin.adplugin.house_ads.save"
/>
{{#if saving}}
{{savingStatus}}
{{else}}
{{#if dirty}}
<a href {{action "cancel"}}>{{i18n "cancel"}}</a>
{{/if}}
{{/if}}
<DButton
@action={{action "destroy"}}
class="btn-danger delete-button"
@label="admin.adplugin.house_ads.delete"
/>
</div>
</DSection>
@@ -1,5 +1,3 @@
# frozen_string_literal: true
module ::AdPlugin module ::AdPlugin
class HouseAdSettingsController < ::ApplicationController class HouseAdSettingsController < ::ApplicationController
requires_plugin AdPlugin.plugin_name requires_plugin AdPlugin.plugin_name
+16 -18
View File
@@ -1,11 +1,12 @@
# frozen_string_literal: true
module ::AdPlugin module ::AdPlugin
class HouseAdsController < ::ApplicationController class HouseAdsController < ::ApplicationController
requires_plugin AdPlugin.plugin_name requires_plugin AdPlugin.plugin_name
def index def index
render_json_dump(house_ads: HouseAd.all.map(&:to_hash), settings: HouseAdSetting.all) render_json_dump(
house_ads: HouseAd.all.map(&:to_hash),
settings: HouseAdSetting.all
)
end end
def show def show
@@ -14,7 +15,11 @@ module ::AdPlugin
def create def create
ad = HouseAd.create(house_ad_params) ad = HouseAd.create(house_ad_params)
ad.valid? ? render_json_dump(house_ad: ad.to_hash) : render_json_error(ad) if ad.valid?
render_json_dump(house_ad: ad.to_hash)
else
render_json_error(ad)
end
end end
def update def update
@@ -24,32 +29,25 @@ module ::AdPlugin
ad = HouseAd.create(house_ad_params.except(:id)) ad = HouseAd.create(house_ad_params.except(:id))
end end
ad.valid? ? render_json_dump(house_ad: ad.to_hash) : render_json_error(ad) if ad.valid?
render_json_dump(house_ad: ad.to_hash)
else
render_json_error(ad)
end
end end
def destroy def destroy
if ad = HouseAd.find(house_ad_params[:id]) if ad = HouseAd.find(house_ad_params[:id])
ad.destroy ad.destroy
else else
render_json_error(I18n.t("not_found"), status: 404) render_json_error(I18n.t('not_found'), status: 404)
end end
end end
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)
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
+11 -34
View File
@@ -1,10 +1,8 @@
# frozen_string_literal: true
module ::AdPlugin module ::AdPlugin
class HouseAd class HouseAd
include ActiveModel::Validations include ActiveModel::Validations
attr_accessor :id, :name, :html, :visible_to_logged_in_users, :visible_to_anons attr_accessor :id, :name, :html
NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i NAME_REGEX = /\A[[:alnum:]\s\.,'!@#$%&\*\-\+\=:]*\z/i
@@ -20,8 +18,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
end end
def self.from_hash(h) def self.from_hash(h)
@@ -29,10 +25,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]
if h.key?(:visible_to_logged_in_users)
ad.visible_to_logged_in_users = h[:visible_to_logged_in_users]
end
ad.visible_to_anons = h[:visible_to_anons] if h.key?(:visible_to_anons)
ad ad
end end
@@ -43,7 +35,7 @@ module ::AdPlugin
end end
def self.alloc_id def self.alloc_id
DistributedMutex.synchronize("adplugin-house-ad-id") do DistributedMutex.synchronize('adplugin-house-ad-id') do
max_id = AdPlugin.pstore_get("ad:_id") max_id = AdPlugin.pstore_get("ad:_id")
max_id = 1 unless max_id max_id = 1 unless max_id
AdPlugin.pstore_set("ad:_id", max_id + 1) AdPlugin.pstore_set("ad:_id", max_id + 1)
@@ -52,7 +44,7 @@ module ::AdPlugin
end end
def self.find(id) def self.find(id)
if r = AdPlugin.pstore_get("ad:#{id}") if r = AdPlugin::pstore_get("ad:#{id}")
from_hash(r) from_hash(r)
else else
nil nil
@@ -60,27 +52,18 @@ module ::AdPlugin
end end
def self.all def self.all
PluginStoreRow PluginStoreRow.where(plugin_name: AdPlugin.plugin_name)
.where(plugin_name: AdPlugin.plugin_name)
.where("key LIKE 'ad:%'") .where("key LIKE 'ad:%'")
.where("key != 'ad:_id'") .where("key != 'ad:_id'")
.map { |psr| from_hash(PluginStore.cast_value(psr.type_name, psr.value)) } .map do |psr|
.sort_by { |ad| ad.id } from_hash(PluginStore.cast_value(psr.type_name, psr.value))
end end.sort_by { |ad| ad.id }
def self.all_for_anons
self.all.select(&:visible_to_anons)
end
def self.all_for_logged_in_users
self.all.select(&:visible_to_logged_in_users)
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
@@ -91,10 +74,6 @@ module ::AdPlugin
def update(attrs) def update(attrs)
self.name = attrs[:name] self.name = attrs[:name]
self.html = attrs[:html] 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_anons = attrs[:visible_to_anons] if attrs.key?(:visible_to_anons)
self.save self.save
end end
@@ -102,15 +81,12 @@ module ::AdPlugin
{ {
id: @id, id: @id,
name: @name, name: @name,
html: @html, html: @html
visible_to_logged_in_users: @visible_to_logged_in_users,
visible_to_anons: @visible_to_anons,
} }
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
@@ -119,5 +95,6 @@ module ::AdPlugin
AdPlugin::HouseAdSetting.publish_settings AdPlugin::HouseAdSetting.publish_settings
end end
end end
end end
end end
+30 -45
View File
@@ -1,80 +1,65 @@
# frozen_string_literal: true
module ::AdPlugin module ::AdPlugin
class HouseAdSetting class HouseAdSetting
DEFAULTS = { DEFAULTS = {
topic_list_top: "", topic_list_top: '',
topic_above_post_stream: "", topic_above_post_stream: '',
topic_above_suggested: "", topic_above_suggested: '',
post_bottom: "", post_bottom: ''
topic_list_between: "",
} }
def self.all def self.all
settings = DEFAULTS.dup settings = DEFAULTS.dup
PluginStoreRow PluginStoreRow.where(plugin_name: AdPlugin.plugin_name)
.where(plugin_name: AdPlugin.plugin_name)
.where("key LIKE 'ad-setting:%'") .where("key LIKE 'ad-setting:%'")
.each { |psr| settings[psr.key[11..-1].to_sym] = psr.value } .each do |psr|
settings[psr.key[11..-1].to_sym] = psr.value
end
settings settings
end end
def self.settings_and_ads(for_anons: true) 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
end
ads = ads.select { |ad| ad_names.include?(ad.name) }
{ {
settings: settings: settings.merge(
settings.merge( after_nth_post: SiteSetting.house_ads_after_nth_post,
after_nth_post: SiteSetting.house_ads_after_nth_post, house_ads_frequency: SiteSetting.house_ads_frequency
after_nth_topic: SiteSetting.house_ads_after_nth_topic, ),
house_ads_frequency: SiteSetting.house_ads_frequency, creatives: ads.inject({}) { |h, ad| h[ad.name] = ad.html; h }
),
creatives:
ads.inject({}) do |h, ad|
h[ad.name] = ad.html
h
end,
} }
end end
def self.update(setting_name, value) def self.update(setting_name, value)
raise Discourse::NotFound unless DEFAULTS.keys.include?(setting_name.to_sym) unless DEFAULTS.keys.include?(setting_name.to_sym)
raise Discourse::NotFound
end
ad_names = value&.split("|") || [] ad_names = value&.split('|') || []
raise Discourse::InvalidParameters if value && ad_names.any? { |v| v !~ HouseAd::NAME_REGEX } if value && ad_names.any? { |v| v !~ HouseAd::NAME_REGEX }
raise Discourse::InvalidParameters
end
ad_names = (HouseAd.all.map(&:name) & ad_names) unless ad_names.empty? unless ad_names.empty?
ad_names = (HouseAd.all.map(&:name) & ad_names)
end
new_value = ad_names.join("|") new_value = ad_names.join('|')
if value.nil? || new_value == DEFAULTS[setting_name.to_sym] if value.nil? || new_value == DEFAULTS[setting_name.to_sym]
AdPlugin.pstore_delete("ad-setting:#{setting_name}") AdPlugin::pstore_delete("ad-setting:#{setting_name}")
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
@@ -2,9 +2,9 @@ export default {
resource: "admin.adminPlugins", resource: "admin.adminPlugins",
path: "/plugins", path: "/plugins",
map() { map() {
this.route("houseAds", { path: "/pluginad/house_creatives" }, function () { this.route("houseAds", { path: "/adplugin/house_ads" }, function() {
this.route("index", { path: "/" }); this.route("index", { path: "/" });
this.route("show", { path: "/:ad_id" }); this.route("show", { path: "/:ad_id" });
}); });
}, }
}; };
@@ -1,106 +0,0 @@
import Component from "@ember/component";
import { alias, or } from "@ember/object/computed";
import { inject as 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(),
currentCategoryId: or(
"router.currentRoute.attributes.category.id",
"router.currentRoute.parent.attributes.category_id"
),
currentCategorySlug: or(
"router.currentRoute.attributes.category.slug",
"router.currentRoute.parent.attributes.category.slug"
),
// Server needs to compute this in case hidden tags are being used.
topicTagsDisableAds: alias(
"router.currentRoute.parent.attributes.tags_disable_ads"
),
isRestrictedCategory: or(
"router.currentRoute.attributes.category.read_restricted",
"router.currentRoute.parent.attributes.category.read_restricted"
),
@discourseComputed(
"router.currentRoute.attributes.__type",
"router.currentRoute.attributes.id"
)
topicListTag(type, tag) {
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
) {
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));
},
@discourseComputed(
"currentCategoryId",
"topicTagsDisableAds",
"topicListTag",
"isPersonalMessage",
"isRestrictedCategory"
)
showOnCurrentPage(
categoryId,
topicTagsDisableAds,
topicListTag,
isPersonalMessage,
isRestrictedCategory
) {
return (
!topicTagsDisableAds &&
(!categoryId ||
!this.siteSettings.no_ads_for_categories ||
!this.siteSettings.no_ads_for_categories
.split("|")
.includes(categoryId.toString())) &&
(!topicListTag ||
!this.siteSettings.no_ads_for_tags ||
!this.siteSettings.no_ads_for_tags.split("|").includes(topicListTag)) &&
(!isPersonalMessage || !this.siteSettings.no_ads_for_personal_messages) &&
(!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