Version bump
This commit is contained in:
commit
686c3f6a2f
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: CI
|
||||
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@ -7,7 +7,7 @@ on:
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'tests-passed'
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "${{ matrix.target }}-${{ matrix.build_types }}"
|
||||
@ -31,14 +31,14 @@ jobs:
|
||||
build_types: [ 'BACKEND', 'FRONTEND', 'LINT' ]
|
||||
target: [ 'PLUGINS', 'CORE' ]
|
||||
os: [ ubuntu-latest ]
|
||||
ruby: [ '2.6.3' ]
|
||||
ruby: [ '2.6' ]
|
||||
postgres: [ '10' ]
|
||||
redis: [ '4.x' ]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:${{ matrix.postgres }}
|
||||
ports:
|
||||
ports:
|
||||
- 5432:5432
|
||||
env:
|
||||
POSTGRES_USER: discourse
|
||||
@ -88,14 +88,14 @@ jobs:
|
||||
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gem-
|
||||
|
||||
|
||||
- name: Setup gems
|
||||
run: bundle install --without development --deployment --jobs 4 --retry 3
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/cache@v1
|
||||
id: yarn-cache
|
||||
@ -113,7 +113,7 @@ jobs:
|
||||
run: bin/rake plugin:install_all_official
|
||||
|
||||
- name: Create database
|
||||
if: env.BUILD_TYPE != 'LINT'
|
||||
if: env.BUILD_TYPE != 'LINT'
|
||||
run: bin/rake db:create && bin/rake db:migrate
|
||||
|
||||
- name: Create parallel databases
|
||||
@ -123,7 +123,7 @@ jobs:
|
||||
- name: Rubocop
|
||||
if: env.BUILD_TYPE == 'LINT'
|
||||
run: bundle exec rubocop .
|
||||
|
||||
|
||||
- name: ESLint
|
||||
if: env.BUILD_TYPE == 'LINT'
|
||||
run: yarn eslint app/assets/javascripts test/javascripts && yarn eslint --ext .es6 app/assets/javascripts test/javascripts plugins
|
||||
@ -133,7 +133,7 @@ jobs:
|
||||
run: |
|
||||
yarn prettier -v
|
||||
yarn prettier --list-different "app/assets/stylesheets/**/*.scss" "app/assets/javascripts/**/*.es6" "test/javascripts/**/*.es6" "plugins/**/*.scss" "plugins/**/*.es6"
|
||||
|
||||
|
||||
- name: Core RSpec
|
||||
if: env.BUILD_TYPE == 'BACKEND' && env.TARGET == 'CORE'
|
||||
run: bin/turbo_rspec && bin/rake plugin:spec
|
||||
@ -146,14 +146,13 @@ jobs:
|
||||
if: env.BUILD_TYPE == 'FRONTEND' && env.TARGET == 'CORE'
|
||||
run: bundle exec rake qunit:test['1200000']
|
||||
timeout-minutes: 30
|
||||
|
||||
|
||||
- name: Wizard QUnit
|
||||
if: env.BUILD_TYPE == 'FRONTEND' && env.TARGET == 'CORE'
|
||||
run: bundle exec rake qunit:test['1200000','/wizard/qunit']
|
||||
timeout-minutes: 30
|
||||
|
||||
|
||||
- name: Plugin QUnit # Tests core plugins in TARGET=CORE, and all plugins in TARGET=PLUGINS
|
||||
if: env.BUILD_TYPE == 'FRONTEND'
|
||||
run: bundle exec rake plugin:qunit
|
||||
timeout-minutes: 30
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -32,6 +32,7 @@ config/discourse.conf
|
||||
# Ignore the default SQLite database and db dumps
|
||||
*.sql
|
||||
*.sql.gz
|
||||
!/spec/fixtures/**/*.sql
|
||||
/db/*.sqlite3
|
||||
/db/structure.sql
|
||||
/db/schema.rb
|
||||
|
||||
@ -1 +1 @@
|
||||
2.6.1
|
||||
2.6.5
|
||||
|
||||
6
Gemfile
6
Gemfile
@ -131,6 +131,12 @@ gem 'mini_racer'
|
||||
# TODO: determine why highline is being held back and upgrade to latest
|
||||
gem 'highline', '~> 1.7.0', require: false
|
||||
|
||||
# TODO: Upgrading breaks Sidekiq Web
|
||||
# This is a bit of a hornets nest cause in an ideal world we much prefer
|
||||
# if Sidekiq reused session and CSRF mitigation with Discourse on the
|
||||
# _forum_session cookie instead of a rack.session cookie
|
||||
gem 'rack', '2.0.8'
|
||||
|
||||
gem 'rack-protection' # security
|
||||
gem 'cbor', require: false
|
||||
gem 'cose', require: false
|
||||
|
||||
76
Gemfile.lock
76
Gemfile.lock
@ -45,16 +45,16 @@ GEM
|
||||
rake (>= 10.4, < 14.0)
|
||||
ast (2.4.0)
|
||||
aws-eventstream (1.0.3)
|
||||
aws-partitions (1.256.0)
|
||||
aws-sdk-core (3.86.0)
|
||||
aws-partitions (1.266.0)
|
||||
aws-sdk-core (3.89.1)
|
||||
aws-eventstream (~> 1.0, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.27.0)
|
||||
aws-sdk-kms (1.28.0)
|
||||
aws-sdk-core (~> 3, >= 3.71.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.59.1)
|
||||
aws-sdk-s3 (1.60.1)
|
||||
aws-sdk-core (~> 3, >= 3.83.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
@ -75,10 +75,10 @@ GEM
|
||||
bootsnap (1.4.5)
|
||||
msgpack (~> 1.0)
|
||||
builder (3.2.4)
|
||||
bullet (6.0.2)
|
||||
bullet (6.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
byebug (11.0.1)
|
||||
byebug (11.1.0)
|
||||
cbor (0.5.9.6)
|
||||
certified (1.0.0)
|
||||
chunky_png (1.3.11)
|
||||
@ -86,12 +86,12 @@ GEM
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.1.5)
|
||||
connection_pool (2.2.2)
|
||||
cose (0.9.0)
|
||||
cose (0.10.0)
|
||||
cbor (~> 0.5.9)
|
||||
cppjieba_rb (0.3.3)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.5)
|
||||
crass (1.0.6)
|
||||
css_parser (1.7.1)
|
||||
addressable
|
||||
debug_inspector (0.0.3)
|
||||
@ -120,7 +120,7 @@ GEM
|
||||
railties (>= 3.1)
|
||||
ember-source (2.18.2)
|
||||
erubi (1.9.0)
|
||||
excon (0.71.0)
|
||||
excon (0.71.1)
|
||||
execjs (2.7.0)
|
||||
exifr (1.3.6)
|
||||
fabrication (2.21.0)
|
||||
@ -133,7 +133,7 @@ GEM
|
||||
rake-compiler
|
||||
fast_xs (0.8.0)
|
||||
fastimage (2.1.7)
|
||||
ffi (1.11.3)
|
||||
ffi (1.12.1)
|
||||
flamegraph (0.9.5)
|
||||
fspath (3.1.2)
|
||||
gc_tracer (1.5.1)
|
||||
@ -146,10 +146,10 @@ GEM
|
||||
hkdf (0.3.0)
|
||||
htmlentities (4.3.4)
|
||||
http_accept_language (2.1.1)
|
||||
i18n (1.7.0)
|
||||
i18n (1.8.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_size (1.5.0)
|
||||
in_threads (1.5.3)
|
||||
in_threads (1.5.4)
|
||||
jaro_winkler (1.5.4)
|
||||
jmespath (1.4.0)
|
||||
jquery-rails (4.3.5)
|
||||
@ -187,14 +187,14 @@ GEM
|
||||
method_source (0.9.2)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
mini_racer (0.2.8)
|
||||
mini_racer (0.2.9)
|
||||
libv8 (>= 6.9.411)
|
||||
mini_scheduler (0.12.2)
|
||||
sidekiq
|
||||
mini_sql (0.2.2)
|
||||
mini_sql (0.2.4)
|
||||
mini_suffix (0.3.0)
|
||||
ffi (~> 1.9)
|
||||
minitest (5.13.0)
|
||||
minitest (5.14.0)
|
||||
mocha (1.8.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mock_redis (0.22.0)
|
||||
@ -215,7 +215,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oj (3.10.0)
|
||||
oj (3.10.1)
|
||||
omniauth (1.9.0)
|
||||
hashie (>= 3.4.6, < 3.7.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
@ -243,7 +243,8 @@ GEM
|
||||
omniauth-twitter (1.4.0)
|
||||
omniauth-oauth (~> 1.1)
|
||||
rack
|
||||
onebox (1.9.24)
|
||||
onebox (1.9.25)
|
||||
addressable (~> 2.7.0)
|
||||
htmlentities (~> 4.3)
|
||||
multi_json (~> 1.11)
|
||||
mustache
|
||||
@ -256,9 +257,9 @@ GEM
|
||||
parallel (1.19.1)
|
||||
parallel_tests (2.30.0)
|
||||
parallel
|
||||
parser (2.6.5.0)
|
||||
parser (2.7.0.2)
|
||||
ast (~> 2.4.0)
|
||||
pg (1.1.4)
|
||||
pg (1.2.2)
|
||||
progress (3.5.2)
|
||||
pry (0.12.2)
|
||||
coderay (~> 1.1.0)
|
||||
@ -267,7 +268,7 @@ GEM
|
||||
pry (>= 0.9.10, < 0.13.0)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.1)
|
||||
public_suffix (4.0.3)
|
||||
puma (4.3.1)
|
||||
nio4r (~> 2.0)
|
||||
r2 (0.2.7)
|
||||
@ -277,7 +278,7 @@ GEM
|
||||
rack-openid (1.3.1)
|
||||
rack (>= 1.1.0)
|
||||
ruby-openid (>= 2.1.8)
|
||||
rack-protection (2.0.7)
|
||||
rack-protection (2.0.8.1)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
@ -298,10 +299,10 @@ GEM
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.0)
|
||||
rake (13.0.1)
|
||||
rake-compiler (1.0.8)
|
||||
rake-compiler (1.1.0)
|
||||
rake
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rbtrace (0.4.11)
|
||||
ffi (>= 1.0.6)
|
||||
@ -311,7 +312,7 @@ GEM
|
||||
redis (4.1.3)
|
||||
redis-namespace (1.7.0)
|
||||
redis (>= 3.0.4)
|
||||
request_store (1.4.1)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
rinku (2.0.6)
|
||||
rotp (5.1.0)
|
||||
@ -324,15 +325,15 @@ GEM
|
||||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-core (3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-core (3.9.1)
|
||||
rspec-support (~> 3.9.1)
|
||||
rspec-expectations (3.9.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-html-matchers (0.9.2)
|
||||
nokogiri (~> 1)
|
||||
rspec (>= 3.0.0.a, < 4)
|
||||
rspec-mocks (3.9.0)
|
||||
rspec-mocks (3.9.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (4.0.0.beta2)
|
||||
@ -343,12 +344,12 @@ GEM
|
||||
rspec-expectations (~> 3.8)
|
||||
rspec-mocks (~> 3.8)
|
||||
rspec-support (~> 3.8)
|
||||
rspec-support (3.9.0)
|
||||
rspec-support (3.9.2)
|
||||
rtlit (0.0.5)
|
||||
rubocop (0.77.0)
|
||||
rubocop (0.79.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.6)
|
||||
parser (>= 2.7.0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
@ -378,9 +379,9 @@ GEM
|
||||
seed-fu (2.3.9)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
shoulda-matchers (4.1.2)
|
||||
shoulda-matchers (4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
sidekiq (6.0.3)
|
||||
sidekiq (6.0.4)
|
||||
connection_pool (>= 2.2.2)
|
||||
rack (>= 2.0.0)
|
||||
rack-protection (>= 2.0.0)
|
||||
@ -398,20 +399,20 @@ GEM
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkey (2.0.0)
|
||||
stackprof (0.2.14)
|
||||
test-prof (0.10.1)
|
||||
stackprof (0.2.15)
|
||||
test-prof (0.10.2)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.10)
|
||||
tzinfo (1.2.5)
|
||||
tzinfo (1.2.6)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.6)
|
||||
unicode-display_width (1.6.0)
|
||||
unicorn (5.5.1)
|
||||
unicode-display_width (1.6.1)
|
||||
unicorn (5.5.2)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
uniform_notifier (1.13.0)
|
||||
@ -511,6 +512,7 @@ DEPENDENCIES
|
||||
pry-rails
|
||||
puma
|
||||
r2
|
||||
rack (= 2.0.8)
|
||||
rack-mini-profiler
|
||||
rack-protection
|
||||
rails_multisite
|
||||
|
||||
12
README.md
12
README.md
@ -44,14 +44,16 @@ If you're looking for business class hosting, see [discourse.org/buy](https://ww
|
||||
|
||||
## Requirements
|
||||
|
||||
Discourse is built for the *next* 10 years of the Internet, so our requirements are high:
|
||||
Discourse is built for the *next* 10 years of the Internet, so our requirements are high.
|
||||
|
||||
Discourse supports the **latest, stable releases** of all major browsers and platforms:
|
||||
|
||||
| Browsers | Tablets | Phones |
|
||||
| --------------------- | ------------ | ------------ |
|
||||
| Safari 10+ | iPad 4+ | iOS 10+ |
|
||||
| Google Chrome 57+ | Android 4.4+ | Android 4.4+ |
|
||||
| Internet Explorer 11+ | | |
|
||||
| Firefox 52+ | | |
|
||||
| Apple Safari | iPadOS | iOS |
|
||||
| Google Chrome | Android | Android |
|
||||
| Microsoft Edge | | |
|
||||
| Mozilla Firefox | | |
|
||||
|
||||
## Built With
|
||||
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
import { getWebauthnCredential } from "discourse/lib/webauthn";
|
||||
|
||||
export default function() {
|
||||
document.getElementById(
|
||||
"activate-security-key-alternative"
|
||||
).onclick = function() {
|
||||
document.getElementById("second-factor-forms").style.display = "block";
|
||||
document.getElementById("primary-security-key-form").style.display = "none";
|
||||
};
|
||||
|
||||
document.getElementById("submit-security-key").onclick = function(e) {
|
||||
e.preventDefault();
|
||||
getWebauthnCredential(
|
||||
document.getElementById("security-key-challenge").value,
|
||||
document
|
||||
.getElementById("security-key-allowed-credential-ids")
|
||||
.value.split(","),
|
||||
credentialData => {
|
||||
document.getElementById(
|
||||
"security-key-credential"
|
||||
).value = JSON.stringify(credentialData);
|
||||
e.target.parentElement.submit();
|
||||
},
|
||||
errorMessage => {
|
||||
document.getElementById("security-key-error").innerText = errorMessage;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const useTotp = I18n.t("login.second_factor_toggle.totp");
|
||||
const useBackup = I18n.t("login.second_factor_toggle.backup_code");
|
||||
const backupForm = document.getElementById("backup-second-factor-form");
|
||||
const primaryForm = document.getElementById("primary-second-factor-form");
|
||||
document.getElementById("toggle-form").onclick = function(event) {
|
||||
event.preventDefault();
|
||||
if (backupForm.style.display === "none") {
|
||||
backupForm.style.display = "block";
|
||||
primaryForm.style.display = "none";
|
||||
document.getElementById("toggle-form").innerHTML = useTotp;
|
||||
} else {
|
||||
backupForm.style.display = "none";
|
||||
primaryForm.style.display = "block";
|
||||
document.getElementById("toggle-form").innerHTML = useBackup;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
require("admin-login/admin-login").default();
|
||||
@ -8,7 +8,7 @@ import ReportLoader from "discourse/lib/reports-loader";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { isNumeric } from "discourse/lib/utilities";
|
||||
import { SCHEMA_VERSION, default as Report } from "admin/models/report";
|
||||
import Report, { SCHEMA_VERSION } from "admin/models/report";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
|
||||
const TABLE_OPTIONS = {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { next } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { fmt } from "discourse/lib/computed";
|
||||
|
||||
export default Component.extend({
|
||||
|
||||
@ -7,8 +7,7 @@ import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
import { i18n } from "discourse/lib/computed";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
import discourseComputed, {
|
||||
observes,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { default as loadScript, loadCSS } from "discourse/lib/load-script";
|
||||
import loadScript, { loadCSS } from "discourse/lib/load-script";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
/**
|
||||
An input field for a color.
|
||||
@ -11,6 +12,8 @@ import { default as loadScript, loadCSS } from "discourse/lib/load-script";
|
||||
**/
|
||||
export default Component.extend({
|
||||
classNames: ["color-picker"],
|
||||
|
||||
@observes("hexValue", "brightnessValue", "valid")
|
||||
hexValueChanged: function() {
|
||||
var hex = this.hexValue;
|
||||
let text = this.element.querySelector("input.hex-input");
|
||||
@ -33,7 +36,7 @@ export default Component.extend({
|
||||
} else {
|
||||
text.setAttribute("style", "");
|
||||
}
|
||||
}.observes("hexValue", "brightnessValue", "valid"),
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
loadScript("/javascripts/spectrum.js").then(() => {
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["inline-edit"],
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import { later } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import copyText from "discourse/lib/copy-text";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { fmt } from "discourse/lib/computed";
|
||||
import Permalink from "admin/models/permalink";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Category from "discourse/models/category";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
|
||||
export default FilterComponent.extend({
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default FilterComponent.extend({
|
||||
classNames: ["group-filter"],
|
||||
|
||||
@ -2,10 +2,7 @@ import { schedule } from "@ember/runloop";
|
||||
import { later } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
|
||||
/*global Resumable:true */
|
||||
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { gt, and } from "@ember/object/computed";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { escape } from "pretty-text/sanitizer";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
|
||||
@ -2,8 +2,7 @@ import { isEmpty } from "@ember/utils";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import WatchedWord from "admin/models/watched-word";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
import discourseComputed, {
|
||||
on,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
@ -2,7 +2,7 @@ import { alias, equal } from "@ember/object/computed";
|
||||
import { inject } from "@ember/controller";
|
||||
import Controller from "@ember/controller";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { setting, i18n } from "discourse/lib/computed";
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Controller.extend({
|
||||
saving: false,
|
||||
|
||||
actions: {
|
||||
massAward() {
|
||||
const file = document.querySelector("#massAwardCSVUpload").files[0];
|
||||
|
||||
if (this.model && file) {
|
||||
const options = {
|
||||
type: "POST",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
data: new FormData()
|
||||
};
|
||||
|
||||
options.data.append("file", file);
|
||||
|
||||
this.set("saving", true);
|
||||
|
||||
ajax(`/admin/badges/award/${this.model.id}`, options)
|
||||
.then(() => {
|
||||
bootbox.alert(I18n.t("admin.badges.mass_award.success"));
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set("saving", false));
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.badges.mass_award.aborted"));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { inject } from "@ember/controller";
|
||||
import Controller from "@ember/controller";
|
||||
@ -27,10 +27,11 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||
return modelQuery && modelQuery.trim().length > 0;
|
||||
},
|
||||
|
||||
@observes("model.id")
|
||||
_resetSaving: function() {
|
||||
this.set("saving", false);
|
||||
this.set("savingStatus", "");
|
||||
}.observes("model.id"),
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
|
||||
@ -1,2 +1,18 @@
|
||||
import Controller from "@ember/controller";
|
||||
export default Controller.extend();
|
||||
import { inject as service } from "@ember/service";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
routing: service("-routing"),
|
||||
|
||||
@discourseComputed("routing.currentRouteName")
|
||||
selectedRoute() {
|
||||
const currentRoute = this.routing.currentRouteName;
|
||||
const indexRoute = "adminBadges.index";
|
||||
if (currentRoute === indexRoute) {
|
||||
return "adminBadges.show";
|
||||
} else {
|
||||
return this.routing.currentRouteName;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import Controller from "@ember/controller";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
@discourseComputed("model.@each.id")
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { url } from "discourse/lib/computed";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
section: null,
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
notEmpty
|
||||
} from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { url } from "discourse/lib/computed";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { THEMES } from "admin/models/theme";
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@observes("filter.{status,user,address,type}")
|
||||
filterEmailLogs: discourseDebounce(function() {
|
||||
this.loadLogs();
|
||||
}, 250).observes("filter.{status,user,address,type}")
|
||||
}, 250)
|
||||
});
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { empty } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
/**
|
||||
Is the "send test email" button disabled?
|
||||
@ -14,9 +16,10 @@ export default Controller.extend({
|
||||
|
||||
@method testEmailAddressChanged
|
||||
**/
|
||||
@observes("testEmailAddress")
|
||||
testEmailAddressChanged: function() {
|
||||
this.set("sentTestEmail", false);
|
||||
}.observes("testEmailAddress"),
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import IncomingEmail from "admin/models/incoming-email";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@observes("filter.{status,from,to,subject}")
|
||||
filterIncomingEmails: discourseDebounce(function() {
|
||||
this.loadLogs(IncomingEmail);
|
||||
}, 250).observes("filter.{status,from,to,subject}"),
|
||||
}, 250),
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import IncomingEmail from "admin/models/incoming-email";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@observes("filter.{status,from,to,subject,error}")
|
||||
filterIncomingEmails: discourseDebounce(function() {
|
||||
this.loadLogs(IncomingEmail);
|
||||
}, 250).observes("filter.{status,from,to,subject,error}"),
|
||||
}, 250),
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@observes("filter.{status,user,address,type,reply_key}")
|
||||
filterEmailLogs: discourseDebounce(function() {
|
||||
this.loadLogs();
|
||||
}, 250).observes("filter.{status,user,address,type,reply_key}")
|
||||
}, 250)
|
||||
});
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@observes("filter.{status,user,address,type}")
|
||||
filterEmailLogs: discourseDebounce(function() {
|
||||
this.loadLogs();
|
||||
}, 250).observes("filter.{status,user,address,type}")
|
||||
}, 250)
|
||||
});
|
||||
|
||||
@ -3,18 +3,20 @@ import discourseDebounce from "discourse/lib/debounce";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import ScreenedIpAddress from "admin/models/screened-ip-address";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
loading: false,
|
||||
filter: null,
|
||||
savedIpAddress: null,
|
||||
|
||||
@observes("filter")
|
||||
show: discourseDebounce(function() {
|
||||
this.set("loading", true);
|
||||
ScreenedIpAddress.findAll(this.filter).then(result => {
|
||||
this.setProperties({ model: result, loading: false });
|
||||
});
|
||||
}, 250).observes("filter"),
|
||||
}, 250),
|
||||
|
||||
actions: {
|
||||
allow(record) {
|
||||
|
||||
@ -4,10 +4,7 @@ import { scheduleOnce } from "@ember/runloop";
|
||||
import Controller from "@ember/controller";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
model: null,
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
import Controller from "@ember/controller";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import Permalink from "admin/models/permalink";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
loading: false,
|
||||
filter: null,
|
||||
|
||||
@observes("filter")
|
||||
show: discourseDebounce(function() {
|
||||
Permalink.findAll(this.filter).then(result => {
|
||||
this.set("model", result);
|
||||
this.set("loading", false);
|
||||
});
|
||||
}, 250).observes("filter"),
|
||||
}, 250),
|
||||
|
||||
actions: {
|
||||
recordAdded(arg) {
|
||||
|
||||
@ -2,6 +2,7 @@ import { isEmpty } from "@ember/utils";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
filter: null,
|
||||
@ -76,13 +77,14 @@ export default Controller.extend({
|
||||
);
|
||||
},
|
||||
|
||||
@observes("filter", "onlyOverridden", "model")
|
||||
filterContent: discourseDebounce(function() {
|
||||
if (this._skipBounce) {
|
||||
this.set("_skipBounce", false);
|
||||
} else {
|
||||
this.filterContentNow();
|
||||
}
|
||||
}, 250).observes("filter", "onlyOverridden", "model"),
|
||||
}, 250),
|
||||
|
||||
actions: {
|
||||
clearFilter() {
|
||||
|
||||
@ -6,7 +6,7 @@ import CanCheckEmails from "discourse/mixins/can-check-emails";
|
||||
import { propertyNotEqual, setting } from "discourse/lib/computed";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { fmt } from "discourse/lib/computed";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import Controller from "@ember/controller";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { i18n } from "discourse/lib/computed";
|
||||
@ -29,9 +29,10 @@ export default Controller.extend(CanCheckEmails, {
|
||||
return I18n.t("admin.users.titles." + query);
|
||||
},
|
||||
|
||||
@observes("listFilter")
|
||||
_filterUsers: discourseDebounce(function() {
|
||||
this.resetFilters();
|
||||
}, 250).observes("listFilter"),
|
||||
}, 250),
|
||||
|
||||
resetFilters() {
|
||||
this._page = 1;
|
||||
|
||||
@ -3,6 +3,7 @@ import { alias } from "@ember/object/computed";
|
||||
import EmberObject from "@ember/object";
|
||||
import Controller from "@ember/controller";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
filter: null,
|
||||
@ -43,10 +44,11 @@ export default Controller.extend({
|
||||
this.set("model", matchesByAction);
|
||||
},
|
||||
|
||||
@observes("filter")
|
||||
filterContent: discourseDebounce(function() {
|
||||
this.filterContentNow();
|
||||
this.set("filtered", !isEmpty(this.filter));
|
||||
}, 250).observes("filter"),
|
||||
}, 250),
|
||||
|
||||
actions: {
|
||||
clearFilter() {
|
||||
|
||||
@ -4,10 +4,7 @@ import { inject } from "@ember/controller";
|
||||
import Controller from "@ember/controller";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
const THEME_FIELD_VARIABLE_TYPE_IDS = [2, 3, 4];
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { alias, map } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
@ -4,10 +4,7 @@ import Controller from "@ember/controller";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { THEMES, COMPONENTS } from "admin/models/theme";
|
||||
import { POPULAR_THEMES } from "discourse-common/helpers/popular-themes";
|
||||
import { set } from "@ember/object";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import {
|
||||
default as discourseComputed,
|
||||
import discourseComputed, {
|
||||
observes,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
|
||||
@ -22,11 +22,6 @@ const Report = EmberObject.extend({
|
||||
percent: false,
|
||||
higher_is_better: true,
|
||||
|
||||
@discourseComputed("modes")
|
||||
isTable(modes) {
|
||||
return modes.some(mode => mode === "table");
|
||||
},
|
||||
|
||||
@discourseComputed("type", "start_date", "end_date")
|
||||
reportUrl(type, start_date, end_date) {
|
||||
start_date = moment
|
||||
|
||||
@ -2,7 +2,7 @@ import { get } from "@ember/object";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { or, gt } from "@ember/object/computed";
|
||||
import RestModel from "discourse/models/rest";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
|
||||
@ -2,10 +2,7 @@ import { isEmpty } from "@ember/utils";
|
||||
import RestModel from "discourse/models/rest";
|
||||
import Category from "discourse/models/category";
|
||||
import Group from "discourse/models/group";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import Site from "discourse/models/site";
|
||||
|
||||
export default RestModel.extend({
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
import Route from "discourse/routes/discourse";
|
||||
|
||||
export default Route.extend({
|
||||
model(params) {
|
||||
if (params.badge_id !== "new") {
|
||||
return this.modelFor("adminBadges").findBy(
|
||||
"id",
|
||||
parseInt(params.badge_id, 10)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,8 +1,5 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import {
|
||||
default as EmailPreview,
|
||||
oneWeekAgo
|
||||
} from "admin/models/email-preview";
|
||||
import EmailPreview, { oneWeekAgo } from "admin/models/email-preview";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
|
||||
@ -190,6 +190,7 @@ export default function() {
|
||||
"adminBadges",
|
||||
{ path: "/badges", resetNamespace: true },
|
||||
function() {
|
||||
this.route("award", { path: "/award/:badge_id" });
|
||||
this.route("show", { path: "/:badge_id" });
|
||||
}
|
||||
);
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
label="admin.api.undo_revoke"}}
|
||||
{{d-button
|
||||
action=(action "deleteKey")
|
||||
actionParam=model icon="trash"
|
||||
actionParam=model icon="trash-alt"
|
||||
label="admin.api.delete"
|
||||
class="btn-danger"}}
|
||||
{{else}}
|
||||
@ -77,4 +77,4 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/admin-form-row}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
32
app/assets/javascripts/admin/templates/badges-award.hbs
Normal file
32
app/assets/javascripts/admin/templates/badges-award.hbs
Normal file
@ -0,0 +1,32 @@
|
||||
{{#d-section class="award-badge"}}
|
||||
<h1>{{i18n 'admin.badges.mass_award.title'}}</h1>
|
||||
<p>{{i18n 'admin.badges.mass_award.description'}}</p>
|
||||
|
||||
{{#if model}}
|
||||
<form class="form-horizontal">
|
||||
<div class='badge-preview'>
|
||||
{{#if model}}
|
||||
{{icon-or-image model}}
|
||||
<span class="badge-display-name">{{model.name}}</span>
|
||||
{{else}}
|
||||
<span class='badge-placeholder'>{{I18n 'admin.badges.mass_award.no_badge_selected'}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div>
|
||||
<h4>{{I18n 'admin.badges.mass_award.upload_csv'}}</h4>
|
||||
<input type='file' id='massAwardCSVUpload' accept='.csv' />
|
||||
</div>
|
||||
{{d-button
|
||||
class="btn-primary"
|
||||
action=(action 'massAward')
|
||||
disabled=saving
|
||||
label="admin.badges.mass_award.perform"}}
|
||||
{{#link-to 'adminBadges.index' class="btn btn-danger"}}
|
||||
{{d-icon "times"}}
|
||||
<span>{{i18n 'cancel'}}</span>
|
||||
{{/link-to}}
|
||||
</form>
|
||||
{{else}}
|
||||
<span class='badge-required'>{{I18n 'admin.badges.mass_award.no_badge_selected'}}</span>
|
||||
{{/if}}
|
||||
{{/d-section}}
|
||||
@ -6,13 +6,18 @@
|
||||
{{d-icon "plus"}}
|
||||
<span>{{i18n 'admin.badges.new'}}</span>
|
||||
{{/link-to}}
|
||||
|
||||
{{#link-to 'adminBadges.award' 'new' class="btn"}}
|
||||
{{d-icon "upload"}}
|
||||
<span>{{i18n 'admin.badges.mass_award.title'}}</span>
|
||||
{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='content-list'>
|
||||
<ul class="admin-badge-list">
|
||||
{{#each model as |badge|}}
|
||||
<li class="admin-badge-list-item">
|
||||
{{#link-to 'adminBadges.show' badge.id}}
|
||||
{{#link-to selectedRoute badge.id}}
|
||||
{{badge-button badge=badge}}
|
||||
{{#if badge.newBadge}}
|
||||
<span class="list-badge">{{i18n 'filters.new.lower_title'}}</span>
|
||||
@ -23,4 +28,4 @@
|
||||
</ul>
|
||||
</div>
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -160,17 +160,15 @@
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{#if model.isTable}}
|
||||
<div class="control">
|
||||
<div class="input">
|
||||
{{d-button
|
||||
class="btn-default export-csv-btn"
|
||||
action=(action "exportCsv")
|
||||
label="admin.export_csv.button_text"
|
||||
icon="download"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="input">
|
||||
{{d-button
|
||||
class="btn-default export-csv-btn"
|
||||
action=(action "exportCsv")
|
||||
label="admin.export_csv.button_text"
|
||||
icon="download"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if showRefresh}}
|
||||
<div class="control">
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
{{#if countWarning}}
|
||||
<div class="count-warning">
|
||||
<p class="heading">
|
||||
{{d-icon "warning"}}
|
||||
{{d-icon "exclamation-triangle"}}
|
||||
{{i18n "admin.badges.preview.bad_count_warning.header"}}
|
||||
</p>
|
||||
<p class="body">
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import { getWebauthnCredential } from "discourse/lib/webauthn";
|
||||
|
||||
document.getElementById("submit-security-key").onclick = function(e) {
|
||||
e.preventDefault();
|
||||
getWebauthnCredential(
|
||||
document.getElementById("security-key-challenge").value,
|
||||
document
|
||||
.getElementById("security-key-allowed-credential-ids")
|
||||
.value.split(","),
|
||||
credentialData => {
|
||||
document.getElementById("security-key-credential").value = JSON.stringify(
|
||||
credentialData
|
||||
);
|
||||
|
||||
$(e.target)
|
||||
.parents("form")
|
||||
.submit();
|
||||
},
|
||||
errorMessage => {
|
||||
document.getElementById("security-key-error").innerText = errorMessage;
|
||||
}
|
||||
);
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
require("confirm-new-email/confirm-new-email").default();
|
||||
@ -1,46 +0,0 @@
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
// Ember 2.0 removes buffered rendering, but we can still implement it ourselves.
|
||||
// In the long term we'll want to remove this.
|
||||
|
||||
const Mixin = {
|
||||
_customRender() {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const buffer = [];
|
||||
this.buildBuffer(buffer);
|
||||
this.element.innerHTML = buffer.join("");
|
||||
},
|
||||
|
||||
rerenderBuffer() {
|
||||
scheduleOnce("render", this, this._customRender);
|
||||
}
|
||||
};
|
||||
|
||||
export function bufferedRender(obj) {
|
||||
if (!obj.buildBuffer) {
|
||||
Ember.warn("Missing `buildBuffer` method", {
|
||||
id: "discourse.buffered-render.missing-build-buffer"
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
const caller = {};
|
||||
|
||||
caller.didRender = function() {
|
||||
this._super(...arguments);
|
||||
this._customRender();
|
||||
};
|
||||
|
||||
const triggers = obj.rerenderTriggers;
|
||||
if (triggers) {
|
||||
caller.init = function() {
|
||||
this._super(...arguments);
|
||||
triggers.forEach(k => this.addObserver(k, this.rerenderBuffer));
|
||||
};
|
||||
}
|
||||
delete obj.rerenderTriggers;
|
||||
|
||||
return Ember.Mixin.create(Mixin, caller, obj);
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { get } from "@ember/object";
|
||||
import Helper from "@ember/component/helper";
|
||||
import RawHandlebars from "discourse-common/lib/raw-handlebars";
|
||||
|
||||
export function makeArray(obj) {
|
||||
if (obj === null || obj === undefined) {
|
||||
@ -88,5 +89,5 @@ export function registerUnbound(name, fn) {
|
||||
_helpers[name] = Helper.extend({
|
||||
compute: (params, args) => fn(...params, args)
|
||||
});
|
||||
Handlebars.registerHelper(name, func);
|
||||
RawHandlebars.registerHelper(name, func);
|
||||
}
|
||||
|
||||
@ -6,11 +6,11 @@ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
||||
let _renderers = [];
|
||||
|
||||
const REPLACEMENTS = {
|
||||
"d-tracking": "circle",
|
||||
"d-muted": "times-circle",
|
||||
"d-regular": "far-circle",
|
||||
"d-watching": "exclamation-circle",
|
||||
"d-watching-first": "far-dot-circle",
|
||||
"d-tracking": "bell",
|
||||
"d-muted": "discourse-bell-slash",
|
||||
"d-regular": "far-bell",
|
||||
"d-watching": "discourse-bell-exclamation",
|
||||
"d-watching-first": "discourse-bell-one",
|
||||
"d-drop-expanded": "caret-down",
|
||||
"d-drop-collapsed": "caret-right",
|
||||
"d-unliked": "far-heart",
|
||||
@ -29,11 +29,11 @@ const REPLACEMENTS = {
|
||||
"notification.invited_to_private_message": "far-envelope",
|
||||
"notification.invited_to_topic": "hand-point-right",
|
||||
"notification.invitee_accepted": "user",
|
||||
"notification.moved_post": "sign-out",
|
||||
"notification.moved_post": "sign-out-alt",
|
||||
"notification.linked": "link",
|
||||
"notification.granted_badge": "certificate",
|
||||
"notification.topic_reminder": "far-clock",
|
||||
"notification.watching_first_post": "far-dot-circle",
|
||||
"notification.watching_first_post": "discourse-bell-one",
|
||||
"notification.group_message_summary": "users",
|
||||
"notification.post_approved": "check",
|
||||
"notification.membership_request_accepted": "user-plus",
|
||||
@ -580,21 +580,25 @@ function warnIfMissing(id) {
|
||||
}
|
||||
}
|
||||
|
||||
const reportedIcons = [];
|
||||
|
||||
function warnIfDeprecated(oldId, newId) {
|
||||
deprecated(
|
||||
`Please replace all occurrences of "${oldId}"" with "${newId}". FontAwesome 4.7 icon names are now deprecated and will be removed in the next release.`
|
||||
);
|
||||
if (!Discourse.testing) {
|
||||
if (!Discourse.testing && !reportedIcons.includes(oldId)) {
|
||||
const errorData = {
|
||||
message: `FA icon deprecation: replace "${oldId}"" with "${newId}".`,
|
||||
stacktrace: Error().stack
|
||||
};
|
||||
|
||||
Ember.$.ajax(`${Discourse.BaseUri}/logs/report_js_error`, {
|
||||
errorData,
|
||||
data: errorData,
|
||||
type: "POST",
|
||||
cache: false
|
||||
});
|
||||
|
||||
reportedIcons.push(oldId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
import { get } from "@ember/object";
|
||||
|
||||
export function registerRawHelpers(hbs, handlebarsClass) {
|
||||
hbs.helper = function() {};
|
||||
hbs.helpers = Object.create(handlebarsClass.helpers);
|
||||
if (!hbs.helpers) {
|
||||
hbs.helpers = Object.create(handlebarsClass.helpers);
|
||||
}
|
||||
|
||||
hbs.helpers["get"] = function(context, options) {
|
||||
var firstContext = options.contexts[0];
|
||||
var val = firstContext[context];
|
||||
if (!context || !options.contexts) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.indexOf("controller.") === 0) {
|
||||
if (typeof context !== "string") {
|
||||
return context;
|
||||
}
|
||||
|
||||
let firstContext = options.contexts[0];
|
||||
let val = firstContext[context];
|
||||
|
||||
if (context.toString().indexOf("controller.") === 0) {
|
||||
context = context.slice(context.indexOf(".") + 1);
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
/*global Mousetrap:true*/
|
||||
import { buildResolver } from "discourse-common/resolver";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { computed } from "@ember/object";
|
||||
import FocusEvent from "discourse-common/mixins/focus-event";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
@ -2,6 +2,6 @@ import RESTAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RESTAdapter.extend({
|
||||
pathFor(store, type, id) {
|
||||
return "/tags/" + id + "/info";
|
||||
return "/tag/" + id + "/info";
|
||||
}
|
||||
});
|
||||
|
||||
@ -2,6 +2,6 @@ import RESTAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RESTAdapter.extend({
|
||||
pathFor(store, type, id) {
|
||||
return "/tags/" + id + "/notifications";
|
||||
return "/tag/" + id + "/notifications";
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
import discourseComputed, {
|
||||
on,
|
||||
observes,
|
||||
default as discourseComputed
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import { findRawTemplate } from "discourse/lib/raw-templates";
|
||||
const { makeArray } = Ember;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { alias, not } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
|
||||
@ -16,9 +16,10 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
@observes("topicList.[]")
|
||||
_topicListChanged: function() {
|
||||
this._initFromTopicList(this.topicList);
|
||||
}.observes("topicList.[]"),
|
||||
},
|
||||
|
||||
_initFromTopicList(topicList) {
|
||||
if (topicList !== null) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { filter } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
|
||||
// A breadcrumb including category drop downs
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import Component from "@ember/component";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "section",
|
||||
@ -19,16 +18,5 @@ export default Component.extend({
|
||||
@discourseComputed("categories.[].subcategories")
|
||||
hasSubcategories() {
|
||||
return this.categories.any(c => !isEmpty(c.get("subcategories")));
|
||||
},
|
||||
|
||||
click(e) {
|
||||
if (!$(e.target).is("a")) {
|
||||
const url = $(e.target)
|
||||
.closest(".category-box")
|
||||
.data("url");
|
||||
if (url) {
|
||||
DiscourseURL.routeTo(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { alias, equal } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import {
|
||||
PRIVATE_MESSAGE,
|
||||
CREATE_TOPIC,
|
||||
|
||||
@ -4,10 +4,7 @@ import { cancel } from "@ember/runloop";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { later } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import Composer from "discourse/models/composer";
|
||||
import afterTransition from "discourse/lib/after-transition";
|
||||
import positioningWorkaround from "discourse/lib/safari-hacks";
|
||||
@ -70,7 +67,7 @@ export default Component.extend(KeyEnterEscape, {
|
||||
return;
|
||||
}
|
||||
|
||||
const h = $("#reply-control").height() || 0;
|
||||
const h = $("#reply-control:not(.saving)").height() || 0;
|
||||
this.movePanels(h);
|
||||
});
|
||||
},
|
||||
|
||||
@ -5,8 +5,7 @@ import { scheduleOnce } from "@ember/runloop";
|
||||
import { later } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import userSearch from "discourse/lib/user-search";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
import discourseComputed, {
|
||||
observes,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
|
||||
@ -97,16 +97,11 @@ export default Component.extend({
|
||||
|
||||
const composer = this.composer;
|
||||
if (composer.get("privateMessage")) {
|
||||
let usernames = composer.get("targetUsernames");
|
||||
|
||||
if (usernames) {
|
||||
usernames = usernames.split(",");
|
||||
}
|
||||
const recipients = composer.targetRecipientsArray;
|
||||
|
||||
if (
|
||||
usernames &&
|
||||
usernames.length === 1 &&
|
||||
usernames[0] === this.currentUser.get("username")
|
||||
recipients.length > 0 &&
|
||||
recipients.every(r => r.name === this.currentUser.get("username"))
|
||||
) {
|
||||
const message =
|
||||
this._yourselfConfirm ||
|
||||
|
||||
@ -3,10 +3,7 @@ import { next } from "@ember/runloop";
|
||||
import { debounce } from "@ember/runloop";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { load } from "pretty-text/oneboxer";
|
||||
import { lookupCache } from "pretty-text/oneboxer-cache";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
showSelector: true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { notEmpty, empty, equal } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default Component.extend({
|
||||
|
||||
@ -6,8 +6,7 @@ import { later } from "@ember/runloop";
|
||||
import { inject as service } from "@ember/service";
|
||||
import Component from "@ember/component";
|
||||
/*global Mousetrap:true */
|
||||
import {
|
||||
default as discourseComputed,
|
||||
import discourseComputed, {
|
||||
on,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
@ -842,7 +841,7 @@ export default Component.extend({
|
||||
}
|
||||
|
||||
const isComposer = $("#reply-control .d-editor-input").is(":focus");
|
||||
let { clipboard, canPasteHtml } = clipboardData(e, isComposer);
|
||||
let { clipboard, canPasteHtml, canUpload } = clipboardData(e, isComposer);
|
||||
|
||||
let plainText = clipboard.getData("text/plain");
|
||||
let html = clipboard.getData("text/html");
|
||||
@ -892,7 +891,7 @@ export default Component.extend({
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
if (handled || canUpload) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
@ -2,10 +2,7 @@ import { next } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
/* global Pikaday:true */
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["d-date-input"],
|
||||
|
||||
@ -2,10 +2,7 @@ import { next } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
/* global Pikaday:true */
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
|
||||
const DATE_FORMAT = "YYYY-MM-DD";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "span",
|
||||
|
||||
@ -13,6 +13,6 @@ export default Component.extend({
|
||||
|
||||
@discourseComputed("tagRecord.id")
|
||||
href(tagRecordId) {
|
||||
return Discourse.getURL("/tags/" + tagRecordId);
|
||||
return Discourse.getURL("/tag/" + tagRecordId);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import EmberObject from "@ember/object";
|
||||
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default buildCategoryPanel("images").extend({
|
||||
@discourseComputed("category.uploaded_background.url")
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { buildCategoryPanel } from "discourse/components/edit-category-panel";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default buildCategoryPanel("topic-template", {
|
||||
@observes("activeTab")
|
||||
_activeTabChanged: function() {
|
||||
if (this.activeTab) {
|
||||
scheduleOnce("afterRender", () =>
|
||||
this.element.querySelector(".d-editor-input").focus()
|
||||
);
|
||||
}
|
||||
}.observes("activeTab")
|
||||
}
|
||||
});
|
||||
|
||||
@ -2,8 +2,7 @@ import { isEmpty } from "@ember/utils";
|
||||
import { alias, equal, or } from "@ember/object/computed";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
import discourseComputed, {
|
||||
observes,
|
||||
on
|
||||
} from "discourse-common/utils/decorators";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { notEmpty, not } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
|
||||
export default Component.extend(UploadMixin, {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "button",
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { equal, and, empty } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import { FORMAT } from "select-kit/components/future-date-input-selector";
|
||||
import { PUBLISH_TO_CATEGORY_STATUS_TYPE } from "discourse/controllers/edit-topic-timer";
|
||||
|
||||
|
||||
@ -1,119 +1,233 @@
|
||||
import { bind } from "@ember/runloop";
|
||||
import { bind, cancel } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import { on } from "discourse-common/utils/decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import LogsNotice from "discourse/services/logs-notice";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
import EmberObject from "@ember/object";
|
||||
|
||||
export default Component.extend(
|
||||
bufferedRender({
|
||||
rerenderTriggers: ["site.isReadOnly", "siteSettings.disable_emails"],
|
||||
const _pluginNotices = [];
|
||||
|
||||
buildBuffer(buffer) {
|
||||
export function addGlobalNotice(text, id, options = {}) {
|
||||
_pluginNotices.push(Notice.create({ text, id, options }));
|
||||
}
|
||||
|
||||
const GLOBAL_NOTICE_DISMISSED_PROMPT_KEY = "dismissed-global-notice-v2";
|
||||
|
||||
const Notice = EmberObject.extend({
|
||||
text: null,
|
||||
id: null,
|
||||
options: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
const defaults = {
|
||||
// can this banner be hidden
|
||||
dismissable: false,
|
||||
// prepend html content
|
||||
html: null,
|
||||
// will define the style of the banner, follows alerts styling
|
||||
level: "info",
|
||||
// should the banner be permanently hidden?
|
||||
persistentDismiss: true,
|
||||
// callback function when dismissing a banner
|
||||
onDismiss: null,
|
||||
// show/hide banner function, will take precedence over everything
|
||||
visibility: null,
|
||||
// how long before banner should show again, eg: moment.duration(1, "week")
|
||||
dismissDuration: null
|
||||
};
|
||||
|
||||
this.options = this.set(
|
||||
"options",
|
||||
Object.assign(defaults, this.options || {})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default Component.extend({
|
||||
logNotice: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._setupObservers();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._tearDownObservers();
|
||||
},
|
||||
|
||||
notices: Ember.computed(
|
||||
"site.isReadOnly",
|
||||
"siteSettings.disable_emails",
|
||||
"logNotice.{id,text,hidden}",
|
||||
function() {
|
||||
let notices = [];
|
||||
|
||||
if ($.cookie("dosp") === "1") {
|
||||
$.removeCookie("dosp", { path: "/" });
|
||||
notices.push([I18n.t("forced_anonymous"), "forced-anonymous"]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("forced_anonymous"),
|
||||
id: "forced-anonymous"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (this.session.get("safe_mode")) {
|
||||
notices.push([I18n.t("safe_mode.enabled"), "safe-mode"]);
|
||||
if (this.session && this.session.safe_mode) {
|
||||
notices.push(
|
||||
Notice.create({ text: I18n.t("safe_mode.enabled"), id: "safe-mode" })
|
||||
);
|
||||
}
|
||||
|
||||
if (this.site.get("isReadOnly")) {
|
||||
notices.push([I18n.t("read_only_mode.enabled"), "alert-read-only"]);
|
||||
if (this.site.isReadOnly) {
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("read_only_mode.enabled"),
|
||||
id: "alert-read-only"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.siteSettings.disable_emails === "yes" ||
|
||||
this.siteSettings.disable_emails === "non-staff"
|
||||
) {
|
||||
notices.push([I18n.t("emails_are_disabled"), "alert-emails-disabled"]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("emails_are_disabled"),
|
||||
id: "alert-emails-disabled"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (this.site.get("wizard_required")) {
|
||||
if (this.site.wizard_required) {
|
||||
const requiredText = I18n.t("wizard_required", {
|
||||
url: Discourse.getURL("/wizard")
|
||||
});
|
||||
notices.push([requiredText, "alert-wizard"]);
|
||||
notices.push(Notice.create({ text: requiredText, id: "alert-wizard" }));
|
||||
}
|
||||
|
||||
if (
|
||||
this.currentUser &&
|
||||
this.currentUser.get("staff") &&
|
||||
this.get("currentUser.staff") &&
|
||||
this.siteSettings.bootstrap_mode_enabled
|
||||
) {
|
||||
if (this.siteSettings.bootstrap_mode_min_users > 0) {
|
||||
notices.push([
|
||||
I18n.t("bootstrap_mode_enabled", {
|
||||
min_users: this.siteSettings.bootstrap_mode_min_users
|
||||
}),
|
||||
"alert-bootstrap-mode"
|
||||
]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("bootstrap_mode_enabled", {
|
||||
min_users: this.siteSettings.bootstrap_mode_min_users
|
||||
}),
|
||||
id: "alert-bootstrap-mode"
|
||||
})
|
||||
);
|
||||
} else {
|
||||
notices.push([
|
||||
I18n.t("bootstrap_mode_disabled"),
|
||||
"alert-bootstrap-mode"
|
||||
]);
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: I18n.t("bootstrap_mode_disabled"),
|
||||
id: "alert-bootstrap-mode"
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.isEmpty(this.siteSettings.global_notice)) {
|
||||
notices.push([this.siteSettings.global_notice, "alert-global-notice"]);
|
||||
}
|
||||
|
||||
if (!LogsNotice.currentProp("hidden")) {
|
||||
notices.push([
|
||||
LogsNotice.currentProp("message"),
|
||||
"alert-logs-notice",
|
||||
`<button class='btn btn-flat close'>${iconHTML("times")}</button>`
|
||||
]);
|
||||
}
|
||||
|
||||
if (notices.length > 0) {
|
||||
buffer.push(
|
||||
notices
|
||||
.map(n => {
|
||||
var html = `<div class='row'><div class='alert alert-info ${n[1]}'>`;
|
||||
if (n[2]) html += n[2];
|
||||
html += `${n[0]}</div></div>`;
|
||||
return html;
|
||||
})
|
||||
.join("")
|
||||
if (
|
||||
this.siteSettings.global_notice &&
|
||||
this.siteSettings.global_notice.length
|
||||
) {
|
||||
notices.push(
|
||||
Notice.create({
|
||||
text: this.siteSettings.global_notice,
|
||||
id: "alert-global-notice"
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_setupLogsNotice() {
|
||||
this._boundRerenderBuffer = bind(this, this.rerenderBuffer);
|
||||
LogsNotice.current().addObserver("hidden", this._boundRerenderBuffer);
|
||||
|
||||
this._boundResetCurrentProp = bind(this, this._resetCurrentProp);
|
||||
$(this.element).on(
|
||||
"click.global-notice",
|
||||
".alert-logs-notice .close",
|
||||
this._boundResetCurrentProp
|
||||
);
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_teardownLogsNotice() {
|
||||
if (this._boundResetCurrentProp) {
|
||||
$(this.element).off("click.global-notice", this._boundResetCurrentProp);
|
||||
if (this.logNotice) {
|
||||
notices.push(this.logNotice);
|
||||
}
|
||||
|
||||
if (this._boundRerenderBuffer) {
|
||||
LogsNotice.current().removeObserver(
|
||||
"hidden",
|
||||
this._boundRerenderBuffer
|
||||
);
|
||||
}
|
||||
},
|
||||
return notices.concat(_pluginNotices).filter(notice => {
|
||||
if (notice.options.visibility) {
|
||||
return notice.options.visibility(notice);
|
||||
} else {
|
||||
const key = `${GLOBAL_NOTICE_DISMISSED_PROMPT_KEY}-${notice.id}`;
|
||||
const value = this.keyValueStore.get(key);
|
||||
|
||||
_resetCurrentProp() {
|
||||
LogsNotice.currentProp("text", "");
|
||||
// banner has never been dismissed
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// banner has no persistent dismiss and should always show on load
|
||||
if (!notice.options.persistentDismiss) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (notice.options.dismissDuration) {
|
||||
const resetAt = moment(value).add(notice.options.dismissDuration);
|
||||
return moment().isAfter(resetAt);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
),
|
||||
|
||||
actions: {
|
||||
dismissNotice(notice) {
|
||||
if (notice.options.onDismiss) {
|
||||
notice.options.onDismiss(notice);
|
||||
}
|
||||
|
||||
if (notice.options.persistentDismiss) {
|
||||
this.keyValueStore.set({
|
||||
key: `${GLOBAL_NOTICE_DISMISSED_PROMPT_KEY}-${notice.id}`,
|
||||
value: moment().toISOString(true)
|
||||
});
|
||||
}
|
||||
|
||||
const alert = document.getElementById(`global-notice-${notice.id}`);
|
||||
if (alert) alert.style.display = "none";
|
||||
}
|
||||
},
|
||||
|
||||
_setupObservers() {
|
||||
this._boundLogsNoticeHandler = bind(this, this._handleLogsNoticeUpdate);
|
||||
LogsNotice.current().addObserver("hidden", this._boundLogsNoticeHandler);
|
||||
LogsNotice.current().addObserver("text", this._boundLogsNoticeHandler);
|
||||
},
|
||||
|
||||
_tearDownObservers() {
|
||||
if (this._boundLogsNoticeHandler) {
|
||||
LogsNotice.current().removeObserver("text", this._boundLogsNoticeHandler);
|
||||
LogsNotice.current().removeObserver(
|
||||
"hidden",
|
||||
this._boundLogsNoticeHandler
|
||||
);
|
||||
cancel(this._boundLogsNoticeHandler);
|
||||
}
|
||||
},
|
||||
|
||||
_handleLogsNoticeUpdate() {
|
||||
const logNotice = Notice.create({
|
||||
text: LogsNotice.currentProp("message"),
|
||||
id: "alert-logs-notice",
|
||||
options: {
|
||||
dismissable: true,
|
||||
persistentDismiss: false,
|
||||
visibility() {
|
||||
return !LogsNotice.currentProp("hidden");
|
||||
},
|
||||
onDismiss() {
|
||||
LogsNotice.currentProp("hidden", true);
|
||||
LogsNotice.currentProp("text", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.set("logNotice", logNotice);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { alias, match, gt, or } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { setting } from "discourse/lib/computed";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import CardContentsBase from "discourse/mixins/card-contents-base";
|
||||
import CleansUp from "discourse/mixins/cleans-up";
|
||||
import { groupPath } from "discourse/lib/url";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
@ -31,6 +31,14 @@ export default Component.extend({
|
||||
$.cookie("destination_url", window.location.href);
|
||||
},
|
||||
|
||||
removeFromGroup() {
|
||||
this.model
|
||||
.removeMember(this.currentUser)
|
||||
.then(() => this.model.set("is_group_user", false))
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set("updatingMembership", false));
|
||||
},
|
||||
|
||||
actions: {
|
||||
joinGroup() {
|
||||
if (this.currentUser) {
|
||||
@ -53,17 +61,21 @@ export default Component.extend({
|
||||
|
||||
leaveGroup() {
|
||||
this.set("updatingMembership", true);
|
||||
const model = this.model;
|
||||
|
||||
model
|
||||
.removeMember(this.currentUser)
|
||||
.then(() => {
|
||||
model.set("is_group_user", false);
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this.set("updatingMembership", false);
|
||||
});
|
||||
if (this.model.public_admission) {
|
||||
this.removeFromGroup();
|
||||
} else {
|
||||
return bootbox.confirm(
|
||||
I18n.t("groups.confirm_leave"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
result
|
||||
? this.removeFromGroup()
|
||||
: this.set("updatingMembership", false);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
showRequestMembershipForm() {
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
import discourseComputed, {
|
||||
on,
|
||||
observes,
|
||||
default as discourseComputed
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import { findRawTemplate } from "discourse/lib/raw-templates";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
init() {
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { not } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import Group from "discourse/models/group";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import discourseDebounce from "discourse/lib/debounce";
|
||||
|
||||
@ -4,7 +4,6 @@ import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":tip", "good", "bad"],
|
||||
rerenderTriggers: ["validation"],
|
||||
tipIcon: null,
|
||||
tipReason: null,
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { next } from "@ember/runloop";
|
||||
import Component from "@ember/component";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { renderedConnectorsFor } from "discourse/lib/plugin-connectors";
|
||||
import FilterModeMixin from "discourse/mixins/filter-mode";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { default as discourseComputed } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { keyValueStore as pushNotificationKeyValueStore } from "discourse/lib/push-notifications";
|
||||
import { default as DesktopNotificationConfig } from "discourse/components/desktop-notification-config";
|
||||
import DesktopNotificationConfig from "discourse/components/desktop-notification-config";
|
||||
|
||||
const userDismissedPromptKey = "dismissed-prompt";
|
||||
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { alias, not } from "@ember/object/computed";
|
||||
import Component from "@ember/component";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import {
|
||||
default as discourseComputed,
|
||||
observes
|
||||
} from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [":popup-tip", "good", "bad", "lastShownAt::hide"],
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import Component from "@ember/component";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: null,
|
||||
href: null,
|
||||
title: null,
|
||||
ariaLabel: null
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user