Compare commits

..

34 Commits

Author SHA1 Message Date
Leonardo Mosquera
8c34d937b3
Add support for suspension fields 2023-02-13 23:06:01 +00:00
Leonardo Mosquera
f1a184eaff
WIP - add FIXMEs to fix progress reporting for records without imported_id 2023-02-13 23:05:54 +00:00
Leonardo Mosquera
1630bb28d9
Clarify that import:ensure_consistency will be run
Since the base bulk importer says "now run that task", and it isn't
obvious that it is already run at the end.
2022-11-15 20:56:43 +00:00
Leonardo Mosquera
ae6b038154
Import views for topics 2022-11-15 19:02:18 +00:00
Leonardo Mosquera
648b970bcf
Import user name as well 2022-04-21 23:35:15 +00:00
Leonardo Mosquera
4feba63d15
Import tags as well 2022-04-21 23:35:15 +00:00
cocococosti
19a2904459
Fix queries format 2022-04-21 19:15:42 -04:00
cocococosti
88448c7236
Adding support for User Stats import to the generic importer 2022-04-21 18:56:15 -04:00
cocococosti
a4f84257e4
Adding support to import Admin and Moderators to the generic importer 2022-04-21 18:56:15 -04:00
cocococosti
cdd612b1da
Adding support for PMs import to the generic importer 2022-04-21 18:56:15 -04:00
cocococosti
095dc0019a
Adding fixes to make the generic importer work with the YAFNET converter, and adding support for Likes import 2022-04-21 18:55:33 -04:00
cocococosti
4500419f14
Revert "Try to make bulk imports work with strings as import_id"
This reverts commit 380424baef.
2022-01-24 23:24:26 -04:00
Gerhard Schlager
c7ddb4f196 Quick & dirty rake task for generating avatars from SSO 2021-12-13 16:22:22 +01:00
Gerhard Schlager
c0ad2adeef Merge remote-tracking branch 'origin/main' into generic-import 2021-12-13 12:52:57 +01:00
Gerhard Schlager
3ae2011e47 Merge branch 'main' into generic-import 2021-12-01 15:31:44 +01:00
Gerhard Schlager
53dcf67362 First post is now stored in the posts table 2021-11-25 16:44:38 +01:00
Gerhard Schlager
a6c7b9949b No rate limit? 2021-11-18 21:27:57 +01:00
Gerhard Schlager
ae9db01c76 Close topics [skip ci] 2021-11-17 17:16:08 +01:00
Gerhard Schlager
0098836e10 Merge branch 'main' into generic-import 2021-11-17 16:56:53 +01:00
Gerhard Schlager
47ea61c36c Fix more problems 2021-11-17 16:56:23 +01:00
Gerhard Schlager
961b5ab848 Fix problems 2021-11-17 01:36:38 +01:00
Gerhard Schlager
380424baef Try to make bulk imports work with strings as import_id 2021-11-17 00:49:20 +01:00
Gerhard Schlager
7376205a74 Trying to get the generic bulk import working 2021-11-17 00:11:22 +01:00
Gerhard Schlager
9e93f089c1 Download avatars from URL in SSO record and create missing user profiles 2021-11-16 17:22:33 +01:00
Gerhard Schlager
d836638758 Import SSO records in generic bulk import 2021-11-16 17:21:37 +01:00
Gerhard Schlager
1c31741a0b Merge branch 'main' into generic-import 2021-11-11 15:24:06 +01:00
Gerhard Schlager
dc821493f8 Create SSO records, import avatars from URL 2021-11-11 00:21:56 +01:00
Gerhard Schlager
29b431cff2 Merge branch 'main' into generic-import 2021-11-09 15:30:43 +01:00
Gerhard Schlager
9e0ac2cc92 WIP 2021-10-31 15:48:26 +01:00
Gerhard Schlager
fd8e8e362e WIP 2021-10-31 15:48:25 +01:00
Gerhard Schlager
4d69eae996 Allow importing of users without full name 2021-10-31 15:48:24 +01:00
Gerhard Schlager
59223b283a WIP bulk importer 2021-10-31 15:48:21 +01:00
Gerhard Schlager
12295e1f7a Small action posts weren't found on incremental import 2021-10-31 15:47:29 +01:00
Gerhard Schlager
ea204ac12f First attempt to create a generic import script 2021-10-31 15:47:28 +01:00
22209 changed files with 355676 additions and 564050 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,16 +1,13 @@
{
"name": "Discourse",
"image": "discourse/discourse_dev:release",
"workspaceMount": "source=${localWorkspaceFolder}/../..,target=/var/www/discourse,type=bind",
"workspaceFolder": "/var/www/discourse",
"settings": {
"search.followSymlinks": false
},
"postStartCommand": "sudo /sbin/boot",
"extensions": ["rebornix.Ruby"],
"forwardPorts": [9292],
"remoteUser": "discourse",
"remoteEnv": {
"DISCOURSE_DEV_HOSTS": ".githubpreview.dev"
}
}
"name": "Discourse",
"image": "discourse/discourse_dev:release",
"workspaceMount": "source=${localWorkspaceFolder}/../..,target=/var/www/discourse,type=bind",
"workspaceFolder": "/var/www/discourse",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"postCreateCommand": "sudo /sbin/boot",
"extensions": ["rebornix.Ruby"],
"forwardPorts": [9292],
"remoteUser": "discourse"
}

View File

@ -12,6 +12,3 @@ indent_size = 2
[*.md]
trim_trailing_whitespace = false
[*.hbs]
insert_final_newline = false

View File

@ -1,15 +1,18 @@
app/assets/javascripts/browser-update.js
app/assets/javascripts/discourse-loader.js
app/assets/javascripts/env.js
app/assets/javascripts/main_include_admin.js
app/assets/javascripts/vendor.js
app/assets/javascripts/locales/i18n.js
app/assets/javascripts/ember-addons/
app/assets/javascripts/discourse/lib/autosize.js
lib/javascripts/locale/
lib/javascripts/messageformat.js
lib/javascripts/messageformat-lookup.js
lib/pretty_text/
lib/highlight_js/
plugins/**/lib/javascripts/locale
public/
vendor/
app/assets/javascripts/discourse/tests/test_helper.js
app/assets/javascripts/discourse/tests/fixtures
node_modules/
spec/
dist/
tmp/
documentation/

View File

@ -2,8 +2,7 @@
"extends": "eslint-config-discourse",
"rules": {
"discourse-ember/global-ember": 2,
"eol-last": 2,
"no-restricted-globals": 0
"eol-last": 2
},
"globals": {
"_": "off",
@ -17,12 +16,10 @@
"currentURL": "off",
"currentUser": "off",
"Discourse": "off",
"Ember": "off",
"exists": "off",
"fillIn": "off",
"find": "off",
"getSettledState": "off",
"globalThis": "readonly",
"hasModule": "off",
"invisible": "off",
"jQuery": "off",

View File

@ -53,21 +53,5 @@ ce3fe2f4c4ddf166949ee3cec3d9ecbf9108ab52
# DEV: Tidy up imports. (#11364)
1c2358ba162eb9f9ba9095c9afe30cf51dd85e04
# DEV: Sort imports alphabetically (#11382)
# DEV: Sort imports alphabetically (#11382)
bbe5d8d5cf1220165842985c0e2cd4c454d501cd
# DEV: Template colocation for sidebar files
95c7cdab941a56686ac5831d2a5c5eca38d780c5
# DEV: Apply prettier to hbs files
c8e2e37fa77d3c3c69c7572866017e9bb92befa3
# DEV: Apply syntax_tree to...
5a003715d366e1d871f9fcb0656dc9e23e9c2259
64171730827c58df26a7ad75f0e58f17c2add118
b0fda61a8e75c81e3458c8af9d2afe9d32183457
cb932d6ee1b3b3571e4d4d9118635e2dbf58f0ef
0cf6421716d0908da57ad7743a2decb08588b48a
7c77cc6a580d7cb49f8c19ceee8cfdd08862259d
436b3b392b9c917510d4ff0d73a5167cd3eb936c
055310cea496519a996b9c3bf4dc7e716cfe62ba

View File

@ -1,50 +1,42 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: bundler
directory: "/"
schedule:
interval: daily
time: "08:00"
timezone: Australia/Sydney
open-pull-requests-limit: 20
versioning-strategy: lockfile-only
allow:
- dependency-type: direct
- dependency-type: indirect
ignore:
- dependency-name: aws-partitions
versions:
- "> 1.329.0"
- "< 2"
- dependency-name: aws-sdk-core
versions:
- "> 3.99.1"
- "< 4"
- dependency-name: aws-sdk-kms
versions:
- "> 1.31.0"
- "< 2"
- dependency-name: aws-sdk-s3
versions:
- "> 1.66.0"
- "< 2"
- dependency-name: aws-sdk-sns
versions:
- "> 1.25.1"
- "< 2"
- dependency-name: aws-sigv4
versions:
- "> 1.2.0"
- "< 2"
- package-ecosystem: "npm"
directory: "/app/assets/javascripts/"
schedule:
interval: daily
time: "08:00"
timezone: Australia/Sydney
open-pull-requests-limit: 20
versioning-strategy: increase
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: bundler
directory: "/"
schedule:
interval: daily
time: "08:00"
timezone: Australia/Sydney
open-pull-requests-limit: 10
versioning-strategy: lockfile-only
allow:
- dependency-type: direct
- dependency-type: indirect
ignore:
- dependency-name: aws-partitions
versions:
- "> 1.329.0"
- "< 2"
- dependency-name: aws-sdk-core
versions:
- "> 3.99.1"
- "< 4"
- dependency-name: aws-sdk-kms
versions:
- "> 1.31.0"
- "< 2"
- dependency-name: aws-sdk-s3
versions:
- "> 1.66.0"
- "< 2"
- dependency-name: aws-sdk-sns
versions:
- "> 1.25.1"
- "< 2"
- dependency-name: aws-sigv4
versions:
- "> 1.2.0"
- "< 2"

2
.github/labeler.yml vendored
View File

@ -1,2 +0,0 @@
chat:
- plugins/chat/**/*

71
.github/workflows/ember.yml vendored Normal file
View File

@ -0,0 +1,71 @@
name: (experimental) Ember CLI tests (core)
on:
pull_request:
push:
branches:
- main
concurrency:
group: ember-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
cancel-in-progress: true
jobs:
build:
name: run
runs-on: ubuntu-latest
container: discourse/discourse_test:release
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
browser: ["Chrome", "Firefox", "Headless Firefox"]
steps:
- uses: actions/checkout@master
with:
fetch-depth: 1
- name: Setup Git
run: |
git config --global user.email "ci@ci.invalid"
git config --global user.name "Discourse CI"
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install
working-directory: ./app/assets/javascripts/discourse
run: yarn install
- name: Ember Build
working-directory: ./app/assets/javascripts/discourse
run: |
sudo -E -u discourse mkdir /tmp/emberbuild
sudo -E -u discourse -H yarn ember build --environment=test -o /tmp/emberbuild
- name: Core QUnit 1
working-directory: ./app/assets/javascripts/discourse
run: sudo -E -u discourse -H yarn ember exam --path /tmp/emberbuild --split=3 --partition=1 --launch "${{ matrix.browser }}"
timeout-minutes: 60
- name: Core QUnit 2
working-directory: ./app/assets/javascripts/discourse
run: sudo -E -u discourse -H yarn ember exam --path /tmp/emberbuild --split=3 --partition=2 --launch "${{ matrix.browser }}"
timeout-minutes: 60
- name: Core QUnit 3
working-directory: ./app/assets/javascripts/discourse
run: sudo -E -u discourse -H yarn ember exam --path /tmp/emberbuild --split=3 --partition=3 --launch "${{ matrix.browser }}"
timeout-minutes: 60

View File

@ -0,0 +1,49 @@
name: (experimental) Ember CLI tests (plugins)
on:
schedule:
- cron: "0 0 * * *"
jobs:
build:
name: run
runs-on: ubuntu-latest
container: discourse/discourse_test:release
timeout-minutes: 60
steps:
- uses: actions/checkout@master
with:
fetch-depth: 1
- name: Setup Git
run: |
git config --global user.email "ci@ci.invalid"
git config --global user.name "Discourse CI"
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Official Plugins Install
run: |
bundle config --local path vendor/bundle
bundle config --local deployment true
bundle config --local without development
bundle install --jobs 4
bundle clean
bundle exec rake plugin:install_all_official
- name: QUnit
working-directory: ./app/assets/javascripts/discourse
run: QUNIT_EMBER_CLI=1 sudo -E -u discourse -H rake plugin:qunit
timeout-minutes: 60

View File

@ -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 }}"

View File

@ -1,87 +0,0 @@
name: Licenses
on:
pull_request:
push:
branches:
- main
concurrency:
group: licenses-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
name: run
runs-on: ubuntu-latest
container: discourse/discourse_test:slim
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Setup Git
run: |
git config --global user.email "ci@ci.invalid"
git config --global user.name "Discourse CI"
- name: Bundler cache
uses: actions/cache@v3
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gem-
- name: Setup gems
run: |
bundle config --local path vendor/bundle
bundle config --local deployment true
bundle config --local without development
bundle install --jobs 4
bundle clean
- name: Setup licensed
run: |
gem install licensed
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Yarn cache
uses: actions/cache@v3
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Check RubyGems Licenses
if: ${{ !cancelled() }}
run: |
licensed cache
licensed status
- name: Yarn install
run: yarn install
- name: Check Yarn Licenses
if: ${{ !cancelled() }}
run: |
yarn global add licensee
yarn global upgrade licensee
licensee --errors-only
- name: Check Ember CLI Workspace Licenses
if: ${{ !cancelled() }}
working-directory: ./app/assets/javascripts
run: |
licensee --errors-only

View File

@ -10,22 +10,15 @@ concurrency:
group: linting-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
name: run
runs-on: ubuntu-latest
container: discourse/discourse_test:slim
container: discourse/discourse_test:release
timeout-minutes: 30
steps:
- name: Set working directory owner
run: chown root:root .
- uses: actions/checkout@v3
- uses: actions/checkout@master
with:
fetch-depth: 1
@ -35,7 +28,7 @@ jobs:
git config --global user.name "Discourse CI"
- name: Bundler cache
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
@ -44,7 +37,6 @@ jobs:
- name: Setup gems
run: |
gem install bundler --conservative -v $(awk '/BUNDLED WITH/ { getline; gsub(/ /,""); print $0 }' Gemfile.lock)
bundle config --local path vendor/bundle
bundle config --local deployment true
bundle config --local without development
@ -53,10 +45,10 @@ jobs:
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn cache
uses: actions/cache@v3
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
@ -68,47 +60,38 @@ jobs:
run: yarn install
- name: Rubocop
if: ${{ !cancelled() }}
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')
if: ${{ always() }}
run: bundle exec rubocop .
- name: ESLint (core)
if: ${{ !cancelled() }}
if: ${{ always() }}
run: yarn eslint app/assets/javascripts
- name: ESLint (core plugins)
if: ${{ !cancelled() }}
if: ${{ always() }}
run: yarn eslint plugins
- name: Prettier
if: ${{ !cancelled() }}
if: ${{ always() }}
run: |
yarn prettier -v
yarn pprettier --list-different \
yarn prettier --list-different \
"app/assets/stylesheets/**/*.scss" \
"app/assets/javascripts/**/*.js" \
"app/assets/javascripts/**/*.hbs" \
"plugins/**/assets/stylesheets/**/*.scss" \
"plugins/**/assets/javascripts/**/*.js" \
"plugins/**/assets/javascripts/**/*.hbs" \
"plugins/**/assets/javascripts/**/*.js"
- name: Ember template lint
if: ${{ !cancelled() }}
if: ${{ always() }}
run: |
yarn ember-template-lint \
--no-error-on-unmatched-pattern \
"app/assets/javascripts/**/*.hbs" \
"plugins/**/assets/javascripts/**/*.hbs"
app/assets/javascripts \
plugins/**/assets/javascripts
- name: English locale lint (core)
if: ${{ !cancelled() }}
if: ${{ always() }}
run: bundle exec ruby script/i18n_lint.rb "config/**/locales/{client,server}.en.yml"
- name: English locale lint (core plugins)
if: ${{ !cancelled() }}
if: ${{ always() }}
run: bundle exec ruby script/i18n_lint.rb "plugins/**/locales/{client,server}.en.yml"

View File

@ -5,51 +5,55 @@ on:
push:
branches:
- main
- beta
- stable
concurrency:
group: tests-${{ format('{0}-{1}', github.head_ref || github.run_number, github.job) }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
name: ${{ matrix.target }} ${{ matrix.build_type }} ${{ matrix.ruby }}
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' || '' }}
timeout-minutes: 20
name: ${{ matrix.target }} ${{ matrix.build_type }}
runs-on: ubuntu-latest
container: discourse/discourse_test:release
timeout-minutes: 60
env:
DISCOURSE_HOSTNAME: www.example.com
RUBY_GLOBAL_METHOD_CACHE_SIZE: 131072
RAILS_ENV: test
PGHOST: postgres
PGUSER: discourse
PGPASSWORD: discourse
USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }}
CAPBYARA_DEFAULT_MAX_WAIT_TIME: 4
strategy:
fail-fast: false
matrix:
build_type: [backend, frontend, system, annotations]
build_type: [backend, frontend, annotations]
target: [core, plugins]
ruby: ['3.2']
postgres: ["13"]
exclude:
- build_type: annotations
target: plugins
- build_type: frontend
target: core # Handled by core_frontend_tests job (below)
services:
postgres:
image: postgres:${{ matrix.postgres }}
ports:
- 5432:5432
env:
POSTGRES_USER: discourse
POSTGRES_PASSWORD: discourse
POSTGRES_DB: discourse_test
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Set working directory owner
run: chown root:root .
- uses: actions/checkout@v3
- uses: actions/checkout@master
with:
fetch-depth: 1
@ -62,23 +66,16 @@ jobs:
run: |
redis-server /etc/redis/redis.conf &
- name: Start Postgres
run: |
chown -R postgres /var/run/postgresql
sudo -E -u postgres script/start_test_db.rb
sudo -u postgres psql -c "CREATE ROLE $PGUSER LOGIN SUPERUSER PASSWORD '$PGPASSWORD';"
- name: Bundler cache
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-${{ matrix.ruby }}-gem-${{ hashFiles('**/Gemfile.lock') }}
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.ruby }}-gem-
${{ runner.os }}-gem-
- name: Setup gems
run: |
gem install bundler --conservative -v $(awk '/BUNDLED WITH/ { getline; gsub(/ /,""); print $0 }' Gemfile.lock)
bundle config --local path vendor/bundle
bundle config --local deployment true
bundle config --local without development
@ -87,10 +84,10 @@ jobs:
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn cache
uses: actions/cache@v3
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
@ -105,112 +102,40 @@ jobs:
if: matrix.target == 'plugins'
run: bin/rake plugin:install_all_official
- name: Pull compatible versions of plugins
if: matrix.target == 'plugins'
run: bin/rake plugin:pull_compatible_all
- name: Fetch app state cache
uses: actions/cache@v3
id: app-cache
with:
path: tmp/app-cache
key: >- # postgres version, hash of migrations, "parallel?"
${{ runner.os }}-
${{ hashFiles('.github/workflows/tests.yml') }}-
${{ matrix.postgres }}-
${{ hashFiles('db/**/*', 'plugins/**/db/**/*') }}-
${{ env.USES_PARALLEL_DATABASES }}
- name: Restore database from cache
if: steps.app-cache.outputs.cache-hit == 'true'
run: psql -f tmp/app-cache/cache.sql postgres
- name: Restore uploads from cache
if: steps.app-cache.outputs.cache-hit == 'true'
run: rm -rf public/uploads && cp -r tmp/app-cache/uploads public/uploads
- name: Create and migrate database
if: steps.app-cache.outputs.cache-hit != 'true'
- name: Create database
run: |
bin/rake db:create
bin/rake db:migrate
- name: Create and migrate parallel databases
if: >-
env.USES_PARALLEL_DATABASES == 'true' &&
steps.app-cache.outputs.cache-hit != 'true'
- name: Create parallel databases
if: matrix.build_type == 'backend' && matrix.target == 'core'
run: |
bin/rake parallel:create
bin/rake parallel:migrate
- name: Dump database for cache
if: steps.app-cache.outputs.cache-hit != 'true'
run: mkdir -p tmp/app-cache && pg_dumpall > tmp/app-cache/cache.sql
- name: Dump uploads for cache
if: steps.app-cache.outputs.cache-hit != 'true'
run: rm -rf tmp/app-cache/uploads && cp -r public/uploads tmp/app-cache/uploads
- name: Fetch turbo_rspec_runtime.log cache
uses: actions/cache@v3
id: test-runtime-cache
if: matrix.build_type == 'backend' && matrix.target == 'core'
with:
path: tmp/turbo_rspec_runtime.log
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
if: matrix.build_type == 'backend' && matrix.target == 'core'
run: bin/turbo_rspec --verbose
- name: Plugin RSpec
if: matrix.build_type == 'backend' && matrix.target == 'plugins'
run: bin/rake plugin:turbo_spec
run: bin/rake plugin:spec
- name: Core QUnit
if: matrix.build_type == 'frontend' && matrix.target == 'core'
run: bin/rake qunit:test['1200000']
timeout-minutes: 30
- name: Wizard QUnit
if: matrix.build_type == 'frontend' && matrix.target == 'core'
run: bin/rake qunit:test['600000','/wizard/qunit']
timeout-minutes: 10
- name: Plugin QUnit
if: matrix.build_type == 'frontend' && matrix.target == 'plugins'
run: QUNIT_PARALLEL=3 bin/rake plugin:qunit['*','1200000']
run: bin/rake plugin:qunit['*','1200000']
timeout-minutes: 30
- name: Ember Build for System Tests
if: matrix.build_type == 'system'
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
if: matrix.build_type == 'system' && matrix.target == 'core'
run: bin/rspec spec/system
- name: Plugin System Tests
if: matrix.build_type == 'system' && matrix.target == 'plugins'
run: LOAD_PLUGINS=1 bin/rspec plugins/*/spec/system
- name: Upload failed system test screenshots
uses: actions/upload-artifact@v3
if: matrix.build_type == 'system' && failure()
with:
name: failed-system-test-screenshots
path: tmp/capybara/*.png
- name: Check Annotations
if: matrix.build_type == 'annotations'
run: |
@ -227,66 +152,3 @@ jobs:
exit 1
fi
timeout-minutes: 30
core_frontend_tests:
if: "!(github.event_name == 'push' && github.repository == 'discourse/discourse-private-mirror')"
name: core frontend (${{ matrix.browser }})
runs-on: ubuntu-20.04-8core
container:
image: discourse/discourse_test:slim-browsers
options: --user discourse
timeout-minutes: 35
strategy:
fail-fast: false
matrix:
browser: ["Chrome", "Firefox ESR", "Firefox Evergreen"]
env:
TESTEM_BROWSER: ${{ (startsWith(matrix.browser, 'Firefox') && 'Firefox') || matrix.browser }}
TESTEM_FIREFOX_PATH: ${{ (matrix.browser == 'Firefox Evergreen') && '/opt/firefox-evergreen/firefox' }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Setup Git
run: |
git config --global user.email "ci@ci.invalid"
git config --global user.name "Discourse CI"
- name: Get yarn cache directory
id: yarn-cache-dir
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Yarn cache
uses: actions/cache@v3
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install
working-directory: ./app/assets/javascripts/discourse
run: yarn install
- name: Ember Build
working-directory: ./app/assets/javascripts/discourse
run: |
mkdir /tmp/emberbuild
yarn ember build --environment=test -o /tmp/emberbuild
- name: Core QUnit
working-directory: ./app/assets/javascripts/discourse
run: yarn ember exam --path /tmp/emberbuild --load-balance --parallel=5 --launch "${{ env.TESTEM_BROWSER }}" --write-execution-file --random
timeout-minutes: 15
- uses: actions/upload-artifact@v3
if: ${{ always() }}
with:
name: ember-exam-execution-${{matrix.browser}}
path: ./app/assets/javascripts/discourse/test-execution-*.json

7
.gitignore vendored
View File

@ -39,7 +39,6 @@
!/plugins/discourse-narrative-bot
!/plugins/discourse-presence
!/plugins/lazy-yt/
!/plugins/chat/
!/plugins/poll/
!/plugins/styleguide
/plugins/*/auto_generated/
@ -50,9 +49,6 @@
/vendor/data/GeoLite2-City.mmdb
/vendor/data/GeoLite2-ASN.mmdb
# We provide a .sample but people can use newer versions if they want to
.ruby-version
# Front-end
dist
node_modules
@ -63,6 +59,3 @@ yarn-error.log
# Generated API documentation files
openapi/*
# Cached License Data Files
/.licenses

21
.jsdoc
View File

@ -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"
}
}

View File

@ -1,62 +1,12 @@
sources:
yarn: true
bundler: true
allowed:
- 0bsd
- mit
- apache-2.0
- bsd-2-clause
- bsd-3-clause
- cc0-1.0
- isc
- mit
- ruby
ignored:
bundler:
- cgi # Ruby (default gem)
- date # Ruby (default gem)
- 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:
bundler:
- activerecord # MIT
- coderay # MIT
- concurrent-ruby # MIT
- css_parser # MIT
- excon # MIT
- faraday-em_http # MIT
- faraday-em_synchrony # MIT
- faraday-excon # MIT
- faraday-httpclient # MIT
- faraday-net_http # MIT
- faraday-patron # MIT
- faraday-rack # MIT
- highline # Ruby or GPL-2.0
- htmlentities # MIT
- image_size # MIT
- jwt # MIT
- kgio # LGPL-2.1+
- logstash-event # Apache-2.0
- net-imap # Ruby (bundled gem)
- net-pop # Ruby (bundled gem)
- net-smtp # Ruby (bundled gem)
- omniauth # MIT
- pg # Ruby
- r2 # Apache-2.0 (Twitter)
- raindrops # LGPL-2.1+
- rubyzip # Ruby
- sidekiq # LGPL (Sidekiq)
- tilt # MIT
- unf # BSD-2-Clause
- unicorn # Ruby or GPLv2/GPLv3
- other
- none

View File

@ -1,18 +0,0 @@
{
"licenses": {
"blueOak": "bronze",
"spdx": [
"CC0-1.0",
"CC-BY-3.0",
"CC-BY-4.0",
"Apache-2.0 WITH LLVM-exception"
]
},
"packages": {
"@fortawesome/fontawesome-free": "*",
"ember-template-lint-plugin-discourse": "*",
"squoosh": "2.0.0",
"taffydb": "2.6.2"
},
"corrections": true
}

View File

@ -1,15 +1,16 @@
app/assets/stylesheets/vendor/
plugins/**/assets/stylesheets/vendor/
plugins/**/assets/javascripts/vendor/
plugins/**/config/locales/**/*.yml
plugins/**/config/*.yml
documentation/
package.json
config/locales/**/*.yml
!config/locales/**/*.en*.yml
script/import_scripts/**/*.yml
app/assets/javascripts/browser-update.js
app/assets/javascripts/discourse-loader.js
app/assets/javascripts/env.js
app/assets/javascripts/main_include_admin.js
app/assets/javascripts/vendor.js
app/assets/javascripts/locales/i18n.js
app/assets/javascripts/ember-addons/
app/assets/javascripts/discourse/lib/autosize.js
@ -18,15 +19,9 @@ lib/javascripts/messageformat.js
lib/highlight_js/
plugins/**/lib/javascripts/locale
public/
!/app/assets/javascripts/discourse/public
vendor/
app/assets/javascripts/discourse/tests/test_helper.js
app/assets/javascripts/discourse/tests/fixtures
spec/
node_modules/
dist/
tmp/
**/*.rb
**/*.html
**/*.json
**/*.md

2
.rspec
View File

@ -1 +1 @@
--require 'rails_helper'
--colour

View File

@ -1,5 +1,5 @@
inherit_gem:
rubocop-discourse: stree-compat.yml
rubocop-discourse: default.yml
# Still work to do in ensuring we don't link old files
Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
@ -7,7 +7,3 @@ Discourse/NoAddReferenceOrAliasesActiveRecordMigration:
Discourse/NoResetColumnInformationInMigrations:
Enabled: true
Lint/Debugger:
Exclude:
- script/**/*

View File

@ -1 +1 @@
3.2.1
2.7.2

View File

@ -1,2 +0,0 @@
--print-width=100
--plugins=plugin/trailing_comma,disable_ternary

View File

@ -1,31 +1,53 @@
module.exports = {
plugins: ["ember-template-lint-plugin-discourse"],
extends: "discourse:recommended",
extends: "recommended",
ignore: ["**/*.raw"],
rules: {
"no-action-modifiers": true,
"block-indentation": true,
"deprecated-render-helper": true,
"eol-last": "always",
"linebreak-style": true,
"link-rel-noopener": "strict",
"no-abstract-roles": true,
"no-args-paths": true,
"no-attrs-in-components": true,
"no-capital-arguments": false, // TODO: we extensively use `args` argument name
"no-curly-component-invocation": {
allow: [
// These are helpers, not components
"directory-item-header-title",
"directory-item-user-field-value",
"directory-item-value",
"directory-table-header-title",
"loading-spinner",
"directory-item-label",
],
},
"no-implicit-this": {
allow: ["loading-spinner"],
},
// Begin prettier compatibility
"eol-last": false,
"self-closing-void-elements": false,
"block-indentation": false,
quotes: false,
// End prettier compatibility
"no-debugger": true,
"no-duplicate-attributes": true,
"no-extra-mut-helper-argument": true,
"no-html-comments": true,
"no-index-component-invocation": true,
"no-inline-styles": false,
"no-input-block": true,
"no-input-tagname": true,
"no-implicit-this": false,
"no-invalid-interactive": true,
"no-invalid-link-text": true,
"no-invalid-meta": true,
"no-invalid-role": true,
"no-log": true,
"no-negated-condition": true,
"no-nested-interactive": true,
"no-multiple-empty-lines": true,
"no-obsolete-elements": true,
"no-outlet-outside-routes": true,
"no-partial": true,
"no-positive-tabindex": false,
"no-quoteless-attributes": true,
"no-shadowed-elements": true,
"no-trailing-spaces": true,
"no-triple-curlies": true,
"no-unbound": true,
"no-unnecessary-concat": true,
"no-unnecessary-component-helper": true,
"no-unused-block-params": true,
quotes: "double",
"require-button-type": true,
"require-iframe-title": true,
"require-valid-alt-text": false,
"self-closing-void-elements": true,
"simple-unless": true,
"style-concatenation": true,
"table-groups": true,
"link-href-attributes": false,
},
};

312
Gemfile
View File

@ -1,51 +1,51 @@
# frozen_string_literal: true
source "https://rubygems.org"
source 'https://rubygems.org'
# if there is a super emergency and rubygems is playing up, try
#source 'http://production.cf.rubygems.org'
gem "bootsnap", require: false, platform: :mri
gem 'bootsnap', require: false, platform: :mri
def rails_master?
ENV["RAILS_MASTER"] == "1"
ENV["RAILS_MASTER"] == '1'
end
if rails_master?
gem "arel", git: "https://github.com/rails/arel.git"
gem "rails", git: "https://github.com/rails/rails.git"
gem 'arel', git: 'https://github.com/rails/arel.git'
gem 'rails', git: 'https://github.com/rails/rails.git'
else
# 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.
#
# To issue a rails update bump the version number here
rails_version = "7.0.4.3"
gem "actionmailer", rails_version
gem "actionpack", rails_version
gem "actionview", rails_version
gem "activemodel", rails_version
gem "activerecord", rails_version
gem "activesupport", rails_version
gem "railties", rails_version
gem "sprockets-rails"
rails_version = '6.1.4.1'
gem 'actionmailer', rails_version
gem 'actionpack', rails_version
gem 'actionview', rails_version
gem 'activemodel', rails_version
gem 'activerecord', rails_version
gem 'activesupport', rails_version
gem 'railties', rails_version
gem 'sprockets-rails'
end
gem "json"
gem 'json'
# 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
gem "sprockets", git: "https://github.com/rails/sprockets", branch: "3.x"
# This is a desired upgrade we should get to.
gem 'sprockets', '3.7.2'
# this will eventually be added to rails,
# 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 'seed-fu'
gem "mail", git: "https://github.com/discourse/mail.git"
gem "mini_mime"
gem "mini_suffix"
gem 'mail', git: 'https://github.com/discourse/mail.git', require: false
gem 'mini_mime'
gem 'mini_suffix'
gem "redis"
gem 'redis'
# This is explicitly used by Sidekiq and is an optional dependency.
# We tell Sidekiq to use the namespace "sidekiq" which triggers this
@ -53,231 +53,213 @@ gem "redis"
# redis namespace support is optional
# We already namespace stuff in DiscourseRedis, so we should consider
# 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
# 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
# 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'
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 "aws-sdk-sns", require: false
gem "excon", require: false
gem "unf", require: false
gem 'fastimage'
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 "multi_json"
gem "mustache"
gem "nokogiri"
gem "loofah"
gem "css_parser", require: false
gem 'email_reply_trimmer'
gem "omniauth"
gem "omniauth-facebook"
gem "omniauth-twitter"
gem "omniauth-github"
gem 'image_optim'
gem 'multi_json'
gem 'mustache'
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
# pending: https://github.com/ohler55/oj/issues/789
gem "oj", "3.13.14"
gem 'omniauth-google-oauth2'
gem "pg"
gem "mini_sql"
gem "pry-rails", require: false
gem "pry-byebug", require: false
gem "rtlcss", require: false
gem "rake"
# Pinning oj until https://github.com/ohler55/oj/issues/699 is resolved.
# Segfaults and stuck processes after upgrading.
gem 'oj', '3.13.2'
gem "thor", require: false
gem "diffy", require: false
gem "rinku"
gem "sidekiq"
gem "mini_scheduler"
gem 'pg'
gem 'mini_sql'
gem 'pry-rails', require: false
gem 'pry-byebug', require: false
gem 'r2', require: false
gem 'rake'
gem "execjs", require: false
gem "mini_racer"
gem 'thor', require: false
gem 'diffy', require: false
gem 'rinku'
gem 'sidekiq'
gem 'mini_scheduler'
gem "highline", require: false
gem 'execjs', require: false
gem 'mini_racer'
gem "rack"
gem 'highline', require: false
gem "rack-protection" # security
gem "cbor", require: false
gem "cose", require: false
gem "addressable"
gem "json_schemer"
gem 'rack'
gem "net-smtp", require: false
gem "net-imap", require: false
gem "net-pop", require: false
gem "digest", require: false
gem 'rack-protection' # security
gem 'cbor', require: false
gem 'cose', require: false
gem 'addressable'
gem 'json_schemer'
# 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
group :assets do
gem "uglifier"
gem 'uglifier'
gem 'rtlit', require: false # for css rtling
end
group :test do
gem "capybara", require: false
gem "webmock", require: false
gem "fakeweb", require: false
gem "minitest", require: false
gem "simplecov", require: false
gem "selenium-webdriver", require: false
gem 'webmock', require: false
gem 'fakeweb', require: false
gem 'minitest', require: false
gem 'simplecov', require: false
gem "test-prof"
gem "webdrivers", require: false
end
group :test, :development do
gem "rspec"
gem "listen", require: false
gem "certified", require: false
gem "fabrication", require: false
gem "mocha", require: false
gem 'rspec'
gem 'mock_redis'
gem 'listen', require: false
gem 'certified', require: false
gem 'fabrication', 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 "rspec-html-matchers"
gem "byebug", require: ENV["RM_INFO"].nil?, platform: :mri
gem 'shoulda-matchers', require: false
gem 'rspec-html-matchers'
gem 'byebug', require: ENV['RM_INFO'].nil?, platform: :mri
gem "rubocop-discourse", require: false
gem "parallel_tests"
gem 'parallel_tests'
gem "rswag-specs"
gem 'rswag-specs'
gem "annotate"
gem "syntax_tree"
gem "syntax_tree-disable_ternary"
gem 'annotate'
end
group :development do
gem "ruby-prof", require: false, platform: :mri
gem "bullet", require: !!ENV["BULLET"]
gem "better_errors", platform: :mri, require: !!ENV["BETTER_ERRORS"]
gem "binding_of_caller"
gem "yaml-lint"
gem "yard"
gem 'ruby-prof', require: false, platform: :mri
gem 'bullet', require: !!ENV['BULLET']
gem 'better_errors', platform: :mri, require: !!ENV['BETTER_ERRORS']
gem 'binding_of_caller'
gem 'yaml-lint'
end
if ENV["ALLOW_DEV_POPULATE"] == "1"
gem "discourse_dev_assets"
gem "faker", "~> 2.16"
gem 'discourse_dev_assets'
gem 'faker', "~> 2.16"
else
group :development, :test do
gem "discourse_dev_assets"
gem "faker", "~> 2.16"
group :development do
gem 'discourse_dev_assets'
gem 'faker', "~> 2.16"
end
end
# this is an optional gem, it provides a high performance replacement
# to String#blank? a method that is called quite frequently in current
# ActiveRecord, this may change in the future
gem "fast_blank", platform: :ruby
gem 'fast_blank', platform: :ruby
# 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
# 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
gem "rack-mini-profiler", require: ["enable_rails_patches"]
gem 'rack-mini-profiler', require: ['enable_rails_patches']
gem "unicorn", require: false, platform: :ruby
gem "puma", require: false
gem "rbtrace", require: false, platform: :mri
gem "gc_tracer", require: false, platform: :mri
gem 'unicorn', require: false, platform: :ruby
gem 'puma', require: false
gem 'rbtrace', require: false, platform: :mri
gem 'gc_tracer', require: false, platform: :mri
# 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
gem "rss", require: false
gem 'rss', require: false
gem "stackprof", require: false, platform: :mri
gem "memory_profiler", require: false, platform: :mri
gem 'stackprof', 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 "logstash-event", require: false
gem "logstash-logger", require: false
gem "logster"
gem 'lograge', require: false
gem 'logstash-event', require: false
gem 'logstash-logger', require: false
gem 'logster'
# These are forks of sassc and sassc-rails with dart-sass support
gem "dartsass-ruby"
gem "dartsass-sprockets"
# NOTE: later versions of sassc are causing a segfault, possibly dependent on processer architecture
# and until resolved should be locked at 2.0.1
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 "lz4-ruby", require: false, platform: :ruby
gem 'rchardet', require: false
gem 'lz4-ruby', require: false, platform: :ruby
gem "sanitize"
gem 'sanitize'
if ENV["IMPORT"] == "1"
gem "mysql2"
gem "redcarpet"
gem 'mysql2'
gem 'redcarpet'
# 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 "ruby-bbcode-to-md", git: "https://github.com/nlalonde/ruby-bbcode-to-md"
gem "reverse_markdown"
gem "tiny_tds"
gem "csv"
gem "parallel", require: false
gem 'sqlite3', '~> 1.3', '>= 1.3.13'
gem 'ruby-bbcode-to-md', git: 'https://github.com/nlalonde/ruby-bbcode-to-md'
gem 'reverse_markdown'
gem 'tiny_tds'
gem 'csv'
end
gem "web-push"
gem "colored2", require: false
gem "maxminddb"
gem 'webpush', require: false
gem 'colored2', require: false
gem 'maxminddb'
gem "rails_failover", require: false
gem "faraday"
gem "faraday-retry"
# workaround for faraday-net_http, see
# https://github.com/ruby/net-imap/issues/16#issuecomment-803086765
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"
gem 'rails_failover', require: false

View File

@ -5,37 +5,25 @@ GIT
mail (2.8.0.edge)
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
remote: https://rubygems.org/
specs:
actionmailer (7.0.4.3)
actionpack (= 7.0.4.3)
actionview (= 7.0.4.3)
activejob (= 7.0.4.3)
activesupport (= 7.0.4.3)
actionmailer (6.1.4.1)
actionpack (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activesupport (= 6.1.4.1)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.4.3)
actionview (= 7.0.4.3)
activesupport (= 7.0.4.3)
rack (~> 2.0, >= 2.2.0)
actionpack (6.1.4.1)
actionview (= 6.1.4.1)
activesupport (= 6.1.4.1)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (7.0.4.3)
activesupport (= 7.0.4.3)
actionview (6.1.4.1)
activesupport (= 6.1.4.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@ -44,162 +32,170 @@ GEM
actionview (>= 6.0.a)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
activejob (7.0.4.3)
activesupport (= 7.0.4.3)
activejob (6.1.4.1)
activesupport (= 6.1.4.1)
globalid (>= 0.3.6)
activemodel (7.0.4.3)
activesupport (= 7.0.4.3)
activerecord (7.0.4.3)
activemodel (= 7.0.4.3)
activesupport (= 7.0.4.3)
activesupport (7.0.4.3)
activemodel (6.1.4.1)
activesupport (= 6.1.4.1)
activerecord (6.1.4.1)
activemodel (= 6.1.4.1)
activesupport (= 6.1.4.1)
activesupport (6.1.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
annotate (3.2.0)
activerecord (>= 3.2, < 8.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
rake (>= 10.4, < 14.0)
ast (2.4.2)
aws-eventstream (1.2.0)
aws-partitions (1.583.0)
aws-sdk-core (3.130.2)
aws-partitions (1.516.0)
aws-sdk-core (3.121.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.56.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (1.44.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-s3 (1.96.1)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sdk-sns (1.53.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.5.0)
aws-sdk-sns (1.46.0)
aws-sdk-core (~> 3, >= 3.121.2)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.4.0)
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)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
bootsnap (1.16.0)
msgpack (~> 1.2)
bootsnap (1.9.3)
msgpack (~> 1.0)
builder (3.2.4)
bullet (7.0.7)
bullet (6.1.5)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.1.3)
capybara (3.38.0)
addressable
matrix
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
cbor (0.5.9.6)
certified (1.0.0)
cgi (0.3.6)
chunky_png (1.4.0)
coderay (1.1.3)
colored2 (3.1.2)
concurrent-ruby (1.2.2)
connection_pool (2.3.0)
cose (1.3.0)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
cose (1.2.0)
cbor (~> 0.5.9)
openssl-signature_algorithm (~> 1.0)
cppjieba_rb (0.4.2)
cppjieba_rb (0.3.3)
crack (0.4.5)
rexml
crass (1.0.6)
css_parser (1.14.0)
css_parser (1.10.0)
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)
diff-lcs (1.5.0)
diffy (3.4.2)
digest (3.1.1)
diff-lcs (1.4.4)
diffy (3.4.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-seed-fu (2.3.12)
activerecord (>= 3.1)
activesupport (>= 3.1)
discourse_dev_assets (0.0.4)
discourse_dev_assets (0.0.3)
faker (~> 2.16)
literate_randomizer
docile (1.4.0)
ecma-re-validator (0.4.0)
regexp_parser (~> 2.2)
ecma-re-validator (0.3.0)
regexp_parser (~> 2.0)
email_reply_trimmer (0.1.13)
erubi (1.12.0)
excon (0.99.0)
ember-data-source (3.0.2)
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.10.0)
excon (0.89.0)
execjs (2.8.1)
exifr (1.3.10)
fabrication (2.30.0)
faker (2.23.0)
i18n (>= 1.8.11, < 2)
exifr (1.3.9)
fabrication (2.23.0)
faker (2.19.0)
i18n (>= 1.6, < 2)
fakeweb (1.3.0)
faraday (2.7.4)
faraday-net_http (>= 2.0, < 3.1)
faraday (1.8.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
faraday-retry (2.1.0)
faraday (~> 2.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
fast_blank (1.0.1)
fast_xs (0.8.0)
fastimage (2.2.6)
ffi (1.15.5)
fastimage (2.2.5)
ffi (1.15.4)
fspath (3.1.2)
gc_tracer (1.5.1)
globalid (1.1.0)
globalid (1.0.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)
hana (1.3.7)
hashdiff (1.0.1)
hashie (5.0.0)
highline (2.1.0)
hkdf (1.0.0)
hashie (4.1.0)
highline (2.0.3)
hkdf (0.3.0)
htmlentities (4.3.4)
http_accept_language (2.1.1)
i18n (1.12.0)
i18n (1.8.11)
concurrent-ruby (~> 1.0)
image_optim (0.31.3)
image_optim (0.31.1)
exifr (~> 1.2, >= 1.2.2)
fspath (~> 3.0)
image_size (>= 1.5, < 4)
in_threads (~> 1.3)
progress (~> 3.0, >= 3.0.1)
image_size (3.2.0)
in_threads (1.6.0)
jmespath (1.6.2)
json (2.6.3)
json-schema (3.0.0)
addressable (>= 2.8)
json_schemer (0.2.23)
image_size (3.0.1)
in_threads (1.5.4)
ipaddr (1.2.3)
jmespath (1.4.0)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.6.1)
json-schema (2.8.1)
addressable (>= 2.4)
json_schemer (0.2.18)
ecma-re-validator (~> 0.3)
hana (~> 1.3)
regexp_parser (~> 2.0)
uri_template (~> 0.7)
jwt (2.7.0)
jwt (2.3.0)
kgio (2.11.4)
libv8-node (16.10.0.0)
libv8-node (16.10.0.0-aarch64-linux)
@ -207,11 +203,11 @@ GEM
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-linux)
listen (3.8.0)
listen (3.7.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
literate_randomizer (0.4.0)
lograge (0.12.0)
lograge (0.11.2)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
@ -219,71 +215,54 @@ GEM
logstash-event (1.2.02)
logstash-logger (0.26.1)
logstash-event (~> 1.2)
logster (2.12.2)
loofah (2.19.1)
logster (2.10.1)
loofah (2.13.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lru_redux (1.1.0)
lz4-ruby (0.3.3)
matrix (0.4.2)
maxminddb (0.1.22)
memory_profiler (1.0.1)
message_bus (4.3.2)
memory_profiler (1.0.0)
message_bus (3.3.6)
rack (>= 1.1.3)
method_source (1.0.0)
mini_mime (1.1.2)
mini_portile2 (2.8.1)
mini_racer (0.6.3)
mini_portile2 (2.6.1)
mini_racer (0.5.0)
libv8-node (~> 16.10.0.0)
mini_scheduler (0.15.0)
sidekiq (>= 4.2.3, < 7.0)
mini_sql (1.4.0)
mini_scheduler (0.13.0)
sidekiq (>= 4.2.3)
mini_sql (1.1.3)
mini_suffix (0.3.3)
ffi (~> 1.9)
minitest (5.18.0)
mocha (2.0.2)
ruby2_keywords (>= 0.0.5)
msgpack (1.6.1)
minitest (5.14.4)
mocha (1.13.0)
mock_redis (0.29.0)
ruby2_keywords
msgpack (1.4.2)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
mustache (1.1.1)
net-http (0.3.2)
uri
net-imap (0.3.4)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
timeout
net-smtp (0.3.3)
net-protocol
nio4r (2.5.8)
nokogiri (1.14.2)
mini_portile2 (~> 2.8.0)
nokogiri (1.12.5)
mini_portile2 (~> 2.6.1)
racc (~> 1.4)
nokogiri (1.14.2-aarch64-linux)
nokogiri (1.12.5-arm64-darwin)
racc (~> 1.4)
nokogiri (1.14.2-arm64-darwin)
nokogiri (1.12.5-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.14.2-x86_64-darwin)
nokogiri (1.12.5-x86_64-linux)
racc (~> 1.4)
nokogiri (1.14.2-x86_64-linux)
racc (~> 1.4)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
oauth-tty (1.0.5)
version_gem (~> 1.1, >= 1.1.1)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
oauth (0.5.8)
oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
oj (3.13.14)
omniauth (1.9.2)
rack (>= 1.2, < 3)
oj (3.13.2)
omniauth (1.9.1)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
omniauth-facebook (9.0.0)
@ -299,66 +278,66 @@ GEM
omniauth-oauth (1.2.0)
oauth
omniauth (>= 1.0, < 3)
omniauth-oauth2 (1.7.3)
oauth2 (>= 1.4, < 3)
omniauth-oauth2 (1.7.2)
oauth2 (~> 1.4)
omniauth (>= 1.9, < 3)
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
openssl (3.1.0)
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
openssl (2.2.1)
ipaddr
openssl-signature_algorithm (1.1.1)
openssl (~> 2.0)
optimist (3.0.1)
parallel (1.22.1)
parallel_tests (4.2.0)
parallel (1.21.0)
parallel_tests (3.7.3)
parallel
parser (3.2.1.1)
parser (3.0.3.2)
ast (~> 2.4.1)
pg (1.4.6)
prettier_print (1.2.1)
pg (1.2.3)
progress (3.6.0)
pry (0.14.2)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.10.1)
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
pry (~> 0.13.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (5.0.1)
puma (6.1.1)
public_suffix (4.0.6)
puma (5.5.2)
nio4r (~> 2.0)
racc (1.6.2)
rack (2.2.6.4)
rack-mini-profiler (3.0.0)
r2 (0.2.7)
racc (1.6.0)
rack (2.2.3)
rack-mini-profiler (2.3.3)
rack (>= 1.2.0)
rack-protection (3.0.5)
rack-protection (2.1.0)
rack
rack-test (2.1.0)
rack (>= 1.3)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
rails_failover (0.8.1)
activerecord (> 6.0, < 7.1)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
rails_failover (0.7.3)
activerecord (~> 6.0)
concurrent-ruby
railties (> 6.0, < 7.1)
rails_multisite (4.0.1)
activerecord (> 5.0, < 7.1)
railties (> 5.0, < 7.1)
railties (7.0.4.3)
actionpack (= 7.0.4.3)
activesupport (= 7.0.4.3)
railties (~> 6.0)
rails_multisite (4.0.0)
activerecord (> 5.0, < 7)
railties (> 5.0, < 7)
railties (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
method_source
rake (>= 12.2)
rake (>= 0.13)
thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
raindrops (0.20.1)
rainbow (3.0.0)
raindrops (0.20.0)
rake (13.0.6)
rb-fsevent (0.11.2)
rb-fsevent (0.11.0)
rb-inotify (0.10.1)
ffi (~> 1.0)
rbtrace (0.4.14)
@ -366,163 +345,137 @@ GEM
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
rchardet (1.8.0)
redis (4.8.1)
redis-namespace (1.10.0)
redis (>= 4)
regexp_parser (2.7.0)
request_store (1.5.1)
redcarpet (3.5.1)
redis (4.5.1)
redis-namespace (1.8.1)
redis (>= 3.0.4)
regexp_parser (2.2.0)
request_store (1.5.0)
rack (>= 1.4)
rexml (3.2.5)
rinku (2.0.6)
rotp (6.2.2)
rqrcode (2.1.2)
rotp (6.2.0)
rqrcode (2.1.0)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.1)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.2)
rspec (3.10.0)
rspec-core (~> 3.10.0)
rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.10.0)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-html-matchers (0.10.0)
rspec-support (~> 3.10.0)
rspec-html-matchers (0.9.4)
nokogiri (~> 1)
rspec (>= 3.0.0.a)
rspec-mocks (3.12.4)
rspec (>= 3.0.0.a, < 4)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-rails (6.0.1)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
rspec-core (~> 3.11)
rspec-expectations (~> 3.11)
rspec-mocks (~> 3.11)
rspec-support (~> 3.11)
rspec-support (3.12.0)
rspec-support (~> 3.10.0)
rspec-rails (5.0.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
railties (>= 5.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.10.3)
rss (0.2.9)
rexml
rswag-specs (2.8.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rtlcss (0.2.0)
mini_racer (~> 0.6.3)
rubocop (1.48.1)
json (~> 2.3)
rswag-specs (2.4.0)
activesupport (>= 3.1, < 7.0)
json-schema (~> 2.2)
railties (>= 3.1, < 7.0)
rtlit (0.0.5)
rubocop (1.23.0)
parallel (~> 1.10)
parser (>= 3.2.0.0)
parser (>= 3.0.0.0)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.26.0, < 2.0)
rexml
rubocop-ast (>= 1.12.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.27.0)
parser (>= 3.2.1.0)
rubocop-capybara (2.17.1)
rubocop (~> 1.41)
rubocop-discourse (3.2.0)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.15.0)
parser (>= 3.0.1.1)
rubocop-discourse (2.5.0)
rubocop (>= 1.1.0)
rubocop-rspec (>= 2.0.0)
rubocop-rspec (2.19.0)
rubocop (~> 1.33)
rubocop-capybara (~> 2.17)
ruby-prof (1.6.1)
ruby-progressbar (1.13.0)
rubocop-rspec (2.6.0)
rubocop (~> 1.19)
ruby-prof (1.4.3)
ruby-progressbar (1.11.0)
ruby-readability (0.7.0)
guess_html_encoding (>= 0.0.4)
nokogiri (>= 1.6.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
sanitize (6.0.1)
sanitize (6.0.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
sass-embedded (1.59.2)
google-protobuf (~> 3.21)
rake (>= 10.0.0)
sass-embedded (1.59.2-aarch64-linux-gnu)
google-protobuf (~> 3.21)
sass-embedded (1.59.2-arm64-darwin)
google-protobuf (~> 3.21)
sass-embedded (1.59.2-x86_64-darwin)
google-protobuf (~> 3.21)
sass-embedded (1.59.2-x86_64-linux-gnu)
google-protobuf (~> 3.21)
selenium-webdriver (4.8.1)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
shoulda-matchers (5.3.0)
sassc (2.0.1)
ffi (~> 1.9)
rake
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
seed-fu (2.3.9)
activerecord (>= 3.1)
activesupport (>= 3.1)
shoulda-matchers (5.0.0)
activesupport (>= 5.2.0)
sidekiq (6.5.8)
connection_pool (>= 2.2.5, < 3)
sidekiq (6.3.1)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.5.0, < 5)
simplecov (0.22.0)
redis (>= 4.2.0)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
simplecov_json_formatter (0.1.3)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
sshkey (2.0.0)
stackprof (0.2.23)
syntax_tree (6.0.2)
prettier_print (>= 1.2.0)
syntax_tree-disable_ternary (1.0.0)
test-prof (1.2.0)
thor (1.2.1)
tilt (2.1.0)
timeout (0.3.2)
tzinfo (2.0.6)
stackprof (0.2.17)
test-prof (1.0.7)
thor (1.1.0)
tilt (2.0.10)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2022.7)
tzinfo (>= 1.0.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.4.2)
unicorn (6.1.0)
unf_ext (0.0.8)
unicode-display_width (2.1.0)
unicorn (6.0.0)
kgio (~> 2.6)
raindrops (~> 0.7)
uniform_notifier (1.16.0)
uri (0.12.0)
uniform_notifier (1.14.2)
uri_template (0.7.0)
version_gem (1.1.1)
web-push (3.0.0)
hkdf (~> 1.0)
jwt (~> 2.0)
openssl (~> 3.0)
webdrivers (5.2.0)
nokogiri (~> 1.6)
rubyzip (>= 1.3.0)
selenium-webdriver (~> 4.0)
webmock (3.18.1)
webmock (3.14.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.7.0)
websocket (1.2.9)
xorcist (1.1.3)
xpath (3.2.0)
nokogiri (~> 1.8)
yaml-lint (0.1.2)
yard (0.9.28)
webrick (~> 1.7.0)
zeitwerk (2.6.7)
webpush (1.1.0)
hkdf (~> 0.2)
jwt (~> 2.0)
xorcist (1.1.2)
yaml-lint (0.0.10)
zeitwerk (2.5.1)
PLATFORMS
aarch64-linux
@ -534,46 +487,42 @@ PLATFORMS
x86_64-linux
DEPENDENCIES
actionmailer (= 7.0.4.3)
actionpack (= 7.0.4.3)
actionview (= 7.0.4.3)
actionmailer (= 6.1.4.1)
actionpack (= 6.1.4.1)
actionview (= 6.1.4.1)
actionview_precompiler
active_model_serializers (~> 0.8.3)
activemodel (= 7.0.4.3)
activerecord (= 7.0.4.3)
activesupport (= 7.0.4.3)
activemodel (= 6.1.4.1)
activerecord (= 6.1.4.1)
activesupport (= 6.1.4.1)
addressable
annotate
aws-sdk-s3
aws-sdk-sns
barber
better_errors
binding_of_caller
bootsnap
bullet
byebug
capybara
cbor
certified
cgi (>= 0.3.6)
colored2
cose
cppjieba_rb
css_parser
dartsass-ruby
dartsass-sprockets
diffy
digest
discourse-ember-rails (= 0.18.6)
discourse-ember-source (~> 3.12.2)
discourse-fonts
discourse-seed-fu
discourse_dev_assets
email_reply_trimmer
ember-handlebars-template (= 0.8.0)
excon
execjs
fabrication
faker (~> 2.16)
fakeweb
faraday
faraday-retry
fast_blank
fast_xs
fastimage
@ -603,14 +552,11 @@ DEPENDENCIES
mini_suffix
minitest
mocha
mock_redis
multi_json
mustache
net-http
net-imap
net-pop
net-smtp
nokogiri
oj (= 3.13.14)
oj (= 3.13.2)
omniauth
omniauth-facebook
omniauth-github
@ -622,16 +568,18 @@ DEPENDENCIES
pry-byebug
pry-rails
puma
r2
rack
rack-mini-profiler
rack-protection
rails_failover
rails_multisite
railties (= 7.0.4.3)
railties (= 6.1.4.1)
rake
rb-fsevent
rbtrace
rchardet
redcarpet
redis
redis-namespace
rinku
@ -642,35 +590,32 @@ DEPENDENCIES
rspec-rails
rss
rswag-specs
rtlcss
rtlit
rubocop-discourse
ruby-prof
ruby-readability
rubyzip
sanitize
selenium-webdriver
sassc (= 2.0.1)
sassc-rails
seed-fu
shoulda-matchers
sidekiq
simplecov
sprockets!
sprockets (= 3.7.2)
sprockets-rails
sqlite3
sshkey
stackprof
syntax_tree
syntax_tree-disable_ternary
test-prof
thor
tzinfo-data
uglifier
unf
unicorn
web-push
webdrivers
webmock
webrick
webpush
xorcist
yaml-lint
yard
BUNDLED WITH
2.4.4
2.2.29

View File

@ -12,9 +12,11 @@ To learn more about the philosophy and goals of the project, [visit **discourse.
## Screenshots
<a href="https://bbs.boingboing.net"><img alt="Boing Boing" src="https://user-images.githubusercontent.com/1681963/52239245-04ad8280-289c-11e9-9c88-8c173d4a0422.png" width="720px"></a>
<a href="https://twittercommunity.com/"><img src="https://user-images.githubusercontent.com/1681963/52239250-04ad8280-289c-11e9-9e42-574f6eaab9d7.png" width="720px"></a>
<a href="https://forums.gearboxsoftware.com/"><img src="https://user-images.githubusercontent.com/1681963/89088042-68ffb400-d364-11ea-93be-161ea04d8b29.png" width="720px"></a>
<img src="https://user-images.githubusercontent.com/1681963/52239118-b304f800-289b-11e9-9904-16450680d9ec.jpg" alt="Mobile" width="414">
@ -30,7 +32,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.
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.0+](https://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first!
## Setting up Discourse
@ -38,6 +40,8 @@ If you want to set up a Discourse forum for production use, see our [**Discourse
If you're looking for business class hosting, see [discourse.org/buy](https://www.discourse.org/buy/).
If you're looking for our remote work solution, see [teams.discourse.com](https://teams.discourse.com/).
## Requirements
Discourse is built for the *next* 10 years of the Internet, so our requirements are high.
@ -51,8 +55,6 @@ Discourse supports the **latest, stable releases** of all major browsers and pla
| Microsoft Edge | | |
| Mozilla Firefox | | |
Additionally, we aim to support Safari on iOS 15.7+.
## Built With
- [Ruby on Rails](https://github.com/rails/rails) &mdash; Our back end API is a Rails app. It responds to requests RESTfully in JSON.
@ -65,7 +67,7 @@ Plus *lots* of Ruby Gems, a complete list of which is at [/main/Gemfile](https:/
## Contributing
[![Build Status](https://github.com/discourse/discourse/actions/workflows/tests.yml/badge.svg)](https://github.com/discourse/discourse/actions)
[![Build Status](https://github.com/discourse/discourse/workflows/CI/badge.svg)](https://github.com/discourse/discourse/actions)
Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that
accepts contributions from the public &ndash; including you!
@ -91,7 +93,7 @@ The original Discourse code contributors can be found in [**AUTHORS.MD**](docs/A
## Copyright / License
Copyright 2014 - 2023 Civilized Discourse Construction Kit, Inc.
Copyright 2014 - 2021 Civilized Discourse Construction Kit, Inc.
Licensed under the GNU General Public License Version 2.0 (or later);
you may not use this work except in compliance with the License.
@ -107,10 +109,6 @@ limitations under the License.
Discourse logo and “Discourse Forum” ®, Civilized Discourse Construction Kit, Inc.
## Accessibility
To guide our ongoing effort to build accessible software we follow the [W3Cs Web Content Accessibility Guidelines (WCAG)](https://www.w3.org/TR/WCAG21/). If you'd like to report an accessibility issue that makes it difficult for you to use Discourse, email accessibility@discourse.org. For more information visit [discourse.org/accessibility](https://discourse.org/accessibility).
## Dedication
Discourse is built with [love, Internet style.](https://www.youtube.com/watch?v=Xe1TZaElTAs)

1
adminjs Symbolic link
View File

@ -0,0 +1 @@
app/assets/javascripts/admin

BIN
app/.DS_Store vendored Normal file

Binary file not shown.

BIN
app/assets/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1 +0,0 @@
Images created in https://fa2png.app/ using 150px size and #919191 color.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
app/assets/javascripts/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,32 +0,0 @@
{
"licenses": {
"blueOak": "bronze",
"spdx": [
"CC0-1.0",
"CC-BY-3.0",
"CC-BY-4.0",
"Apache-2.0 WITH LLVM-exception",
"ISC"
]
},
"packages": {
"component-bind": "1.0.0",
"component-inherit": "0.0.3",
"duplex": "1.0.0",
"glob": "3.1.21",
"indexof": "0.0.1",
"inherits": "1.0.2",
"jsonify": "0.0.0",
"messageformat": "0.1.5",
"line-stream": "0.0.0",
"regenerator-transform": "0.10.1",
"source-map": "0.1.43",
"sourcemap-validator": "1.1.1"
},
"corrections": true,
"ignore": [
{
"author": "Discourse"
}
]
}

View File

@ -0,0 +1,20 @@
// discourse-skip-module
(function () {
setTimeout(function () {
const $activateButton = $("#activate-account-button");
$activateButton.on("click", function () {
$activateButton.prop("disabled", true);
const hpPath = document.getElementById("data-activate-account").dataset
.path;
$.ajax(hpPath)
.then(function (hp) {
$("#password_confirmation").val(hp.value);
$("#challenge").val(hp.challenge.split("").reverse().join(""));
$("#activate-account-form").submit();
})
.fail(function () {
$activateButton.prop("disabled", false);
});
});
}, 50);
})();

View File

@ -0,0 +1,13 @@
<%
require_asset("main_include_admin.js")
DiscoursePluginRegistry.admin_javascripts.each { |js| require_asset(js) }
DiscoursePluginRegistry.each_globbed_asset(admin: true) do |f|
if File.directory?(f)
depend_on(f)
else
require_asset(f)
end
end
%>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
<div class="ace">{{this.content}}</div>

View File

@ -1,33 +1,30 @@
import { action } from "@ember/object";
import { classNames } from "@ember-decorators/component";
import { observes, on } from "@ember-decorators/object";
import Component from "@ember/component";
import getURL from "discourse-common/lib/get-url";
import loadScript from "discourse/lib/load-script";
import I18n from "I18n";
import { bind } from "discourse-common/utils/decorators";
import { observes } from "discourse-common/utils/decorators";
import { on } from "@ember/object/evented";
const COLOR_VARS_REGEX =
/\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g;
const COLOR_VARS_REGEX = /\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g;
@classNames("ace-wrapper")
export default class AceEditor extends Component {
mode = "css";
disabled = false;
htmlPlaceholder = false;
_editor = null;
_skipContentChangeEvent = null;
export default Component.extend({
mode: "css",
classNames: ["ace-wrapper"],
_editor: null,
_skipContentChangeEvent: null,
disabled: false,
htmlPlaceholder: false,
@observes("editorId")
editorIdChanged() {
if (this.autofocus) {
this.send("focus");
}
}
},
didRender() {
this._skipContentChangeEvent = false;
}
},
@observes("content")
contentChanged() {
@ -35,14 +32,14 @@ export default class AceEditor extends Component {
if (this._editor && !this._skipContentChangeEvent) {
this._editor.getSession().setValue(content);
}
}
},
@observes("mode")
modeChanged() {
if (this._editor && !this._skipContentChangeEvent) {
this._editor.getSession().setMode("ace/mode/" + this.mode);
}
}
},
@observes("placeholder")
placeholderChanged() {
@ -51,12 +48,12 @@ export default class AceEditor extends Component {
placeholder: this.placeholder,
});
}
}
},
@observes("disabled")
disabledStateChanged() {
this.changeDisabledState();
}
},
changeDisabledState() {
const editor = this._editor;
@ -69,10 +66,9 @@ export default class AceEditor extends Component {
});
editor.container.parentNode.setAttribute("data-disabled", disabled);
}
}
},
@on("willDestroyElement")
_destroyEditor() {
_destroyEditor: on("willDestroyElement", function () {
if (this._editor) {
this._editor.destroy();
this._editor = null;
@ -83,16 +79,16 @@ export default class AceEditor extends Component {
}
$(window).off("ace:resize");
}
}),
resize() {
if (this._editor) {
this._editor.resize();
}
}
},
didInsertElement() {
super.didInsertElement(...arguments);
this._super(...arguments);
loadScript("/javascripts/ace/ace.js").then(() => {
window.ace.require(["ace/ace"], (loadedAce) => {
loadedAce.config.set("loadWorkerFromBlob", false);
@ -107,6 +103,7 @@ export default class AceEditor extends Component {
}
const editor = loadedAce.edit(this.element.querySelector(".ace"));
editor.setTheme("ace/theme/chrome");
editor.setShowPrintMargin(false);
editor.setOptions({ fontSize: "14px", placeholder: this.placeholder });
editor.getSession().setMode("ace/mode/" + this.mode);
@ -148,32 +145,9 @@ export default class AceEditor extends Component {
if (this.autofocus) {
this.send("focus");
}
this.setAceTheme();
this._darkModeListener = window.matchMedia(
"(prefers-color-scheme: dark)"
);
this._darkModeListener.addListener(this.setAceTheme);
});
});
}
willDestroyElement() {
if (this._darkModeListener) {
this._darkModeListener.removeListener(this.setAceTheme);
}
}
@bind
setAceTheme() {
const schemeType = getComputedStyle(document.body)
.getPropertyValue("--scheme-type")
.trim();
this._editor.setTheme(
`ace/theme/${schemeType === "dark" ? "chaos" : "chrome"}`
);
}
},
warnSCSSDeprecations() {
if (
@ -200,20 +174,21 @@ export default class AceEditor extends Component {
this._editor.getSession().setAnnotations(warnings);
this.setWarning?.(
this.setWarning(
warnings.length
? I18n.t("admin.customize.theme.scss_color_variables_warning")
: false
);
}
},
@action
focus() {
if (this._editor) {
this._editor.focus();
this._editor.navigateFileEnd();
}
}
actions: {
focus() {
if (this._editor) {
this._editor.focus();
this._editor.navigateFileEnd();
}
},
},
_overridePlaceholder(loadedAce) {
const originalPlaceholderSetter =
@ -241,5 +216,5 @@ export default class AceEditor extends Component {
this.$updatePlaceholder();
};
}
}
},
});

View File

@ -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}}

View File

@ -1,26 +1,28 @@
import { classNames } from "@ember-decorators/component";
import { observes, on } from "@ember-decorators/object";
import { observes, on } from "discourse-common/utils/decorators";
import Component from "@ember/component";
import I18n from "I18n";
import discourseDebounce from "discourse-common/lib/debounce";
import { scheduleOnce } from "@ember/runloop";
@classNames("admin-backups-logs")
export default class AdminBackupsLogs extends Component {
showLoadingSpinner = false;
hasFormattedLogs = false;
noLogsMessage = I18n.t("admin.backups.logs.none");
formattedLogs = "";
index = 0;
export default Component.extend({
classNames: ["admin-backups-logs"],
showLoadingSpinner: false,
hasFormattedLogs: false,
noLogsMessage: I18n.t("admin.backups.logs.none"),
init() {
this._super(...arguments);
this._reset();
},
_reset() {
this.setProperties({ formattedLogs: "", index: 0 });
}
},
_scrollDown() {
const div = this.element;
div.scrollTop = div.scrollHeight;
}
},
@on("init")
@observes("logs.[]")
@ -29,7 +31,7 @@ export default class AdminBackupsLogs extends Component {
this._reset(); // reset the cached logs whenever the model is reset
this.renderLogs();
}
}
},
_updateFormattedLogsFunc() {
const logs = this.logs;
@ -53,13 +55,13 @@ export default class AdminBackupsLogs extends Component {
this.renderLogs();
scheduleOnce("afterRender", this, this._scrollDown);
}
},
@on("init")
@observes("logs.[]")
_updateFormattedLogs() {
discourseDebounce(this, this._updateFormattedLogsFunc, 150);
}
},
renderLogs() {
const formattedLogs = this.formattedLogs;
@ -74,5 +76,5 @@ export default class AdminBackupsLogs extends Component {
} else {
this.set("showLoadingSpinner", false);
}
}
}
},
});

View File

@ -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>

View File

@ -1,22 +1,24 @@
import { tagName } from "@ember-decorators/component";
import Component from "@ember/component";
import { action } from "@ember/object";
export default Component.extend({
tagName: "",
@tagName("")
export default class AdminEditableField extends Component {
buffer = "";
editing = false;
buffer: "",
editing: false,
@action
edit(event) {
event?.preventDefault();
this.set("buffer", this.value);
this.toggleProperty("editing");
}
init() {
this._super(...arguments);
this.set("editing", false);
},
@action
save() {
// Action has to toggle 'editing' property.
this.action(this.buffer);
}
}
actions: {
edit() {
this.set("buffer", this.value);
this.toggleProperty("editing");
},
save() {
// Action has to toggle 'editing' property.
this.action(this.buffer);
},
},
});

View File

@ -1,14 +0,0 @@
<div class="form-element label-area">
{{#if this.label}}
<label>{{i18n this.label}}</label>
{{else}}
&nbsp;
{{/if}}
</div>
<div class="form-element input-area">
{{#if this.wrapLabel}}
<label>{{yield}}</label>
{{else}}
{{yield}}
{{/if}}
</div>

View File

@ -1,5 +1,4 @@
import { classNames } from "@ember-decorators/component";
import Component from "@ember/component";
@classNames("row")
export default class AdminFormRow extends Component {}
export default Component.extend({
classNames: ["row"],
});

View File

@ -1,10 +1,9 @@
import { tagName } from "@ember-decorators/component";
import Component from "@ember/component";
import loadScript from "discourse/lib/load-script";
@tagName("canvas")
export default class AdminGraph extends Component {
type = "line";
export default Component.extend({
tagName: "canvas",
type: "line",
refreshChart() {
const ctx = this.element.getContext("2d");
@ -50,11 +49,11 @@ export default class AdminGraph extends Component {
};
this._chart = new window.Chart(ctx, config);
}
},
didInsertElement() {
loadScript("/javascripts/Chart.min.js").then(() =>
this.refreshChart.apply(this)
);
}
}
},
});

View File

@ -1,7 +0,0 @@
<div class="admin-controls">
<nav>
<ul class="nav nav-pills">
{{yield}}
</ul>
</nav>
</div>

View File

@ -1,5 +1,4 @@
import { tagName } from "@ember-decorators/component";
import Component from "@ember/component";
@tagName("")
export default class AdminNav extends Component {}
export default Component.extend({
tagName: "",
});

View File

@ -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>

View File

@ -1,16 +1,16 @@
import { classNames } from "@ember-decorators/component";
import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";
@classNames("penalty-history")
export default class AdminPenaltyHistory extends Component {
export default Component.extend({
classNames: ["penalty-history"],
@discourseComputed("user.penalty_counts.suspended")
suspendedCountClass(count) {
if (count > 0) {
return "danger";
}
return "";
}
},
@discourseComputed("user.penalty_counts.silenced")
silencedCountClass(count) {
@ -18,5 +18,5 @@ export default class AdminPenaltyHistory extends Component {
return "danger";
}
return "";
}
}
},
});

View File

@ -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}}

View File

@ -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();
}
}

View File

@ -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>

View File

@ -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}`)
);
}
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -1,3 +0,0 @@
<div class="chart-canvas-container">
<canvas class="chart-canvas"></canvas>
</div>

View File

@ -1,4 +1,3 @@
import { classNames } from "@ember-decorators/component";
import Report from "admin/models/report";
import Component from "@ember/component";
import discourseDebounce from "discourse-common/lib/debounce";
@ -8,31 +7,31 @@ import { number } from "discourse/lib/formatter";
import { schedule } from "@ember/runloop";
import { bind } from "discourse-common/utils/decorators";
@classNames("admin-report-chart")
export default class AdminReportChart extends Component {
limit = 8;
total = 0;
options = null;
export default Component.extend({
classNames: ["admin-report-chart"],
limit: 8,
total: 0,
options: null,
didInsertElement() {
super.didInsertElement(...arguments);
this._super(...arguments);
window.addEventListener("resize", this._resizeHandler);
}
},
willDestroyElement() {
super.willDestroyElement(...arguments);
this._super(...arguments);
window.removeEventListener("resize", this._resizeHandler);
this._resetChart();
}
},
didReceiveAttrs() {
super.didReceiveAttrs(...arguments);
this._super(...arguments);
discourseDebounce(this, this._scheduleChartRendering, 100);
}
},
_scheduleChartRendering() {
schedule("afterRender", () => {
@ -41,7 +40,7 @@ export default class AdminReportChart extends Component {
this.element && this.element.querySelector(".chart-canvas")
);
});
}
},
_renderChart(model, chartCanvas) {
if (!chartCanvas) {
@ -100,7 +99,7 @@ export default class AdminReportChart extends Component {
this._buildChartConfig(data, this.options)
);
});
}
},
_buildChartConfig(data, options) {
return {
@ -162,21 +161,21 @@ export default class AdminReportChart extends Component {
},
},
};
}
},
_resetChart() {
if (this._chart) {
this._chart.destroy();
this._chart = null;
}
}
},
_applyChartGrouping(model, data, options) {
return Report.collapse(model, data, options.chartGrouping);
}
},
@bind
_resizeHandler() {
discourseDebounce(this, this._scheduleChartRendering, 500);
}
}
},
});

View File

@ -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>

View File

@ -1,6 +1,6 @@
import { attributeBindings, classNames } from "@ember-decorators/component";
import Component from "@ember/component";
export default Component.extend({
classNames: ["admin-report-counters"],
@classNames("admin-report-counters")
@attributeBindings("model.description:title")
export default class AdminReportCounters extends Component {}
attributeBindings: ["model.description:title"],
});

View File

@ -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}}

View File

@ -1,12 +1,11 @@
import { classNameBindings, tagName } from "@ember-decorators/component";
import { match } from "@ember/object/computed";
import Component from "@ember/component";
@tagName("tr")
@classNameBindings("reverseColors")
export default class AdminReportCounts extends Component {
allTime = true;
@match("report.type", /^(time_to_first_response|topics_with_no_response)$/)
reverseColors;
}
import { match } from "@ember/object/computed";
export default Component.extend({
allTime: true,
tagName: "tr",
reverseColors: match(
"report.type",
/^(time_to_first_response|topics_with_no_response)$/
),
classNameBindings: ["reverseColors"],
});

View File

@ -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>

View File

@ -1,5 +1,4 @@
import { classNames } from "@ember-decorators/component";
import Component from "@ember/component";
@classNames("admin-report-inline-table")
export default class AdminReportInlineTable extends Component {}
export default Component.extend({
classNames: ["admin-report-inline-table"],
});

View File

@ -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>

View File

@ -1,5 +1,4 @@
import { tagName } from "@ember-decorators/component";
import Component from "@ember/component";
@tagName("tr")
export default class AdminReportPerDayCounts extends Component {}
export default Component.extend({
tagName: "tr",
});

View File

@ -1,3 +0,0 @@
<div class="chart-canvas-container">
<canvas class="chart-canvas"></canvas>
</div>

View File

@ -1,4 +1,3 @@
import { classNames } from "@ember-decorators/component";
import Report from "admin/models/report";
import Component from "@ember/component";
import discourseDebounce from "discourse-common/lib/debounce";
@ -8,31 +7,32 @@ import { number } from "discourse/lib/formatter";
import { schedule } from "@ember/runloop";
import { bind } from "discourse-common/utils/decorators";
@classNames("admin-report-chart", "admin-report-stacked-chart")
export default class AdminReportStackedChart extends Component {
export default Component.extend({
classNames: ["admin-report-chart", "admin-report-stacked-chart"],
didInsertElement() {
super.didInsertElement(...arguments);
this._super(...arguments);
window.addEventListener("resize", this._resizeHandler);
}
},
willDestroyElement() {
super.willDestroyElement(...arguments);
this._super(...arguments);
window.removeEventListener("resize", this._resizeHandler);
this._resetChart();
}
},
didReceiveAttrs() {
super.didReceiveAttrs(...arguments);
this._super(...arguments);
discourseDebounce(this, this._scheduleChartRendering, 100);
}
},
@bind
_resizeHandler() {
discourseDebounce(this, this._scheduleChartRendering, 500);
}
},
_scheduleChartRendering() {
schedule("afterRender", () => {
@ -45,7 +45,7 @@ export default class AdminReportStackedChart extends Component {
this.element.querySelector(".chart-canvas")
);
});
}
},
_renderChart(model, chartCanvas) {
if (!chartCanvas) {
@ -54,13 +54,7 @@ export default class AdminReportStackedChart extends Component {
const context = chartCanvas.getContext("2d");
const chartData = makeArray(model.chartData || model.data).map((cd) => {
return {
label: cd.label,
color: cd.color,
data: Report.collapse(model, cd.data),
};
});
const chartData = makeArray(model.get("chartData") || model.get("data"));
const data = {
labels: chartData[0].data.mapBy("x"),
@ -68,7 +62,7 @@ export default class AdminReportStackedChart extends Component {
return {
label: cd.label,
stack: "pageviews-stack",
data: cd.data,
data: Report.collapse(model, cd.data),
backgroundColor: cd.color,
};
}),
@ -79,7 +73,7 @@ export default class AdminReportStackedChart extends Component {
this._chart = new window.Chart(context, this._buildChartConfig(data));
});
}
},
_buildChartConfig(data) {
return {
@ -150,10 +144,10 @@ export default class AdminReportStackedChart extends Component {
},
},
};
}
},
_resetChart() {
this._chart?.destroy();
this._chart = null;
}
}
},
});

View File

@ -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>

View File

@ -1,45 +1,43 @@
import { classNames } from "@ember-decorators/component";
import { alias } from "@ember/object/computed";
import Component from "@ember/component";
import I18n from "I18n";
import { alias } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import { setting } from "discourse/lib/computed";
@classNames("admin-report-storage-stats")
export default class AdminReportStorageStats extends Component {
@setting("backup_location") backupLocation;
export default Component.extend({
classNames: ["admin-report-storage-stats"],
@alias("model.data.backups") backupStats;
@alias("model.data.uploads") uploadStats;
backupLocation: setting("backup_location"),
backupStats: alias("model.data.backups"),
uploadStats: alias("model.data.uploads"),
@discourseComputed("backupStats")
showBackupStats(stats) {
return stats && this.currentUser.admin;
}
},
@discourseComputed("backupLocation")
backupLocationName(backupLocation) {
return I18n.t(`admin.backups.location.${backupLocation}`);
}
},
@discourseComputed("backupStats.used_bytes")
usedBackupSpace(bytes) {
return I18n.toHumanSize(bytes);
}
},
@discourseComputed("backupStats.free_bytes")
freeBackupSpace(bytes) {
return I18n.toHumanSize(bytes);
}
},
@discourseComputed("uploadStats.used_bytes")
usedUploadSpace(bytes) {
return I18n.toHumanSize(bytes);
}
},
@discourseComputed("uploadStats.free_bytes")
freeUploadSpace(bytes) {
return I18n.toHumanSize(bytes);
}
}
},
});

View File

@ -1 +0,0 @@
{{html-safe this.formattedValue}}

View File

@ -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 { alias } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
@tagName("td")
@classNames("admin-report-table-cell")
@classNameBindings("type", "property")
@attributeBindings("value:title")
export default class AdminReportTableCell extends Component {
options = null;
@alias("label.type") type;
@alias("label.mainProperty") property;
@alias("computedLabel.formattedValue") formattedValue;
@alias("computedLabel.value") value;
export default Component.extend({
tagName: "td",
classNames: ["admin-report-table-cell"],
classNameBindings: ["type", "property"],
attributeBindings: ["value:title"],
options: null,
@discourseComputed("label", "data", "options")
computedLabel(label, data, options) {
return label.compute(data, options || {});
}
}
},
type: alias("label.type"),
property: alias("label.mainProperty"),
formatedValue: alias("computedLabel.formatedValue"),
value: alias("computedLabel.value"),
});

View File

@ -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}}

View File

@ -1,24 +1,19 @@
import {
attributeBindings,
classNameBindings,
classNames,
tagName,
} from "@ember-decorators/component";
import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";
@tagName("th")
@classNames("admin-report-table-header")
@classNameBindings("label.mainProperty", "label.type", "isCurrentSort")
@attributeBindings("label.title:title")
export default class AdminReportTableHeader extends Component {
export default Component.extend({
tagName: "th",
classNames: ["admin-report-table-header"],
classNameBindings: ["label.mainProperty", "label.type", "isCurrentSort"],
attributeBindings: ["label.title:title"],
@discourseComputed("currentSortLabel.sortProperty", "label.sortProperty")
isCurrentSort(currentSortField, labelSortField) {
return currentSortField === labelSortField;
}
},
@discourseComputed("currentSortDirection")
sortIcon(currentSortDirection) {
return currentSortDirection === 1 ? "caret-up" : "caret-down";
}
}
},
});

View File

@ -1,7 +0,0 @@
{{#each this.labels as |label|}}
<AdminReportTableCell
@label={{label}}
@data={{this.data}}
@options={{this.options}}
/>
{{/each}}

View File

@ -1,8 +1,6 @@
import { classNames, tagName } from "@ember-decorators/component";
import Component from "@ember/component";
@tagName("tr")
@classNames("admin-report-table-row")
export default class AdminReportTableRow extends Component {
options = null;
}
export default Component.extend({
tagName: "tr",
classNames: ["admin-report-table-row"],
options: null,
});

View File

@ -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>

View File

@ -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 { alias } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import { makeArray } from "discourse-common/lib/helpers";
const PAGES_LIMIT = 8;
@classNameBindings("sortable", "twoColumns")
@classNames("admin-report-table")
export default class AdminReportTable extends Component {
sortable = false;
sortDirection = 1;
@alias("options.perPage") perPage;
page = 0;
export default Component.extend({
classNameBindings: ["sortable", "twoColumns"],
classNames: ["admin-report-table"],
sortable: false,
sortDirection: 1,
perPage: alias("options.perPage"),
page: 0,
@discourseComputed("model.computedLabels.length")
twoColumns(labelsLength) {
return labelsLength === 2;
}
},
@discourseComputed(
"totalsForSample",
@ -35,12 +31,12 @@ export default class AdminReportTable extends Component {
.reduce((s, v) => s + v, 0);
return sum >= 1 && total && datesFiltering;
}
},
@discourseComputed("model.total", "options.total", "twoColumns")
showTotal(reportTotal, total, twoColumns) {
return reportTotal && total && twoColumns;
}
},
@discourseComputed(
"model.{average,data}",
@ -54,17 +50,17 @@ export default class AdminReportTable extends Component {
sampleTotalValue &&
hasTwoColumns
);
}
},
@discourseComputed("totalsForSample.1.value", "model.data.length")
averageForSample(totals, count) {
return (totals / count).toFixed(0);
}
},
@discourseComputed("model.data.length")
showSortingUI(dataLength) {
return dataLength >= 5;
}
},
@discourseComputed("totalsForSampleRow", "model.computedLabels")
totalsForSample(row, labels) {
@ -74,7 +70,7 @@ export default class AdminReportTable extends Component {
computedLabel.property = label.mainProperty;
return computedLabel;
});
}
},
@discourseComputed("model.data", "model.computedLabels")
totalsForSampleRow(rows, labels) {
@ -102,7 +98,7 @@ export default class AdminReportTable extends Component {
});
return totalsRow;
}
},
@discourseComputed("sortLabel", "sortDirection", "model.data.[]")
sortedData(sortLabel, sortDirection, data) {
@ -122,7 +118,7 @@ export default class AdminReportTable extends Component {
}
return data;
}
},
@discourseComputed("sortedData.[]", "perPage", "page")
paginatedData(data, perPage, page) {
@ -132,7 +128,7 @@ export default class AdminReportTable extends Component {
}
return data;
}
},
@discourseComputed("model.data", "perPage", "page")
pages(data, perPage, page) {
@ -160,19 +156,19 @@ export default class AdminReportTable extends Component {
}
return pages;
}
},
@action
changePage(page) {
this.set("page", page);
}
actions: {
changePage(page) {
this.set("page", page);
},
@action
sortByLabel(label) {
if (this.sortLabel === label) {
this.set("sortDirection", this.sortDirection === 1 ? -1 : 1);
} else {
this.set("sortLabel", label);
}
}
}
sortByLabel(label) {
if (this.sortLabel === label) {
this.set("sortDirection", this.sortDirection === 1 ? -1 : 1);
} else {
this.set("sortLabel", label);
}
},
},
});

View File

@ -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>

View File

@ -1,5 +1,4 @@
import { tagName } from "@ember-decorators/component";
import Component from "@ember/component";
@tagName("tr")
export default class AdminReportTrustLevelCounts extends Component {}
export default Component.extend({
tagName: "tr",
});

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