Compare commits
12 Commits
main
...
dev-api-ta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85302d9c17 | ||
|
|
c15e39bca3 | ||
|
|
b6acecd53c | ||
|
|
de7f7cbbc4 | ||
|
|
1e68529955 | ||
|
|
2a0bc9b1c7 | ||
|
|
46c95cf3fc | ||
|
|
ecede62825 | ||
|
|
7dae39d797 | ||
|
|
f8397b0d64 | ||
|
|
dcc522599a | ||
|
|
8177dfab2a |
@ -12,6 +12,3 @@ indent_size = 2
|
|||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
[*.hbs]
|
|
||||||
insert_final_newline = false
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
|
app/assets/javascripts/browser-update.js
|
||||||
app/assets/javascripts/locales/i18n.js
|
app/assets/javascripts/locales/i18n.js
|
||||||
app/assets/javascripts/ember-addons/
|
app/assets/javascripts/ember-addons/
|
||||||
|
app/assets/javascripts/discourse/lib/autosize.js
|
||||||
lib/javascripts/locale/
|
lib/javascripts/locale/
|
||||||
lib/javascripts/messageformat.js
|
lib/javascripts/messageformat.js
|
||||||
lib/javascripts/messageformat-lookup.js
|
lib/highlight_js/
|
||||||
lib/pretty_text/
|
lib/pretty_text/
|
||||||
plugins/**/lib/javascripts/locale
|
plugins/**/lib/javascripts/locale
|
||||||
public/
|
public/
|
||||||
@ -12,4 +14,3 @@ node_modules/
|
|||||||
spec/
|
spec/
|
||||||
dist/
|
dist/
|
||||||
tmp/
|
tmp/
|
||||||
documentation/
|
|
||||||
|
|||||||
@ -58,16 +58,3 @@ bbe5d8d5cf1220165842985c0e2cd4c454d501cd
|
|||||||
|
|
||||||
# DEV: Template colocation for sidebar files
|
# DEV: Template colocation for sidebar files
|
||||||
95c7cdab941a56686ac5831d2a5c5eca38d780c5
|
95c7cdab941a56686ac5831d2a5c5eca38d780c5
|
||||||
|
|
||||||
# DEV: Apply prettier to hbs files
|
|
||||||
c8e2e37fa77d3c3c69c7572866017e9bb92befa3
|
|
||||||
|
|
||||||
# DEV: Apply syntax_tree to...
|
|
||||||
5a003715d366e1d871f9fcb0656dc9e23e9c2259
|
|
||||||
64171730827c58df26a7ad75f0e58f17c2add118
|
|
||||||
b0fda61a8e75c81e3458c8af9d2afe9d32183457
|
|
||||||
cb932d6ee1b3b3571e4d4d9118635e2dbf58f0ef
|
|
||||||
0cf6421716d0908da57ad7743a2decb08588b48a
|
|
||||||
7c77cc6a580d7cb49f8c19ceee8cfdd08862259d
|
|
||||||
436b3b392b9c917510d4ff0d73a5167cd3eb936c
|
|
||||||
055310cea496519a996b9c3bf4dc7e716cfe62ba
|
|
||||||
|
|||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -10,7 +10,7 @@ updates:
|
|||||||
interval: daily
|
interval: daily
|
||||||
time: "08:00"
|
time: "08:00"
|
||||||
timezone: Australia/Sydney
|
timezone: Australia/Sydney
|
||||||
open-pull-requests-limit: 20
|
open-pull-requests-limit: 10
|
||||||
versioning-strategy: lockfile-only
|
versioning-strategy: lockfile-only
|
||||||
allow:
|
allow:
|
||||||
- dependency-type: direct
|
- dependency-type: direct
|
||||||
|
|||||||
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
@ -1,2 +0,0 @@
|
|||||||
chat:
|
|
||||||
- plugins/chat/**/*
|
|
||||||
14
.github/workflows/labeler.yml
vendored
14
.github/workflows/labeler.yml
vendored
@ -1,14 +0,0 @@
|
|||||||
name: "Pull Request Labeler"
|
|
||||||
on:
|
|
||||||
- pull_request_target
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
triage:
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/labeler@v4
|
|
||||||
with:
|
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
3
.github/workflows/licenses.yml
vendored
3
.github/workflows/licenses.yml
vendored
@ -15,7 +15,6 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
|
|
||||||
name: run
|
name: run
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: discourse/discourse_test:slim
|
container: discourse/discourse_test:slim
|
||||||
@ -53,7 +52,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get yarn cache directory
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
- name: Yarn cache
|
- name: Yarn cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
|
|||||||
16
.github/workflows/linting.yml
vendored
16
.github/workflows/linting.yml
vendored
@ -15,16 +15,12 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
|
|
||||||
name: run
|
name: run
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: discourse/discourse_test:slim
|
container: discourse/discourse_test:slim
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set working directory owner
|
|
||||||
run: chown root:root .
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
@ -53,7 +49,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get yarn cache directory
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
- name: Yarn cache
|
- name: Yarn cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
@ -71,12 +67,6 @@ jobs:
|
|||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
run: bundle exec rubocop --parallel .
|
run: bundle exec rubocop --parallel .
|
||||||
|
|
||||||
- name: syntax_tree
|
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
run: |
|
|
||||||
set -E
|
|
||||||
bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake')
|
|
||||||
|
|
||||||
- name: ESLint (core)
|
- name: ESLint (core)
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
run: yarn eslint app/assets/javascripts
|
run: yarn eslint app/assets/javascripts
|
||||||
@ -92,10 +82,8 @@ jobs:
|
|||||||
yarn pprettier --list-different \
|
yarn pprettier --list-different \
|
||||||
"app/assets/stylesheets/**/*.scss" \
|
"app/assets/stylesheets/**/*.scss" \
|
||||||
"app/assets/javascripts/**/*.js" \
|
"app/assets/javascripts/**/*.js" \
|
||||||
"app/assets/javascripts/**/*.hbs" \
|
|
||||||
"plugins/**/assets/stylesheets/**/*.scss" \
|
"plugins/**/assets/stylesheets/**/*.scss" \
|
||||||
"plugins/**/assets/javascripts/**/*.js" \
|
"plugins/**/assets/javascripts/**/*.js"
|
||||||
"plugins/**/assets/javascripts/**/*.hbs" \
|
|
||||||
|
|
||||||
- name: Ember template lint
|
- name: Ember template lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
|
|||||||
51
.github/workflows/tests.yml
vendored
51
.github/workflows/tests.yml
vendored
@ -17,10 +17,9 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
|
name: ${{ matrix.target }} ${{ matrix.build_type }}
|
||||||
name: ${{ matrix.target }} ${{ matrix.build_type }} ${{ matrix.ruby }}
|
|
||||||
runs-on: ${{ (matrix.build_type == 'annotations') && 'ubuntu-latest' || 'ubuntu-20.04-8core' }}
|
runs-on: ${{ (matrix.build_type == 'annotations') && 'ubuntu-latest' || 'ubuntu-20.04-8core' }}
|
||||||
container: discourse/discourse_test:slim${{ (matrix.build_type == 'frontend' || matrix.build_type == 'system') && '-browsers' || '' }}${{ (matrix.ruby == '3.1') && '-ruby-3.1.0' || '' }}
|
container: discourse/discourse_test:slim${{ (matrix.build_type == 'frontend' || matrix.build_type == 'system') && '-browsers' || '' }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@ -29,8 +28,7 @@ jobs:
|
|||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
PGUSER: discourse
|
PGUSER: discourse
|
||||||
PGPASSWORD: discourse
|
PGPASSWORD: discourse
|
||||||
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }}
|
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' }}
|
||||||
CAPBYARA_DEFAULT_MAX_WAIT_TIME: 4
|
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -38,17 +36,15 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
build_type: [backend, frontend, system, annotations]
|
build_type: [backend, frontend, system, annotations]
|
||||||
target: [core, plugins]
|
target: [core, plugins]
|
||||||
ruby: ['3.2']
|
|
||||||
exclude:
|
exclude:
|
||||||
- build_type: annotations
|
- build_type: annotations
|
||||||
target: plugins
|
target: plugins
|
||||||
- build_type: frontend
|
- build_type: frontend
|
||||||
target: core # Handled by core_frontend_tests job (below)
|
target: core # Handled by core_frontend_tests job (below)
|
||||||
|
- build_type: system
|
||||||
|
target: plugins # Enable once at least 1 plugin has system tests
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set working directory owner
|
|
||||||
run: chown root:root .
|
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
@ -72,9 +68,9 @@ jobs:
|
|||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: vendor/bundle
|
path: vendor/bundle
|
||||||
key: ${{ runner.os }}-${{ matrix.ruby }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-${{ matrix.ruby }}-gem-
|
${{ runner.os }}-gem-
|
||||||
|
|
||||||
- name: Setup gems
|
- name: Setup gems
|
||||||
run: |
|
run: |
|
||||||
@ -87,7 +83,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get yarn cache directory
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
- name: Yarn cache
|
- name: Yarn cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
@ -159,22 +155,6 @@ jobs:
|
|||||||
path: tmp/turbo_rspec_runtime.log
|
path: tmp/turbo_rspec_runtime.log
|
||||||
key: rspec-runtime-backend-core
|
key: rspec-runtime-backend-core
|
||||||
|
|
||||||
- name: Run Zeitwerk check
|
|
||||||
if: matrix.build_type == 'backend'
|
|
||||||
env:
|
|
||||||
LOAD_PLUGINS: ${{ (matrix.target == 'plugins') && '1' || '0' }}
|
|
||||||
run: |
|
|
||||||
if ! bin/rails zeitwerk:check --trace; then
|
|
||||||
echo
|
|
||||||
echo "---------------------------------------------"
|
|
||||||
echo
|
|
||||||
echo "::error::'bin/rails zeitwerk:check' failed - the app will fail to boot with 'eager_load=true' (e.g. in production)."
|
|
||||||
echo "To reproduce locally, run 'bin/rails zeitwerk:check'."
|
|
||||||
echo "Alternatively, you can run your local server/tests with the 'DISCOURSE_ZEITWERK_EAGER_LOAD=1' environment variable."
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Core RSpec
|
- name: Core RSpec
|
||||||
if: matrix.build_type == 'backend' && matrix.target == 'core'
|
if: matrix.build_type == 'backend' && matrix.target == 'core'
|
||||||
run: bin/turbo_rspec --verbose
|
run: bin/turbo_rspec --verbose
|
||||||
@ -192,24 +172,20 @@ jobs:
|
|||||||
if: matrix.build_type == 'system'
|
if: matrix.build_type == 'system'
|
||||||
run: bin/ember-cli --build
|
run: bin/ember-cli --build
|
||||||
|
|
||||||
- name: Setup Webdriver
|
|
||||||
if: matrix.build_type == 'system'
|
|
||||||
run: bin/rails runner "require 'webdrivers'; Webdrivers::Chromedriver.update"
|
|
||||||
|
|
||||||
- name: Core System Tests
|
- name: Core System Tests
|
||||||
if: matrix.build_type == 'system' && matrix.target == 'core'
|
if: matrix.build_type == 'system' && matrix.target == 'core'
|
||||||
run: bin/rspec spec/system
|
run: bin/system_rspec
|
||||||
|
|
||||||
- name: Plugin System Tests
|
- name: Plugin System Tests
|
||||||
if: matrix.build_type == 'system' && matrix.target == 'plugins'
|
if: matrix.build_type == 'system' && matrix.target == 'plugins'
|
||||||
run: LOAD_PLUGINS=1 bin/rspec plugins/*/spec/system
|
run: bin/system_rspec plugins/*/spec/system
|
||||||
|
|
||||||
- name: Upload failed system test screenshots
|
- name: Upload failed system test screenshots
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
if: matrix.build_type == 'system' && failure()
|
if: matrix.build_type == 'system' && failure()
|
||||||
with:
|
with:
|
||||||
name: failed-system-test-screenshots
|
name: failed-system-test-screenshots
|
||||||
path: tmp/capybara/*.png
|
path: tmp/screenshots/*.png
|
||||||
|
|
||||||
- name: Check Annotations
|
- name: Check Annotations
|
||||||
if: matrix.build_type == 'annotations'
|
if: matrix.build_type == 'annotations'
|
||||||
@ -229,7 +205,6 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
core_frontend_tests:
|
core_frontend_tests:
|
||||||
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
|
|
||||||
name: core frontend (${{ matrix.browser }})
|
name: core frontend (${{ matrix.browser }})
|
||||||
runs-on: ubuntu-20.04-8core
|
runs-on: ubuntu-20.04-8core
|
||||||
container:
|
container:
|
||||||
@ -248,7 +223,7 @@ jobs:
|
|||||||
TESTEM_FIREFOX_PATH: ${{ (matrix.browser == 'Firefox Evergreen') && '/opt/firefox-evergreen/firefox' }}
|
TESTEM_FIREFOX_PATH: ${{ (matrix.browser == 'Firefox Evergreen') && '/opt/firefox-evergreen/firefox' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
@ -259,7 +234,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get yarn cache directory
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
- name: Yarn cache
|
- name: Yarn cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -39,7 +39,6 @@
|
|||||||
!/plugins/discourse-narrative-bot
|
!/plugins/discourse-narrative-bot
|
||||||
!/plugins/discourse-presence
|
!/plugins/discourse-presence
|
||||||
!/plugins/lazy-yt/
|
!/plugins/lazy-yt/
|
||||||
!/plugins/chat/
|
|
||||||
!/plugins/poll/
|
!/plugins/poll/
|
||||||
!/plugins/styleguide
|
!/plugins/styleguide
|
||||||
/plugins/*/auto_generated/
|
/plugins/*/auto_generated/
|
||||||
@ -50,9 +49,6 @@
|
|||||||
/vendor/data/GeoLite2-City.mmdb
|
/vendor/data/GeoLite2-City.mmdb
|
||||||
/vendor/data/GeoLite2-ASN.mmdb
|
/vendor/data/GeoLite2-ASN.mmdb
|
||||||
|
|
||||||
# We provide a .sample but people can use newer versions if they want to
|
|
||||||
.ruby-version
|
|
||||||
|
|
||||||
# Front-end
|
# Front-end
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
21
.jsdoc
21
.jsdoc
@ -1,21 +0,0 @@
|
|||||||
// jsdoc doesn't accept paths starting with _ (which is the case on github runners)
|
|
||||||
// so we need to alter the default config
|
|
||||||
{
|
|
||||||
"source": {
|
|
||||||
"excludePattern": ""
|
|
||||||
},
|
|
||||||
"templates": {
|
|
||||||
"default": {
|
|
||||||
"includeDate": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"opts": {
|
|
||||||
"template": "./node_modules/tidy-jsdoc",
|
|
||||||
"prism-theme": "prism-custom",
|
|
||||||
"encoding": "utf8",
|
|
||||||
"recurse": true
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"title": "Discourse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
sources:
|
sources:
|
||||||
bundler: true
|
bundler: true
|
||||||
|
|
||||||
allowed:
|
allowed:
|
||||||
- 0bsd
|
- 0bsd
|
||||||
- apache-2.0
|
- apache-2.0
|
||||||
@ -13,20 +12,8 @@ allowed:
|
|||||||
|
|
||||||
ignored:
|
ignored:
|
||||||
bundler:
|
bundler:
|
||||||
- cgi # Ruby (default gem)
|
- rchardet # Ruby terms
|
||||||
- date # Ruby (default gem)
|
- strscan # Ruby
|
||||||
- digest # Ruby (default gem)
|
|
||||||
- io-wait # Ruby (default gem)
|
|
||||||
- json # Ruby (default gem)
|
|
||||||
- net-http # Ruby (default gem)
|
|
||||||
- net-protocol # Ruby (default gem)
|
|
||||||
- openssl # Ruby (default gem)
|
|
||||||
- racc # Ruby (default gem)
|
|
||||||
- rchardet # LGPL
|
|
||||||
- ruby2_keywords # Ruby (default gem)
|
|
||||||
- strscan # Ruby (default gem)
|
|
||||||
- timeout # Ruby (default gem)
|
|
||||||
- uri # Ruby (default gem)
|
|
||||||
|
|
||||||
reviewed:
|
reviewed:
|
||||||
bundler:
|
bundler:
|
||||||
@ -39,24 +26,32 @@ reviewed:
|
|||||||
- faraday-em_synchrony # MIT
|
- faraday-em_synchrony # MIT
|
||||||
- faraday-excon # MIT
|
- faraday-excon # MIT
|
||||||
- faraday-httpclient # MIT
|
- faraday-httpclient # MIT
|
||||||
- faraday-net_http # MIT
|
|
||||||
- faraday-patron # MIT
|
- faraday-patron # MIT
|
||||||
|
- faraday-net_http # MIT
|
||||||
- faraday-rack # MIT
|
- faraday-rack # MIT
|
||||||
- highline # Ruby or GPL-2.0
|
- highline # GPL-2.0 OR Ruby terms
|
||||||
- htmlentities # MIT
|
- htmlentities # MIT
|
||||||
- image_size # MIT
|
- image_size # MIT
|
||||||
|
- io-wait # Ruby terms
|
||||||
|
- json # Ruby terms
|
||||||
- jwt # MIT
|
- jwt # MIT
|
||||||
- kgio # LGPL-2.1+
|
- kgio # LGPL-2.1+
|
||||||
- logstash-event # Apache-2.0
|
- logstash-event # Apache-2.0
|
||||||
- net-imap # Ruby (bundled gem)
|
- net-http # Ruby
|
||||||
- net-pop # Ruby (bundled gem)
|
- net-imap # Ruby
|
||||||
- net-smtp # Ruby (bundled gem)
|
- net-pop # Ruby
|
||||||
|
- net-protocol # Ruby
|
||||||
|
- net-smtp # Ruby
|
||||||
- omniauth # MIT
|
- omniauth # MIT
|
||||||
- pg # Ruby
|
- openssl # Ruby terms
|
||||||
|
- pg # Ruby terms
|
||||||
- r2 # Apache-2.0 (Twitter)
|
- r2 # Apache-2.0 (Twitter)
|
||||||
|
- racc # Ruby terms
|
||||||
- raindrops # LGPL-2.1+
|
- raindrops # LGPL-2.1+
|
||||||
- rubyzip # Ruby
|
- rubyzip # Ruby terms
|
||||||
- sidekiq # LGPL (Sidekiq)
|
- sidekiq # LGPL (Sidekiq)
|
||||||
- tilt # MIT
|
- tilt
|
||||||
|
- timeout # Ruby
|
||||||
- unf # BSD-2-Clause
|
- unf # BSD-2-Clause
|
||||||
- unicorn # Ruby or GPLv2/GPLv3
|
- unicorn
|
||||||
|
- uri # Ruby
|
||||||
|
|||||||
@ -11,8 +11,7 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"@fortawesome/fontawesome-free": "*",
|
"@fortawesome/fontawesome-free": "*",
|
||||||
"ember-template-lint-plugin-discourse": "*",
|
"ember-template-lint-plugin-discourse": "*",
|
||||||
"squoosh": "2.0.0",
|
"squoosh": "2.0.0"
|
||||||
"taffydb": "2.6.2"
|
|
||||||
},
|
},
|
||||||
"corrections": true
|
"corrections": true
|
||||||
}
|
}
|
||||||
@ -3,7 +3,6 @@ plugins/**/assets/stylesheets/vendor/
|
|||||||
plugins/**/assets/javascripts/vendor/
|
plugins/**/assets/javascripts/vendor/
|
||||||
plugins/**/config/locales/**/*.yml
|
plugins/**/config/locales/**/*.yml
|
||||||
plugins/**/config/*.yml
|
plugins/**/config/*.yml
|
||||||
documentation/
|
|
||||||
package.json
|
package.json
|
||||||
config/locales/**/*.yml
|
config/locales/**/*.yml
|
||||||
!config/locales/**/*.en*.yml
|
!config/locales/**/*.en*.yml
|
||||||
@ -27,6 +26,7 @@ dist/
|
|||||||
tmp/
|
tmp/
|
||||||
|
|
||||||
**/*.rb
|
**/*.rb
|
||||||
|
**/*.hbs
|
||||||
**/*.html
|
**/*.html
|
||||||
**/*.json
|
**/*.json
|
||||||
**/*.md
|
**/*.md
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
inherit_gem:
|
inherit_gem:
|
||||||
rubocop-discourse: stree-compat.yml
|
rubocop-discourse: default.yml
|
||||||
|
|
||||||
# Still work to do in ensuring we don't link old files
|
# Still work to do in ensuring we don't link old files
|
||||||
Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
|
Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
|
||||||
@ -7,7 +7,3 @@ Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
|
|||||||
|
|
||||||
Discourse/NoResetColumnInformationInMigrations:
|
Discourse/NoResetColumnInformationInMigrations:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Lint/Debugger:
|
|
||||||
Exclude:
|
|
||||||
- script/**/*
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
3.2.1
|
2.7.6
|
||||||
|
|||||||
2
.streerc
2
.streerc
@ -1,2 +0,0 @@
|
|||||||
--print-width=100
|
|
||||||
--plugins=plugin/trailing_comma,disable_ternary
|
|
||||||
@ -15,17 +15,11 @@ module.exports = {
|
|||||||
"directory-item-value",
|
"directory-item-value",
|
||||||
"directory-table-header-title",
|
"directory-table-header-title",
|
||||||
"loading-spinner",
|
"loading-spinner",
|
||||||
"directory-item-label",
|
"mobile-directory-item-label",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"no-implicit-this": {
|
"no-implicit-this": {
|
||||||
allow: ["loading-spinner"],
|
allow: ["loading-spinner"],
|
||||||
},
|
},
|
||||||
// Begin prettier compatibility
|
|
||||||
"eol-last": false,
|
|
||||||
"self-closing-void-elements": false,
|
|
||||||
"block-indentation": false,
|
|
||||||
quotes: false,
|
|
||||||
// End prettier compatibility
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
306
Gemfile
306
Gemfile
@ -1,51 +1,51 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source "https://rubygems.org"
|
source 'https://rubygems.org'
|
||||||
# if there is a super emergency and rubygems is playing up, try
|
# if there is a super emergency and rubygems is playing up, try
|
||||||
#source 'http://production.cf.rubygems.org'
|
#source 'http://production.cf.rubygems.org'
|
||||||
|
|
||||||
gem "bootsnap", require: false, platform: :mri
|
gem 'bootsnap', require: false, platform: :mri
|
||||||
|
|
||||||
def rails_master?
|
def rails_master?
|
||||||
ENV["RAILS_MASTER"] == "1"
|
ENV["RAILS_MASTER"] == '1'
|
||||||
end
|
end
|
||||||
|
|
||||||
if rails_master?
|
if rails_master?
|
||||||
gem "arel", git: "https://github.com/rails/arel.git"
|
gem 'arel', git: 'https://github.com/rails/arel.git'
|
||||||
gem "rails", git: "https://github.com/rails/rails.git"
|
gem 'rails', git: 'https://github.com/rails/rails.git'
|
||||||
else
|
else
|
||||||
# NOTE: Until rubygems gives us optional dependencies we are stuck with this needing to be explicit
|
# NOTE: Until rubygems gives us optional dependencies we are stuck with this needing to be explicit
|
||||||
# this allows us to include the bits of rails we use without pieces we do not.
|
# this allows us to include the bits of rails we use without pieces we do not.
|
||||||
#
|
#
|
||||||
# To issue a rails update bump the version number here
|
# To issue a rails update bump the version number here
|
||||||
rails_version = "7.0.4.3"
|
rails_version = '7.0.3.1'
|
||||||
gem "actionmailer", rails_version
|
gem 'actionmailer', rails_version
|
||||||
gem "actionpack", rails_version
|
gem 'actionpack', rails_version
|
||||||
gem "actionview", rails_version
|
gem 'actionview', rails_version
|
||||||
gem "activemodel", rails_version
|
gem 'activemodel', rails_version
|
||||||
gem "activerecord", rails_version
|
gem 'activerecord', rails_version
|
||||||
gem "activesupport", rails_version
|
gem 'activesupport', rails_version
|
||||||
gem "railties", rails_version
|
gem 'railties', rails_version
|
||||||
gem "sprockets-rails"
|
gem 'sprockets-rails'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem "json"
|
gem 'json'
|
||||||
|
|
||||||
# TODO: At the moment Discourse does not work with Sprockets 4, we would need to correct internals
|
# TODO: At the moment Discourse does not work with Sprockets 4, we would need to correct internals
|
||||||
# We intend to drop sprockets rather than upgrade to 4.x
|
# This is a desired upgrade we should get to.
|
||||||
gem "sprockets", git: "https://github.com/rails/sprockets", branch: "3.x"
|
gem 'sprockets', '3.7.2'
|
||||||
|
|
||||||
# this will eventually be added to rails,
|
# this will eventually be added to rails,
|
||||||
# allows us to precompile all our templates in the unicorn master
|
# allows us to precompile all our templates in the unicorn master
|
||||||
gem "actionview_precompiler", require: false
|
gem 'actionview_precompiler', require: false
|
||||||
|
|
||||||
gem "discourse-seed-fu"
|
gem 'discourse-seed-fu'
|
||||||
|
|
||||||
gem "mail", git: "https://github.com/discourse/mail.git"
|
gem 'mail', git: 'https://github.com/discourse/mail.git'
|
||||||
gem "mini_mime"
|
gem 'mini_mime'
|
||||||
gem "mini_suffix"
|
gem 'mini_suffix'
|
||||||
|
|
||||||
gem "redis"
|
gem 'redis'
|
||||||
|
|
||||||
# This is explicitly used by Sidekiq and is an optional dependency.
|
# This is explicitly used by Sidekiq and is an optional dependency.
|
||||||
# We tell Sidekiq to use the namespace "sidekiq" which triggers this
|
# We tell Sidekiq to use the namespace "sidekiq" which triggers this
|
||||||
@ -53,79 +53,86 @@ gem "redis"
|
|||||||
# redis namespace support is optional
|
# redis namespace support is optional
|
||||||
# We already namespace stuff in DiscourseRedis, so we should consider
|
# We already namespace stuff in DiscourseRedis, so we should consider
|
||||||
# just using a single implementation in core vs having 2 namespace implementations
|
# just using a single implementation in core vs having 2 namespace implementations
|
||||||
gem "redis-namespace"
|
gem 'redis-namespace'
|
||||||
|
|
||||||
# NOTE: AM serializer gets a lot slower with recent updates
|
# NOTE: AM serializer gets a lot slower with recent updates
|
||||||
# we used an old branch which is the fastest one out there
|
# we used an old branch which is the fastest one out there
|
||||||
# are long term goal here is to fork this gem so we have a
|
# are long term goal here is to fork this gem so we have a
|
||||||
# better maintained living fork
|
# better maintained living fork
|
||||||
gem "active_model_serializers", "~> 0.8.3"
|
gem 'active_model_serializers', '~> 0.8.3'
|
||||||
|
|
||||||
gem "http_accept_language", require: false
|
gem 'http_accept_language', require: false
|
||||||
|
|
||||||
gem "discourse-fonts", require: "discourse_fonts"
|
# Ember related gems need to be pinned cause they control client side
|
||||||
|
# behavior, we will push these versions up when upgrading ember
|
||||||
|
gem 'discourse-ember-rails', '0.18.6', require: 'ember-rails'
|
||||||
|
gem 'discourse-ember-source', '~> 3.12.2'
|
||||||
|
gem 'ember-handlebars-template', '0.8.0'
|
||||||
|
gem 'discourse-fonts', require: 'discourse_fonts'
|
||||||
|
|
||||||
gem "message_bus"
|
gem 'barber'
|
||||||
|
|
||||||
gem "rails_multisite"
|
gem 'message_bus'
|
||||||
|
|
||||||
gem "fast_xs", platform: :ruby
|
gem 'rails_multisite'
|
||||||
|
|
||||||
gem "xorcist"
|
gem 'fast_xs', platform: :ruby
|
||||||
|
|
||||||
gem "fastimage"
|
gem 'xorcist'
|
||||||
|
|
||||||
gem "aws-sdk-s3", require: false
|
gem 'fastimage'
|
||||||
gem "aws-sdk-sns", require: false
|
|
||||||
gem "excon", require: false
|
|
||||||
gem "unf", require: false
|
|
||||||
|
|
||||||
gem "email_reply_trimmer"
|
gem 'aws-sdk-s3', require: false
|
||||||
|
gem 'aws-sdk-sns', require: false
|
||||||
|
gem 'excon', require: false
|
||||||
|
gem 'unf', require: false
|
||||||
|
|
||||||
gem "image_optim"
|
gem 'email_reply_trimmer'
|
||||||
gem "multi_json"
|
|
||||||
gem "mustache"
|
|
||||||
gem "nokogiri"
|
|
||||||
gem "loofah"
|
|
||||||
gem "css_parser", require: false
|
|
||||||
|
|
||||||
gem "omniauth"
|
gem 'image_optim'
|
||||||
gem "omniauth-facebook"
|
gem 'multi_json'
|
||||||
gem "omniauth-twitter"
|
gem 'mustache'
|
||||||
gem "omniauth-github"
|
gem 'nokogiri'
|
||||||
|
gem 'loofah'
|
||||||
|
gem 'css_parser', require: false
|
||||||
|
|
||||||
gem "omniauth-oauth2", require: false
|
gem 'omniauth'
|
||||||
|
gem 'omniauth-facebook'
|
||||||
|
gem 'omniauth-twitter'
|
||||||
|
gem 'omniauth-github'
|
||||||
|
|
||||||
gem "omniauth-google-oauth2"
|
gem 'omniauth-oauth2', require: false
|
||||||
|
|
||||||
|
gem 'omniauth-google-oauth2'
|
||||||
|
|
||||||
# pending: https://github.com/ohler55/oj/issues/789
|
# pending: https://github.com/ohler55/oj/issues/789
|
||||||
gem "oj", "3.13.14"
|
gem 'oj', '3.13.14'
|
||||||
|
|
||||||
gem "pg"
|
gem 'pg'
|
||||||
gem "mini_sql"
|
gem 'mini_sql'
|
||||||
gem "pry-rails", require: false
|
gem 'pry-rails', require: false
|
||||||
gem "pry-byebug", require: false
|
gem 'pry-byebug', require: false
|
||||||
gem "rtlcss", require: false
|
gem 'r2', require: false
|
||||||
gem "rake"
|
gem 'rake'
|
||||||
|
|
||||||
gem "thor", require: false
|
gem 'thor', require: false
|
||||||
gem "diffy", require: false
|
gem 'diffy', require: false
|
||||||
gem "rinku"
|
gem 'rinku'
|
||||||
gem "sidekiq"
|
gem 'sidekiq'
|
||||||
gem "mini_scheduler"
|
gem 'mini_scheduler'
|
||||||
|
|
||||||
gem "execjs", require: false
|
gem 'execjs', require: false
|
||||||
gem "mini_racer"
|
gem 'mini_racer'
|
||||||
|
|
||||||
gem "highline", require: false
|
gem 'highline', require: false
|
||||||
|
|
||||||
gem "rack"
|
gem 'rack'
|
||||||
|
|
||||||
gem "rack-protection" # security
|
gem 'rack-protection' # security
|
||||||
gem "cbor", require: false
|
gem 'cbor', require: false
|
||||||
gem "cose", require: false
|
gem 'cose', require: false
|
||||||
gem "addressable"
|
gem 'addressable'
|
||||||
gem "json_schemer"
|
gem 'json_schemer'
|
||||||
|
|
||||||
gem "net-smtp", require: false
|
gem "net-smtp", require: false
|
||||||
gem "net-imap", require: false
|
gem "net-imap", require: false
|
||||||
@ -135,149 +142,136 @@ gem "digest", require: false
|
|||||||
# Gems used only for assets and not required in production environments by default.
|
# Gems used only for assets and not required in production environments by default.
|
||||||
# Allow everywhere for now cause we are allowing asset debugging in production
|
# Allow everywhere for now cause we are allowing asset debugging in production
|
||||||
group :assets do
|
group :assets do
|
||||||
gem "uglifier"
|
gem 'uglifier'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem "capybara", require: false
|
gem 'capybara', require: false
|
||||||
gem "webmock", require: false
|
gem 'webmock', require: false
|
||||||
gem "fakeweb", require: false
|
gem 'fakeweb', require: false
|
||||||
gem "minitest", require: false
|
gem 'minitest', require: false
|
||||||
gem "simplecov", require: false
|
gem 'simplecov', require: false
|
||||||
gem "selenium-webdriver", require: false
|
gem 'selenium-webdriver', require: false
|
||||||
gem "test-prof"
|
gem "test-prof"
|
||||||
gem "webdrivers", require: false
|
gem 'webdrivers', require: false
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test, :development do
|
group :test, :development do
|
||||||
gem "rspec"
|
gem 'rspec'
|
||||||
gem "listen", require: false
|
gem 'listen', require: false
|
||||||
gem "certified", require: false
|
gem 'certified', require: false
|
||||||
gem "fabrication", require: false
|
gem 'fabrication', require: false
|
||||||
gem "mocha", require: false
|
gem 'mocha', require: false
|
||||||
|
|
||||||
gem "rb-fsevent", require: RUBY_PLATFORM =~ /darwin/i ? "rb-fsevent" : false
|
gem 'rb-fsevent', require: RUBY_PLATFORM =~ /darwin/i ? 'rb-fsevent' : false
|
||||||
|
|
||||||
gem "rspec-rails"
|
gem 'rspec-rails'
|
||||||
|
|
||||||
gem "shoulda-matchers", require: false
|
gem 'shoulda-matchers', require: false
|
||||||
gem "rspec-html-matchers"
|
gem 'rspec-html-matchers'
|
||||||
gem "byebug", require: ENV["RM_INFO"].nil?, platform: :mri
|
gem 'byebug', require: ENV['RM_INFO'].nil?, platform: :mri
|
||||||
gem "rubocop-discourse", require: false
|
gem 'rubocop-discourse', require: false
|
||||||
gem "parallel_tests"
|
gem 'parallel_tests'
|
||||||
|
|
||||||
gem "rswag-specs"
|
gem 'rswag-specs'
|
||||||
|
|
||||||
gem "annotate"
|
gem 'annotate'
|
||||||
|
|
||||||
gem "syntax_tree"
|
|
||||||
gem "syntax_tree-disable_ternary"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem "ruby-prof", require: false, platform: :mri
|
gem 'ruby-prof', require: false, platform: :mri
|
||||||
gem "bullet", require: !!ENV["BULLET"]
|
gem 'bullet', require: !!ENV['BULLET']
|
||||||
gem "better_errors", platform: :mri, require: !!ENV["BETTER_ERRORS"]
|
gem 'better_errors', platform: :mri, require: !!ENV['BETTER_ERRORS']
|
||||||
gem "binding_of_caller"
|
gem 'binding_of_caller'
|
||||||
gem "yaml-lint"
|
gem 'yaml-lint'
|
||||||
gem "yard"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if ENV["ALLOW_DEV_POPULATE"] == "1"
|
if ENV["ALLOW_DEV_POPULATE"] == "1"
|
||||||
gem "discourse_dev_assets"
|
gem 'discourse_dev_assets'
|
||||||
gem "faker", "~> 2.16"
|
gem 'faker', "~> 2.16"
|
||||||
else
|
else
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem "discourse_dev_assets"
|
gem 'discourse_dev_assets'
|
||||||
gem "faker", "~> 2.16"
|
gem 'faker', "~> 2.16"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# this is an optional gem, it provides a high performance replacement
|
# this is an optional gem, it provides a high performance replacement
|
||||||
# to String#blank? a method that is called quite frequently in current
|
# to String#blank? a method that is called quite frequently in current
|
||||||
# ActiveRecord, this may change in the future
|
# ActiveRecord, this may change in the future
|
||||||
gem "fast_blank", platform: :ruby
|
gem 'fast_blank', platform: :ruby
|
||||||
|
|
||||||
# this provides a very efficient lru cache
|
# this provides a very efficient lru cache
|
||||||
gem "lru_redux"
|
gem 'lru_redux'
|
||||||
|
|
||||||
gem "htmlentities", require: false
|
gem 'htmlentities', require: false
|
||||||
|
|
||||||
# IMPORTANT: mini profiler monkey patches, so it better be required last
|
# IMPORTANT: mini profiler monkey patches, so it better be required last
|
||||||
# If you want to amend mini profiler to do the monkey patches in the railties
|
# If you want to amend mini profiler to do the monkey patches in the railties
|
||||||
# we are open to it. by deferring require to the initializer we can configure discourse installs without it
|
# we are open to it. by deferring require to the initializer we can configure discourse installs without it
|
||||||
|
|
||||||
gem "rack-mini-profiler", require: ["enable_rails_patches"]
|
gem 'rack-mini-profiler', require: ['enable_rails_patches']
|
||||||
|
|
||||||
gem "unicorn", require: false, platform: :ruby
|
gem 'unicorn', require: false, platform: :ruby
|
||||||
gem "puma", require: false
|
gem 'puma', require: false
|
||||||
gem "rbtrace", require: false, platform: :mri
|
gem 'rbtrace', require: false, platform: :mri
|
||||||
gem "gc_tracer", require: false, platform: :mri
|
gem 'gc_tracer', require: false, platform: :mri
|
||||||
|
|
||||||
# required for feed importing and embedding
|
# required for feed importing and embedding
|
||||||
gem "ruby-readability", require: false
|
gem 'ruby-readability', require: false
|
||||||
|
|
||||||
# rss gem is a bundled gem from Ruby 3 onwards
|
# rss gem is a bundled gem from Ruby 3 onwards
|
||||||
gem "rss", require: false
|
gem 'rss', require: false
|
||||||
|
|
||||||
gem "stackprof", require: false, platform: :mri
|
gem 'stackprof', require: false, platform: :mri
|
||||||
gem "memory_profiler", require: false, platform: :mri
|
gem 'memory_profiler', require: false, platform: :mri
|
||||||
|
|
||||||
gem "cppjieba_rb", require: false
|
gem 'cppjieba_rb', require: false
|
||||||
|
|
||||||
gem "lograge", require: false
|
gem 'lograge', require: false
|
||||||
gem "logstash-event", require: false
|
gem 'logstash-event', require: false
|
||||||
gem "logstash-logger", require: false
|
gem 'logstash-logger', require: false
|
||||||
gem "logster"
|
gem 'logster'
|
||||||
|
|
||||||
# These are forks of sassc and sassc-rails with dart-sass support
|
# NOTE: later versions of sassc are causing a segfault, possibly dependent on processer architecture
|
||||||
gem "dartsass-ruby"
|
# and until resolved should be locked at 2.0.1
|
||||||
gem "dartsass-sprockets"
|
gem 'sassc', '2.0.1', require: false
|
||||||
|
gem "sassc-rails"
|
||||||
|
|
||||||
gem "rotp", require: false
|
gem 'rotp', require: false
|
||||||
|
|
||||||
gem "rqrcode"
|
gem 'rqrcode'
|
||||||
|
|
||||||
gem "rubyzip", require: false
|
gem 'rubyzip', require: false
|
||||||
|
|
||||||
gem "sshkey", require: false
|
gem 'sshkey', require: false
|
||||||
|
|
||||||
gem "rchardet", require: false
|
gem 'rchardet', require: false
|
||||||
gem "lz4-ruby", require: false, platform: :ruby
|
gem 'lz4-ruby', require: false, platform: :ruby
|
||||||
|
|
||||||
gem "sanitize"
|
gem 'sanitize'
|
||||||
|
|
||||||
if ENV["IMPORT"] == "1"
|
if ENV["IMPORT"] == "1"
|
||||||
gem "mysql2"
|
gem 'mysql2'
|
||||||
gem "redcarpet"
|
gem 'redcarpet'
|
||||||
|
|
||||||
# NOTE: in import mode the version of sqlite can matter a lot, so we stick it to a specific one
|
# NOTE: in import mode the version of sqlite can matter a lot, so we stick it to a specific one
|
||||||
gem "sqlite3", "~> 1.3", ">= 1.3.13"
|
gem 'sqlite3', '~> 1.3', '>= 1.3.13'
|
||||||
gem "ruby-bbcode-to-md", git: "https://github.com/nlalonde/ruby-bbcode-to-md"
|
gem 'ruby-bbcode-to-md', git: 'https://github.com/nlalonde/ruby-bbcode-to-md'
|
||||||
gem "reverse_markdown"
|
gem 'reverse_markdown'
|
||||||
gem "tiny_tds"
|
gem 'tiny_tds'
|
||||||
gem "csv"
|
gem 'csv'
|
||||||
|
|
||||||
gem "parallel", require: false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
gem "web-push"
|
gem 'webpush', require: false
|
||||||
gem "colored2", require: false
|
gem 'colored2', require: false
|
||||||
gem "maxminddb"
|
gem 'maxminddb'
|
||||||
|
|
||||||
gem "rails_failover", require: false
|
gem 'rails_failover', require: false
|
||||||
|
|
||||||
gem "faraday"
|
gem 'faraday'
|
||||||
gem "faraday-retry"
|
gem 'faraday-retry'
|
||||||
|
|
||||||
# workaround for faraday-net_http, see
|
# workaround for faraday-net_http, see
|
||||||
# https://github.com/ruby/net-imap/issues/16#issuecomment-803086765
|
# https://github.com/ruby/net-imap/issues/16#issuecomment-803086765
|
||||||
gem "net-http"
|
gem 'net-http'
|
||||||
|
|
||||||
# workaround for prometheus-client
|
|
||||||
gem "webrick", require: false
|
|
||||||
|
|
||||||
# Workaround until Ruby ships with cgi version 0.3.6 or higher.
|
|
||||||
gem "cgi", ">= 0.3.6", require: false
|
|
||||||
|
|
||||||
gem "tzinfo-data"
|
|
||||||
|
|||||||
346
Gemfile.lock
346
Gemfile.lock
@ -5,37 +5,28 @@ GIT
|
|||||||
mail (2.8.0.edge)
|
mail (2.8.0.edge)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
|
|
||||||
GIT
|
|
||||||
remote: https://github.com/rails/sprockets
|
|
||||||
revision: f4d3dae71ef29c44b75a49cfbf8032cce07b423a
|
|
||||||
branch: 3.x
|
|
||||||
specs:
|
|
||||||
sprockets (3.7.2)
|
|
||||||
concurrent-ruby (~> 1.0)
|
|
||||||
rack (> 1, < 3)
|
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actionmailer (7.0.4.3)
|
actionmailer (7.0.3.1)
|
||||||
actionpack (= 7.0.4.3)
|
actionpack (= 7.0.3.1)
|
||||||
actionview (= 7.0.4.3)
|
actionview (= 7.0.3.1)
|
||||||
activejob (= 7.0.4.3)
|
activejob (= 7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
net-imap
|
net-imap
|
||||||
net-pop
|
net-pop
|
||||||
net-smtp
|
net-smtp
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (7.0.4.3)
|
actionpack (7.0.3.1)
|
||||||
actionview (= 7.0.4.3)
|
actionview (= 7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
rack (~> 2.0, >= 2.2.0)
|
rack (~> 2.0, >= 2.2.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||||
actionview (7.0.4.3)
|
actionview (7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
@ -44,15 +35,15 @@ GEM
|
|||||||
actionview (>= 6.0.a)
|
actionview (>= 6.0.a)
|
||||||
active_model_serializers (0.8.4)
|
active_model_serializers (0.8.4)
|
||||||
activemodel (>= 3.0)
|
activemodel (>= 3.0)
|
||||||
activejob (7.0.4.3)
|
activejob (7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (7.0.4.3)
|
activemodel (7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
activerecord (7.0.4.3)
|
activerecord (7.0.3.1)
|
||||||
activemodel (= 7.0.4.3)
|
activemodel (= 7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
activesupport (7.0.4.3)
|
activesupport (7.0.3.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
@ -82,20 +73,23 @@ GEM
|
|||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sigv4 (1.5.0)
|
aws-sigv4 (1.5.0)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
barber (0.12.2)
|
||||||
|
ember-source (>= 1.0, < 3.1)
|
||||||
|
execjs (>= 1.2, < 3)
|
||||||
better_errors (2.9.1)
|
better_errors (2.9.1)
|
||||||
coderay (>= 1.0.0)
|
coderay (>= 1.0.0)
|
||||||
erubi (>= 1.0.0)
|
erubi (>= 1.0.0)
|
||||||
rack (>= 0.9.0)
|
rack (>= 0.9.0)
|
||||||
binding_of_caller (1.0.0)
|
binding_of_caller (1.0.0)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
bootsnap (1.16.0)
|
bootsnap (1.13.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
bullet (7.0.7)
|
bullet (7.0.3)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
uniform_notifier (~> 1.11)
|
uniform_notifier (~> 1.11)
|
||||||
byebug (11.1.3)
|
byebug (11.1.3)
|
||||||
capybara (3.38.0)
|
capybara (3.37.1)
|
||||||
addressable
|
addressable
|
||||||
matrix
|
matrix
|
||||||
mini_mime (>= 0.1.3)
|
mini_mime (>= 0.1.3)
|
||||||
@ -106,11 +100,11 @@ GEM
|
|||||||
xpath (~> 3.2)
|
xpath (~> 3.2)
|
||||||
cbor (0.5.9.6)
|
cbor (0.5.9.6)
|
||||||
certified (1.0.0)
|
certified (1.0.0)
|
||||||
cgi (0.3.6)
|
childprocess (4.1.0)
|
||||||
chunky_png (1.4.0)
|
chunky_png (1.4.0)
|
||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
concurrent-ruby (1.2.2)
|
concurrent-ruby (1.1.10)
|
||||||
connection_pool (2.3.0)
|
connection_pool (2.3.0)
|
||||||
cose (1.3.0)
|
cose (1.3.0)
|
||||||
cbor (~> 0.5.9)
|
cbor (~> 0.5.9)
|
||||||
@ -119,21 +113,20 @@ GEM
|
|||||||
crack (0.4.5)
|
crack (0.4.5)
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
css_parser (1.14.0)
|
css_parser (1.12.0)
|
||||||
addressable
|
addressable
|
||||||
dartsass-ruby (3.0.1)
|
|
||||||
sass-embedded (~> 1.54)
|
|
||||||
dartsass-sprockets (3.0.0)
|
|
||||||
dartsass-ruby (~> 3.0)
|
|
||||||
railties (>= 4.0.0)
|
|
||||||
sprockets (> 3.0)
|
|
||||||
sprockets-rails
|
|
||||||
tilt
|
|
||||||
date (3.3.3)
|
|
||||||
debug_inspector (1.1.0)
|
debug_inspector (1.1.0)
|
||||||
diff-lcs (1.5.0)
|
diff-lcs (1.5.0)
|
||||||
diffy (3.4.2)
|
diffy (3.4.2)
|
||||||
digest (3.1.1)
|
digest (3.1.0)
|
||||||
|
discourse-ember-rails (0.18.6)
|
||||||
|
active_model_serializers
|
||||||
|
ember-data-source (>= 1.0.0.beta.5)
|
||||||
|
ember-handlebars-template (>= 0.1.1, < 1.0)
|
||||||
|
ember-source (>= 1.1.0)
|
||||||
|
jquery-rails (>= 1.0.17)
|
||||||
|
railties (>= 3.1)
|
||||||
|
discourse-ember-source (3.12.2.3)
|
||||||
discourse-fonts (0.0.9)
|
discourse-fonts (0.0.9)
|
||||||
discourse-seed-fu (2.3.12)
|
discourse-seed-fu (2.3.12)
|
||||||
activerecord (>= 3.1)
|
activerecord (>= 3.1)
|
||||||
@ -145,19 +138,25 @@ GEM
|
|||||||
ecma-re-validator (0.4.0)
|
ecma-re-validator (0.4.0)
|
||||||
regexp_parser (~> 2.2)
|
regexp_parser (~> 2.2)
|
||||||
email_reply_trimmer (0.1.13)
|
email_reply_trimmer (0.1.13)
|
||||||
erubi (1.12.0)
|
ember-data-source (3.0.2)
|
||||||
excon (0.99.0)
|
ember-source (>= 2, < 3.0)
|
||||||
|
ember-handlebars-template (0.8.0)
|
||||||
|
barber (>= 0.11.0)
|
||||||
|
sprockets (>= 3.3, < 4.1)
|
||||||
|
ember-source (2.18.2)
|
||||||
|
erubi (1.11.0)
|
||||||
|
excon (0.93.1)
|
||||||
execjs (2.8.1)
|
execjs (2.8.1)
|
||||||
exifr (1.3.10)
|
exifr (1.3.10)
|
||||||
fabrication (2.30.0)
|
fabrication (2.30.0)
|
||||||
faker (2.23.0)
|
faker (2.23.0)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
fakeweb (1.3.0)
|
fakeweb (1.3.0)
|
||||||
faraday (2.7.4)
|
faraday (2.6.0)
|
||||||
faraday-net_http (>= 2.0, < 3.1)
|
faraday-net_http (>= 2.0, < 3.1)
|
||||||
ruby2_keywords (>= 0.0.4)
|
ruby2_keywords (>= 0.0.4)
|
||||||
faraday-net_http (3.0.2)
|
faraday-net_http (3.0.1)
|
||||||
faraday-retry (2.1.0)
|
faraday-retry (2.0.0)
|
||||||
faraday (~> 2.0)
|
faraday (~> 2.0)
|
||||||
fast_blank (1.0.1)
|
fast_blank (1.0.1)
|
||||||
fast_xs (0.8.0)
|
fast_xs (0.8.0)
|
||||||
@ -165,41 +164,40 @@ GEM
|
|||||||
ffi (1.15.5)
|
ffi (1.15.5)
|
||||||
fspath (3.1.2)
|
fspath (3.1.2)
|
||||||
gc_tracer (1.5.1)
|
gc_tracer (1.5.1)
|
||||||
globalid (1.1.0)
|
globalid (1.0.0)
|
||||||
activesupport (>= 5.0)
|
activesupport (>= 5.0)
|
||||||
google-protobuf (3.22.2)
|
|
||||||
google-protobuf (3.22.2-aarch64-linux)
|
|
||||||
google-protobuf (3.22.2-arm64-darwin)
|
|
||||||
google-protobuf (3.22.2-x86_64-darwin)
|
|
||||||
google-protobuf (3.22.2-x86_64-linux)
|
|
||||||
guess_html_encoding (0.0.11)
|
guess_html_encoding (0.0.11)
|
||||||
hana (1.3.7)
|
hana (1.3.7)
|
||||||
hashdiff (1.0.1)
|
hashdiff (1.0.1)
|
||||||
hashie (5.0.0)
|
hashie (5.0.0)
|
||||||
highline (2.1.0)
|
highline (2.0.3)
|
||||||
hkdf (1.0.0)
|
hkdf (0.3.0)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
i18n (1.12.0)
|
i18n (1.12.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
image_optim (0.31.3)
|
image_optim (0.31.1)
|
||||||
exifr (~> 1.2, >= 1.2.2)
|
exifr (~> 1.2, >= 1.2.2)
|
||||||
fspath (~> 3.0)
|
fspath (~> 3.0)
|
||||||
image_size (>= 1.5, < 4)
|
image_size (>= 1.5, < 4)
|
||||||
in_threads (~> 1.3)
|
in_threads (~> 1.3)
|
||||||
progress (~> 3.0, >= 3.0.1)
|
progress (~> 3.0, >= 3.0.1)
|
||||||
image_size (3.2.0)
|
image_size (3.1.0)
|
||||||
in_threads (1.6.0)
|
in_threads (1.6.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.1)
|
||||||
json (2.6.3)
|
jquery-rails (4.5.0)
|
||||||
|
rails-dom-testing (>= 1, < 3)
|
||||||
|
railties (>= 4.2.0)
|
||||||
|
thor (>= 0.14, < 2.0)
|
||||||
|
json (2.6.2)
|
||||||
json-schema (3.0.0)
|
json-schema (3.0.0)
|
||||||
addressable (>= 2.8)
|
addressable (>= 2.8)
|
||||||
json_schemer (0.2.23)
|
json_schemer (0.2.22)
|
||||||
ecma-re-validator (~> 0.3)
|
ecma-re-validator (~> 0.3)
|
||||||
hana (~> 1.3)
|
hana (~> 1.3)
|
||||||
regexp_parser (~> 2.0)
|
regexp_parser (~> 2.0)
|
||||||
uri_template (~> 0.7)
|
uri_template (~> 0.7)
|
||||||
jwt (2.7.0)
|
jwt (2.5.0)
|
||||||
kgio (2.11.4)
|
kgio (2.11.4)
|
||||||
libv8-node (16.10.0.0)
|
libv8-node (16.10.0.0)
|
||||||
libv8-node (16.10.0.0-aarch64-linux)
|
libv8-node (16.10.0.0-aarch64-linux)
|
||||||
@ -207,7 +205,7 @@ GEM
|
|||||||
libv8-node (16.10.0.0-x86_64-darwin)
|
libv8-node (16.10.0.0-x86_64-darwin)
|
||||||
libv8-node (16.10.0.0-x86_64-darwin-19)
|
libv8-node (16.10.0.0-x86_64-darwin-19)
|
||||||
libv8-node (16.10.0.0-x86_64-linux)
|
libv8-node (16.10.0.0-x86_64-linux)
|
||||||
listen (3.8.0)
|
listen (3.7.1)
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
literate_randomizer (0.4.0)
|
literate_randomizer (0.4.0)
|
||||||
@ -219,8 +217,8 @@ GEM
|
|||||||
logstash-event (1.2.02)
|
logstash-event (1.2.02)
|
||||||
logstash-logger (0.26.1)
|
logstash-logger (0.26.1)
|
||||||
logstash-event (~> 1.2)
|
logstash-event (~> 1.2)
|
||||||
logster (2.12.2)
|
logster (2.11.3)
|
||||||
loofah (2.19.1)
|
loofah (2.19.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
lru_redux (1.1.0)
|
lru_redux (1.1.0)
|
||||||
@ -228,47 +226,45 @@ GEM
|
|||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
maxminddb (0.1.22)
|
maxminddb (0.1.22)
|
||||||
memory_profiler (1.0.1)
|
memory_profiler (1.0.1)
|
||||||
message_bus (4.3.2)
|
message_bus (4.2.0)
|
||||||
rack (>= 1.1.3)
|
rack (>= 1.1.3)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
mini_mime (1.1.2)
|
mini_mime (1.1.2)
|
||||||
mini_portile2 (2.8.1)
|
mini_portile2 (2.8.0)
|
||||||
mini_racer (0.6.3)
|
mini_racer (0.6.3)
|
||||||
libv8-node (~> 16.10.0.0)
|
libv8-node (~> 16.10.0.0)
|
||||||
mini_scheduler (0.15.0)
|
mini_scheduler (0.14.0)
|
||||||
sidekiq (>= 4.2.3, < 7.0)
|
sidekiq (>= 4.2.3)
|
||||||
mini_sql (1.4.0)
|
mini_sql (1.4.0)
|
||||||
mini_suffix (0.3.3)
|
mini_suffix (0.3.3)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
minitest (5.18.0)
|
minitest (5.16.3)
|
||||||
mocha (2.0.2)
|
mocha (1.16.0)
|
||||||
ruby2_keywords (>= 0.0.5)
|
msgpack (1.6.0)
|
||||||
msgpack (1.6.1)
|
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
mustache (1.1.1)
|
mustache (1.1.1)
|
||||||
net-http (0.3.2)
|
net-http (0.3.0)
|
||||||
uri
|
uri
|
||||||
net-imap (0.3.4)
|
net-imap (0.3.1)
|
||||||
date
|
|
||||||
net-protocol
|
net-protocol
|
||||||
net-pop (0.1.2)
|
net-pop (0.1.2)
|
||||||
net-protocol
|
net-protocol
|
||||||
net-protocol (0.2.1)
|
net-protocol (0.1.3)
|
||||||
timeout
|
timeout
|
||||||
net-smtp (0.3.3)
|
net-smtp (0.3.3)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.5.8)
|
nio4r (2.5.8)
|
||||||
nokogiri (1.14.2)
|
nokogiri (1.13.9)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.14.2-aarch64-linux)
|
nokogiri (1.13.9-aarch64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.14.2-arm64-darwin)
|
nokogiri (1.13.9-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.14.2-x86_64-darwin)
|
nokogiri (1.13.9-x86_64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.14.2-x86_64-linux)
|
nokogiri (1.13.9-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oauth (1.1.0)
|
oauth (1.1.0)
|
||||||
oauth-tty (~> 1.0, >= 1.0.1)
|
oauth-tty (~> 1.0, >= 1.0.1)
|
||||||
@ -305,19 +301,18 @@ GEM
|
|||||||
omniauth-twitter (1.4.0)
|
omniauth-twitter (1.4.0)
|
||||||
omniauth-oauth (~> 1.1)
|
omniauth-oauth (~> 1.1)
|
||||||
rack
|
rack
|
||||||
openssl (3.1.0)
|
openssl (3.0.1)
|
||||||
openssl-signature_algorithm (1.3.0)
|
openssl-signature_algorithm (1.2.1)
|
||||||
openssl (> 2.0)
|
openssl (> 2.0, < 3.1)
|
||||||
optimist (3.0.1)
|
optimist (3.0.1)
|
||||||
parallel (1.22.1)
|
parallel (1.22.1)
|
||||||
parallel_tests (4.2.0)
|
parallel_tests (3.13.0)
|
||||||
parallel
|
parallel
|
||||||
parser (3.2.1.1)
|
parser (3.1.2.1)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
pg (1.4.6)
|
pg (1.4.4)
|
||||||
prettier_print (1.2.1)
|
|
||||||
progress (3.6.0)
|
progress (3.6.0)
|
||||||
pry (0.14.2)
|
pry (0.14.1)
|
||||||
coderay (~> 1.1)
|
coderay (~> 1.1)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
pry-byebug (3.10.1)
|
pry-byebug (3.10.1)
|
||||||
@ -325,22 +320,23 @@ GEM
|
|||||||
pry (>= 0.13, < 0.15)
|
pry (>= 0.13, < 0.15)
|
||||||
pry-rails (0.3.9)
|
pry-rails (0.3.9)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (5.0.1)
|
public_suffix (5.0.0)
|
||||||
puma (6.1.1)
|
puma (5.6.5)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
racc (1.6.2)
|
r2 (0.2.7)
|
||||||
rack (2.2.6.4)
|
racc (1.6.0)
|
||||||
|
rack (2.2.4)
|
||||||
rack-mini-profiler (3.0.0)
|
rack-mini-profiler (3.0.0)
|
||||||
rack (>= 1.2.0)
|
rack (>= 1.2.0)
|
||||||
rack-protection (3.0.5)
|
rack-protection (3.0.2)
|
||||||
rack
|
rack
|
||||||
rack-test (2.1.0)
|
rack-test (2.0.2)
|
||||||
rack (>= 1.3)
|
rack (>= 1.3)
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.5.0)
|
rails-html-sanitizer (1.4.3)
|
||||||
loofah (~> 2.19, >= 2.19.1)
|
loofah (~> 2.3)
|
||||||
rails_failover (0.8.1)
|
rails_failover (0.8.1)
|
||||||
activerecord (> 6.0, < 7.1)
|
activerecord (> 6.0, < 7.1)
|
||||||
concurrent-ruby
|
concurrent-ruby
|
||||||
@ -348,15 +344,15 @@ GEM
|
|||||||
rails_multisite (4.0.1)
|
rails_multisite (4.0.1)
|
||||||
activerecord (> 5.0, < 7.1)
|
activerecord (> 5.0, < 7.1)
|
||||||
railties (> 5.0, < 7.1)
|
railties (> 5.0, < 7.1)
|
||||||
railties (7.0.4.3)
|
railties (7.0.3.1)
|
||||||
actionpack (= 7.0.4.3)
|
actionpack (= 7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
zeitwerk (~> 2.5)
|
zeitwerk (~> 2.5)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
raindrops (0.20.1)
|
raindrops (0.20.0)
|
||||||
rake (13.0.6)
|
rake (13.0.6)
|
||||||
rb-fsevent (0.11.2)
|
rb-fsevent (0.11.2)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
@ -366,15 +362,15 @@ GEM
|
|||||||
msgpack (>= 0.4.3)
|
msgpack (>= 0.4.3)
|
||||||
optimist (>= 3.0.0)
|
optimist (>= 3.0.0)
|
||||||
rchardet (1.8.0)
|
rchardet (1.8.0)
|
||||||
redis (4.8.1)
|
redis (4.7.1)
|
||||||
redis-namespace (1.10.0)
|
redis-namespace (1.9.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
regexp_parser (2.7.0)
|
regexp_parser (2.6.0)
|
||||||
request_store (1.5.1)
|
request_store (1.5.1)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
rinku (2.0.6)
|
rinku (2.0.6)
|
||||||
rotp (6.2.2)
|
rotp (6.2.0)
|
||||||
rqrcode (2.1.2)
|
rqrcode (2.1.2)
|
||||||
chunky_png (~> 1.0)
|
chunky_png (~> 1.0)
|
||||||
rqrcode_core (~> 1.0)
|
rqrcode_core (~> 1.0)
|
||||||
@ -383,15 +379,15 @@ GEM
|
|||||||
rspec-core (~> 3.12.0)
|
rspec-core (~> 3.12.0)
|
||||||
rspec-expectations (~> 3.12.0)
|
rspec-expectations (~> 3.12.0)
|
||||||
rspec-mocks (~> 3.12.0)
|
rspec-mocks (~> 3.12.0)
|
||||||
rspec-core (3.12.1)
|
rspec-core (3.12.0)
|
||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-expectations (3.12.2)
|
rspec-expectations (3.12.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-html-matchers (0.10.0)
|
rspec-html-matchers (0.10.0)
|
||||||
nokogiri (~> 1)
|
nokogiri (~> 1)
|
||||||
rspec (>= 3.0.0.a)
|
rspec (>= 3.0.0.a)
|
||||||
rspec-mocks (3.12.4)
|
rspec-mocks (3.12.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.12.0)
|
rspec-support (~> 3.12.0)
|
||||||
rspec-rails (6.0.1)
|
rspec-rails (6.0.1)
|
||||||
@ -405,65 +401,59 @@ GEM
|
|||||||
rspec-support (3.12.0)
|
rspec-support (3.12.0)
|
||||||
rss (0.2.9)
|
rss (0.2.9)
|
||||||
rexml
|
rexml
|
||||||
rswag-specs (2.8.0)
|
rswag-specs (2.7.0)
|
||||||
activesupport (>= 3.1, < 7.1)
|
activesupport (>= 3.1, < 7.1)
|
||||||
json-schema (>= 2.2, < 4.0)
|
json-schema (>= 2.2, < 4.0)
|
||||||
railties (>= 3.1, < 7.1)
|
railties (>= 3.1, < 7.1)
|
||||||
rspec-core (>= 2.14)
|
rspec-core (>= 2.14)
|
||||||
rtlcss (0.2.0)
|
rubocop (1.37.1)
|
||||||
mini_racer (~> 0.6.3)
|
|
||||||
rubocop (1.48.1)
|
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.2.0.0)
|
parser (>= 3.1.2.1)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.8, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rexml (>= 3.2.5, < 4.0)
|
rexml (>= 3.2.5, < 4.0)
|
||||||
rubocop-ast (>= 1.26.0, < 2.0)
|
rubocop-ast (>= 1.23.0, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 3.0)
|
unicode-display_width (>= 1.4.0, < 3.0)
|
||||||
rubocop-ast (1.27.0)
|
rubocop-ast (1.23.0)
|
||||||
parser (>= 3.2.1.0)
|
parser (>= 3.1.1.0)
|
||||||
rubocop-capybara (2.17.1)
|
rubocop-discourse (3.0)
|
||||||
rubocop (~> 1.41)
|
|
||||||
rubocop-discourse (3.2.0)
|
|
||||||
rubocop (>= 1.1.0)
|
rubocop (>= 1.1.0)
|
||||||
rubocop-rspec (>= 2.0.0)
|
rubocop-rspec (>= 2.0.0)
|
||||||
rubocop-rspec (2.19.0)
|
rubocop-rspec (2.14.2)
|
||||||
rubocop (~> 1.33)
|
rubocop (~> 1.33)
|
||||||
rubocop-capybara (~> 2.17)
|
ruby-prof (1.4.3)
|
||||||
ruby-prof (1.6.1)
|
ruby-progressbar (1.11.0)
|
||||||
ruby-progressbar (1.13.0)
|
|
||||||
ruby-readability (0.7.0)
|
ruby-readability (0.7.0)
|
||||||
guess_html_encoding (>= 0.0.4)
|
guess_html_encoding (>= 0.0.4)
|
||||||
nokogiri (>= 1.6.0)
|
nokogiri (>= 1.6.0)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
sanitize (6.0.1)
|
sanitize (6.0.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
sass-embedded (1.59.2)
|
sassc (2.0.1)
|
||||||
google-protobuf (~> 3.21)
|
ffi (~> 1.9)
|
||||||
rake (>= 10.0.0)
|
rake
|
||||||
sass-embedded (1.59.2-aarch64-linux-gnu)
|
sassc-rails (2.1.2)
|
||||||
google-protobuf (~> 3.21)
|
railties (>= 4.0.0)
|
||||||
sass-embedded (1.59.2-arm64-darwin)
|
sassc (>= 2.0)
|
||||||
google-protobuf (~> 3.21)
|
sprockets (> 3.0)
|
||||||
sass-embedded (1.59.2-x86_64-darwin)
|
sprockets-rails
|
||||||
google-protobuf (~> 3.21)
|
tilt
|
||||||
sass-embedded (1.59.2-x86_64-linux-gnu)
|
selenium-webdriver (4.5.0)
|
||||||
google-protobuf (~> 3.21)
|
childprocess (>= 0.5, < 5.0)
|
||||||
selenium-webdriver (4.8.1)
|
|
||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
rubyzip (>= 1.2.2, < 3.0)
|
rubyzip (>= 1.2.2, < 3.0)
|
||||||
websocket (~> 1.0)
|
websocket (~> 1.0)
|
||||||
shoulda-matchers (5.3.0)
|
shoulda-matchers (5.2.0)
|
||||||
activesupport (>= 5.2.0)
|
activesupport (>= 5.2.0)
|
||||||
sidekiq (6.5.8)
|
sidekiq (6.5.7)
|
||||||
connection_pool (>= 2.2.5, < 3)
|
connection_pool (>= 2.2.5)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
redis (>= 4.5.0, < 5)
|
redis (>= 4.5.0, < 5)
|
||||||
simplecov (0.22.0)
|
simplecov (0.21.2)
|
||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
simplecov-html (~> 0.11)
|
simplecov-html (~> 0.11)
|
||||||
simplecov_json_formatter (~> 0.1)
|
simplecov_json_formatter (~> 0.1)
|
||||||
@ -472,40 +462,34 @@ GEM
|
|||||||
snaky_hash (2.0.1)
|
snaky_hash (2.0.1)
|
||||||
hashie
|
hashie
|
||||||
version_gem (~> 1.1, >= 1.1.1)
|
version_gem (~> 1.1, >= 1.1.1)
|
||||||
|
sprockets (3.7.2)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.4.2)
|
sprockets-rails (3.4.2)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sshkey (2.0.0)
|
sshkey (2.0.0)
|
||||||
stackprof (0.2.23)
|
stackprof (0.2.22)
|
||||||
syntax_tree (6.0.2)
|
test-prof (1.0.11)
|
||||||
prettier_print (>= 1.2.0)
|
|
||||||
syntax_tree-disable_ternary (1.0.0)
|
|
||||||
test-prof (1.2.0)
|
|
||||||
thor (1.2.1)
|
thor (1.2.1)
|
||||||
tilt (2.1.0)
|
tilt (2.0.11)
|
||||||
timeout (0.3.2)
|
timeout (0.3.0)
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
tzinfo-data (1.2022.7)
|
|
||||||
tzinfo (>= 1.0.0)
|
|
||||||
uglifier (4.2.0)
|
uglifier (4.2.0)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8.2)
|
unf_ext (0.0.8.2)
|
||||||
unicode-display_width (2.4.2)
|
unicode-display_width (2.3.0)
|
||||||
unicorn (6.1.0)
|
unicorn (6.1.0)
|
||||||
kgio (~> 2.6)
|
kgio (~> 2.6)
|
||||||
raindrops (~> 0.7)
|
raindrops (~> 0.7)
|
||||||
uniform_notifier (1.16.0)
|
uniform_notifier (1.16.0)
|
||||||
uri (0.12.0)
|
uri (0.11.0)
|
||||||
uri_template (0.7.0)
|
uri_template (0.7.0)
|
||||||
version_gem (1.1.1)
|
version_gem (1.1.1)
|
||||||
web-push (3.0.0)
|
|
||||||
hkdf (~> 1.0)
|
|
||||||
jwt (~> 2.0)
|
|
||||||
openssl (~> 3.0)
|
|
||||||
webdrivers (5.2.0)
|
webdrivers (5.2.0)
|
||||||
nokogiri (~> 1.6)
|
nokogiri (~> 1.6)
|
||||||
rubyzip (>= 1.3.0)
|
rubyzip (>= 1.3.0)
|
||||||
@ -514,15 +498,15 @@ GEM
|
|||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
webrick (1.7.0)
|
webpush (1.1.0)
|
||||||
|
hkdf (~> 0.2)
|
||||||
|
jwt (~> 2.0)
|
||||||
websocket (1.2.9)
|
websocket (1.2.9)
|
||||||
xorcist (1.1.3)
|
xorcist (1.1.3)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
yaml-lint (0.1.2)
|
yaml-lint (0.0.10)
|
||||||
yard (0.9.28)
|
zeitwerk (2.6.1)
|
||||||
webrick (~> 1.7.0)
|
|
||||||
zeitwerk (2.6.7)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
aarch64-linux
|
aarch64-linux
|
||||||
@ -534,18 +518,19 @@ PLATFORMS
|
|||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
actionmailer (= 7.0.4.3)
|
actionmailer (= 7.0.3.1)
|
||||||
actionpack (= 7.0.4.3)
|
actionpack (= 7.0.3.1)
|
||||||
actionview (= 7.0.4.3)
|
actionview (= 7.0.3.1)
|
||||||
actionview_precompiler
|
actionview_precompiler
|
||||||
active_model_serializers (~> 0.8.3)
|
active_model_serializers (~> 0.8.3)
|
||||||
activemodel (= 7.0.4.3)
|
activemodel (= 7.0.3.1)
|
||||||
activerecord (= 7.0.4.3)
|
activerecord (= 7.0.3.1)
|
||||||
activesupport (= 7.0.4.3)
|
activesupport (= 7.0.3.1)
|
||||||
addressable
|
addressable
|
||||||
annotate
|
annotate
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
aws-sdk-sns
|
aws-sdk-sns
|
||||||
|
barber
|
||||||
better_errors
|
better_errors
|
||||||
binding_of_caller
|
binding_of_caller
|
||||||
bootsnap
|
bootsnap
|
||||||
@ -554,19 +539,19 @@ DEPENDENCIES
|
|||||||
capybara
|
capybara
|
||||||
cbor
|
cbor
|
||||||
certified
|
certified
|
||||||
cgi (>= 0.3.6)
|
|
||||||
colored2
|
colored2
|
||||||
cose
|
cose
|
||||||
cppjieba_rb
|
cppjieba_rb
|
||||||
css_parser
|
css_parser
|
||||||
dartsass-ruby
|
|
||||||
dartsass-sprockets
|
|
||||||
diffy
|
diffy
|
||||||
digest
|
digest
|
||||||
|
discourse-ember-rails (= 0.18.6)
|
||||||
|
discourse-ember-source (~> 3.12.2)
|
||||||
discourse-fonts
|
discourse-fonts
|
||||||
discourse-seed-fu
|
discourse-seed-fu
|
||||||
discourse_dev_assets
|
discourse_dev_assets
|
||||||
email_reply_trimmer
|
email_reply_trimmer
|
||||||
|
ember-handlebars-template (= 0.8.0)
|
||||||
excon
|
excon
|
||||||
execjs
|
execjs
|
||||||
fabrication
|
fabrication
|
||||||
@ -622,12 +607,13 @@ DEPENDENCIES
|
|||||||
pry-byebug
|
pry-byebug
|
||||||
pry-rails
|
pry-rails
|
||||||
puma
|
puma
|
||||||
|
r2
|
||||||
rack
|
rack
|
||||||
rack-mini-profiler
|
rack-mini-profiler
|
||||||
rack-protection
|
rack-protection
|
||||||
rails_failover
|
rails_failover
|
||||||
rails_multisite
|
rails_multisite
|
||||||
railties (= 7.0.4.3)
|
railties (= 7.0.3.1)
|
||||||
rake
|
rake
|
||||||
rb-fsevent
|
rb-fsevent
|
||||||
rbtrace
|
rbtrace
|
||||||
@ -642,35 +628,31 @@ DEPENDENCIES
|
|||||||
rspec-rails
|
rspec-rails
|
||||||
rss
|
rss
|
||||||
rswag-specs
|
rswag-specs
|
||||||
rtlcss
|
|
||||||
rubocop-discourse
|
rubocop-discourse
|
||||||
ruby-prof
|
ruby-prof
|
||||||
ruby-readability
|
ruby-readability
|
||||||
rubyzip
|
rubyzip
|
||||||
sanitize
|
sanitize
|
||||||
|
sassc (= 2.0.1)
|
||||||
|
sassc-rails
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
shoulda-matchers
|
shoulda-matchers
|
||||||
sidekiq
|
sidekiq
|
||||||
simplecov
|
simplecov
|
||||||
sprockets!
|
sprockets (= 3.7.2)
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
sshkey
|
sshkey
|
||||||
stackprof
|
stackprof
|
||||||
syntax_tree
|
|
||||||
syntax_tree-disable_ternary
|
|
||||||
test-prof
|
test-prof
|
||||||
thor
|
thor
|
||||||
tzinfo-data
|
|
||||||
uglifier
|
uglifier
|
||||||
unf
|
unf
|
||||||
unicorn
|
unicorn
|
||||||
web-push
|
|
||||||
webdrivers
|
webdrivers
|
||||||
webmock
|
webmock
|
||||||
webrick
|
webpush
|
||||||
xorcist
|
xorcist
|
||||||
yaml-lint
|
yaml-lint
|
||||||
yard
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.4.4
|
2.3.22
|
||||||
|
|||||||
@ -30,7 +30,7 @@ To get your environment setup, follow the community setup guide for your operati
|
|||||||
|
|
||||||
If you're familiar with how Rails works and are comfortable setting up your own environment, you can also try out the [**Discourse Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md), which is aimed primarily at Ubuntu and macOS environments.
|
If you're familiar with how Rails works and are comfortable setting up your own environment, you can also try out the [**Discourse Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md), which is aimed primarily at Ubuntu and macOS environments.
|
||||||
|
|
||||||
Before you get started, ensure you have the following minimum versions: [Ruby 3.2+](https://www.ruby-lang.org/en/downloads/), [PostgreSQL 13](https://www.postgresql.org/download/), [Redis 7](https://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first!
|
Before you get started, ensure you have the following minimum versions: [Ruby 2.7+](https://www.ruby-lang.org/en/downloads/), [PostgreSQL 13+](https://www.postgresql.org/download/), [Redis 6.2+](https://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first!
|
||||||
|
|
||||||
## Setting up Discourse
|
## Setting up Discourse
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ Discourse supports the **latest, stable releases** of all major browsers and pla
|
|||||||
| Microsoft Edge | | |
|
| Microsoft Edge | | |
|
||||||
| Mozilla Firefox | | |
|
| Mozilla Firefox | | |
|
||||||
|
|
||||||
Additionally, we aim to support Safari on iOS 15.7+.
|
Additionally, we aim to support Safari on iOS 12.5+ until January 2023 (Discourse 3.0).
|
||||||
|
|
||||||
## Built With
|
## Built With
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ The original Discourse code contributors can be found in [**AUTHORS.MD**](docs/A
|
|||||||
|
|
||||||
## Copyright / License
|
## Copyright / License
|
||||||
|
|
||||||
Copyright 2014 - 2023 Civilized Discourse Construction Kit, Inc.
|
Copyright 2014 - 2022 Civilized Discourse Construction Kit, Inc.
|
||||||
|
|
||||||
Licensed under the GNU General Public License Version 2.0 (or later);
|
Licensed under the GNU General Public License Version 2.0 (or later);
|
||||||
you may not use this work except in compliance with the License.
|
you may not use this work except in compliance with the License.
|
||||||
|
|||||||
@ -21,7 +21,8 @@
|
|||||||
"line-stream": "0.0.0",
|
"line-stream": "0.0.0",
|
||||||
"regenerator-transform": "0.10.1",
|
"regenerator-transform": "0.10.1",
|
||||||
"source-map": "0.1.43",
|
"source-map": "0.1.43",
|
||||||
"sourcemap-validator": "1.1.1"
|
"sourcemap-validator": "1.1.1",
|
||||||
|
"xmldom": "0.1.31"
|
||||||
},
|
},
|
||||||
"corrections": true,
|
"corrections": true,
|
||||||
"ignore": [
|
"ignore": [
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RESTAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class ApiKey extends RestAdapter {
|
export default RESTAdapter.extend({
|
||||||
jsonMode = true;
|
jsonMode: true,
|
||||||
|
|
||||||
basePath() {
|
basePath() {
|
||||||
return "/admin/api/";
|
return "/admin/api/";
|
||||||
}
|
},
|
||||||
|
|
||||||
apiNameFor() {
|
apiNameFor() {
|
||||||
return "key";
|
return "key";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default function buildPluginAdapter(pluginName) {
|
export default function buildPluginAdapter(pluginName) {
|
||||||
return class extends RestAdapter {
|
return RestAdapter.extend({
|
||||||
pathFor(store, type, findArgs) {
|
pathFor(store, type, findArgs) {
|
||||||
return (
|
return (
|
||||||
"/admin/plugins/" + pluginName + super.pathFor(store, type, findArgs)
|
"/admin/plugins/" + pluginName + this._super(store, type, findArgs)
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class CustomizationBase extends RestAdapter {
|
export default RestAdapter.extend({
|
||||||
basePath() {
|
basePath() {
|
||||||
return "/admin/customize/";
|
return "/admin/customize/";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class EmailStyle extends RestAdapter {
|
export default RestAdapter.extend({
|
||||||
pathFor() {
|
pathFor() {
|
||||||
return "/admin/customize/email_style";
|
return "/admin/customize/email_style";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class Embedding extends RestAdapter {
|
export default RestAdapter.extend({
|
||||||
pathFor() {
|
pathFor() {
|
||||||
return "/admin/customize/embedding";
|
return "/admin/customize/embedding";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class StaffActionLog extends RestAdapter {
|
export default RestAdapter.extend({
|
||||||
basePath() {
|
basePath() {
|
||||||
return "/admin/logs/";
|
return "/admin/logs/";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class TagGroup extends RestAdapter {
|
export default RestAdapter.extend({
|
||||||
jsonMode = true;
|
jsonMode: true,
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RestAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class Theme extends RestAdapter {
|
export default RestAdapter.extend({
|
||||||
jsonMode = true;
|
|
||||||
basePath() {
|
basePath() {
|
||||||
return "/admin/";
|
return "/admin/";
|
||||||
}
|
},
|
||||||
|
|
||||||
afterFindAll(results) {
|
afterFindAll(results) {
|
||||||
let map = {};
|
let map = {};
|
||||||
@ -21,5 +20,7 @@ export default class Theme extends RestAdapter {
|
|||||||
theme.set("parentThemes", mappedParents);
|
theme.set("parentThemes", mappedParents);
|
||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
jsonMode: true,
|
||||||
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RESTAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class WebHookEvent extends RestAdapter {
|
export default RESTAdapter.extend({
|
||||||
basePath() {
|
basePath() {
|
||||||
return "/admin/api/";
|
return "/admin/api/";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import RestAdapter from "discourse/adapters/rest";
|
import RESTAdapter from "discourse/adapters/rest";
|
||||||
|
|
||||||
export default class WebHook extends RestAdapter {
|
export default RESTAdapter.extend({
|
||||||
basePath() {
|
basePath() {
|
||||||
return "/admin/api/";
|
return "/admin/api/";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
<div class="ace">{{this.content}}</div>
|
|
||||||
@ -1,33 +1,31 @@
|
|||||||
import { action } from "@ember/object";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { observes, on } from "@ember-decorators/object";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind, observes } from "discourse-common/utils/decorators";
|
||||||
|
import { on } from "@ember/object/evented";
|
||||||
|
|
||||||
const COLOR_VARS_REGEX =
|
const COLOR_VARS_REGEX =
|
||||||
/\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g;
|
/\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g;
|
||||||
|
|
||||||
@classNames("ace-wrapper")
|
export default Component.extend({
|
||||||
export default class AceEditor extends Component {
|
mode: "css",
|
||||||
mode = "css";
|
classNames: ["ace-wrapper"],
|
||||||
disabled = false;
|
_editor: null,
|
||||||
htmlPlaceholder = false;
|
_skipContentChangeEvent: null,
|
||||||
_editor = null;
|
disabled: false,
|
||||||
_skipContentChangeEvent = null;
|
htmlPlaceholder: false,
|
||||||
|
|
||||||
@observes("editorId")
|
@observes("editorId")
|
||||||
editorIdChanged() {
|
editorIdChanged() {
|
||||||
if (this.autofocus) {
|
if (this.autofocus) {
|
||||||
this.send("focus");
|
this.send("focus");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
didRender() {
|
didRender() {
|
||||||
this._skipContentChangeEvent = false;
|
this._skipContentChangeEvent = false;
|
||||||
}
|
},
|
||||||
|
|
||||||
@observes("content")
|
@observes("content")
|
||||||
contentChanged() {
|
contentChanged() {
|
||||||
@ -35,14 +33,14 @@ export default class AceEditor extends Component {
|
|||||||
if (this._editor && !this._skipContentChangeEvent) {
|
if (this._editor && !this._skipContentChangeEvent) {
|
||||||
this._editor.getSession().setValue(content);
|
this._editor.getSession().setValue(content);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
@observes("mode")
|
@observes("mode")
|
||||||
modeChanged() {
|
modeChanged() {
|
||||||
if (this._editor && !this._skipContentChangeEvent) {
|
if (this._editor && !this._skipContentChangeEvent) {
|
||||||
this._editor.getSession().setMode("ace/mode/" + this.mode);
|
this._editor.getSession().setMode("ace/mode/" + this.mode);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
@observes("placeholder")
|
@observes("placeholder")
|
||||||
placeholderChanged() {
|
placeholderChanged() {
|
||||||
@ -51,12 +49,12 @@ export default class AceEditor extends Component {
|
|||||||
placeholder: this.placeholder,
|
placeholder: this.placeholder,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
@observes("disabled")
|
@observes("disabled")
|
||||||
disabledStateChanged() {
|
disabledStateChanged() {
|
||||||
this.changeDisabledState();
|
this.changeDisabledState();
|
||||||
}
|
},
|
||||||
|
|
||||||
changeDisabledState() {
|
changeDisabledState() {
|
||||||
const editor = this._editor;
|
const editor = this._editor;
|
||||||
@ -69,10 +67,9 @@ export default class AceEditor extends Component {
|
|||||||
});
|
});
|
||||||
editor.container.parentNode.setAttribute("data-disabled", disabled);
|
editor.container.parentNode.setAttribute("data-disabled", disabled);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
@on("willDestroyElement")
|
_destroyEditor: on("willDestroyElement", function () {
|
||||||
_destroyEditor() {
|
|
||||||
if (this._editor) {
|
if (this._editor) {
|
||||||
this._editor.destroy();
|
this._editor.destroy();
|
||||||
this._editor = null;
|
this._editor = null;
|
||||||
@ -83,16 +80,16 @@ export default class AceEditor extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(window).off("ace:resize");
|
$(window).off("ace:resize");
|
||||||
}
|
}),
|
||||||
|
|
||||||
resize() {
|
resize() {
|
||||||
if (this._editor) {
|
if (this._editor) {
|
||||||
this._editor.resize();
|
this._editor.resize();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
super.didInsertElement(...arguments);
|
this._super(...arguments);
|
||||||
loadScript("/javascripts/ace/ace.js").then(() => {
|
loadScript("/javascripts/ace/ace.js").then(() => {
|
||||||
window.ace.require(["ace/ace"], (loadedAce) => {
|
window.ace.require(["ace/ace"], (loadedAce) => {
|
||||||
loadedAce.config.set("loadWorkerFromBlob", false);
|
loadedAce.config.set("loadWorkerFromBlob", false);
|
||||||
@ -156,13 +153,13 @@ export default class AceEditor extends Component {
|
|||||||
this._darkModeListener.addListener(this.setAceTheme);
|
this._darkModeListener.addListener(this.setAceTheme);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
if (this._darkModeListener) {
|
if (this._darkModeListener) {
|
||||||
this._darkModeListener.removeListener(this.setAceTheme);
|
this._darkModeListener.removeListener(this.setAceTheme);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
setAceTheme() {
|
setAceTheme() {
|
||||||
@ -173,7 +170,7 @@ export default class AceEditor extends Component {
|
|||||||
this._editor.setTheme(
|
this._editor.setTheme(
|
||||||
`ace/theme/${schemeType === "dark" ? "chaos" : "chrome"}`
|
`ace/theme/${schemeType === "dark" ? "chaos" : "chrome"}`
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
warnSCSSDeprecations() {
|
warnSCSSDeprecations() {
|
||||||
if (
|
if (
|
||||||
@ -200,20 +197,21 @@ export default class AceEditor extends Component {
|
|||||||
|
|
||||||
this._editor.getSession().setAnnotations(warnings);
|
this._editor.getSession().setAnnotations(warnings);
|
||||||
|
|
||||||
this.setWarning?.(
|
this.setWarning(
|
||||||
warnings.length
|
warnings.length
|
||||||
? I18n.t("admin.customize.theme.scss_color_variables_warning")
|
? I18n.t("admin.customize.theme.scss_color_variables_warning")
|
||||||
: false
|
: false
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
actions: {
|
||||||
focus() {
|
focus() {
|
||||||
if (this._editor) {
|
if (this._editor) {
|
||||||
this._editor.focus();
|
this._editor.focus();
|
||||||
this._editor.navigateFileEnd();
|
this._editor.navigateFileEnd();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
|
||||||
_overridePlaceholder(loadedAce) {
|
_overridePlaceholder(loadedAce) {
|
||||||
const originalPlaceholderSetter =
|
const originalPlaceholderSetter =
|
||||||
@ -241,5 +239,5 @@ export default class AceEditor extends Component {
|
|||||||
|
|
||||||
this.$updatePlaceholder();
|
this.$updatePlaceholder();
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
{{#if this.hasFormattedLogs}}
|
|
||||||
<pre>{{this.formattedLogs}}</pre>
|
|
||||||
{{else}}
|
|
||||||
<p>{{this.noLogsMessage}}</p>
|
|
||||||
{{/if}}
|
|
||||||
{{#if this.showLoadingSpinner}}
|
|
||||||
<div class="spinner small"></div>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,26 +1,28 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
import { observes, on } from "discourse-common/utils/decorators";
|
||||||
import { observes, on } from "@ember-decorators/object";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
import { scheduleOnce } from "@ember/runloop";
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
|
|
||||||
@classNames("admin-backups-logs")
|
export default Component.extend({
|
||||||
export default class AdminBackupsLogs extends Component {
|
classNames: ["admin-backups-logs"],
|
||||||
showLoadingSpinner = false;
|
showLoadingSpinner: false,
|
||||||
hasFormattedLogs = false;
|
hasFormattedLogs: false,
|
||||||
noLogsMessage = I18n.t("admin.backups.logs.none");
|
noLogsMessage: I18n.t("admin.backups.logs.none"),
|
||||||
formattedLogs = "";
|
|
||||||
index = 0;
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this._reset();
|
||||||
|
},
|
||||||
|
|
||||||
_reset() {
|
_reset() {
|
||||||
this.setProperties({ formattedLogs: "", index: 0 });
|
this.setProperties({ formattedLogs: "", index: 0 });
|
||||||
}
|
},
|
||||||
|
|
||||||
_scrollDown() {
|
_scrollDown() {
|
||||||
const div = this.element;
|
const div = this.element;
|
||||||
div.scrollTop = div.scrollHeight;
|
div.scrollTop = div.scrollHeight;
|
||||||
}
|
},
|
||||||
|
|
||||||
@on("init")
|
@on("init")
|
||||||
@observes("logs.[]")
|
@observes("logs.[]")
|
||||||
@ -29,7 +31,7 @@ export default class AdminBackupsLogs extends Component {
|
|||||||
this._reset(); // reset the cached logs whenever the model is reset
|
this._reset(); // reset the cached logs whenever the model is reset
|
||||||
this.renderLogs();
|
this.renderLogs();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_updateFormattedLogsFunc() {
|
_updateFormattedLogsFunc() {
|
||||||
const logs = this.logs;
|
const logs = this.logs;
|
||||||
@ -53,13 +55,13 @@ export default class AdminBackupsLogs extends Component {
|
|||||||
this.renderLogs();
|
this.renderLogs();
|
||||||
|
|
||||||
scheduleOnce("afterRender", this, this._scrollDown);
|
scheduleOnce("afterRender", this, this._scrollDown);
|
||||||
}
|
},
|
||||||
|
|
||||||
@on("init")
|
@on("init")
|
||||||
@observes("logs.[]")
|
@observes("logs.[]")
|
||||||
_updateFormattedLogs() {
|
_updateFormattedLogs() {
|
||||||
discourseDebounce(this, this._updateFormattedLogsFunc, 150);
|
discourseDebounce(this, this._updateFormattedLogsFunc, 150);
|
||||||
}
|
},
|
||||||
|
|
||||||
renderLogs() {
|
renderLogs() {
|
||||||
const formattedLogs = this.formattedLogs;
|
const formattedLogs = this.formattedLogs;
|
||||||
@ -74,5 +76,5 @@ export default class AdminBackupsLogs extends Component {
|
|||||||
} else {
|
} else {
|
||||||
this.set("showLoadingSpinner", false);
|
this.set("showLoadingSpinner", false);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
<div class="field">{{i18n this.name}}</div>
|
|
||||||
<div class="value">
|
|
||||||
{{#if this.editing}}
|
|
||||||
<TextField
|
|
||||||
@value={{this.buffer}}
|
|
||||||
@autofocus="autofocus"
|
|
||||||
@autocomplete="off"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<a href {{on "click" this.edit}} class="inline-editable-field">
|
|
||||||
<span>{{this.value}}</span>
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="controls">
|
|
||||||
{{#if this.editing}}
|
|
||||||
<DButton
|
|
||||||
@class="btn-default"
|
|
||||||
@action={{action "save"}}
|
|
||||||
@label="admin.user_fields.save"
|
|
||||||
/>
|
|
||||||
<a href {{on "click" this.edit}}>{{i18n "cancel"}}</a>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
@class="btn-default"
|
|
||||||
@action={{action "edit"}}
|
|
||||||
@icon="pencil-alt"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
@ -1,22 +1,28 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
|
|
||||||
@tagName("")
|
export default Component.extend({
|
||||||
export default class AdminEditableField extends Component {
|
tagName: "",
|
||||||
buffer = "";
|
|
||||||
editing = false;
|
buffer: "",
|
||||||
|
editing: false,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.set("editing", false);
|
||||||
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
edit(event) {
|
edit(event) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
this.set("buffer", this.value);
|
this.set("buffer", this.value);
|
||||||
this.toggleProperty("editing");
|
this.toggleProperty("editing");
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
actions: {
|
||||||
save() {
|
save() {
|
||||||
// Action has to toggle 'editing' property.
|
// Action has to toggle 'editing' property.
|
||||||
this.action(this.buffer);
|
this.action(this.buffer);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
<div class="form-element label-area">
|
|
||||||
{{#if this.label}}
|
|
||||||
<label>{{i18n this.label}}</label>
|
|
||||||
{{else}}
|
|
||||||
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="form-element input-area">
|
|
||||||
{{#if this.wrapLabel}}
|
|
||||||
<label>{{yield}}</label>
|
|
||||||
{{else}}
|
|
||||||
{{yield}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@classNames("row")
|
classNames: ["row"],
|
||||||
export default class AdminFormRow extends Component {}
|
});
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
|
|
||||||
@tagName("canvas")
|
export default Component.extend({
|
||||||
export default class AdminGraph extends Component {
|
tagName: "canvas",
|
||||||
type = "line";
|
type: "line",
|
||||||
|
|
||||||
refreshChart() {
|
refreshChart() {
|
||||||
const ctx = this.element.getContext("2d");
|
const ctx = this.element.getContext("2d");
|
||||||
@ -50,11 +49,11 @@ export default class AdminGraph extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this._chart = new window.Chart(ctx, config);
|
this._chart = new window.Chart(ctx, config);
|
||||||
}
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
loadScript("/javascripts/Chart.min.js").then(() =>
|
loadScript("/javascripts/Chart.min.js").then(() =>
|
||||||
this.refreshChart.apply(this)
|
this.refreshChart.apply(this)
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
<div class="admin-controls">
|
|
||||||
<nav>
|
|
||||||
<ul class="nav nav-pills">
|
|
||||||
{{yield}}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@tagName("")
|
tagName: "",
|
||||||
export default class AdminNav extends Component {}
|
});
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
<div
|
|
||||||
class="suspended-count {{this.suspendedCountClass}}"
|
|
||||||
title={{i18n "admin.user.last_six_months"}}
|
|
||||||
>
|
|
||||||
<label>{{i18n "admin.user.suspended_count"}}</label>
|
|
||||||
<span>{{this.user.penalty_counts.suspended}}</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="silenced-count {{this.silencedCountClass}}"
|
|
||||||
title={{i18n "admin.user.last_six_months"}}
|
|
||||||
>
|
|
||||||
<label>{{i18n "admin.user.silenced_count"}}</label>
|
|
||||||
<span>{{this.user.penalty_counts.silenced}}</span>
|
|
||||||
</div>
|
|
||||||
@ -1,16 +1,16 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
@classNames("penalty-history")
|
export default Component.extend({
|
||||||
export default class AdminPenaltyHistory extends Component {
|
classNames: ["penalty-history"],
|
||||||
|
|
||||||
@discourseComputed("user.penalty_counts.suspended")
|
@discourseComputed("user.penalty_counts.suspended")
|
||||||
suspendedCountClass(count) {
|
suspendedCountClass(count) {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
return "danger";
|
return "danger";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("user.penalty_counts.silenced")
|
@discourseComputed("user.penalty_counts.silenced")
|
||||||
silencedCountClass(count) {
|
silencedCountClass(count) {
|
||||||
@ -18,5 +18,5 @@ export default class AdminPenaltyHistory extends Component {
|
|||||||
return "danger";
|
return "danger";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
<div class="penalty-post-controls">
|
|
||||||
<label>
|
|
||||||
<div class="penalty-post-label">
|
|
||||||
{{html-safe (i18n "admin.user.penalty_post_actions")}}
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<ComboBox
|
|
||||||
@value={{this.postAction}}
|
|
||||||
@content={{this.penaltyActions}}
|
|
||||||
@onChange={{action "penaltyChanged"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.editing}}
|
|
||||||
<div class="penalty-post-edit">
|
|
||||||
<Textarea @value={{this.postEdit}} class="post-editor" />
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { action } from "@ember/object";
|
|
||||||
import { equal } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import discourseComputed, {
|
|
||||||
afterRender,
|
|
||||||
} from "discourse-common/utils/decorators";
|
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
const ACTIONS = ["delete", "delete_replies", "edit", "none"];
|
|
||||||
|
|
||||||
export default class AdminPenaltyPostAction extends Component {
|
|
||||||
postId = null;
|
|
||||||
postAction = null;
|
|
||||||
postEdit = null;
|
|
||||||
|
|
||||||
@equal("postAction", "edit") editing;
|
|
||||||
@discourseComputed
|
|
||||||
penaltyActions() {
|
|
||||||
return ACTIONS.map((id) => {
|
|
||||||
return { id, name: I18n.t(`admin.user.penalty_post_${id}`) };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
penaltyChanged(postAction) {
|
|
||||||
this.set("postAction", postAction);
|
|
||||||
|
|
||||||
// If we switch to edit mode, jump to the edit textarea
|
|
||||||
if (postAction === "edit") {
|
|
||||||
this._focusEditTextarea();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@afterRender
|
|
||||||
_focusEditTextarea() {
|
|
||||||
const elem = this.element;
|
|
||||||
const body = elem.closest(".modal-body");
|
|
||||||
body.scrollTo(0, body.clientHeight);
|
|
||||||
elem.querySelector(".post-editor").focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
<div class="penalty-reason-controls">
|
|
||||||
{{#if (eq @penaltyType "suspend")}}
|
|
||||||
<label class="suspend-reason-title">{{i18n
|
|
||||||
"admin.user.suspend_reason_title"
|
|
||||||
}}</label>
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.reasons}}
|
|
||||||
@value={{this.selectedReason}}
|
|
||||||
@class="suspend-reason"
|
|
||||||
@onChange={{this.setSelectedReason}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if this.isCustomReason}}
|
|
||||||
<TextField
|
|
||||||
@value={{this.customReason}}
|
|
||||||
@class="suspend-reason"
|
|
||||||
@onChange={{this.setCustomReason}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{else if (eq @penaltyType "silence")}}
|
|
||||||
<label class="silence-reason-title">{{html-safe
|
|
||||||
(i18n "admin.user.silence_reason_label")
|
|
||||||
}}</label>
|
|
||||||
<TextField
|
|
||||||
@value={{this.customReason}}
|
|
||||||
@class="silence-reason"
|
|
||||||
@onChange={{this.setCustomReason}}
|
|
||||||
@placeholderKey="admin.user.silence_reason_placeholder"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="penalty-message-controls">
|
|
||||||
<label>{{i18n "admin.user.suspend_message"}}</label>
|
|
||||||
<Textarea
|
|
||||||
@value={{this.message}}
|
|
||||||
class="suspend-message"
|
|
||||||
placeholder={{i18n "admin.user.suspend_message_placeholder"}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import { equal } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
const CUSTOM_REASON_KEY = "custom";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class AdminPenaltyReason extends Component {
|
|
||||||
selectedReason = CUSTOM_REASON_KEY;
|
|
||||||
customReason = "";
|
|
||||||
|
|
||||||
reasonKeys = [
|
|
||||||
"not_listening_to_staff",
|
|
||||||
"consuming_staff_time",
|
|
||||||
"combative",
|
|
||||||
"in_wrong_place",
|
|
||||||
"no_constructive_purpose",
|
|
||||||
CUSTOM_REASON_KEY,
|
|
||||||
];
|
|
||||||
|
|
||||||
@equal("selectedReason", CUSTOM_REASON_KEY) isCustomReason;
|
|
||||||
|
|
||||||
@discourseComputed("reasonKeys")
|
|
||||||
reasons(keys) {
|
|
||||||
return keys.map((key) => {
|
|
||||||
return { id: key, name: I18n.t(`admin.user.suspend_reasons.${key}`) };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setSelectedReason(value) {
|
|
||||||
this.set("selectedReason", value);
|
|
||||||
this.setReason();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setCustomReason(value) {
|
|
||||||
this.set("customReason", value);
|
|
||||||
this.setReason();
|
|
||||||
}
|
|
||||||
|
|
||||||
setReason() {
|
|
||||||
if (this.isCustomReason) {
|
|
||||||
this.set("reason", this.customReason);
|
|
||||||
} else {
|
|
||||||
this.set(
|
|
||||||
"reason",
|
|
||||||
I18n.t(`admin.user.suspend_reasons.${this.selectedReason}`)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
<div class="penalty-similar-users">
|
|
||||||
<p class="alert alert-warning">
|
|
||||||
{{html-safe
|
|
||||||
(i18n
|
|
||||||
"admin.user.other_matches"
|
|
||||||
(hash count=this.user.similar_users_count username=this.user.username)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th>{{i18n "username"}}</th>
|
|
||||||
<th>{{i18n "last_seen"}}</th>
|
|
||||||
<th>{{i18n "admin.user.topics_entered"}}</th>
|
|
||||||
<th>{{i18n "admin.user.posts_read_count"}}</th>
|
|
||||||
<th>{{i18n "admin.user.time_read"}}</th>
|
|
||||||
<th>{{i18n "created"}}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{{#each this.user.similar_users as |user|}}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
disabled={{not (get user this.penaltyField)}}
|
|
||||||
{{on "click" (action "selectUserId" user.id)}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>{{avatar user imageSize="small"}} {{user.username}}</td>
|
|
||||||
<td>{{format-duration user.last_seen_age}}</td>
|
|
||||||
<td>{{number user.topics_entered}}</td>
|
|
||||||
<td>{{number user.posts_read_count}}</td>
|
|
||||||
<td>{{format-duration user.time_read}}</td>
|
|
||||||
<td>{{format-duration user.created_at_age}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class AdminPenaltySimilarUsers extends Component {
|
|
||||||
@discourseComputed("penaltyType")
|
|
||||||
penaltyField(penaltyType) {
|
|
||||||
if (penaltyType === "suspend") {
|
|
||||||
return "can_be_suspended";
|
|
||||||
} else if (penaltyType === "silence") {
|
|
||||||
return "can_be_silenced";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
selectUserId(userId, event) {
|
|
||||||
if (!this.selectedUserIds) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.target.checked) {
|
|
||||||
this.selectedUserIds.pushObject(userId);
|
|
||||||
} else {
|
|
||||||
this.selectedUserIds.removeObject(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
<div class="chart-canvas-container">
|
|
||||||
<canvas class="chart-canvas"></canvas>
|
|
||||||
</div>
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import Report from "admin/models/report";
|
import Report from "admin/models/report";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
@ -8,31 +7,31 @@ import { number } from "discourse/lib/formatter";
|
|||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
@classNames("admin-report-chart")
|
export default Component.extend({
|
||||||
export default class AdminReportChart extends Component {
|
classNames: ["admin-report-chart"],
|
||||||
limit = 8;
|
limit: 8,
|
||||||
total = 0;
|
total: 0,
|
||||||
options = null;
|
options: null,
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
super.didInsertElement(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
window.addEventListener("resize", this._resizeHandler);
|
window.addEventListener("resize", this._resizeHandler);
|
||||||
}
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
super.willDestroyElement(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
window.removeEventListener("resize", this._resizeHandler);
|
window.removeEventListener("resize", this._resizeHandler);
|
||||||
|
|
||||||
this._resetChart();
|
this._resetChart();
|
||||||
}
|
},
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
super.didReceiveAttrs(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
discourseDebounce(this, this._scheduleChartRendering, 100);
|
discourseDebounce(this, this._scheduleChartRendering, 100);
|
||||||
}
|
},
|
||||||
|
|
||||||
_scheduleChartRendering() {
|
_scheduleChartRendering() {
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
@ -41,7 +40,7 @@ export default class AdminReportChart extends Component {
|
|||||||
this.element && this.element.querySelector(".chart-canvas")
|
this.element && this.element.querySelector(".chart-canvas")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_renderChart(model, chartCanvas) {
|
_renderChart(model, chartCanvas) {
|
||||||
if (!chartCanvas) {
|
if (!chartCanvas) {
|
||||||
@ -100,7 +99,7 @@ export default class AdminReportChart extends Component {
|
|||||||
this._buildChartConfig(data, this.options)
|
this._buildChartConfig(data, this.options)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_buildChartConfig(data, options) {
|
_buildChartConfig(data, options) {
|
||||||
return {
|
return {
|
||||||
@ -162,21 +161,21 @@ export default class AdminReportChart extends Component {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
_resetChart() {
|
_resetChart() {
|
||||||
if (this._chart) {
|
if (this._chart) {
|
||||||
this._chart.destroy();
|
this._chart.destroy();
|
||||||
this._chart = null;
|
this._chart = null;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_applyChartGrouping(model, data, options) {
|
_applyChartGrouping(model, data, options) {
|
||||||
return Report.collapse(model, data, options.chartGrouping);
|
return Report.collapse(model, data, options.chartGrouping);
|
||||||
}
|
},
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
_resizeHandler() {
|
_resizeHandler() {
|
||||||
discourseDebounce(this, this._scheduleChartRendering, 500);
|
discourseDebounce(this, this._scheduleChartRendering, 500);
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
<div class="cell title">
|
|
||||||
{{#if this.model.icon}}
|
|
||||||
{{d-icon this.model.icon}}
|
|
||||||
{{/if}}
|
|
||||||
<a href={{this.model.reportUrl}}>{{this.model.title}}</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell value today-count">{{number this.model.todayCount}}</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="cell value yesterday-count {{this.model.yesterdayTrend}}"
|
|
||||||
title={{this.model.yesterdayCountTitle}}
|
|
||||||
>
|
|
||||||
{{number this.model.yesterdayCount}}
|
|
||||||
{{d-icon this.model.yesterdayTrendIcon}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="cell value sevendays-count {{this.model.sevenDaysTrend}}"
|
|
||||||
title={{this.model.sevenDaysCountTitle}}
|
|
||||||
>
|
|
||||||
{{number this.model.lastSevenDaysCount}}
|
|
||||||
{{d-icon this.model.sevenDaysTrendIcon}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="cell value thirty-days-count {{this.model.thirtyDaysTrend}}"
|
|
||||||
title={{this.model.thirtyDaysCountTitle}}
|
|
||||||
>
|
|
||||||
{{number this.model.lastThirtyDaysCount}}
|
|
||||||
|
|
||||||
{{#if this.model.canDisplayTrendIcon}}
|
|
||||||
{{d-icon this.model.thirtyDaysTrendIcon}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { attributeBindings, classNames } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
|
classNames: ["admin-report-counters"],
|
||||||
|
|
||||||
@classNames("admin-report-counters")
|
attributeBindings: ["model.description:title"],
|
||||||
@attributeBindings("model.description:title")
|
});
|
||||||
export default class AdminReportCounters extends Component {}
|
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
<td class="title">
|
|
||||||
{{#if this.report.icon}}
|
|
||||||
{{d-icon this.report.icon}}
|
|
||||||
{{/if}}
|
|
||||||
<a href={{this.report.reportUrl}}>{{this.report.title}}</a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="value">{{number this.report.todayCount}}</td>
|
|
||||||
|
|
||||||
<td
|
|
||||||
class="value {{this.report.yesterdayTrend}}"
|
|
||||||
title={{this.report.yesterdayCountTitle}}
|
|
||||||
>
|
|
||||||
{{number this.report.yesterdayCount}}
|
|
||||||
{{d-icon this.report.yesterdayTrendIcon}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td
|
|
||||||
class="value {{this.report.sevenDaysTrend}}"
|
|
||||||
title={{this.report.sevenDaysCountTitle}}
|
|
||||||
>
|
|
||||||
{{number this.report.lastSevenDaysCount}}
|
|
||||||
{{d-icon this.report.sevenDaysTrendIcon}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td
|
|
||||||
class="value {{this.report.thirtyDaysTrend}}"
|
|
||||||
title={{this.report.thirtyDaysCountTitle}}
|
|
||||||
>
|
|
||||||
{{number this.report.lastThirtyDaysCount}}
|
|
||||||
{{d-icon this.report.thirtyDaysTrendIcon}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{{#if this.allTime}}
|
|
||||||
<td class="value">{{number this.report.total}}</td>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,12 +1,11 @@
|
|||||||
import { classNameBindings, tagName } from "@ember-decorators/component";
|
|
||||||
import { match } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import { match } from "@ember/object/computed";
|
||||||
@tagName("tr")
|
export default Component.extend({
|
||||||
@classNameBindings("reverseColors")
|
allTime: true,
|
||||||
export default class AdminReportCounts extends Component {
|
tagName: "tr",
|
||||||
allTime = true;
|
reverseColors: match(
|
||||||
|
"report.type",
|
||||||
@match("report.type", /^(time_to_first_response|topics_with_no_response)$/)
|
/^(time_to_first_response|topics_with_no_response)$/
|
||||||
reverseColors;
|
),
|
||||||
}
|
classNameBindings: ["reverseColors"],
|
||||||
|
});
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
<div class="table-container">
|
|
||||||
{{#each this.model.data as |data|}}
|
|
||||||
<a class="table-cell user-{{data.key}}" href={{data.url}}>
|
|
||||||
<span class="label">
|
|
||||||
{{#if data.icon}}
|
|
||||||
{{d-icon data.icon}}
|
|
||||||
{{/if}}
|
|
||||||
{{data.x}}
|
|
||||||
</span>
|
|
||||||
<span class="value">
|
|
||||||
{{number data.y}}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@classNames("admin-report-inline-table")
|
classNames: ["admin-report-inline-table"],
|
||||||
export default class AdminReportInlineTable extends Component {}
|
});
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
<td class="title"><a
|
|
||||||
href={{this.report.reportUrl}}
|
|
||||||
>{{this.report.title}}</a></td>
|
|
||||||
<td class="value">{{this.report.todayCount}}</td>
|
|
||||||
<td class="value">{{this.report.yesterdayCount}}</td>
|
|
||||||
<td class="value">{{this.report.sevenDaysAgoCount}}</td>
|
|
||||||
<td class="value">{{this.report.thirtyDaysAgoCount}}</td>
|
|
||||||
<td class="value"></td>
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@tagName("tr")
|
tagName: "tr",
|
||||||
export default class AdminReportPerDayCounts extends Component {}
|
});
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
<div class="chart-canvas-container">
|
|
||||||
<canvas class="chart-canvas"></canvas>
|
|
||||||
</div>
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import Report from "admin/models/report";
|
import Report from "admin/models/report";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
@ -8,31 +7,32 @@ import { number } from "discourse/lib/formatter";
|
|||||||
import { schedule } from "@ember/runloop";
|
import { schedule } from "@ember/runloop";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
@classNames("admin-report-chart", "admin-report-stacked-chart")
|
export default Component.extend({
|
||||||
export default class AdminReportStackedChart extends Component {
|
classNames: ["admin-report-chart", "admin-report-stacked-chart"],
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
super.didInsertElement(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
window.addEventListener("resize", this._resizeHandler);
|
window.addEventListener("resize", this._resizeHandler);
|
||||||
}
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
super.willDestroyElement(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
window.removeEventListener("resize", this._resizeHandler);
|
window.removeEventListener("resize", this._resizeHandler);
|
||||||
this._resetChart();
|
this._resetChart();
|
||||||
}
|
},
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
super.didReceiveAttrs(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
discourseDebounce(this, this._scheduleChartRendering, 100);
|
discourseDebounce(this, this._scheduleChartRendering, 100);
|
||||||
}
|
},
|
||||||
|
|
||||||
@bind
|
@bind
|
||||||
_resizeHandler() {
|
_resizeHandler() {
|
||||||
discourseDebounce(this, this._scheduleChartRendering, 500);
|
discourseDebounce(this, this._scheduleChartRendering, 500);
|
||||||
}
|
},
|
||||||
|
|
||||||
_scheduleChartRendering() {
|
_scheduleChartRendering() {
|
||||||
schedule("afterRender", () => {
|
schedule("afterRender", () => {
|
||||||
@ -45,7 +45,7 @@ export default class AdminReportStackedChart extends Component {
|
|||||||
this.element.querySelector(".chart-canvas")
|
this.element.querySelector(".chart-canvas")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_renderChart(model, chartCanvas) {
|
_renderChart(model, chartCanvas) {
|
||||||
if (!chartCanvas) {
|
if (!chartCanvas) {
|
||||||
@ -79,7 +79,7 @@ export default class AdminReportStackedChart extends Component {
|
|||||||
|
|
||||||
this._chart = new window.Chart(context, this._buildChartConfig(data));
|
this._chart = new window.Chart(context, this._buildChartConfig(data));
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_buildChartConfig(data) {
|
_buildChartConfig(data) {
|
||||||
return {
|
return {
|
||||||
@ -150,10 +150,10 @@ export default class AdminReportStackedChart extends Component {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
|
||||||
_resetChart() {
|
_resetChart() {
|
||||||
this._chart?.destroy();
|
this._chart?.destroy();
|
||||||
this._chart = null;
|
this._chart = null;
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
{{#if this.showBackupStats}}
|
|
||||||
<div class="backups">
|
|
||||||
<h3 class="storage-stats-title">
|
|
||||||
<a href={{get-url "/admin/backups"}}>{{d-icon "archive"}}
|
|
||||||
{{i18n "admin.dashboard.backups"}}</a>
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
{{#if this.backupStats.free_bytes}}
|
|
||||||
{{i18n
|
|
||||||
"admin.dashboard.space_used_and_free"
|
|
||||||
usedSize=this.usedBackupSpace
|
|
||||||
freeSize=this.freeBackupSpace
|
|
||||||
}}
|
|
||||||
{{else}}
|
|
||||||
{{i18n "admin.dashboard.space_used" usedSize=this.usedBackupSpace}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<br />
|
|
||||||
{{i18n
|
|
||||||
"admin.dashboard.backup_count"
|
|
||||||
count=this.backupStats.count
|
|
||||||
location=this.backupLocationName
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{#if this.backupStats.last_backup_taken_at}}
|
|
||||||
<br />
|
|
||||||
{{html-safe
|
|
||||||
(i18n
|
|
||||||
"admin.dashboard.lastest_backup"
|
|
||||||
date=(format-date
|
|
||||||
this.backupStats.last_backup_taken_at leaveAgo="true"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="uploads">
|
|
||||||
<h3 class="storage-stats-title">{{d-icon "upload"}}
|
|
||||||
{{i18n "admin.dashboard.uploads"}}</h3>
|
|
||||||
<p>
|
|
||||||
{{#if this.uploadStats.free_bytes}}
|
|
||||||
{{i18n
|
|
||||||
"admin.dashboard.space_used_and_free"
|
|
||||||
usedSize=this.usedUploadSpace
|
|
||||||
freeSize=this.freeUploadSpace
|
|
||||||
}}
|
|
||||||
{{else}}
|
|
||||||
{{i18n "admin.dashboard.space_used" usedSize=this.usedUploadSpace}}
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
@ -1,45 +1,43 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { alias } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { alias } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { setting } from "discourse/lib/computed";
|
import { setting } from "discourse/lib/computed";
|
||||||
|
|
||||||
@classNames("admin-report-storage-stats")
|
export default Component.extend({
|
||||||
export default class AdminReportStorageStats extends Component {
|
classNames: ["admin-report-storage-stats"],
|
||||||
@setting("backup_location") backupLocation;
|
|
||||||
|
|
||||||
@alias("model.data.backups") backupStats;
|
backupLocation: setting("backup_location"),
|
||||||
|
backupStats: alias("model.data.backups"),
|
||||||
@alias("model.data.uploads") uploadStats;
|
uploadStats: alias("model.data.uploads"),
|
||||||
|
|
||||||
@discourseComputed("backupStats")
|
@discourseComputed("backupStats")
|
||||||
showBackupStats(stats) {
|
showBackupStats(stats) {
|
||||||
return stats && this.currentUser.admin;
|
return stats && this.currentUser.admin;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("backupLocation")
|
@discourseComputed("backupLocation")
|
||||||
backupLocationName(backupLocation) {
|
backupLocationName(backupLocation) {
|
||||||
return I18n.t(`admin.backups.location.${backupLocation}`);
|
return I18n.t(`admin.backups.location.${backupLocation}`);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("backupStats.used_bytes")
|
@discourseComputed("backupStats.used_bytes")
|
||||||
usedBackupSpace(bytes) {
|
usedBackupSpace(bytes) {
|
||||||
return I18n.toHumanSize(bytes);
|
return I18n.toHumanSize(bytes);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("backupStats.free_bytes")
|
@discourseComputed("backupStats.free_bytes")
|
||||||
freeBackupSpace(bytes) {
|
freeBackupSpace(bytes) {
|
||||||
return I18n.toHumanSize(bytes);
|
return I18n.toHumanSize(bytes);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("uploadStats.used_bytes")
|
@discourseComputed("uploadStats.used_bytes")
|
||||||
usedUploadSpace(bytes) {
|
usedUploadSpace(bytes) {
|
||||||
return I18n.toHumanSize(bytes);
|
return I18n.toHumanSize(bytes);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("uploadStats.free_bytes")
|
@discourseComputed("uploadStats.free_bytes")
|
||||||
freeUploadSpace(bytes) {
|
freeUploadSpace(bytes) {
|
||||||
return I18n.toHumanSize(bytes);
|
return I18n.toHumanSize(bytes);
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
{{html-safe this.formattedValue}}
|
|
||||||
@ -1,27 +1,21 @@
|
|||||||
import {
|
|
||||||
attributeBindings,
|
|
||||||
classNameBindings,
|
|
||||||
classNames,
|
|
||||||
tagName,
|
|
||||||
} from "@ember-decorators/component";
|
|
||||||
import { alias } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import { alias } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
@tagName("td")
|
export default Component.extend({
|
||||||
@classNames("admin-report-table-cell")
|
tagName: "td",
|
||||||
@classNameBindings("type", "property")
|
classNames: ["admin-report-table-cell"],
|
||||||
@attributeBindings("value:title")
|
classNameBindings: ["type", "property"],
|
||||||
export default class AdminReportTableCell extends Component {
|
attributeBindings: ["value:title"],
|
||||||
options = null;
|
options: null,
|
||||||
|
|
||||||
@alias("label.type") type;
|
|
||||||
@alias("label.mainProperty") property;
|
|
||||||
@alias("computedLabel.formattedValue") formattedValue;
|
|
||||||
@alias("computedLabel.value") value;
|
|
||||||
|
|
||||||
@discourseComputed("label", "data", "options")
|
@discourseComputed("label", "data", "options")
|
||||||
computedLabel(label, data, options) {
|
computedLabel(label, data, options) {
|
||||||
return label.compute(data, options || {});
|
return label.compute(data, options || {});
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
type: alias("label.type"),
|
||||||
|
property: alias("label.mainProperty"),
|
||||||
|
formattedValue: alias("computedLabel.formattedValue"),
|
||||||
|
value: alias("computedLabel.value"),
|
||||||
|
});
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
{{#if this.showSortingUI}}
|
|
||||||
<DButton
|
|
||||||
@action={{this.sortByLabel}}
|
|
||||||
@icon={{this.sortIcon}}
|
|
||||||
@class="sort-btn"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.label.htmlTitle}}
|
|
||||||
<span class="title">{{html-safe this.label.htmlTitle}}</span>
|
|
||||||
{{else}}
|
|
||||||
<span class="title">{{this.label.title}}</span>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,24 +1,19 @@
|
|||||||
import {
|
|
||||||
attributeBindings,
|
|
||||||
classNameBindings,
|
|
||||||
classNames,
|
|
||||||
tagName,
|
|
||||||
} from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
@tagName("th")
|
export default Component.extend({
|
||||||
@classNames("admin-report-table-header")
|
tagName: "th",
|
||||||
@classNameBindings("label.mainProperty", "label.type", "isCurrentSort")
|
classNames: ["admin-report-table-header"],
|
||||||
@attributeBindings("label.title:title")
|
classNameBindings: ["label.mainProperty", "label.type", "isCurrentSort"],
|
||||||
export default class AdminReportTableHeader extends Component {
|
attributeBindings: ["label.title:title"],
|
||||||
|
|
||||||
@discourseComputed("currentSortLabel.sortProperty", "label.sortProperty")
|
@discourseComputed("currentSortLabel.sortProperty", "label.sortProperty")
|
||||||
isCurrentSort(currentSortField, labelSortField) {
|
isCurrentSort(currentSortField, labelSortField) {
|
||||||
return currentSortField === labelSortField;
|
return currentSortField === labelSortField;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentSortDirection")
|
@discourseComputed("currentSortDirection")
|
||||||
sortIcon(currentSortDirection) {
|
sortIcon(currentSortDirection) {
|
||||||
return currentSortDirection === 1 ? "caret-up" : "caret-down";
|
return currentSortDirection === 1 ? "caret-up" : "caret-down";
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
{{#each this.labels as |label|}}
|
|
||||||
<AdminReportTableCell
|
|
||||||
@label={{label}}
|
|
||||||
@data={{this.data}}
|
|
||||||
@options={{this.options}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
@ -1,8 +1,6 @@
|
|||||||
import { classNames, tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@tagName("tr")
|
tagName: "tr",
|
||||||
@classNames("admin-report-table-row")
|
classNames: ["admin-report-table-row"],
|
||||||
export default class AdminReportTableRow extends Component {
|
options: null,
|
||||||
options = null;
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{{#if this.model.computedLabels}}
|
|
||||||
{{#each this.model.computedLabels as |label|}}
|
|
||||||
<AdminReportTableHeader
|
|
||||||
@showSortingUI={{this.showSortingUI}}
|
|
||||||
@currentSortDirection={{this.sortDirection}}
|
|
||||||
@currentSortLabel={{this.sortLabel}}
|
|
||||||
@label={{label}}
|
|
||||||
@sortByLabel={{action "sortByLabel" label}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
{{#each this.model.data as |data|}}
|
|
||||||
<th>{{data.x}}</th>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each this.paginatedData as |data|}}
|
|
||||||
<AdminReportTableRow
|
|
||||||
@data={{data}}
|
|
||||||
@labels={{this.model.computedLabels}}
|
|
||||||
@options={{this.options}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if this.showTotalForSample}}
|
|
||||||
<tr class="total-row">
|
|
||||||
<td colspan={{this.totalsForSample.length}}>
|
|
||||||
{{i18n "admin.dashboard.reports.totals_for_sample"}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="admin-report-table-row">
|
|
||||||
{{#each this.totalsForSample as |total|}}
|
|
||||||
<td class="admin-report-table-cell {{total.type}} {{total.property}}">
|
|
||||||
{{total.formattedValue}}
|
|
||||||
</td>
|
|
||||||
{{/each}}
|
|
||||||
</tr>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.showTotal}}
|
|
||||||
<tr class="total-row">
|
|
||||||
<td colspan="2">
|
|
||||||
{{i18n "admin.dashboard.reports.total"}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="admin-report-table-row">
|
|
||||||
<td class="admin-report-table-cell date x">—</td>
|
|
||||||
<td class="admin-report-table-cell number y">{{number
|
|
||||||
this.model.total
|
|
||||||
}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.showAverage}}
|
|
||||||
<tr class="total-row">
|
|
||||||
<td colspan="2">
|
|
||||||
{{i18n "admin.dashboard.reports.average_for_sample"}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="admin-report-table-row">
|
|
||||||
<td class="admin-report-table-cell date x">—</td>
|
|
||||||
<td class="admin-report-table-cell number y">{{number
|
|
||||||
this.averageForSample
|
|
||||||
}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/if}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="pagination">
|
|
||||||
{{#each this.pages as |pageState|}}
|
|
||||||
<DButton
|
|
||||||
@translatedLabel={{pageState.page}}
|
|
||||||
@action={{action "changePage"}}
|
|
||||||
@actionParam={{pageState.index}}
|
|
||||||
@class={{pageState.class}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
@ -1,26 +1,22 @@
|
|||||||
import { action } from "@ember/object";
|
|
||||||
import { classNameBindings, classNames } from "@ember-decorators/component";
|
|
||||||
import { alias } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import { alias } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { makeArray } from "discourse-common/lib/helpers";
|
import { makeArray } from "discourse-common/lib/helpers";
|
||||||
|
|
||||||
const PAGES_LIMIT = 8;
|
const PAGES_LIMIT = 8;
|
||||||
|
|
||||||
@classNameBindings("sortable", "twoColumns")
|
export default Component.extend({
|
||||||
@classNames("admin-report-table")
|
classNameBindings: ["sortable", "twoColumns"],
|
||||||
export default class AdminReportTable extends Component {
|
classNames: ["admin-report-table"],
|
||||||
sortable = false;
|
sortable: false,
|
||||||
sortDirection = 1;
|
sortDirection: 1,
|
||||||
|
perPage: alias("options.perPage"),
|
||||||
@alias("options.perPage") perPage;
|
page: 0,
|
||||||
|
|
||||||
page = 0;
|
|
||||||
|
|
||||||
@discourseComputed("model.computedLabels.length")
|
@discourseComputed("model.computedLabels.length")
|
||||||
twoColumns(labelsLength) {
|
twoColumns(labelsLength) {
|
||||||
return labelsLength === 2;
|
return labelsLength === 2;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"totalsForSample",
|
"totalsForSample",
|
||||||
@ -35,12 +31,12 @@ export default class AdminReportTable extends Component {
|
|||||||
.reduce((s, v) => s + v, 0);
|
.reduce((s, v) => s + v, 0);
|
||||||
|
|
||||||
return sum >= 1 && total && datesFiltering;
|
return sum >= 1 && total && datesFiltering;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("model.total", "options.total", "twoColumns")
|
@discourseComputed("model.total", "options.total", "twoColumns")
|
||||||
showTotal(reportTotal, total, twoColumns) {
|
showTotal(reportTotal, total, twoColumns) {
|
||||||
return reportTotal && total && twoColumns;
|
return reportTotal && total && twoColumns;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"model.{average,data}",
|
"model.{average,data}",
|
||||||
@ -54,17 +50,17 @@ export default class AdminReportTable extends Component {
|
|||||||
sampleTotalValue &&
|
sampleTotalValue &&
|
||||||
hasTwoColumns
|
hasTwoColumns
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("totalsForSample.1.value", "model.data.length")
|
@discourseComputed("totalsForSample.1.value", "model.data.length")
|
||||||
averageForSample(totals, count) {
|
averageForSample(totals, count) {
|
||||||
return (totals / count).toFixed(0);
|
return (totals / count).toFixed(0);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("model.data.length")
|
@discourseComputed("model.data.length")
|
||||||
showSortingUI(dataLength) {
|
showSortingUI(dataLength) {
|
||||||
return dataLength >= 5;
|
return dataLength >= 5;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("totalsForSampleRow", "model.computedLabels")
|
@discourseComputed("totalsForSampleRow", "model.computedLabels")
|
||||||
totalsForSample(row, labels) {
|
totalsForSample(row, labels) {
|
||||||
@ -74,7 +70,7 @@ export default class AdminReportTable extends Component {
|
|||||||
computedLabel.property = label.mainProperty;
|
computedLabel.property = label.mainProperty;
|
||||||
return computedLabel;
|
return computedLabel;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("model.data", "model.computedLabels")
|
@discourseComputed("model.data", "model.computedLabels")
|
||||||
totalsForSampleRow(rows, labels) {
|
totalsForSampleRow(rows, labels) {
|
||||||
@ -102,7 +98,7 @@ export default class AdminReportTable extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return totalsRow;
|
return totalsRow;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("sortLabel", "sortDirection", "model.data.[]")
|
@discourseComputed("sortLabel", "sortDirection", "model.data.[]")
|
||||||
sortedData(sortLabel, sortDirection, data) {
|
sortedData(sortLabel, sortDirection, data) {
|
||||||
@ -122,7 +118,7 @@ export default class AdminReportTable extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("sortedData.[]", "perPage", "page")
|
@discourseComputed("sortedData.[]", "perPage", "page")
|
||||||
paginatedData(data, perPage, page) {
|
paginatedData(data, perPage, page) {
|
||||||
@ -132,7 +128,7 @@ export default class AdminReportTable extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("model.data", "perPage", "page")
|
@discourseComputed("model.data", "perPage", "page")
|
||||||
pages(data, perPage, page) {
|
pages(data, perPage, page) {
|
||||||
@ -160,19 +156,19 @@ export default class AdminReportTable extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
actions: {
|
||||||
changePage(page) {
|
changePage(page) {
|
||||||
this.set("page", page);
|
this.set("page", page);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
sortByLabel(label) {
|
||||||
sortByLabel(label) {
|
if (this.sortLabel === label) {
|
||||||
if (this.sortLabel === label) {
|
this.set("sortDirection", this.sortDirection === 1 ? -1 : 1);
|
||||||
this.set("sortDirection", this.sortDirection === 1 ? -1 : 1);
|
} else {
|
||||||
} else {
|
this.set("sortLabel", label);
|
||||||
this.set("sortLabel", label);
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
<td class="title">{{this.report.title}}</td>
|
|
||||||
<td class="value">
|
|
||||||
<LinkTo @route="adminUsersList.show" @model="newuser">
|
|
||||||
{{number (value-at-tl this.report.data level="0")}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
<td class="value">
|
|
||||||
<LinkTo @route="adminUsersList.show" @model="basic">
|
|
||||||
{{number (value-at-tl this.report.data level="1")}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
<td class="value">
|
|
||||||
<LinkTo @route="adminUsersList.show" @model="member">
|
|
||||||
{{number (value-at-tl this.report.data level="2")}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
<td class="value">
|
|
||||||
<LinkTo @route="adminUsersList.show" @model="regular">
|
|
||||||
{{number (value-at-tl this.report.data level="3")}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
<td class="value">
|
|
||||||
<LinkTo @route="adminUsersList.show" @model="leader">
|
|
||||||
{{number (value-at-tl this.report.data level="4")}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@tagName("tr")
|
tagName: "tr",
|
||||||
export default class AdminReportTrustLevelCounts extends Component {}
|
});
|
||||||
|
|||||||
@ -1,244 +0,0 @@
|
|||||||
{{#unless this.isHidden}}
|
|
||||||
{{#if this.isEnabled}}
|
|
||||||
<ConditionalLoadingSection @isLoading={{this.isLoading}}>
|
|
||||||
{{#if this.showHeader}}
|
|
||||||
<div class="header">
|
|
||||||
{{#if this.showTitle}}
|
|
||||||
<ul class="breadcrumb">
|
|
||||||
{{#if this.showAllReportsLink}}
|
|
||||||
<li class="item all-reports">
|
|
||||||
<LinkTo @route="admin.dashboardReports" class="report-url">
|
|
||||||
{{i18n "admin.dashboard.all_reports"}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
{{#unless this.showNotFoundError}}
|
|
||||||
<li class="item separator">|</li>
|
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#unless this.showNotFoundError}}
|
|
||||||
<li class="item report">
|
|
||||||
<a href={{this.model.reportUrl}} class="report-url">
|
|
||||||
{{this.model.title}}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{{#if this.model.description}}
|
|
||||||
{{#if this.model.description_link}}
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={{this.model.description_link}}
|
|
||||||
class="info"
|
|
||||||
data-tooltip={{this.model.description}}
|
|
||||||
>
|
|
||||||
{{d-icon "question-circle"}}
|
|
||||||
</a>
|
|
||||||
{{else}}
|
|
||||||
<span
|
|
||||||
class="info"
|
|
||||||
data-tooltip={{this.model.description}}
|
|
||||||
>
|
|
||||||
{{d-icon "question-circle"}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</li>
|
|
||||||
{{/unless}}
|
|
||||||
</ul>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.shouldDisplayTrend}}
|
|
||||||
<div class="trend {{this.model.trend}}">
|
|
||||||
<span class="value" title={{this.model.trendTitle}}>
|
|
||||||
{{#if this.model.average}}
|
|
||||||
{{number this.model.currentAverage}}{{#if
|
|
||||||
this.model.percent
|
|
||||||
}}%{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{number this.model.currentTotal noTitle="true"}}{{#if
|
|
||||||
this.model.percent
|
|
||||||
}}%{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.model.trendIcon}}
|
|
||||||
{{d-icon this.model.trendIcon class="icon"}}
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="body">
|
|
||||||
<div class="main">
|
|
||||||
{{#if this.showError}}
|
|
||||||
{{#if this.showTimeoutError}}
|
|
||||||
<div class="alert alert-error report-alert timeout">
|
|
||||||
{{d-icon "exclamation-triangle"}}
|
|
||||||
<span>{{i18n "admin.dashboard.timeout_error"}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.showExceptionError}}
|
|
||||||
<div class="alert alert-error report-alert exception">
|
|
||||||
{{d-icon "exclamation-triangle"}}
|
|
||||||
<span>{{i18n "admin.dashboard.exception_error"}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.showNotFoundError}}
|
|
||||||
<div class="alert alert-error report-alert not-found">
|
|
||||||
{{d-icon "exclamation-triangle"}}
|
|
||||||
<span>{{i18n "admin.dashboard.not_found_error"}}</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#if this.hasData}}
|
|
||||||
{{#if this.currentMode}}
|
|
||||||
{{component
|
|
||||||
this.modeComponent
|
|
||||||
model=this.model
|
|
||||||
options=this.options
|
|
||||||
}}
|
|
||||||
|
|
||||||
{{#if this.model.relatedReport}}
|
|
||||||
<AdminReport
|
|
||||||
@showFilteringUI={{false}}
|
|
||||||
@dataSourceName={{this.model.relatedReport.type}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#if this.rateLimitationString}}
|
|
||||||
<div class="alert alert-error report-alert rate-limited">
|
|
||||||
{{d-icon "thermometer-three-quarters"}}
|
|
||||||
<span>{{this.rateLimitationString}}</span>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="alert alert-info report-alert no-data">
|
|
||||||
{{d-icon "chart-pie"}}
|
|
||||||
{{#if this.model.reportUrl}}
|
|
||||||
<a href={{this.model.reportUrl}} class="report-url">
|
|
||||||
<span>
|
|
||||||
{{#if this.model.title}}
|
|
||||||
{{this.model.title}}
|
|
||||||
—
|
|
||||||
{{/if}}
|
|
||||||
{{i18n "admin.dashboard.reports.no_data"}}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{{else}}
|
|
||||||
<span>{{i18n "admin.dashboard.reports.no_data"}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.showFilteringUI}}
|
|
||||||
<div class="filters">
|
|
||||||
{{#if this.showModes}}
|
|
||||||
<div class="modes">
|
|
||||||
{{#each this.displayedModes as |displayedMode|}}
|
|
||||||
<DButton
|
|
||||||
@action={{action "onChangeMode"}}
|
|
||||||
@actionParam={{displayedMode.mode}}
|
|
||||||
@class={{displayedMode.cssClass}}
|
|
||||||
@icon={{displayedMode.icon}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.isChartMode}}
|
|
||||||
{{#if this.model.average}}
|
|
||||||
<span class="average-chart">
|
|
||||||
{{i18n "admin.dashboard.reports.average_chart_label"}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
<div class="chart-groupings">
|
|
||||||
{{#each this.chartGroupings as |chartGrouping|}}
|
|
||||||
<DButton
|
|
||||||
@label={{chartGrouping.label}}
|
|
||||||
@action={{action "changeGrouping" chartGrouping.id}}
|
|
||||||
@class={{chartGrouping.class}}
|
|
||||||
@disabled={{chartGrouping.disabled}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.showDatesOptions}}
|
|
||||||
<div class="control">
|
|
||||||
<span class="label">
|
|
||||||
{{i18n "admin.dashboard.reports.dates"}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="input">
|
|
||||||
<DateTimeInputRange
|
|
||||||
@from={{this.startDate}}
|
|
||||||
@to={{this.endDate}}
|
|
||||||
@onChange={{action "onChangeDateRange"}}
|
|
||||||
@showFromTime={{false}}
|
|
||||||
@showToTime={{false}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#each this.model.available_filters as |filter|}}
|
|
||||||
<div class="control">
|
|
||||||
<span class="label">
|
|
||||||
{{i18n
|
|
||||||
(concat
|
|
||||||
"admin.dashboard.reports.filters." filter.id ".label"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div class="input">
|
|
||||||
{{component
|
|
||||||
(concat "report-filters/" filter.type)
|
|
||||||
model=this.model
|
|
||||||
filter=filter
|
|
||||||
applyFilter=(action "applyFilter")
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<div class="control">
|
|
||||||
<div class="input">
|
|
||||||
<DButton
|
|
||||||
@class="btn-default export-csv-btn"
|
|
||||||
@action={{action "exportCsv"}}
|
|
||||||
@label="admin.export_csv.button_text"
|
|
||||||
@icon="download"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.showRefresh}}
|
|
||||||
<div class="control">
|
|
||||||
<div class="input">
|
|
||||||
<DButton
|
|
||||||
@class="refresh-report-btn btn-primary"
|
|
||||||
@action={{action "refreshReport"}}
|
|
||||||
@label="admin.dashboard.reports.refresh_report"
|
|
||||||
@icon="sync"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</ConditionalLoadingSection>
|
|
||||||
{{else}}
|
|
||||||
<div class="alert alert-info">
|
|
||||||
{{html-safe this.disabledLabel}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/unless}}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { classNameBindings, classNames } from "@ember-decorators/component";
|
|
||||||
import { alias, and, equal, notEmpty, or } from "@ember/object/computed";
|
|
||||||
import EmberObject, { action, computed } from "@ember/object";
|
import EmberObject, { action, computed } from "@ember/object";
|
||||||
import Report, { DAILY_LIMIT_DAYS, SCHEMA_VERSION } from "admin/models/report";
|
import Report, { DAILY_LIMIT_DAYS, SCHEMA_VERSION } from "admin/models/report";
|
||||||
|
import { alias, and, equal, notEmpty, or } from "@ember/object/computed";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import ReportLoader from "discourse/lib/reports-loader";
|
import ReportLoader from "discourse/lib/reports-loader";
|
||||||
@ -22,58 +21,51 @@ const TABLE_OPTIONS = {
|
|||||||
|
|
||||||
const CHART_OPTIONS = {};
|
const CHART_OPTIONS = {};
|
||||||
|
|
||||||
@classNameBindings(
|
export default Component.extend({
|
||||||
"isHidden:hidden",
|
classNameBindings: [
|
||||||
"isHidden::is-visible",
|
"isHidden:hidden",
|
||||||
"isEnabled",
|
"isHidden::is-visible",
|
||||||
"isLoading",
|
"isEnabled",
|
||||||
"dasherizedDataSourceName"
|
"isLoading",
|
||||||
)
|
"dasherizedDataSourceName",
|
||||||
@classNames("admin-report")
|
],
|
||||||
export default class AdminReport extends Component {
|
classNames: ["admin-report"],
|
||||||
isEnabled = true;
|
isEnabled: true,
|
||||||
disabledLabel = I18n.t("admin.dashboard.disabled");
|
disabledLabel: I18n.t("admin.dashboard.disabled"),
|
||||||
isLoading = false;
|
isLoading: false,
|
||||||
rateLimitationString = null;
|
rateLimitationString: null,
|
||||||
dataSourceName = null;
|
dataSourceName: null,
|
||||||
report = null;
|
report: null,
|
||||||
model = null;
|
model: null,
|
||||||
reportOptions = null;
|
reportOptions: null,
|
||||||
forcedModes = null;
|
forcedModes: null,
|
||||||
showAllReportsLink = false;
|
showAllReportsLink: false,
|
||||||
filters = null;
|
filters: null,
|
||||||
showTrend = false;
|
showTrend: false,
|
||||||
showHeader = true;
|
showHeader: true,
|
||||||
showTitle = true;
|
showTitle: true,
|
||||||
showFilteringUI = false;
|
showFilteringUI: false,
|
||||||
|
showDatesOptions: alias("model.dates_filtering"),
|
||||||
|
showRefresh: or("showDatesOptions", "model.available_filters.length"),
|
||||||
|
shouldDisplayTrend: and("showTrend", "model.prev_period"),
|
||||||
|
endDate: null,
|
||||||
|
startDate: null,
|
||||||
|
|
||||||
@alias("model.dates_filtering") showDatesOptions;
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
@or("showDatesOptions", "model.available_filters.length") showRefresh;
|
this._reports = [];
|
||||||
|
},
|
||||||
|
|
||||||
@and("showTrend", "model.prev_period") shouldDisplayTrend;
|
isHidden: computed("siteSettings.dashboard_hidden_reports", function () {
|
||||||
|
|
||||||
endDate = null;
|
|
||||||
startDate = null;
|
|
||||||
|
|
||||||
@or("showTimeoutError", "showExceptionError", "showNotFoundError") showError;
|
|
||||||
@equal("model.error", "not_found") showNotFoundError;
|
|
||||||
@equal("model.error", "timeout") showTimeoutError;
|
|
||||||
@equal("model.error", "exception") showExceptionError;
|
|
||||||
@notEmpty("model.data") hasData;
|
|
||||||
|
|
||||||
_reports = [];
|
|
||||||
|
|
||||||
@computed("siteSettings.dashboard_hidden_reports")
|
|
||||||
get isHidden() {
|
|
||||||
return (this.siteSettings.dashboard_hidden_reports || "")
|
return (this.siteSettings.dashboard_hidden_reports || "")
|
||||||
.split("|")
|
.split("|")
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.includes(this.dataSourceName);
|
.includes(this.dataSourceName);
|
||||||
}
|
}),
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
super.didReceiveAttrs(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
let startDate = moment();
|
let startDate = moment();
|
||||||
if (this.filters && isPresent(this.filters.startDate)) {
|
if (this.filters && isPresent(this.filters.startDate)) {
|
||||||
@ -96,35 +88,42 @@ export default class AdminReport extends Component {
|
|||||||
} else if (this.dataSourceName) {
|
} else if (this.dataSourceName) {
|
||||||
this._fetchReport();
|
this._fetchReport();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
showError: or("showTimeoutError", "showExceptionError", "showNotFoundError"),
|
||||||
|
showNotFoundError: equal("model.error", "not_found"),
|
||||||
|
showTimeoutError: equal("model.error", "timeout"),
|
||||||
|
showExceptionError: equal("model.error", "exception"),
|
||||||
|
|
||||||
|
hasData: notEmpty("model.data"),
|
||||||
|
|
||||||
@discourseComputed("dataSourceName", "model.type")
|
@discourseComputed("dataSourceName", "model.type")
|
||||||
dasherizedDataSourceName(dataSourceName, type) {
|
dasherizedDataSourceName(dataSourceName, type) {
|
||||||
return (dataSourceName || type || "undefined").replace(/_/g, "-");
|
return (dataSourceName || type || "undefined").replace(/_/g, "-");
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("dataSourceName", "model.type")
|
@discourseComputed("dataSourceName", "model.type")
|
||||||
dataSource(dataSourceName, type) {
|
dataSource(dataSourceName, type) {
|
||||||
dataSourceName = dataSourceName || type;
|
dataSourceName = dataSourceName || type;
|
||||||
return `/admin/reports/${dataSourceName}`;
|
return `/admin/reports/${dataSourceName}`;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("displayedModes.length")
|
@discourseComputed("displayedModes.length")
|
||||||
showModes(displayedModesLength) {
|
showModes(displayedModesLength) {
|
||||||
return displayedModesLength > 1;
|
return displayedModesLength > 1;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentMode")
|
@discourseComputed("currentMode")
|
||||||
isChartMode(currentMode) {
|
isChartMode(currentMode) {
|
||||||
return currentMode === "chart";
|
return currentMode === "chart";
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
changeGrouping(grouping) {
|
changeGrouping(grouping) {
|
||||||
this.send("refreshReport", {
|
this.send("refreshReport", {
|
||||||
chartGrouping: grouping,
|
chartGrouping: grouping,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentMode", "model.modes", "forcedModes")
|
@discourseComputed("currentMode", "model.modes", "forcedModes")
|
||||||
displayedModes(currentMode, reportModes, forcedModes) {
|
displayedModes(currentMode, reportModes, forcedModes) {
|
||||||
@ -140,12 +139,12 @@ export default class AdminReport extends Component {
|
|||||||
icon: mode === "table" ? "table" : "signal",
|
icon: mode === "table" ? "table" : "signal",
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentMode")
|
@discourseComputed("currentMode")
|
||||||
modeComponent(currentMode) {
|
modeComponent(currentMode) {
|
||||||
return `admin-report-${currentMode.replace(/_/g, "-")}`;
|
return `admin-report-${currentMode.replace(/_/g, "-")}`;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"dataSourceName",
|
"dataSourceName",
|
||||||
@ -179,7 +178,7 @@ export default class AdminReport extends Component {
|
|||||||
.join(":");
|
.join(":");
|
||||||
|
|
||||||
return reportKey;
|
return reportKey;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("options.chartGrouping", "model.chartData.length")
|
@discourseComputed("options.chartGrouping", "model.chartData.length")
|
||||||
chartGroupings(grouping, count) {
|
chartGroupings(grouping, count) {
|
||||||
@ -193,7 +192,7 @@ export default class AdminReport extends Component {
|
|||||||
class: `chart-grouping ${grouping === id ? "active" : "inactive"}`,
|
class: `chart-grouping ${grouping === id ? "active" : "inactive"}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onChangeDateRange(range) {
|
onChangeDateRange(range) {
|
||||||
@ -201,7 +200,7 @@ export default class AdminReport extends Component {
|
|||||||
startDate: range.from,
|
startDate: range.from,
|
||||||
endDate: range.to,
|
endDate: range.to,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
applyFilter(id, value) {
|
applyFilter(id, value) {
|
||||||
@ -216,7 +215,7 @@ export default class AdminReport extends Component {
|
|||||||
this.send("refreshReport", {
|
this.send("refreshReport", {
|
||||||
filters: customFilters,
|
filters: customFilters,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
refreshReport(options = {}) {
|
refreshReport(options = {}) {
|
||||||
@ -239,7 +238,7 @@ export default class AdminReport extends Component {
|
|||||||
? this.get("filters.customFilters")
|
? this.get("filters.customFilters")
|
||||||
: options.filters,
|
: options.filters,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
exportCsv() {
|
exportCsv() {
|
||||||
@ -255,7 +254,7 @@ export default class AdminReport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exportEntity("report", args).then(outputExportResult);
|
exportEntity("report", args).then(outputExportResult);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onChangeMode(mode) {
|
onChangeMode(mode) {
|
||||||
@ -264,7 +263,7 @@ export default class AdminReport extends Component {
|
|||||||
this.send("refreshReport", {
|
this.send("refreshReport", {
|
||||||
chartGrouping: null,
|
chartGrouping: null,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_computeReport() {
|
_computeReport() {
|
||||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||||
@ -307,7 +306,7 @@ export default class AdminReport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._renderReport(report, this.forcedModes, this.currentMode);
|
this._renderReport(report, this.forcedModes, this.currentMode);
|
||||||
}
|
},
|
||||||
|
|
||||||
_renderReport(report, forcedModes, currentMode) {
|
_renderReport(report, forcedModes, currentMode) {
|
||||||
const modes = forcedModes ? forcedModes.split(",") : report.modes;
|
const modes = forcedModes ? forcedModes.split(",") : report.modes;
|
||||||
@ -318,9 +317,11 @@ export default class AdminReport extends Component {
|
|||||||
currentMode,
|
currentMode,
|
||||||
options: this._buildOptions(currentMode, report),
|
options: this._buildOptions(currentMode, report),
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_fetchReport() {
|
_fetchReport() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
this.setProperties({ isLoading: true, rateLimitationString: null });
|
this.setProperties({ isLoading: true, rateLimitationString: null });
|
||||||
|
|
||||||
next(() => {
|
next(() => {
|
||||||
@ -348,7 +349,7 @@ export default class AdminReport extends Component {
|
|||||||
|
|
||||||
ReportLoader.enqueue(this.dataSourceName, payload.data, callback);
|
ReportLoader.enqueue(this.dataSourceName, payload.data, callback);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
_buildPayload(facets) {
|
_buildPayload(facets) {
|
||||||
let payload = { data: { facets } };
|
let payload = { data: { facets } };
|
||||||
@ -374,7 +375,7 @@ export default class AdminReport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return payload;
|
return payload;
|
||||||
}
|
},
|
||||||
|
|
||||||
_buildOptions(mode, report) {
|
_buildOptions(mode, report) {
|
||||||
if (mode === "table") {
|
if (mode === "table") {
|
||||||
@ -392,7 +393,7 @@ export default class AdminReport extends Component {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_loadReport(jsonReport) {
|
_loadReport(jsonReport) {
|
||||||
Report.fillMissingDates(jsonReport, { filledField: "chartData" });
|
Report.fillMissingDates(jsonReport, { filledField: "chartData" });
|
||||||
@ -422,5 +423,5 @@ export default class AdminReport extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Report.create(jsonReport);
|
return Report.create(jsonReport);
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,130 +0,0 @@
|
|||||||
<div class="edit-main-nav admin-controls">
|
|
||||||
<nav>
|
|
||||||
<ul class="nav nav-pills target">
|
|
||||||
{{#each this.visibleTargets as |target|}}
|
|
||||||
<li>
|
|
||||||
<LinkTo
|
|
||||||
@route={{this.editRouteName}}
|
|
||||||
@models={{array this.theme.id target.name this.fieldName}}
|
|
||||||
@replace={{true}}
|
|
||||||
title={{this.field.title}}
|
|
||||||
class={{if target.edited "edited" "blank"}}
|
|
||||||
>
|
|
||||||
{{#if target.error}}{{d-icon "exclamation-triangle"}}{{/if}}
|
|
||||||
{{#if target.icon}}{{d-icon target.icon}}{{/if}}
|
|
||||||
{{i18n (concat "admin.customize.theme." target.name)}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if this.allowAdvanced}}
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
{{on "click" this.toggleShowAdvanced}}
|
|
||||||
href
|
|
||||||
title={{i18n
|
|
||||||
(concat
|
|
||||||
"admin.customize.theme."
|
|
||||||
(if this.showAdvanced "hide_advanced" "show_advanced")
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
class="no-text"
|
|
||||||
>
|
|
||||||
{{d-icon
|
|
||||||
(if this.showAdvanced "angle-double-left" "angle-double-right")
|
|
||||||
}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
||||||
<li class="spacer"></li>
|
|
||||||
<li>
|
|
||||||
<label>
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
@checked={{this.onlyOverridden}}
|
|
||||||
{{on
|
|
||||||
"click"
|
|
||||||
(action this.onlyOverriddenChanged value="target.checked")
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{{i18n "admin.customize.theme.hide_unused_fields"}}
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin-controls">
|
|
||||||
<nav>
|
|
||||||
<ul class="nav nav-pills fields">
|
|
||||||
{{#each this.visibleFields as |field|}}
|
|
||||||
<li>
|
|
||||||
<LinkTo
|
|
||||||
@route={{this.editRouteName}}
|
|
||||||
@models={{array this.theme.id this.currentTargetName field.name}}
|
|
||||||
@replace={{true}}
|
|
||||||
title={{field.title}}
|
|
||||||
class={{if field.edited "edited" "blank"}}
|
|
||||||
>
|
|
||||||
{{#if field.error}}{{d-icon "exclamation-triangle"}}{{/if}}
|
|
||||||
{{#if field.icon}}{{d-icon field.icon}}{{/if}}
|
|
||||||
{{field.translatedName}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#if this.showAddField}}
|
|
||||||
<li>
|
|
||||||
{{#if this.addingField}}
|
|
||||||
<Input
|
|
||||||
@type={{this.text}}
|
|
||||||
@value={{this.newFieldName}}
|
|
||||||
@enter={{action "addField"}}
|
|
||||||
@escape-press={{action "cancelAddField"}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@class="ok"
|
|
||||||
@action={{action "addField" this.newFieldName}}
|
|
||||||
@icon="check"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@class="cancel"
|
|
||||||
@action={{action "cancelAddField"}}
|
|
||||||
@icon="times"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<a href {{on "click" this.toggleAddField}} class="no-text">
|
|
||||||
{{d-icon "plus"}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</li>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<li class="spacer"></li>
|
|
||||||
<li>
|
|
||||||
<a href {{on "click" this.toggleMaximize}} class="no-text">
|
|
||||||
{{d-icon this.maximizeIcon}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.error}}
|
|
||||||
<pre class="field-error">{{this.error}}</pre>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.warning}}
|
|
||||||
<pre class="field-warning">{{html-safe this.warning}}</pre>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<AceEditor
|
|
||||||
@content={{this.activeSection}}
|
|
||||||
@editorId={{this.editorId}}
|
|
||||||
@mode={{this.activeSectionMode}}
|
|
||||||
@autofocus="true"
|
|
||||||
@placeholder={{this.placeholder}}
|
|
||||||
@htmlPlaceholder={{true}}
|
|
||||||
@save={{this.save}}
|
|
||||||
@setWarning={{action "setWarning"}}
|
|
||||||
/>
|
|
||||||
@ -3,13 +3,11 @@ import I18n from "I18n";
|
|||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { fmt } from "discourse/lib/computed";
|
import { fmt } from "discourse/lib/computed";
|
||||||
import { isDocumentRTL } from "discourse/lib/text-direction";
|
import { isDocumentRTL } from "discourse/lib/text-direction";
|
||||||
import { action, computed } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { next } from "@ember/runloop";
|
import { next } from "@ember/runloop";
|
||||||
|
|
||||||
export default class AdminThemeEditor extends Component {
|
export default Component.extend({
|
||||||
warning = null;
|
warning: null,
|
||||||
|
|
||||||
@fmt("fieldName", "currentTargetName", "%@|%@") editorId;
|
|
||||||
|
|
||||||
@discourseComputed("theme.targets", "onlyOverridden", "showAdvanced")
|
@discourseComputed("theme.targets", "onlyOverridden", "showAdvanced")
|
||||||
visibleTargets(targets, onlyOverridden, showAdvanced) {
|
visibleTargets(targets, onlyOverridden, showAdvanced) {
|
||||||
@ -22,7 +20,7 @@ export default class AdminThemeEditor extends Component {
|
|||||||
}
|
}
|
||||||
return target.edited;
|
return target.edited;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentTargetName", "onlyOverridden", "theme.fields")
|
@discourseComputed("currentTargetName", "onlyOverridden", "theme.fields")
|
||||||
visibleFields(targetName, onlyOverridden, fields) {
|
visibleFields(targetName, onlyOverridden, fields) {
|
||||||
@ -31,7 +29,7 @@ export default class AdminThemeEditor extends Component {
|
|||||||
fields = fields.filter((field) => field.edited);
|
fields = fields.filter((field) => field.edited);
|
||||||
}
|
}
|
||||||
return fields;
|
return fields;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentTargetName", "fieldName")
|
@discourseComputed("currentTargetName", "fieldName")
|
||||||
activeSectionMode(targetName, fieldName) {
|
activeSectionMode(targetName, fieldName) {
|
||||||
@ -45,7 +43,7 @@ export default class AdminThemeEditor extends Component {
|
|||||||
return "scss";
|
return "scss";
|
||||||
}
|
}
|
||||||
return fieldName && fieldName.includes("scss") ? "scss" : "html";
|
return fieldName && fieldName.includes("scss") ? "scss" : "html";
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentTargetName", "fieldName")
|
@discourseComputed("currentTargetName", "fieldName")
|
||||||
placeholder(targetName, fieldName) {
|
placeholder(targetName, fieldName) {
|
||||||
@ -60,27 +58,30 @@ export default class AdminThemeEditor extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
},
|
||||||
|
|
||||||
@computed("fieldName", "currentTargetName", "theme")
|
@discourseComputed("fieldName", "currentTargetName", "theme")
|
||||||
get activeSection() {
|
activeSection: {
|
||||||
return this.theme.getField(this.currentTargetName, this.fieldName);
|
get(fieldName, target, model) {
|
||||||
}
|
return model.getField(target, fieldName);
|
||||||
|
},
|
||||||
|
set(value, fieldName, target, model) {
|
||||||
|
model.setField(target, fieldName, value);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
set activeSection(value) {
|
editorId: fmt("fieldName", "currentTargetName", "%@|%@"),
|
||||||
this.theme.setField(this.currentTargetName, this.fieldName, value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed("maximized")
|
@discourseComputed("maximized")
|
||||||
maximizeIcon(maximized) {
|
maximizeIcon(maximized) {
|
||||||
return maximized ? "discourse-compress" : "discourse-expand";
|
return maximized ? "discourse-compress" : "discourse-expand";
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("currentTargetName", "theme.targets")
|
@discourseComputed("currentTargetName", "theme.targets")
|
||||||
showAddField(currentTargetName, targets) {
|
showAddField(currentTargetName, targets) {
|
||||||
return targets.find((t) => t.name === currentTargetName).customNames;
|
return targets.find((t) => t.name === currentTargetName).customNames;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"currentTargetName",
|
"currentTargetName",
|
||||||
@ -89,45 +90,52 @@ export default class AdminThemeEditor extends Component {
|
|||||||
)
|
)
|
||||||
error(target, fieldName) {
|
error(target, fieldName) {
|
||||||
return this.theme.getError(target, fieldName);
|
return this.theme.getError(target, fieldName);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleShowAdvanced(event) {
|
toggleShowAdvanced(event) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
this.toggleProperty("showAdvanced");
|
this.toggleProperty("showAdvanced");
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleAddField(event) {
|
toggleAddField(event) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
this.toggleProperty("addingField");
|
this.toggleProperty("addingField");
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleMaximize(event) {
|
toggleMaximize(event) {
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
this.toggleProperty("maximized");
|
this.toggleProperty("maximized");
|
||||||
next(() => this.appEvents.trigger("ace:resize"));
|
next(() => this.appEvents.trigger("ace:resize"));
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
actions: {
|
||||||
cancelAddField() {
|
cancelAddField() {
|
||||||
this.set("addingField", false);
|
this.set("addingField", false);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
addField(name) {
|
||||||
addField(name) {
|
if (!name) {
|
||||||
if (!name) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
name = name.replace(/[^a-zA-Z0-9-_/]/g, "");
|
||||||
name = name.replace(/[^a-zA-Z0-9-_/]/g, "");
|
this.theme.setField(this.currentTargetName, name, "");
|
||||||
this.theme.setField(this.currentTargetName, name, "");
|
this.setProperties({ newFieldName: "", addingField: false });
|
||||||
this.setProperties({ newFieldName: "", addingField: false });
|
this.fieldAdded(this.currentTargetName, name);
|
||||||
this.fieldAdded(this.currentTargetName, name);
|
},
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
onlyOverriddenChanged(value) {
|
||||||
setWarning(message) {
|
this.onlyOverriddenChanged(value);
|
||||||
this.set("warning", message);
|
},
|
||||||
}
|
|
||||||
}
|
save() {
|
||||||
|
this.attrs.save();
|
||||||
|
},
|
||||||
|
|
||||||
|
setWarning(message) {
|
||||||
|
this.set("warning", message);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@ -1,114 +0,0 @@
|
|||||||
<div class="user-field">
|
|
||||||
{{#if (or this.isEditing (not this.userField.id))}}
|
|
||||||
<AdminFormRow @label="admin.user_fields.type">
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.fieldTypes}}
|
|
||||||
@value={{this.buffered.field_type}}
|
|
||||||
@onChange={{action (mut this.buffered.field_type)}}
|
|
||||||
/>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow @label="admin.user_fields.name">
|
|
||||||
<Input
|
|
||||||
@value={{this.buffered.name}}
|
|
||||||
class="user-field-name"
|
|
||||||
maxlength="255"
|
|
||||||
/>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow @label="admin.user_fields.description">
|
|
||||||
<Input
|
|
||||||
@value={{this.buffered.description}}
|
|
||||||
class="user-field-desc"
|
|
||||||
maxlength="255"
|
|
||||||
/>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
{{#if this.bufferedFieldType.hasOptions}}
|
|
||||||
<AdminFormRow @label="admin.user_fields.options">
|
|
||||||
<ValueList @values={{this.buffered.options}} @inputType="array" />
|
|
||||||
</AdminFormRow>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<AdminFormRow @wrapLabel="true">
|
|
||||||
<Input @type="checkbox" @checked={{this.buffered.editable}} />
|
|
||||||
<span>{{i18n "admin.user_fields.editable.title"}}</span>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow @wrapLabel="true">
|
|
||||||
<Input @type="checkbox" @checked={{this.buffered.required}} />
|
|
||||||
<span>{{i18n "admin.user_fields.required.title"}}</span>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow @wrapLabel="true">
|
|
||||||
<Input @type="checkbox" @checked={{this.buffered.show_on_profile}} />
|
|
||||||
<span>{{i18n "admin.user_fields.show_on_profile.title"}}</span>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow @wrapLabel="true">
|
|
||||||
<Input @type="checkbox" @checked={{this.buffered.show_on_user_card}} />
|
|
||||||
<span>{{i18n "admin.user_fields.show_on_user_card.title"}}</span>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow @wrapLabel="true">
|
|
||||||
<Input @type="checkbox" @checked={{this.buffered.searchable}} />
|
|
||||||
<span>{{i18n "admin.user_fields.searchable.title"}}</span>
|
|
||||||
</AdminFormRow>
|
|
||||||
|
|
||||||
<AdminFormRow>
|
|
||||||
<DButton
|
|
||||||
@action={{action "save"}}
|
|
||||||
@class="btn-primary save"
|
|
||||||
@icon="check"
|
|
||||||
@label="admin.user_fields.save"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@action={{action "cancel"}}
|
|
||||||
@class="btn-danger cancel"
|
|
||||||
@icon="times"
|
|
||||||
@label="admin.user_fields.cancel"
|
|
||||||
/>
|
|
||||||
</AdminFormRow>
|
|
||||||
{{else}}
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-display">
|
|
||||||
<b class="name">{{this.userField.name}}</b>
|
|
||||||
<br />
|
|
||||||
<span class="description">{{html-safe
|
|
||||||
this.userField.description
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
<div class="form-display field-type">{{this.fieldName}}</div>
|
|
||||||
<div class="form-element controls">
|
|
||||||
<DButton
|
|
||||||
@action={{action "edit"}}
|
|
||||||
@class="btn-default edit"
|
|
||||||
@icon="pencil-alt"
|
|
||||||
@label="admin.user_fields.edit"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@action={{this.destroyAction}}
|
|
||||||
@actionParam={{this.userField}}
|
|
||||||
@class="btn-danger cancel"
|
|
||||||
@icon="far-trash-alt"
|
|
||||||
@label="admin.user_fields.delete"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@action={{this.moveUpAction}}
|
|
||||||
@actionParam={{this.userField}}
|
|
||||||
@class="btn-default"
|
|
||||||
@icon="arrow-up"
|
|
||||||
@disabled={{this.cantMoveUp}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@action={{this.moveDownAction}}
|
|
||||||
@actionParam={{this.userField}}
|
|
||||||
@class="btn-default"
|
|
||||||
@icon="arrow-down"
|
|
||||||
@disabled={{this.cantMoveDown}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">{{this.flags}}</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
<span
|
|
||||||
role="button"
|
|
||||||
onclick={{action "deleteWord"}}
|
|
||||||
class="delete-word-record"
|
|
||||||
>{{d-icon "times"}}</span>
|
|
||||||
{{this.word.word}}
|
|
||||||
{{#if (or this.isReplace this.isLink)}}
|
|
||||||
→
|
|
||||||
<span class="replacement">{{this.word.replacement}}</span>
|
|
||||||
{{else if this.isTag}}
|
|
||||||
→
|
|
||||||
{{#each this.tags as |tag|}}
|
|
||||||
<span class="tag">{{tag}}</span>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
{{#if this.isCaseSensitive}}
|
|
||||||
<span class="case-sensitive">{{i18n
|
|
||||||
"admin.watched_words.case_sensitive"
|
|
||||||
}}</span>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,27 +1,23 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import { alias, equal } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
import { alias, equal } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
@classNames("watched-word")
|
export default Component.extend({
|
||||||
export default class AdminWatchedWord extends Component {
|
classNames: ["watched-word"],
|
||||||
@service dialog;
|
dialog: service(),
|
||||||
|
|
||||||
@equal("actionKey", "replace") isReplace;
|
isReplace: equal("actionKey", "replace"),
|
||||||
|
isTag: equal("actionKey", "tag"),
|
||||||
@equal("actionKey", "tag") isTag;
|
isLink: equal("actionKey", "link"),
|
||||||
|
isCaseSensitive: alias("word.case_sensitive"),
|
||||||
@equal("actionKey", "link") isLink;
|
|
||||||
|
|
||||||
@alias("word.case_sensitive") isCaseSensitive;
|
|
||||||
|
|
||||||
@discourseComputed("word.replacement")
|
@discourseComputed("word.replacement")
|
||||||
tags(replacement) {
|
tags(replacement) {
|
||||||
return replacement.split(",");
|
return replacement.split(",");
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
deleteWord() {
|
deleteWord() {
|
||||||
@ -37,5 +33,5 @@ export default class AdminWatchedWord extends Component {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import { alias } from "@ember/object/computed";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classNames: ["hook-event"],
|
||||||
|
typeName: alias("type.name"),
|
||||||
|
|
||||||
|
@discourseComputed("typeName")
|
||||||
|
name(typeName) {
|
||||||
|
return I18n.t(`admin.web_hooks.${typeName}_event.name`);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("typeName")
|
||||||
|
details(typeName) {
|
||||||
|
return I18n.t(`admin.web_hooks.${typeName}_event.details`);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("model.[]", "typeName")
|
||||||
|
eventTypeExists(eventTypes, typeName) {
|
||||||
|
return eventTypes.any((event) => event.name === typeName);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("eventTypeExists")
|
||||||
|
enabled: {
|
||||||
|
get(eventTypeExists) {
|
||||||
|
return eventTypeExists;
|
||||||
|
},
|
||||||
|
set(value, eventTypeExists) {
|
||||||
|
const type = this.type;
|
||||||
|
const model = this.model;
|
||||||
|
// add an association when not exists
|
||||||
|
if (value !== eventTypeExists) {
|
||||||
|
if (value) {
|
||||||
|
model.addObject(type);
|
||||||
|
} else {
|
||||||
|
model.removeObjects(
|
||||||
|
model.filter((eventType) => eventType.name === type.name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
import { ensureJSON, plainJSON, prettyJSON } from "discourse/lib/formatter";
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: "li",
|
||||||
|
expandDetails: null,
|
||||||
|
expandDetailsRequestKey: "request",
|
||||||
|
expandDetailsResponseKey: "response",
|
||||||
|
dialog: service(),
|
||||||
|
|
||||||
|
@discourseComputed("model.status")
|
||||||
|
statusColorClasses(status) {
|
||||||
|
if (!status) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status >= 200 && status <= 299) {
|
||||||
|
return "text-successful";
|
||||||
|
} else {
|
||||||
|
return "text-danger";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("model.created_at")
|
||||||
|
createdAt(createdAt) {
|
||||||
|
return moment(createdAt).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("model.duration")
|
||||||
|
completion(duration) {
|
||||||
|
const seconds = Math.floor(duration / 10.0) / 100.0;
|
||||||
|
return I18n.t("admin.web_hooks.events.completed_in", { count: seconds });
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("expandDetails")
|
||||||
|
expandRequestIcon(expandDetails) {
|
||||||
|
return expandDetails === this.expandDetailsRequestKey
|
||||||
|
? "ellipsis-h"
|
||||||
|
: "ellipsis-v";
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("expandDetails")
|
||||||
|
expandResponseIcon(expandDetails) {
|
||||||
|
return expandDetails === this.expandDetailsResponseKey
|
||||||
|
? "ellipsis-h"
|
||||||
|
: "ellipsis-v";
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
redeliver() {
|
||||||
|
return this.dialog.yesNoConfirm({
|
||||||
|
message: I18n.t("admin.web_hooks.events.redeliver_confirm"),
|
||||||
|
didConfirm: () => {
|
||||||
|
return ajax(
|
||||||
|
`/admin/api/web_hooks/${this.get(
|
||||||
|
"model.web_hook_id"
|
||||||
|
)}/events/${this.get("model.id")}/redeliver`,
|
||||||
|
{ type: "POST" }
|
||||||
|
)
|
||||||
|
.then((json) => {
|
||||||
|
this.set("model", json.web_hook_event);
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleRequest() {
|
||||||
|
const expandDetailsKey = this.expandDetailsRequestKey;
|
||||||
|
|
||||||
|
if (this.expandDetails !== expandDetailsKey) {
|
||||||
|
let headers = Object.assign(
|
||||||
|
{
|
||||||
|
"Request URL": this.get("model.request_url"),
|
||||||
|
"Request method": "POST",
|
||||||
|
},
|
||||||
|
ensureJSON(this.get("model.headers"))
|
||||||
|
);
|
||||||
|
this.setProperties({
|
||||||
|
headers: plainJSON(headers),
|
||||||
|
body: prettyJSON(this.get("model.payload")),
|
||||||
|
expandDetails: expandDetailsKey,
|
||||||
|
bodyLabel: I18n.t("admin.web_hooks.events.payload"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.set("expandDetails", null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleResponse() {
|
||||||
|
const expandDetailsKey = this.expandDetailsResponseKey;
|
||||||
|
|
||||||
|
if (this.expandDetails !== expandDetailsKey) {
|
||||||
|
this.setProperties({
|
||||||
|
headers: plainJSON(this.get("model.response_headers")),
|
||||||
|
body: this.get("model.response_body"),
|
||||||
|
expandDetails: expandDetailsKey,
|
||||||
|
bodyLabel: I18n.t("admin.web_hooks.events.body"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.set("expandDetails", null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
import Component from "@ember/component";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||||
|
import { htmlSafe } from "@ember/template";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classes: ["text-muted", "text-danger", "text-successful", "text-muted"],
|
||||||
|
icons: ["far-circle", "times-circle", "circle", "circle"],
|
||||||
|
circleIcon: null,
|
||||||
|
deliveryStatus: null,
|
||||||
|
|
||||||
|
@discourseComputed("deliveryStatuses", "model.last_delivery_status")
|
||||||
|
status(deliveryStatuses, lastDeliveryStatus) {
|
||||||
|
return deliveryStatuses.find((s) => s.id === lastDeliveryStatus);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("status.id", "icons")
|
||||||
|
icon(statusId, icons) {
|
||||||
|
return icons[statusId - 1];
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("status.id", "classes")
|
||||||
|
class(statusId, classes) {
|
||||||
|
return classes[statusId - 1];
|
||||||
|
},
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.set(
|
||||||
|
"circleIcon",
|
||||||
|
htmlSafe(iconHTML(this.icon, { class: this.class }))
|
||||||
|
);
|
||||||
|
this.set(
|
||||||
|
"deliveryStatus",
|
||||||
|
I18n.t(`admin.web_hooks.delivery_status.${this.get("status.name")}`)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,15 +1,14 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
export default class AdminWrapper extends Component {
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
super.didInsertElement(...arguments);
|
this._super(...arguments);
|
||||||
document.querySelector("html").classList.add("admin-area");
|
document.querySelector("html").classList.add("admin-area");
|
||||||
document.querySelector("body").classList.add("admin-interface");
|
document.querySelector("body").classList.add("admin-interface");
|
||||||
}
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
super.willDestroyElement(...arguments);
|
this._super(...arguments);
|
||||||
document.querySelector("html").classList.remove("admin-area");
|
document.querySelector("html").classList.remove("admin-area");
|
||||||
document.querySelector("body").classList.remove("admin-interface");
|
document.querySelector("body").classList.remove("admin-interface");
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
export default Component.extend({
|
||||||
@tagName("")
|
tagName: "",
|
||||||
export default class CancelLink extends Component {}
|
});
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
{{#if this.onlyHex}}<span class="add-on">#</span>{{/if}}<TextField
|
|
||||||
@class="hex-input"
|
|
||||||
@value={{this.hexValue}}
|
|
||||||
@maxlength={{this.maxlength}}
|
|
||||||
@input={{action "onHexInput" value="target.value"}}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
class="picker"
|
|
||||||
type="color"
|
|
||||||
value={{this.normalizedHexValue}}
|
|
||||||
{{on "input" this.onPickerInput}}
|
|
||||||
/>
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
import { action, computed } from "@ember/object";
|
import { action, computed } from "@ember/object";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { observes } from "@ember-decorators/object";
|
import { observes } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
An input field for a color.
|
An input field for a color.
|
||||||
@ -10,20 +9,20 @@ import { observes } from "@ember-decorators/object";
|
|||||||
@param brightnessValue is a number from 0 to 255 representing the brightness of the color. See ColorSchemeColor.
|
@param brightnessValue is a number from 0 to 255 representing the brightness of the color. See ColorSchemeColor.
|
||||||
@params valid is a boolean indicating if the input field is a valid color.
|
@params valid is a boolean indicating if the input field is a valid color.
|
||||||
**/
|
**/
|
||||||
@classNames("color-picker")
|
export default Component.extend({
|
||||||
export default class ColorInput extends Component {
|
classNames: ["color-picker"],
|
||||||
onlyHex = true;
|
|
||||||
styleSelection = true;
|
|
||||||
|
|
||||||
@computed("onlyHex")
|
onlyHex: true,
|
||||||
get maxlength() {
|
|
||||||
|
styleSelection: true,
|
||||||
|
|
||||||
|
maxlength: computed("onlyHex", function () {
|
||||||
return this.onlyHex ? 6 : null;
|
return this.onlyHex ? 6 : null;
|
||||||
}
|
}),
|
||||||
|
|
||||||
@computed("hexValue")
|
normalizedHexValue: computed("hexValue", function () {
|
||||||
get normalizedHexValue() {
|
|
||||||
return this.normalize(this.hexValue);
|
return this.normalize(this.hexValue);
|
||||||
}
|
}),
|
||||||
|
|
||||||
normalize(color) {
|
normalize(color) {
|
||||||
if (this._valid(color)) {
|
if (this._valid(color)) {
|
||||||
@ -41,19 +40,19 @@ export default class ColorInput extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return color;
|
return color;
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onHexInput(color) {
|
onHexInput(color) {
|
||||||
if (this.attrs.onChangeColor) {
|
if (this.attrs.onChangeColor) {
|
||||||
this.attrs.onChangeColor(this.normalize(color || ""));
|
this.attrs.onChangeColor(this.normalize(color || ""));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
onPickerInput(event) {
|
onPickerInput(event) {
|
||||||
this.set("hexValue", event.target.value.replace("#", ""));
|
this.set("hexValue", event.target.value.replace("#", ""));
|
||||||
}
|
},
|
||||||
|
|
||||||
@observes("hexValue", "brightnessValue", "valid")
|
@observes("hexValue", "brightnessValue", "valid")
|
||||||
hexValueChanged() {
|
hexValueChanged() {
|
||||||
@ -66,9 +65,9 @@ export default class ColorInput extends Component {
|
|||||||
if (this._valid()) {
|
if (this._valid()) {
|
||||||
this.element.querySelector(".picker").value = this.normalize(hex);
|
this.element.querySelector(".picker").value = this.normalize(hex);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_valid(color = this.hexValue) {
|
_valid(color = this.hexValue) {
|
||||||
return /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color);
|
return /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color);
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
<div class="admin-new-feature-item">
|
|
||||||
<div class="new-feature-emoji">{{this.item.emoji}}</div>
|
|
||||||
<div class="new-feature-content">
|
|
||||||
<div class="header">
|
|
||||||
{{#if this.item.link}}
|
|
||||||
<a
|
|
||||||
href={{this.item.link}}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>{{this.item.title}}</a>
|
|
||||||
{{else}}
|
|
||||||
{{this.item.title}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class="feature-description">{{this.item.description}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
|
||||||
export default class DashboardNewFeatureItem extends Component {}
|
export default Component.extend({});
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
{{#if this.newFeatures}}
|
|
||||||
<div class="section-title">
|
|
||||||
<h2>{{replace-emoji (i18n "admin.dashboard.new_features.title")}}</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section-body {{this.columnCountClass}}">
|
|
||||||
{{#each this.newFeatures as |feature|}}
|
|
||||||
<DashboardNewFeatureItem @item={{feature}} @tagName="" />
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
<div class="section-footer">
|
|
||||||
{{#if this.releaseNotesLink}}
|
|
||||||
<a
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
href={{this.releaseNotesLink}}
|
|
||||||
class="btn btn-primary new-features-release-notes"
|
|
||||||
>
|
|
||||||
{{i18n "admin.dashboard.new_features.learn_more"}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
<DButton
|
|
||||||
@label="admin.dashboard.new_features.dismiss"
|
|
||||||
@class="btn-default new-features-dismiss"
|
|
||||||
@action={{this.dismissNewFeatures}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,19 +1,18 @@
|
|||||||
import { classNameBindings, classNames } from "@ember-decorators/component";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action, computed } from "@ember/object";
|
import { action, computed } from "@ember/object";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
@classNames("section", "dashboard-new-features")
|
export default Component.extend({
|
||||||
@classNameBindings("hasUnseenFeatures:ordered-first")
|
newFeatures: null,
|
||||||
export default class DashboardNewFeatures extends Component {
|
classNames: ["section", "dashboard-new-features"],
|
||||||
newFeatures = null;
|
classNameBindings: ["hasUnseenFeatures:ordered-first"],
|
||||||
releaseNotesLink = null;
|
releaseNotesLink: null,
|
||||||
|
|
||||||
constructor() {
|
init() {
|
||||||
super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
ajax("/admin/dashboard/new-features.json").then((json) => {
|
ajax("/admin/dashboard/new-features.json").then((json) => {
|
||||||
if (this.isDestroying || this.isDestroyed) {
|
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,17 +22,16 @@ export default class DashboardNewFeatures extends Component {
|
|||||||
releaseNotesLink: json.release_notes_link,
|
releaseNotesLink: json.release_notes_link,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
@computed("newFeatures")
|
columnCountClass: computed("newFeatures", function () {
|
||||||
get columnCountClass() {
|
|
||||||
return this.newFeatures.length > 2 ? "three-or-more-items" : "";
|
return this.newFeatures.length > 2 ? "three-or-more-items" : "";
|
||||||
}
|
}),
|
||||||
|
|
||||||
@action
|
@action
|
||||||
dismissNewFeatures() {
|
dismissNewFeatures() {
|
||||||
ajax("/admin/dashboard/mark-new-features-as-seen.json", {
|
ajax("/admin/dashboard/mark-new-features-as-seen.json", {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
}).then(() => this.set("hasUnseenFeatures", false));
|
}).then(() => this.set("hasUnseenFeatures", false));
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
{{#if this.foundProblems}}
|
|
||||||
<div class="section dashboard-problems">
|
|
||||||
<div class="section-title">
|
|
||||||
<h2>
|
|
||||||
{{d-icon "heart"}}
|
|
||||||
{{i18n "admin.dashboard.problems_found"}}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section-body">
|
|
||||||
<ConditionalLoadingSection @isLoading={{this.loadingProblems}}>
|
|
||||||
{{#if this.highPriorityProblems.length}}
|
|
||||||
<div class="problem-messages priority-high">
|
|
||||||
<ul>
|
|
||||||
{{#each this.highPriorityProblems as |problem|}}
|
|
||||||
<li
|
|
||||||
class={{concat
|
|
||||||
"dashboard-problem "
|
|
||||||
"priority-"
|
|
||||||
problem.priority
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{{d-icon "exclamation-triangle"}}
|
|
||||||
{{html-safe problem.message}}
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="problem-messages priority-low">
|
|
||||||
<ul>
|
|
||||||
{{#each this.lowPriorityProblems as |problem|}}
|
|
||||||
<li
|
|
||||||
class={{concat
|
|
||||||
"dashboard-problem "
|
|
||||||
"priority-"
|
|
||||||
problem.priority
|
|
||||||
}}
|
|
||||||
>{{html-safe problem.message}}</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="actions">
|
|
||||||
<DButton
|
|
||||||
@action={{this.refreshProblems}}
|
|
||||||
@class="btn-default"
|
|
||||||
@icon="sync"
|
|
||||||
@label="admin.dashboard.refresh_problems"
|
|
||||||
/>
|
|
||||||
{{i18n "admin.dashboard.last_checked"}}:
|
|
||||||
{{this.problemsTimestamp}}
|
|
||||||
</p>
|
|
||||||
</ConditionalLoadingSection>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
|
|
||||||
export default class DashboardProblems extends Component {}
|
export default Component.extend({});
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
<div class="row">
|
|
||||||
<div class="admin-controls">
|
|
||||||
<nav>
|
|
||||||
<ul class="nav nav-pills">
|
|
||||||
<li>
|
|
||||||
<LinkTo
|
|
||||||
@route="adminCustomizeEmailStyle.edit"
|
|
||||||
@model="html"
|
|
||||||
@replace={{true}}
|
|
||||||
>
|
|
||||||
{{i18n "admin.customize.email_style.html"}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<LinkTo
|
|
||||||
@route="adminCustomizeEmailStyle.edit"
|
|
||||||
@model="css"
|
|
||||||
@replace={{true}}
|
|
||||||
>
|
|
||||||
{{i18n "admin.customize.email_style.css"}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AceEditor
|
|
||||||
@content={{this.editorContents}}
|
|
||||||
@mode={{this.currentEditorMode}}
|
|
||||||
@editorId={{this.editorId}}
|
|
||||||
@save={{@save}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="admin-footer">
|
|
||||||
<div class="buttons">
|
|
||||||
<DButton
|
|
||||||
@action={{action "reset"}}
|
|
||||||
@disabled={{this.resetDisabled}}
|
|
||||||
@class="btn-default"
|
|
||||||
@label="admin.customize.email_style.reset"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@ -1,19 +1,17 @@
|
|||||||
import { action, computed } from "@ember/object";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import { reads } from "@ember/object/computed";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { reads } from "@ember/object/computed";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
export default class EmailStylesEditor extends Component {
|
export default Component.extend({
|
||||||
@service dialog;
|
dialog: service(),
|
||||||
|
editorId: reads("fieldName"),
|
||||||
@reads("fieldName") editorId;
|
|
||||||
|
|
||||||
@discourseComputed("fieldName")
|
@discourseComputed("fieldName")
|
||||||
currentEditorMode(fieldName) {
|
currentEditorMode(fieldName) {
|
||||||
return fieldName === "css" ? "scss" : fieldName;
|
return fieldName === "css" ? "scss" : fieldName;
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("fieldName", "styles.html", "styles.css")
|
@discourseComputed("fieldName", "styles.html", "styles.css")
|
||||||
resetDisabled(fieldName) {
|
resetDisabled(fieldName) {
|
||||||
@ -21,31 +19,36 @@ export default class EmailStylesEditor extends Component {
|
|||||||
this.get(`styles.${fieldName}`) ===
|
this.get(`styles.${fieldName}`) ===
|
||||||
this.get(`styles.default_${fieldName}`)
|
this.get(`styles.default_${fieldName}`)
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
|
|
||||||
@computed("styles", "fieldName")
|
@discourseComputed("styles", "fieldName")
|
||||||
get editorContents() {
|
editorContents: {
|
||||||
return this.styles[this.fieldName];
|
get(styles, fieldName) {
|
||||||
}
|
return styles[fieldName];
|
||||||
|
},
|
||||||
|
set(value, styles, fieldName) {
|
||||||
|
styles.setField(fieldName, value);
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
set editorContents(value) {
|
actions: {
|
||||||
this.styles.setField(this.fieldName, value);
|
reset() {
|
||||||
return value;
|
this.dialog.yesNoConfirm({
|
||||||
}
|
message: I18n.t("admin.customize.email_style.reset_confirm", {
|
||||||
|
fieldName: I18n.t(`admin.customize.email_style.${this.fieldName}`),
|
||||||
@action
|
}),
|
||||||
reset() {
|
didConfirm: () => {
|
||||||
this.dialog.yesNoConfirm({
|
this.styles.setField(
|
||||||
message: I18n.t("admin.customize.email_style.reset_confirm", {
|
this.fieldName,
|
||||||
fieldName: I18n.t(`admin.customize.email_style.${this.fieldName}`),
|
this.styles.get(`default_${this.fieldName}`)
|
||||||
}),
|
);
|
||||||
didConfirm: () => {
|
this.notifyPropertyChange("editorContents");
|
||||||
this.styles.setField(
|
},
|
||||||
this.fieldName,
|
});
|
||||||
this.styles.get(`default_${this.fieldName}`)
|
},
|
||||||
);
|
save() {
|
||||||
this.notifyPropertyChange("editorContents");
|
this.attrs.save();
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
{{#if this.editing}}
|
|
||||||
<td class="editing-input">
|
|
||||||
<div class="label">{{i18n "admin.embedding.host"}}</div>
|
|
||||||
<Input
|
|
||||||
@value={{this.buffered.host}}
|
|
||||||
placeholder="example.com"
|
|
||||||
@enter={{action "save"}}
|
|
||||||
class="host-name"
|
|
||||||
autofocus={{true}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="editing-input">
|
|
||||||
<div class="label">{{i18n "admin.embedding.allowed_paths"}}</div>
|
|
||||||
<Input
|
|
||||||
@value={{this.buffered.allowed_paths}}
|
|
||||||
placeholder="/blog/.*"
|
|
||||||
@enter={{action "save"}}
|
|
||||||
class="path-allowlist"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="editing-input">
|
|
||||||
<div class="label">{{i18n "admin.embedding.category"}}</div>
|
|
||||||
<CategoryChooser
|
|
||||||
@value={{this.categoryId}}
|
|
||||||
@class="small"
|
|
||||||
@onChange={{action (mut this.categoryId)}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="editing-controls">
|
|
||||||
<DButton
|
|
||||||
@icon="check"
|
|
||||||
@action={{action "save"}}
|
|
||||||
@class="btn-primary"
|
|
||||||
@disabled={{this.cantSave}}
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@icon="times"
|
|
||||||
@action={{action "cancel"}}
|
|
||||||
@class="btn-danger"
|
|
||||||
@disabled={{this.host.isSaving}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{{else}}
|
|
||||||
<td>
|
|
||||||
<div class="label">{{i18n "admin.embedding.host"}}</div>
|
|
||||||
{{this.host.host}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="label">
|
|
||||||
{{i18n "admin.embedding.allowed_paths"}}
|
|
||||||
</div>
|
|
||||||
{{this.host.allowed_paths}}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="label">{{i18n "admin.embedding.category"}}</div>
|
|
||||||
{{category-badge this.host.category allowUncategorized=true}}
|
|
||||||
</td>
|
|
||||||
<td class="controls">
|
|
||||||
<DButton @icon="pencil-alt" @action={{action "edit"}} />
|
|
||||||
<DButton
|
|
||||||
@icon="far-trash-alt"
|
|
||||||
@action={{action "delete"}}
|
|
||||||
@class="btn-danger"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
@ -1,91 +1,85 @@
|
|||||||
import { action } from "@ember/object";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import { inject as service } from "@ember/service";
|
|
||||||
import { or } from "@ember/object/computed";
|
|
||||||
import Category from "discourse/models/category";
|
import Category from "discourse/models/category";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
|
import { or } from "@ember/object/computed";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
@tagName("tr")
|
export default Component.extend(bufferedProperty("host"), {
|
||||||
export default class EmbeddableHost extends Component.extend(
|
editToggled: false,
|
||||||
bufferedProperty("host")
|
tagName: "tr",
|
||||||
) {
|
categoryId: null,
|
||||||
@service dialog;
|
category: null,
|
||||||
editToggled = false;
|
dialog: service(),
|
||||||
categoryId = null;
|
|
||||||
category = null;
|
|
||||||
|
|
||||||
@or("host.isNew", "editToggled") editing;
|
editing: or("host.isNew", "editToggled"),
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
const host = this.host;
|
const host = this.host;
|
||||||
const categoryId = host.category_id || this.site.uncategorized_category_id;
|
const categoryId = host.category_id || this.site.uncategorized_category_id;
|
||||||
const category = Category.findById(categoryId);
|
const category = Category.findById(categoryId);
|
||||||
|
|
||||||
host.set("category", category);
|
host.set("category", category);
|
||||||
}
|
},
|
||||||
|
|
||||||
@discourseComputed("buffered.host", "host.isSaving")
|
@discourseComputed("buffered.host", "host.isSaving")
|
||||||
cantSave(host, isSaving) {
|
cantSave(host, isSaving) {
|
||||||
return isSaving || isEmpty(host);
|
return isSaving || isEmpty(host);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
actions: {
|
||||||
edit() {
|
edit() {
|
||||||
this.set("categoryId", this.get("host.category.id"));
|
this.set("categoryId", this.get("host.category.id"));
|
||||||
this.set("editToggled", true);
|
this.set("editToggled", true);
|
||||||
}
|
},
|
||||||
|
|
||||||
@action
|
save() {
|
||||||
save() {
|
if (this.cantSave) {
|
||||||
if (this.cantSave) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const props = this.buffered.getProperties(
|
const props = this.buffered.getProperties(
|
||||||
"host",
|
"host",
|
||||||
"allowed_paths",
|
"allowed_paths",
|
||||||
"class_name"
|
"class_name"
|
||||||
);
|
);
|
||||||
props.category_id = this.categoryId;
|
props.category_id = this.categoryId;
|
||||||
|
|
||||||
const host = this.host;
|
const host = this.host;
|
||||||
|
|
||||||
host
|
host
|
||||||
.save(props)
|
.save(props)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
host.set("category", Category.findById(this.categoryId));
|
host.set("category", Category.findById(this.categoryId));
|
||||||
|
this.set("editToggled", false);
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
},
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
return this.dialog.confirm({
|
||||||
|
message: I18n.t("admin.embedding.confirm_delete"),
|
||||||
|
didConfirm: () => {
|
||||||
|
return this.host.destroyRecord().then(() => {
|
||||||
|
this.deleteHost(this.host);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
const host = this.host;
|
||||||
|
if (host.get("isNew")) {
|
||||||
|
this.deleteHost(host);
|
||||||
|
} else {
|
||||||
|
this.rollbackBuffer();
|
||||||
this.set("editToggled", false);
|
this.set("editToggled", false);
|
||||||
})
|
}
|
||||||
.catch(popupAjaxError);
|
},
|
||||||
}
|
},
|
||||||
|
});
|
||||||
@action
|
|
||||||
delete() {
|
|
||||||
return this.dialog.confirm({
|
|
||||||
message: I18n.t("admin.embedding.confirm_delete"),
|
|
||||||
didConfirm: () => {
|
|
||||||
return this.host.destroyRecord().then(() => {
|
|
||||||
this.deleteHost(this.host);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
cancel() {
|
|
||||||
const host = this.host;
|
|
||||||
if (host.get("isNew")) {
|
|
||||||
this.deleteHost(host);
|
|
||||||
} else {
|
|
||||||
this.rollbackBuffer();
|
|
||||||
this.set("editToggled", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
{{#if this.isCheckbox}}
|
|
||||||
<label for={{this.inputId}}>
|
|
||||||
<Input @checked={{this.checked}} id={{this.inputId}} @type="checkbox" />
|
|
||||||
{{i18n this.translationKey}}
|
|
||||||
</label>
|
|
||||||
{{else}}
|
|
||||||
<label for={{this.inputId}}>{{i18n this.translationKey}}</label>
|
|
||||||
<Input
|
|
||||||
@value={{this.value}}
|
|
||||||
id={{this.inputId}}
|
|
||||||
placeholder={{this.placeholder}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user