Compare commits

..

No commits in common. "main" and "latest-release" have entirely different histories.

1474 changed files with 23940 additions and 40268 deletions

View File

@ -76,5 +76,3 @@ cb932d6ee1b3b3571e4d4d9118635e2dbf58f0ef
1d0d7ddbb5ce5dae9bdbdab3797628b6565fb4c4
a017f566a84af2e74c132c4acc89028d5f779a29
b29e0b6e1b6001890ce92f96448258550f80132b
999ae73c7836ff20a9537e582d52b861355f1ec5
7b2b08cf89bdab332d14b68cd17cdfb4aa8a7113

View File

@ -84,9 +84,6 @@ updates:
patterns:
- "@highlightjs/cdn-assets"
- "highlight.js"
prosemirror:
patterns:
- "prosemirror-*"
types:
patterns:
- "@types/*"

View File

@ -23,16 +23,21 @@ permissions:
jobs:
tests:
if: github.event_name == 'pull_request' || github.repository != 'discourse/discourse-private-mirror'
name: Tests
runs-on: ${{ (github.repository_owner == 'discourse' && 'debian-12') || 'ubuntu-latest' }}
container: discourse/discourse_test:release
name: Tests with Ruby ${{ matrix.ruby }}
runs-on: ${{ (github.repository != 'discourse/discourse' && 'ubuntu-latest') || 'debian-12' }}
container: discourse/discourse_test:slim
timeout-minutes: 20
env:
RAILS_ENV: test
PGUSER: discourse
PGPASSWORD: discourse
LOAD_PLUGINS: 1
strategy:
fail-fast: false
matrix:
ruby: ["3.3"]
steps:
- name: Set working directory owner
@ -57,9 +62,23 @@ jobs:
sudo -E -u postgres script/start_test_db.rb
sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';"
- name: Symlink vendor/bundle from image
- name: Container envs
id: container-envs
run: |
ln -s /var/www/discourse/vendor/bundle vendor/bundle
echo "ruby_version=$RUBY_VERSION" >> $GITHUB_OUTPUT
echo "debian_release=$DEBIAN_RELEASE" >> $GITHUB_OUTPUT
shell: bash
- name: Bundler cache
uses: actions/cache@v4
with:
path: vendor/bundle
key: >-
${{ runner.os }}-
${{ steps.container-envs.outputs.ruby_version }}-
${{ steps.container-envs.outputs.debian_release }}-
${{ hashFiles('**/Gemfile.lock') }}-
migrations-tooling
- name: Setup gems
run: |
@ -69,14 +88,11 @@ jobs:
bundle config --local without development
bundle config --local with migrations
bundle install --jobs $(($(nproc) - 1))
bundle clean
- name: pnpm install
run: pnpm install --frozen-lockfile
- name: Get CPU cores
id: cpu-info
run: echo "cpu-cores=$(nproc)" >> $GITHUB_OUTPUT
- name: Fetch app state cache
uses: actions/cache@v4
id: app-cache
@ -84,8 +100,7 @@ jobs:
path: tmp/app-cache
key: >-
${{ runner.os }}-
${{ steps.cpu-info.outputs.cpu-cores }}-
${{ hashFiles('.github/workflows/migration-tests.yml') }}-
${{ hashFiles('.github/workflows/tests.yml') }}-
${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}-
${{ hashFiles('config/environments/test.rb') }}
@ -111,16 +126,11 @@ jobs:
if: steps.app-cache.outputs.cache-hit != 'true'
run: rm -rf tmp/app-cache/uploads && cp -r public/uploads tmp/app-cache/uploads
- name: Validate IntermediateDB schema
run: |
migrations/bin/cli schema generate --db=intermediate_db
if [ ! -z "$(git status --porcelain migrations/db/intermediate_db_schema/)" ]; then
echo "IntermediateDB schema is not up to date."
echo "---------------------------------------------"
git -c color.ui=always diff migrations/db/intermediate_db_schema/
exit 1
fi
# - name: Check core database drift
# run: |
# mkdir /tmp/intermediate_db
# ./migrations/scripts/schema_generator /tmp/intermediate_db/base_migration.sql
# diff -u migrations/common/intermediate_db_schema/000_base_schema.sql /tmp/intermediate_db/base_migration.sql
- name: RSpec
run: bin/rspec --default-path migrations/spec

View File

@ -38,14 +38,13 @@ jobs:
PGUSER: discourse
PGPASSWORD: discourse
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }}
CAPYBARA_DEFAULT_MAX_WAIT_TIME: 20
CAPYBARA_DEFAULT_MAX_WAIT_TIME: 10
MINIO_RUNNER_LOG_LEVEL: DEBUG
DISCOURSE_TURBO_RSPEC_RETRY_AND_LOG_FLAKY_TESTS: ${{ (matrix.build_type == 'system' || matrix.build_type == 'backend') && '1' }}
CHEAP_SOURCE_MAPS: "1"
MINIO_RUNNER_INSTALL_DIR: /home/discourse/.minio_runner
TESTEM_BROWSER: ${{ (startsWith(matrix.browser, 'Firefox') && 'Firefox') || matrix.browser }}
TESTEM_FIREFOX_PATH: ${{ (matrix.browser == 'Firefox Evergreen') && '/opt/firefox-evergreen/firefox' }}
EMBER_ENV: development
strategy:
fail-fast: false
@ -263,7 +262,7 @@ jobs:
- name: Ember Build for System Tests
if: matrix.build_type == 'system'
run: script/assemble_ember_build.rb
run: bin/ember-cli --build
- name: Core System Tests
if: matrix.build_type == 'system' && matrix.target == 'core'

2
.gitignore vendored
View File

@ -53,7 +53,7 @@
/spec/fixtures/plugins/my_plugin/auto_generated
/vendor/bundle
/vendor/bundle/*
/vendor/data/GeoLite2-City.mmdb
/vendor/data/GeoLite2-ASN.mmdb

View File

@ -88,10 +88,10 @@ GEM
bootsnap (1.18.4)
msgpack (~> 1.2)
builder (3.3.0)
bullet (8.0.3)
bullet (8.0.1)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (12.0.0)
byebug (11.1.3)
capybara (3.40.0)
addressable
matrix
@ -119,15 +119,15 @@ GEM
crass (1.0.6)
css_parser (1.21.1)
addressable
csv (3.3.4)
csv (3.3.3)
date (3.4.1)
debug_inspector (1.2.0)
diff-lcs (1.6.1)
diff-lcs (1.6.0)
diffy (3.4.3)
digest (3.2.0)
digest-xxhash (0.2.9)
discourse-emojis (1.0.39)
discourse-fonts (0.0.19)
discourse-emojis (1.0.38)
discourse-fonts (0.0.18)
discourse-seed-fu (2.3.12)
activerecord (>= 3.1)
activesupport (>= 3.1)
@ -143,45 +143,45 @@ GEM
logger
execjs (2.10.0)
exifr (1.4.1)
extralite-bundle (2.12)
extralite-bundle (2.10)
fabrication (2.31.0)
faker (3.5.1)
i18n (>= 1.8.11, < 2)
fakeweb (1.3.0)
faraday (2.13.0)
faraday (2.12.2)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
faraday-retry (2.3.1)
faraday-retry (2.2.1)
faraday (~> 2.0)
fast_blank (1.0.1)
fastimage (2.3.1)
ffi (1.17.2-aarch64-linux-gnu)
ffi (1.17.2-aarch64-linux-musl)
ffi (1.17.2-arm-linux-gnu)
ffi (1.17.2-arm-linux-musl)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
ffi (1.17.2-x86_64-linux-musl)
ffi (1.17.1-aarch64-linux-gnu)
ffi (1.17.1-aarch64-linux-musl)
ffi (1.17.1-arm-linux-gnu)
ffi (1.17.1-arm-linux-musl)
ffi (1.17.1-arm64-darwin)
ffi (1.17.1-x86_64-darwin)
ffi (1.17.1-x86_64-linux-gnu)
ffi (1.17.1-x86_64-linux-musl)
fspath (3.1.2)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (4.30.2)
google-protobuf (4.30.1)
bigdecimal
rake (>= 13)
google-protobuf (4.30.2-aarch64-linux)
google-protobuf (4.30.1-aarch64-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.30.2-arm64-darwin)
google-protobuf (4.30.1-arm64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.30.2-x86_64-darwin)
google-protobuf (4.30.1-x86_64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.30.2-x86_64-linux)
google-protobuf (4.30.1-x86_64-linux)
bigdecimal
rake (>= 13)
guess_html_encoding (0.0.11)
@ -203,7 +203,7 @@ GEM
image_size (3.4.0)
in_threads (1.6.0)
io-console (0.8.0)
irb (1.15.2)
irb (1.15.1)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
@ -233,7 +233,7 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
literate_randomizer (0.4.0)
logger (1.7.0)
logger (1.6.6)
lograge (0.14.0)
actionpack (>= 4)
activesupport (>= 4)
@ -260,7 +260,7 @@ GEM
mini_racer (>= 0.6.3)
method_source (1.1.0)
mini_mime (1.1.5)
mini_racer (0.18.1)
mini_racer (0.18.0)
libv8-node (~> 23.6.1.0)
mini_scheduler (0.18.0)
sidekiq (>= 6.5, < 8.0)
@ -288,21 +288,21 @@ GEM
net-smtp (0.5.1)
net-protocol
nio4r (2.7.4)
nokogiri (1.18.7-aarch64-linux-gnu)
nokogiri (1.18.6-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.7-aarch64-linux-musl)
nokogiri (1.18.6-aarch64-linux-musl)
racc (~> 1.4)
nokogiri (1.18.7-arm-linux-gnu)
nokogiri (1.18.6-arm-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.7-arm-linux-musl)
nokogiri (1.18.6-arm-linux-musl)
racc (~> 1.4)
nokogiri (1.18.7-arm64-darwin)
nokogiri (1.18.6-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.7-x86_64-darwin)
nokogiri (1.18.6-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.7-x86_64-linux-gnu)
nokogiri (1.18.6-x86_64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.7-x86_64-linux-musl)
nokogiri (1.18.6-x86_64-linux-musl)
racc (~> 1.4)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
@ -348,10 +348,10 @@ GEM
openssl (> 2.0)
optimist (3.2.1)
ostruct (0.6.1)
parallel (1.27.0)
parallel (1.26.3)
parallel_tests (5.1.0)
parallel
parser (3.3.8.0)
parser (3.3.7.2)
ast (~> 2.4.1)
racc
pg (1.5.9)
@ -359,14 +359,13 @@ GEM
prettyprint
prettier_print (1.2.1)
prettyprint (0.2.0)
prism (1.4.0)
progress (3.6.0)
pry (0.15.2)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.11.0)
byebug (~> 12.0)
pry (>= 0.13, < 0.16)
pry-byebug (3.10.1)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
pry-rails (0.3.11)
pry (>= 0.13.0)
pry-stack_explorer (0.6.1)
@ -425,7 +424,7 @@ GEM
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
rchardet (1.9.0)
rdoc (6.13.1)
rdoc (6.12.0)
psych (>= 4.0.0)
redcarpet (3.6.1)
redis (5.4.0)
@ -435,7 +434,7 @@ GEM
redis-namespace (1.11.0)
redis (>= 4)
regexp_parser (2.10.0)
reline (0.6.1)
reline (0.6.0)
io-console (~> 0.5)
request_store (1.7.0)
rack (>= 1.4)
@ -484,7 +483,7 @@ GEM
rspec-core (>= 2.14)
rtlcss (0.2.1)
mini_racer (>= 0.6.3)
rubocop (1.75.2)
rubocop (1.74.0)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@ -492,12 +491,11 @@ GEM
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.44.1)
rubocop-ast (1.41.0)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-capybara (2.22.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
@ -513,11 +511,11 @@ GEM
rubocop-factory_bot (2.27.1)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rails (2.31.0)
rubocop-rails (2.30.3)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop (>= 1.72.1, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rspec (3.5.0)
lint_roller (~> 1.1)
@ -555,9 +553,9 @@ GEM
sassc-embedded (1.80.2)
sass-embedded (~> 1.80)
securerandom (0.4.1)
selenium-devtools (0.135.0)
selenium-devtools (0.133.0)
selenium-webdriver (~> 4.2)
selenium-webdriver (4.31.0)
selenium-webdriver (4.29.1)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
@ -599,7 +597,7 @@ GEM
sqlite3 (2.6.0-x86_64-linux-musl)
sshkey (3.0.0)
stackprof (0.2.27)
stringio (3.1.6)
stringio (3.1.5)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0)
test-prof (1.4.4)
@ -622,7 +620,7 @@ GEM
uniform_notifier (1.16.0)
uri (1.0.3)
useragent (0.16.11)
version_gem (1.1.7)
version_gem (1.1.6)
web-push (3.0.1)
jwt (~> 2.0)
openssl (~> 3.0)
@ -827,8 +825,8 @@ CHECKSUMS
binding_of_caller (1.0.1) sha256=2b2902abff4246ddcfbc4da9b69bc4a019e22aeb300c2ff6289a173d4b90b29a
bootsnap (1.18.4) sha256=ac4c42af397f7ee15521820198daeff545e4c360d2772c601fbdc2c07d92af55
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
bullet (8.0.3) sha256=60e41446500a7e59272b3c2398cded214deec3662e883009de5bf30028a0642c
byebug (12.0.0) sha256=d4a150d291cca40b66ec9ca31f754e93fed8aa266a17335f71bb0afa7fca1a1e
bullet (8.0.1) sha256=a18f7aa3a20f063a48e7a9dbcaf868bd2ede6e9b1056818ce825f74a717cd2f0
byebug (11.1.3) sha256=2485944d2bb21283c593d562f9ae1019bf80002143cc3a255aaffd4e9cf4a35b
capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef
cbor (0.5.9.8) sha256=9ee097fc58d9bc5e406d112cd2d4e112c7354ec16f8b6ff34e4732c1e44b4eb7
certified (1.0.0) sha256=aa4cdf0e90e7ee96f6e0ce3daae39eaa8f0486124e0d92daf64d2105aeb9069c
@ -843,15 +841,15 @@ CHECKSUMS
crack (1.0.0) sha256=c83aefdb428cdc7b66c7f287e488c796f055c0839e6e545fec2c7047743c4a49
crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d
css_parser (1.21.1) sha256=6cfd3ffc0a97333b39d2b1b49c95397b05e0e3b684d68f77ec471ba4ec2ef7c7
csv (3.3.4) sha256=e96ecd5a8c3494aa5b596282249daba5c6033203c199248e6146e36d2a78d8cd
csv (3.3.3) sha256=7e2966befb7bdaf7d5e9b36e1de73e6a5e7a72f584f180a1726aec88a1b0a900
date (3.4.1) sha256=bf268e14ef7158009bfeaec40b5fa3c7271906e88b196d958a89d4b408abe64f
debug_inspector (1.2.0) sha256=9bdfa02eebc3da163833e6a89b154084232f5766087e59573b70521c77ea68a2
diff-lcs (1.6.1) sha256=12a5a83f3e37a8e2f4427268e305914d5f1879f22b4e73bb1a09f76a3dd86cd4
diff-lcs (1.6.0) sha256=a1e7f7b272962f8fc769358ad00001b87cdcf32ba349d6c70c6b544613d2da2e
diffy (3.4.3) sha256=4264b9e7db00d1cd426fcd32e36565779163cedc2340a95b0e6f025e71f9aaa7
digest (3.2.0) sha256=fa2e7092ec683f65d82fadde5ff4ca3b32e23ee0b19f1fc1a5e09993ad2d3991
digest-xxhash (0.2.9) sha256=a989d8309c03c4136a4bea9981ec0a146a2750de7f3dfb7b5624a3038aa598d7
discourse-emojis (1.0.39) sha256=7c7ce56b7a97f7ca2b9b26ffb2c2bb6454d2bfbb45dd3b68d91dd1e90b8e9c04
discourse-fonts (0.0.19) sha256=78d4ddd671615908303a675427039d8d787c935e6deae184c6e143c18c6e0033
discourse-emojis (1.0.38) sha256=4f9cfcbc7ea8aef69e1210e8796233ad9923ae908ee7696424f3ad92e51d6c51
discourse-fonts (0.0.18) sha256=a7d25c13edd3325ae40010ca277530d69fc7952a05aa7b2ff07c1d07412b72a1
discourse-seed-fu (2.3.12) sha256=4f61d95c11ed54609046cd04eb3a51b531c5fa863fa86d1bea7d74264e5c75e4
discourse_dev_assets (0.0.5) sha256=a09a801a210aa3f7247dd382dfd73b389d4bd02be86be675eca2d451f9898285
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
@ -862,30 +860,30 @@ CHECKSUMS
excon (1.2.5) sha256=ca040bb61bc0059968f34a17115a00d2db8562e3c0c5c5c7432072b551c85a9d
execjs (2.10.0) sha256=6bcb8be8f0052ff9d370b65d1c080f2406656e150452a0abdb185a133048450d
exifr (1.4.1) sha256=768374cc6b6ff3743acba57c1c35229bdd8c6b9fbc1285952047fc1215c4b894
extralite-bundle (2.12) sha256=9c9912b7ab592e7064089ee608cf86ab9edd81d20065acb87d1f4fed06052987
extralite-bundle (2.10) sha256=d765b84abe359f8ed8ee1735de016167ab9f7c7760e3f9b49ba7434fa81ff192
fabrication (2.31.0) sha256=2c79f10d1b88034a2ebd47ce77acba66847fc4636581c8282b3408adc68e85aa
faker (3.5.1) sha256=1ad1fbea279d882f486059c23fe3ddb816ccd1d7052c05a45014b4450d859bfc
fakeweb (1.3.0) sha256=1ec996be13020a00b3464560c09180b424477c698f59f82edf2b99b16cfa09a8
faraday (2.13.0) sha256=f2697cd61a434dc446ee035f0370de654c2ad64707c4fc2541eb2338702e9614
faraday (2.12.2) sha256=157339c25c7b8bcb739f5cf1207cb0cefe8fa1c65027266bcbc34c90c84b9ad6
faraday-net_http (3.4.0) sha256=a1f1e4cd6a2cf21599c8221595e27582d9936819977bbd4089a601f24c64e54a
faraday-retry (2.3.1) sha256=4004faa21f41fc5360d359bc79889fc58fb7fae0ce93bfb737a784ac76805c03
faraday-retry (2.2.1) sha256=4146fed14549c0580bf14591fca419a40717de0dd24f267a8ec2d9a728677608
fast_blank (1.0.1) sha256=269fc30414fed4e6403bc4a49081e1ea539f8b9226e59276ed1efaefabaa17ea
fastimage (2.3.1) sha256=23c629f1f3e7d61bcfcc06c25b3d2418bc6bf41d2e615dbf5132c0e3b63ecce9
ffi (1.17.2-aarch64-linux-gnu) sha256=c910bd3cae70b76690418cce4572b7f6c208d271f323d692a067d59116211a1a
ffi (1.17.2-aarch64-linux-musl) sha256=69e6556b091d45df83e6c3b19d3c54177c206910965155a6ec98de5e893c7b7c
ffi (1.17.2-arm-linux-gnu) sha256=d4a438f2b40224ae42ec72f293b3ebe0ba2159f7d1bd47f8417e6af2f68dbaa5
ffi (1.17.2-arm-linux-musl) sha256=977dfb7f3a6381206dbda9bc441d9e1f9366bf189a634559c3b7c182c497aaa3
ffi (1.17.2-arm64-darwin) sha256=54dd9789be1d30157782b8de42d8f887a3c3c345293b57ffb6b45b4d1165f813
ffi (1.17.2-x86_64-darwin) sha256=981f2d4e32ea03712beb26e55e972797c2c5a7b0257955d8667ba58f2da6440e
ffi (1.17.2-x86_64-linux-gnu) sha256=05d2026fc9dbb7cfd21a5934559f16293815b7ce0314846fee2ac8efbdb823ea
ffi (1.17.2-x86_64-linux-musl) sha256=97c0eb3981414309285a64dc4d466bd149e981c279a56371ef811395d68cb95c
ffi (1.17.1-aarch64-linux-gnu) sha256=c5d22cb545a3a691d46060f1343c461d1a8d38c3fd71b96b4cbbe6906bf1fd38
ffi (1.17.1-aarch64-linux-musl) sha256=88b9d6ae905d21142df27c94bb300042c1aae41b67291885f600eaad16326b1d
ffi (1.17.1-arm-linux-gnu) sha256=fe14f5ece94082f3b0e651a09008113281f2764e7ea95f522b64e2fe32e11504
ffi (1.17.1-arm-linux-musl) sha256=df14927ca7bd9095148a7d1938bb762bbf189d190cf25d9547395ec7acc198a0
ffi (1.17.1-arm64-darwin) sha256=a8e04f79d375742c54ee7f9fff4b4022b87200a4ec0eb082128d3b6559e67b4d
ffi (1.17.1-x86_64-darwin) sha256=0036199c290462dd7f03bc22933644c1685b7834a21788062bd5df48c72aa7a6
ffi (1.17.1-x86_64-linux-gnu) sha256=8c0ade2a5d19f3672bccfe3b58e016ae5f159e3e2e741c856db87fcf07c903d0
ffi (1.17.1-x86_64-linux-musl) sha256=3a343086820c96d6fbea4a5ef807fb69105b2b8174678f103b3db210c3f78401
fspath (3.1.2) sha256=b5ac9bafb97e2c8f8f9e303cd98ebd484be76fe9aa588bc4d01c6d99e78c9d75
globalid (1.2.1) sha256=70bf76711871f843dbba72beb8613229a49429d1866828476f9c9d6ccc327ce9
google-protobuf (4.30.2) sha256=0f35168dbeeccf13d928acf6c128cfec17b9a826ae4505246a02c115f4ae16ed
google-protobuf (4.30.2-aarch64-linux) sha256=a99d2f31bc2bebf4994b7cd2dd4c1d3ef28a0acc8f1b37be236982f7dc21bd8d
google-protobuf (4.30.2-arm64-darwin) sha256=c66a93ceef100fb2390615e76310491cc6e1944f7b9cda2cf1e3279887f817d1
google-protobuf (4.30.2-x86_64-darwin) sha256=17f4567dff431f8dd5be5ff6395824ec044413f67d2803a9941ebc8c70dec604
google-protobuf (4.30.2-x86_64-linux) sha256=c96993d98732ea185d98279f6c76e130eb9595437dda39610b3398c9e348518e
google-protobuf (4.30.1) sha256=0c88b433866eac8a07d13ea19182e23334b14ac99d2a186cd88b72e2413d0778
google-protobuf (4.30.1-aarch64-linux) sha256=5e155eae7b686bd4f826f947f311d1c00b4c3b467ca21b27c47610b400a215c5
google-protobuf (4.30.1-arm64-darwin) sha256=f69682f65a0c2f689040714e2c6407c838b9113dce87d76e7e568f1213b7fdeb
google-protobuf (4.30.1-x86_64-darwin) sha256=626836baf2470c5590fedda795598cea1c3385f690a97f2ed6d7d43a31110d9a
google-protobuf (4.30.1-x86_64-linux) sha256=84c87b239dec7a200dddbcbd9bdb03a59c109f4b32667a50bec84286faa9784e
guess_html_encoding (0.0.11) sha256=cab6468b945f38673fc41ad147fbbc89693b3c6c34b03b071e2ed669a603e098
hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
hashdiff (1.1.2) sha256=2c30eeded6ed3dce8401d2b5b99e6963fe5f14ed85e60dd9e33c545a44b71a77
@ -898,7 +896,7 @@ CHECKSUMS
image_size (3.4.0) sha256=c6a580513fe74947e25e5d3f0aea1e33add6c20f7d0007efa65504317b7f029a
in_threads (1.6.0) sha256=91a7e6138d279dc632f59b8a9a409e47148948e297c0f69c92f9a2479a182149
io-console (0.8.0) sha256=cd6a9facbc69871d69b2cb8b926fc6ea7ef06f06e505e81a64f14a470fddefa2
irb (1.15.2) sha256=222f32952e278da34b58ffe45e8634bf4afc2dc7aa9da23fed67e581aa50fdba
irb (1.15.1) sha256=d9bca745ac4207a8b728a52b98b766ca909b86ff1a504bcde3d6f8c84faae890
iso8601 (0.13.0) sha256=298c2b15b7be5fa95a1372813d36a2257656cd8e906dfbc1f5cb409851425aa2
jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1
json (2.10.2) sha256=34e0eada93022b2a0a3345bb0b5efddb6e9ff5be7c48e409cfb54ff8a36a8b06
@ -916,7 +914,7 @@ CHECKSUMS
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
listen (3.9.0) sha256=db9e4424e0e5834480385197c139cb6b0ae0ef28cc13310cfd1ca78377d59c67
literate_randomizer (0.4.0) sha256=05073c9b383983b1ed7e26c40b963468e91bc86e663b3eeff3a4af91b84217b1
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
logger (1.6.6) sha256=dd618d24e637715472732e7eed02e33cfbdf56deaad225edd0f1f89d38024017
lograge (0.14.0) sha256=42371a75823775f166f727639f5ddce73dd149452a55fc94b90c303213dc9ae1
logstash-event (1.2.02) sha256=89a7dc60fac67070a5f60ba07409e541b09cb58906c391e90cb74b9f217467ae
logster (2.20.1) sha256=a84bbe58e7f99c4eb0246ea3a952ed87950f63aeea3cdbe7147018d10cc9a2a8
@ -931,7 +929,7 @@ CHECKSUMS
messageformat-wrapper (1.1.0) sha256=ecea879626e412d1bc841c457dacfcbb1a62cf88ca83573e4ea34bb371f160bc
method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
mini_racer (0.18.1) sha256=c6694c37672da05027fe0894215a94c18a2117bdbb4ba90d80d275e4b23b3413
mini_racer (0.18.0) sha256=742ea6f25e45ad33efaa3e0b7c5c5a0ab4f2c1f5b116de1324c04cc89fd5607e
mini_scheduler (0.18.0) sha256=d2f084f38da8d76c5844a92f0d6bd01fc9982a8b5e6c7679b6cf44c82da33503
mini_sql (1.6.0) sha256=5296637f6a4af5bb43e06788037e9a2968ff9c8eb65928befcba8cb41f42d6ee
mini_suffix (0.3.3) sha256=8d1d33f92f69a2247c9b7d27173235da90479d955cdb863b63a7f53843b722e7
@ -948,14 +946,14 @@ CHECKSUMS
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9
nokogiri (1.18.7-aarch64-linux-gnu) sha256=57a064ab5440814a69a0e040817bd8154adea68a30d2ff2b3aa515a6a06dbb5f
nokogiri (1.18.7-aarch64-linux-musl) sha256=3e442dc5b69376e84288295fe37cbb890a21ad816a7e571e5e9967b3c1e30cd3
nokogiri (1.18.7-arm-linux-gnu) sha256=337d9149deb5ae01022dff7c90f97bed81715fd586aacab0c5809ef933994c5e
nokogiri (1.18.7-arm-linux-musl) sha256=97a26edcc975f780a0822aaf7f7d7427c561067c1c9ee56bd3542960f0c28a6e
nokogiri (1.18.7-arm64-darwin) sha256=083abb2e9ed2646860f6b481a981485a658c6064caafaa81bf1cda1bada2e9d5
nokogiri (1.18.7-x86_64-darwin) sha256=081d1aa517454ba3415304e2ea51fe411d6a3a809490d0c4aa42799cada417b7
nokogiri (1.18.7-x86_64-linux-gnu) sha256=3a0bf946eb2defde13d760f869b61bc8b0c18875afdd3cffa96543cfa3a18005
nokogiri (1.18.7-x86_64-linux-musl) sha256=9d83f8ec1fc37a305fa835d7ee61a4f37899e6ccc6dcb05be6645fa9797605af
nokogiri (1.18.6-aarch64-linux-gnu) sha256=1b11f9a814068282cc2b47ebe61395b2a69d1918092d2ca3bd664074f72540e9
nokogiri (1.18.6-aarch64-linux-musl) sha256=797662f201c37a8feac3bd5b0c0e3447053bc71e6633d273fefd4c68b03e6a54
nokogiri (1.18.6-arm-linux-gnu) sha256=2da07a07ef4c9d9e9da809b3dc0937ed90b031e32c2c658d9918941b85d68b95
nokogiri (1.18.6-arm-linux-musl) sha256=e8ae1c9a4d8cfa7a92d632a6f596a88235ebe66d4b70418543378ba16c601f70
nokogiri (1.18.6-arm64-darwin) sha256=727a441d179d934b4b7c73e0e28e6723ee46463d96bb0cc6e2e33a13540962c4
nokogiri (1.18.6-x86_64-darwin) sha256=fb72568c97ccd90a8d68cb765b0ff0720b109bd62e3babbf372e854ef8fef995
nokogiri (1.18.6-x86_64-linux-gnu) sha256=df065db6ba6e1e80f76ef04f860fcf260cc24685125fe33cdc3d1572a1c66b71
nokogiri (1.18.6-x86_64-linux-musl) sha256=75ec7a93cec54687aa63b2eaf830dc4ac5b4f3d8c969f20c035e67c9e6a30cef
oauth (1.1.0) sha256=38902b7f0f5ed91e858d6353f5e1e06b2c16a8aa0fd91984671eab1a1d1cddeb
oauth-tty (1.0.5) sha256=34e25c307da4509d4deec266ff3690bbf42e391355f496201c029268862d8b17
oauth2 (1.4.11) sha256=6739fcc8872bc94f476b0cae3e8bd78a56d8364b1b79b0757794c25e152d5c10
@ -971,17 +969,16 @@ CHECKSUMS
openssl-signature_algorithm (1.3.0) sha256=a3b40b5e8276162d4a6e50c7c97cdaf1446f9b2c3946a6fa2c14628e0c957e80
optimist (3.2.1) sha256=8cf8a0fd69f3aa24ab48885d3a666717c27bc3d9edd6e976e18b9d771e72e34e
ostruct (0.6.1) sha256=09a3fb7ecc1fa4039f25418cc05ae9c82bd520472c5c6a6f515f03e4988cb817
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
parallel (1.26.3) sha256=d86babb7a2b814be9f4b81587bf0b6ce2da7d45969fab24d8ae4bf2bb4d4c7ef
parallel_tests (5.1.0) sha256=c8cc0eef06577543dc3e09220ac5271809d9a1c65fa52ef86dd759bb4cb5aba5
parser (3.3.8.0) sha256=2476364142b307fa5a1b1ece44f260728be23858a9c71078e956131a75453c45
parser (3.3.7.2) sha256=c71de9076be0b84459a268581b5aa75e5eeaac892b564430371d1f8eb55fb65f
pg (1.5.9) sha256=761efbdf73b66516f0c26fcbe6515dc7500c3f0aa1a1b853feae245433c64fdc
pp (0.6.2) sha256=947ec3120c6f92195f8ee8aa25a7b2c5297bb106d83b41baa02983686577b6ff
prettier_print (1.2.1) sha256=a72838b5f23facff21f90a5423cdcdda19e4271092b41f4ea7f50b83929e6ff9
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
prism (1.4.0) sha256=dc0e3e00e93160213dc2a65519d9002a4a1e7b962db57d444cf1a71565bb703e
progress (3.6.0) sha256=360ed306dfa43d6174e847d563c70736dca249e2333cfec4b0387306c86cd573
pry (0.15.2) sha256=12d54b8640d3fa29c9211dd4ffb08f3fd8bf7a4fd9b5a73ce5b59c8709385b6b
pry-byebug (3.11.0) sha256=0b0abb7d309bc7f00044d512a3c8567274f7012b944b38becc8440439a1cea72
pry (0.14.2) sha256=c4fe54efedaca1d351280b45b8849af363184696fcac1c72e0415f9bdac4334d
pry-byebug (3.10.1) sha256=c8f975c32255bfdb29e151f5532130be64ff3d0042dc858d0907e849125581f8
pry-rails (0.3.11) sha256=a69e28e24a34d75d1f60bcf241192a54253f8f7ef8a62cba1e75750a9653593d
pry-stack_explorer (0.6.1) sha256=a2dbea9b47c4ad00cf5c1ce21499f8128b915089e90015f7bafb6e9453baf340
psych (5.2.3) sha256=84a54bb952d14604fea22d99938348814678782f58b12648fcdfa4d2fce859ee
@ -1006,13 +1003,13 @@ CHECKSUMS
rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
rbtrace (0.5.1) sha256=e8cba64d462bfb8ba102d7be2ecaacc789247d52ac587d8003549d909cb9c5dc
rchardet (1.9.0) sha256=26889486cdd83b378652baf7603f71d93e431bb11bc237b4cd8c65151af4a590
rdoc (6.13.1) sha256=62a0dac99493c94e8eb7a3fb44e55aefcb4cecb119f7991f25bddc5ed8d472f7
rdoc (6.12.0) sha256=7d6f706e070bffa5d18a448f24076cbfb34923a99c1eab842aa18e6ca69f56e0
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
redis (5.4.0) sha256=798900d869418a9fc3977f916578375b45c38247a556b61d58cba6bb02f7d06b
redis-client (0.24.0) sha256=ee65ee39cb2c38608b734566167fd912384f3c1241f59075e22858f23a085dbb
redis-namespace (1.11.0) sha256=e91a1aa2b2d888b6dea1d4ab8d39e1ae6fac3426161feb9d91dd5cca598a2239
regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61
reline (0.6.1) sha256=1afcc9d7cb1029cdbe780d72f2f09251ce46d3780050f3ec39c3ccc6b60675fb
reline (0.6.0) sha256=57620375dcbe56ec09bac7192bfb7460c716bbf0054dc94345ecaa5438e539d2
request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb
rexml (3.4.1) sha256=c74527a9a0a04b4ec31dbe0dc4ed6004b960af943d8db42e539edde3a871abca
rinku (2.0.6) sha256=8b60670e3143f3db2b37efa262971ce3619ec23092045498ef9f077d82828d7d
@ -1032,12 +1029,12 @@ CHECKSUMS
rss (0.3.1) sha256=b46234c04551b925180f8bedfc6f6045bf2d9998417feda72f300e7980226737
rswag-specs (2.16.0) sha256=8ba26085c408b0bd2ed21dc8015c80f417c7d34c63720ab7133c2549b5bd2a91
rtlcss (0.2.1) sha256=213d5a00bf61267f93a7a516d699d77e1cc5f396743abb33c01e3f3243a7bf60
rubocop (1.75.2) sha256=8efde647e278417e8074421b007e0d7d7c591482ef99d980528b18fea015a7c8
rubocop-ast (1.44.1) sha256=e3cc04203b2ef04f6d6cf5f85fe6d643f442b18cc3b23e3ada0ce5b6521b8e92
rubocop (1.74.0) sha256=06138a35d7d11c963d5abc0148b355e3999007cb0225a619940db0e75521379b
rubocop-ast (1.41.0) sha256=0eeff9ad3743f42823e3c687cbc15f8c552ff63c9f8e3d35fbafe746977c7c0d
rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c
rubocop-discourse (3.12.1) sha256=ebf7e2224f053047372071419052828c3e3a01bccb14ea1f282ac143547df9bc
rubocop-factory_bot (2.27.1) sha256=9d744b5916778c1848e5fe6777cc69855bd96548853554ec239ba9961b8573fe
rubocop-rails (2.31.0) sha256=79476e1075299c3e60fc50549c7c32614f9ebaae719b899ed75785c6786c52bd
rubocop-rails (2.30.3) sha256=fc5a6506daa916d15e282cc806943afa64a020bf592b93a94025d89a2a78a715
rubocop-rspec (3.5.0) sha256=710c942fe1af884ba8eea75cbb8bdbb051929a2208880a6fc2e2dce1eed5304c
rubocop-rspec_rails (2.31.0) sha256=775375e18a26a1184a812ef3054b79d218e85601b9ae897f38f8be24dddf1f45
ruby-prof (1.7.1) sha256=026393448cf92fd24a91739bf71ccd2bfe88fe8a1401ee8afc4948a16d62ea24
@ -1056,8 +1053,8 @@ CHECKSUMS
sass-embedded (1.85.0-x86_64-linux-musl) sha256=ca01931c54b59db1130a779386bcfc798f8dcfee1f98f60fbcf8c5dbdc0f9c23
sassc-embedded (1.80.2) sha256=625ac3923fe9af1a8072210cf22b01b66f3dc23748a4997a9b28bfdff7c7967d
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
selenium-devtools (0.135.0) sha256=3862ec1f3e940a030f492b3691d11af35544789f961fcd0633cb89e4ea4251ec
selenium-webdriver (4.31.0) sha256=ddb2d88eee23cddb5d6a9dadd909427a9e5163718338e11a91eef2fbead100e9
selenium-devtools (0.133.0) sha256=aba5a5225ac38d235bbc6ebb4e0b8209e973ac7af4226e4304a1417573f73f64
selenium-webdriver (4.29.1) sha256=0a7fe53cc4d2c515adbb89e115c6e786c64e9b98f85939d21071c6e32883a146
shoulda-matchers (6.4.0) sha256=9055bb7f4bb342125fb860809798855c630e05ef5e75837b3168b8e6ee1608b0
sidekiq (7.3.9) sha256=1108712e1def89002b28e3545d5ae15d4a57ffd4d2c25d97bb1360988826b5a7
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
@ -1077,7 +1074,7 @@ CHECKSUMS
sqlite3 (2.6.0-x86_64-linux-musl) sha256=6f7c9346430c4aacc9280dee6bd4d23a6f1f31013d4f9c0a720fb14ef6a801c2
sshkey (3.0.0) sha256=655ba351d6e01a48dfe59d65530af8975c777b8cc57a061770de3228ff2d11cd
stackprof (0.2.27) sha256=aff6d28656c852e74cf632cc2046f849033dc1dedffe7cb8c030d61b5745e80c
stringio (3.1.6) sha256=292c495d1657adfcdf0a32eecf12a60e6691317a500c3112ad3b2e31068274f5
stringio (3.1.5) sha256=bca92461515a131535743bc81d5559fa1de7d80cff9a654d6c0af6f9f27e35c8
syntax_tree (6.2.0) sha256=a50a01c246601af3c258edbb6b12e44373d17966ab3bebd1f7224b3b994a343d
test-prof (1.4.4) sha256=1a59513ed9d33a1f5ca17c0b89da4e70f60a91c83ec62e9a873dbb99141353ef
thor (1.3.2) sha256=eef0293b9e24158ccad7ab383ae83534b7ad4ed99c09f96f1a6b036550abbeda
@ -1093,7 +1090,7 @@ CHECKSUMS
uniform_notifier (1.16.0) sha256=99b39ee4a0864e3b49f375b5e5803eb26d35ed6eb1719c96407573a87bc4dbb5
uri (1.0.3) sha256=e9f2244608eea2f7bc357d954c65c910ce0399ca5e18a7a29207ac22d8767011
useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
version_gem (1.1.7) sha256=df3bacb16c09d9069d51625f6e009da28e69ed8f9cbd2dd14753cec944e0cacc
version_gem (1.1.6) sha256=b989cf19880ee18907083ba9bb6fdbe40826bd698fbd7cdfab7345a2550bf203
web-push (3.0.1) sha256=5b4dd2f2bba3bd8951da6416492fe920a6f203d14d3080f943c5d01c0cc4b18d
webmock (3.25.1) sha256=ab9d5d9353bcbe6322c83e1c60a7103988efc7b67cd72ffb9012629c3d396323
webrick (1.9.1) sha256=b42d3c94f166f3fb73d87e9b359def9b5836c426fc8beacf38f2184a21b2a989

View File

@ -10,7 +10,6 @@ import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
import AdminFilteredSiteSettings from "admin/components/admin-filtered-site-settings";
import AdminSiteSettingsChangesBanner from "admin/components/admin-site-settings-changes-banner";
import SiteSetting from "admin/models/site-setting";
export default class AdminAreaSettings extends Component {
@ -95,7 +94,5 @@ export default class AdminAreaSettings extends Component {
</ConditionalLoadingSpinner>
{{/if}}
</div>
<AdminSiteSettingsChangesBanner />
</template>
}

View File

@ -12,9 +12,8 @@ import { i18n } from "discourse-i18n";
import AdminConfigAreaCardSection from "admin/components/admin-config-area-card-section";
import SimpleList from "admin/components/simple-list";
export default class AdminLogoForm extends Component {
export default class AdminBrandingLogoForm extends Component {
@service siteSettings;
@service siteSettingChangeTracker;
@service toasts;
@tracked placeholders = {};
@ -57,7 +56,7 @@ export default class AdminLogoForm extends Component {
@action
async save(data) {
try {
await ajax("/admin/config/logo.json", {
await ajax("/admin/config/branding/logo.json", {
type: "PUT",
data: {
logo: data.logo,
@ -79,10 +78,9 @@ export default class AdminLogoForm extends Component {
this.toasts.success({
duration: 3000,
data: {
message: i18n("admin.config.logo_and_fonts.logo.form.saved"),
message: i18n("admin.config.branding.logo.form.saved"),
},
});
this.siteSettingChangeTracker.refreshPage(data);
} catch (err) {
this.toasts.error({
duration: 3000,
@ -131,13 +129,11 @@ export default class AdminLogoForm extends Component {
>
<form.Field
@name="logo"
@title={{i18n "admin.config.logo_and_fonts.logo.form.logo.title"}}
@title={{i18n "admin.config.branding.logo.form.logo.title"}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.logo.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.logo.help_text"
"admin.config.branding.logo.form.logo.description"
}}
@helpText={{i18n "admin.config.branding.logo.form.logo.help_text"}}
@onSet={{fn this.handleUpload "logo"}}
as |field|
>
@ -145,9 +141,7 @@ export default class AdminLogoForm extends Component {
</form.Field>
<form.Field
@name="logo_dark_required"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_dark.required"
}}
@title={{i18n "admin.config.branding.logo.form.logo_dark.required"}}
@format="full"
as |field|
>
@ -157,11 +151,9 @@ export default class AdminLogoForm extends Component {
<form.Section>
<form.Field
@name="logo_dark"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_dark.title"
}}
@title={{i18n "admin.config.branding.logo.form.logo_dark.title"}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.logo_dark.help_text"
"admin.config.branding.logo.form.logo_dark.help_text"
}}
@onSet={{fn this.handleUpload "logo_dark"}}
as |field|
@ -172,14 +164,12 @@ export default class AdminLogoForm extends Component {
{{/if}}
<form.Field
@name="large_icon"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.large_icon.title"
}}
@title={{i18n "admin.config.branding.logo.form.large_icon.title"}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.large_icon.description"
"admin.config.branding.logo.form.large_icon.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.large_icon.help_text"
"admin.config.branding.logo.form.large_icon.help_text"
}}
@onSet={{fn this.handleUpload "large_icon"}}
@placeholderUrl={{this.placeholders.large_icon}}
@ -189,9 +179,9 @@ export default class AdminLogoForm extends Component {
</form.Field>
<form.Field
@name="favicon"
@title={{i18n "admin.config.logo_and_fonts.logo.form.favicon.title"}}
@title={{i18n "admin.config.branding.logo.form.favicon.title"}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.favicon.description"
"admin.config.branding.logo.form.favicon.description"
}}
@onSet={{fn this.handleUpload "favicon"}}
@placeholderUrl={{this.placeholders.favicon}}
@ -201,14 +191,12 @@ export default class AdminLogoForm extends Component {
</form.Field>
<form.Field
@name="logo_small"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small.title"
}}
@title={{i18n "admin.config.branding.logo.form.logo_small.title"}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small.description"
"admin.config.branding.logo.form.logo_small.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small.help_text"
"admin.config.branding.logo.form.logo_small.help_text"
}}
@onSet={{fn this.handleUpload "logo_small"}}
as |field|
@ -218,7 +206,7 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="logo_small_dark_required"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small_dark.required"
"admin.config.branding.logo.form.logo_small_dark.required"
}}
@format="full"
as |field|
@ -230,10 +218,10 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="logo_small_dark"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small_dark.title"
"admin.config.branding.logo.form.logo_small_dark.title"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.logo_small_dark.help_text"
"admin.config.branding.logo.form.logo_small_dark.help_text"
}}
@onSet={{fn this.handleUpload "logo_small_dark"}}
as |field|
@ -244,7 +232,7 @@ export default class AdminLogoForm extends Component {
{{/if}}
<AdminConfigAreaCardSection
@heading={{i18n "admin.config.logo_and_fonts.logo.form.mobile"}}
@heading={{i18n "admin.config.branding.logo.form.mobile"}}
class="admin-logo-form__mobile-section"
@collapsable={{true}}
@collapsed={{true}}
@ -253,13 +241,13 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="mobile_logo"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.mobile_logo.title"
"admin.config.branding.logo.form.mobile_logo.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.mobile_logo.description"
"admin.config.branding.logo.form.mobile_logo.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.mobile_logo.help_text"
"admin.config.branding.logo.form.mobile_logo.help_text"
}}
@onSet={{fn this.handleUpload "mobile_logo"}}
@placeholderUrl={{this.placeholders.mobile_logo}}
@ -270,7 +258,7 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="mobile_logo_dark_required"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.mobile_logo_dark.required"
"admin.config.branding.logo.form.mobile_logo_dark.required"
}}
@format="full"
as |field|
@ -282,10 +270,10 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="mobile_logo_dark"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.mobile_logo_dark.title"
"admin.config.branding.logo.form.mobile_logo_dark.title"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.mobile_logo_dark.help_text"
"admin.config.branding.logo.form.mobile_logo_dark.help_text"
}}
@onSet={{fn this.handleUpload "mobile_logo_dark"}}
as |field|
@ -297,13 +285,13 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="manifest_icon"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.manifest_icon.title"
"admin.config.branding.logo.form.manifest_icon.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.manifest_icon.description"
"admin.config.branding.logo.form.manifest_icon.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.manifest_icon.help_text"
"admin.config.branding.logo.form.manifest_icon.help_text"
}}
@onSet={{fn this.handleUpload "manifest_icon"}}
as |field|
@ -313,10 +301,10 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="manifest_screenshots"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.manifest_screenshots.title"
"admin.config.branding.logo.form.manifest_screenshots.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.manifest_screenshots.description"
"admin.config.branding.logo.form.manifest_screenshots.description"
}}
@format="full"
as |field|
@ -334,13 +322,13 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="apple_touch_icon"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.apple_touch_icon.title"
"admin.config.branding.logo.form.apple_touch_icon.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.apple_touch_icon.description"
"admin.config.branding.logo.form.apple_touch_icon.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.apple_touch_icon.help_text"
"admin.config.branding.logo.form.apple_touch_icon.help_text"
}}
@onSet={{fn this.handleUpload "apple_touch_icon"}}
@placeholderUrl={{this.placeholders.apple_touch_icon}}
@ -351,7 +339,7 @@ export default class AdminLogoForm extends Component {
</:content>
</AdminConfigAreaCardSection>
<AdminConfigAreaCardSection
@heading={{i18n "admin.config.logo_and_fonts.logo.form.email"}}
@heading={{i18n "admin.config.branding.logo.form.email"}}
class="admin-logo-form__email-section"
@collapsable={{true}}
@collapsed={{true}}
@ -360,13 +348,13 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="digest_logo"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.digest_logo.title"
"admin.config.branding.logo.form.digest_logo.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.digest_logo.description"
"admin.config.branding.logo.form.digest_logo.description"
}}
@helpText={{i18n
"admin.config.logo_and_fonts.logo.form.digest_logo.help_text"
"admin.config.branding.logo.form.digest_logo.help_text"
}}
@onSet={{fn this.handleUpload "digest_logo"}}
@placeholderUrl={{this.placeholders.digest_logo}}
@ -380,7 +368,7 @@ export default class AdminLogoForm extends Component {
</:content>
</AdminConfigAreaCardSection>
<AdminConfigAreaCardSection
@heading={{i18n "admin.config.logo_and_fonts.logo.form.social_media"}}
@heading={{i18n "admin.config.branding.logo.form.social_media"}}
class="admin-logo-form__social-media-section"
@collapsable={{true}}
@collapsed={{true}}
@ -389,10 +377,10 @@ export default class AdminLogoForm extends Component {
<form.Field
@name="opengraph_image"
@title={{i18n
"admin.config.logo_and_fonts.logo.form.opengraph_image.title"
"admin.config.branding.logo.form.opengraph_image.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.logo.form.opengraph_image.description"
"admin.config.branding.logo.form.opengraph_image.description"
}}
@onSet={{fn this.handleUpload "opengraph_image"}}
@placeholderUrl={{this.placeholders.opengraph_image}}

View File

@ -10,10 +10,10 @@ import DButton from "discourse/components/d-button";
import Form from "discourse/components/form";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { API_KEY_SCOPE_MODES } from "discourse/lib/constants";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import ApiKeyUrlsModal from "admin/components/modal/api-key-urls";
import { API_KEY_SCOPE_MODES } from "admin/lib/constants";
import EmailGroupUserChooser from "select-kit/components/email-group-user-chooser";
export default class AdminConfigAreasApiKeysNew extends Component {

View File

@ -207,12 +207,12 @@ export default class AdminConfigAreasColorPalette extends Component {
/>
</div>
<form.Alert class="fonts-and-logos-hint">
<div class="admin-config-color-palettes__logo-and-fonts-hint">
<div class="admin-config-color-palettes__fonts-and-logos-hint">
<span>{{i18n
"admin.config_areas.color_palettes.logo_and_fonts_hint"
"admin.config_areas.color_palettes.fonts_and_logos_hint"
}}</span>
<LinkTo @route="adminConfig.logo-and-fonts">{{i18n
"admin.config_areas.color_palettes.go_to_logo_and_fonts"
<LinkTo @route="adminConfig.branding">{{i18n
"admin.config_areas.color_palettes.go_to_branding"
}}</LinkTo>
</div>
</form.Alert>

View File

@ -1,71 +1,17 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { array, concat, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import DButton from "discourse/components/d-button";
import DPageSubheader from "discourse/components/d-page-subheader";
import DSelect from "discourse/components/d-select";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import DropdownMenu from "discourse/components/dropdown-menu";
import FilterInput from "discourse/components/filter-input";
import LoadMore from "discourse/components/load-more";
import icon from "discourse/helpers/d-icon";
import { ajax } from "discourse/lib/ajax";
import { extractErrorInfo } from "discourse/lib/ajax-error";
import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import getURL from "discourse/lib/get-url";
import { descriptionForRemoteUrl } from "discourse/lib/popular-themes";
import { i18n } from "discourse-i18n";
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
import InstallThemeCard from "admin/components/admin-config-area-cards/install-theme-card";
import InstallComponentModal from "admin/components/modal/install-theme";
import ThemesGrid from "admin/components/themes-grid";
import { COMPONENTS } from "admin/models/theme";
import DMenu from "float-kit/components/d-menu";
const STATUS_FILTER_OPTIONS = [
{
value: "all",
label: "admin.config_areas.themes_and_components.components.filter_by_all",
},
{
value: "used",
label: "admin.config_areas.themes_and_components.components.filter_by_used",
},
{
value: "unused",
label:
"admin.config_areas.themes_and_components.components.filter_by_unused",
},
{
value: "updates_available",
label:
"admin.config_areas.themes_and_components.components.filter_by_updates_available",
},
];
export default class AdminConfigAreasComponents extends Component {
@service modal;
@service router;
@service toasts;
@tracked loading = true;
@tracked components = [];
@tracked nameFilter;
@tracked statusFilter;
@tracked hasComponents = false;
@tracked loadingMore = false;
page = 0;
hasMore = false;
constructor() {
super(...arguments);
this.load();
}
@action
installModal() {
this.modal.show(InstallComponentModal, {
@ -82,7 +28,7 @@ export default class AdminConfigAreasComponents extends Component {
selectedType: COMPONENTS,
userId: null,
content: [],
installedThemes: this.components,
installedThemes: this.args.components,
addTheme: this.addComponent,
updateSelectedType: () => {},
showComponentsOnly: true,
@ -99,482 +45,19 @@ export default class AdminConfigAreasComponents extends Component {
},
duration: 2000,
});
this.load();
}
@action
onNameFilterChange(event) {
this.loading = true;
this.nameFilter = event.target.value;
this.page = 0;
discourseDebounce(this, this.load, INPUT_DELAY);
}
@action
onStatusFilterChange(value) {
this.loading = true;
this.statusFilter = value;
this.page = 0;
this.load();
}
@action
async load({ append = false } = {}) {
try {
const data = await ajax("/admin/config/customize/components", {
data: {
name: this.nameFilter,
status: this.statusFilter,
page: this.page,
},
});
if (append) {
this.components = [...this.components, ...data.components];
} else {
this.components = data.components;
}
this.hasMore = data.has_more;
if (!this.hasComponents && !this.nameFilter && !this.statusFilter) {
this.hasComponents = !!data.components.length;
}
} finally {
this.loading = false;
}
}
@action
async loadMore() {
if (this.loadingMore) {
return;
}
if (this.hasMore) {
this.page += 1;
this.loadingMore = true;
try {
await this.load({ append: true });
} finally {
this.loadingMore = false;
}
}
this.router.refresh();
}
<template>
<DPageSubheader
@titleLabel={{i18n
"admin.config_areas.themes_and_components.components.title"
}}
@descriptionLabel={{i18n
"admin.config_areas.themes_and_components.components.description"
}}
>
<:actions as |actions|>
<actions.Primary
disabled={{this.loading}}
@label="admin.config_areas.themes_and_components.components.install"
@action={{this.installModal}}
/>
</:actions>
</DPageSubheader>
<div class="container">
{{#if this.hasComponents}}
<div class="d-admin-filter">
<div
class="admin-filter__input-container admin-config-components__name-filter"
>
<FilterInput
placeholder={{i18n
"admin.config_areas.themes_and_components.components.search_components"
}}
@filterAction={{this.onNameFilterChange}}
class="admin-filter__input"
/>
</div>
<label class="admin-config-components__status-filter">
{{i18n
"admin.config_areas.themes_and_components.components.filter_by"
}}
<DSelect
@value="all"
@includeNone={{false}}
@onChange={{this.onStatusFilterChange}}
as |select|
>
{{#each STATUS_FILTER_OPTIONS as |option|}}
<select.Option @value={{option.value}}>
{{i18n option.label}}
</select.Option>
{{/each}}
</DSelect>
</label>
</div>
{{/if}}
<ConditionalLoadingSpinner @condition={{this.loading}}>
{{#if this.components.length}}
<LoadMore @selector=".component-list tr" @action={{this.loadMore}}>
<table class="d-admin-table component-list">
<thead>
<th>{{i18n
"admin.config_areas.themes_and_components.components.name"
}}</th>
<th>{{i18n
"admin.config_areas.themes_and_components.components.used_on"
}}</th>
<th>{{i18n
"admin.config_areas.themes_and_components.components.enabled"
}}</th>
<th></th>
</thead>
<tbody>
{{#each this.components as |comp|}}
<ComponentRow @component={{comp}} @refresh={{this.load}} />
{{/each}}
</tbody>
</table>
<ConditionalLoadingSpinner @condition={{this.loadingMore}} />
</LoadMore>
{{else}}
{{#if this.hasComponents}}
{{i18n
"admin.config_areas.themes_and_components.components.no_components_found"
}}
{{else}}
<AdminConfigAreaEmptyList
@emptyLabel="admin.config_areas.themes_and_components.components.no_components"
/>
{{/if}}
{{/if}}
</ConditionalLoadingSpinner>
<div class="admin-detail">
<ThemesGrid @themes={{@components}}>
<:specialCard>
<InstallThemeCard
@component={{true}}
@openModal={{this.installModal}}
/>
</:specialCard>
</ThemesGrid>
</div>
</template>
}
class ComponentRow extends Component {
@service toasts;
@service dialog;
@tracked enabled = this.args.component.enabled;
@tracked hasUpdates = this.args.component.remote_theme?.commits_behind > 0;
@tracked disableToggle = false;
@tracked checkingForUpdates = false;
@tracked updating = false;
get parentThemesCell() {
const names = this.args.component.parent_themes.map((theme) => theme.name);
names.sort();
if (!names.length) {
return;
} else if (names.length === 1) {
return names[0];
} else if (names.length === 2) {
return i18n(
"admin.config_areas.themes_and_components.components.parent_themes_two",
{
name1: names[0],
name2: names[1],
}
);
} else if (names.length === 3) {
return i18n(
"admin.config_areas.themes_and_components.components.parent_themes_three",
{
name1: names[0],
name2: names[1],
name3: names[2],
}
);
} else {
return i18n(
"admin.config_areas.themes_and_components.components.parent_themes_more_than_three",
{
name1: names[0],
name2: names[1],
name3: names[2],
count: names.length - 3,
}
);
}
}
get description() {
const remoteUrl = this.args.component.remote_theme?.remote_url;
return (
this.args.component.description ??
(remoteUrl && descriptionForRemoteUrl(remoteUrl))
);
}
@action
async toggleEnabled() {
this.disableToggle = true;
try {
const data = await this.save({ enabled: !this.enabled });
this.enabled = data.theme.enabled;
} finally {
this.disableToggle = false;
}
}
@action
async checkForUpdates() {
this.checkingForUpdates = true;
try {
const data = await this.save({ remote_check: true });
if (data.theme.remote_theme.commits_behind > 0) {
this.hasUpdates = true;
this.toasts.default({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.new_update_for_component",
{ name: this.args.component.name }
),
},
});
} else {
this.hasUpdates = false;
this.toasts.default({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.component_up_to_date",
{ name: this.args.component.name }
),
},
});
}
} finally {
this.checkingForUpdates = false;
}
}
@action
async updateToLatest() {
this.updating = true;
try {
await this.save({ remote_update: true });
this.hasUpdates = false;
this.toasts.success({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.updated_successfully",
{ name: this.args.component.name }
),
},
});
} finally {
this.updating = false;
}
}
@action
delete() {
return this.dialog.yesNoConfirm({
message: i18n(
"admin.config_areas.themes_and_components.components.delete_confirm",
{ name: this.args.component.name }
),
didConfirm: async () => {
try {
await ajax(`/admin/themes/${this.args.component.id}`, {
type: "DELETE",
});
this.toasts.success({
duration: 5000,
data: {
message: i18n(
"admin.config_areas.themes_and_components.components.deleted_successfully",
{ name: this.args.component.name }
),
},
});
this.args.refresh();
} catch (error) {
this.toasts.error({
duration: 5000,
data: {
message: extractErrorInfo(error),
},
});
}
},
});
}
async save(attrs) {
try {
return await ajax(`/admin/themes/${this.args.component.id}.json`, {
type: "PUT",
data: {
theme: attrs,
},
});
} catch (error) {
this.toasts.error({
duration: 5000,
data: {
message: extractErrorInfo(error),
},
});
throw error;
}
}
<template>
<tr
data-component-id={{@component.id}}
class="d-admin-row__content admin-config-components__component-row
{{if this.hasUpdates 'has-update'}}"
>
<td class="d-admin-row__overview">
<div class="d-admin-row__overview-name">
{{@component.name}}
</div>
{{#if @component.remote_theme.authors}}
<div
class="d-admin-row__overview-author admin-config-components__author-name"
>{{i18n
"admin.config_areas.themes_and_components.components.by_author"
(hash name=@component.remote_theme.authors)
}}</div>
{{/if}}
{{#if this.description}}
<div
class="d-admin-row__overview-about admin-config-components__description"
>
{{this.description}}
{{#if @component.remote_theme.about_url}}
<a href={{@component.remote_theme.about_url}}>{{i18n
"admin.config_areas.themes_and_components.components.learn_more"
}}
{{icon "up-right-from-square"}}
</a>
{{/if}}
</div>
{{/if}}
{{#if this.hasUpdates}}
<div
class="d-admin-row__overview-about admin-config-components__update-available"
>
{{i18n
"admin.config_areas.themes_and_components.components.update_available"
}}
</div>
{{/if}}
</td>
<td class="d-admin-row__detail admin-config-components__parent-themes">
<div class="d-admin-row__mobile-label">
{{i18n "admin.config_areas.themes_and_components.components.used_on"}}
</div>
<div class="admin-config-components__parent-themes-list">
{{#if @component.parent_themes.length}}
{{this.parentThemesCell}}
{{else}}
<div class="status-label --inactive">
<div class="status-label-indicator"></div>
<div class="status-label-text">
{{i18n
"admin.config_areas.themes_and_components.components.badge_unused"
}}
</div>
</div>
{{/if}}
</div>
</td>
<td class="d-admin-row__detail">
<div class="d-admin-row__mobile-label">
{{i18n "admin.config_areas.themes_and_components.components.enabled"}}
</div>
<DToggleSwitch
@state={{this.enabled}}
class="admin-config-components__toggle"
disabled={{this.disableToggle}}
{{on "click" this.toggleEnabled}}
/>
</td>
<td class="d-admin-row__controls">
<div class="d-admin-row__controls-options">
<DButton
class="admin-config-components__edit"
@label="admin.config_areas.themes_and_components.components.edit"
@route="adminCustomizeThemes.show"
@routeModels={{array "themes" @component.id}}
/>
<DMenu
@identifier="component-menu"
@title={{i18n "admin.config_areas.flags.more_options.title"}}
@icon="ellipsis"
@class="btn-default admin-config-components__more-actions"
>
<:content>
<DropdownMenu as |dropdown|>
<dropdown.item>
<DButton
class="btn-transparent admin-config-components__preview"
target="_blank"
rel="noopener noreferrer"
@label="admin.config_areas.themes_and_components.components.preview"
@icon="desktop"
@href={{getURL
(concat "/admin/themes/" @component.id "/preview")
}}
/>
</dropdown.item>
{{#if @component.remote_theme.is_git}}
<dropdown.item>
{{#if this.hasUpdates}}
<DButton
class="btn-transparent admin-config-components__update"
@label="admin.config_areas.themes_and_components.components.update"
@icon="cloud-arrow-down"
@action={{this.updateToLatest}}
@isLoading={{this.updating}}
/>
{{else}}
<DButton
class="btn-transparent admin-config-components__check-updates"
@label="admin.config_areas.themes_and_components.components.check_update"
@icon="arrows-rotate"
@action={{this.checkForUpdates}}
@isLoading={{this.checkingForUpdates}}
/>
{{/if}}
</dropdown.item>
{{/if}}
<dropdown.item>
<DButton
class="btn-transparent admin-config-components__export"
target="_blank"
rel="noopener noreferrer"
@label="admin.config_areas.themes_and_components.components.export"
@icon="download"
@href={{getURL
(concat
"/admin/customize/themes/" @component.id "/export"
)
}}
/>
</dropdown.item>
<dropdown.item>
<DButton
class="btn-danger admin-config-components__delete"
@label="admin.config_areas.themes_and_components.components.delete"
@icon="trash-can"
@action={{this.delete}}
/>
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
</div>
</td>
</tr>
</template>
}

View File

@ -4,10 +4,10 @@ import { action } from "@ember/object";
import { service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { SYSTEM_FLAG_IDS } from "discourse/lib/constants";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminFlagItem from "admin/components/admin-flag-item";
import { SYSTEM_FLAG_IDS } from "admin/lib/constants";
export default class AdminConfigAreasFlags extends Component {
@service site;

View File

@ -12,8 +12,8 @@ import DropdownMenu from "discourse/components/dropdown-menu";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { SYSTEM_FLAG_IDS } from "discourse/lib/constants";
import { i18n } from "discourse-i18n";
import { SYSTEM_FLAG_IDS } from "admin/lib/constants";
import DMenu from "float-kit/components/d-menu";
export default class AdminFlagItem extends Component {
@ -28,11 +28,10 @@ export default class AdminFlagItem extends Component {
}
get canEdit() {
return !Object.values(SYSTEM_FLAG_IDS).includes(this.args.flag.id);
}
get canDelete() {
return this.canEdit && !this.args.flag.is_used;
return (
!Object.values(SYSTEM_FLAG_IDS).includes(this.args.flag.id) &&
!this.args.flag.is_used
);
}
get editTitle() {
@ -42,9 +41,9 @@ export default class AdminFlagItem extends Component {
}
get deleteTitle() {
return this.canDelete
? "admin.config_areas.flags.form.delete_flag"
: "admin.config_areas.flags.form.non_deletable";
return this.canEdit
? "admin.config_areas.flags.form.edit_flag"
: "admin.config_areas.flags.form.non_editable";
}
@action
@ -192,7 +191,7 @@ export default class AdminFlagItem extends Component {
@icon="trash-can"
class="btn-transparent btn-danger admin-flag-item__delete"
@action={{this.delete}}
@disabled={{not this.canDelete}}
@disabled={{not this.canEdit}}
@title={{this.deleteTitle}}
/>
</dropdown.item>

View File

@ -133,14 +133,6 @@ export default class AdminFlagsForm extends Component {
<AdminConfigAreaCard @heading={{this.header}}>
<:content>
<Form @onSubmit={{this.save}} @data={{this.formData}} as |form|>
<form.Alert @type="warning" @icon="circle-info">
{{#if this.isUpdate}}
{{i18n "admin.config_areas.flags.form.edit_warning"}}
{{else}}
{{i18n "admin.config_areas.flags.form.create_warning"}}
{{/if}}
</form.Alert>
<form.Field
@name="name"
@title={{i18n "admin.config_areas.flags.form.name"}}
@ -218,6 +210,10 @@ export default class AdminFlagsForm extends Component {
</checkboxGroup.Field>
</form.CheckboxGroup>
<form.Alert @icon="circle-info">
{{i18n "admin.config_areas.flags.form.alert"}}
</form.Alert>
<form.Submit @label="admin.config_areas.flags.form.save" />
</Form>
</:content>

View File

@ -1,63 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { concat, fn } from "@ember/helper";
import { action } from "@ember/object";
import { dasherize } from "@ember/string";
import DButton from "discourse/components/d-button";
import concatClass from "discourse/helpers/concat-class";
import { i18n } from "discourse-i18n";
import { MAIN_FONTS, MORE_FONTS } from "admin/lib/constants";
import eq from "truth-helpers/helpers/eq";
export default class AdminFontChooser extends Component {
@tracked showMoreFonts = MORE_FONTS.map((font) => font.key).includes(
this.args.selectedFont
);
@action
setButtonValue(fieldSet, value) {
fieldSet(value);
}
@action
toggleMoreFonts() {
this.showMoreFonts = !this.showMoreFonts;
}
<template>
<@field.Custom>
{{#each MAIN_FONTS as |font|}}
<DButton
@action={{fn this.setButtonValue @field.set font.key}}
class={{concatClass
"admin-fonts-form__button-option font btn-flat"
(concat "body-font-" (dasherize font.key))
(if (eq @selectedFont font.key) "active")
}}
>{{font.name}}</DButton>
{{/each}}
{{#if this.showMoreFonts}}
{{#each MORE_FONTS as |font|}}
<DButton
@action={{fn this.setButtonValue @field.set font.key}}
class={{concatClass
"admin-fonts-form__button-option font btn-flat"
(concat "body-font-" (dasherize font.key))
(if (eq @selectedFont font.key) "active")
}}
>{{font.name}}</DButton>
{{/each}}
{{/if}}
<DButton
@action={{this.toggleMoreFonts}}
class="admin-fonts-form__more font"
>
{{#if this.showMoreFonts}}
{{i18n "admin.config.logo_and_fonts.fonts.form.less_fonts"}}
{{else}}
{{i18n "admin.config.logo_and_fonts.fonts.form.more_fonts"}}
{{/if}}
</DButton>
</@field.Custom>
</template>
}

View File

@ -1,175 +0,0 @@
import Component from "@glimmer/component";
import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { decamelize, underscore } from "@ember/string";
import DButton from "discourse/components/d-button";
import Form from "discourse/components/form";
import UpdateDefaultTextSize from "discourse/components/modal/update-default-text-size";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import AdminFontChooser from "admin/components/admin-font-chooser";
import {
DEFAULT_TEXT_SIZES,
MAIN_FONTS,
MORE_FONTS,
} from "admin/lib/constants";
import eq from "truth-helpers/helpers/eq";
const ALL_FONTS = [...MAIN_FONTS, ...MORE_FONTS];
export default class AdminFontsForm extends Component {
@service siteSettings;
@service siteSettingChangeTracker;
@service toasts;
@service modal;
@service router;
updateExistingUsers = null;
@bind
setUpdateExistingUsers(value) {
this.updateExistingUsers = value;
}
@action
setButtonValue(fieldSet, value) {
fieldSet(decamelize(underscore(value)));
}
@action
async update(data) {
if (this.siteSettings.default_text_size === data.default_text_size) {
await this.#save(data);
return;
}
const result = await ajax(
`/admin/site_settings/default_text_size/user_count.json`,
{
type: "PUT",
data: {
default_text_size: data.default_text_size,
},
}
);
const count = result.user_count;
if (count > 0) {
await this.modal.show(UpdateDefaultTextSize, {
model: {
setUpdateExistingUsers: this.setUpdateExistingUsers,
count,
},
});
await this.#save(data);
} else {
await this.#save(data);
}
}
@action
async #save(data) {
try {
await ajax("/admin/config/fonts.json", {
type: "PUT",
data: {
base_font: data.base_font,
heading_font: data.heading_font,
default_text_size: data.default_text_size,
update_existing_users: this.updateExistingUsers,
},
});
this.toasts.success({
duration: 3000,
data: {
message: i18n("admin.config.logo_and_fonts.fonts.form.saved"),
},
});
this.siteSettingChangeTracker.refreshPage({
base_font: ALL_FONTS.find((font) => font.key === data.base_font).name,
heading_font: ALL_FONTS.find((font) => font.key === data.heading_font)
.name,
default_text_size: data.default_text_size,
});
} catch (err) {
this.toasts.error({
duration: 3000,
data: {
message: err.jqXHR.responseJSON.errors[0],
},
});
}
}
get formData() {
return {
base_font: this.siteSettings.base_font,
heading_font: this.siteSettings.heading_font,
default_text_size: this.siteSettings.default_text_size,
};
}
<template>
<Form
@onSubmit={{this.update}}
@data={{this.formData}}
class="admin-fonts-form"
as |form transientData|
>
<form.Field
@name="base_font"
@title={{i18n "admin.config.logo_and_fonts.fonts.form.base_font.title"}}
@validation="required"
@format="full"
as |field|
>
<AdminFontChooser
@field={{field}}
@selectedFont={{transientData.base_font}}
/>
</form.Field>
<form.Field
@name="heading_font"
@title={{i18n
"admin.config.logo_and_fonts.fonts.form.heading_font.title"
}}
@validation="required"
@format="full"
as |field|
>
<AdminFontChooser
@field={{field}}
@selectedFont={{transientData.heading_font}}
/>
</form.Field>
<form.Field
@name="default_text_size"
@title={{i18n
"admin.config.logo_and_fonts.fonts.form.default_text_size.title"
}}
@description={{i18n
"admin.config.logo_and_fonts.fonts.form.default_text_size.description"
}}
@validation="required"
@format="full"
as |field|
>
<field.Custom>
{{#each DEFAULT_TEXT_SIZES as |textSize|}}
<DButton
@action={{fn this.setButtonValue field.set textSize}}
class={{concatClass
"admin-fonts-form__button-option text-size btn-flat"
textSize
(if (eq transientData.default_text_size textSize) "active")
}}
>{{textSize}}</DButton>
{{/each}}
</field.Custom>
</form.Field>
<form.Submit />
</Form>
</template>
}

View File

@ -1,8 +1,8 @@
import Component from "@ember/component";
import { classNames } from "@ember-decorators/component";
import { eq } from "truth-helpers";
import concatClass from "discourse/helpers/concat-class";
import { i18n } from "discourse-i18n";
import eq from "truth-helpers/helpers/eq";
@classNames("row")
export default class AdminFormRow extends Component {

View File

@ -0,0 +1,25 @@
// TODO (martin) Delete this once we have removed references from plugins.
import {
DangerActionListItem,
DangerButton,
DefaultActionListItem,
DefaultButton,
DPageActionButton,
DPageActionListItem,
PrimaryActionListItem,
PrimaryButton,
WrappedActionListItem,
WrappedButton,
} from "discourse/components/d-page-action-button";
export { DangerActionListItem as DangerActionListItem };
export { DangerButton as DangerButton };
export { DefaultActionListItem as DefaultActionListItem };
export { DefaultButton as DefaultButton };
export { DPageActionButton as DPageActionButton };
export { DPageActionListItem as DPageActionListItem };
export { PrimaryActionListItem as PrimaryActionListItem };
export { PrimaryButton as PrimaryButton };
export { WrappedActionListItem as WrappedActionListItem };
export { WrappedButton as WrappedButton };

View File

@ -2,12 +2,12 @@ import Component, { Textarea } from "@ember/component";
import { action } from "@ember/object";
import { equal } from "@ember/object/computed";
import { tagName } from "@ember-decorators/component";
import { eq } from "truth-helpers";
import TextField from "discourse/components/text-field";
import htmlSafe from "discourse/helpers/html-safe";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import eq from "truth-helpers/helpers/eq";
const CUSTOM_REASON_KEY = "custom";

View File

@ -4,7 +4,6 @@ import { Input } from "@ember/component";
import { fn, get, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { not } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import avatar from "discourse/helpers/avatar";
import formatDuration from "discourse/helpers/format-duration";
@ -12,6 +11,7 @@ import htmlSafe from "discourse/helpers/html-safe";
import number from "discourse/helpers/number";
import { ajax } from "discourse/lib/ajax";
import { i18n } from "discourse-i18n";
import not from "truth-helpers/helpers/not";
export default class AdminPenaltySimilarUsers extends Component {
@tracked isLoading;

View File

@ -15,6 +15,7 @@ import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import number from "discourse/helpers/number";
import { reportModeComponent } from "discourse/lib/admin-report-additional-modes";
import { REPORT_MODES } from "discourse/lib/constants";
import { bind } from "discourse/lib/decorators";
import { isTesting } from "discourse/lib/environment";
import { exportEntity } from "discourse/lib/export-csv";
@ -34,7 +35,6 @@ import ReportFilterBoolComponent from "admin/components/report-filters/bool";
import ReportFilterCategoryComponent from "admin/components/report-filters/category";
import ReportFilterGroupComponent from "admin/components/report-filters/group";
import ReportFilterListComponent from "admin/components/report-filters/list";
import { REPORT_MODES } from "admin/lib/constants";
import Report, { DAILY_LIMIT_DAYS, SCHEMA_VERSION } from "admin/models/report";
import DTooltip from "float-kit/components/d-tooltip";

View File

@ -9,12 +9,12 @@ import { TrackedObject } from "@ember-compat/tracked-built-ins";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import DButton from "discourse/components/d-button";
import icon from "discourse/helpers/d-icon";
import { ADMIN_SEARCH_RESULT_TYPES } from "discourse/lib/constants";
import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import autoFocus from "discourse/modifiers/auto-focus";
import { i18n } from "discourse-i18n";
import AdminSearchFilters from "admin/components/admin-search-filters";
import { ADMIN_SEARCH_RESULT_TYPES } from "admin/lib/constants";
const ADMIN_SEARCH_FILTERS = "admin_search_filters";

View File

@ -1,109 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import htmlSafe from "discourse/helpers/html-safe";
import { i18n } from "discourse-i18n";
export default class AdminSiteSettingsChangesBanner extends Component {
@service siteSettingChangeTracker;
@tracked isSaving = false;
_resizer = null;
@action
async save() {
this.isSaving = true;
try {
await this.siteSettingChangeTracker.save();
} finally {
this.isSaving = false;
}
}
@action
discard() {
this.siteSettingChangeTracker.discard();
}
@action
setupResizeObserver(element) {
const container = document.getElementById("main-container");
this._resizer = () => this.positionBanner(container, element);
this._resizer();
this._resizeObserver = window.addEventListener("resize", this._resizer);
}
@action
teardownResizeObserver() {
window.removeEventListener("resize", this._resizer);
}
positionBanner(container, element) {
if (container) {
const { width } = container.getBoundingClientRect();
element.style.width = `${width}px`;
}
}
get dirtyCount() {
return this.siteSettingChangeTracker.count;
}
get showBanner() {
return this.dirtyCount > 0;
}
get bannerLabel() {
return i18n("admin.site_settings.dirty_banner", {
count: this.dirtyCount,
});
}
get saveLabel() {
return i18n("admin.site_settings.save", {
count: this.dirtyCount,
});
}
get discardLabel() {
return i18n("admin.site_settings.discard", {
count: this.dirtyCount,
});
}
<template>
{{#if this.showBanner}}
<div
class="admin-site-settings__changes-banner"
{{didInsert this.setupResizeObserver}}
{{willDestroy this.teardownResizeObserver}}
>
<span>{{htmlSafe this.bannerLabel}}</span>
<div class="controls">
<DButton
@action={{this.discard}}
@disabled={{this.isSaving}}
class="btn-secondary btn-small"
>
{{this.discardLabel}}
</DButton>
<DButton
@action={{this.save}}
@isLoading={{this.isSaving}}
class="btn-primary btn-small"
>
{{this.saveLabel}}
</DButton>
</div>
</div>
{{/if}}
</template>
}

View File

@ -6,7 +6,6 @@ import { action, computed } from "@ember/object";
import { LinkTo } from "@ember/routing";
import { next } from "@ember/runloop";
import { service } from "@ember/service";
import { gt, lte } from "truth-helpers";
import AceEditor from "discourse/components/ace-editor";
import icon from "discourse/helpers/d-icon";
import htmlSafe from "discourse/helpers/html-safe";
@ -14,6 +13,8 @@ import { fmt } from "discourse/lib/computed";
import discourseComputed from "discourse/lib/decorators";
import { isDocumentRTL } from "discourse/lib/text-direction";
import { i18n } from "discourse-i18n";
import gt from "truth-helpers/helpers/gt";
import lte from "truth-helpers/helpers/lte";
const JS_DEFAULT_VALUE = `import { apiInitializer } from "discourse/lib/api";

View File

@ -4,8 +4,8 @@ import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import DButton from "discourse/components/d-button";
import DropdownMenu from "discourse/components/dropdown-menu";
import { USER_FIELD_FLAGS } from "discourse/lib/constants";
import { i18n } from "discourse-i18n";
import { USER_FIELD_FLAGS } from "admin/lib/constants";
import UserField from "admin/models/user-field";
import DMenu from "float-kit/components/d-menu";

View File

@ -5,13 +5,13 @@ import { action, set, setProperties } from "@ember/object";
import { schedule } from "@ember/runloop";
import { service } from "@ember/service";
import { classNameBindings } from "@ember-decorators/component";
import { not } from "truth-helpers";
import DButton from "discourse/components/d-button";
import EmojiPicker from "discourse/components/emoji-picker";
import EmojiPickerDetached from "discourse/components/emoji-picker/detached";
import discourseComputed from "discourse/lib/decorators";
import { emojiUrlFor } from "discourse/lib/text";
import { i18n } from "discourse-i18n";
import not from "truth-helpers/helpers/not";
@classNameBindings(":value-list", ":emoji-list")
export default class EmojiValueList extends Component {

View File

@ -0,0 +1,57 @@
import Component from "@ember/component";
import { classNames } from "@ember-decorators/component";
import UserFlagPercentage from "discourse/components/user-flag-percentage";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
import FlagUser from "admin/components/flag-user";
import dispositionIcon from "admin/helpers/disposition-icon";
import postActionTitle from "admin/helpers/post-action-title";
@classNames("flag-user-lists")
export default class FlagUserLists extends Component {
<template>
<div class="flagged-by">
<div class="user-list-title">
{{i18n "admin.flags.flagged_by"}}
</div>
<div class="flag-users">
{{#each this.flaggedPost.post_actions as |postAction|}}
<FlagUser @user={{postAction.user}} @date={{postAction.created_at}}>
<div class="flagger-flag-type">
{{postActionTitle
postAction.post_action_type_id
postAction.name_key
}}
</div>
<UserFlagPercentage
@agreed={{postAction.user.flags_agreed}}
@disagreed={{postAction.user.flags_disagreed}}
@ignored={{postAction.user.flags_ignored}}
/>
</FlagUser>
{{/each}}
</div>
</div>
{{#if this.showResolvedBy}}
<div class="flagged-post-resolved-by">
<div class="user-list-title">
{{i18n "admin.flags.resolved_by"}}
</div>
<div class="flag-users">
{{#each this.flaggedPost.post_actions as |postAction|}}
<FlagUser
@user={{postAction.disposed_by}}
@date={{postAction.disposed_at}}
>
{{dispositionIcon postAction.disposition}}
{{#if postAction.staff_took_action}}
{{icon "gavel" title="admin.flags.took_action"}}
{{/if}}
</FlagUser>
{{/each}}
</div>
</div>
{{/if}}
</template>
}

View File

@ -49,10 +49,6 @@ export default class FormTemplateForm extends Component {
type: "multiselect",
icon: "bullseye",
},
{
type: "tagchooser",
icon: "bullseye",
},
];
get disablePreviewButton() {

View File

@ -4,7 +4,6 @@ import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { service } from "@ember/service";
import { isEmpty } from "@ember/utils";
import { eq } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import FutureDateInput from "discourse/components/future-date-input";
@ -14,6 +13,7 @@ import I18n, { i18n } from "discourse-i18n";
import AdminPenaltyPostAction from "admin/components/admin-penalty-post-action";
import AdminPenaltyReason from "admin/components/admin-penalty-reason";
import AdminPenaltySimilarUsers from "admin/components/admin-penalty-similar-users";
import eq from "truth-helpers/helpers/eq";
export default class PenalizeUser extends Component {
@service dialog;

View File

@ -1,63 +0,0 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { isEmpty } from "@ember/utils";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import TextField from "discourse/components/text-field";
import { i18n } from "discourse-i18n";
export default class ScrubRejectedUserModal extends Component {
@tracked isScrubbing = false;
@tracked scrubReason = "";
@action
async confirmScrub() {
this.isScrubbing = true;
await this.args.model.confirmScrub(this.scrubReason);
this.args.closeModal();
}
get scrubButtonDisabled() {
return isEmpty(this.scrubReason);
}
<template>
<DModal
@bodyClass="scrub-rejected-user"
class="admin-scrub-rejected-user-modal"
@title={{i18n "review.user.scrub_record.confirm_title"}}
@closeModal={{if this.isScrubbing null @closeModal}}
>
<:body>
<p>{{i18n "review.user.scrub_record.confirm_body"}}</p>
<label class="scrub-reason-title" for="scrub-reason">{{i18n
"review.user.scrub_record.reason_title"
}}</label>
<TextField
class="scrub-reason"
id="scrub-reason"
@placeholderKey="review.user.scrub_record.reason_placeholder"
{{on "input" (action (mut this.scrubReason) value="target.value")}}
/>
</:body>
<:footer>
<DButton
class="btn btn-danger"
@action={{this.confirmScrub}}
@isLoading={{this.isScrubbing}}
@disabled={{this.scrubButtonDisabled}}
@label="review.user.scrub_record.confirm_button"
/>
<DButton
class="btn btn-default"
@action={{@closeModal}}
@disabled={{this.isScrubbing}}
@label="review.user.scrub_record.cancel_button"
/>
</:footer>
</DModal>
</template>
}

View File

@ -6,11 +6,11 @@ import { action } from "@ember/object";
import { empty } from "@ember/object/computed";
import { isEmpty } from "@ember/utils";
import { classNameBindings } from "@ember-decorators/component";
import { gt } from "truth-helpers";
import DButton from "discourse/components/d-button";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import gt from "truth-helpers/helpers/gt";
@classNameBindings(":simple-list", ":value-list")
export default class SimpleList extends Component {

View File

@ -1,10 +1,11 @@
import { tracked } from "@glimmer/tracking";
import { cached, tracked } from "@glimmer/tracking";
import Component from "@ember/component";
import { hash } from "@ember/helper";
import { dependentKeyCompat } from "@ember/object/compat";
import { readOnly } from "@ember/object/computed";
import { getOwner } from "@ember/owner";
import { LinkTo } from "@ember/routing";
import BufferedProxy from "ember-buffered-proxy/proxy";
import DButton from "discourse/components/d-button";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
@ -27,15 +28,18 @@ export default class SiteSettingComponent extends Component.extend(
);
}
@cached
@dependentKeyCompat
get buffered() {
return this.setting.buffered;
return BufferedProxy.create({
content: this.setting,
});
}
_save() {
const setting = this.buffered;
return SiteSetting.update(setting.get("setting"), setting.get("value"), {
updateExistingUsers: this.setting.updateExistingUsers,
updateExistingUsers: this.updateExistingUsers,
});
}
@ -86,7 +90,7 @@ export default class SiteSettingComponent extends Component.extend(
@changeValueCallback={{this.changeValueCallback}}
@setValidationMessage={{this.setValidationMessage}}
/>
<SettingValidationMessage @message={{this.setting.validationMessage}} />
<SettingValidationMessage @message={{this.validationMessage}} />
{{#if this.displayDescription}}
<Description @description={{this.setting.description}} />
{{/if}}
@ -98,14 +102,13 @@ export default class SiteSettingComponent extends Component.extend(
<DButton
@action={{this.update}}
@icon="check"
@isLoading={{this.disableControls}}
@disabled={{this.disableSaveButton}}
@ariaLabel="admin.settings.save"
class="ok setting-controls__ok"
/>
<DButton
@action={{this.cancel}}
@icon="xmark"
@isLoading={{this.disableControls}}
@ariaLabel="admin.settings.cancel"
class="cancel setting-controls__cancel"
/>

View File

@ -1,10 +1,7 @@
import Component from "@glimmer/component";
import { htmlSafe } from "@ember/template";
import { eq } from "truth-helpers";
export default class ThemesGridPlaceholder extends Component {
randomVariant = Math.floor(Math.random() * 4);
get themeColors() {
if (this.args.theme.color_scheme) {
return {
@ -31,305 +28,59 @@ export default class ThemesGridPlaceholder extends Component {
}
}
get safeThemeColors() {
let colors = this.themeColors;
return {
primary: htmlSafe(colors.primary),
secondary: htmlSafe(colors.secondary),
tertiary: htmlSafe(colors.tertiary),
quaternary: htmlSafe(colors.quaternary),
highlight: htmlSafe(colors.highlight),
danger: htmlSafe(colors.danger),
success: htmlSafe(colors.success),
love: htmlSafe(colors.love),
};
}
get gradientId() {
return `bgGradient-${this.args.theme.id}-${this.randomVariant}`;
}
<template>
{{#if (eq this.randomVariant 0)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 800 200"
preserveAspectRatio="xMidYMid slice"
>
<rect fill={{this.safeThemeColors.tertiary}} width="800" height="200" />
<g transform="translate(280, -100) scale(0.4)">
<g
fill="none"
stroke={{this.safeThemeColors.secondary}}
stroke-width="1"
>
<path
d="M769 229L1037 260.9M927 880L731 737 520 660 309 538 40 599 295 764 126.5 879.5 40 599-197 493 102 382-31 229 126.5 79.5-69-63"
/>
<path
d="M-31 229L237 261 390 382 603 493 308.5 537.5 101.5 381.5M370 905L295 764"
/>
<path
d="M520 660L578 842 731 737 840 599 603 493 520 660 295 764 309 538 390 382 539 269 769 229 577.5 41.5 370 105 295 -36 126.5 79.5 237 261 102 382 40 599 -69 737 127 880"
/>
<path
d="M520-140L578.5 42.5 731-63M603 493L539 269 237 261 370 105M902 382L539 269M390 382L102 382"
/>
<path
d="M-222 42L126.5 79.5 370 105 539 269 577.5 41.5 927 80 769 229 902 382 603 493 731 737M295-36L577.5 41.5M578 842L295 764M40-201L127 80M102 382L-261 269"
/>
</g>
<g fill={{this.safeThemeColors.tertiary}}>
<circle cx="769" cy="229" r="5" />
<circle cx="539" cy="269" r="5" />
<circle cx="603" cy="493" r="5" />
<circle cx="731" cy="737" r="5" />
<circle cx="520" cy="660" r="5" />
<circle cx="309" cy="538" r="5" />
<circle cx="295" cy="764" r="5" />
<circle cx="40" cy="599" r="5" />
<circle cx="102" cy="382" r="5" />
<circle cx="127" cy="80" r="5" />
<circle cx="370" cy="105" r="5" />
<circle cx="578" cy="42" r="5" />
<circle cx="237" cy="261" r="5" />
<circle cx="390" cy="382" r="5" />
</g>
</g>
</svg>
{{else if (eq this.randomVariant 1)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 800 200"
preserveAspectRatio="xMidYMid slice"
>
<rect fill={{this.safeThemeColors.tertiary}} width="800" height="200" />
<defs>
<radialGradient
id="{{this.gradientId}}-a"
cx="0"
cy="347"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.quaternary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-b"
cx="636"
cy="347"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.tertiary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-c"
cx="600"
cy="0"
r="600"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.highlight}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.highlight}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-d"
cx="600"
cy="347"
r="600"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.quaternary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-e"
cx="0"
cy="0"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.tertiary}}
stop-opacity="0"
/>
</radialGradient>
<radialGradient
id="{{this.gradientId}}-f"
cx="636"
cy="0"
r="347"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop
offset="1"
stop-color={{this.safeThemeColors.tertiary}}
stop-opacity="0"
/>
</radialGradient>
</defs>
<rect fill="url(#{{this.gradientId}}-a)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-b)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-c)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-d)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-e)" width="800" height="200" />
<rect fill="url(#{{this.gradientId}}-f)" width="800" height="200" />
</svg>
{{else if (eq this.randomVariant 2)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 1600 800"
preserveAspectRatio="xMidYMid slice"
>
<rect
fill={{this.safeThemeColors.tertiary}}
width="1600"
height="800"
/>
<g fill-opacity=".4" transform="scale(1.2)">
<path
fill={{this.safeThemeColors.tertiary}}
d="M486 705.8c-109.3-21.8-223.4-32.2-335.3-19.4C99.5 692.1 49 703 0 719.8V800h843.8c-115.9-33.2-230.8-68.1-347.6-92.2C492.8 707.1 489.4 706.5 486 705.8z"
/>
<path
fill={{this.safeThemeColors.tertiary}}
d="M1600 0H0v719.8c49-16.8 99.5-27.8 150.7-33.5c111.9-12.7 226-2.4 335.3 19.4c3.4 0.7 6.8 1.4 10.2 2c116.8 24 231.7 59 347.6 92.2H1600V0z"
/>
<path
fill={{this.safeThemeColors.quaternary}}
d="M478.4 581c3.2 0.8 6.4 1.7 9.5 2.5c196.2 52.5 388.7 133.5 593.5 176.6c174.2 36.6 349.5 29.2 518.6-10.2V0H0v574.9c52.3-17.6 106.5-27.7 161.1-30.9C268.4 537.4 375.7 554.2 478.4 581z"
/>
<path
fill={{this.safeThemeColors.quaternary}}
d="M0 0v429.4c55.6-18.4 113.5-27.3 171.4-27.7c102.8-0.8 203.2 22.7 299.3 54.5c3 1 5.9 2 8.9 3c183.6 62 365.7 146.1 562.4 192.1c186.7 43.7 376.3 34.4 557.9-12.6V0H0z"
/>
<path
fill={{this.safeThemeColors.highlight}}
d="M181.8 259.4c98.2 6 191.9 35.2 281.3 72.1c2.8 1.1 5.5 2.3 8.3 3.4c171 71.6 342.7 158.5 531.3 207.7c198.8 51.8 403.4 40.8 597.3-14.8V0H0v283.2C59 263.6 120.6 255.7 181.8 259.4z"
/>
<path
fill={{this.safeThemeColors.highlight}}
d="M1600 0H0v136.3c62.3-20.9 127.7-27.5 192.2-19.2c93.6 12.1 180.5 47.7 263.3 89.6c2.6 1.3 5.1 2.6 7.7 3.9c158.4 81.1 319.7 170.9 500.3 223.2c210.5 61 430.8 49 636.6-16.6V0z"
/>
<path
fill={{this.safeThemeColors.danger}}
d="M454.9 86.3C600.7 177 751.6 269.3 924.1 325c208.6 67.4 431.3 60.8 637.9-5.3c12.8-4.1 25.4-8.4 38.1-12.9V0H288.1c56 21.3 108.7 50.6 159.7 82C450.2 83.4 452.5 84.9 454.9 86.3z"
/>
<path
fill={{this.safeThemeColors.danger}}
d="M1600 0H498c118.1 85.8 243.5 164.5 386.8 216.2c191.8 69.2 400 74.7 595 21.1c40.8-11.2 81.1-25.2 120.3-41.7V0z"
/>
<path
fill={{this.safeThemeColors.success}}
d="M1397.5 154.8c47.2-10.6 93.6-25.3 138.6-43.8c21.7-8.9 43-18.8 63.9-29.5V0H643.4c62.9 41.7 129.7 78.2 202.1 107.4C1020.4 178.1 1214.2 196.1 1397.5 154.8z"
/>
<path
fill={{this.safeThemeColors.success}}
d="M1315.3 72.4c75.3-12.6 148.9-37.1 216.8-72.4h-723C966.8 71 1144.7 101 1315.3 72.4z"
/>
</g>
</svg>
{{else if (eq this.randomVariant 3)}}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100%"
height="100%"
viewBox="0 0 2 1"
preserveAspectRatio="none"
>
<rect fill={{this.safeThemeColors.tertiary}} width="2" height="1" />
<defs>
<linearGradient
id="{{this.gradientId}}-a"
gradientUnits="userSpaceOnUse"
x1="0"
x2="0"
y1="0"
y2="1"
>
<stop offset="0" stop-color={{this.safeThemeColors.tertiary}} />
<stop offset="1" stop-color={{this.safeThemeColors.quaternary}} />
</linearGradient>
<linearGradient
id="{{this.gradientId}}-b"
gradientUnits="userSpaceOnUse"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="0"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
<stop
offset="1"
stop-color={{this.safeThemeColors.highlight}}
stop-opacity="1"
/>
</linearGradient>
<linearGradient
id="{{this.gradientId}}-c"
gradientUnits="userSpaceOnUse"
x1="0"
y1="0"
x2="2"
y2="2"
>
<stop
offset="0"
stop-color={{this.safeThemeColors.quaternary}}
stop-opacity="0"
/>
<stop
offset="1"
stop-color={{this.safeThemeColors.highlight}}
stop-opacity="1"
/>
</linearGradient>
</defs>
<rect
x="0"
y="0"
fill="url(#{{this.gradientId}}-a)"
width="2"
height="1"
/>
<g fill-opacity="0.5">
<polygon fill="url(#{{this.gradientId}}-b)" points="0 1 0 0 2 0" />
<polygon fill="url(#{{this.gradientId}}-c)" points="2 1 2 0 0 0" />
</g>
</svg>
{{/if}}
<svg viewBox="0 0 636 347" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect
width="635.115"
height="347"
fill={{htmlSafe this.themeColors.secondary}}
/>
<path
d="M54.9766 79.1039C55.9198 76.0065 59.8623 75.0916 62.0732 77.4571L121.448 140.986C123.659 143.351 122.48 147.223 119.326 147.955L34.621 167.611C31.467 168.343 28.7034 165.386 29.6466 162.288L54.9766 79.1039Z"
fill={{htmlSafe this.themeColors.primary}}
/>
<path
d="M398.487 211.02C400.651 208.611 404.611 209.448 405.615 212.527L432.579 295.196C433.584 298.274 430.879 301.285 427.711 300.615L342.635 282.633C339.467 281.963 338.212 278.115 340.376 275.707L398.487 211.02Z"
fill={{htmlSafe this.themeColors.tertiary}}
/>
<circle cx="109.357" cy="262.879" r="44.1636" fill="#D1F0FF" />
<circle cx="365.927" cy="103.048" r="44.1636" fill="#E45735" />
<rect
x="166.139"
y="68.751"
width="81.9226"
height="81.9226"
rx="4.20606"
transform="rotate(-15.9297 166.139 68.751)"
fill={{htmlSafe this.themeColors.danger}}
/>
<rect
x="500.521"
y="100.296"
width="81.9226"
height="81.9226"
rx="4.20606"
transform="rotate(-15.9297 500.521 100.296)"
fill={{htmlSafe this.themeColors.success}}
/>
<rect
x="481.857"
y="222.921"
width="121.976"
height="54.6788"
rx="4.20606"
transform="rotate(9.12857 481.857 222.921)"
fill={{htmlSafe this.themeColors.love}}
/>
<rect
x="176.654"
y="240.608"
width="121.976"
height="54.6788"
rx="4.20606"
transform="rotate(-22.7296 176.654 240.608)"
fill={{htmlSafe this.themeColors.primary}}
/>
</svg>
</template>
}

View File

@ -5,7 +5,6 @@ import { action } from "@ember/object";
import { equal, gt, gte } from "@ember/object/computed";
import { service } from "@ember/service";
import { classNames } from "@ember-decorators/component";
import { and, eq, not, or } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DeleteThemesConfirm from "discourse/components/modal/delete-themes-confirm";
import concatClass from "discourse/helpers/concat-class";
@ -15,6 +14,10 @@ import { i18n } from "discourse-i18n";
import ThemesListItem from "admin/components/themes-list-item";
import { COMPONENTS, THEMES } from "admin/models/theme";
import ComboBox from "select-kit/components/combo-box";
import and from "truth-helpers/helpers/and";
import eq from "truth-helpers/helpers/eq";
import not from "truth-helpers/helpers/not";
import or from "truth-helpers/helpers/or";
const ALL_FILTER = "all";
const ACTIVE_FILTER = "active";

View File

@ -8,7 +8,6 @@ import didInsert from "@ember/render-modifiers/modifiers/did-insert";
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
import { service } from "@ember/service";
import { not } from "truth-helpers";
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
import CountI18n from "discourse/components/count-i18n";
import DButton from "discourse/components/d-button";
@ -19,6 +18,7 @@ import { bind } from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import WebhookEvent from "admin/components/webhook-event";
import ComboBox from "select-kit/components/combo-box";
import not from "truth-helpers/helpers/not";
export default class WebhookEvents extends Component {
@service messageBus;

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigAnalyticsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminApiKeysSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,10 +1,3 @@
import Controller from "@ember/controller";
import { service } from "@ember/service";
export default class AdminApiKeysController extends Controller {
@service router;
get hideTabs() {
return ["adminApiKeys.show"].includes(this.router.currentRouteName);
}
}
export default class AdminApiKeysController extends Controller {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentCategoriesAndTagsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentPostsAndTopicsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentSharingController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigContentPostsAndTopicsController extends AdminAreaSettingsBaseController {}

View File

@ -0,0 +1,3 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigFontsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigInterfaceSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigSiteAdminSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminConfigUserDefaultsSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,5 +1,5 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import EmberObject, { action } from "@ember/object";
import {
empty,
filterBy,
@ -16,7 +16,6 @@ import discourseComputed from "discourse/lib/decorators";
import { makeArray } from "discourse/lib/helpers";
import { i18n } from "discourse-i18n";
import ThemeSettingsEditor from "admin/components/theme-settings-editor";
import SiteSetting from "admin/models/site-setting";
import { COMPONENTS, THEMES } from "admin/models/theme";
import ThemeSettings from "admin/models/theme-settings";
import ThemeUploadAddModal from "../components/theme-upload-add";
@ -92,11 +91,11 @@ export default class AdminCustomizeThemesShowController extends Controller {
@discourseComputed("model.parentThemes.[]")
relativesSelectorSettingsForComponent() {
return SiteSetting.create({
return EmberObject.create({
list_type: "compact",
type: "list",
preview: null,
allow_any: false,
anyValue: false,
setting: "parent_theme_ids",
label: i18n("admin.customize.theme.component_on_themes"),
choices: this.availableThemesNames,
@ -110,11 +109,11 @@ export default class AdminCustomizeThemesShowController extends Controller {
@discourseComputed("model.parentThemes.[]")
relativesSelectorSettingsForTheme() {
return SiteSetting.create({
return EmberObject.create({
list_type: "compact",
type: "list",
preview: null,
allow_any: false,
anyValue: false,
setting: "child_theme_ids",
label: i18n("admin.customize.theme.included_components"),
choices: this.availableComponentsNames,
@ -312,12 +311,10 @@ export default class AdminCustomizeThemesShowController extends Controller {
@action
updateLocale(value) {
this.set("model.loadingTranslations", true);
this.set("model.locale", value);
ajax(this.getTranslationsUrl).then(({ translations }) => {
this.set("model.translations", translations);
this.set("model.loadingTranslations", false);
});
ajax(this.getTranslationsUrl).then(({ translations }) =>
this.set("model.translations", translations)
);
}
@action

View File

@ -2,11 +2,11 @@ import { inject as controller } from "@ember/controller";
import { computed } from "@ember/object";
import { service } from "@ember/service";
import { setting } from "discourse/lib/computed";
import { REPORT_MODES } from "discourse/lib/constants";
import discourseComputed from "discourse/lib/decorators";
import getURL from "discourse/lib/get-url";
import { makeArray } from "discourse/lib/helpers";
import { i18n } from "discourse-i18n";
import { REPORT_MODES } from "admin/lib/constants";
import AdminDashboard from "admin/models/admin-dashboard";
import Report from "admin/models/report";
import AdminDashboardTabController from "./admin-dashboard-tab";

View File

@ -4,7 +4,7 @@ import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
export default class AdminEmailLogsBouncedController extends AdminEmailLogsController {
export default class AdminEmailBouncedController extends AdminEmailLogsController {
@action
handleShowIncomingEmail(id, event) {
event?.preventDefault();

View File

@ -5,7 +5,7 @@ import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import IncomingEmail from "admin/models/incoming-email";
export default class AdminEmailLogsReceivedController extends AdminEmailLogsController {
export default class AdminEmailReceivedController extends AdminEmailLogsController {
@observes("filter.{status,from,to,subject}")
filterIncomingEmails() {
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);

View File

@ -5,7 +5,7 @@ import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
import IncomingEmail from "admin/models/incoming-email";
export default class AdminEmailLogsRejectedController extends AdminEmailLogsController {
export default class AdminEmailRejectedController extends AdminEmailLogsController {
@observes("filter.{status,from,to,subject,error}")
filterIncomingEmails() {
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);

View File

@ -3,7 +3,7 @@ import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
export default class AdminEmailLogsSentController extends AdminEmailLogsController {
export default class AdminEmailSentController extends AdminEmailLogsController {
ccAddressDisplayThreshold = 2;
sortWithAddressFilter = (addresses) => {
if (!Array.isArray(addresses) || addresses.length === 0) {

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminEmailSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -3,7 +3,7 @@ import discourseDebounce from "discourse/lib/debounce";
import { INPUT_DELAY } from "discourse/lib/environment";
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
export default class AdminEmailLogsSkippedController extends AdminEmailLogsController {
export default class AdminEmailSkippedController extends AdminEmailLogsController {
@observes("filter.{status,user,address,type}")
filterEmailLogs() {
discourseDebounce(this, this.loadLogs, INPUT_DELAY);

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminReportsDashboardSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,10 +0,0 @@
import Controller from "@ember/controller";
import { service } from "@ember/service";
export default class AdminReportsController extends Controller {
@service router;
get hideTabs() {
return ["adminReports.show"].includes(this.router.currentRouteName);
}
}

View File

@ -1,11 +1,5 @@
import Controller from "@ember/controller";
import { PLATFORM_KEY_MODIFIER } from "discourse/lib/keyboard-shortcuts";
import { translateModKey } from "discourse/lib/utilities";
export default class AdminSearchIndexController extends Controller {
queryParams = ["filter"];
get shortcutHTML() {
return `<kbd>${translateModKey(PLATFORM_KEY_MODIFIER)}</kbd> + <kbd>/</kbd>`;
}
}

View File

@ -24,7 +24,7 @@ export default class AdminSiteTextEdit extends Controller {
});
}
@discourseComputed("buffered.value", "siteText.value")
@discourseComputed("buffered.value")
saveDisabled(value) {
return this.siteText.value === value;
}

View File

@ -133,7 +133,7 @@ export default class AdminUserIndexController extends Controller {
@computed("model.id", "currentUser.id")
get canCheckEmails() {
return new CanCheckEmailsHelper(
this.model.id,
this.model,
this.canModeratorsViewEmails,
this.currentUser
).canCheckEmails;
@ -142,7 +142,7 @@ export default class AdminUserIndexController extends Controller {
@computed("model.id", "currentUser.id")
get canAdminCheckEmails() {
return new CanCheckEmailsHelper(
this.model.id,
this.model,
this.canModeratorsViewEmails,
this.currentUser
).canAdminCheckEmails;

View File

@ -66,7 +66,7 @@ export default class AdminUsersListShowController extends Controller {
@computed("model.id", "currentUser.id")
get canCheckEmails() {
return new CanCheckEmailsHelper(
this.model?.id,
this.model,
this.canModeratorsViewEmails,
this.currentUser
).canCheckEmails;
@ -75,7 +75,7 @@ export default class AdminUsersListShowController extends Controller {
@computed("model.id", "currentUser.id")
get canAdminCheckEmails() {
return new CanCheckEmailsHelper(
this.model?.id,
this.model,
this.canModeratorsViewEmails,
this.currentUser
).canAdminCheckEmails;

View File

@ -1,3 +0,0 @@
import AdminAreaSettingsBaseController from "admin/controllers/admin-area-settings-base";
export default class AdminUsersSettingsController extends AdminAreaSettingsBaseController {}

View File

@ -1,6 +1,9 @@
import { htmlSafe } from "@ember/template";
import { registerRawHelper } from "discourse/lib/helpers";
import { renderIcon } from "discourse/lib/icon-library";
registerRawHelper("check-icon", checkIcon);
export default function checkIcon(value) {
let icon = value ? "check" : "xmark";
return htmlSafe(renderIcon("string", icon));

View File

@ -0,0 +1,28 @@
import Helper from "@ember/component/helper";
import { htmlSafe } from "@ember/template";
import { iconHTML } from "discourse/lib/icon-library";
export default class DispositionIcon extends Helper {
compute([disposition]) {
if (!disposition) {
return null;
}
let icon;
let title = "admin.flags.dispositions." + disposition;
switch (disposition) {
case "deferred": {
icon = "up-right-from-square";
break;
}
case "agreed": {
icon = "thumbs-o-up";
break;
}
case "disagreed": {
icon = "thumbs-o-down";
break;
}
}
return htmlSafe(iconHTML(icon, { title }));
}
}

View File

@ -0,0 +1,17 @@
import Helper from "@ember/component/helper";
import { i18n } from "discourse-i18n";
function postActionTitle([id, nameKey]) {
let title = i18n(`admin.flags.short_names.${nameKey}`, {
defaultValue: null,
});
// TODO: We can remove this once other translations have been updated
if (!title) {
return i18n(`admin.flags.summary.action_type_${id}`, { count: 1 });
}
return title;
}
export default Helper.helper(postActionTitle);

View File

@ -0,0 +1,6 @@
import { htmlHelper } from "discourse/lib/helpers";
import { escapeExpression } from "discourse/lib/utilities";
export default htmlHelper((str) =>
escapeExpression(str).replace(/\n/g, "<br>")
);

View File

@ -0,0 +1,17 @@
import { registerRawHelper } from "discourse/lib/helpers";
registerRawHelper("value-at-tl", valueAtTl);
export default function valueAtTl(data, params = {}) {
let tl = parseInt(params.level, 10);
if (data) {
let item = data.find(function (d) {
return parseInt(d.x, 10) === tl;
});
if (item) {
return item.y;
} else {
return 0;
}
}
}

View File

@ -1,124 +0,0 @@
// DO NOT EDIT THIS FILE!!!
// Update it by running `rake javascript:update_constants`
export const ADMIN_SEARCH_RESULT_TYPES = [
"page",
"setting",
"theme",
"component",
"report",
];
export const SITE_SETTING_REQUIRES_CONFIRMATION_TYPES = {
simple: "simple",
user_option: "user_option",
};
export const API_KEY_SCOPE_MODES = ["global", "read_only", "granular"];
export const SYSTEM_FLAG_IDS = {
like: 2,
notify_user: 6,
off_topic: 3,
inappropriate: 4,
spam: 8,
illegal: 10,
notify_moderators: 7,
};
export const REPORT_MODES = {
table: "table",
chart: "chart",
stacked_chart: "stacked_chart",
stacked_line_chart: "stacked_line_chart",
radar: "radar",
counters: "counters",
inline_table: "inline_table",
storage_stats: "storage_stats",
};
export const USER_FIELD_FLAGS = [
"editable",
"show_on_profile",
"show_on_user_card",
"searchable",
];
export const DEFAULT_USER_PREFERENCES = [
"default_email_digest_frequency",
"default_include_tl0_in_digests",
"default_email_level",
"default_email_messages_level",
"default_email_mailing_list_mode",
"default_email_mailing_list_mode_frequency",
"default_email_previous_replies",
"default_email_in_reply_to",
"default_hide_profile",
"default_hide_presence",
"default_other_new_topic_duration_minutes",
"default_other_auto_track_topics_after_msecs",
"default_other_notification_level_when_replying",
"default_other_external_links_in_new_tab",
"default_other_enable_quoting",
"default_other_enable_smart_lists",
"default_other_enable_defer",
"default_other_dynamic_favicon",
"default_other_like_notification_frequency",
"default_other_skip_new_user_tips",
"default_topics_automatic_unpin",
"default_categories_watching",
"default_categories_tracking",
"default_categories_muted",
"default_categories_watching_first_post",
"default_categories_normal",
"default_tags_watching",
"default_tags_tracking",
"default_tags_muted",
"default_tags_watching_first_post",
"default_text_size",
"default_title_count_mode",
"default_navigation_menu_categories",
"default_navigation_menu_tags",
"default_sidebar_link_to_filtered_list",
"default_sidebar_show_count_of_new_items",
];
export const MAIN_FONTS = [
{ key: "open_sans", name: "Open Sans" },
{ key: "roboto", name: "Roboto" },
{ key: "lato", name: "Lato" },
{ key: "inter", name: "Inter" },
{ key: "montserrat", name: "Montserrat" },
{ key: "poppins", name: "Poppins" },
{ key: "merriweather", name: "Merriweather" },
{ key: "mukta", name: "Mukta" },
{ key: "helvetica", name: "Helvetica" },
];
export const MORE_FONTS = [
{ key: "arial", name: "Arial" },
{ key: "system", name: "System" },
{ key: "oxanium", name: "Oxanium" },
{ key: "noto_sans_jp", name: "NotoSansJP" },
{ key: "roboto_condensed", name: "RobotoCondensed" },
{ key: "source_sans_pro", name: "SourceSansPro" },
{ key: "oswald", name: "Oswald" },
{ key: "raleway", name: "Raleway" },
{ key: "roboto_mono", name: "RobotoMono" },
{ key: "noto_sans", name: "NotoSans" },
{ key: "roboto_slab", name: "RobotoSlab" },
{ key: "ubuntu", name: "Ubuntu" },
{ key: "pt_sans", name: "PTSans" },
{ key: "playfair_display", name: "PlayfairDisplay" },
{ key: "nunito", name: "Nunito" },
{ key: "lora", name: "Lora" },
{ key: "jet_brains_mono", name: "JetBrains Mono" },
];
export const DEFAULT_TEXT_SIZES = [
"smallest",
"smaller",
"normal",
"larger",
"largest",
];

View File

@ -73,16 +73,4 @@ export const templateFormFields = [
validations:
# ${i18n("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "tagchooser",
structure: `- type: tag-chooser
id: ${i18n("admin.form_templates.field_placeholders.id")}
tag_group: ""
attributes:
none_label: "${i18n("admin.form_templates.field_placeholders.none_label")}"
label: "${i18n("admin.form_templates.field_placeholders.label")}"
multiple: true
validations:
# ${i18n("admin.form_templates.field_placeholders.validations")}`,
},
];

View File

@ -5,12 +5,16 @@ import Mixin from "@ember/object/mixin";
import { service } from "@ember/service";
import { htmlSafe } from "@ember/template";
import { isNone } from "@ember/utils";
import { Promise } from "rsvp";
import JsonSchemaEditorModal from "discourse/components/modal/json-schema-editor";
import { ajax } from "discourse/lib/ajax";
import { fmt, propertyNotEqual } from "discourse/lib/computed";
import { SITE_SETTING_REQUIRES_CONFIRMATION_TYPES } from "discourse/lib/constants";
import { deepEqual } from "discourse/lib/object";
import { humanizedSettingName } from "discourse/lib/site-settings-utils";
import { splitString } from "discourse/lib/utilities";
import { i18n } from "discourse-i18n";
import SiteSettingDefaultCategoriesModal from "../components/modal/site-setting-default-categories";
const CUSTOM_TYPES = [
"bool",
@ -38,14 +42,55 @@ const CUSTOM_TYPES = [
"font_list",
];
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
const DEFAULT_USER_PREFERENCES = [
"default_email_digest_frequency",
"default_include_tl0_in_digests",
"default_email_level",
"default_email_messages_level",
"default_email_mailing_list_mode",
"default_email_mailing_list_mode_frequency",
"default_email_previous_replies",
"default_email_in_reply_to",
"default_hide_profile",
"default_hide_presence",
"default_other_new_topic_duration_minutes",
"default_other_auto_track_topics_after_msecs",
"default_other_notification_level_when_replying",
"default_other_external_links_in_new_tab",
"default_other_enable_quoting",
"default_other_enable_smart_lists",
"default_other_enable_defer",
"default_other_dynamic_favicon",
"default_other_like_notification_frequency",
"default_other_skip_new_user_tips",
"default_topics_automatic_unpin",
"default_categories_watching",
"default_categories_tracking",
"default_categories_muted",
"default_categories_watching_first_post",
"default_categories_normal",
"default_tags_watching",
"default_tags_tracking",
"default_tags_muted",
"default_tags_watching_first_post",
"default_text_size",
"default_title_count_mode",
"default_navigation_menu_categories",
"default_navigation_menu_tags",
"default_sidebar_link_to_filtered_list",
"default_sidebar_show_count_of_new_items",
];
export default Mixin.create({
modal: service(),
router: service(),
site: service(),
dialog: service(),
siteSettingChangeTracker: service(),
attributeBindings: ["setting.setting:data-setting"],
classNameBindings: [":row", ":setting", "overridden", "typeClass"],
validationMessage: null,
content: alias("setting"),
isSecret: oneWay("setting.secret"),
@ -67,7 +112,7 @@ export default Mixin.create({
}),
dirty: computed("buffered.value", "setting.value", function () {
let bufferVal = this.buffered.get("value");
let bufferVal = this.get("buffered.value");
let settingVal = this.setting?.value;
if (isNone(bufferVal)) {
@ -78,20 +123,12 @@ export default Mixin.create({
settingVal = "";
}
const dirty = !deepEqual(bufferVal, settingVal);
if (dirty) {
this.siteSettingChangeTracker.add(this.setting);
} else {
this.siteSettingChangeTracker.remove(this.setting);
}
return dirty;
return !deepEqual(bufferVal, settingVal);
}),
preview: computed("setting", "buffered.value", function () {
const setting = this.setting;
const value = this.buffered.get("value");
const value = this.get("buffered.value");
const preview = setting.preview;
if (preview) {
const escapedValue = preview.replace(/\{\{value\}\}/g, value);
@ -128,7 +165,7 @@ export default Mixin.create({
}),
bufferedValues: computed("buffered.value", function () {
const value = this.buffered.get("value");
const value = this.get("buffered.value");
return splitString(value, "|");
}),
@ -179,41 +216,102 @@ export default Mixin.create({
}
}),
disableControls: computed("setting.isSaving", function () {
return !!this.setting.isSaving;
disableSaveButton: computed("validationMessage", function () {
return !!this.validationMessage;
}),
confirmChanges(settingKey) {
return new Promise((resolve) => {
// Fallback is needed in case the setting does not have a custom confirmation
// prompt/confirm defined.
this.dialog.alert({
message: i18n(
`admin.site_settings.requires_confirmation_messages.${settingKey}.prompt`,
{
translatedFallback: i18n(
"admin.site_settings.requires_confirmation_messages.default.prompt"
),
}
),
buttons: [
{
label: i18n(
`admin.site_settings.requires_confirmation_messages.${settingKey}.confirm`,
{
translatedFallback: i18n(
"admin.site_settings.requires_confirmation_messages.default.confirm"
),
}
),
class: "btn-primary",
action: () => resolve(true),
},
{
label: i18n("no_value"),
class: "btn-default",
action: () => resolve(false),
},
],
});
});
},
update: action(async function () {
if (this.setting.requiresConfirmation) {
const confirm = await this.siteSettingChangeTracker.confirmChanges(
this.setting
);
const key = this.buffered.get("setting");
if (!confirm) {
return;
}
let confirm = true;
if (
this.buffered.get("requires_confirmation") ===
SITE_SETTING_REQUIRES_CONFIRMATION_TYPES.simple
) {
confirm = await this.confirmChanges(key);
}
if (this.setting.affectsExistingUsers) {
await this.siteSettingChangeTracker.configureBackfill(this.setting);
if (!confirm) {
this.cancel();
return;
}
await this.save();
if (!DEFAULT_USER_PREFERENCES.includes(key)) {
await this.save();
return;
}
const data = {
[key]: this.buffered.get("value"),
};
const result = await ajax(`/admin/site_settings/${key}/user_count.json`, {
type: "PUT",
data,
});
const count = result.user_count;
if (count > 0) {
await this.modal.show(SiteSettingDefaultCategoriesModal, {
model: {
siteSetting: { count, key: key.replaceAll("_", " ") },
setUpdateExistingUsers: this.setUpdateExistingUsers,
},
});
this.save();
} else {
await this.save();
}
}),
setUpdateExistingUsers: action(function (value) {
this.updateExistingUsers = value;
}),
save: action(async function () {
try {
this.setting.isSaving = true;
await this._save();
this.setting.validationMessage = null;
this.set("validationMessage", null);
this.buffered.applyChanges();
if (this.setting.requiresReload) {
this.siteSettingChangeTracker.refreshPage({
[this.setting.setting]: this.setting.value,
});
if (AUTO_REFRESH_ON_SAVE.includes(this.setting.setting)) {
this.afterSave();
}
} catch (e) {
const json = e.jqXHR?.responseJSON;
@ -224,31 +322,29 @@ export default Mixin.create({
errorString = htmlSafe(errorString);
}
this.setting.validationMessage = errorString;
this.set("validationMessage", errorString);
} else {
this.setting.validationMessage = i18n("generic_error");
this.set("validationMessage", i18n("generic_error"));
}
} finally {
this.setting.isSaving = false;
}
}),
changeValueCallback: action(function (value) {
this.buffered.set("value", value);
this.set("buffered.value", value);
}),
setValidationMessage: action(function (message) {
this.setting.validationMessage = message;
this.set("validationMessage", message);
}),
cancel: action(function () {
this.buffered.discardChanges();
this.setting.validationMessage = null;
this.set("validationMessage", null);
}),
resetDefault: action(function () {
this.buffered.set("value", this.setting.default);
this.setting.validationMessage = null;
this.set("buffered.value", this.setting.default);
this.set("validationMessage", null);
}),
toggleSecret: action(function () {
@ -256,11 +352,11 @@ export default Mixin.create({
}),
setDefaultValues: action(function () {
this.buffered.set(
"value",
this.set(
"buffered.value",
this.bufferedValues.concat(this.defaultValues).uniq().join("|")
);
this.setting.validationMessage = null;
this.set("validationMessage", null);
return false;
}),

View File

@ -25,7 +25,7 @@ export default class EmailLog extends EmberObject {
const status = filter.status || "sent";
delete filter.status;
return ajax(`/admin/email-logs/${status}.json?offset=${offset}`, {
return ajax(`/admin/email/${status}.json?offset=${offset}`, {
data: filter,
}).then((logs) => logs.map((log) => EmailLog.create(log)));
}

View File

@ -3,7 +3,7 @@ import { ajax } from "discourse/lib/ajax";
export default class EmailSettings extends EmberObject {
static find() {
return ajax("/admin/email/server-settings.json").then(function (settings) {
return ajax("/admin/email.json").then(function (settings) {
return EmailSettings.create(settings);
});
}

View File

@ -14,11 +14,11 @@ export default class IncomingEmail extends EmberObject {
}
static find(id) {
return ajax(`/admin/email-logs/incoming/${id}.json`);
return ajax(`/admin/email/incoming/${id}.json`);
}
static findByBounced(id) {
return ajax(`/admin/email-logs/incoming_from_bounced/${id}.json`);
return ajax(`/admin/email/incoming_from_bounced/${id}.json`);
}
static findAll(filter, offset) {
@ -28,7 +28,7 @@ export default class IncomingEmail extends EmberObject {
const status = filter.status || "received";
delete filter.status;
return ajax(`/admin/email-logs/${status}.json?offset=${offset}`, {
return ajax(`/admin/email/${status}.json?offset=${offset}`, {
data: filter,
}).then((incomings) =>
incomings.map((incoming) => IncomingEmail.create(incoming))

View File

@ -1,24 +1,10 @@
import { tracked } from "@glimmer/tracking";
import EmberObject from "@ember/object";
import { alias } from "@ember/object/computed";
import BufferedProxy from "ember-buffered-proxy/proxy";
import { ajax } from "discourse/lib/ajax";
import discourseComputed, { bind } from "discourse/lib/decorators";
import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import {
DEFAULT_USER_PREFERENCES,
SITE_SETTING_REQUIRES_CONFIRMATION_TYPES,
} from "admin/lib/constants";
import SettingObjectHelper from "admin/lib/setting-object-helper";
const AUTO_REFRESH_ON_SAVE = [
"logo",
"mobile_logo",
"base_font",
"heading_font",
"default_text_size",
];
export default class SiteSetting extends EmberObject {
static findAll(params = {}) {
return ajax("/admin/site_settings", { data: params }).then(
@ -54,17 +40,6 @@ export default class SiteSetting extends EmberObject {
return ajax(`/admin/site_settings/${key}`, { type: "PUT", data });
}
static bulkUpdate(settings) {
return ajax(`/admin/site_settings/bulk_update.json`, {
type: "PUT",
data: { settings },
});
}
@tracked isSaving = false;
@tracked validationMessage = null;
updateExistingUsers = false;
settingObjectHelper = new SettingObjectHelper(this);
@alias("settingObjectHelper.overridden") overridden;
@ -74,11 +49,6 @@ export default class SiteSetting extends EmberObject {
@alias("settingObjectHelper.allowsNone") allowsNone;
@alias("settingObjectHelper.anyValue") anyValue;
constructor() {
super(...arguments);
this.buffered = BufferedProxy.create({ content: this });
}
@discourseComputed("setting")
staffLogFilter(setting) {
if (!setting) {
@ -90,24 +60,4 @@ export default class SiteSetting extends EmberObject {
action_name: "change_site_setting",
};
}
get requiresConfirmation() {
return (
this.requires_confirmation ===
SITE_SETTING_REQUIRES_CONFIRMATION_TYPES.simple
);
}
get requiresReload() {
return AUTO_REFRESH_ON_SAVE.includes(this.setting);
}
get affectsExistingUsers() {
return DEFAULT_USER_PREFERENCES.includes(this.setting);
}
@bind
setUpdateExistingUsers(value) {
this.updateExistingUsers = value;
}
}

View File

@ -99,7 +99,7 @@ export default class StaffActionLog extends RestModel {
@discourseComputed("details")
useModalForDetails(details) {
return details && (details.length > 100 || details.includes("\n"));
return details && details.length > 100;
}
@discourseComputed("action_name")

View File

@ -1,8 +1,19 @@
import EmberObject from "@ember/object";
import { alias } from "@ember/object/computed";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import SiteSetting from "admin/models/site-setting";
import SettingObjectHelper from "admin/lib/setting-object-helper";
export default class ThemeSettings extends EmberObject {
settingObjectHelper = new SettingObjectHelper(this);
@alias("settingObjectHelper.overridden") overridden;
@alias("settingObjectHelper.computedValueProperty") computedValueProperty;
@alias("settingObjectHelper.computedNameProperty") computedNameProperty;
@alias("settingObjectHelper.validValues") validValues;
@alias("settingObjectHelper.allowsNone") allowsNone;
@alias("settingObjectHelper.anyValue") anyValue;
export default class ThemeSettings extends SiteSetting {
updateSetting(themeId, newValue) {
if (this.objects_schema) {
newValue = JSON.stringify(newValue);

View File

@ -1,11 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminApiKeysIndexRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.api_keys.title");
}
import Route from "@ember/routing/route";
export default class AdminApiKeysIndexRoute extends Route {
model() {
return this.store.findAll("api-key");
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminApiKeysSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("settings");
}
}

View File

@ -0,0 +1,3 @@
import Route from "@ember/routing/route";
export default class AdminApiKeysRoute extends Route {}

View File

@ -12,10 +12,6 @@ export default class AdminBadgesRoute extends DiscourseRoute {
_json = null;
titleToken() {
return i18n("admin.config.badges.title");
}
async model() {
let json = await ajax("/admin/badges.json");
this._json = json;

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentCategoriesAndTagsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.categories_and_tags.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentPostsAndTopicsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.posts_and_topics.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentSharingRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.sharing.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigContentStatsAndThresholdsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.content.sub_pages.stats_and_thresholds.title");
}
}

View File

@ -2,6 +2,11 @@ import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigThemesAndComponentsComponentsRoute extends DiscourseRoute {
async model() {
const components = await this.store.findAll("theme");
return components.reject((t) => !t.component);
}
titleToken() {
return i18n("admin.config_areas.themes_and_components.components.title");
}

View File

@ -0,0 +1,8 @@
import { i18n } from "discourse-i18n";
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
export default class AdminConfigFontsRoute extends AdminConfigWithSettingsRoute {
titleToken() {
return i18n("admin.config.font_style.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigInterfaceSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.interface.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigLogoAndFontsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.logo_and_fonts.title");
}
}

View File

@ -1,8 +1,8 @@
import { i18n } from "discourse-i18n";
import AdminConfigWithSettingsRoute from "./admin-config-with-settings-route";
export default class AdminAnalyticsRoute extends AdminConfigWithSettingsRoute {
export default class AdminConfigLogoRoute extends AdminConfigWithSettingsRoute {
titleToken() {
return i18n("admin.config.analytics.title");
return i18n("admin.config.logo.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigSiteAdminSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.site_admin.title");
}
}

View File

@ -1,8 +0,0 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminConfigUserDefaultsSettingsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.user_defaults.title");
}
}

View File

@ -1,10 +1,6 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import DiscourseRoute from "discourse/routes/discourse";
export default class AdminConfigWithSettingsRoute extends DiscourseRoute {
@service siteSettingChangeTracker;
resetController(controller, isExiting) {
// Have to do this because this is the parent route. We don't want to have
// to make a controller for every single settings route when we can reset
@ -16,15 +12,4 @@ export default class AdminConfigWithSettingsRoute extends DiscourseRoute {
settingsController.set("filter", "");
}
}
@action
async willTransition(transition) {
if (this.siteSettingChangeTracker.hasUnsavedChanges) {
transition.abort();
await this.siteSettingChangeTracker.confirmTransition();
transition.retry();
}
}
}

View File

@ -1,12 +1,7 @@
import { scrollTop } from "discourse/mixins/scroll-top";
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminDashboardRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.dashboard.title");
}
activate() {
this.controllerFor("admin-dashboard").fetchProblems();
this.controllerFor("admin-dashboard").fetchDashboard();

View File

@ -1,20 +1,15 @@
import { action } from "@ember/object";
import { service } from "@ember/service";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { i18n } from "discourse-i18n";
import IncomingEmail from "admin/models/incoming-email";
import AdminEmailLogs from "admin/routes/admin-email-logs";
import IncomingEmailModal from "../components/modal/incoming-email";
export default class AdminEmailLogsBouncedRoute extends AdminEmailLogs {
export default class AdminEmailBouncedRoute extends AdminEmailLogs {
@service modal;
status = "bounced";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.bounced.title");
}
@action
async showIncomingEmail(id) {
const model = await this.loadFromBounced(id);

View File

@ -1,10 +0,0 @@
import { i18n } from "discourse-i18n";
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
export default class AdminEmailLogsReceivedRoute extends AdminEmailIncomings {
status = "received";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.received.title");
}
}

View File

@ -1,10 +0,0 @@
import { i18n } from "discourse-i18n";
import AdminEmailLogs from "admin/routes/admin-email-logs";
export default class AdminEmailLogsSentRoute extends AdminEmailLogs {
status = "sent";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.sent.title");
}
}

View File

@ -1,10 +0,0 @@
import { i18n } from "discourse-i18n";
import AdminEmailLogs from "admin/routes/admin-email-logs";
export default class AdminEmailLogsSkippedRoute extends AdminEmailLogs {
status = "skipped";
titleToken() {
return i18n("admin.config.email_logs.sub_pages.skipped.title");
}
}

View File

@ -1,11 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
export default class AdminEmailLogsRoute extends DiscourseRoute {
titleToken() {
return i18n("admin.config.email_logs.title");
}
setupController(controller) {
controller.setProperties({
loading: true,

View File

@ -0,0 +1,5 @@
import AdminEmailIncomings from "admin/routes/admin-email-incomings";
export default class AdminEmailReceivedRoute extends AdminEmailIncomings {
status = "received";
}

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