Compare commits

..

4 Commits

Author SHA1 Message Date
David Taylor
217abaf446
Update @on and @observes to work with native class syntax 2023-02-20 23:27:27 +00:00
David Taylor
b229e7a865
Manual whitespace tweaks 2023-02-20 19:39:16 +00:00
David Taylor
cbc47a7af9
Manually fix clashing action names and [object] fields 2023-02-20 19:36:16 +00:00
David Taylor
f1c8aef911
DEV: Apply ember-native-class-codemod to admin components 2023-02-20 19:01:18 +00:00
1434 changed files with 23696 additions and 31951 deletions

View File

@ -20,7 +20,7 @@ jobs:
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')" if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
name: ${{ matrix.target }} ${{ matrix.build_type }} ${{ matrix.ruby }} name: ${{ matrix.target }} ${{ matrix.build_type }} ${{ matrix.ruby }}
runs-on: ${{ (matrix.build_type == 'annotations') && 'ubuntu-latest' || 'ubuntu-20.04-8core' }} runs-on: ${{ (matrix.build_type == 'annotations') && 'ubuntu-latest' || 'ubuntu-20.04-8core' }}
container: discourse/discourse_test:slim${{ (matrix.build_type == 'frontend' || matrix.build_type == 'system') && '-browsers' || '' }}${{ (matrix.ruby == '3.1') && '-ruby-3.1.0' || '' }} container: discourse/discourse_test:slim${{ (matrix.build_type == 'frontend' || matrix.build_type == 'system') && '-browsers' || '' }}${{ (matrix.ruby == '3.2') && '-ruby-3.2.0' || '' }}
timeout-minutes: 20 timeout-minutes: 20
env: env:
@ -38,7 +38,7 @@ jobs:
matrix: matrix:
build_type: [backend, frontend, system, annotations] build_type: [backend, frontend, system, annotations]
target: [core, plugins] target: [core, plugins]
ruby: ['3.2'] ruby: ['3.1']
exclude: exclude:
- build_type: annotations - build_type: annotations
target: plugins target: plugins
@ -159,22 +159,6 @@ jobs:
path: tmp/turbo_rspec_runtime.log path: tmp/turbo_rspec_runtime.log
key: rspec-runtime-backend-core key: rspec-runtime-backend-core
- name: Run Zeitwerk check
if: matrix.build_type == 'backend'
env:
LOAD_PLUGINS: ${{ (matrix.target == 'plugins') && '1' || '0' }}
run: |
if ! bin/rails zeitwerk:check --trace; then
echo
echo "---------------------------------------------"
echo
echo "::error::'bin/rails zeitwerk:check' failed - the app will fail to boot with 'eager_load=true' (e.g. in production)."
echo "To reproduce locally, run 'bin/rails zeitwerk:check'."
echo "Alternatively, you can run your local server/tests with the 'DISCOURSE_ZEITWERK_EAGER_LOAD=1' environment variable."
echo
exit 1
fi
- name: Core RSpec - name: Core RSpec
if: matrix.build_type == 'backend' && matrix.target == 'core' if: matrix.build_type == 'backend' && matrix.target == 'core'
run: bin/turbo_rspec --verbose run: bin/turbo_rspec --verbose
@ -198,11 +182,11 @@ jobs:
- name: Core System Tests - name: Core System Tests
if: matrix.build_type == 'system' && matrix.target == 'core' if: matrix.build_type == 'system' && matrix.target == 'core'
run: bin/rspec spec/system run: PARALLEL_TEST_PROCESSORS=1 bin/turbo_rspec --verbose spec/system
- name: Plugin System Tests - name: Plugin System Tests
if: matrix.build_type == 'system' && matrix.target == 'plugins' if: matrix.build_type == 'system' && matrix.target == 'plugins'
run: LOAD_PLUGINS=1 bin/rspec plugins/*/spec/system run: LOAD_PLUGINS=1 PARALLEL_TEST_PROCESSORS=1 bin/turbo_rspec --verbose plugins/*/spec/system
- name: Upload failed system test screenshots - name: Upload failed system test screenshots
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

View File

@ -7,7 +7,3 @@ Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
Discourse/NoResetColumnInformationInMigrations: Discourse/NoResetColumnInformationInMigrations:
Enabled: true Enabled: true
Lint/Debugger:
Exclude:
- script/**/*

View File

@ -1 +1 @@
3.2.1 3.1.3

View File

@ -15,7 +15,7 @@ module.exports = {
"directory-item-value", "directory-item-value",
"directory-table-header-title", "directory-table-header-title",
"loading-spinner", "loading-spinner",
"directory-item-label", "mobile-directory-item-label",
], ],
}, },
"no-implicit-this": { "no-implicit-this": {

View File

@ -18,7 +18,7 @@ else
# this allows us to include the bits of rails we use without pieces we do not. # this allows us to include the bits of rails we use without pieces we do not.
# #
# To issue a rails update bump the version number here # To issue a rails update bump the version number here
rails_version = "7.0.4.3" rails_version = "7.0.4.1"
gem "actionmailer", rails_version gem "actionmailer", rails_version
gem "actionpack", rails_version gem "actionpack", rails_version
gem "actionview", rails_version gem "actionview", rails_version
@ -279,5 +279,3 @@ gem "webrick", require: false
# Workaround until Ruby ships with cgi version 0.3.6 or higher. # Workaround until Ruby ships with cgi version 0.3.6 or higher.
gem "cgi", ">= 0.3.6", require: false gem "cgi", ">= 0.3.6", require: false
gem "tzinfo-data"

View File

@ -17,25 +17,25 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (7.0.4.3) actionmailer (7.0.4.1)
actionpack (= 7.0.4.3) actionpack (= 7.0.4.1)
actionview (= 7.0.4.3) actionview (= 7.0.4.1)
activejob (= 7.0.4.3) activejob (= 7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (7.0.4.3) actionpack (7.0.4.1)
actionview (= 7.0.4.3) actionview (= 7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
rack (~> 2.0, >= 2.2.0) rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (7.0.4.3) actionview (7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -44,15 +44,15 @@ GEM
actionview (>= 6.0.a) actionview (>= 6.0.a)
active_model_serializers (0.8.4) active_model_serializers (0.8.4)
activemodel (>= 3.0) activemodel (>= 3.0)
activejob (7.0.4.3) activejob (7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.0.4.3) activemodel (7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
activerecord (7.0.4.3) activerecord (7.0.4.1)
activemodel (= 7.0.4.3) activemodel (= 7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
activesupport (7.0.4.3) activesupport (7.0.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -110,7 +110,7 @@ GEM
chunky_png (1.4.0) chunky_png (1.4.0)
coderay (1.1.3) coderay (1.1.3)
colored2 (3.1.2) colored2 (3.1.2)
concurrent-ruby (1.2.2) concurrent-ruby (1.2.0)
connection_pool (2.3.0) connection_pool (2.3.0)
cose (1.3.0) cose (1.3.0)
cbor (~> 0.5.9) cbor (~> 0.5.9)
@ -157,7 +157,7 @@ GEM
faraday-net_http (>= 2.0, < 3.1) faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4) ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2) faraday-net_http (3.0.2)
faraday-retry (2.1.0) faraday-retry (2.0.0)
faraday (~> 2.0) faraday (~> 2.0)
fast_blank (1.0.1) fast_blank (1.0.1)
fast_xs (0.8.0) fast_xs (0.8.0)
@ -167,11 +167,10 @@ GEM
gc_tracer (1.5.1) gc_tracer (1.5.1)
globalid (1.1.0) globalid (1.1.0)
activesupport (>= 5.0) activesupport (>= 5.0)
google-protobuf (3.22.2) google-protobuf (3.22.0)
google-protobuf (3.22.2-aarch64-linux) google-protobuf (3.22.0-arm64-darwin)
google-protobuf (3.22.2-arm64-darwin) google-protobuf (3.22.0-x86_64-darwin)
google-protobuf (3.22.2-x86_64-darwin) google-protobuf (3.22.0-x86_64-linux)
google-protobuf (3.22.2-x86_64-linux)
guess_html_encoding (0.0.11) guess_html_encoding (0.0.11)
hana (1.3.7) hana (1.3.7)
hashdiff (1.0.1) hashdiff (1.0.1)
@ -219,7 +218,7 @@ GEM
logstash-event (1.2.02) logstash-event (1.2.02)
logstash-logger (0.26.1) logstash-logger (0.26.1)
logstash-event (~> 1.2) logstash-event (~> 1.2)
logster (2.12.2) logster (2.11.4)
loofah (2.19.1) loofah (2.19.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
@ -240,10 +239,10 @@ GEM
mini_sql (1.4.0) mini_sql (1.4.0)
mini_suffix (0.3.3) mini_suffix (0.3.3)
ffi (~> 1.9) ffi (~> 1.9)
minitest (5.18.0) minitest (5.17.0)
mocha (2.0.2) mocha (2.0.2)
ruby2_keywords (>= 0.0.5) ruby2_keywords (>= 0.0.5)
msgpack (1.6.1) msgpack (1.6.0)
multi_json (1.15.0) multi_json (1.15.0)
multi_xml (0.6.0) multi_xml (0.6.0)
mustache (1.1.1) mustache (1.1.1)
@ -312,10 +311,10 @@ GEM
parallel (1.22.1) parallel (1.22.1)
parallel_tests (4.2.0) parallel_tests (4.2.0)
parallel parallel
parser (3.2.1.1) parser (3.2.1.0)
ast (~> 2.4.1) ast (~> 2.4.1)
pg (1.4.6) pg (1.4.5)
prettier_print (1.2.1) prettier_print (1.2.0)
progress (3.6.0) progress (3.6.0)
pry (0.14.2) pry (0.14.2)
coderay (~> 1.1) coderay (~> 1.1)
@ -326,15 +325,15 @@ GEM
pry-rails (0.3.9) pry-rails (0.3.9)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (5.0.1) public_suffix (5.0.1)
puma (6.1.1) puma (6.1.0)
nio4r (~> 2.0) nio4r (~> 2.0)
racc (1.6.2) racc (1.6.2)
rack (2.2.6.4) rack (2.2.6.2)
rack-mini-profiler (3.0.0) rack-mini-profiler (3.0.0)
rack (>= 1.2.0) rack (>= 1.2.0)
rack-protection (3.0.5) rack-protection (3.0.5)
rack rack
rack-test (2.1.0) rack-test (2.0.2)
rack (>= 1.3) rack (>= 1.3)
rails-dom-testing (2.0.3) rails-dom-testing (2.0.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
@ -348,15 +347,15 @@ GEM
rails_multisite (4.0.1) rails_multisite (4.0.1)
activerecord (> 5.0, < 7.1) activerecord (> 5.0, < 7.1)
railties (> 5.0, < 7.1) railties (> 5.0, < 7.1)
railties (7.0.4.3) railties (7.0.4.1)
actionpack (= 7.0.4.3) actionpack (= 7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
zeitwerk (~> 2.5) zeitwerk (~> 2.5)
rainbow (3.1.1) rainbow (3.1.1)
raindrops (0.20.1) raindrops (0.20.0)
rake (13.0.6) rake (13.0.6)
rb-fsevent (0.11.2) rb-fsevent (0.11.2)
rb-inotify (0.10.1) rb-inotify (0.10.1)
@ -391,7 +390,7 @@ GEM
rspec-html-matchers (0.10.0) rspec-html-matchers (0.10.0)
nokogiri (~> 1) nokogiri (~> 1)
rspec (>= 3.0.0.a) rspec (>= 3.0.0.a)
rspec-mocks (3.12.4) rspec-mocks (3.12.3)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0) rspec-support (~> 3.12.0)
rspec-rails (6.0.1) rspec-rails (6.0.1)
@ -412,28 +411,28 @@ GEM
rspec-core (>= 2.14) rspec-core (>= 2.14)
rtlcss (0.2.0) rtlcss (0.2.0)
mini_racer (~> 0.6.3) mini_racer (~> 0.6.3)
rubocop (1.48.1) rubocop (1.44.1)
json (~> 2.3) json (~> 2.3)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.2.0.0) parser (>= 3.2.0.0)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0) rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.26.0, < 2.0) rubocop-ast (>= 1.24.1, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.27.0) rubocop-ast (1.26.0)
parser (>= 3.2.1.0) parser (>= 3.2.1.0)
rubocop-capybara (2.17.1) rubocop-capybara (2.17.0)
rubocop (~> 1.41) rubocop (~> 1.41)
rubocop-discourse (3.2.0) rubocop-discourse (3.0.3)
rubocop (>= 1.1.0) rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0) rubocop-rspec (>= 2.0.0)
rubocop-rspec (2.19.0) rubocop-rspec (2.18.1)
rubocop (~> 1.33) rubocop (~> 1.33)
rubocop-capybara (~> 2.17) rubocop-capybara (~> 2.17)
ruby-prof (1.6.1) ruby-prof (1.5.0)
ruby-progressbar (1.13.0) ruby-progressbar (1.11.0)
ruby-readability (0.7.0) ruby-readability (0.7.0)
guess_html_encoding (>= 0.0.4) guess_html_encoding (>= 0.0.4)
nokogiri (>= 1.6.0) nokogiri (>= 1.6.0)
@ -442,16 +441,16 @@ GEM
sanitize (6.0.1) sanitize (6.0.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
sass-embedded (1.59.2) sass-embedded (1.58.3)
google-protobuf (~> 3.21) google-protobuf (~> 3.21)
rake (>= 10.0.0) rake (>= 10.0.0)
sass-embedded (1.59.2-aarch64-linux-gnu) sass-embedded (1.58.3-aarch64-linux-gnu)
google-protobuf (~> 3.21) google-protobuf (~> 3.21)
sass-embedded (1.59.2-arm64-darwin) sass-embedded (1.58.3-arm64-darwin)
google-protobuf (~> 3.21) google-protobuf (~> 3.21)
sass-embedded (1.59.2-x86_64-darwin) sass-embedded (1.58.3-x86_64-darwin)
google-protobuf (~> 3.21) google-protobuf (~> 3.21)
sass-embedded (1.59.2-x86_64-linux-gnu) sass-embedded (1.58.3-x86_64-linux-gnu)
google-protobuf (~> 3.21) google-protobuf (~> 3.21)
selenium-webdriver (4.8.1) selenium-webdriver (4.8.1)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
@ -478,7 +477,7 @@ GEM
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sshkey (2.0.0) sshkey (2.0.0)
stackprof (0.2.23) stackprof (0.2.23)
syntax_tree (6.0.2) syntax_tree (6.0.0)
prettier_print (>= 1.2.0) prettier_print (>= 1.2.0)
syntax_tree-disable_ternary (1.0.0) syntax_tree-disable_ternary (1.0.0)
test-prof (1.2.0) test-prof (1.2.0)
@ -487,8 +486,6 @@ GEM
timeout (0.3.2) timeout (0.3.2)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
tzinfo-data (1.2022.7)
tzinfo (>= 1.0.0)
uglifier (4.2.0) uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
unf (0.1.4) unf (0.1.4)
@ -534,14 +531,14 @@ PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
actionmailer (= 7.0.4.3) actionmailer (= 7.0.4.1)
actionpack (= 7.0.4.3) actionpack (= 7.0.4.1)
actionview (= 7.0.4.3) actionview (= 7.0.4.1)
actionview_precompiler actionview_precompiler
active_model_serializers (~> 0.8.3) active_model_serializers (~> 0.8.3)
activemodel (= 7.0.4.3) activemodel (= 7.0.4.1)
activerecord (= 7.0.4.3) activerecord (= 7.0.4.1)
activesupport (= 7.0.4.3) activesupport (= 7.0.4.1)
addressable addressable
annotate annotate
aws-sdk-s3 aws-sdk-s3
@ -627,7 +624,7 @@ DEPENDENCIES
rack-protection rack-protection
rails_failover rails_failover
rails_multisite rails_multisite
railties (= 7.0.4.3) railties (= 7.0.4.1)
rake rake
rb-fsevent rb-fsevent
rbtrace rbtrace
@ -660,7 +657,6 @@ DEPENDENCIES
syntax_tree-disable_ternary syntax_tree-disable_ternary
test-prof test-prof
thor thor
tzinfo-data
uglifier uglifier
unf unf
unicorn unicorn

View File

@ -30,7 +30,7 @@ To get your environment setup, follow the community setup guide for your operati
If you're familiar with how Rails works and are comfortable setting up your own environment, you can also try out the [**Discourse Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md), which is aimed primarily at Ubuntu and macOS environments. If you're familiar with how Rails works and are comfortable setting up your own environment, you can also try out the [**Discourse Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md), which is aimed primarily at Ubuntu and macOS environments.
Before you get started, ensure you have the following minimum versions: [Ruby 3.2+](https://www.ruby-lang.org/en/downloads/), [PostgreSQL 13](https://www.postgresql.org/download/), [Redis 7](https://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first! Before you get started, ensure you have the following minimum versions: [Ruby 3.1+](https://www.ruby-lang.org/en/downloads/), [PostgreSQL 13](https://www.postgresql.org/download/), [Redis 7](https://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first!
## Setting up Discourse ## Setting up Discourse

View File

@ -1,13 +1,13 @@
import RestAdapter from "discourse/adapters/rest"; import RESTAdapter from "discourse/adapters/rest";
export default class ApiKey extends RestAdapter { export default RESTAdapter.extend({
jsonMode = true; jsonMode: true,
basePath() { basePath() {
return "/admin/api/"; return "/admin/api/";
} },
apiNameFor() { apiNameFor() {
return "key"; return "key";
} },
} });

View File

@ -1,11 +1,11 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default function buildPluginAdapter(pluginName) { export default function buildPluginAdapter(pluginName) {
return class extends RestAdapter { return RestAdapter.extend({
pathFor(store, type, findArgs) { pathFor(store, type, findArgs) {
return ( return (
"/admin/plugins/" + pluginName + super.pathFor(store, type, findArgs) "/admin/plugins/" + pluginName + this._super(store, type, findArgs)
); );
} },
}; });
} }

View File

@ -1,7 +1,7 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default class CustomizationBase extends RestAdapter { export default RestAdapter.extend({
basePath() { basePath() {
return "/admin/customize/"; return "/admin/customize/";
} },
} });

View File

@ -1,7 +1,7 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default class EmailStyle extends RestAdapter { export default RestAdapter.extend({
pathFor() { pathFor() {
return "/admin/customize/email_style"; return "/admin/customize/email_style";
} },
} });

View File

@ -1,7 +1,7 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default class Embedding extends RestAdapter { export default RestAdapter.extend({
pathFor() { pathFor() {
return "/admin/customize/embedding"; return "/admin/customize/embedding";
} },
} });

View File

@ -1,7 +1,7 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default class StaffActionLog extends RestAdapter { export default RestAdapter.extend({
basePath() { basePath() {
return "/admin/logs/"; return "/admin/logs/";
} },
} });

View File

@ -1,5 +1,5 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default class TagGroup extends RestAdapter { export default RestAdapter.extend({
jsonMode = true; jsonMode: true,
} });

View File

@ -1,10 +1,9 @@
import RestAdapter from "discourse/adapters/rest"; import RestAdapter from "discourse/adapters/rest";
export default class Theme extends RestAdapter { export default RestAdapter.extend({
jsonMode = true;
basePath() { basePath() {
return "/admin/"; return "/admin/";
} },
afterFindAll(results) { afterFindAll(results) {
let map = {}; let map = {};
@ -21,5 +20,7 @@ export default class Theme extends RestAdapter {
theme.set("parentThemes", mappedParents); theme.set("parentThemes", mappedParents);
}); });
return results; return results;
} },
}
jsonMode: true,
});

View File

@ -1,7 +1,7 @@
import RestAdapter from "discourse/adapters/rest"; import RESTAdapter from "discourse/adapters/rest";
export default class WebHookEvent extends RestAdapter { export default RESTAdapter.extend({
basePath() { basePath() {
return "/admin/api/"; return "/admin/api/";
} },
} });

View File

@ -1,7 +1,7 @@
import RestAdapter from "discourse/adapters/rest"; import RESTAdapter from "discourse/adapters/rest";
export default class WebHook extends RestAdapter { export default RESTAdapter.extend({
basePath() { basePath() {
return "/admin/api/"; return "/admin/api/";
} },
} });

View File

@ -1,11 +1,11 @@
import { action } from "@ember/object"; import { action } from "@ember/object";
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import { observes, on } from "@ember-decorators/object"; import { on } from "@ember-decorators/object";
import Component from "@ember/component"; import Component from "@ember/component";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import loadScript from "discourse/lib/load-script"; import loadScript from "discourse/lib/load-script";
import I18n from "I18n"; import I18n from "I18n";
import { bind } from "discourse-common/utils/decorators"; import { bind, observes } from "discourse-common/utils/decorators";
const COLOR_VARS_REGEX = const COLOR_VARS_REGEX =
/\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g; /\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g;
@ -200,7 +200,7 @@ export default class AceEditor extends Component {
this._editor.getSession().setAnnotations(warnings); this._editor.getSession().setAnnotations(warnings);
this.setWarning?.( this.setWarning(
warnings.length warnings.length
? I18n.t("admin.customize.theme.scss_color_variables_warning") ? I18n.t("admin.customize.theme.scss_color_variables_warning")
: false : false

View File

@ -1,5 +1,5 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import { observes, on } from "@ember-decorators/object"; import { observes, on } from "discourse-common/utils/decorators";
import Component from "@ember/component"; import Component from "@ember/component";
import I18n from "I18n"; import I18n from "I18n";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
@ -10,8 +10,11 @@ export default class AdminBackupsLogs extends Component {
showLoadingSpinner = false; showLoadingSpinner = false;
hasFormattedLogs = false; hasFormattedLogs = false;
noLogsMessage = I18n.t("admin.backups.logs.none"); noLogsMessage = I18n.t("admin.backups.logs.none");
formattedLogs = "";
index = 0; init() {
super.init(...arguments);
this._reset();
}
_reset() { _reset() {
this.setProperties({ formattedLogs: "", index: 0 }); this.setProperties({ formattedLogs: "", index: 0 });

View File

@ -7,6 +7,11 @@ export default class AdminEditableField extends Component {
buffer = ""; buffer = "";
editing = false; editing = false;
init() {
super.init(...arguments);
this.set("editing", false);
}
@action @action
edit(event) { edit(event) {
event?.preventDefault(); event?.preventDefault();

View File

@ -61,8 +61,11 @@ export default class AdminReport extends Component {
@equal("model.error", "timeout") showTimeoutError; @equal("model.error", "timeout") showTimeoutError;
@equal("model.error", "exception") showExceptionError; @equal("model.error", "exception") showExceptionError;
@notEmpty("model.data") hasData; @notEmpty("model.data") hasData;
init() {
super.init(...arguments);
_reports = []; this._reports = [];
}
@computed("siteSettings.dashboard_hidden_reports") @computed("siteSettings.dashboard_hidden_reports")
get isHidden() { get isHidden() {
@ -321,6 +324,8 @@ export default class AdminReport extends Component {
} }
_fetchReport() { _fetchReport() {
undefined;
this.setProperties({ isLoading: true, rateLimitationString: null }); this.setProperties({ isLoading: true, rateLimitationString: null });
next(() => { next(() => {

View File

@ -44,7 +44,7 @@
@checked={{this.onlyOverridden}} @checked={{this.onlyOverridden}}
{{on {{on
"click" "click"
(action this.onlyOverriddenChanged value="target.checked") (action "onlyOverriddenChanged" value="target.checked")
}} }}
/> />
{{i18n "admin.customize.theme.hide_unused_fields"}} {{i18n "admin.customize.theme.hide_unused_fields"}}
@ -125,6 +125,6 @@
@autofocus="true" @autofocus="true"
@placeholder={{this.placeholder}} @placeholder={{this.placeholder}}
@htmlPlaceholder={{true}} @htmlPlaceholder={{true}}
@save={{this.save}} @save={{action "save"}}
@setWarning={{action "setWarning"}} @setWarning={{action "setWarning"}}
/> />

View File

@ -3,7 +3,7 @@ import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { fmt } from "discourse/lib/computed"; import { fmt } from "discourse/lib/computed";
import { isDocumentRTL } from "discourse/lib/text-direction"; import { isDocumentRTL } from "discourse/lib/text-direction";
import { action, computed } from "@ember/object"; import { action } from "@ember/object";
import { next } from "@ember/runloop"; import { next } from "@ember/runloop";
export default class AdminThemeEditor extends Component { export default class AdminThemeEditor extends Component {
@ -62,13 +62,13 @@ export default class AdminThemeEditor extends Component {
return ""; return "";
} }
@computed("fieldName", "currentTargetName", "theme") @discourseComputed("fieldName", "currentTargetName", "theme")
get activeSection() { get activeSection() {
return this.theme.getField(this.currentTargetName, this.fieldName); return this.model.getField(this.currentTargetName, this.fieldName);
} }
set activeSection(value) { set activeSection(value) {
this.theme.setField(this.currentTargetName, this.fieldName, value); this.theme.setField(this.fieldName, value);
return value; return value;
} }
@ -126,6 +126,16 @@ export default class AdminThemeEditor extends Component {
this.fieldAdded(this.currentTargetName, name); this.fieldAdded(this.currentTargetName, name);
} }
@action
onlyOverriddenChanged(value) {
this.onlyOverriddenChanged(value);
}
@action
save() {
this.attrs.save();
}
@action @action
setWarning(message) { setWarning(message) {
this.set("warning", message); this.set("warning", message);

View File

@ -1,7 +1,7 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import { action, computed } from "@ember/object"; import { action, computed } from "@ember/object";
import Component from "@ember/component"; import Component from "@ember/component";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
/** /**
An input field for a color. An input field for a color.

View File

@ -9,11 +9,11 @@ export default class DashboardNewFeatures extends Component {
newFeatures = null; newFeatures = null;
releaseNotesLink = null; releaseNotesLink = null;
constructor() { init() {
super(...arguments); super.init(...arguments);
ajax("/admin/dashboard/new-features.json").then((json) => { ajax("/admin/dashboard/new-features.json").then((json) => {
if (this.isDestroying || this.isDestroyed) { if (!this.element || this.isDestroying || this.isDestroyed) {
return; return;
} }

View File

@ -29,7 +29,7 @@
@content={{this.editorContents}} @content={{this.editorContents}}
@mode={{this.currentEditorMode}} @mode={{this.currentEditorMode}}
@editorId={{this.editorId}} @editorId={{this.editorId}}
@save={{@save}} @save={{action "save"}}
/> />
<div class="admin-footer"> <div class="admin-footer">

View File

@ -48,4 +48,9 @@ export default class EmailStylesEditor extends Component {
}, },
}); });
} }
@action
save() {
this.attrs.save();
}
} }

View File

@ -9,6 +9,15 @@
autofocus={{true}} autofocus={{true}}
/> />
</td> </td>
<td class="editing-input">
<div class="label">{{i18n "admin.embedding.class_name"}}</div>
<Input
@value={{this.buffered.class_name}}
placeholder="class"
@enter={{action "save"}}
class="class-name"
/>
</td>
<td class="editing-input"> <td class="editing-input">
<div class="label">{{i18n "admin.embedding.allowed_paths"}}</div> <div class="label">{{i18n "admin.embedding.allowed_paths"}}</div>
<Input <Input
@ -45,6 +54,12 @@
<div class="label">{{i18n "admin.embedding.host"}}</div> <div class="label">{{i18n "admin.embedding.host"}}</div>
{{this.host.host}} {{this.host.host}}
</td> </td>
<td>
<div class="label">
{{i18n "admin.embedding.class_name"}}
</div>
{{this.host.class_name}}
</td>
<td> <td>
<div class="label"> <div class="label">
{{i18n "admin.embedding.allowed_paths"}} {{i18n "admin.embedding.allowed_paths"}}

View File

@ -1,4 +1,4 @@
<div class="form-templates__form"> <div class="form-templates--form">
<div class="control-group"> <div class="control-group">
<label for="template-name"> <label for="template-name">
{{i18n "admin.form_templates.new_template_form.name.label"}} {{i18n "admin.form_templates.new_template_form.name.label"}}
@ -6,38 +6,24 @@
<TextField <TextField
@value={{this.templateName}} @value={{this.templateName}}
@name="template-name" @name="template-name"
@class="form-templates__form-name-input" @class="form-templates--form-name-input"
@placeholderKey="admin.form_templates.new_template_form.name.placeholder" @placeholderKey="admin.form_templates.new_template_form.name.placeholder"
/> />
</div> </div>
<div class="control-group form-templates__editor">
<div class="form-templates__quick-insert-field-buttons"> <div class="control-group form-templates--quick-insert-field-buttons">
<span> <span>
{{I18n "admin.form_templates.quick_insert_fields.add_new_field"}} {{I18n "admin.form_templates.quick_insert_fields.add_new_field"}}
</span> </span>
{{#each this.quickInsertFields as |field|}} {{#each this.quickInsertFields as |field|}}
<DButton
@class="btn-flat btn-icon-text quick-insert-{{field.type}}"
@icon={{field.icon}}
@label="admin.form_templates.quick_insert_fields.{{field.type}}"
@action={{this.onInsertField}}
@actionParam={{field.type}}
/>
{{/each}}
<DButton <DButton
class="btn-flat btn-icon-text form-templates__validations-modal-button" @class="btn-flat btn-icon-text quick-insert-{{field.type}}"
@label="admin.form_templates.validations_modal.button_title" @icon={{field.icon}}
@icon="check-circle" @label="admin.form_templates.quick_insert_fields.{{field.type}}"
@action={{this.showValidationOptionsModal}} @action={{this.onInsertField}}
@actionParam={{field.type}}
/> />
</div> {{/each}}
<DButton
@class="form-templates__preview-button"
@icon="eye"
@label="admin.form_templates.new_template_form.preview"
@action={{this.showPreview}}
@disabled={{this.disablePreviewButton}}
/>
</div> </div>
<div class="control-group"> <div class="control-group">
@ -50,7 +36,7 @@
@label="admin.form_templates.new_template_form.submit" @label="admin.form_templates.new_template_form.submit"
@icon="check" @icon="check"
@action={{this.onSubmit}} @action={{this.onSubmit}}
@disabled={{this.disableSubmitButton}} @disabled={{this.formSubmitted}}
/> />
<DButton <DButton

View File

@ -6,15 +6,14 @@ import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { templateFormFields } from "admin/lib/template-form-fields"; import { templateFormFields } from "admin/lib/template-form-fields";
import FormTemplate from "admin/models/form-template"; import FormTemplate from "admin/models/form-template";
import showModal from "discourse/lib/show-modal";
export default class FormTemplateForm extends Component { export default class FormTemplateForm extends Component {
@service router; @service router;
@service dialog; @service dialog;
@tracked formSubmitted = false; @tracked formSubmitted = false;
@tracked templateContent = this.args.model?.template || ""; @tracked templateContent = this.args.model?.template || "";
@tracked templateName = this.args.model?.name || "";
isEditing = this.args.model?.id ? true : false; isEditing = this.args.model?.id ? true : false;
templateName = this.args.model?.name;
quickInsertFields = [ quickInsertFields = [
{ {
type: "checkbox", type: "checkbox",
@ -42,17 +41,6 @@ export default class FormTemplateForm extends Component {
}, },
]; ];
get disablePreviewButton() {
return Boolean(!this.templateName.length || !this.templateContent.length);
}
get disableSubmitButton() {
return (
Boolean(!this.templateName.length || !this.templateContent.length) ||
this.formSubmitted
);
}
@action @action
onSubmit() { onSubmit() {
if (!this.formSubmitted) { if (!this.formSubmitted) {
@ -66,17 +54,27 @@ export default class FormTemplateForm extends Component {
if (this.isEditing) { if (this.isEditing) {
postData["id"] = this.args.model.id; postData["id"] = this.args.model.id;
}
FormTemplate.createOrUpdateTemplate(postData) FormTemplate.updateTemplate(this.args.model.id, postData)
.then(() => { .then(() => {
this.formSubmitted = false; this.formSubmitted = false;
this.router.transitionTo("adminCustomizeFormTemplates.index"); this.router.transitionTo("adminCustomizeFormTemplates.index");
}) })
.catch((e) => { .catch((e) => {
popupAjaxError(e); popupAjaxError(e);
this.formSubmitted = false; this.formSubmitted = false;
}); });
} else {
FormTemplate.createTemplate(postData)
.then(() => {
this.formSubmitted = false;
this.router.transitionTo("adminCustomizeFormTemplates.index");
})
.catch((e) => {
popupAjaxError(e);
this.formSubmitted = false;
});
}
} }
@action @action
@ -108,33 +106,4 @@ export default class FormTemplateForm extends Component {
this.templateContent += `\n${structure}`; this.templateContent += `\n${structure}`;
} }
} }
@action
showValidationOptionsModal() {
return showModal("admin-form-template-validation-options", {
admin: true,
});
}
@action
showPreview() {
const data = {
name: this.templateName,
template: this.templateContent,
};
if (this.isEditing) {
data["id"] = this.args.model.id;
}
FormTemplate.validateTemplate(data)
.then(() => {
return showModal("form-template-form-preview", {
model: {
content: this.templateContent,
},
});
})
.catch(popupAjaxError);
}
} }

View File

@ -1,10 +1,5 @@
<tr class="admin-list-item"> <tr class="admin-list-item">
<td class="col first">{{@template.name}}</td> <td class="col first">{{@template.name}}</td>
<td class="col categories">
{{#each this.activeCategories as |category|}}
{{category-link category}}
{{/each}}
</td>
<td class="col action"> <td class="col action">
<DButton <DButton
@title="admin.form_templates.list_table.actions.view" @title="admin.form_templates.list_table.actions.view"

View File

@ -9,17 +9,11 @@ import I18n from "I18n";
export default class FormTemplateRowItem extends Component { export default class FormTemplateRowItem extends Component {
@service router; @service router;
@service dialog; @service dialog;
@service site;
get activeCategories() {
return this.site?.categories?.filter((c) =>
c["form_template_ids"].includes(this.args.template.id)
);
}
@action @action
viewTemplate() { viewTemplate() {
showModal("customize-form-template-view", { showModal("admin-customize-form-template-view", {
admin: true,
model: this.args.template, model: this.args.template,
refreshModel: this.args.refreshModel, refreshModel: this.args.refreshModel,
}); });

View File

@ -1,4 +1,4 @@
import { observes, on } from "@ember-decorators/object"; import { observes, on } from "discourse-common/utils/decorators";
import Component from "@ember/component"; import Component from "@ember/component";
import highlightSyntax from "discourse/lib/highlight-syntax"; import highlightSyntax from "discourse/lib/highlight-syntax";

View File

@ -2,7 +2,7 @@ import { classNameBindings } from "@ember-decorators/component";
import Component from "@ember/component"; import Component from "@ember/component";
import I18n from "I18n"; import I18n from "I18n";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { on } from "@ember-decorators/object"; import { on } from "discourse-common/utils/decorators";
import { action, set } from "@ember/object"; import { action, set } from "@ember/object";
@classNameBindings(":value-list", ":secret-value-list") @classNameBindings(":value-list", ":secret-value-list")

View File

@ -2,8 +2,7 @@ import { classNameBindings } from "@ember-decorators/component";
import { empty } from "@ember/object/computed"; import { empty } from "@ember/object/computed";
import Component from "@ember/component"; import Component from "@ember/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed, { on } from "discourse-common/utils/decorators";
import { on } from "@ember-decorators/object";
@classNameBindings(":simple-list", ":value-list") @classNameBindings(":simple-list", ":value-list")
export default class SimpleList extends Component { export default class SimpleList extends Component {

View File

@ -1,8 +1,7 @@
<div class="setting-label"> <div class="setting-label">
<h3> <h3>
{{this.settingName}}
{{#if this.staffLogFilter}} {{#if this.staffLogFilter}}
{{this.settingName}}
<LinkTo <LinkTo
@route="adminLogs.staffActionLogs" @route="adminLogs.staffActionLogs"
@query={{hash filters=this.staffLogFilter force_refresh=true}} @query={{hash filters=this.staffLogFilter force_refresh=true}}
@ -12,18 +11,17 @@
{{d-icon "history"}} {{d-icon "history"}}
</span> </span>
</LinkTo> </LinkTo>
{{else}}
{{this.settingName}}
{{/if}} {{/if}}
</h3> </h3>
{{#if this.defaultIsAvailable}} {{#if this.defaultIsAvailable}}
<DButton <a
class="btn-link" href
@action={{this.setDefaultValues}} onClick={{action "setDefaultValues"}}
@translatedLabel={{this.setting.setDefaultValuesLabel}} >{{this.setting.setDefaultValuesLabel}}</a>
/>
{{/if}} {{/if}}
</div> </div>
<div class="setting-value"> <div class="setting-value">
{{component {{component
this.componentName this.componentName
@ -35,20 +33,18 @@
allowAny=this.allowAny allowAny=this.allowAny
}} }}
</div> </div>
{{#if this.dirty}} {{#if this.dirty}}
<div class="setting-controls"> <div class="setting-controls">
<DButton class="ok" @action={{this.update}} @icon="check" /> <DButton @class="ok" @action={{action "update"}} @icon="check" />
<DButton class="cancel" @action={{this.cancel}} @icon="times" /> <DButton @class="cancel" @action={{action "cancel"}} @icon="times" />
</div> </div>
{{else if this.setting.overridden}} {{else if this.setting.overridden}}
{{#if this.setting.secret}} {{#if this.setting.secret}}
<DButton @action={{this.toggleSecret}} @icon="far-eye-slash" /> <DButton @action={{action "toggleSecret"}} @icon="far-eye-slash" />
{{/if}} {{/if}}
<DButton <DButton
class="btn-default undo" @class="btn-default undo"
@action={{this.resetDefault}} @action={{action "resetDefault"}}
@icon="undo" @icon="undo"
@label="admin.settings.reset" @label="admin.settings.reset"
/> />

View File

@ -11,7 +11,6 @@ export default class SiteSettingComponent extends Component.extend(
updateExistingUsers = null; updateExistingUsers = null;
@readOnly("setting.staffLogFilter") staffLogFilter; @readOnly("setting.staffLogFilter") staffLogFilter;
_save() { _save() {
const setting = this.buffered; const setting = this.buffered;
return SiteSetting.update(setting.get("setting"), setting.get("value"), { return SiteSetting.update(setting.get("setting"), setting.get("value"), {

View File

@ -1,7 +1,7 @@
import { classNameBindings, classNames } from "@ember-decorators/component"; import { classNameBindings, classNames } from "@ember-decorators/component";
import Component from "@ember/component"; import Component from "@ember/component";
import highlightHTML from "discourse/lib/highlight-html"; import highlightHTML from "discourse/lib/highlight-html";
import { on } from "@ember-decorators/object"; import { on } from "discourse-common/utils/decorators";
@classNames("site-text") @classNames("site-text")
@classNameBindings("siteText.overridden") @classNameBindings("siteText.overridden")

View File

@ -56,7 +56,6 @@
{{#if this.hasInactiveThemes}} {{#if this.hasInactiveThemes}}
{{#each this.inactiveThemes as |theme|}} {{#each this.inactiveThemes as |theme|}}
<ThemesListItem <ThemesListItem
@classNames="inactive-theme"
@theme={{theme}} @theme={{theme}}
@navigateToTheme={{action "navigateToTheme" theme}} @navigateToTheme={{action "navigateToTheme" theme}}
/> />

View File

@ -1,12 +1,11 @@
import { action } from "@ember/object"; import { action } from "@ember/object";
import { classNames } from "@ember-decorators/component"; import { classNameBindings } from "@ember-decorators/component";
import discourseComputed from "discourse-common/utils/decorators";
import { on } from "@ember-decorators/object";
import { empty, reads } from "@ember/object/computed"; import { empty, reads } from "@ember/object/computed";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import Component from "@ember/component"; import Component from "@ember/component";
import { makeArray } from "discourse-common/lib/helpers"; import { makeArray } from "discourse-common/lib/helpers";
@classNames("value-list") @classNameBindings(":value-list")
export default class ValueList extends Component { export default class ValueList extends Component {
@empty("newValue") inputInvalid; @empty("newValue") inputInvalid;

View File

@ -2,8 +2,7 @@ import { action } from "@ember/object";
import { classNames, tagName } from "@ember-decorators/component"; import { classNames, tagName } from "@ember-decorators/component";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { equal, not } from "@ember/object/computed"; import { equal, not } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { observes } from "@ember-decorators/object";
import Component from "@ember/component"; import Component from "@ember/component";
import I18n from "I18n"; import I18n from "I18n";
import WatchedWord from "admin/models/watched-word"; import WatchedWord from "admin/models/watched-word";

View File

@ -2,18 +2,18 @@ import Controller from "@ember/controller";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
export default class AdminApiKeysIndexController extends Controller { export default Controller.extend({
loading = false; loading: false,
@action @action
revokeKey(key) { revokeKey(key) {
key.revoke().catch(popupAjaxError); key.revoke().catch(popupAjaxError);
} },
@action @action
undoRevokeKey(key) { undoRevokeKey(key) {
key.undoRevoke().catch(popupAjaxError); key.undoRevoke().catch(popupAjaxError);
} },
@action @action
loadMore() { loadMore() {
@ -35,5 +35,5 @@ export default class AdminApiKeysIndexController extends Controller {
.finally(() => { .finally(() => {
this.set("loading", false); this.set("loading", false);
}); });
} },
} });

View File

@ -1,32 +1,37 @@
import { equal } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { isBlank } from "@ember/utils"; import { isBlank } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { action, get } from "@ember/object"; import { action, get } from "@ember/object";
import { equal } from "@ember/object/computed";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
export default class AdminApiKeysNewController extends Controller { export default Controller.extend({
userModes = [ userModes: null,
{ id: "all", name: I18n.t("admin.api.all_users") }, scopeModes: null,
{ id: "single", name: I18n.t("admin.api.single_user") }, globalScopes: null,
]; scopes: null,
scopeModes = [
{ id: "granular", name: I18n.t("admin.api.scopes.granular") },
{ id: "read_only", name: I18n.t("admin.api.scopes.read_only") },
{ id: "global", name: I18n.t("admin.api.scopes.global") },
];
globalScopes = null;
scopes = null;
@equal("userMode", "single") showUserSelector;
init() { init() {
super.init(...arguments); this._super(...arguments);
this.set("userModes", [
{ id: "all", name: I18n.t("admin.api.all_users") },
{ id: "single", name: I18n.t("admin.api.single_user") },
]);
this.set("scopeModes", [
{ id: "granular", name: I18n.t("admin.api.scopes.granular") },
{ id: "read_only", name: I18n.t("admin.api.scopes.read_only") },
{ id: "global", name: I18n.t("admin.api.scopes.global") },
]);
this._loadScopes(); this._loadScopes();
} },
showUserSelector: equal("userMode", "single"),
@discourseComputed("model.{description,username}", "showUserSelector") @discourseComputed("model.{description,username}", "showUserSelector")
saveDisabled(model, showUserSelector) { saveDisabled(model, showUserSelector) {
@ -37,12 +42,12 @@ export default class AdminApiKeysNewController extends Controller {
return true; return true;
} }
return false; return false;
} },
@action @action
updateUsername(selected) { updateUsername(selected) {
this.set("model.username", get(selected, "firstObject")); this.set("model.username", get(selected, "firstObject"));
} },
@action @action
changeUserMode(userMode) { changeUserMode(userMode) {
@ -50,12 +55,12 @@ export default class AdminApiKeysNewController extends Controller {
this.model.set("username", null); this.model.set("username", null);
} }
this.set("userMode", userMode); this.set("userMode", userMode);
} },
@action @action
changeScopeMode(scopeMode) { changeScopeMode(scopeMode) {
this.set("scopeMode", scopeMode); this.set("scopeMode", scopeMode);
} },
@action @action
save() { save() {
@ -72,12 +77,12 @@ export default class AdminApiKeysNewController extends Controller {
} }
return this.model.save().catch(popupAjaxError); return this.model.save().catch(popupAjaxError);
} },
@action @action
continue() { continue() {
this.transitionToRoute("adminApiKeys.show", this.model.id); this.transitionToRoute("adminApiKeys.show", this.model.id);
} },
@action @action
showURLs(urls) { showURLs(urls) {
@ -85,7 +90,7 @@ export default class AdminApiKeysNewController extends Controller {
admin: true, admin: true,
model: { urls }, model: { urls },
}); });
} },
_loadScopes() { _loadScopes() {
return ajax("/admin/api/keys/scopes.json") return ajax("/admin/api/keys/scopes.json")
@ -97,5 +102,5 @@ export default class AdminApiKeysNewController extends Controller {
this.set("scopes", data.scopes); this.set("scopes", data.scopes);
}) })
.catch(popupAjaxError); .catch(popupAjaxError);
} },
} });

View File

@ -1,74 +1,66 @@
import { action } from "@ember/object";
import { empty } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { bufferedProperty } from "discourse/mixins/buffered-content"; import { bufferedProperty } from "discourse/mixins/buffered-content";
import { empty } from "@ember/object/computed";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
export default class AdminApiKeysShowController extends Controller.extend( export default Controller.extend(bufferedProperty("model"), {
bufferedProperty("model") isNew: empty("model.id"),
) {
@empty("model.id") isNew;
@action actions: {
saveDescription() { saveDescription() {
const buffered = this.buffered; const buffered = this.buffered;
const attrs = buffered.getProperties("description"); const attrs = buffered.getProperties("description");
this.model this.model
.save(attrs) .save(attrs)
.then(() => { .then(() => {
this.set("editingDescription", false); this.set("editingDescription", false);
this.rollbackBuffer();
})
.catch(popupAjaxError);
},
cancel() {
const id = this.get("userField.id");
if (isEmpty(id)) {
this.destroyAction(this.userField);
} else {
this.rollbackBuffer(); this.rollbackBuffer();
}) this.set("editing", false);
.catch(popupAjaxError); }
} },
@action editDescription() {
cancel() { this.toggleProperty("editingDescription");
const id = this.get("userField.id"); if (!this.editingDescription) {
if (isEmpty(id)) { this.rollbackBuffer();
this.destroyAction(this.userField); }
} else { },
this.rollbackBuffer();
this.set("editing", false);
}
}
@action revokeKey(key) {
editDescription() { key.revoke().catch(popupAjaxError);
this.toggleProperty("editingDescription"); },
if (!this.editingDescription) {
this.rollbackBuffer();
}
}
@action deleteKey(key) {
revokeKey(key) { key
key.revoke().catch(popupAjaxError); .destroyRecord()
} .then(() => this.transitionToRoute("adminApiKeys.index"))
.catch(popupAjaxError);
},
@action undoRevokeKey(key) {
deleteKey(key) { key.undoRevoke().catch(popupAjaxError);
key },
.destroyRecord()
.then(() => this.transitionToRoute("adminApiKeys.index"))
.catch(popupAjaxError);
}
@action showURLs(urls) {
undoRevokeKey(key) { return showModal("admin-api-key-urls", {
key.undoRevoke().catch(popupAjaxError); admin: true,
} model: {
urls,
@action },
showURLs(urls) { });
return showModal("admin-api-key-urls", { },
admin: true, },
model: { });
urls,
},
});
}
}

View File

@ -1,3 +1,3 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default class AdminApiKeysController extends Controller {} export default Controller.extend();

View File

@ -1,21 +1,19 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { alias, equal } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import { alias, equal } from "@ember/object/computed";
import { i18n, setting } from "discourse/lib/computed"; import { i18n, setting } from "discourse/lib/computed";
import I18n from "I18n"; import I18n from "I18n";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
export default class AdminBackupsIndexController extends Controller { export default Controller.extend({
@service dialog; adminBackups: controller(),
@controller adminBackups; dialog: service(),
status: alias("adminBackups.model"),
@alias("adminBackups.model") status; uploadLabel: i18n("admin.backups.upload.label"),
@i18n("admin.backups.upload.label") uploadLabel; backupLocation: setting("backup_location"),
@setting("backup_location") backupLocation; localBackupStorage: equal("backupLocation", "local"),
@equal("backupLocation", "local") localBackupStorage;
@discourseComputed("status.allowRestore", "status.isOperationRunning") @discourseComputed("status.allowRestore", "status.isOperationRunning")
restoreTitle(allowRestore, isOperationRunning) { restoreTitle(allowRestore, isOperationRunning) {
@ -26,35 +24,35 @@ export default class AdminBackupsIndexController extends Controller {
} else { } else {
return "admin.backups.operations.restore.title"; return "admin.backups.operations.restore.title";
} }
} },
@action actions: {
toggleReadOnlyMode() { toggleReadOnlyMode() {
if (!this.site.get("isReadOnly")) { if (!this.site.get("isReadOnly")) {
this.dialog.yesNoConfirm({ this.dialog.yesNoConfirm({
message: I18n.t("admin.backups.read_only.enable.confirm"), message: I18n.t("admin.backups.read_only.enable.confirm"),
didConfirm: () => { didConfirm: () => {
this.set("currentUser.hideReadOnlyAlert", true); this.set("currentUser.hideReadOnlyAlert", true);
this._toggleReadOnlyMode(true); this._toggleReadOnlyMode(true);
}, },
}); });
} else { } else {
this._toggleReadOnlyMode(false); this._toggleReadOnlyMode(false);
} }
} },
@action download(backup) {
download(backup) { const link = backup.get("filename");
const link = backup.get("filename"); ajax(`/admin/backups/${link}`, { type: "PUT" }).then(() =>
ajax(`/admin/backups/${link}`, { type: "PUT" }).then(() => this.dialog.alert(I18n.t("admin.backups.operations.download.alert"))
this.dialog.alert(I18n.t("admin.backups.operations.download.alert")) );
); },
} },
_toggleReadOnlyMode(enable) { _toggleReadOnlyMode(enable) {
ajax("/admin/backups/readonly", { ajax("/admin/backups/readonly", {
type: "PUT", type: "PUT",
data: { enable }, data: { enable },
}).then(() => this.site.set("isReadOnly", enable)); }).then(() => this.site.set("isReadOnly", enable));
} },
} });

View File

@ -1,10 +1,13 @@
import { alias } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import { alias } from "@ember/object/computed";
export default class AdminBackupsLogsController extends Controller { export default Controller.extend({
@controller adminBackups; adminBackups: controller(),
status: alias("adminBackups.model"),
@alias("adminBackups.model") status; init() {
this._super(...arguments);
logs = []; this.logs = [];
} },
});

View File

@ -1,8 +1,11 @@
import { and, not } from "@ember/object/computed"; import { and, not } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default class AdminBackupsController extends Controller { export default Controller.extend({
@not("model.isOperationRunning") noOperationIsRunning; noOperationIsRunning: not("model.isOperationRunning"),
@not("rollbackEnabled") rollbackDisabled; rollbackEnabled: and(
@and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning") "model.canRollback",
rollbackEnabled; "model.restoreEnabled",
} "noOperationIsRunning"
),
rollbackDisabled: not("rollbackEnabled"),
});

View File

@ -1,5 +1,5 @@
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
import I18n from "I18n"; import I18n from "I18n";
import { bufferedProperty } from "discourse/mixins/buffered-content"; import { bufferedProperty } from "discourse/mixins/buffered-content";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
@ -248,13 +248,8 @@ export default class AdminBadgesShowController extends Controller.extend(
@action @action
toggleBadge() { toggleBadge() {
const originalState = this.buffered.get("enabled"); this.model
const newState = !this.buffered.get("enabled"); .save({ enabled: !this.buffered.get("enabled") })
.catch(popupAjaxError);
this.buffered.set("enabled", newState);
this.model.save({ enabled: newState }).catch((error) => {
this.buffered.set("enabled", originalState);
return popupAjaxError(error);
});
} }
} }

View File

@ -1,19 +1,19 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import EmberObject, { action } from "@ember/object"; import EmberObject from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
export default class AdminCustomizeColorsController extends Controller { export default Controller.extend({
@discourseComputed("model.@each.id") @discourseComputed("model.@each.id")
baseColorScheme() { baseColorScheme() {
return this.model.findBy("is_base", true); return this.model.findBy("is_base", true);
} },
@discourseComputed("model.@each.id") @discourseComputed("model.@each.id")
baseColorSchemes() { baseColorSchemes() {
return this.model.filterBy("is_base", true); return this.model.filterBy("is_base", true);
} },
@discourseComputed("baseColorScheme") @discourseComputed("baseColorScheme")
baseColors(baseColorScheme) { baseColors(baseColorScheme) {
@ -22,28 +22,28 @@ export default class AdminCustomizeColorsController extends Controller {
baseColorsHash.set(color.get("name"), color); baseColorsHash.set(color.get("name"), color);
}); });
return baseColorsHash; return baseColorsHash;
} },
@action actions: {
newColorSchemeWithBase(baseKey) { newColorSchemeWithBase(baseKey) {
const base = this.baseColorSchemes.findBy("base_scheme_id", baseKey); const base = this.baseColorSchemes.findBy("base_scheme_id", baseKey);
const newColorScheme = base.copy(); const newColorScheme = base.copy();
newColorScheme.setProperties({ newColorScheme.setProperties({
name: I18n.t("admin.customize.colors.new_name"), name: I18n.t("admin.customize.colors.new_name"),
base_scheme_id: base.get("base_scheme_id"), base_scheme_id: base.get("base_scheme_id"),
}); });
newColorScheme.save().then(() => { newColorScheme.save().then(() => {
this.model.pushObject(newColorScheme); this.model.pushObject(newColorScheme);
newColorScheme.set("savingStatus", null); newColorScheme.set("savingStatus", null);
this.replaceRoute("adminCustomize.colors.show", newColorScheme); this.replaceRoute("adminCustomize.colors.show", newColorScheme);
}); });
} },
@action newColorScheme() {
newColorScheme() { showModal("admin-color-scheme-select-base", {
showModal("admin-color-scheme-select-base", { model: this.baseColorSchemes,
model: this.baseColorSchemes, admin: true,
admin: true, });
}); },
} },
} });

View File

@ -1,38 +1,38 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
export default class AdminCustomizeEmailStyleEditController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
@discourseComputed("model.isSaving") @discourseComputed("model.isSaving")
saveButtonText(isSaving) { saveButtonText(isSaving) {
return isSaving ? I18n.t("saving") : I18n.t("admin.customize.save"); return isSaving ? I18n.t("saving") : I18n.t("admin.customize.save");
} },
@discourseComputed("model.changed", "model.isSaving") @discourseComputed("model.changed", "model.isSaving")
saveDisabled(changed, isSaving) { saveDisabled(changed, isSaving) {
return !changed || isSaving; return !changed || isSaving;
} },
@action actions: {
save() { save() {
if (!this.model.saving) { if (!this.model.saving) {
this.set("saving", true); this.set("saving", true);
this.model this.model
.update(this.model.getProperties("html", "css")) .update(this.model.getProperties("html", "css"))
.catch((e) => { .catch((e) => {
const msg = const msg =
e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors
? I18n.t("admin.customize.email_style.save_error_with_reason", { ? I18n.t("admin.customize.email_style.save_error_with_reason", {
error: e.jqXHR.responseJSON.errors.join(". "), error: e.jqXHR.responseJSON.errors.join(". "),
}) })
: I18n.t("generic_error"); : I18n.t("generic_error");
this.dialog.alert(msg); this.dialog.alert(msg);
}) })
.finally(() => this.set("model.changed", false)); .finally(() => this.set("model.changed", false));
} }
} },
} },
});

View File

@ -1,26 +1,23 @@
import { inject as service } from "@ember/service";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { bufferedProperty } from "discourse/mixins/buffered-content"; import { bufferedProperty } from "discourse/mixins/buffered-content";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default class AdminCustomizeEmailTemplatesEditController extends Controller.extend( export default Controller.extend(bufferedProperty("emailTemplate"), {
bufferedProperty("emailTemplate") adminCustomizeEmailTemplates: controller(),
) { dialog: service(),
@service dialog; emailTemplate: null,
@controller adminCustomizeEmailTemplates; saved: false,
emailTemplate = null;
saved = false;
@discourseComputed("buffered.body", "buffered.subject") @discourseComputed("buffered.body", "buffered.subject")
saveDisabled(body, subject) { saveDisabled(body, subject) {
return ( return (
this.emailTemplate.body === body && this.emailTemplate.subject === subject this.emailTemplate.body === body && this.emailTemplate.subject === subject
); );
} },
@discourseComputed("buffered") @discourseComputed("buffered")
hasMultipleSubjects(buffered) { hasMultipleSubjects(buffered) {
@ -29,7 +26,7 @@ export default class AdminCustomizeEmailTemplatesEditController extends Controll
} else { } else {
return buffered.getProperties("id")["id"]; return buffered.getProperties("id")["id"];
} }
} },
@action @action
saveChanges() { saveChanges() {
@ -41,7 +38,7 @@ export default class AdminCustomizeEmailTemplatesEditController extends Controll
this.set("saved", true); this.set("saved", true);
}) })
.catch(popupAjaxError); .catch(popupAjaxError);
} },
@action @action
revertChanges() { revertChanges() {
@ -60,5 +57,5 @@ export default class AdminCustomizeEmailTemplatesEditController extends Controll
.catch(popupAjaxError); .catch(popupAjaxError);
}, },
}); });
} },
} });

View File

@ -1,13 +1,18 @@
import { sort } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { sort } from "@ember/object/computed";
export default class AdminCustomizeEmailTemplatesController extends Controller { export default Controller.extend({
titleSorting = ["title"]; sortedTemplates: sort("emailTemplates", "titleSorting"),
@sort("emailTemplates", "titleSorting") sortedTemplates;
init() {
this._super(...arguments);
this.set("titleSorting", ["title"]);
},
@action @action
onSelectTemplate(template) { onSelectTemplate(template) {
this.transitionToRoute("adminCustomizeEmailTemplates.edit", template); this.transitionToRoute("adminCustomizeEmailTemplates.edit", template);
} },
} });

View File

@ -1,52 +1,47 @@
import { action } from "@ember/object";
import { not } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { bufferedProperty } from "discourse/mixins/buffered-content"; import { bufferedProperty } from "discourse/mixins/buffered-content";
import { not } from "@ember/object/computed";
import { propertyEqual } from "discourse/lib/computed"; import { propertyEqual } from "discourse/lib/computed";
export default class AdminCustomizeRobotsTxtController extends Controller.extend( export default Controller.extend(bufferedProperty("model"), {
bufferedProperty("model") saved: false,
) { isSaving: false,
saved = false; saveDisabled: propertyEqual("model.robots_txt", "buffered.robots_txt"),
isSaving = false; resetDisabled: not("model.overridden"),
@propertyEqual("model.robots_txt", "buffered.robots_txt") saveDisabled; actions: {
save() {
this.setProperties({
isSaving: true,
saved: false,
});
@not("model.overridden") resetDisabled; ajax("robots.json", {
type: "PUT",
@action data: { robots_txt: this.buffered.get("robots_txt") },
save() {
this.setProperties({
isSaving: true,
saved: false,
});
ajax("robots.json", {
type: "PUT",
data: { robots_txt: this.buffered.get("robots_txt") },
})
.then((data) => {
this.commitBuffer();
this.set("saved", true);
this.set("model.overridden", data.overridden);
}) })
.finally(() => this.set("isSaving", false)); .then((data) => {
} this.commitBuffer();
this.set("saved", true);
this.set("model.overridden", data.overridden);
})
.finally(() => this.set("isSaving", false));
},
@action reset() {
reset() { this.setProperties({
this.setProperties({ isSaving: true,
isSaving: true, saved: false,
saved: false, });
}); ajax("robots.json", { type: "DELETE" })
ajax("robots.json", { type: "DELETE" }) .then((data) => {
.then((data) => { this.buffered.set("robots_txt", data.robots_txt);
this.buffered.set("robots_txt", data.robots_txt); this.commitBuffer();
this.commitBuffer(); this.set("saved", true);
this.set("saved", true); this.set("model.overridden", false);
this.set("model.overridden", false); })
}) .finally(() => this.set("isSaving", false));
.finally(() => this.set("isSaving", false)); },
} },
} });

View File

@ -1,24 +1,21 @@
import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { url } from "discourse/lib/computed"; import { url } from "discourse/lib/computed";
export default class AdminCustomizeThemesEditController extends Controller { export default Controller.extend({
section = null; section: null,
currentTarget = 0; currentTarget: 0,
maximized = false; maximized: false,
previewUrl: url("model.id", "/admin/themes/%@/preview"),
@url("model.id", "/admin/themes/%@/preview") previewUrl; showAdvanced: false,
editRouteName: "adminCustomizeThemes.edit",
showAdvanced = false; showRouteName: "adminCustomizeThemes.show",
editRouteName = "adminCustomizeThemes.edit";
showRouteName = "adminCustomizeThemes.show";
setTargetName(name) { setTargetName(name) {
const target = this.get("model.targets").find((t) => t.name === name); const target = this.get("model.targets").find((t) => t.name === name);
this.set("currentTarget", target && target.id); this.set("currentTarget", target && target.id);
} },
@discourseComputed("currentTarget") @discourseComputed("currentTarget")
currentTargetName(id) { currentTargetName(id) {
@ -26,52 +23,50 @@ export default class AdminCustomizeThemesEditController extends Controller {
(t) => t.id === parseInt(id, 10) (t) => t.id === parseInt(id, 10)
); );
return target && target.name; return target && target.name;
} },
@discourseComputed("model.isSaving") @discourseComputed("model.isSaving")
saveButtonText(isSaving) { saveButtonText(isSaving) {
return isSaving ? I18n.t("saving") : I18n.t("admin.customize.save"); return isSaving ? I18n.t("saving") : I18n.t("admin.customize.save");
} },
@discourseComputed("model.changed", "model.isSaving") @discourseComputed("model.changed", "model.isSaving")
saveDisabled(changed, isSaving) { saveDisabled(changed, isSaving) {
return !changed || isSaving; return !changed || isSaving;
} },
@action actions: {
save() { save() {
this.set("saving", true); this.set("saving", true);
this.model.saveChanges("theme_fields").finally(() => { this.model.saveChanges("theme_fields").finally(() => {
this.set("saving", false); this.set("saving", false);
}); });
} },
@action fieldAdded(target, name) {
fieldAdded(target, name) { this.replaceRoute(this.editRouteName, this.get("model.id"), target, name);
this.replaceRoute(this.editRouteName, this.get("model.id"), target, name); },
}
@action onlyOverriddenChanged(onlyShowOverridden) {
onlyOverriddenChanged(onlyShowOverridden) { if (onlyShowOverridden) {
if (onlyShowOverridden) { if (!this.model.hasEdited(this.currentTargetName, this.fieldName)) {
if (!this.model.hasEdited(this.currentTargetName, this.fieldName)) { let firstTarget = this.get("model.targets").find((t) => t.edited);
let firstTarget = this.get("model.targets").find((t) => t.edited); let firstField = this.get(`model.fields.${firstTarget.name}`).find(
let firstField = this.get(`model.fields.${firstTarget.name}`).find( (f) => f.edited
(f) => f.edited );
);
this.replaceRoute( this.replaceRoute(
this.editRouteName, this.editRouteName,
this.get("model.id"), this.get("model.id"),
firstTarget.name, firstTarget.name,
firstField.name firstField.name
); );
}
} }
} },
}
@action goBack() {
goBack() { this.replaceRoute(this.showRouteName, this.model.id);
this.replaceRoute(this.showRouteName, this.model.id); },
} },
} });

View File

@ -1,4 +1,4 @@
import { inject as service } from "@ember/service"; import { COMPONENTS, THEMES } from "admin/models/theme";
import { import {
empty, empty,
filterBy, filterBy,
@ -6,9 +6,8 @@ import {
match, match,
notEmpty, notEmpty,
} from "@ember/object/computed"; } from "@ember/object/computed";
import { COMPONENTS, THEMES } from "admin/models/theme";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import EmberObject, { action } from "@ember/object"; import EmberObject from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import ThemeSettings from "admin/models/theme-settings"; import ThemeSettings from "admin/models/theme-settings";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
@ -16,35 +15,31 @@ import { makeArray } from "discourse-common/lib/helpers";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
import { url } from "discourse/lib/computed"; import { url } from "discourse/lib/computed";
import { inject as service } from "@ember/service";
const THEME_UPLOAD_VAR = 2; const THEME_UPLOAD_VAR = 2;
export default class AdminCustomizeThemesShowController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
downloadUrl: url("model.id", "/admin/customize/themes/%@/export"),
editRouteName = "adminCustomizeThemes.edit"; previewUrl: url("model.id", "/admin/themes/%@/preview"),
addButtonDisabled: empty("selectedChildThemeId"),
@url("model.id", "/admin/customize/themes/%@/export") downloadUrl; editRouteName: "adminCustomizeThemes.edit",
@url("model.id", "/admin/themes/%@/preview") previewUrl; parentThemesNames: mapBy("model.parentThemes", "name"),
@empty("selectedChildThemeId") addButtonDisabled; availableParentThemes: filterBy("allThemes", "component", false),
@mapBy("model.parentThemes", "name") parentThemesNames; availableActiveParentThemes: filterBy("availableParentThemes", "isActive"),
@filterBy("allThemes", "component", false) availableParentThemes; availableThemesNames: mapBy("availableParentThemes", "name"),
@filterBy("availableParentThemes", "isActive") availableActiveParentThemes; availableActiveThemesNames: mapBy("availableActiveParentThemes", "name"),
@mapBy("availableParentThemes", "name") availableThemesNames; availableActiveChildThemes: filterBy("availableChildThemes", "hasParents"),
@mapBy("availableActiveParentThemes", "name") availableActiveThemesNames; availableComponentsNames: mapBy("availableChildThemes", "name"),
@filterBy("availableChildThemes", "hasParents") availableActiveChildThemes; availableActiveComponentsNames: mapBy("availableActiveChildThemes", "name"),
@mapBy("availableChildThemes", "name") availableComponentsNames; childThemesNames: mapBy("model.childThemes", "name"),
@mapBy("availableActiveChildThemes", "name") availableActiveComponentsNames; extraFiles: filterBy("model.theme_fields", "target", "extra_js"),
@mapBy("model.childThemes", "name") childThemesNames;
@filterBy("model.theme_fields", "target", "extra_js") extraFiles;
@notEmpty("settings") hasSettings;
@notEmpty("translations") hasTranslations;
@match("model.remote_theme.remote_url", /^http(s)?:\/\//) sourceIsHttp;
@discourseComputed("model.component", "model.remote_theme") @discourseComputed("model.component", "model.remote_theme")
showCheckboxes() { showCheckboxes() {
return !this.model.component || this.model.remote_theme; return !this.model.component || this.model.remote_theme;
} },
@discourseComputed("model.editedFields") @discourseComputed("model.editedFields")
editedFieldsFormatted() { editedFieldsFormatted() {
@ -62,13 +57,13 @@ export default class AdminCustomizeThemesShowController extends Controller {
descriptions.push(resultString); descriptions.push(resultString);
}); });
return descriptions; return descriptions;
} },
@discourseComputed("colorSchemeId", "model.color_scheme_id") @discourseComputed("colorSchemeId", "model.color_scheme_id")
colorSchemeChanged(colorSchemeId, existingId) { colorSchemeChanged(colorSchemeId, existingId) {
colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId, 10); colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId, 10);
return colorSchemeId !== existingId; return colorSchemeId !== existingId;
} },
@discourseComputed("availableChildThemes", "model.childThemes.[]", "model") @discourseComputed("availableChildThemes", "model.childThemes.[]", "model")
selectableChildThemes(available, childThemes) { selectableChildThemes(available, childThemes) {
@ -78,7 +73,7 @@ export default class AdminCustomizeThemesShowController extends Controller {
: available.filter((theme) => !childThemes.includes(theme)); : available.filter((theme) => !childThemes.includes(theme));
return themes.length === 0 ? null : themes; return themes.length === 0 ? null : themes;
} }
} },
@discourseComputed("model.parentThemes.[]") @discourseComputed("model.parentThemes.[]")
relativesSelectorSettingsForComponent() { relativesSelectorSettingsForComponent() {
@ -96,7 +91,7 @@ export default class AdminCustomizeThemesShowController extends Controller {
allThemes: this.allThemes, allThemes: this.allThemes,
setDefaultValuesLabel: I18n.t("admin.customize.theme.add_all_themes"), setDefaultValuesLabel: I18n.t("admin.customize.theme.add_all_themes"),
}); });
} },
@discourseComputed("model.parentThemes.[]") @discourseComputed("model.parentThemes.[]")
relativesSelectorSettingsForTheme() { relativesSelectorSettingsForTheme() {
@ -114,7 +109,7 @@ export default class AdminCustomizeThemesShowController extends Controller {
allThemes: this.allThemes, allThemes: this.allThemes,
setDefaultValuesLabel: I18n.t("admin.customize.theme.add_all"), setDefaultValuesLabel: I18n.t("admin.customize.theme.add_all"),
}); });
} },
@discourseComputed("allThemes", "model.component", "model") @discourseComputed("allThemes", "model.component", "model")
availableChildThemes(allThemes) { availableChildThemes(allThemes) {
@ -124,36 +119,40 @@ export default class AdminCustomizeThemesShowController extends Controller {
(theme) => theme.get("id") !== themeId && theme.get("component") (theme) => theme.get("id") !== themeId && theme.get("component")
); );
} }
} },
@discourseComputed("model.component") @discourseComputed("model.component")
convertKey(component) { convertKey(component) {
const type = component ? "component" : "theme"; const type = component ? "component" : "theme";
return `admin.customize.theme.convert_${type}`; return `admin.customize.theme.convert_${type}`;
} },
@discourseComputed("model.component") @discourseComputed("model.component")
convertIcon(component) { convertIcon(component) {
return component ? "cube" : ""; return component ? "cube" : "";
} },
@discourseComputed("model.component") @discourseComputed("model.component")
convertTooltip(component) { convertTooltip(component) {
const type = component ? "component" : "theme"; const type = component ? "component" : "theme";
return `admin.customize.theme.convert_${type}_tooltip`; return `admin.customize.theme.convert_${type}_tooltip`;
} },
@discourseComputed("model.settings") @discourseComputed("model.settings")
settings(settings) { settings(settings) {
return settings.map((setting) => ThemeSettings.create(setting)); return settings.map((setting) => ThemeSettings.create(setting));
} },
hasSettings: notEmpty("settings"),
@discourseComputed("model.translations") @discourseComputed("model.translations")
translations(translations) { translations(translations) {
return translations.map((setting) => return translations.map((setting) =>
ThemeSettings.create({ ...setting, textarea: true }) ThemeSettings.create({ ...setting, textarea: true })
); );
} },
hasTranslations: notEmpty("translations"),
@discourseComputed( @discourseComputed(
"model.remote_theme.local_version", "model.remote_theme.local_version",
@ -162,12 +161,12 @@ export default class AdminCustomizeThemesShowController extends Controller {
) )
hasOverwrittenHistory(localVersion, remoteVersion, commitsBehind) { hasOverwrittenHistory(localVersion, remoteVersion, commitsBehind) {
return localVersion !== remoteVersion && commitsBehind === -1; return localVersion !== remoteVersion && commitsBehind === -1;
} },
@discourseComputed("model.remoteError", "updatingRemote") @discourseComputed("model.remoteError", "updatingRemote")
showRemoteError(errorMessage, updating) { showRemoteError(errorMessage, updating) {
return errorMessage && !updating; return errorMessage && !updating;
} },
@discourseComputed( @discourseComputed(
"model.remote_theme.remote_url", "model.remote_theme.remote_url",
@ -176,13 +175,13 @@ export default class AdminCustomizeThemesShowController extends Controller {
) )
finishInstall(remoteUrl, localVersion, commitsBehind) { finishInstall(remoteUrl, localVersion, commitsBehind) {
return remoteUrl && !localVersion && !commitsBehind; return remoteUrl && !localVersion && !commitsBehind;
} },
editedFieldsForTarget(target) { editedFieldsForTarget(target) {
return this.get("model.editedFields").filter( return this.get("model.editedFields").filter(
(field) => field.target === target (field) => field.target === target
); );
} },
commitSwitchType() { commitSwitchType() {
const model = this.model; const model = this.model;
@ -223,8 +222,7 @@ export default class AdminCustomizeThemesShowController extends Controller {
}); });
}) })
.catch(popupAjaxError); .catch(popupAjaxError);
} },
transitionToEditRoute() { transitionToEditRoute() {
this.transitionToRoute( this.transitionToRoute(
this.editRouteName, this.editRouteName,
@ -232,7 +230,8 @@ export default class AdminCustomizeThemesShowController extends Controller {
"common", "common",
"scss" "scss"
); );
} },
sourceIsHttp: match("model.remote_theme.remote_url", /^http(s)?:\/\//),
@discourseComputed( @discourseComputed(
"model.remote_theme.remote_url", "model.remote_theme.remote_url",
@ -242,186 +241,168 @@ export default class AdminCustomizeThemesShowController extends Controller {
return remoteThemeBranch return remoteThemeBranch
? `${remoteThemeUrl.replace(/\.git$/, "")}/tree/${remoteThemeBranch}` ? `${remoteThemeUrl.replace(/\.git$/, "")}/tree/${remoteThemeBranch}`
: remoteThemeUrl; : remoteThemeUrl;
} },
@discourseComputed("model.user.id", "model.default") @discourseComputed("model.user.id", "model.default")
showConvert(userId, defaultTheme) { showConvert(userId, defaultTheme) {
return userId > 0 && !defaultTheme; return userId > 0 && !defaultTheme;
} },
@action actions: {
updateToLatest() { updateToLatest() {
this.set("updatingRemote", true); this.set("updatingRemote", true);
this.model this.model
.updateToLatest() .updateToLatest()
.catch(popupAjaxError) .catch(popupAjaxError)
.finally(() => { .finally(() => {
this.set("updatingRemote", false); this.set("updatingRemote", false);
});
},
checkForThemeUpdates() {
this.set("updatingRemote", true);
this.model
.checkForUpdates()
.catch(popupAjaxError)
.finally(() => {
this.set("updatingRemote", false);
});
},
addUploadModal() {
showModal("admin-add-upload", { admin: true, name: "" });
},
addUpload(info) {
let model = this.model;
model.setField("common", info.name, "", info.upload_id, THEME_UPLOAD_VAR);
model.saveChanges("theme_fields").catch((e) => popupAjaxError(e));
},
cancelChangeScheme() {
this.set("colorSchemeId", this.get("model.color_scheme_id"));
},
changeScheme() {
let schemeId = this.colorSchemeId;
this.set(
"model.color_scheme_id",
schemeId === null ? null : parseInt(schemeId, 10)
);
this.model.saveChanges("color_scheme_id");
},
startEditingName() {
this.set("oldName", this.get("model.name"));
this.set("editingName", true);
},
cancelEditingName() {
this.set("model.name", this.oldName);
this.set("editingName", false);
},
finishedEditingName() {
this.model.saveChanges("name");
this.set("editingName", false);
},
editTheme() {
if (this.get("model.remote_theme.is_git")) {
this.dialog.confirm({
message: I18n.t("admin.customize.theme.edit_confirm"),
didConfirm: () => this.transitionToEditRoute(),
});
} else {
this.transitionToEditRoute();
}
},
applyDefault() {
const model = this.model;
model.saveChanges("default").then(() => {
if (model.get("default")) {
this.allThemes.forEach((theme) => {
if (theme !== model && theme.get("default")) {
theme.set("default", false);
}
});
}
}); });
} },
@action applyUserSelectable() {
checkForThemeUpdates() { this.model.saveChanges("user_selectable");
this.set("updatingRemote", true); },
this.model
.checkForUpdates() applyAutoUpdateable() {
.catch(popupAjaxError) this.model.saveChanges("auto_update");
.finally(() => { },
this.set("updatingRemote", false);
addChildTheme() {
let themeId = parseInt(this.selectedChildThemeId, 10);
let theme = this.allThemes.findBy("id", themeId);
this.model.addChildTheme(theme).then(() => this.store.findAll("theme"));
},
removeUpload(upload) {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.customize.theme.delete_upload_confirm"),
didConfirm: () => this.model.removeField(upload),
}); });
} },
@action removeChildTheme(theme) {
addUploadModal() { this.model
showModal("admin-add-upload", { admin: true, name: "" }); .removeChildTheme(theme)
} .then(() => this.store.findAll("theme"));
},
@action destroy() {
addUpload(info) { return this.dialog.yesNoConfirm({
let model = this.model; message: I18n.t("admin.customize.delete_confirm", {
model.setField("common", info.name, "", info.upload_id, THEME_UPLOAD_VAR); theme_name: this.get("model.name"),
model.saveChanges("theme_fields").catch((e) => popupAjaxError(e)); }),
} didConfirm: () => {
const model = this.model;
@action model.setProperties({ recentlyInstalled: false });
cancelChangeScheme() { model.destroyRecord().then(() => {
this.set("colorSchemeId", this.get("model.color_scheme_id")); this.allThemes.removeObject(model);
} this.transitionToRoute("adminCustomizeThemes");
});
@action },
changeScheme() {
let schemeId = this.colorSchemeId;
this.set(
"model.color_scheme_id",
schemeId === null ? null : parseInt(schemeId, 10)
);
this.model.saveChanges("color_scheme_id");
}
@action
startEditingName() {
this.set("oldName", this.get("model.name"));
this.set("editingName", true);
}
@action
cancelEditingName() {
this.set("model.name", this.oldName);
this.set("editingName", false);
}
@action
finishedEditingName() {
this.model.saveChanges("name");
this.set("editingName", false);
}
@action
editTheme() {
if (this.get("model.remote_theme.is_git")) {
this.dialog.confirm({
message: I18n.t("admin.customize.theme.edit_confirm"),
didConfirm: () => this.transitionToEditRoute(),
}); });
} else { },
this.transitionToEditRoute();
}
}
@action switchType() {
applyDefault() { const relatives = this.get("model.component")
const model = this.model; ? this.get("model.parentThemes")
model.saveChanges("default").then(() => { : this.get("model.childThemes");
if (model.get("default")) {
this.allThemes.forEach((theme) => { let message = I18n.t(`${this.convertKey}_alert_generic`);
if (theme !== model && theme.get("default")) {
theme.set("default", false); if (relatives && relatives.length > 0) {
} message = I18n.t(`${this.convertKey}_alert`, {
relatives: relatives
.map((relative) => relative.get("name"))
.join(", "),
}); });
} }
});
}
@action return this.dialog.yesNoConfirm({
applyUserSelectable() { message,
this.model.saveChanges("user_selectable"); didConfirm: () => this.commitSwitchType(),
}
@action
applyAutoUpdateable() {
this.model.saveChanges("auto_update");
}
@action
addChildTheme() {
let themeId = parseInt(this.selectedChildThemeId, 10);
let theme = this.allThemes.findBy("id", themeId);
this.model.addChildTheme(theme).then(() => this.store.findAll("theme"));
}
@action
removeUpload(upload) {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.customize.theme.delete_upload_confirm"),
didConfirm: () => this.model.removeField(upload),
});
}
@action
removeChildTheme(theme) {
this.model.removeChildTheme(theme).then(() => this.store.findAll("theme"));
}
@action
destroyTheme() {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.customize.delete_confirm", {
theme_name: this.get("model.name"),
}),
didConfirm: () => {
const model = this.model;
model.setProperties({ recentlyInstalled: false });
model.destroyRecord().then(() => {
this.allThemes.removeObject(model);
this.transitionToRoute("adminCustomizeThemes");
});
},
});
}
@action
switchType() {
const relatives = this.get("model.component")
? this.get("model.parentThemes")
: this.get("model.childThemes");
let message = I18n.t(`${this.convertKey}_alert_generic`);
if (relatives && relatives.length > 0) {
message = I18n.t(`${this.convertKey}_alert`, {
relatives: relatives.map((relative) => relative.get("name")).join(", "),
}); });
} },
return this.dialog.yesNoConfirm({ enableComponent() {
message, this.model.set("enabled", true);
didConfirm: () => this.commitSwitchType(), this.model
}); .saveChanges("enabled")
} .catch(() => this.model.set("enabled", false));
},
@action disableComponent() {
enableComponent() { this.model.set("enabled", false);
this.model.set("enabled", true); this.model
this.model .saveChanges("enabled")
.saveChanges("enabled") .catch(() => this.model.set("enabled", true));
.catch(() => this.model.set("enabled", false)); },
} },
});
@action
disableComponent() {
this.model.set("enabled", false);
this.model
.saveChanges("enabled")
.catch(() => this.model.set("enabled", true));
}
}

View File

@ -2,21 +2,21 @@ import Controller from "@ember/controller";
import { THEMES } from "admin/models/theme"; import { THEMES } from "admin/models/theme";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
export default class AdminCustomizeThemesController extends Controller { export default Controller.extend({
currentTab = THEMES; currentTab: THEMES,
@discourseComputed("model", "model.@each.component") @discourseComputed("model", "model.@each.component")
fullThemes(themes) { fullThemes(themes) {
return themes.filter((t) => !t.get("component")); return themes.filter((t) => !t.get("component"));
} },
@discourseComputed("model", "model.@each.component") @discourseComputed("model", "model.@each.component")
childThemes(themes) { childThemes(themes) {
return themes.filter((t) => t.get("component")); return themes.filter((t) => t.get("component"));
} },
@discourseComputed("model.content") @discourseComputed("model.content")
installedThemes(content) { installedThemes(content) {
return content || []; return content || [];
} },
} });

View File

@ -1,9 +1,9 @@
import { computed } from "@ember/object";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import AdminDashboard from "admin/models/admin-dashboard"; import AdminDashboard from "admin/models/admin-dashboard";
import I18n from "I18n"; import I18n from "I18n";
import PeriodComputationMixin from "admin/mixins/period-computation"; import PeriodComputationMixin from "admin/mixins/period-computation";
import Report from "admin/models/report"; import Report from "admin/models/report";
import { computed } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import { makeArray } from "discourse-common/lib/helpers"; import { makeArray } from "discourse-common/lib/helpers";
@ -15,49 +15,41 @@ function staticReport(reportType) {
}); });
} }
export default class AdminDashboardGeneralController extends Controller.extend( export default Controller.extend(PeriodComputationMixin, {
PeriodComputationMixin isLoading: false,
) { dashboardFetchedAt: null,
@controller("exception") exceptionController; exceptionController: controller("exception"),
logSearchQueriesEnabled: setting("log_search_queries"),
isLoading = false;
dashboardFetchedAt = null;
@setting("log_search_queries") logSearchQueriesEnabled;
@staticReport("users_by_type") usersByTypeReport;
@staticReport("users_by_trust_level") usersByTrustLevelReport;
@staticReport("storage_report") storageReport;
@discourseComputed("siteSettings.dashboard_general_tab_activity_metrics") @discourseComputed("siteSettings.dashboard_general_tab_activity_metrics")
activityMetrics(metrics) { activityMetrics(metrics) {
return (metrics || "").split("|").filter(Boolean); return (metrics || "").split("|").filter(Boolean);
} },
@computed("siteSettings.dashboard_hidden_reports") hiddenReports: computed("siteSettings.dashboard_hidden_reports", function () {
get hiddenReports() {
return (this.siteSettings.dashboard_hidden_reports || "") return (this.siteSettings.dashboard_hidden_reports || "")
.split("|") .split("|")
.filter(Boolean); .filter(Boolean);
} }),
@computed("activityMetrics", "hiddenReports") isActivityMetricsVisible: computed(
get isActivityMetricsVisible() { "activityMetrics",
return ( "hiddenReports",
this.activityMetrics.length && function () {
this.activityMetrics.some((x) => !this.hiddenReports.includes(x)) return (
); this.activityMetrics.length &&
} this.activityMetrics.some((x) => !this.hiddenReports.includes(x))
);
}
),
@computed("hiddenReports") isSearchReportsVisible: computed("hiddenReports", function () {
get isSearchReportsVisible() {
return ["top_referred_topics", "trending_search"].some( return ["top_referred_topics", "trending_search"].some(
(x) => !this.hiddenReports.includes(x) (x) => !this.hiddenReports.includes(x)
); );
} }),
@computed("hiddenReports") isCommunityHealthVisible: computed("hiddenReports", function () {
get isCommunityHealthVisible() {
return [ return [
"consolidated_page_views", "consolidated_page_views",
"signups", "signups",
@ -67,7 +59,7 @@ export default class AdminDashboardGeneralController extends Controller.extend(
"daily_engaged_users", "daily_engaged_users",
"new_contributors", "new_contributors",
].some((x) => !this.hiddenReports.includes(x)); ].some((x) => !this.hiddenReports.includes(x));
} }),
@discourseComputed @discourseComputed
activityMetricsFilters() { activityMetricsFilters() {
@ -75,14 +67,14 @@ export default class AdminDashboardGeneralController extends Controller.extend(
startDate: this.lastMonth, startDate: this.lastMonth,
endDate: this.today, endDate: this.today,
}; };
} },
@discourseComputed @discourseComputed
topReferredTopicsOptions() { topReferredTopicsOptions() {
return { return {
table: { total: false, limit: 8 }, table: { total: false, limit: 8 },
}; };
} },
@discourseComputed @discourseComputed
topReferredTopicsFilters() { topReferredTopicsFilters() {
@ -90,7 +82,7 @@ export default class AdminDashboardGeneralController extends Controller.extend(
startDate: moment().subtract(6, "days").startOf("day"), startDate: moment().subtract(6, "days").startOf("day"),
endDate: this.today, endDate: this.today,
}; };
} },
@discourseComputed @discourseComputed
trendingSearchFilters() { trendingSearchFilters() {
@ -98,21 +90,25 @@ export default class AdminDashboardGeneralController extends Controller.extend(
startDate: moment().subtract(1, "month").startOf("day"), startDate: moment().subtract(1, "month").startOf("day"),
endDate: this.today, endDate: this.today,
}; };
} },
@discourseComputed @discourseComputed
trendingSearchOptions() { trendingSearchOptions() {
return { return {
table: { total: false, limit: 8 }, table: { total: false, limit: 8 },
}; };
} },
@discourseComputed @discourseComputed
trendingSearchDisabledLabel() { trendingSearchDisabledLabel() {
return I18n.t("admin.dashboard.reports.trending_search.disabled", { return I18n.t("admin.dashboard.reports.trending_search.disabled", {
basePath: getURL(""), basePath: getURL(""),
}); });
} },
usersByTypeReport: staticReport("users_by_type"),
usersByTrustLevelReport: staticReport("users_by_trust_level"),
storageReport: staticReport("storage_report"),
fetchDashboard() { fetchDashboard() {
if (this.isLoading) { if (this.isLoading) {
@ -141,14 +137,14 @@ export default class AdminDashboardGeneralController extends Controller.extend(
}) })
.finally(() => this.set("isLoading", false)); .finally(() => this.set("isLoading", false));
} }
} },
@discourseComputed("startDate", "endDate") @discourseComputed("startDate", "endDate")
filters(startDate, endDate) { filters(startDate, endDate) {
return { startDate, endDate }; return { startDate, endDate };
} },
_reportsForPeriodURL(period) { _reportsForPeriodURL(period) {
return getURL(`/admin?period=${period}`); return getURL(`/admin?period=${period}`);
} },
} });

View File

@ -1,12 +1,10 @@
import { computed } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import PeriodComputationMixin from "admin/mixins/period-computation"; import PeriodComputationMixin from "admin/mixins/period-computation";
import { computed } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
export default class AdminDashboardModerationController extends Controller.extend( export default Controller.extend(PeriodComputationMixin, {
PeriodComputationMixin
) {
@discourseComputed @discourseComputed
flagsStatusOptions() { flagsStatusOptions() {
return { return {
@ -15,15 +13,17 @@ export default class AdminDashboardModerationController extends Controller.exten
perPage: 10, perPage: 10,
}, },
}; };
} },
@computed("siteSettings.dashboard_hidden_reports") isModeratorsActivityVisible: computed(
get isModeratorsActivityVisible() { "siteSettings.dashboard_hidden_reports",
return !(this.siteSettings.dashboard_hidden_reports || "") function () {
.split("|") return !(this.siteSettings.dashboard_hidden_reports || "")
.filter(Boolean) .split("|")
.includes("moderators_activity"); .filter(Boolean)
} .includes("moderators_activity");
}
),
@discourseComputed @discourseComputed
userFlaggingRatioOptions() { userFlaggingRatioOptions() {
@ -33,19 +33,19 @@ export default class AdminDashboardModerationController extends Controller.exten
perPage: 10, perPage: 10,
}, },
}; };
} },
@discourseComputed("startDate", "endDate") @discourseComputed("startDate", "endDate")
filters(startDate, endDate) { filters(startDate, endDate) {
return { startDate, endDate }; return { startDate, endDate };
} },
@discourseComputed("lastWeek", "endDate") @discourseComputed("lastWeek", "endDate")
lastWeekfilters(startDate, endDate) { lastWeekfilters(startDate, endDate) {
return { startDate, endDate }; return { startDate, endDate };
} },
_reportsForPeriodURL(period) { _reportsForPeriodURL(period) {
return getURL(`/admin/dashboard/moderation?period=${period}`); return getURL(`/admin/dashboard/moderation?period=${period}`);
} },
} });

View File

@ -2,10 +2,10 @@ import Controller from "@ember/controller";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { action, get } from "@ember/object"; import { get } from "@ember/object";
export default class AdminDashboardReportsController extends Controller { export default Controller.extend({
filter = null; filter: null,
@discourseComputed( @discourseComputed(
"model.[]", "model.[]",
@ -29,14 +29,15 @@ export default class AdminDashboardReportsController extends Controller {
reports = reports.filter((report) => !hiddenReports.includes(report.type)); reports = reports.filter((report) => !hiddenReports.includes(report.type));
return reports; return reports;
} },
@action actions: {
filterReports(filter) { filterReports(filter) {
discourseDebounce(this, this._performFiltering, filter, INPUT_DELAY); discourseDebounce(this, this._performFiltering, filter, INPUT_DELAY);
} },
},
_performFiltering(filter) { _performFiltering(filter) {
this.set("filter", filter); this.set("filter", filter);
} },
} });

View File

@ -1,19 +1,17 @@
import { action, computed } from "@ember/object";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import AdminDashboard from "admin/models/admin-dashboard"; import AdminDashboard from "admin/models/admin-dashboard";
import VersionCheck from "admin/models/version-check"; import VersionCheck from "admin/models/version-check";
import { computed } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { setting } from "discourse/lib/computed"; import { setting } from "discourse/lib/computed";
const PROBLEMS_CHECK_MINUTES = 1; const PROBLEMS_CHECK_MINUTES = 1;
export default class AdminDashboardController extends Controller { export default Controller.extend({
@controller("exception") exceptionController; isLoading: false,
dashboardFetchedAt: null,
isLoading = false; exceptionController: controller("exception"),
dashboardFetchedAt = null; showVersionChecks: setting("version_checks"),
@setting("version_checks") showVersionChecks;
@discourseComputed( @discourseComputed(
"lowPriorityProblems.length", "lowPriorityProblems.length",
@ -23,29 +21,25 @@ export default class AdminDashboardController extends Controller {
const problemsLength = const problemsLength =
lowPriorityProblemsLength + highPriorityProblemsLength; lowPriorityProblemsLength + highPriorityProblemsLength;
return this.currentUser.admin && problemsLength > 0; return this.currentUser.admin && problemsLength > 0;
} },
@computed("siteSettings.dashboard_visible_tabs") visibleTabs: computed("siteSettings.dashboard_visible_tabs", function () {
get visibleTabs() {
return (this.siteSettings.dashboard_visible_tabs || "") return (this.siteSettings.dashboard_visible_tabs || "")
.split("|") .split("|")
.filter(Boolean); .filter(Boolean);
} }),
@computed("visibleTabs") isModerationTabVisible: computed("visibleTabs", function () {
get isModerationTabVisible() {
return this.visibleTabs.includes("moderation"); return this.visibleTabs.includes("moderation");
} }),
@computed("visibleTabs") isSecurityTabVisible: computed("visibleTabs", function () {
get isSecurityTabVisible() {
return this.visibleTabs.includes("security"); return this.visibleTabs.includes("security");
} }),
@computed("visibleTabs") isReportsTabVisible: computed("visibleTabs", function () {
get isReportsTabVisible() {
return this.visibleTabs.includes("reports"); return this.visibleTabs.includes("reports");
} }),
fetchProblems() { fetchProblems() {
if (this.isLoadingProblems) { if (this.isLoadingProblems) {
@ -59,7 +53,7 @@ export default class AdminDashboardController extends Controller {
) { ) {
this._loadProblems(); this._loadProblems();
} }
} },
fetchDashboard() { fetchDashboard() {
const versionChecks = this.siteSettings.version_checks; const versionChecks = this.siteSettings.version_checks;
@ -94,7 +88,7 @@ export default class AdminDashboardController extends Controller {
this.set("isLoading", false); this.set("isLoading", false);
}); });
} }
} },
_loadProblems() { _loadProblems() {
this.setProperties({ this.setProperties({
@ -114,15 +108,16 @@ export default class AdminDashboardController extends Controller {
); );
}) })
.finally(() => this.set("loadingProblems", false)); .finally(() => this.set("loadingProblems", false));
} },
@discourseComputed("problemsFetchedAt") @discourseComputed("problemsFetchedAt")
problemsTimestamp(problemsFetchedAt) { problemsTimestamp(problemsFetchedAt) {
return moment(problemsFetchedAt).locale("en").format("LLL"); return moment(problemsFetchedAt).locale("en").format("LLL");
} },
@action actions: {
refreshProblems() { refreshProblems() {
this._loadProblems(); this._loadProblems();
} },
} },
});

View File

@ -1,31 +1,31 @@
import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
export default class AdminEmailAdvancedTestController extends Controller { export default Controller.extend({
email = null; email: null,
text = null; text: null,
elided = null; elided: null,
format = null; format: null,
loading = null; loading: null,
@action actions: {
run() { run() {
this.set("loading", true); this.set("loading", true);
ajax("/admin/email/advanced-test", { ajax("/admin/email/advanced-test", {
type: "POST", type: "POST",
data: { email: this.email }, data: { email: this.email },
})
.then((data) => {
this.setProperties({
text: data.text,
elided: data.elided,
format: data.format,
});
}) })
.catch(popupAjaxError) .then((data) => {
.finally(() => this.set("loading", false)); this.setProperties({
} text: data.text,
} elided: data.elided,
format: data.format,
});
})
.catch(popupAjaxError)
.finally(() => this.set("loading", false));
},
},
});

View File

@ -1,18 +1,18 @@
import AdminEmailLogsController from "admin/controllers/admin-email-logs"; import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
import { action } from "@ember/object"; import { action } from "@ember/object";
export default class AdminEmailBouncedController extends AdminEmailLogsController { export default AdminEmailLogsController.extend({
@action @action
handleShowIncomingEmail(id, event) { handleShowIncomingEmail(id, event) {
event?.preventDefault(); event?.preventDefault();
this.send("showIncomingEmail", id); this.send("showIncomingEmail", id);
} },
@observes("filter.{status,user,address,type}") @observes("filter.{status,user,address,type}")
filterEmailLogs() { filterEmailLogs() {
discourseDebounce(this, this.loadLogs, INPUT_DELAY); discourseDebounce(this, this.loadLogs, INPUT_DELAY);
} },
} });

View File

@ -1,22 +1,21 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { empty } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { observes } from "@ember-decorators/object"; import { empty } from "@ember/object/computed";
import { observes } from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import { escapeExpression } from "discourse/lib/utilities"; import { escapeExpression } from "discourse/lib/utilities";
export default class AdminEmailIndexController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
/** /**
Is the "send test email" button disabled? Is the "send test email" button disabled?
@property sendTestEmailDisabled @property sendTestEmailDisabled
**/ **/
@empty("testEmailAddress") sendTestEmailDisabled; sendTestEmailDisabled: empty("testEmailAddress"),
/** /**
Clears the 'sentTestEmail' property on successful send. Clears the 'sentTestEmail' property on successful send.
@ -26,40 +25,43 @@ export default class AdminEmailIndexController extends Controller {
@observes("testEmailAddress") @observes("testEmailAddress")
testEmailAddressChanged() { testEmailAddressChanged() {
this.set("sentTestEmail", false); this.set("sentTestEmail", false);
} },
/** actions: {
Sends a test email to the currently entered email address /**
Sends a test email to the currently entered email address
@method sendTestEmail @method sendTestEmail
**/ **/
@action sendTestEmail() {
sendTestEmail() { this.setProperties({
this.setProperties({ sendingEmail: true,
sendingEmail: true, sentTestEmail: false,
sentTestEmail: false, });
});
ajax("/admin/email/test", { ajax("/admin/email/test", {
type: "POST", type: "POST",
data: { email_address: this.testEmailAddress }, data: { email_address: this.testEmailAddress },
})
.then((response) =>
this.set("sentTestEmailMessage", response.sent_test_email_message)
)
.catch((e) => {
if (e.jqXHR.responseJSON?.errors) {
this.dialog.alert({
message: htmlSafe(
I18n.t("admin.email.error", {
server_error: escapeExpression(e.jqXHR.responseJSON.errors[0]),
})
),
});
} else {
this.dialog.alert({ message: I18n.t("admin.email.test_error") });
}
}) })
.finally(() => this.set("sendingEmail", false)); .then((response) =>
} this.set("sentTestEmailMessage", response.sent_test_email_message)
} )
.catch((e) => {
if (e.jqXHR.responseJSON?.errors) {
this.dialog.alert({
message: htmlSafe(
I18n.t("admin.email.error", {
server_error: escapeExpression(
e.jqXHR.responseJSON.errors[0]
),
})
),
});
} else {
this.dialog.alert({ message: I18n.t("admin.email.test_error") });
}
})
.finally(() => this.set("sendingEmail", false));
},
},
});

View File

@ -1,11 +1,14 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import EmailLog from "admin/models/email-log"; import EmailLog from "admin/models/email-log";
import EmberObject, { action } from "@ember/object"; import EmberObject from "@ember/object";
export default class AdminEmailLogsController extends Controller { export default Controller.extend({
loading = false; loading: false,
filter = EmberObject.create();
init() {
this._super(...arguments);
this.set("filter", EmberObject.create());
},
loadLogs(sourceModel, loadMore) { loadLogs(sourceModel, loadMore) {
if ((loadMore && this.loading) || this.get("model.allLoaded")) { if ((loadMore && this.loading) || this.get("model.allLoaded")) {
return; return;
@ -35,10 +38,11 @@ export default class AdminEmailLogsController extends Controller {
} }
}) })
.finally(() => this.set("loading", false)); .finally(() => this.set("loading", false));
} },
@action actions: {
loadMore() { loadMore() {
this.loadLogs(EmailLog, true); this.loadLogs(EmailLog, true);
} },
} },
});

View File

@ -1,67 +1,66 @@
import { inject as service } from "@ember/service";
import { empty, notEmpty, or } from "@ember/object/computed"; import { empty, notEmpty, or } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import EmailPreview from "admin/models/email-preview"; import EmailPreview from "admin/models/email-preview";
import { action, get } from "@ember/object"; import { action, get } from "@ember/object";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default class AdminEmailPreviewDigestController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
username: null,
username = null; lastSeen: null,
lastSeen = null; emailEmpty: empty("email"),
sendEmailDisabled: or("emailEmpty", "sendingEmail"),
@empty("email") emailEmpty; showSendEmailForm: notEmpty("model.html_content"),
@or("emailEmpty", "sendingEmail") sendEmailDisabled; htmlEmpty: empty("model.html_content"),
@notEmpty("model.html_content") showSendEmailForm;
@empty("model.html_content") htmlEmpty;
@action @action
toggleShowHtml(event) { toggleShowHtml(event) {
event?.preventDefault(); event?.preventDefault();
this.toggleProperty("showHtml"); this.toggleProperty("showHtml");
} },
@action actions: {
updateUsername(selected) { updateUsername(selected) {
this.set("username", get(selected, "firstObject")); this.set("username", get(selected, "firstObject"));
} },
@action refresh() {
refresh() { const model = this.model;
const model = this.model;
this.set("loading", true); this.set("loading", true);
this.set("sentEmail", false); this.set("sentEmail", false);
let username = this.username; let username = this.username;
if (!username) { if (!username) {
username = this.currentUser.get("username"); username = this.currentUser.get("username");
this.set("username", username); this.set("username", username);
} }
EmailPreview.findDigest(username, this.lastSeen).then((email) => { EmailPreview.findDigest(username, this.lastSeen).then((email) => {
model.setProperties(email.getProperties("html_content", "text_content")); model.setProperties(
this.set("loading", false); email.getProperties("html_content", "text_content")
}); );
} this.set("loading", false);
@action
sendEmail() {
this.set("sendingEmail", true);
this.set("sentEmail", false);
EmailPreview.sendDigest(this.username, this.lastSeen, this.email)
.then((result) => {
if (result.errors) {
this.dialog.alert(result.errors);
} else {
this.set("sentEmail", true);
}
})
.catch(popupAjaxError)
.finally(() => {
this.set("sendingEmail", false);
}); });
} },
}
sendEmail() {
this.set("sendingEmail", true);
this.set("sentEmail", false);
EmailPreview.sendDigest(this.username, this.lastSeen, this.email)
.then((result) => {
if (result.errors) {
this.dialog.alert(result.errors);
} else {
this.set("sentEmail", true);
}
})
.catch(popupAjaxError)
.finally(() => {
this.set("sendingEmail", false);
});
},
},
});

View File

@ -1,18 +1,18 @@
import { action } from "@ember/object";
import AdminEmailLogsController from "admin/controllers/admin-email-logs"; import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import IncomingEmail from "admin/models/incoming-email"; import IncomingEmail from "admin/models/incoming-email";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
export default class AdminEmailReceivedController extends AdminEmailLogsController { export default AdminEmailLogsController.extend({
@observes("filter.{status,from,to,subject}") @observes("filter.{status,from,to,subject}")
filterIncomingEmails() { filterIncomingEmails() {
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY); discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);
} },
@action actions: {
loadMore() { loadMore() {
this.loadLogs(IncomingEmail, true); this.loadLogs(IncomingEmail, true);
} },
} },
});

View File

@ -2,23 +2,24 @@ import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import IncomingEmail from "admin/models/incoming-email"; import IncomingEmail from "admin/models/incoming-email";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
import { action } from "@ember/object"; import { action } from "@ember/object";
export default class AdminEmailRejectedController extends AdminEmailLogsController { export default AdminEmailLogsController.extend({
@observes("filter.{status,from,to,subject,error}") @observes("filter.{status,from,to,subject,error}")
filterIncomingEmails() { filterIncomingEmails() {
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY); discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);
} },
@action @action
handleShowIncomingEmail(id, event) { handleShowIncomingEmail(id, event) {
event?.preventDefault(); event?.preventDefault();
this.send("showIncomingEmail", id); this.send("showIncomingEmail", id);
} },
@action actions: {
loadMore() { loadMore() {
this.loadLogs(IncomingEmail, true); this.loadLogs(IncomingEmail, true);
} },
} },
});

View File

@ -1,11 +1,11 @@
import AdminEmailLogsController from "admin/controllers/admin-email-logs"; import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
export default class AdminEmailSentController extends AdminEmailLogsController { export default AdminEmailLogsController.extend({
@observes("filter.{status,user,address,type,reply_key}") @observes("filter.{status,user,address,type,reply_key}")
filterEmailLogs() { filterEmailLogs() {
discourseDebounce(this, this.loadLogs, INPUT_DELAY); discourseDebounce(this, this.loadLogs, INPUT_DELAY);
} },
} });

View File

@ -1,11 +1,11 @@
import AdminEmailLogsController from "admin/controllers/admin-email-logs"; import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
export default class AdminEmailSkippedController extends AdminEmailLogsController { export default AdminEmailLogsController.extend({
@observes("filter.{status,user,address,type}") @observes("filter.{status,user,address,type}")
filterEmailLogs() { filterEmailLogs() {
discourseDebounce(this, this.loadLogs, INPUT_DELAY); discourseDebounce(this, this.loadLogs, INPUT_DELAY);
} },
} });

View File

@ -1,30 +1,25 @@
import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
export default class AdminEmbeddingController extends Controller { export default Controller.extend({
saved = false; saved: false,
embedding = null; embedding: null,
// show settings if we have at least one created host // show settings if we have at least one created host
@discourseComputed("embedding.embeddable_hosts.@each.isCreated") @discourseComputed("embedding.embeddable_hosts.@each.isCreated")
showSecondary() { showSecondary() {
const hosts = this.get("embedding.embeddable_hosts"); const hosts = this.get("embedding.embeddable_hosts");
return hosts.length && hosts.findBy("isCreated"); return hosts.length && hosts.findBy("isCreated");
} },
@discourseComputed("embedding.base_url") @discourseComputed("embedding.base_url")
embeddingCode(baseUrl) { embeddingCode(baseUrl) {
const html = `<div id='discourse-comments'></div> const html = `<div id='discourse-comments'></div>
<meta name='discourse-username' content='DISCOURSE_USERNAME'>
<script type="text/javascript"> <script type="text/javascript">
DiscourseEmbed = { DiscourseEmbed = { discourseUrl: '${baseUrl}/',
discourseUrl: '${baseUrl}/', discourseEmbedUrl: 'REPLACE_ME' };
discourseEmbedUrl: 'EMBED_URL',
// className: 'CLASS_NAME',
};
(function() { (function() {
var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true; var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
@ -34,28 +29,27 @@ export default class AdminEmbeddingController extends Controller {
</script>`; </script>`;
return html; return html;
} },
@action actions: {
saveChanges() { saveChanges() {
const embedding = this.embedding; const embedding = this.embedding;
const updates = embedding.getProperties(embedding.get("fields")); const updates = embedding.getProperties(embedding.get("fields"));
this.set("saved", false); this.set("saved", false);
this.embedding this.embedding
.update(updates) .update(updates)
.then(() => this.set("saved", true)) .then(() => this.set("saved", true))
.catch(popupAjaxError); .catch(popupAjaxError);
} },
@action addHost() {
addHost() { const host = this.store.createRecord("embeddable-host");
const host = this.store.createRecord("embeddable-host"); this.get("embedding.embeddable_hosts").pushObject(host);
this.get("embedding.embeddable_hosts").pushObject(host); },
}
@action deleteHost(host) {
deleteHost(host) { this.get("embedding.embeddable_hosts").removeObject(host);
this.get("embedding.embeddable_hosts").removeObject(host); },
} },
} });

View File

@ -1,46 +1,49 @@
import { inject as service } from "@ember/service";
import { sort } from "@ember/object/computed";
import EmberObject, { action, computed } from "@ember/object"; import EmberObject, { action, computed } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { sort } from "@ember/object/computed";
import { inject as service } from "@ember/service";
const ALL_FILTER = "all"; const ALL_FILTER = "all";
export default class AdminEmojisController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
filter: null,
sorting: null,
filter = null;
sorting = null;
@sort("filteredEmojis.[]", "sorting") sortedEmojis;
init() { init() {
super.init(...arguments); this._super(...arguments);
this.setProperties({ this.setProperties({
filter: ALL_FILTER, filter: ALL_FILTER,
sorting: ["group", "name"], sorting: ["group", "name"],
}); });
} },
@computed("model") sortedEmojis: sort("filteredEmojis.[]", "sorting"),
get emojiGroups() {
return this.model.mapBy("group").uniq();
}
@computed("emojiGroups.[]") emojiGroups: computed("model", {
get sortingGroups() { get() {
return [ALL_FILTER].concat(this.emojiGroups); return this.model.mapBy("group").uniq();
} },
}),
@computed("model.[]", "filter") sortingGroups: computed("emojiGroups.[]", {
get filteredEmojis() { get() {
if (!this.filter || this.filter === ALL_FILTER) { return [ALL_FILTER].concat(this.emojiGroups);
return this.model; },
} else { }),
return this.model.filterBy("group", this.filter);
} filteredEmojis: computed("model.[]", "filter", {
} get() {
if (!this.filter || this.filter === ALL_FILTER) {
return this.model;
} else {
return this.model.filterBy("group", this.filter);
}
},
}),
_highlightEmojiList() { _highlightEmojiList() {
const customEmojiListEl = document.querySelector("#custom_emoji"); const customEmojiListEl = document.querySelector("#custom_emoji");
@ -53,12 +56,12 @@ export default class AdminEmojisController extends Controller {
customEmojiListEl.classList.remove("highlighted"); customEmojiListEl.classList.remove("highlighted");
}); });
} }
} },
@action @action
filterGroups(value) { filterGroups(value) {
this.set("filter", value); this.set("filter", value);
} },
@action @action
emojiUploaded(emoji, group) { emojiUploaded(emoji, group) {
@ -66,7 +69,7 @@ export default class AdminEmojisController extends Controller {
emoji.group = group; emoji.group = group;
this.model.pushObject(EmberObject.create(emoji)); this.model.pushObject(EmberObject.create(emoji));
this._highlightEmojiList(); this._highlightEmojiList();
} },
@action @action
destroyEmoji(emoji) { destroyEmoji(emoji) {
@ -82,5 +85,5 @@ export default class AdminEmojisController extends Controller {
}); });
}, },
}); });
} },
} });

View File

@ -1,24 +1,23 @@
import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import ScreenedEmail from "admin/models/screened-email"; import ScreenedEmail from "admin/models/screened-email";
import { exportEntity } from "discourse/lib/export-csv"; import { exportEntity } from "discourse/lib/export-csv";
import { outputExportResult } from "discourse/lib/export-result"; import { outputExportResult } from "discourse/lib/export-result";
export default class AdminLogsScreenedEmailsController extends Controller { export default Controller.extend({
loading = false; loading: false,
@action actions: {
clearBlock(row) { clearBlock(row) {
row.clearBlock().then(function () { row.clearBlock().then(function () {
// feeling lazy // feeling lazy
window.location.reload(); window.location.reload();
}); });
} },
@action exportScreenedEmailList() {
exportScreenedEmailList() { exportEntity("screened_email").then(outputExportResult);
exportEntity("screened_email").then(outputExportResult); },
} },
show() { show() {
this.set("loading", true); this.set("loading", true);
@ -26,5 +25,5 @@ export default class AdminLogsScreenedEmailsController extends Controller {
this.set("model", result); this.set("model", result);
this.set("loading", false); this.set("loading", false);
}); });
} },
} });

View File

@ -1,32 +1,31 @@
import { inject as service } from "@ember/service";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import ScreenedIpAddress from "admin/models/screened-ip-address"; import ScreenedIpAddress from "admin/models/screened-ip-address";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { exportEntity } from "discourse/lib/export-csv"; import { exportEntity } from "discourse/lib/export-csv";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
import { outputExportResult } from "discourse/lib/export-result"; import { outputExportResult } from "discourse/lib/export-result";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { inject as service } from "@ember/service";
export default class AdminLogsScreenedIpAddressesController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
loading: false,
loading = false; filter: null,
filter = null; savedIpAddress: null,
savedIpAddress = null;
_debouncedShow() { _debouncedShow() {
this.set("loading", true); this.set("loading", true);
ScreenedIpAddress.findAll(this.filter).then((result) => { ScreenedIpAddress.findAll(this.filter).then((result) => {
this.setProperties({ model: result, loading: false }); this.setProperties({ model: result, loading: false });
}); });
} },
@observes("filter") @observes("filter")
show() { show() {
discourseDebounce(this, this._debouncedShow, INPUT_DELAY); discourseDebounce(this, this._debouncedShow, INPUT_DELAY);
} },
@action @action
edit(record, event) { edit(record, event) {
@ -35,86 +34,81 @@ export default class AdminLogsScreenedIpAddressesController extends Controller {
this.set("savedIpAddress", record.get("ip_address")); this.set("savedIpAddress", record.get("ip_address"));
} }
record.set("editing", true); record.set("editing", true);
} },
@action actions: {
allow(record) { allow(record) {
record.set("action_name", "do_nothing"); record.set("action_name", "do_nothing");
record.save(); record.save();
} },
@action block(record) {
block(record) { record.set("action_name", "block");
record.set("action_name", "block"); record.save();
record.save(); },
}
@action cancel(record) {
cancel(record) { const savedIpAddress = this.savedIpAddress;
const savedIpAddress = this.savedIpAddress; if (savedIpAddress && record.get("editing")) {
if (savedIpAddress && record.get("editing")) { record.set("ip_address", savedIpAddress);
record.set("ip_address", savedIpAddress); }
} record.set("editing", false);
record.set("editing", false); },
}
@action save(record) {
save(record) { const wasEditing = record.get("editing");
const wasEditing = record.get("editing"); record.set("editing", false);
record.set("editing", false); record
record .save()
.save() .then(() => this.set("savedIpAddress", null))
.then(() => this.set("savedIpAddress", null)) .catch((e) => {
.catch((e) => { if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) {
this.dialog.alert(
I18n.t("generic_error_with_reason", {
error: e.jqXHR.responseJSON.errors.join(". "),
})
);
} else {
this.dialog.alert(I18n.t("generic_error"));
}
if (wasEditing) {
record.set("editing", true);
}
});
}
@action
destroyRecord(record) {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.logs.screened_ips.delete_confirm", {
ip_address: record.get("ip_address"),
}),
didConfirm: () => {
return record
.destroy()
.then((deleted) => {
if (deleted) {
this.model.removeObject(record);
} else {
this.dialog.alert(I18n.t("generic_error"));
}
})
.catch((e) => {
this.dialog.alert( this.dialog.alert(
I18n.t("generic_error_with_reason", { I18n.t("generic_error_with_reason", {
error: `http: ${e.status} - ${e.body}`, error: e.jqXHR.responseJSON.errors.join(". "),
}) })
); );
}); } else {
}, this.dialog.alert(I18n.t("generic_error"));
}); }
} if (wasEditing) {
record.set("editing", true);
}
});
},
@action destroy(record) {
recordAdded(arg) { return this.dialog.yesNoConfirm({
this.model.unshiftObject(arg); message: I18n.t("admin.logs.screened_ips.delete_confirm", {
} ip_address: record.get("ip_address"),
}),
didConfirm: () => {
return record
.destroy()
.then((deleted) => {
if (deleted) {
this.model.removeObject(record);
} else {
this.dialog.alert(I18n.t("generic_error"));
}
})
.catch((e) => {
this.dialog.alert(
I18n.t("generic_error_with_reason", {
error: `http: ${e.status} - ${e.body}`,
})
);
});
},
});
},
@action recordAdded(arg) {
exportScreenedIpList() { this.model.unshiftObject(arg);
exportEntity("screened_ip").then(outputExportResult); },
}
} exportScreenedIpList() {
exportEntity("screened_ip").then(outputExportResult);
},
},
});

View File

@ -1,11 +1,10 @@
import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import ScreenedUrl from "admin/models/screened-url"; import ScreenedUrl from "admin/models/screened-url";
import { exportEntity } from "discourse/lib/export-csv"; import { exportEntity } from "discourse/lib/export-csv";
import { outputExportResult } from "discourse/lib/export-result"; import { outputExportResult } from "discourse/lib/export-result";
export default class AdminLogsScreenedUrlsController extends Controller { export default Controller.extend({
loading = false; loading: false,
show() { show() {
this.set("loading", true); this.set("loading", true);
@ -13,10 +12,11 @@ export default class AdminLogsScreenedUrlsController extends Controller {
this.set("model", result); this.set("model", result);
this.set("loading", false); this.set("loading", false);
}); });
} },
@action actions: {
exportScreenedUrlList() { exportScreenedUrlList() {
exportEntity("screened_url").then(outputExportResult); exportEntity("screened_url").then(outputExportResult);
} },
} },
});

View File

@ -7,21 +7,22 @@ import { outputExportResult } from "discourse/lib/export-result";
import { scheduleOnce } from "@ember/runloop"; import { scheduleOnce } from "@ember/runloop";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
export default class AdminLogsStaffActionLogsController extends Controller { export default Controller.extend({
queryParams = ["filters"]; queryParams: ["filters"],
model = null;
filters = null; model: null,
userHistoryActions = null; filters: null,
userHistoryActions: null,
@discourseComputed("filters.action_name") @discourseComputed("filters.action_name")
actionFilter(name) { actionFilter(name) {
return name ? I18n.t("admin.logs.staff_actions.actions." + name) : null; return name ? I18n.t("admin.logs.staff_actions.actions." + name) : null;
} },
@discourseComputed("filters") @discourseComputed("filters")
filtersExists(filters) { filtersExists(filters) {
return filters && Object.keys(filters).length > 0; return filters && Object.keys(filters).length > 0;
} },
_refresh() { _refresh() {
this.store.findAll("staff-action-log", this.filters).then((result) => { this.store.findAll("staff-action-log", this.filters).then((result) => {
@ -43,11 +44,11 @@ export default class AdminLogsStaffActionLogsController extends Controller {
); );
} }
}); });
} },
scheduleRefresh() { scheduleRefresh() {
scheduleOnce("afterRender", this, this._refresh); scheduleOnce("afterRender", this, this._refresh);
} },
resetFilters() { resetFilters() {
this.setProperties({ this.setProperties({
@ -55,7 +56,7 @@ export default class AdminLogsStaffActionLogsController extends Controller {
filters: EmberObject.create(), filters: EmberObject.create(),
}); });
this.scheduleRefresh(); this.scheduleRefresh();
} },
changeFilters(props) { changeFilters(props) {
this.set("model", EmberObject.create({ loadingMore: true })); this.set("model", EmberObject.create({ loadingMore: true }));
@ -75,7 +76,7 @@ export default class AdminLogsStaffActionLogsController extends Controller {
this.send("onFiltersChange", this.filters); this.send("onFiltersChange", this.filters);
this.scheduleRefresh(); this.scheduleRefresh();
} },
@action @action
filterActionIdChanged(filterActionId) { filterActionIdChanged(filterActionId) {
@ -86,7 +87,7 @@ export default class AdminLogsStaffActionLogsController extends Controller {
.action_id, .action_id,
}); });
} }
} },
@action @action
clearFilter(key, event) { clearFilter(key, event) {
@ -101,14 +102,14 @@ export default class AdminLogsStaffActionLogsController extends Controller {
} else { } else {
this.changeFilters({ [key]: null }); this.changeFilters({ [key]: null });
} }
} },
@action @action
clearAllFilters(event) { clearAllFilters(event) {
event?.preventDefault(); event?.preventDefault();
this.set("filterActionId", null); this.set("filterActionId", null);
this.resetFilters(); this.resetFilters();
} },
@action @action
filterByAction(logItem, event) { filterByAction(logItem, event) {
@ -118,35 +119,35 @@ export default class AdminLogsStaffActionLogsController extends Controller {
action_id: logItem.get("action"), action_id: logItem.get("action"),
custom_type: logItem.get("custom_type"), custom_type: logItem.get("custom_type"),
}); });
} },
@action @action
filterByStaffUser(acting_user, event) { filterByStaffUser(acting_user, event) {
event?.preventDefault(); event?.preventDefault();
this.changeFilters({ acting_user: acting_user.username }); this.changeFilters({ acting_user: acting_user.username });
} },
@action @action
filterByTargetUser(target_user, event) { filterByTargetUser(target_user, event) {
event?.preventDefault(); event?.preventDefault();
this.changeFilters({ target_user: target_user.username }); this.changeFilters({ target_user: target_user.username });
} },
@action @action
filterBySubject(subject, event) { filterBySubject(subject, event) {
event?.preventDefault(); event?.preventDefault();
this.changeFilters({ subject }); this.changeFilters({ subject });
} },
@action @action
exportStaffActionLogs() { exportStaffActionLogs() {
exportEntity("staff_action").then(outputExportResult); exportEntity("staff_action").then(outputExportResult);
} },
@action @action
loadMore() { loadMore() {
this.model.loadMore(); this.model.loadMore();
} },
@action @action
showDetailsModal(model, event) { showDetailsModal(model, event) {
@ -156,7 +157,7 @@ export default class AdminLogsStaffActionLogsController extends Controller {
admin: true, admin: true,
modalClass: "log-details-modal", modalClass: "log-details-modal",
}); });
} },
@action @action
showCustomDetailsModal(model, event) { showCustomDetailsModal(model, event) {
@ -167,5 +168,5 @@ export default class AdminLogsStaffActionLogsController extends Controller {
modalClass: "history-modal", modalClass: "history-modal",
}); });
modal.loadDiff(); modal.loadDiff();
} },
} });

View File

@ -1,63 +1,59 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { or } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import Permalink from "admin/models/permalink"; import Permalink from "admin/models/permalink";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
import { clipboardCopy } from "discourse/lib/utilities"; import { clipboardCopy } from "discourse/lib/utilities";
import { inject as service } from "@ember/service";
import { or } from "@ember/object/computed";
export default class AdminPermalinksController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
loading: false,
loading = false; filter: null,
filter = null; showSearch: or("model.length", "filter"),
@or("model.length", "filter") showSearch;
_debouncedShow() { _debouncedShow() {
Permalink.findAll(this.filter).then((result) => { Permalink.findAll(this.filter).then((result) => {
this.set("model", result); this.set("model", result);
this.set("loading", false); this.set("loading", false);
}); });
} },
@observes("filter") @observes("filter")
show() { show() {
discourseDebounce(this, this._debouncedShow, INPUT_DELAY); discourseDebounce(this, this._debouncedShow, INPUT_DELAY);
} },
@action actions: {
recordAdded(arg) { recordAdded(arg) {
this.model.unshiftObject(arg); this.model.unshiftObject(arg);
} },
@action copyUrl(pl) {
copyUrl(pl) { let linkElement = document.querySelector(`#admin-permalink-${pl.id}`);
let linkElement = document.querySelector(`#admin-permalink-${pl.id}`); clipboardCopy(linkElement.textContent);
clipboardCopy(linkElement.textContent); },
}
@action destroy(record) {
destroyRecord(record) { return this.dialog.yesNoConfirm({
return this.dialog.yesNoConfirm({ message: I18n.t("admin.permalink.delete_confirm"),
message: I18n.t("admin.permalink.delete_confirm"), didConfirm: () => {
didConfirm: () => { return record.destroy().then(
return record.destroy().then( (deleted) => {
(deleted) => { if (deleted) {
if (deleted) { this.model.removeObject(record);
this.model.removeObject(record); } else {
} else { this.dialog.alert(I18n.t("generic_error"));
}
},
function () {
this.dialog.alert(I18n.t("generic_error")); this.dialog.alert(I18n.t("generic_error"));
} }
}, );
function () { },
this.dialog.alert(I18n.t("generic_error")); });
} },
); },
}, });
});
}
}

View File

@ -1,18 +1,18 @@
import { inject as service } from "@ember/service";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { inject as service } from "@ember/service";
export default class AdminPluginsController extends Controller { export default Controller.extend({
@service router; router: service(),
get adminRoutes() { get adminRoutes() {
return this.allAdminRoutes.filter((r) => this.routeExists(r.full_location)); return this.allAdminRoutes.filter((r) => this.routeExists(r.full_location));
} },
get brokenAdminRoutes() { get brokenAdminRoutes() {
return this.allAdminRoutes.filter( return this.allAdminRoutes.filter(
(r) => !this.routeExists(r.full_location) (r) => !this.routeExists(r.full_location)
); );
} },
get allAdminRoutes() { get allAdminRoutes() {
return this.model return this.model
@ -21,7 +21,7 @@ export default class AdminPluginsController extends Controller {
return p.admin_route; return p.admin_route;
}) })
.filter(Boolean); .filter(Boolean);
} },
routeExists(routeName) { routeExists(routeName) {
try { try {
@ -30,5 +30,5 @@ export default class AdminPluginsController extends Controller {
} catch (e) { } catch (e) {
return false; return false;
} }
} },
} });

View File

@ -1,12 +1,12 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
export default class AdminReportsShowController extends Controller { export default Controller.extend({
queryParams = ["start_date", "end_date", "filters", "chart_grouping", "mode"]; queryParams: ["start_date", "end_date", "filters", "chart_grouping", "mode"],
start_date = null; start_date: null,
end_date = null; end_date: null,
filters = null; filters: null,
chart_grouping = null; chart_grouping: null,
@discourseComputed("model.type") @discourseComputed("model.type")
reportOptions(type) { reportOptions(type) {
@ -19,5 +19,5 @@ export default class AdminReportsShowController extends Controller {
options.chartGrouping = this.chart_grouping; options.chartGrouping = this.chart_grouping;
return options; return options;
} },
} });

View File

@ -2,19 +2,24 @@ import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
export const DEFAULT_PERIOD = "yearly"; export const DEFAULT_PERIOD = "yearly";
export default class AdminSearchLogsIndexController extends Controller { export default Controller.extend({
loading = false; loading: false,
period = DEFAULT_PERIOD; period: DEFAULT_PERIOD,
searchType = "all"; searchType: "all",
searchTypeOptions = [
{ init() {
id: "all", this._super(...arguments);
name: I18n.t("admin.logs.search_logs.types.all_search_types"),
}, this.searchTypeOptions = [
{ id: "header", name: I18n.t("admin.logs.search_logs.types.header") }, {
{ id: "all",
id: "full_page", name: I18n.t("admin.logs.search_logs.types.all_search_types"),
name: I18n.t("admin.logs.search_logs.types.full_page"), },
}, { id: "header", name: I18n.t("admin.logs.search_logs.types.header") },
]; {
} id: "full_page",
name: I18n.t("admin.logs.search_logs.types.full_page"),
},
];
},
});

View File

@ -2,24 +2,29 @@ import Controller from "@ember/controller";
import { DEFAULT_PERIOD } from "admin/controllers/admin-search-logs-index"; import { DEFAULT_PERIOD } from "admin/controllers/admin-search-logs-index";
import I18n from "I18n"; import I18n from "I18n";
export default class AdminSearchLogsTermController extends Controller { export default Controller.extend({
loading = false; loading: false,
term = null; term: null,
period = DEFAULT_PERIOD; period: DEFAULT_PERIOD,
searchType = "all"; searchType: "all",
searchTypeOptions = [
{ init() {
id: "all", this._super(...arguments);
name: I18n.t("admin.logs.search_logs.types.all_search_types"),
}, this.searchTypeOptions = [
{ id: "header", name: I18n.t("admin.logs.search_logs.types.header") }, {
{ id: "all",
id: "full_page", name: I18n.t("admin.logs.search_logs.types.all_search_types"),
name: I18n.t("admin.logs.search_logs.types.full_page"), },
}, { id: "header", name: I18n.t("admin.logs.search_logs.types.header") },
{ {
id: "click_through_only", id: "full_page",
name: I18n.t("admin.logs.search_logs.types.click_through_only"), name: I18n.t("admin.logs.search_logs.types.full_page"),
}, },
]; {
} id: "click_through_only",
name: I18n.t("admin.logs.search_logs.types.click_through_only"),
},
];
},
});

View File

@ -1,18 +1,17 @@
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
export default class AdminSiteSettingsCategoryController extends Controller { export default Controller.extend({
@controller adminSiteSettings; adminSiteSettings: controller(),
categoryNameKey: null,
categoryNameKey = null;
@discourseComputed("adminSiteSettings.visibleSiteSettings", "categoryNameKey") @discourseComputed("adminSiteSettings.visibleSiteSettings", "categoryNameKey")
category(categories, nameKey) { category(categories, nameKey) {
return (categories || []).findBy("nameKey", nameKey); return (categories || []).findBy("nameKey", nameKey);
} },
@discourseComputed("category") @discourseComputed("category")
filteredContent(category) { filteredContent(category) {
return category ? category.siteSettings : []; return category ? category.siteSettings : [];
} },
} });

View File

@ -1,23 +1,20 @@
import { alias } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import { alias } from "@ember/object/computed";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { debounce } from "discourse-common/utils/decorators"; import { debounce, observes } from "discourse-common/utils/decorators";
import { observes } from "@ember-decorators/object";
import { action } from "@ember/object"; import { action } from "@ember/object";
export default class AdminSiteSettingsController extends Controller { export default Controller.extend({
filter = null; filter: null,
allSiteSettings: alias("model"),
@alias("model") allSiteSettings; visibleSiteSettings: null,
onlyOverridden: false,
visibleSiteSettings = null;
onlyOverridden = false;
filterContentNow(category) { filterContentNow(category) {
// If we have no content, don't bother filtering anything // If we have no content, don't bother filtering anything
if (isEmpty(this.allSiteSettings)) { if (!!isEmpty(this.allSiteSettings)) {
return; return;
} }
@ -76,7 +73,7 @@ export default class AdminSiteSettingsController extends Controller {
setting.includes(filter) || setting.includes(filter) ||
setting.replace(/_/g, " ").includes(filter) || setting.replace(/_/g, " ").includes(filter) ||
item.get("description").toLowerCase().includes(filter) || item.get("description").toLowerCase().includes(filter) ||
(item.get("value") || "").toString().toLowerCase().includes(filter) (item.get("value") || "").toLowerCase().includes(filter)
); );
} else { } else {
return true; return true;
@ -112,13 +109,9 @@ export default class AdminSiteSettingsController extends Controller {
"adminSiteSettingsCategory", "adminSiteSettingsCategory",
category || "all_results" category || "all_results"
); );
} },
@observes("filter", "onlyOverridden", "model") @observes("filter", "onlyOverridden", "model")
optsChanged() {
this.filterContent();
}
@debounce(INPUT_DELAY) @debounce(INPUT_DELAY)
filterContent() { filterContent() {
if (this._skipBounce) { if (this._skipBounce) {
@ -126,12 +119,12 @@ export default class AdminSiteSettingsController extends Controller {
} else { } else {
this.filterContentNow(this.categoryNameKey); this.filterContentNow(this.categoryNameKey);
} }
} },
@action @action
clearFilter() { clearFilter() {
this.setProperties({ filter: "", onlyOverridden: false }); this.setProperties({ filter: "", onlyOverridden: false });
} },
@action @action
toggleMenu() { toggleMenu() {
@ -139,5 +132,5 @@ export default class AdminSiteSettingsController extends Controller {
["mobile-closed", "mobile-open"].forEach((state) => { ["mobile-closed", "mobile-open"].forEach((state) => {
adminDetail.classList.toggle(state); adminDetail.classList.toggle(state);
}); });
} },
} });

View File

@ -1,23 +1,23 @@
import { action } from "@ember/object";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
let lastSearch; let lastSearch;
export default class AdminSiteTextIndexController extends Controller { export default Controller.extend({
searching = false; searching: false,
siteTexts = null; siteTexts: null,
preferred = false; preferred: false,
queryParams = ["q", "overridden", "locale"]; queryParams: ["q", "overridden", "locale"],
locale = null; locale: null,
q = null;
overridden = false; q: null,
overridden: false,
init() { init() {
super.init(...arguments); this._super(...arguments);
this.set("locale", this.siteSettings.default_locale); this.set("locale", this.siteSettings.default_locale);
} },
_performSearch() { _performSearch() {
this.store this.store
@ -26,12 +26,12 @@ export default class AdminSiteTextIndexController extends Controller {
this.set("siteTexts", results); this.set("siteTexts", results);
}) })
.finally(() => this.set("searching", false)); .finally(() => this.set("searching", false));
} },
@discourseComputed() @discourseComputed()
availableLocales() { availableLocales() {
return JSON.parse(this.siteSettings.available_locales); return JSON.parse(this.siteSettings.available_locales);
} },
@discourseComputed("locale") @discourseComputed("locale")
fallbackLocaleFullName() { fallbackLocaleFullName() {
@ -40,41 +40,39 @@ export default class AdminSiteTextIndexController extends Controller {
return l.value === this.siteTexts.extras.fallback_locale; return l.value === this.siteTexts.extras.fallback_locale;
}).name; }).name;
} }
} },
@action actions: {
edit(siteText) { edit(siteText) {
this.transitionToRoute("adminSiteText.edit", siteText.get("id"), { this.transitionToRoute("adminSiteText.edit", siteText.get("id"), {
queryParams: { queryParams: {
locale: this.locale, locale: this.locale,
}, },
}); });
} },
@action toggleOverridden() {
toggleOverridden() { this.toggleProperty("overridden");
this.toggleProperty("overridden");
this.set("searching", true);
discourseDebounce(this, this._performSearch, 400);
}
@action
search() {
const q = this.q;
if (q !== lastSearch) {
this.set("searching", true); this.set("searching", true);
discourseDebounce(this, this._performSearch, 400); discourseDebounce(this, this._performSearch, 400);
lastSearch = q; },
}
}
@action search() {
updateLocale(value) { const q = this.q;
this.setProperties({ if (q !== lastSearch) {
searching: true, this.set("searching", true);
locale: value, discourseDebounce(this, this._performSearch, 400);
}); lastSearch = q;
}
},
discourseDebounce(this, this._performSearch, 400); updateLocale(value) {
} this.setProperties({
} searching: true,
locale: value,
});
discourseDebounce(this, this._performSearch, 400);
},
},
});

View File

@ -1,25 +1,25 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { alias, sort } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import { alias, sort } from "@ember/object/computed";
import GrantBadgeController from "discourse/mixins/grant-badge-controller"; import GrantBadgeController from "discourse/mixins/grant-badge-controller";
import I18n from "I18n"; import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { next } from "@ember/runloop"; import { next } from "@ember/runloop";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default class AdminUserBadgesController extends Controller.extend( export default Controller.extend(GrantBadgeController, {
GrantBadgeController adminUser: controller(),
) { dialog: service(),
@service dialog; user: alias("adminUser.model"),
@controller adminUser; userBadges: alias("model"),
allBadges: alias("badges"),
sortedBadges: sort("model", "badgeSortOrder"),
@alias("adminUser.model") user; init() {
@alias("model") userBadges; this._super(...arguments);
@alias("badges") allBadges;
@sort("model", "badgeSortOrder") sortedBadges;
badgeSortOrder = ["granted_at:desc"]; this.badgeSortOrder = ["granted_at:desc"];
},
@discourseComputed("model", "model.[]", "model.expandedBadges.[]") @discourseComputed("model", "model.[]", "model.expandedBadges.[]")
groupedBadges() { groupedBadges() {
@ -59,47 +59,46 @@ export default class AdminUserBadgesController extends Controller.extend(
}); });
return expanded.sortBy("granted_at").reverse(); return expanded.sortBy("granted_at").reverse();
} },
@action actions: {
expandGroup(userBadge) { expandGroup(userBadge) {
const model = this.model; const model = this.model;
model.set("expandedBadges", model.get("expandedBadges") || []); model.set("expandedBadges", model.get("expandedBadges") || []);
model.get("expandedBadges").pushObject(userBadge.badge.id); model.get("expandedBadges").pushObject(userBadge.badge.id);
} },
@action grantBadge() {
grantBadge() { this.grantBadge(
this.grantBadge( this.selectedBadgeId,
this.selectedBadgeId, this.get("user.username"),
this.get("user.username"), this.badgeReason
this.badgeReason ).then(
).then( () => {
() => { this.set("badgeReason", "");
this.set("badgeReason", ""); next(() => {
next(() => { // Update the selected badge ID after the combobox has re-rendered.
// Update the selected badge ID after the combobox has re-rendered. const newSelectedBadge = this.grantableBadges[0];
const newSelectedBadge = this.grantableBadges[0]; if (newSelectedBadge) {
if (newSelectedBadge) { this.set("selectedBadgeId", newSelectedBadge.get("id"));
this.set("selectedBadgeId", newSelectedBadge.get("id")); }
} });
}); },
}, function (error) {
function (error) { popupAjaxError(error);
popupAjaxError(error); }
} );
); },
}
@action revokeBadge(userBadge) {
revokeBadge(userBadge) { return this.dialog.yesNoConfirm({
return this.dialog.yesNoConfirm({ message: I18n.t("admin.badges.revoke_confirm"),
message: I18n.t("admin.badges.revoke_confirm"), didConfirm: () => {
didConfirm: () => { return userBadge.revoke().then(() => {
return userBadge.revoke().then(() => { this.model.removeObject(userBadge);
this.model.removeObject(userBadge); });
}); },
}, });
}); },
} },
} });

View File

@ -1,74 +1,73 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { gte, sort } from "@ember/object/computed"; import { gte, sort } from "@ember/object/computed";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
const MAX_FIELDS = 30; const MAX_FIELDS = 30;
export default class AdminUserFieldsController extends Controller { export default Controller.extend({
@service dialog; dialog: service(),
fieldTypes: null,
createDisabled: gte("model.length", MAX_FIELDS),
sortedFields: sort("model", "fieldSortOrder"),
fieldTypes = null; init() {
this._super(...arguments);
@gte("model.length", MAX_FIELDS) createDisabled; this.fieldSortOrder = ["position"];
@sort("model", "fieldSortOrder") sortedFields; },
fieldSortOrder = ["position"]; actions: {
createField() {
@action const f = this.store.createRecord("user-field", {
createField() { field_type: "text",
const f = this.store.createRecord("user-field", { position: MAX_FIELDS,
field_type: "text",
position: MAX_FIELDS,
});
this.model.pushObject(f);
}
@action
moveUp(f) {
const idx = this.sortedFields.indexOf(f);
if (idx) {
const prev = this.sortedFields.objectAt(idx - 1);
const prevPos = prev.get("position");
prev.update({ position: f.get("position") });
f.update({ position: prevPos });
}
}
@action
moveDown(f) {
const idx = this.sortedFields.indexOf(f);
if (idx > -1) {
const next = this.sortedFields.objectAt(idx + 1);
const nextPos = next.get("position");
next.update({ position: f.get("position") });
f.update({ position: nextPos });
}
}
@action
destroyField(f) {
const model = this.model;
// Only confirm if we already been saved
if (f.get("id")) {
this.dialog.yesNoConfirm({
message: I18n.t("admin.user_fields.delete_confirm"),
didConfirm: () => {
return f
.destroyRecord()
.then(function () {
model.removeObject(f);
})
.catch(popupAjaxError);
},
}); });
} else { this.model.pushObject(f);
model.removeObject(f); },
}
} moveUp(f) {
} const idx = this.sortedFields.indexOf(f);
if (idx) {
const prev = this.sortedFields.objectAt(idx - 1);
const prevPos = prev.get("position");
prev.update({ position: f.get("position") });
f.update({ position: prevPos });
}
},
moveDown(f) {
const idx = this.sortedFields.indexOf(f);
if (idx > -1) {
const next = this.sortedFields.objectAt(idx + 1);
const nextPos = next.get("position");
next.update({ position: f.get("position") });
f.update({ position: nextPos });
}
},
destroy(f) {
const model = this.model;
// Only confirm if we already been saved
if (f.get("id")) {
this.dialog.yesNoConfirm({
message: I18n.t("admin.user_fields.delete_confirm"),
didConfirm: () => {
return f
.destroyRecord()
.then(function () {
model.removeObject(f);
})
.catch(popupAjaxError);
},
});
} else {
model.removeObject(f);
}
},
},
});

View File

@ -1,2 +1,2 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default class AdminUserController extends Controller {} export default Controller.extend();

View File

@ -1,6 +1,4 @@
import { action } from "@ember/object"; import discourseComputed, { observes } from "discourse-common/utils/decorators";
import discourseComputed from "discourse-common/utils/decorators";
import { observes } from "@ember-decorators/object";
import AdminUser from "admin/models/admin-user"; import AdminUser from "admin/models/admin-user";
import CanCheckEmails from "discourse/mixins/can-check-emails"; import CanCheckEmails from "discourse/mixins/can-check-emails";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
@ -9,55 +7,41 @@ import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { i18n } from "discourse/lib/computed"; import { i18n } from "discourse/lib/computed";
export default class AdminUsersListShowController extends Controller.extend( export default Controller.extend(CanCheckEmails, {
CanCheckEmails model: null,
) { query: null,
model = null; order: null,
query = null; asc: null,
order = null; showEmails: false,
asc = null; refreshing: false,
showEmails = false; listFilter: null,
refreshing = false; selectAll: false,
listFilter = null; searchHint: i18n("search_hint"),
selectAll = false;
@i18n("search_hint") searchHint; init() {
this._super(...arguments);
_page = 1; this._page = 1;
_results = []; this._results = [];
_canLoadMore = true; this._canLoadMore = true;
},
@discourseComputed("query") @discourseComputed("query")
title(query) { title(query) {
return I18n.t("admin.users.titles." + query); return I18n.t("admin.users.titles." + query);
} },
@discourseComputed("showEmails")
columnCount(showEmails) {
let colCount = 7; // note that the first column is hardcoded in the template
if (showEmails) {
colCount += 1;
}
if (this.siteSettings.must_approve_users) {
colCount += 1;
}
return colCount;
}
@observes("listFilter") @observes("listFilter")
_filterUsers() { _filterUsers() {
discourseDebounce(this, this.resetFilters, INPUT_DELAY); discourseDebounce(this, this.resetFilters, INPUT_DELAY);
} },
resetFilters() { resetFilters() {
this._page = 1; this._page = 1;
this._results = []; this._results = [];
this._canLoadMore = true; this._canLoadMore = true;
this._refreshUsers(); this._refreshUsers();
} },
_refreshUsers() { _refreshUsers() {
if (!this._canLoadMore) { if (!this._canLoadMore) {
@ -85,17 +69,17 @@ export default class AdminUsersListShowController extends Controller.extend(
.finally(() => { .finally(() => {
this.set("refreshing", false); this.set("refreshing", false);
}); });
} },
@action actions: {
loadMore() { loadMore() {
this._page += 1; this._page += 1;
this._refreshUsers(); this._refreshUsers();
} },
@action toggleEmailVisibility() {
toggleEmailVisibility() { this.toggleProperty("showEmails");
this.toggleProperty("showEmails"); this.resetFilters();
this.resetFilters(); },
} },
} });

View File

@ -1,35 +1,32 @@
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { or } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import WatchedWord from "admin/models/watched-word"; import WatchedWord from "admin/models/watched-word";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { fmt } from "discourse/lib/computed"; import { fmt } from "discourse/lib/computed";
import { or } from "@ember/object/computed";
import { schedule } from "@ember/runloop"; import { schedule } from "@ember/runloop";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
import { inject as service } from "@ember/service";
export default class AdminWatchedWordsActionController extends Controller { export default Controller.extend({
@service dialog; adminWatchedWords: controller(),
@controller adminWatchedWords; actionNameKey: null,
dialog: service(),
actionNameKey = null; downloadLink: fmt(
"actionNameKey",
@fmt("actionNameKey", "/admin/customize/watched_words/action/%@/download") "/admin/customize/watched_words/action/%@/download"
downloadLink; ),
showWordsList: or("adminWatchedWords.showWords", "adminWatchedWords.filter"),
@or("adminWatchedWords.showWords", "adminWatchedWords.filter")
showWordsList;
findAction(actionName) { findAction(actionName) {
return (this.adminWatchedWords.model || []).findBy("nameKey", actionName); return (this.adminWatchedWords.model || []).findBy("nameKey", actionName);
} },
@discourseComputed("actionNameKey", "adminWatchedWords.model") @discourseComputed("actionNameKey", "adminWatchedWords.model")
currentAction(actionName) { currentAction(actionName) {
return this.findAction(actionName); return this.findAction(actionName);
} },
@discourseComputed("currentAction.words.[]") @discourseComputed("currentAction.words.[]")
regexpError(words) { regexpError(words) {
@ -40,81 +37,78 @@ export default class AdminWatchedWordsActionController extends Controller {
return I18n.t("admin.watched_words.invalid_regex", { word }); return I18n.t("admin.watched_words.invalid_regex", { word });
} }
} }
} },
@discourseComputed("actionNameKey") @discourseComputed("actionNameKey")
actionDescription(actionNameKey) { actionDescription(actionNameKey) {
return I18n.t("admin.watched_words.action_descriptions." + actionNameKey); return I18n.t("admin.watched_words.action_descriptions." + actionNameKey);
} },
@action actions: {
recordAdded(arg) { recordAdded(arg) {
const foundAction = this.findAction(this.actionNameKey); const action = this.findAction(this.actionNameKey);
if (!foundAction) { if (!action) {
return; return;
} }
foundAction.words.unshiftObject(arg); action.words.unshiftObject(arg);
schedule("afterRender", () => { schedule("afterRender", () => {
// remove from other actions lists // remove from other actions lists
let match = null; let match = null;
this.adminWatchedWords.model.forEach((otherAction) => { this.adminWatchedWords.model.forEach((otherAction) => {
if (match) {
return;
}
if (otherAction.nameKey !== this.actionNameKey) {
match = otherAction.words.findBy("id", arg.id);
if (match) { if (match) {
otherAction.words.removeObject(match); return;
} }
}
});
});
}
@action if (otherAction.nameKey !== this.actionNameKey) {
recordRemoved(arg) { match = otherAction.words.findBy("id", arg.id);
if (this.currentAction) { if (match) {
this.currentAction.words.removeObject(arg); otherAction.words.removeObject(match);
} }
}
@action
uploadComplete() {
WatchedWord.findAll().then((data) => {
this.adminWatchedWords.set("model", data);
});
}
@action
test() {
WatchedWord.findAll().then((data) => {
this.adminWatchedWords.set("model", data);
showModal("admin-watched-word-test", {
admin: true,
model: this.currentAction,
});
});
}
@action
clearAll() {
const actionKey = this.actionNameKey;
this.dialog.yesNoConfirm({
message: I18n.t("admin.watched_words.clear_all_confirm", {
action: I18n.t("admin.watched_words.actions." + actionKey),
}),
didConfirm: () => {
ajax(`/admin/customize/watched_words/action/${actionKey}.json`, {
type: "DELETE",
}).then(() => {
const foundAction = this.findAction(actionKey);
if (foundAction) {
foundAction.set("words", []);
} }
}); });
}, });
}); },
}
} recordRemoved(arg) {
if (this.currentAction) {
this.currentAction.words.removeObject(arg);
}
},
uploadComplete() {
WatchedWord.findAll().then((data) => {
this.adminWatchedWords.set("model", data);
});
},
test() {
WatchedWord.findAll().then((data) => {
this.adminWatchedWords.set("model", data);
showModal("admin-watched-word-test", {
admin: true,
model: this.currentAction,
});
});
},
clearAll() {
const actionKey = this.actionNameKey;
this.dialog.yesNoConfirm({
message: I18n.t("admin.watched_words.clear_all_confirm", {
action: I18n.t("admin.watched_words.actions." + actionKey),
}),
didConfirm: () => {
ajax(`/admin/customize/watched_words/action/${actionKey}.json`, {
type: "DELETE",
}).then(() => {
const action = this.findAction(actionKey);
if (action) {
action.set("words", []);
}
});
},
});
},
},
});

View File

@ -3,11 +3,11 @@ import EmberObject, { action } from "@ember/object";
import { INPUT_DELAY } from "discourse-common/config/environment"; import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce"; import discourseDebounce from "discourse-common/lib/debounce";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { observes } from "@ember-decorators/object"; import { observes } from "discourse-common/utils/decorators";
export default class AdminWatchedWordsController extends Controller { export default Controller.extend({
filter = null; filter: null,
showWords = false; showWords: false,
_filterContent() { _filterContent() {
if (isEmpty(this.allWatchedWords)) { if (isEmpty(this.allWatchedWords)) {
@ -36,17 +36,17 @@ export default class AdminWatchedWordsController extends Controller {
); );
}); });
this.set("model", model); this.set("model", model);
} },
@observes("filter") @observes("filter")
filterContent() { filterContent() {
discourseDebounce(this, this._filterContent, INPUT_DELAY); discourseDebounce(this, this._filterContent, INPUT_DELAY);
} },
@action @action
clearFilter() { clearFilter() {
this.set("filter", ""); this.set("filter", "");
} },
@action @action
toggleMenu() { toggleMenu() {
@ -54,5 +54,5 @@ export default class AdminWatchedWordsController extends Controller {
["mobile-closed", "mobile-open"].forEach((state) => { ["mobile-closed", "mobile-open"].forEach((state) => {
adminDetail.classList.toggle(state); adminDetail.classList.toggle(state);
}); });
} },
} });

View File

@ -1,24 +1,23 @@
import { inject as service } from "@ember/service";
import { alias } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import EmberObject, { action } from "@ember/object"; import EmberObject, { action } from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import { alias } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default class AdminWebHooksEditController extends Controller { export default Controller.extend({
@service dialog; adminWebHooks: controller(),
@controller adminWebHooks; dialog: service(),
eventTypes: alias("adminWebHooks.eventTypes"),
@alias("adminWebHooks.eventTypes") eventTypes; defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
@alias("adminWebHooks.defaultEventTypes") defaultEventTypes; contentTypes: alias("adminWebHooks.contentTypes"),
@alias("adminWebHooks.contentTypes") contentTypes;
@discourseComputed @discourseComputed
showTagsFilter() { showTagsFilter() {
return this.siteSettings.tagging_enabled; return this.siteSettings.tagging_enabled;
} },
@discourseComputed("model.isSaving", "saved", "saveButtonDisabled") @discourseComputed("model.isSaving", "saved", "saveButtonDisabled")
savingStatus(isSaving, saved, saveButtonDisabled) { savingStatus(isSaving, saved, saveButtonDisabled) {
@ -30,14 +29,14 @@ export default class AdminWebHooksEditController extends Controller {
// Use side effect of validation to clear saved text // Use side effect of validation to clear saved text
this.set("saved", false); this.set("saved", false);
return ""; return "";
} },
@discourseComputed("model.isNew") @discourseComputed("model.isNew")
saveButtonText(isNew) { saveButtonText(isNew) {
return isNew return isNew
? I18n.t("admin.web_hooks.create") ? I18n.t("admin.web_hooks.create")
: I18n.t("admin.web_hooks.save"); : I18n.t("admin.web_hooks.save");
} },
@discourseComputed("model.secret") @discourseComputed("model.secret")
secretValidation(secret) { secretValidation(secret) {
@ -56,7 +55,7 @@ export default class AdminWebHooksEditController extends Controller {
}); });
} }
} }
} },
@discourseComputed("model.wildcard_web_hook", "model.web_hook_event_types.[]") @discourseComputed("model.wildcard_web_hook", "model.web_hook_event_types.[]")
eventTypeValidation(isWildcard, eventTypes) { eventTypeValidation(isWildcard, eventTypes) {
@ -66,7 +65,7 @@ export default class AdminWebHooksEditController extends Controller {
reason: I18n.t("admin.web_hooks.event_type_missing"), reason: I18n.t("admin.web_hooks.event_type_missing"),
}); });
} }
} },
@discourseComputed( @discourseComputed(
"model.isSaving", "model.isSaving",
@ -83,7 +82,7 @@ export default class AdminWebHooksEditController extends Controller {
return isSaving return isSaving
? false ? false
: secretValidation || eventTypeValidation || isEmpty(payloadUrl); : secretValidation || eventTypeValidation || isEmpty(payloadUrl);
} },
@action @action
async save() { async save() {
@ -98,5 +97,5 @@ export default class AdminWebHooksEditController extends Controller {
} catch (e) { } catch (e) {
popupAjaxError(e); popupAjaxError(e);
} }
} },
} });

View File

@ -1,19 +1,18 @@
import { inject as service } from "@ember/service";
import { alias } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import I18n from "I18n"; import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { alias } from "@ember/object/computed";
export default class AdminWebHooksIndexController extends Controller { export default Controller.extend({
@service dialog; adminWebHooks: controller(),
@controller adminWebHooks; dialog: service(),
contentTypes: alias("adminWebHooks.contentTypes"),
@alias("adminWebHooks.contentTypes") contentTypes; defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
@alias("adminWebHooks.defaultEventTypes") defaultEventTypes; deliveryStatuses: alias("adminWebHooks.deliveryStatuses"),
@alias("adminWebHooks.deliveryStatuses") deliveryStatuses; eventTypes: alias("adminWebHooks.eventTypes"),
@alias("adminWebHooks.eventTypes") eventTypes; model: alias("adminWebHooks.model"),
@alias("adminWebHooks.model") model;
@action @action
destroy(webhook) { destroy(webhook) {
@ -28,10 +27,10 @@ export default class AdminWebHooksIndexController extends Controller {
} }
}, },
}); });
} },
@action @action
loadMore() { loadMore() {
this.model.loadMore(); this.model.loadMore();
} },
} });

View File

@ -1,18 +1,18 @@
import { inject as service } from "@ember/service";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import { action } from "@ember/object"; import { action } from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
export default class AdminWebHooksShowController extends Controller { export default Controller.extend({
@service dialog; adminWebHooks: controller(),
@service router; dialog: service(),
@controller adminWebHooks; router: service(),
@action @action
edit() { edit() {
return this.router.transitionTo("adminWebHooks.edit", this.model); return this.router.transitionTo("adminWebHooks.edit", this.model);
} },
@action @action
destroy() { destroy() {
@ -28,5 +28,5 @@ export default class AdminWebHooksShowController extends Controller {
} }
}, },
}); });
} },
} });

View File

@ -1,3 +1,3 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default class AdminWebHooksController extends Controller {} export default Controller.extend({});

View File

@ -1,20 +1,20 @@
import { inject as service } from "@ember/service";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { dasherize } from "@ember/string"; import { dasherize } from "@ember/string";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
export default class AdminController extends Controller { export default Controller.extend({
@service router; router: service(),
@discourseComputed("siteSettings.enable_group_directory") @discourseComputed("siteSettings.enable_group_directory")
showGroups(enableGroupDirectory) { showGroups(enableGroupDirectory) {
return !enableGroupDirectory; return !enableGroupDirectory;
} },
@discourseComputed("siteSettings.enable_badges") @discourseComputed("siteSettings.enable_badges")
showBadges(enableBadges) { showBadges(enableBadges) {
return this.currentUser.get("admin") && enableBadges; return this.currentUser.get("admin") && enableBadges;
} },
@discourseComputed("router._router.currentPath") @discourseComputed("router._router.currentPath")
adminContentsClassName(currentPath) { adminContentsClassName(currentPath) {
@ -37,5 +37,5 @@ export default class AdminController extends Controller {
} }
return cssClasses; return cssClasses;
} },
} });

View File

@ -1,8 +1,6 @@
import { action } from "@ember/object";
import { and, not } from "@ember/object/computed";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators"; import { and, not } from "@ember/object/computed";
import { observes } from "@ember-decorators/object"; import discourseComputed, { observes } from "discourse-common/utils/decorators";
import I18n from "I18n"; import I18n from "I18n";
import ModalFunctionality from "discourse/mixins/modal-functionality"; import ModalFunctionality from "discourse/mixins/modal-functionality";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
@ -55,20 +53,18 @@ const SCSS_VARIABLE_NAMES = [
"love-low", "love-low",
]; ];
export default class AdminAddUploadController extends Controller.extend( export default Controller.extend(ModalFunctionality, {
ModalFunctionality adminCustomizeThemesShow: controller(),
) {
@controller adminCustomizeThemesShow;
uploadUrl = "/admin/themes/upload_asset"; uploadUrl: "/admin/themes/upload_asset",
@and("nameValid", "fileSelected") enabled;
@not("enabled") disabled;
onShow() { onShow() {
this.set("name", null); this.set("name", null);
this.set("fileSelected", false); this.set("fileSelected", false);
} },
enabled: and("nameValid", "fileSelected"),
disabled: not("enabled"),
@discourseComputed("name", "adminCustomizeThemesShow.model.theme_fields") @discourseComputed("name", "adminCustomizeThemesShow.model.theme_fields")
errorMessage(name, themeFields) { errorMessage(name, themeFields) {
@ -93,54 +89,54 @@ export default class AdminAddUploadController extends Controller.extend(
} }
return null; return null;
} },
@discourseComputed("errorMessage") @discourseComputed("errorMessage")
nameValid(errorMessage) { nameValid(errorMessage) {
return null === errorMessage; return null === errorMessage;
} },
@observes("name") @observes("name")
uploadChanged() { uploadChanged() {
const file = $("#file-input")[0]; const file = $("#file-input")[0];
this.set("fileSelected", file && file.files[0]); this.set("fileSelected", file && file.files[0]);
} },
@action actions: {
updateName() { updateName() {
let name = this.name; let name = this.name;
if (isEmpty(name)) { if (isEmpty(name)) {
name = $("#file-input")[0].files[0].name; name = $("#file-input")[0].files[0].name;
this.set("name", name.split(".")[0]); this.set("name", name.split(".")[0]);
} }
this.uploadChanged(); this.uploadChanged();
} },
@action upload() {
upload() { const file = $("#file-input")[0].files[0];
const file = $("#file-input")[0].files[0];
const options = { const options = {
type: "POST", type: "POST",
processData: false, processData: false,
contentType: false, contentType: false,
data: new FormData(), data: new FormData(),
}; };
options.data.append("file", file); options.data.append("file", file);
ajax(this.uploadUrl, options) ajax(this.uploadUrl, options)
.then((result) => { .then((result) => {
const upload = { const upload = {
upload_id: result.upload_id, upload_id: result.upload_id,
name: this.name, name: this.name,
original_filename: file.name, original_filename: file.name,
}; };
this.adminCustomizeThemesShow.send("addUpload", upload); this.adminCustomizeThemesShow.send("addUpload", upload);
this.send("closeModal"); this.send("closeModal");
}) })
.catch((e) => { .catch((e) => {
popupAjaxError(e); popupAjaxError(e);
}); });
} },
} },
});

View File

@ -4,12 +4,39 @@ import I18n from "I18n";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { escapeExpression } from "discourse/lib/utilities"; import { escapeExpression } from "discourse/lib/utilities";
export default class AdminBadgePreviewController extends Controller { export default Controller.extend({
@alias("model.sample") sample; sample: alias("model.sample"),
@alias("model.errors") errors; errors: alias("model.errors"),
@alias("model.grant_count") count; count: alias("model.grant_count"),
@map("model.sample", (grant) => { @discourseComputed("count", "sample.length")
countWarning(count, sampleLength) {
if (count <= 10) {
return sampleLength !== count;
} else {
return sampleLength !== 10;
}
},
@discourseComputed("model.query_plan")
hasQueryPlan(queryPlan) {
return !!queryPlan;
},
@discourseComputed("model.query_plan")
queryPlanHtml(queryPlan) {
let output = `<pre class="badge-query-plan">`;
queryPlan.forEach((linehash) => {
output += escapeExpression(linehash["QUERY PLAN"]);
output += "<br>";
});
output += "</pre>";
return output;
},
processedSample: map("model.sample", (grant) => {
let i18nKey = "admin.badges.preview.grant.with"; let i18nKey = "admin.badges.preview.grant.with";
const i18nParams = { username: escapeExpression(grant.username) }; const i18nParams = { username: escapeExpression(grant.username) };
@ -28,33 +55,5 @@ export default class AdminBadgePreviewController extends Controller {
} }
return I18n.t(i18nKey, i18nParams); return I18n.t(i18nKey, i18nParams);
}) }),
processedSample; });
@discourseComputed("count", "sample.length")
countWarning(count, sampleLength) {
if (count <= 10) {
return sampleLength !== count;
} else {
return sampleLength !== 10;
}
}
@discourseComputed("model.query_plan")
hasQueryPlan(queryPlan) {
return !!queryPlan;
}
@discourseComputed("model.query_plan")
queryPlanHtml(queryPlan) {
let output = `<pre class="badge-query-plan">`;
queryPlan.forEach((linehash) => {
output += escapeExpression(linehash["QUERY PLAN"]);
output += "<br>";
});
output += "</pre>";
return output;
}
}

View File

@ -1,29 +1,27 @@
import { action } from "@ember/object";
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality"; import ModalFunctionality from "discourse/mixins/modal-functionality";
export default class AdminColorSchemeSelectBaseController extends Controller.extend( export default Controller.extend(ModalFunctionality, {
ModalFunctionality adminCustomizeColors: controller(),
) {
@controller adminCustomizeColors;
selectedBaseThemeId = null; selectedBaseThemeId: null,
init() { init() {
super.init(...arguments); this._super(...arguments);
const defaultScheme = this.get( const defaultScheme = this.get(
"adminCustomizeColors.baseColorSchemes.0.base_scheme_id" "adminCustomizeColors.baseColorSchemes.0.base_scheme_id"
); );
this.set("selectedBaseThemeId", defaultScheme); this.set("selectedBaseThemeId", defaultScheme);
} },
@action actions: {
selectBase() { selectBase() {
this.adminCustomizeColors.send( this.adminCustomizeColors.send(
"newColorSchemeWithBase", "newColorSchemeWithBase",
this.selectedBaseThemeId this.selectedBaseThemeId
); );
this.send("closeModal"); this.send("closeModal");
} },
} },
});

View File

@ -5,19 +5,12 @@ import { inject as service } from "@ember/service";
import I18n from "I18n"; import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { tracked } from "@glimmer/tracking";
export default class AdminCustomizeFormTemplateView extends Controller.extend( export default class AdminCustomizeFormTemplateView extends Controller.extend(
ModalFunctionality ModalFunctionality
) { ) {
@service router; @service router;
@service dialog; @service dialog;
@tracked showPreview = false;
@action
togglePreview() {
this.showPreview = !this.showPreview;
}
@action @action
editTemplate() { editTemplate() {

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