diff --git a/.eslintignore b/.eslintignore index 4447ad6726..885e2cf3c7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -12,3 +12,4 @@ node_modules/ spec/ dist/ tmp/ +documentation/ diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml deleted file mode 100644 index aec086257a..0000000000 --- a/.github/workflows/documentation.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Documentation - -on: - pull_request: - push: - branches: - - main - -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: 30 - - 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: | - 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 - bundle install --jobs 4 - bundle clean - - - 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 - run: yarn install - - - name: Check Chat documentation - run: | - LOAD_PLUGINS=1 bin/rake chat:doc - - if [ ! -z "$(git status --porcelain plugins/chat/docs/)" ]; then - echo "Chat documentation is not up to date. To resolve, run:" - echo " LOAD_PLUGINS=1 bin/rake chat:doc" - echo - echo "Or manually apply the diff printed below:" - echo "---------------------------------------------" - git -c color.ui=always diff plugins/chat/docs/ - exit 1 - fi - timeout-minutes: 30 diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index b2125eea16..6489153036 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -22,6 +22,9 @@ jobs: timeout-minutes: 30 steps: + - name: Set working directory owner + run: chown root:root . + - uses: actions/checkout@v3 with: fetch-depth: 1 @@ -70,7 +73,9 @@ jobs: - name: syntax_tree if: ${{ !cancelled() }} - run: bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake') + run: | + set -E + bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake') - name: ESLint (core) if: ${{ !cancelled() }} @@ -87,8 +92,10 @@ jobs: yarn pprettier --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/**/*.js" \ + "plugins/**/assets/javascripts/**/*.hbs" \ - name: Ember template lint if: ${{ !cancelled() }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9374c85d70..f8759bdb47 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: 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.2') && '-ruby-3.2.0' || '' }} + 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 env: @@ -29,7 +29,7 @@ jobs: RAILS_ENV: test PGUSER: discourse PGPASSWORD: discourse - USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' }} + USES_PARALLEL_DATABASES: ${{ matrix.build_type == 'backend' || matrix.build_type == 'system' }} CAPBYARA_DEFAULT_MAX_WAIT_TIME: 4 strategy: @@ -38,7 +38,7 @@ jobs: matrix: build_type: [backend, frontend, system, annotations] target: [core, plugins] - ruby: ['3.1'] + ruby: ['3.2'] exclude: - build_type: annotations target: plugins @@ -46,6 +46,9 @@ jobs: target: core # Handled by core_frontend_tests job (below) steps: + - name: Set working directory owner + run: chown root:root . + - uses: actions/checkout@v3 with: fetch-depth: 1 @@ -156,6 +159,22 @@ jobs: 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 @@ -173,13 +192,17 @@ jobs: 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 --format documentation --profile + 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 --format documentation --profile + run: LOAD_PLUGINS=1 bin/rspec plugins/*/spec/system - name: Upload failed system test screenshots uses: actions/upload-artifact@v3 diff --git a/.jsdoc b/.jsdoc index 92345fda22..06d1cf6083 100644 --- a/.jsdoc +++ b/.jsdoc @@ -3,5 +3,19 @@ { "source": { "excludePattern": "" + }, + "templates": { + "default": { + "includeDate": false + } + }, + "opts": { + "template": "./node_modules/tidy-jsdoc", + "prism-theme": "prism-custom", + "encoding": "utf8", + "recurse": true + }, + "metadata": { + "title": "Discourse" } } diff --git a/.licensed.yml b/.licensed.yml index 38f97dd7e4..044e732fad 100644 --- a/.licensed.yml +++ b/.licensed.yml @@ -15,6 +15,7 @@ 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) diff --git a/.licensee.json b/.licensee.json index 6fe0d279c8..d93f88f460 100644 --- a/.licensee.json +++ b/.licensee.json @@ -11,7 +11,8 @@ "packages": { "@fortawesome/fontawesome-free": "*", "ember-template-lint-plugin-discourse": "*", - "squoosh": "2.0.0" + "squoosh": "2.0.0", + "taffydb": "2.6.2" }, "corrections": true } \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 93b35f929e..c1caccc068 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,6 +3,7 @@ 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 diff --git a/.rubocop.yml b/.rubocop.yml index 4d4ad6d8d8..68abba36ad 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,3 +7,7 @@ Discourse/NoAddReferenceOrAliasesActiveRecordMigration: Discourse/NoResetColumnInformationInMigrations: Enabled: true + +Lint/Debugger: + Exclude: + - script/**/* diff --git a/.ruby-version.sample b/.ruby-version.sample index ff365e06b9..e4604e3afd 100644 --- a/.ruby-version.sample +++ b/.ruby-version.sample @@ -1 +1 @@ -3.1.3 +3.2.1 diff --git a/.template-lintrc.js b/.template-lintrc.js index a5c4998a0c..77a11b8f2c 100644 --- a/.template-lintrc.js +++ b/.template-lintrc.js @@ -15,7 +15,7 @@ module.exports = { "directory-item-value", "directory-table-header-title", "loading-spinner", - "mobile-directory-item-label", + "directory-item-label", ], }, "no-implicit-this": { diff --git a/Gemfile b/Gemfile index 4bd110bf4b..259140f992 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ else # 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.1" + rails_version = "7.0.4.3" gem "actionmailer", rails_version gem "actionpack", rails_version gem "actionview", rails_version @@ -105,7 +105,7 @@ gem "pg" gem "mini_sql" gem "pry-rails", require: false gem "pry-byebug", require: false -gem "r2", require: false +gem "rtlcss", require: false gem "rake" gem "thor", require: false @@ -180,6 +180,7 @@ group :development do gem "better_errors", platform: :mri, require: !!ENV["BETTER_ERRORS"] gem "binding_of_caller" gem "yaml-lint" + gem "yard" end if ENV["ALLOW_DEV_POPULATE"] == "1" @@ -229,10 +230,9 @@ gem "logstash-event", require: false gem "logstash-logger", require: false gem "logster" -# 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" +# These are forks of sassc and sassc-rails with dart-sass support +gem "dartsass-ruby" +gem "dartsass-sprockets" gem "rotp", require: false @@ -279,3 +279,5 @@ 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" diff --git a/Gemfile.lock b/Gemfile.lock index 0f22d6f063..49690bd677 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,25 +17,25 @@ GIT GEM remote: https://rubygems.org/ specs: - actionmailer (7.0.4.1) - actionpack (= 7.0.4.1) - actionview (= 7.0.4.1) - activejob (= 7.0.4.1) - activesupport (= 7.0.4.1) + 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) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.4.1) - actionview (= 7.0.4.1) - activesupport (= 7.0.4.1) + actionpack (7.0.4.3) + actionview (= 7.0.4.3) + activesupport (= 7.0.4.3) rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actionview (7.0.4.1) - activesupport (= 7.0.4.1) + actionview (7.0.4.3) + activesupport (= 7.0.4.3) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -44,15 +44,15 @@ GEM actionview (>= 6.0.a) active_model_serializers (0.8.4) activemodel (>= 3.0) - activejob (7.0.4.1) - activesupport (= 7.0.4.1) + activejob (7.0.4.3) + activesupport (= 7.0.4.3) globalid (>= 0.3.6) - activemodel (7.0.4.1) - activesupport (= 7.0.4.1) - activerecord (7.0.4.1) - activemodel (= 7.0.4.1) - activesupport (= 7.0.4.1) - activesupport (7.0.4.1) + 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) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -88,7 +88,7 @@ GEM rack (>= 0.9.0) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) - bootsnap (1.15.0) + bootsnap (1.16.0) msgpack (~> 1.2) builder (3.2.4) bullet (7.0.7) @@ -110,7 +110,7 @@ GEM chunky_png (1.4.0) coderay (1.1.3) colored2 (3.1.2) - concurrent-ruby (1.2.0) + concurrent-ruby (1.2.2) connection_pool (2.3.0) cose (1.3.0) cbor (~> 0.5.9) @@ -121,6 +121,14 @@ GEM crass (1.0.6) css_parser (1.14.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) @@ -138,7 +146,7 @@ GEM regexp_parser (~> 2.2) email_reply_trimmer (0.1.13) erubi (1.12.0) - excon (0.97.2) + excon (0.99.0) execjs (2.8.1) exifr (1.3.10) fabrication (2.30.0) @@ -149,7 +157,7 @@ GEM faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) faraday-net_http (3.0.2) - faraday-retry (2.0.0) + faraday-retry (2.1.0) faraday (~> 2.0) fast_blank (1.0.1) fast_xs (0.8.0) @@ -157,8 +165,13 @@ GEM ffi (1.15.5) fspath (3.1.2) gc_tracer (1.5.1) - globalid (1.0.1) + globalid (1.1.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) @@ -169,7 +182,7 @@ GEM http_accept_language (2.1.1) i18n (1.12.0) concurrent-ruby (~> 1.0) - image_optim (0.31.2) + image_optim (0.31.3) exifr (~> 1.2, >= 1.2.2) fspath (~> 3.0) image_size (>= 1.5, < 4) @@ -186,7 +199,7 @@ GEM hana (~> 1.3) regexp_parser (~> 2.0) uri_template (~> 0.7) - jwt (2.6.0) + jwt (2.7.0) kgio (2.11.4) libv8-node (16.10.0.0) libv8-node (16.10.0.0-aarch64-linux) @@ -206,7 +219,7 @@ GEM logstash-event (1.2.02) logstash-logger (0.26.1) logstash-event (~> 1.2) - logster (2.11.3) + logster (2.12.2) loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) @@ -227,10 +240,10 @@ GEM mini_sql (1.4.0) mini_suffix (0.3.3) ffi (~> 1.9) - minitest (5.17.0) + minitest (5.18.0) mocha (2.0.2) ruby2_keywords (>= 0.0.5) - msgpack (1.6.0) + msgpack (1.6.1) multi_json (1.15.0) multi_xml (0.6.0) mustache (1.1.1) @@ -246,16 +259,16 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.8) - nokogiri (1.14.0) + nokogiri (1.14.2) mini_portile2 (~> 2.8.0) racc (~> 1.4) - nokogiri (1.14.0-aarch64-linux) + nokogiri (1.14.2-aarch64-linux) racc (~> 1.4) - nokogiri (1.14.0-arm64-darwin) + nokogiri (1.14.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.14.0-x86_64-darwin) + nokogiri (1.14.2-x86_64-darwin) racc (~> 1.4) - nokogiri (1.14.0-x86_64-linux) + nokogiri (1.14.2-x86_64-linux) racc (~> 1.4) oauth (1.1.0) oauth-tty (~> 1.0, >= 1.0.1) @@ -292,17 +305,17 @@ GEM omniauth-twitter (1.4.0) omniauth-oauth (~> 1.1) rack - openssl (3.0.2) - openssl-signature_algorithm (1.2.1) - openssl (> 2.0, < 3.1) + openssl (3.1.0) + openssl-signature_algorithm (1.3.0) + openssl (> 2.0) optimist (3.0.1) parallel (1.22.1) - parallel_tests (4.1.0) + parallel_tests (4.2.0) parallel - parser (3.2.0.0) + parser (3.2.1.1) ast (~> 2.4.1) - pg (1.4.5) - prettier_print (1.2.0) + pg (1.4.6) + prettier_print (1.2.1) progress (3.6.0) pry (0.14.2) coderay (~> 1.1) @@ -313,16 +326,15 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (5.0.1) - puma (6.0.2) + puma (6.1.1) nio4r (~> 2.0) - r2 (0.2.7) racc (1.6.2) - rack (2.2.6.2) + rack (2.2.6.4) rack-mini-profiler (3.0.0) rack (>= 1.2.0) rack-protection (3.0.5) rack - rack-test (2.0.2) + rack-test (2.1.0) rack (>= 1.3) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) @@ -336,15 +348,15 @@ GEM rails_multisite (4.0.1) activerecord (> 5.0, < 7.1) railties (> 5.0, < 7.1) - railties (7.0.4.1) - actionpack (= 7.0.4.1) - activesupport (= 7.0.4.1) + railties (7.0.4.3) + actionpack (= 7.0.4.3) + activesupport (= 7.0.4.3) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) - raindrops (0.20.0) + raindrops (0.20.1) rake (13.0.6) rb-fsevent (0.11.2) rb-inotify (0.10.1) @@ -354,10 +366,10 @@ GEM msgpack (>= 0.4.3) optimist (>= 3.0.0) rchardet (1.8.0) - redis (4.8.0) + redis (4.8.1) redis-namespace (1.10.0) redis (>= 4) - regexp_parser (2.6.2) + regexp_parser (2.7.0) request_store (1.5.1) rack (>= 1.4) rexml (3.2.5) @@ -371,7 +383,7 @@ GEM rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.12.0) - rspec-core (3.12.0) + rspec-core (3.12.1) rspec-support (~> 3.12.0) rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) @@ -379,7 +391,7 @@ GEM rspec-html-matchers (0.10.0) nokogiri (~> 1) rspec (>= 3.0.0.a) - rspec-mocks (3.12.3) + rspec-mocks (3.12.4) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-rails (6.0.1) @@ -398,46 +410,50 @@ GEM json-schema (>= 2.2, < 4.0) railties (>= 3.1, < 7.1) rspec-core (>= 2.14) - rubocop (1.44.0) + rtlcss (0.2.0) + mini_racer (~> 0.6.3) + rubocop (1.48.1) json (~> 2.3) parallel (~> 1.10) parser (>= 3.2.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.24.1, < 2.0) + rubocop-ast (>= 1.26.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.24.1) - parser (>= 3.1.1.0) - rubocop-capybara (2.17.0) + rubocop-ast (1.27.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.17.1) rubocop (~> 1.41) - rubocop-discourse (3.0.3) + rubocop-discourse (3.2.0) rubocop (>= 1.1.0) rubocop-rspec (>= 2.0.0) - rubocop-rspec (2.18.1) + rubocop-rspec (2.19.0) rubocop (~> 1.33) rubocop-capybara (~> 2.17) - ruby-prof (1.4.5) - ruby-progressbar (1.11.0) + ruby-prof (1.6.1) + ruby-progressbar (1.13.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.0) + sanitize (6.0.1) crass (~> 1.0.2) nokogiri (>= 1.12.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 - selenium-webdriver (4.8.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) @@ -462,15 +478,17 @@ GEM sprockets (>= 3.0.0) sshkey (2.0.0) stackprof (0.2.23) - syntax_tree (5.2.0) + syntax_tree (6.0.2) prettier_print (>= 1.2.0) syntax_tree-disable_ternary (1.0.0) - test-prof (1.1.0) + test-prof (1.2.0) thor (1.2.1) - tilt (2.0.11) - timeout (0.3.1) - tzinfo (2.0.5) + tilt (2.1.0) + timeout (0.3.2) + tzinfo (2.0.6) 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) @@ -502,7 +520,9 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yaml-lint (0.1.2) - zeitwerk (2.6.6) + yard (0.9.28) + webrick (~> 1.7.0) + zeitwerk (2.6.7) PLATFORMS aarch64-linux @@ -514,14 +534,14 @@ PLATFORMS x86_64-linux DEPENDENCIES - actionmailer (= 7.0.4.1) - actionpack (= 7.0.4.1) - actionview (= 7.0.4.1) + actionmailer (= 7.0.4.3) + actionpack (= 7.0.4.3) + actionview (= 7.0.4.3) actionview_precompiler active_model_serializers (~> 0.8.3) - activemodel (= 7.0.4.1) - activerecord (= 7.0.4.1) - activesupport (= 7.0.4.1) + activemodel (= 7.0.4.3) + activerecord (= 7.0.4.3) + activesupport (= 7.0.4.3) addressable annotate aws-sdk-s3 @@ -539,6 +559,8 @@ DEPENDENCIES cose cppjieba_rb css_parser + dartsass-ruby + dartsass-sprockets diffy digest discourse-fonts @@ -600,13 +622,12 @@ DEPENDENCIES pry-byebug pry-rails puma - r2 rack rack-mini-profiler rack-protection rails_failover rails_multisite - railties (= 7.0.4.1) + railties (= 7.0.4.3) rake rb-fsevent rbtrace @@ -621,13 +642,12 @@ DEPENDENCIES rspec-rails rss rswag-specs + rtlcss rubocop-discourse ruby-prof ruby-readability rubyzip sanitize - sassc (= 2.0.1) - sassc-rails selenium-webdriver shoulda-matchers sidekiq @@ -640,6 +660,7 @@ DEPENDENCIES syntax_tree-disable_ternary test-prof thor + tzinfo-data uglifier unf unicorn @@ -649,6 +670,7 @@ DEPENDENCIES webrick xorcist yaml-lint + yard BUNDLED WITH - 2.4.1 + 2.4.4 diff --git a/README.md b/README.md index 2b914eab91..42452e4f87 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ To get your environment setup, follow the community setup guide for your operati If you're familiar with how Rails works and are comfortable setting up your own environment, you can also try out the [**Discourse Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md), which is aimed primarily at Ubuntu and macOS environments. -Before you get started, ensure you have the following minimum versions: [Ruby 3.1+](https://www.ruby-lang.org/en/downloads/), [PostgreSQL 13](https://www.postgresql.org/download/), [Redis 7](https://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first! +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! ## Setting up Discourse diff --git a/app/assets/javascripts/admin/addon/templates/components/ace-editor.hbs b/app/assets/javascripts/admin/addon/components/ace-editor.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/ace-editor.hbs rename to app/assets/javascripts/admin/addon/components/ace-editor.hbs diff --git a/app/assets/javascripts/admin/addon/components/ace-editor.js b/app/assets/javascripts/admin/addon/components/ace-editor.js index 06ed376303..84aa4351e1 100644 --- a/app/assets/javascripts/admin/addon/components/ace-editor.js +++ b/app/assets/javascripts/admin/addon/components/ace-editor.js @@ -1,31 +1,33 @@ +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, observes } from "discourse-common/utils/decorators"; -import { on } from "@ember/object/evented"; +import { bind } from "discourse-common/utils/decorators"; const COLOR_VARS_REGEX = /\$(primary|secondary|tertiary|quaternary|header_background|header_primary|highlight|danger|success|love)(\s|;|-(low|medium|high))/g; -export default Component.extend({ - mode: "css", - classNames: ["ace-wrapper"], - _editor: null, - _skipContentChangeEvent: null, - disabled: false, - htmlPlaceholder: false, +@classNames("ace-wrapper") +export default class AceEditor extends Component { + mode = "css"; + disabled = false; + htmlPlaceholder = false; + _editor = null; + _skipContentChangeEvent = null; @observes("editorId") editorIdChanged() { if (this.autofocus) { this.send("focus"); } - }, + } didRender() { this._skipContentChangeEvent = false; - }, + } @observes("content") contentChanged() { @@ -33,14 +35,14 @@ export default Component.extend({ 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() { @@ -49,12 +51,12 @@ export default Component.extend({ placeholder: this.placeholder, }); } - }, + } @observes("disabled") disabledStateChanged() { this.changeDisabledState(); - }, + } changeDisabledState() { const editor = this._editor; @@ -67,9 +69,10 @@ export default Component.extend({ }); editor.container.parentNode.setAttribute("data-disabled", disabled); } - }, + } - _destroyEditor: on("willDestroyElement", function () { + @on("willDestroyElement") + _destroyEditor() { if (this._editor) { this._editor.destroy(); this._editor = null; @@ -80,16 +83,16 @@ export default Component.extend({ } $(window).off("ace:resize"); - }), + } resize() { if (this._editor) { this._editor.resize(); } - }, + } didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); loadScript("/javascripts/ace/ace.js").then(() => { window.ace.require(["ace/ace"], (loadedAce) => { loadedAce.config.set("loadWorkerFromBlob", false); @@ -153,13 +156,13 @@ export default Component.extend({ this._darkModeListener.addListener(this.setAceTheme); }); }); - }, + } willDestroyElement() { if (this._darkModeListener) { this._darkModeListener.removeListener(this.setAceTheme); } - }, + } @bind setAceTheme() { @@ -170,7 +173,7 @@ export default Component.extend({ this._editor.setTheme( `ace/theme/${schemeType === "dark" ? "chaos" : "chrome"}` ); - }, + } warnSCSSDeprecations() { if ( @@ -197,21 +200,20 @@ export default Component.extend({ this._editor.getSession().setAnnotations(warnings); - this.setWarning( + this.setWarning?.( warnings.length ? I18n.t("admin.customize.theme.scss_color_variables_warning") : false ); - }, + } - actions: { - focus() { - if (this._editor) { - this._editor.focus(); - this._editor.navigateFileEnd(); - } - }, - }, + @action + focus() { + if (this._editor) { + this._editor.focus(); + this._editor.navigateFileEnd(); + } + } _overridePlaceholder(loadedAce) { const originalPlaceholderSetter = @@ -239,5 +241,5 @@ export default Component.extend({ this.$updatePlaceholder(); }; - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-backups-logs.hbs b/app/assets/javascripts/admin/addon/components/admin-backups-logs.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-backups-logs.hbs rename to app/assets/javascripts/admin/addon/components/admin-backups-logs.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-backups-logs.js b/app/assets/javascripts/admin/addon/components/admin-backups-logs.js index c707c99b70..e1c12d04a3 100644 --- a/app/assets/javascripts/admin/addon/components/admin-backups-logs.js +++ b/app/assets/javascripts/admin/addon/components/admin-backups-logs.js @@ -1,28 +1,26 @@ -import { observes, on } from "discourse-common/utils/decorators"; +import { classNames } from "@ember-decorators/component"; +import { observes, on } from "@ember-decorators/object"; import Component from "@ember/component"; import I18n from "I18n"; import discourseDebounce from "discourse-common/lib/debounce"; import { scheduleOnce } from "@ember/runloop"; -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(); - }, +@classNames("admin-backups-logs") +export default class AdminBackupsLogs extends Component { + showLoadingSpinner = false; + hasFormattedLogs = false; + noLogsMessage = I18n.t("admin.backups.logs.none"); + formattedLogs = ""; + index = 0; _reset() { this.setProperties({ formattedLogs: "", index: 0 }); - }, + } _scrollDown() { const div = this.element; div.scrollTop = div.scrollHeight; - }, + } @on("init") @observes("logs.[]") @@ -31,7 +29,7 @@ export default Component.extend({ this._reset(); // reset the cached logs whenever the model is reset this.renderLogs(); } - }, + } _updateFormattedLogsFunc() { const logs = this.logs; @@ -55,13 +53,13 @@ export default Component.extend({ this.renderLogs(); scheduleOnce("afterRender", this, this._scrollDown); - }, + } @on("init") @observes("logs.[]") _updateFormattedLogs() { discourseDebounce(this, this._updateFormattedLogsFunc, 150); - }, + } renderLogs() { const formattedLogs = this.formattedLogs; @@ -76,5 +74,5 @@ export default Component.extend({ } else { this.set("showLoadingSpinner", false); } - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-editable-field.hbs b/app/assets/javascripts/admin/addon/components/admin-editable-field.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-editable-field.hbs rename to app/assets/javascripts/admin/addon/components/admin-editable-field.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-editable-field.js b/app/assets/javascripts/admin/addon/components/admin-editable-field.js index 892a3208e8..a99265b20c 100644 --- a/app/assets/javascripts/admin/addon/components/admin-editable-field.js +++ b/app/assets/javascripts/admin/addon/components/admin-editable-field.js @@ -1,28 +1,22 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; import { action } from "@ember/object"; -export default Component.extend({ - tagName: "", - - buffer: "", - editing: false, - - init() { - this._super(...arguments); - this.set("editing", false); - }, +@tagName("") +export default class AdminEditableField extends Component { + buffer = ""; + editing = false; @action edit(event) { event?.preventDefault(); this.set("buffer", this.value); this.toggleProperty("editing"); - }, + } - actions: { - save() { - // Action has to toggle 'editing' property. - this.action(this.buffer); - }, - }, -}); + @action + save() { + // Action has to toggle 'editing' property. + this.action(this.buffer); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-form-row.hbs b/app/assets/javascripts/admin/addon/components/admin-form-row.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-form-row.hbs rename to app/assets/javascripts/admin/addon/components/admin-form-row.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-form-row.js b/app/assets/javascripts/admin/addon/components/admin-form-row.js index 6217c6b913..266301263d 100644 --- a/app/assets/javascripts/admin/addon/components/admin-form-row.js +++ b/app/assets/javascripts/admin/addon/components/admin-form-row.js @@ -1,4 +1,5 @@ +import { classNames } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - classNames: ["row"], -}); + +@classNames("row") +export default class AdminFormRow extends Component {} diff --git a/app/assets/javascripts/admin/addon/components/admin-graph.js b/app/assets/javascripts/admin/addon/components/admin-graph.js index 1107abeb2c..4ef05fca32 100644 --- a/app/assets/javascripts/admin/addon/components/admin-graph.js +++ b/app/assets/javascripts/admin/addon/components/admin-graph.js @@ -1,9 +1,10 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; import loadScript from "discourse/lib/load-script"; -export default Component.extend({ - tagName: "canvas", - type: "line", +@tagName("canvas") +export default class AdminGraph extends Component { + type = "line"; refreshChart() { const ctx = this.element.getContext("2d"); @@ -49,11 +50,11 @@ export default Component.extend({ }; this._chart = new window.Chart(ctx, config); - }, + } didInsertElement() { loadScript("/javascripts/Chart.min.js").then(() => this.refreshChart.apply(this) ); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-nav.hbs b/app/assets/javascripts/admin/addon/components/admin-nav.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-nav.hbs rename to app/assets/javascripts/admin/addon/components/admin-nav.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-nav.js b/app/assets/javascripts/admin/addon/components/admin-nav.js index 0e6d50b17d..2542a2238b 100644 --- a/app/assets/javascripts/admin/addon/components/admin-nav.js +++ b/app/assets/javascripts/admin/addon/components/admin-nav.js @@ -1,4 +1,5 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - tagName: "", -}); + +@tagName("") +export default class AdminNav extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-penalty-history.hbs b/app/assets/javascripts/admin/addon/components/admin-penalty-history.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-penalty-history.hbs rename to app/assets/javascripts/admin/addon/components/admin-penalty-history.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-penalty-history.js b/app/assets/javascripts/admin/addon/components/admin-penalty-history.js index 32c288b248..0931ac8e4a 100644 --- a/app/assets/javascripts/admin/addon/components/admin-penalty-history.js +++ b/app/assets/javascripts/admin/addon/components/admin-penalty-history.js @@ -1,16 +1,16 @@ +import { classNames } from "@ember-decorators/component"; import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - classNames: ["penalty-history"], - +@classNames("penalty-history") +export default class AdminPenaltyHistory extends Component { @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 Component.extend({ return "danger"; } return ""; - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-penalty-post-action.hbs b/app/assets/javascripts/admin/addon/components/admin-penalty-post-action.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-penalty-post-action.hbs rename to app/assets/javascripts/admin/addon/components/admin-penalty-post-action.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-penalty-post-action.js b/app/assets/javascripts/admin/addon/components/admin-penalty-post-action.js index eb201ae089..0831492546 100644 --- a/app/assets/javascripts/admin/addon/components/admin-penalty-post-action.js +++ b/app/assets/javascripts/admin/addon/components/admin-penalty-post-action.js @@ -1,5 +1,6 @@ -import Component from "@ember/component"; +import { action } from "@ember/object"; import { equal } from "@ember/object/computed"; +import Component from "@ember/component"; import discourseComputed, { afterRender, } from "discourse-common/utils/decorators"; @@ -7,30 +8,28 @@ import I18n from "I18n"; const ACTIONS = ["delete", "delete_replies", "edit", "none"]; -export default Component.extend({ - postId: null, - postAction: null, - postEdit: null, +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}`) }; }); - }, + } - editing: equal("postAction", "edit"), + @action + penaltyChanged(postAction) { + this.set("postAction", postAction); - actions: { - penaltyChanged(postAction) { - this.set("postAction", postAction); - - // If we switch to edit mode, jump to the edit textarea - if (postAction === "edit") { - this._focusEditTextarea(); - } - }, - }, + // If we switch to edit mode, jump to the edit textarea + if (postAction === "edit") { + this._focusEditTextarea(); + } + } @afterRender _focusEditTextarea() { @@ -38,5 +37,5 @@ export default Component.extend({ const body = elem.closest(".modal-body"); body.scrollTo(0, body.clientHeight); elem.querySelector(".post-editor").focus(); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-penalty-reason.hbs b/app/assets/javascripts/admin/addon/components/admin-penalty-reason.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-penalty-reason.hbs rename to app/assets/javascripts/admin/addon/components/admin-penalty-reason.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-penalty-reason.js b/app/assets/javascripts/admin/addon/components/admin-penalty-reason.js index ca6164e763..e8d037d249 100644 --- a/app/assets/javascripts/admin/addon/components/admin-penalty-reason.js +++ b/app/assets/javascripts/admin/addon/components/admin-penalty-reason.js @@ -1,43 +1,46 @@ +import { tagName } from "@ember-decorators/component"; +import { equal } from "@ember/object/computed"; import Component from "@ember/component"; import { action } from "@ember/object"; -import { equal } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import I18n from "I18n"; const CUSTOM_REASON_KEY = "custom"; -export default Component.extend({ - tagName: "", - selectedReason: CUSTOM_REASON_KEY, - customReason: "", - reasonKeys: [ +@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, - ], - isCustomReason: equal("selectedReason", 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) { @@ -48,5 +51,5 @@ export default Component.extend({ I18n.t(`admin.user.suspend_reasons.${this.selectedReason}`) ); } - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-penalty-similar-users.hbs b/app/assets/javascripts/admin/addon/components/admin-penalty-similar-users.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-penalty-similar-users.hbs rename to app/assets/javascripts/admin/addon/components/admin-penalty-similar-users.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-penalty-similar-users.js b/app/assets/javascripts/admin/addon/components/admin-penalty-similar-users.js index 241927a7b7..52996d458f 100644 --- a/app/assets/javascripts/admin/addon/components/admin-penalty-similar-users.js +++ b/app/assets/javascripts/admin/addon/components/admin-penalty-similar-users.js @@ -1,10 +1,10 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; import { action } from "@ember/object"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - tagName: "", - +@tagName("") +export default class AdminPenaltySimilarUsers extends Component { @discourseComputed("penaltyType") penaltyField(penaltyType) { if (penaltyType === "suspend") { @@ -12,7 +12,7 @@ export default Component.extend({ } else if (penaltyType === "silence") { return "can_be_silenced"; } - }, + } @action selectUserId(userId, event) { @@ -25,5 +25,5 @@ export default Component.extend({ } else { this.selectedUserIds.removeObject(userId); } - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-chart.hbs b/app/assets/javascripts/admin/addon/components/admin-report-chart.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-chart.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-chart.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-chart.js b/app/assets/javascripts/admin/addon/components/admin-report-chart.js index 1c4ad1caa3..64f09aa0d7 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-chart.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-chart.js @@ -1,3 +1,4 @@ +import { classNames } from "@ember-decorators/component"; import Report from "admin/models/report"; import Component from "@ember/component"; import discourseDebounce from "discourse-common/lib/debounce"; @@ -7,31 +8,31 @@ import { number } from "discourse/lib/formatter"; import { schedule } from "@ember/runloop"; import { bind } from "discourse-common/utils/decorators"; -export default Component.extend({ - classNames: ["admin-report-chart"], - limit: 8, - total: 0, - options: null, +@classNames("admin-report-chart") +export default class AdminReportChart extends Component { + limit = 8; + total = 0; + options = null; didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); window.addEventListener("resize", this._resizeHandler); - }, + } willDestroyElement() { - this._super(...arguments); + super.willDestroyElement(...arguments); window.removeEventListener("resize", this._resizeHandler); this._resetChart(); - }, + } didReceiveAttrs() { - this._super(...arguments); + super.didReceiveAttrs(...arguments); discourseDebounce(this, this._scheduleChartRendering, 100); - }, + } _scheduleChartRendering() { schedule("afterRender", () => { @@ -40,7 +41,7 @@ export default Component.extend({ this.element && this.element.querySelector(".chart-canvas") ); }); - }, + } _renderChart(model, chartCanvas) { if (!chartCanvas) { @@ -99,7 +100,7 @@ export default Component.extend({ this._buildChartConfig(data, this.options) ); }); - }, + } _buildChartConfig(data, options) { return { @@ -161,21 +162,21 @@ export default Component.extend({ }, }, }; - }, + } _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); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-counters.hbs b/app/assets/javascripts/admin/addon/components/admin-report-counters.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-counters.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-counters.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-counters.js b/app/assets/javascripts/admin/addon/components/admin-report-counters.js index c956b93e35..6e3dab5236 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-counters.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-counters.js @@ -1,6 +1,6 @@ +import { attributeBindings, classNames } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - classNames: ["admin-report-counters"], - attributeBindings: ["model.description:title"], -}); +@classNames("admin-report-counters") +@attributeBindings("model.description:title") +export default class AdminReportCounters extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-counts.hbs b/app/assets/javascripts/admin/addon/components/admin-report-counts.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-counts.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-counts.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-counts.js b/app/assets/javascripts/admin/addon/components/admin-report-counts.js index 03c690dbbd..fd1b6cc68d 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-counts.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-counts.js @@ -1,11 +1,12 @@ -import Component from "@ember/component"; +import { classNameBindings, tagName } from "@ember-decorators/component"; 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"], -}); +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; +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-inline-table.hbs b/app/assets/javascripts/admin/addon/components/admin-report-inline-table.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-inline-table.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-inline-table.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-inline-table.js b/app/assets/javascripts/admin/addon/components/admin-report-inline-table.js index 753320cc31..3e770da646 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-inline-table.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-inline-table.js @@ -1,4 +1,5 @@ +import { classNames } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - classNames: ["admin-report-inline-table"], -}); + +@classNames("admin-report-inline-table") +export default class AdminReportInlineTable extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-per-day-counts.hbs b/app/assets/javascripts/admin/addon/components/admin-report-per-day-counts.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-per-day-counts.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-per-day-counts.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-per-day-counts.js b/app/assets/javascripts/admin/addon/components/admin-report-per-day-counts.js index 7f039c061e..ee48a5138f 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-per-day-counts.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-per-day-counts.js @@ -1,4 +1,5 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - tagName: "tr", -}); + +@tagName("tr") +export default class AdminReportPerDayCounts extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-stacked-chart.hbs b/app/assets/javascripts/admin/addon/components/admin-report-stacked-chart.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-stacked-chart.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-stacked-chart.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-stacked-chart.js b/app/assets/javascripts/admin/addon/components/admin-report-stacked-chart.js index 6b773957f6..b69e75141e 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-stacked-chart.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-stacked-chart.js @@ -1,3 +1,4 @@ +import { classNames } from "@ember-decorators/component"; import Report from "admin/models/report"; import Component from "@ember/component"; import discourseDebounce from "discourse-common/lib/debounce"; @@ -7,32 +8,31 @@ import { number } from "discourse/lib/formatter"; import { schedule } from "@ember/runloop"; import { bind } from "discourse-common/utils/decorators"; -export default Component.extend({ - classNames: ["admin-report-chart", "admin-report-stacked-chart"], - +@classNames("admin-report-chart", "admin-report-stacked-chart") +export default class AdminReportStackedChart extends Component { didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); window.addEventListener("resize", this._resizeHandler); - }, + } willDestroyElement() { - this._super(...arguments); + super.willDestroyElement(...arguments); window.removeEventListener("resize", this._resizeHandler); this._resetChart(); - }, + } didReceiveAttrs() { - this._super(...arguments); + super.didReceiveAttrs(...arguments); discourseDebounce(this, this._scheduleChartRendering, 100); - }, + } @bind _resizeHandler() { discourseDebounce(this, this._scheduleChartRendering, 500); - }, + } _scheduleChartRendering() { schedule("afterRender", () => { @@ -45,7 +45,7 @@ export default Component.extend({ this.element.querySelector(".chart-canvas") ); }); - }, + } _renderChart(model, chartCanvas) { if (!chartCanvas) { @@ -79,7 +79,7 @@ export default Component.extend({ this._chart = new window.Chart(context, this._buildChartConfig(data)); }); - }, + } _buildChartConfig(data) { return { @@ -150,10 +150,10 @@ export default Component.extend({ }, }, }; - }, + } _resetChart() { this._chart?.destroy(); this._chart = null; - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-storage-stats.hbs b/app/assets/javascripts/admin/addon/components/admin-report-storage-stats.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-storage-stats.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-storage-stats.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-storage-stats.js b/app/assets/javascripts/admin/addon/components/admin-report-storage-stats.js index e12bc6432f..ee0d7d2476 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-storage-stats.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-storage-stats.js @@ -1,43 +1,45 @@ +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"; -export default Component.extend({ - classNames: ["admin-report-storage-stats"], +@classNames("admin-report-storage-stats") +export default class AdminReportStorageStats extends Component { + @setting("backup_location") backupLocation; - backupLocation: setting("backup_location"), - backupStats: alias("model.data.backups"), - uploadStats: alias("model.data.uploads"), + @alias("model.data.backups") backupStats; + + @alias("model.data.uploads") uploadStats; @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); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-table-cell.hbs b/app/assets/javascripts/admin/addon/components/admin-report-table-cell.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-table-cell.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-table-cell.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-table-cell.js b/app/assets/javascripts/admin/addon/components/admin-report-table-cell.js index 838d8dbe1d..89997c9afa 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-table-cell.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-table-cell.js @@ -1,21 +1,27 @@ -import Component from "@ember/component"; +import { + attributeBindings, + classNameBindings, + classNames, + tagName, +} from "@ember-decorators/component"; import { alias } from "@ember/object/computed"; +import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - tagName: "td", - classNames: ["admin-report-table-cell"], - classNameBindings: ["type", "property"], - attributeBindings: ["value:title"], - options: null, +@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; @discourseComputed("label", "data", "options") computedLabel(label, data, options) { return label.compute(data, options || {}); - }, - - type: alias("label.type"), - property: alias("label.mainProperty"), - formattedValue: alias("computedLabel.formattedValue"), - value: alias("computedLabel.value"), -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-table-header.hbs b/app/assets/javascripts/admin/addon/components/admin-report-table-header.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-table-header.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-table-header.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-table-header.js b/app/assets/javascripts/admin/addon/components/admin-report-table-header.js index 5c7cdf1e4c..7de3ac9708 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-table-header.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-table-header.js @@ -1,19 +1,24 @@ +import { + attributeBindings, + classNameBindings, + classNames, + tagName, +} from "@ember-decorators/component"; import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - tagName: "th", - classNames: ["admin-report-table-header"], - classNameBindings: ["label.mainProperty", "label.type", "isCurrentSort"], - attributeBindings: ["label.title:title"], - +@tagName("th") +@classNames("admin-report-table-header") +@classNameBindings("label.mainProperty", "label.type", "isCurrentSort") +@attributeBindings("label.title:title") +export default class AdminReportTableHeader extends Component { @discourseComputed("currentSortLabel.sortProperty", "label.sortProperty") isCurrentSort(currentSortField, labelSortField) { return currentSortField === labelSortField; - }, + } @discourseComputed("currentSortDirection") sortIcon(currentSortDirection) { return currentSortDirection === 1 ? "caret-up" : "caret-down"; - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-table-row.hbs b/app/assets/javascripts/admin/addon/components/admin-report-table-row.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-table-row.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-table-row.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-table-row.js b/app/assets/javascripts/admin/addon/components/admin-report-table-row.js index ff34cb94e8..e5e275aa7a 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-table-row.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-table-row.js @@ -1,6 +1,8 @@ +import { classNames, tagName } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - tagName: "tr", - classNames: ["admin-report-table-row"], - options: null, -}); + +@tagName("tr") +@classNames("admin-report-table-row") +export default class AdminReportTableRow extends Component { + options = null; +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-table.hbs b/app/assets/javascripts/admin/addon/components/admin-report-table.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-table.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-table.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-table.js b/app/assets/javascripts/admin/addon/components/admin-report-table.js index a237e93ee4..3638625f87 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-table.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-table.js @@ -1,22 +1,26 @@ -import Component from "@ember/component"; +import { action } from "@ember/object"; +import { classNameBindings, classNames } from "@ember-decorators/component"; import { alias } from "@ember/object/computed"; +import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; import { makeArray } from "discourse-common/lib/helpers"; const PAGES_LIMIT = 8; -export default Component.extend({ - classNameBindings: ["sortable", "twoColumns"], - classNames: ["admin-report-table"], - sortable: false, - sortDirection: 1, - perPage: alias("options.perPage"), - page: 0, +@classNameBindings("sortable", "twoColumns") +@classNames("admin-report-table") +export default class AdminReportTable extends Component { + sortable = false; + sortDirection = 1; + + @alias("options.perPage") perPage; + + page = 0; @discourseComputed("model.computedLabels.length") twoColumns(labelsLength) { return labelsLength === 2; - }, + } @discourseComputed( "totalsForSample", @@ -31,12 +35,12 @@ export default Component.extend({ .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}", @@ -50,17 +54,17 @@ export default Component.extend({ 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) { @@ -70,7 +74,7 @@ export default Component.extend({ computedLabel.property = label.mainProperty; return computedLabel; }); - }, + } @discourseComputed("model.data", "model.computedLabels") totalsForSampleRow(rows, labels) { @@ -98,7 +102,7 @@ export default Component.extend({ }); return totalsRow; - }, + } @discourseComputed("sortLabel", "sortDirection", "model.data.[]") sortedData(sortLabel, sortDirection, data) { @@ -118,7 +122,7 @@ export default Component.extend({ } return data; - }, + } @discourseComputed("sortedData.[]", "perPage", "page") paginatedData(data, perPage, page) { @@ -128,7 +132,7 @@ export default Component.extend({ } return data; - }, + } @discourseComputed("model.data", "perPage", "page") pages(data, perPage, page) { @@ -156,19 +160,19 @@ export default Component.extend({ } return pages; - }, + } - actions: { - changePage(page) { - this.set("page", page); - }, + @action + changePage(page) { + this.set("page", page); + } - sortByLabel(label) { - if (this.sortLabel === label) { - this.set("sortDirection", this.sortDirection === 1 ? -1 : 1); - } else { - this.set("sortLabel", label); - } - }, - }, -}); + @action + sortByLabel(label) { + if (this.sortLabel === label) { + this.set("sortDirection", this.sortDirection === 1 ? -1 : 1); + } else { + this.set("sortLabel", label); + } + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report-trust-level-counts.hbs b/app/assets/javascripts/admin/addon/components/admin-report-trust-level-counts.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report-trust-level-counts.hbs rename to app/assets/javascripts/admin/addon/components/admin-report-trust-level-counts.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report-trust-level-counts.js b/app/assets/javascripts/admin/addon/components/admin-report-trust-level-counts.js index 7f039c061e..6da6a74893 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report-trust-level-counts.js +++ b/app/assets/javascripts/admin/addon/components/admin-report-trust-level-counts.js @@ -1,4 +1,5 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - tagName: "tr", -}); + +@tagName("tr") +export default class AdminReportTrustLevelCounts extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-report.hbs b/app/assets/javascripts/admin/addon/components/admin-report.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-report.hbs rename to app/assets/javascripts/admin/addon/components/admin-report.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-report.js b/app/assets/javascripts/admin/addon/components/admin-report.js index 9f60e09a25..fede9cc2d6 100644 --- a/app/assets/javascripts/admin/addon/components/admin-report.js +++ b/app/assets/javascripts/admin/addon/components/admin-report.js @@ -1,6 +1,7 @@ +import { classNameBindings, classNames } from "@ember-decorators/component"; +import { alias, and, equal, notEmpty, or } from "@ember/object/computed"; import EmberObject, { action, computed } from "@ember/object"; import Report, { DAILY_LIMIT_DAYS, SCHEMA_VERSION } from "admin/models/report"; -import { alias, and, equal, notEmpty, or } from "@ember/object/computed"; import Component from "@ember/component"; import I18n from "I18n"; import ReportLoader from "discourse/lib/reports-loader"; @@ -21,51 +22,58 @@ const TABLE_OPTIONS = { const CHART_OPTIONS = {}; -export default Component.extend({ - classNameBindings: [ - "isHidden:hidden", - "isHidden::is-visible", - "isEnabled", - "isLoading", - "dasherizedDataSourceName", - ], - classNames: ["admin-report"], - isEnabled: true, - disabledLabel: I18n.t("admin.dashboard.disabled"), - isLoading: false, - rateLimitationString: null, - dataSourceName: null, - report: null, - model: null, - reportOptions: null, - forcedModes: null, - showAllReportsLink: false, - filters: null, - showTrend: false, - showHeader: true, - showTitle: true, - showFilteringUI: false, - showDatesOptions: alias("model.dates_filtering"), - showRefresh: or("showDatesOptions", "model.available_filters.length"), - shouldDisplayTrend: and("showTrend", "model.prev_period"), - endDate: null, - startDate: null, +@classNameBindings( + "isHidden:hidden", + "isHidden::is-visible", + "isEnabled", + "isLoading", + "dasherizedDataSourceName" +) +@classNames("admin-report") +export default class AdminReport extends Component { + isEnabled = true; + disabledLabel = I18n.t("admin.dashboard.disabled"); + isLoading = false; + rateLimitationString = null; + dataSourceName = null; + report = null; + model = null; + reportOptions = null; + forcedModes = null; + showAllReportsLink = false; + filters = null; + showTrend = false; + showHeader = true; + showTitle = true; + showFilteringUI = false; - init() { - this._super(...arguments); + @alias("model.dates_filtering") showDatesOptions; - this._reports = []; - }, + @or("showDatesOptions", "model.available_filters.length") showRefresh; - isHidden: computed("siteSettings.dashboard_hidden_reports", function () { + @and("showTrend", "model.prev_period") shouldDisplayTrend; + + endDate = null; + startDate = null; + + @or("showTimeoutError", "showExceptionError", "showNotFoundError") showError; + @equal("model.error", "not_found") showNotFoundError; + @equal("model.error", "timeout") showTimeoutError; + @equal("model.error", "exception") showExceptionError; + @notEmpty("model.data") hasData; + + _reports = []; + + @computed("siteSettings.dashboard_hidden_reports") + get isHidden() { return (this.siteSettings.dashboard_hidden_reports || "") .split("|") .filter(Boolean) .includes(this.dataSourceName); - }), + } didReceiveAttrs() { - this._super(...arguments); + super.didReceiveAttrs(...arguments); let startDate = moment(); if (this.filters && isPresent(this.filters.startDate)) { @@ -88,42 +96,35 @@ export default Component.extend({ } else if (this.dataSourceName) { this._fetchReport(); } - }, - - showError: or("showTimeoutError", "showExceptionError", "showNotFoundError"), - showNotFoundError: equal("model.error", "not_found"), - showTimeoutError: equal("model.error", "timeout"), - showExceptionError: equal("model.error", "exception"), - - hasData: notEmpty("model.data"), + } @discourseComputed("dataSourceName", "model.type") dasherizedDataSourceName(dataSourceName, type) { return (dataSourceName || type || "undefined").replace(/_/g, "-"); - }, + } @discourseComputed("dataSourceName", "model.type") dataSource(dataSourceName, type) { dataSourceName = dataSourceName || type; return `/admin/reports/${dataSourceName}`; - }, + } @discourseComputed("displayedModes.length") showModes(displayedModesLength) { return displayedModesLength > 1; - }, + } @discourseComputed("currentMode") isChartMode(currentMode) { return currentMode === "chart"; - }, + } @action changeGrouping(grouping) { this.send("refreshReport", { chartGrouping: grouping, }); - }, + } @discourseComputed("currentMode", "model.modes", "forcedModes") displayedModes(currentMode, reportModes, forcedModes) { @@ -139,12 +140,12 @@ export default Component.extend({ icon: mode === "table" ? "table" : "signal", }; }); - }, + } @discourseComputed("currentMode") modeComponent(currentMode) { return `admin-report-${currentMode.replace(/_/g, "-")}`; - }, + } @discourseComputed( "dataSourceName", @@ -178,7 +179,7 @@ export default Component.extend({ .join(":"); return reportKey; - }, + } @discourseComputed("options.chartGrouping", "model.chartData.length") chartGroupings(grouping, count) { @@ -192,7 +193,7 @@ export default Component.extend({ class: `chart-grouping ${grouping === id ? "active" : "inactive"}`, }; }); - }, + } @action onChangeDateRange(range) { @@ -200,7 +201,7 @@ export default Component.extend({ startDate: range.from, endDate: range.to, }); - }, + } @action applyFilter(id, value) { @@ -215,7 +216,7 @@ export default Component.extend({ this.send("refreshReport", { filters: customFilters, }); - }, + } @action refreshReport(options = {}) { @@ -238,7 +239,7 @@ export default Component.extend({ ? this.get("filters.customFilters") : options.filters, }); - }, + } @action exportCsv() { @@ -254,7 +255,7 @@ export default Component.extend({ } exportEntity("report", args).then(outputExportResult); - }, + } @action onChangeMode(mode) { @@ -263,7 +264,7 @@ export default Component.extend({ this.send("refreshReport", { chartGrouping: null, }); - }, + } _computeReport() { if (!this.element || this.isDestroying || this.isDestroyed) { @@ -306,7 +307,7 @@ export default Component.extend({ } this._renderReport(report, this.forcedModes, this.currentMode); - }, + } _renderReport(report, forcedModes, currentMode) { const modes = forcedModes ? forcedModes.split(",") : report.modes; @@ -317,11 +318,9 @@ export default Component.extend({ currentMode, options: this._buildOptions(currentMode, report), }); - }, + } _fetchReport() { - this._super(...arguments); - this.setProperties({ isLoading: true, rateLimitationString: null }); next(() => { @@ -349,7 +348,7 @@ export default Component.extend({ ReportLoader.enqueue(this.dataSourceName, payload.data, callback); }); - }, + } _buildPayload(facets) { let payload = { data: { facets } }; @@ -375,7 +374,7 @@ export default Component.extend({ } return payload; - }, + } _buildOptions(mode, report) { if (mode === "table") { @@ -393,7 +392,7 @@ export default Component.extend({ }) ); } - }, + } _loadReport(jsonReport) { Report.fillMissingDates(jsonReport, { filledField: "chartData" }); @@ -423,5 +422,5 @@ export default Component.extend({ } return Report.create(jsonReport); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs b/app/assets/javascripts/admin/addon/components/admin-theme-editor.hbs similarity index 97% rename from app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs rename to app/assets/javascripts/admin/addon/components/admin-theme-editor.hbs index b74b2239f8..e6c201cb78 100644 --- a/app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs +++ b/app/assets/javascripts/admin/addon/components/admin-theme-editor.hbs @@ -44,7 +44,7 @@ @checked={{this.onlyOverridden}} {{on "click" - (action "onlyOverriddenChanged" value="target.checked") + (action this.onlyOverriddenChanged value="target.checked") }} /> {{i18n "admin.customize.theme.hide_unused_fields"}} @@ -125,6 +125,6 @@ @autofocus="true" @placeholder={{this.placeholder}} @htmlPlaceholder={{true}} - @save={{action "save"}} + @save={{this.save}} @setWarning={{action "setWarning"}} /> \ No newline at end of file diff --git a/app/assets/javascripts/admin/addon/components/admin-theme-editor.js b/app/assets/javascripts/admin/addon/components/admin-theme-editor.js index de8f063579..31a0d02ec9 100644 --- a/app/assets/javascripts/admin/addon/components/admin-theme-editor.js +++ b/app/assets/javascripts/admin/addon/components/admin-theme-editor.js @@ -3,11 +3,13 @@ import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; import { fmt } from "discourse/lib/computed"; import { isDocumentRTL } from "discourse/lib/text-direction"; -import { action } from "@ember/object"; +import { action, computed } from "@ember/object"; import { next } from "@ember/runloop"; -export default Component.extend({ - warning: null, +export default class AdminThemeEditor extends Component { + warning = null; + + @fmt("fieldName", "currentTargetName", "%@|%@") editorId; @discourseComputed("theme.targets", "onlyOverridden", "showAdvanced") visibleTargets(targets, onlyOverridden, showAdvanced) { @@ -20,7 +22,7 @@ export default Component.extend({ } return target.edited; }); - }, + } @discourseComputed("currentTargetName", "onlyOverridden", "theme.fields") visibleFields(targetName, onlyOverridden, fields) { @@ -29,7 +31,7 @@ export default Component.extend({ fields = fields.filter((field) => field.edited); } return fields; - }, + } @discourseComputed("currentTargetName", "fieldName") activeSectionMode(targetName, fieldName) { @@ -43,7 +45,7 @@ export default Component.extend({ return "scss"; } return fieldName && fieldName.includes("scss") ? "scss" : "html"; - }, + } @discourseComputed("currentTargetName", "fieldName") placeholder(targetName, fieldName) { @@ -58,30 +60,27 @@ export default Component.extend({ }); } return ""; - }, + } - @discourseComputed("fieldName", "currentTargetName", "theme") - activeSection: { - get(fieldName, target, model) { - return model.getField(target, fieldName); - }, - set(value, fieldName, target, model) { - model.setField(target, fieldName, value); - return value; - }, - }, + @computed("fieldName", "currentTargetName", "theme") + get activeSection() { + return this.theme.getField(this.currentTargetName, this.fieldName); + } - editorId: fmt("fieldName", "currentTargetName", "%@|%@"), + set activeSection(value) { + this.theme.setField(this.currentTargetName, this.fieldName, value); + return value; + } @discourseComputed("maximized") maximizeIcon(maximized) { return maximized ? "discourse-compress" : "discourse-expand"; - }, + } @discourseComputed("currentTargetName", "theme.targets") showAddField(currentTargetName, targets) { return targets.find((t) => t.name === currentTargetName).customNames; - }, + } @discourseComputed( "currentTargetName", @@ -90,52 +89,45 @@ export default Component.extend({ ) error(target, fieldName) { return this.theme.getError(target, fieldName); - }, + } @action toggleShowAdvanced(event) { event?.preventDefault(); this.toggleProperty("showAdvanced"); - }, + } @action toggleAddField(event) { event?.preventDefault(); this.toggleProperty("addingField"); - }, + } @action toggleMaximize(event) { event?.preventDefault(); this.toggleProperty("maximized"); next(() => this.appEvents.trigger("ace:resize")); - }, + } - actions: { - cancelAddField() { - this.set("addingField", false); - }, + @action + cancelAddField() { + this.set("addingField", false); + } - addField(name) { - if (!name) { - return; - } - name = name.replace(/[^a-zA-Z0-9-_/]/g, ""); - this.theme.setField(this.currentTargetName, name, ""); - this.setProperties({ newFieldName: "", addingField: false }); - this.fieldAdded(this.currentTargetName, name); - }, + @action + addField(name) { + if (!name) { + return; + } + name = name.replace(/[^a-zA-Z0-9-_/]/g, ""); + this.theme.setField(this.currentTargetName, name, ""); + this.setProperties({ newFieldName: "", addingField: false }); + this.fieldAdded(this.currentTargetName, name); + } - onlyOverriddenChanged(value) { - this.onlyOverriddenChanged(value); - }, - - save() { - this.attrs.save(); - }, - - setWarning(message) { - this.set("warning", message); - }, - }, -}); + @action + setWarning(message) { + this.set("warning", message); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-user-field-item.hbs b/app/assets/javascripts/admin/addon/components/admin-user-field-item.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-user-field-item.hbs rename to app/assets/javascripts/admin/addon/components/admin-user-field-item.hbs diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-watched-word.hbs b/app/assets/javascripts/admin/addon/components/admin-watched-word.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/admin-watched-word.hbs rename to app/assets/javascripts/admin/addon/components/admin-watched-word.hbs diff --git a/app/assets/javascripts/admin/addon/components/admin-watched-word.js b/app/assets/javascripts/admin/addon/components/admin-watched-word.js index 7de7e3d8ab..d6bde8016d 100644 --- a/app/assets/javascripts/admin/addon/components/admin-watched-word.js +++ b/app/assets/javascripts/admin/addon/components/admin-watched-word.js @@ -1,23 +1,27 @@ -import Component from "@ember/component"; +import { classNames } from "@ember-decorators/component"; +import { inject as service } from "@ember/service"; import { alias, equal } from "@ember/object/computed"; +import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; import { action } from "@ember/object"; import I18n from "I18n"; -import { inject as service } from "@ember/service"; -export default Component.extend({ - classNames: ["watched-word"], - dialog: service(), +@classNames("watched-word") +export default class AdminWatchedWord extends Component { + @service dialog; - isReplace: equal("actionKey", "replace"), - isTag: equal("actionKey", "tag"), - isLink: equal("actionKey", "link"), - isCaseSensitive: alias("word.case_sensitive"), + @equal("actionKey", "replace") isReplace; + + @equal("actionKey", "tag") isTag; + + @equal("actionKey", "link") isLink; + + @alias("word.case_sensitive") isCaseSensitive; @discourseComputed("word.replacement") tags(replacement) { return replacement.split(","); - }, + } @action deleteWord() { @@ -33,5 +37,5 @@ export default Component.extend({ }) ); }); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/components/admin-wrapper.js b/app/assets/javascripts/admin/addon/components/admin-wrapper.js index d6a3564d42..98d18997d4 100644 --- a/app/assets/javascripts/admin/addon/components/admin-wrapper.js +++ b/app/assets/javascripts/admin/addon/components/admin-wrapper.js @@ -1,14 +1,15 @@ import Component from "@ember/component"; -export default Component.extend({ + +export default class AdminWrapper extends Component { didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); document.querySelector("html").classList.add("admin-area"); document.querySelector("body").classList.add("admin-interface"); - }, + } willDestroyElement() { - this._super(...arguments); + super.willDestroyElement(...arguments); document.querySelector("html").classList.remove("admin-area"); document.querySelector("body").classList.remove("admin-interface"); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/components/cancel-link.js b/app/assets/javascripts/admin/addon/components/cancel-link.js index 0e6d50b17d..aa5d4dda65 100644 --- a/app/assets/javascripts/admin/addon/components/cancel-link.js +++ b/app/assets/javascripts/admin/addon/components/cancel-link.js @@ -1,4 +1,5 @@ +import { tagName } from "@ember-decorators/component"; import Component from "@ember/component"; -export default Component.extend({ - tagName: "", -}); + +@tagName("") +export default class CancelLink extends Component {} diff --git a/app/assets/javascripts/discourse/app/templates/components/color-input.hbs b/app/assets/javascripts/admin/addon/components/color-input.hbs similarity index 100% rename from app/assets/javascripts/discourse/app/templates/components/color-input.hbs rename to app/assets/javascripts/admin/addon/components/color-input.hbs diff --git a/app/assets/javascripts/admin/addon/components/color-input.js b/app/assets/javascripts/admin/addon/components/color-input.js index 2974b43871..9c1c67f044 100644 --- a/app/assets/javascripts/admin/addon/components/color-input.js +++ b/app/assets/javascripts/admin/addon/components/color-input.js @@ -1,6 +1,7 @@ +import { classNames } from "@ember-decorators/component"; import { action, computed } from "@ember/object"; import Component from "@ember/component"; -import { observes } from "discourse-common/utils/decorators"; +import { observes } from "@ember-decorators/object"; /** An input field for a color. @@ -9,20 +10,20 @@ import { observes } from "discourse-common/utils/decorators"; @param brightnessValue is a number from 0 to 255 representing the brightness of the color. See ColorSchemeColor. @params valid is a boolean indicating if the input field is a valid color. **/ -export default Component.extend({ - classNames: ["color-picker"], +@classNames("color-picker") +export default class ColorInput extends Component { + onlyHex = true; + styleSelection = true; - onlyHex: true, - - styleSelection: true, - - maxlength: computed("onlyHex", function () { + @computed("onlyHex") + get maxlength() { return this.onlyHex ? 6 : null; - }), + } - normalizedHexValue: computed("hexValue", function () { + @computed("hexValue") + get normalizedHexValue() { return this.normalize(this.hexValue); - }), + } normalize(color) { if (this._valid(color)) { @@ -40,19 +41,19 @@ export default Component.extend({ } } return color; - }, + } @action onHexInput(color) { if (this.attrs.onChangeColor) { this.attrs.onChangeColor(this.normalize(color || "")); } - }, + } @action onPickerInput(event) { this.set("hexValue", event.target.value.replace("#", "")); - }, + } @observes("hexValue", "brightnessValue", "valid") hexValueChanged() { @@ -65,9 +66,9 @@ export default Component.extend({ if (this._valid()) { this.element.querySelector(".picker").value = this.normalize(hex); } - }, + } _valid(color = this.hexValue) { return /^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/dashboard-new-feature-item.hbs b/app/assets/javascripts/admin/addon/components/dashboard-new-feature-item.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/dashboard-new-feature-item.hbs rename to app/assets/javascripts/admin/addon/components/dashboard-new-feature-item.hbs diff --git a/app/assets/javascripts/admin/addon/components/dashboard-new-feature-item.js b/app/assets/javascripts/admin/addon/components/dashboard-new-feature-item.js index 87d5ddb040..80e6cd8a16 100644 --- a/app/assets/javascripts/admin/addon/components/dashboard-new-feature-item.js +++ b/app/assets/javascripts/admin/addon/components/dashboard-new-feature-item.js @@ -1,3 +1,3 @@ import Component from "@ember/component"; -export default Component.extend({}); +export default class DashboardNewFeatureItem extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/dashboard-new-features.hbs b/app/assets/javascripts/admin/addon/components/dashboard-new-features.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/dashboard-new-features.hbs rename to app/assets/javascripts/admin/addon/components/dashboard-new-features.hbs diff --git a/app/assets/javascripts/admin/addon/components/dashboard-new-features.js b/app/assets/javascripts/admin/addon/components/dashboard-new-features.js index 6e8defb93c..46b7559585 100644 --- a/app/assets/javascripts/admin/addon/components/dashboard-new-features.js +++ b/app/assets/javascripts/admin/addon/components/dashboard-new-features.js @@ -1,18 +1,19 @@ +import { classNameBindings, classNames } from "@ember-decorators/component"; import Component from "@ember/component"; import { action, computed } from "@ember/object"; import { ajax } from "discourse/lib/ajax"; -export default Component.extend({ - newFeatures: null, - classNames: ["section", "dashboard-new-features"], - classNameBindings: ["hasUnseenFeatures:ordered-first"], - releaseNotesLink: null, +@classNames("section", "dashboard-new-features") +@classNameBindings("hasUnseenFeatures:ordered-first") +export default class DashboardNewFeatures extends Component { + newFeatures = null; + releaseNotesLink = null; - init() { - this._super(...arguments); + constructor() { + super(...arguments); ajax("/admin/dashboard/new-features.json").then((json) => { - if (!this.element || this.isDestroying || this.isDestroyed) { + if (this.isDestroying || this.isDestroyed) { return; } @@ -22,16 +23,17 @@ export default Component.extend({ releaseNotesLink: json.release_notes_link, }); }); - }, + } - columnCountClass: computed("newFeatures", function () { + @computed("newFeatures") + get columnCountClass() { return this.newFeatures.length > 2 ? "three-or-more-items" : ""; - }), + } @action dismissNewFeatures() { ajax("/admin/dashboard/mark-new-features-as-seen.json", { type: "PUT", }).then(() => this.set("hasUnseenFeatures", false)); - }, -}); + } +} diff --git a/app/assets/javascripts/admin/addon/templates/components/dashboard-problems.hbs b/app/assets/javascripts/admin/addon/components/dashboard-problems.hbs similarity index 100% rename from app/assets/javascripts/admin/addon/templates/components/dashboard-problems.hbs rename to app/assets/javascripts/admin/addon/components/dashboard-problems.hbs diff --git a/app/assets/javascripts/admin/addon/components/dashboard-problems.js b/app/assets/javascripts/admin/addon/components/dashboard-problems.js index 87d5ddb040..c4df8401e0 100644 --- a/app/assets/javascripts/admin/addon/components/dashboard-problems.js +++ b/app/assets/javascripts/admin/addon/components/dashboard-problems.js @@ -1,3 +1,3 @@ import Component from "@ember/component"; -export default Component.extend({}); +export default class DashboardProblems extends Component {} diff --git a/app/assets/javascripts/admin/addon/templates/components/email-styles-editor.hbs b/app/assets/javascripts/admin/addon/components/email-styles-editor.hbs similarity index 96% rename from app/assets/javascripts/admin/addon/templates/components/email-styles-editor.hbs rename to app/assets/javascripts/admin/addon/components/email-styles-editor.hbs index 1ca8055f6b..78b63780e1 100644 --- a/app/assets/javascripts/admin/addon/templates/components/email-styles-editor.hbs +++ b/app/assets/javascripts/admin/addon/components/email-styles-editor.hbs @@ -29,7 +29,7 @@ @content={{this.editorContents}} @mode={{this.currentEditorMode}} @editorId={{this.editorId}} - @save={{action "save"}} + @save={{@save}} />