Merge master
This commit is contained in:
commit
ea7ee8e9f7
@ -4,7 +4,8 @@
|
||||
"node": true,
|
||||
"mocha": true,
|
||||
"browser": true,
|
||||
"builtin": true
|
||||
"builtin": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 7,
|
||||
@ -47,6 +48,7 @@
|
||||
"selectKitSelectRowByValue":true,
|
||||
"selectKitSelectRowByName":true,
|
||||
"selectKitSelectRowByIndex":true,
|
||||
"keyboardHelper":true,
|
||||
"selectKitSelectNoneRow":true,
|
||||
"selectKitFillInFilter":true,
|
||||
"asyncTestDiscourse":true,
|
||||
|
||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
app/assets/stylesheets/vendor/
|
||||
plugins/**/assets/stylesheets/vendor/
|
||||
package.json
|
||||
@ -102,6 +102,9 @@ Layout/EndAlignment:
|
||||
Lint/RequireParentheses:
|
||||
Enabled: true
|
||||
|
||||
Lint/ShadowingOuterLocalVariable:
|
||||
Enabled: true
|
||||
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
Enabled: true
|
||||
EnforcedStyle: indented
|
||||
|
||||
@ -1 +1 @@
|
||||
2.4.1
|
||||
2.4.4
|
||||
|
||||
@ -29,6 +29,9 @@ addons:
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
exclude:
|
||||
- rvm: 2.4.4
|
||||
env: "RAILS_MASTER=0 QUNIT_RUN=0 RUN_LINT=1"
|
||||
|
||||
rvm:
|
||||
- 2.5.1
|
||||
@ -58,6 +61,7 @@ before_install:
|
||||
- git clone --depth=1 https://github.com/discourse/discourse-assign.git plugins/discourse-assign
|
||||
- git clone --depth=1 https://github.com/discourse/discourse-patreon.git plugins/discourse-patreon
|
||||
- git clone --depth=1 https://github.com/discourse/discourse-staff-notes.git plugins/discourse-staff-notes
|
||||
- git clone --depth=1 https://github.com/discourse/discourse-group-tracker
|
||||
- export PATH=$HOME/.yarn/bin:$PATH
|
||||
|
||||
install:
|
||||
@ -71,6 +75,7 @@ script:
|
||||
bash -c "
|
||||
if [ '$RUN_LINT' == '1' ]; then
|
||||
bundle exec rubocop --parallel && \
|
||||
bundle exec danger && \
|
||||
yarn eslint --ext .es6 app/assets/javascripts && \
|
||||
yarn eslint --ext .es6 test/javascripts && \
|
||||
yarn eslint --ext .es6 plugins/**/assets/javascripts && \
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = el_GR: el, es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt, sk_SK: sk, vi_VN: vi
|
||||
lang_map = el_GR: el, es_ES: es, fr_FR: fr, hu_HU: hu, ko_KR: ko, pt_PT: pt, sk_SK: sk, vi_VN: vi
|
||||
|
||||
[discourse-org.core-client-yml]
|
||||
file_filter = config/locales/client.<lang>.yml
|
||||
|
||||
11
Dangerfile
Normal file
11
Dangerfile
Normal file
@ -0,0 +1,11 @@
|
||||
if github.pr_json && (github.pr_json["additions"] || 0) > 250 || (github.pr_json["deletions"] || 0) > 250
|
||||
warn("This pull request is big! We prefer smaller PRs whenever possible, as they are easier to review. Can this be split into a few smaller PRs?")
|
||||
end
|
||||
|
||||
prettier_offenses = `prettier --list-different "app/assets/stylesheets/**/*.scss" "app/assets/javascripts/**/*.es6" "test/javascripts/**/*.es6"`.split('\n')
|
||||
if !prettier_offenses.empty?
|
||||
fail(%{
|
||||
This PR doesn't match our required code formatting standards, as enforced by prettier.io. <a href='https://meta.discourse.org/t/prettier-code-formatting-tool/93212'>Here's how to set up prettier in your code editor.</a>\n
|
||||
#{prettier_offenses.map { |o| github.html_link(o) }.join("\n")}
|
||||
})
|
||||
end
|
||||
29
Gemfile
29
Gemfile
@ -13,13 +13,13 @@ if rails_master?
|
||||
gem 'rails', git: 'https://github.com/rails/rails.git'
|
||||
gem 'seed-fu', git: 'https://github.com/SamSaffron/seed-fu.git', branch: 'discourse'
|
||||
else
|
||||
gem 'actionmailer', '~> 5.1'
|
||||
gem 'actionpack', '~> 5.1'
|
||||
gem 'actionview', '~> 5.1'
|
||||
gem 'activemodel', '~> 5.1'
|
||||
gem 'activerecord', '~> 5.1'
|
||||
gem 'activesupport', '~> 5.1'
|
||||
gem 'railties', '~> 5.1'
|
||||
gem 'actionmailer', '5.2'
|
||||
gem 'actionpack', '5.2'
|
||||
gem 'actionview', '5.2'
|
||||
gem 'activemodel', '5.2'
|
||||
gem 'activerecord', '5.2'
|
||||
gem 'activesupport', '5.2'
|
||||
gem 'railties', '5.2'
|
||||
gem 'sprockets-rails'
|
||||
gem 'seed-fu'
|
||||
end
|
||||
@ -34,7 +34,7 @@ gem 'redis-namespace'
|
||||
|
||||
gem 'active_model_serializers', '~> 0.8.3'
|
||||
|
||||
gem 'onebox', '1.8.48'
|
||||
gem 'onebox', '1.8.60'
|
||||
|
||||
gem 'http_accept_language', '~>2.0.5', require: false
|
||||
|
||||
@ -60,7 +60,7 @@ gem 'unf', require: false
|
||||
|
||||
gem 'email_reply_trimmer', '~> 0.1'
|
||||
|
||||
# Forked until https://github.com/toy/image_optim/pull/149 is merged
|
||||
# Forked until https://github.com/toy/image_optim/pull/162 is merged
|
||||
gem 'discourse_image_optim', require: 'image_optim'
|
||||
gem 'multi_json'
|
||||
gem 'mustache'
|
||||
@ -78,7 +78,8 @@ gem 'omniauth-oauth2', require: false
|
||||
|
||||
gem 'omniauth-google-oauth2'
|
||||
gem 'oj'
|
||||
gem 'pg', '~> 0.21.0'
|
||||
gem 'pg'
|
||||
gem 'mini_sql'
|
||||
gem 'pry-rails', require: false
|
||||
gem 'r2', '~> 0.2.5', require: false
|
||||
gem 'rake'
|
||||
@ -87,13 +88,14 @@ gem 'thor', require: false
|
||||
gem 'rinku'
|
||||
gem 'sanitize'
|
||||
gem 'sidekiq'
|
||||
gem 'mini_scheduler'
|
||||
|
||||
# for sidekiq web
|
||||
gem 'tilt', require: false
|
||||
|
||||
gem 'execjs', require: false
|
||||
gem 'mini_racer'
|
||||
gem 'highline', require: false
|
||||
gem 'highline', '~> 1.7.0', require: false
|
||||
gem 'rack-protection' # security
|
||||
|
||||
# Gems used only for assets and not required in production environments by default.
|
||||
@ -107,6 +109,7 @@ group :test do
|
||||
gem 'webmock', require: false
|
||||
gem 'fakeweb', '~> 1.3.0', require: false
|
||||
gem 'minitest', require: false
|
||||
gem 'danger'
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
@ -178,12 +181,16 @@ gem 'rqrcode'
|
||||
|
||||
gem 'sshkey', require: false
|
||||
|
||||
gem 'rchardet', require: false
|
||||
|
||||
if ENV["IMPORT"] == "1"
|
||||
gem 'mysql2'
|
||||
gem 'redcarpet'
|
||||
gem 'sqlite3', '~> 1.3.13'
|
||||
gem 'ruby-bbcode-to-md', github: 'nlalonde/ruby-bbcode-to-md'
|
||||
gem 'reverse_markdown'
|
||||
gem 'tiny_tds'
|
||||
end
|
||||
|
||||
gem 'webpush', require: false
|
||||
gem 'colored2', require: false
|
||||
|
||||
348
Gemfile.lock
348
Gemfile.lock
@ -1,97 +1,119 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionmailer (5.1.4)
|
||||
actionpack (= 5.1.4)
|
||||
actionview (= 5.1.4)
|
||||
activejob (= 5.1.4)
|
||||
actionmailer (5.2.0)
|
||||
actionpack (= 5.2.0)
|
||||
actionview (= 5.2.0)
|
||||
activejob (= 5.2.0)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.1.4)
|
||||
actionview (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
actionpack (5.2.0)
|
||||
actionview (= 5.2.0)
|
||||
activesupport (= 5.2.0)
|
||||
rack (~> 2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
actionview (5.2.0)
|
||||
activesupport (= 5.2.0)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
active_model_serializers (0.8.3)
|
||||
active_model_serializers (0.8.4)
|
||||
activemodel (>= 3.0)
|
||||
activejob (5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
activejob (5.2.0)
|
||||
activesupport (= 5.2.0)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
activerecord (5.1.4)
|
||||
activemodel (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
arel (~> 8.0)
|
||||
activesupport (5.1.4)
|
||||
activemodel (5.2.0)
|
||||
activesupport (= 5.2.0)
|
||||
activerecord (5.2.0)
|
||||
activemodel (= 5.2.0)
|
||||
activesupport (= 5.2.0)
|
||||
arel (>= 9.0)
|
||||
activesupport (5.2.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (~> 0.7)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.1)
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
annotate (2.7.2)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
annotate (2.7.4)
|
||||
activerecord (>= 3.2, < 6.0)
|
||||
rake (>= 10.4, < 13.0)
|
||||
arel (8.0.0)
|
||||
arel (9.0.0)
|
||||
ast (2.4.0)
|
||||
aws-partitions (1.24.0)
|
||||
aws-sdk-core (3.6.0)
|
||||
aws-eventstream (1.0.1)
|
||||
aws-partitions (1.92.0)
|
||||
aws-sdk-core (3.21.2)
|
||||
aws-eventstream (~> 1.0)
|
||||
aws-partitions (~> 1.0)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.2.0)
|
||||
aws-sdk-kms (1.5.0)
|
||||
aws-sdk-core (~> 3)
|
||||
aws-sigv4 (~> 1.0)
|
||||
aws-sdk-s3 (1.4.0)
|
||||
aws-sdk-core (~> 3)
|
||||
aws-sdk-s3 (1.14.0)
|
||||
aws-sdk-core (~> 3, >= 3.21.2)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.0)
|
||||
aws-sigv4 (1.0.2)
|
||||
barber (0.11.2)
|
||||
ember-source (>= 1.0, < 3)
|
||||
barber (0.12.0)
|
||||
ember-source (>= 1.0, < 3.1)
|
||||
execjs (>= 1.2, < 3)
|
||||
better_errors (2.1.1)
|
||||
better_errors (2.4.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.8.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootsnap (1.1.8)
|
||||
bootsnap (1.3.0)
|
||||
msgpack (~> 1.0)
|
||||
builder (3.2.3)
|
||||
bullet (5.5.1)
|
||||
bullet (5.7.5)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.10.0)
|
||||
byebug (9.0.6)
|
||||
uniform_notifier (~> 1.11.0)
|
||||
byebug (10.0.2)
|
||||
certified (1.0.0)
|
||||
chunky_png (1.3.8)
|
||||
chunky_png (1.3.10)
|
||||
claide (1.0.2)
|
||||
claide-plugins (0.9.2)
|
||||
cork
|
||||
nap
|
||||
open4 (~> 1.3)
|
||||
coderay (1.1.2)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
connection_pool (2.2.1)
|
||||
connection_pool (2.2.2)
|
||||
cork (0.3.0)
|
||||
colored2 (~> 3.1)
|
||||
cppjieba_rb (0.3.0)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.3)
|
||||
crass (1.0.4)
|
||||
danger (5.6.3)
|
||||
claide (~> 1.0)
|
||||
claide-plugins (>= 0.9.2)
|
||||
colored2 (~> 3.1)
|
||||
cork (~> 0.1)
|
||||
faraday (~> 0.9)
|
||||
faraday-http-cache (~> 1.0)
|
||||
git (~> 1)
|
||||
kramdown (~> 1.5)
|
||||
no_proxy_fix
|
||||
octokit (~> 4.7)
|
||||
terminal-table (~> 1)
|
||||
debug_inspector (0.0.3)
|
||||
diff-lcs (1.3)
|
||||
discourse_image_optim (0.24.5)
|
||||
discourse_image_optim (0.26.2)
|
||||
exifr (~> 1.2, >= 1.2.2)
|
||||
fspath (~> 3.0)
|
||||
image_size (~> 1.5)
|
||||
in_threads (~> 1.3)
|
||||
progress (~> 3.0, >= 3.0.1)
|
||||
email_reply_trimmer (0.1.12)
|
||||
ember-data-source (2.2.1)
|
||||
ember-source (>= 1.8, < 3.0)
|
||||
ember-data-source (3.0.2)
|
||||
ember-source (>= 2, < 3.0)
|
||||
ember-handlebars-template (0.7.5)
|
||||
barber (>= 0.11.0)
|
||||
sprockets (>= 3.3, < 4)
|
||||
@ -103,61 +125,66 @@ GEM
|
||||
jquery-rails (>= 1.0.17)
|
||||
railties (>= 3.1)
|
||||
ember-source (2.13.3)
|
||||
erubi (1.6.1)
|
||||
erubis (2.7.0)
|
||||
excon (0.56.0)
|
||||
erubi (1.7.1)
|
||||
excon (0.62.0)
|
||||
execjs (2.7.0)
|
||||
exifr (1.2.5)
|
||||
fabrication (2.9.8)
|
||||
exifr (1.3.4)
|
||||
fabrication (2.20.1)
|
||||
fakeweb (1.3.0)
|
||||
faraday (0.11.0)
|
||||
faraday (0.12.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-http-cache (1.3.1)
|
||||
faraday (~> 0.8)
|
||||
fast_blank (1.0.0)
|
||||
fast_xor (1.1.3)
|
||||
rake
|
||||
rake-compiler
|
||||
fast_xs (0.8.0)
|
||||
fastimage (2.1.1)
|
||||
ffi (1.9.18)
|
||||
fastimage (2.1.3)
|
||||
ffi (1.9.25)
|
||||
flamegraph (0.9.5)
|
||||
foreman (0.84.0)
|
||||
foreman (0.85.0)
|
||||
thor (~> 0.19.1)
|
||||
fspath (3.1.0)
|
||||
gc_tracer (1.5.1)
|
||||
globalid (0.4.0)
|
||||
git (1.4.0)
|
||||
globalid (0.4.1)
|
||||
activesupport (>= 4.2.0)
|
||||
guess_html_encoding (0.0.11)
|
||||
hashdiff (0.3.4)
|
||||
hashie (3.5.5)
|
||||
highline (1.7.8)
|
||||
hashdiff (0.3.7)
|
||||
hashie (3.5.7)
|
||||
highline (1.7.10)
|
||||
hiredis (0.6.1)
|
||||
hkdf (0.3.0)
|
||||
htmlentities (4.3.4)
|
||||
http_accept_language (2.0.5)
|
||||
i18n (0.8.6)
|
||||
i18n (1.0.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_size (1.5.0)
|
||||
in_threads (1.4.0)
|
||||
jmespath (1.3.1)
|
||||
jquery-rails (4.3.1)
|
||||
in_threads (1.5.0)
|
||||
jaro_winkler (1.5.1)
|
||||
jmespath (1.4.0)
|
||||
jquery-rails (4.3.3)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jwt (1.5.6)
|
||||
kgio (2.11.1)
|
||||
kgio (2.11.2)
|
||||
kramdown (1.17.0)
|
||||
libv8 (6.7.288.46.1)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
lograge (0.7.1)
|
||||
actionpack (>= 4, < 5.2)
|
||||
activesupport (>= 4, < 5.2)
|
||||
railties (>= 4, < 5.2)
|
||||
lograge (0.10.0)
|
||||
actionpack (>= 4)
|
||||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
logstash-event (1.2.02)
|
||||
logstash-logger (0.25.1)
|
||||
logstash-logger (0.26.1)
|
||||
logstash-event (~> 1.2)
|
||||
logster (1.2.9)
|
||||
logster (1.2.11)
|
||||
loofah (2.2.2)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
@ -173,94 +200,100 @@ GEM
|
||||
mini_portile2 (2.3.0)
|
||||
mini_racer (0.2.0)
|
||||
libv8 (>= 6.3)
|
||||
mini_scheduler (0.8.1)
|
||||
mini_sql (0.1.10)
|
||||
mini_suffix (0.3.0)
|
||||
ffi (~> 1.9)
|
||||
minitest (5.10.3)
|
||||
mocha (1.2.1)
|
||||
minitest (5.11.3)
|
||||
mocha (1.5.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mock_redis (0.17.3)
|
||||
mock_redis (0.18.0)
|
||||
moneta (1.0.0)
|
||||
msgpack (1.2.4)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustache (1.0.5)
|
||||
nokogiri (1.8.2)
|
||||
nap (1.1.0)
|
||||
no_proxy_fix (0.1.2)
|
||||
nokogiri (1.8.3)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.5.0)
|
||||
nokogiri
|
||||
oauth (0.5.1)
|
||||
oauth2 (1.3.1)
|
||||
faraday (>= 0.8, < 0.12)
|
||||
oauth (0.5.4)
|
||||
oauth2 (1.4.0)
|
||||
faraday (>= 0.8, < 0.13)
|
||||
jwt (~> 1.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oj (3.4.0)
|
||||
omniauth (1.6.1)
|
||||
octokit (4.9.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
oj (3.6.2)
|
||||
omniauth (1.8.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-facebook (4.0.0)
|
||||
omniauth-facebook (5.0.0)
|
||||
omniauth-oauth2 (~> 1.2)
|
||||
omniauth-github (1.3.0)
|
||||
omniauth (~> 1.5)
|
||||
omniauth-oauth2 (>= 1.4.0, < 2.0)
|
||||
omniauth-google-oauth2 (0.3.1)
|
||||
jwt (~> 1.0)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-google-oauth2 (0.5.3)
|
||||
jwt (>= 1.5)
|
||||
omniauth (>= 1.1.1)
|
||||
omniauth-oauth2 (>= 1.3.1)
|
||||
omniauth-instagram (1.0.2)
|
||||
omniauth-oauth2 (>= 1.5)
|
||||
omniauth-instagram (1.3.0)
|
||||
omniauth (~> 1)
|
||||
omniauth-oauth2 (~> 1)
|
||||
omniauth-oauth (1.1.0)
|
||||
oauth
|
||||
omniauth (~> 1.0)
|
||||
omniauth-oauth2 (1.4.0)
|
||||
oauth2 (~> 1.0)
|
||||
omniauth-oauth2 (1.5.0)
|
||||
oauth2 (~> 1.1)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-openid (1.0.1)
|
||||
omniauth (~> 1.0)
|
||||
rack-openid (~> 1.3.1)
|
||||
omniauth-twitter (1.3.0)
|
||||
omniauth-twitter (1.4.0)
|
||||
omniauth-oauth (~> 1.1)
|
||||
rack
|
||||
onebox (1.8.48)
|
||||
onebox (1.8.60)
|
||||
htmlentities (~> 4.3)
|
||||
moneta (~> 1.0)
|
||||
multi_json (~> 1.11)
|
||||
mustache
|
||||
nokogiri (~> 1.7)
|
||||
sanitize
|
||||
open4 (1.3.4)
|
||||
openid-redis-store (0.0.2)
|
||||
redis
|
||||
ruby-openid
|
||||
parallel (1.12.1)
|
||||
parser (2.5.0.3)
|
||||
parser (2.5.1.0)
|
||||
ast (~> 2.4.0)
|
||||
pg (0.21.0)
|
||||
powerpack (0.1.1)
|
||||
progress (3.3.1)
|
||||
pg (1.1.0)
|
||||
powerpack (0.1.2)
|
||||
progress (3.4.0)
|
||||
pry (0.10.4)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
pry-nav (0.2.4)
|
||||
pry (>= 0.9.10, < 0.11.0)
|
||||
pry-rails (0.3.4)
|
||||
pry (>= 0.9.10)
|
||||
public_suffix (2.0.5)
|
||||
puma (3.9.1)
|
||||
r2 (0.2.6)
|
||||
pry-rails (0.3.6)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (3.0.2)
|
||||
puma (3.11.4)
|
||||
r2 (0.2.7)
|
||||
rack (2.0.5)
|
||||
rack-mini-profiler (1.0.0)
|
||||
rack (>= 1.2.0)
|
||||
rack-openid (1.3.1)
|
||||
rack (>= 1.1.0)
|
||||
ruby-openid (>= 2.1.8)
|
||||
rack-protection (2.0.1)
|
||||
rack-protection (2.0.3)
|
||||
rack
|
||||
rack-test (0.7.0)
|
||||
rack-test (1.0.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
@ -270,58 +303,61 @@ GEM
|
||||
rails_multisite (2.0.4)
|
||||
activerecord (> 4.2, < 6)
|
||||
railties (> 4.2, < 6)
|
||||
railties (5.1.4)
|
||||
actionpack (= 5.1.4)
|
||||
activesupport (= 5.1.4)
|
||||
railties (5.2.0)
|
||||
actionpack (= 5.2.0)
|
||||
activesupport (= 5.2.0)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.0)
|
||||
rake (12.3.0)
|
||||
rake (12.3.1)
|
||||
rake-compiler (1.0.4)
|
||||
rake
|
||||
rb-fsevent (0.9.8)
|
||||
rb-inotify (0.9.8)
|
||||
ffi (>= 0.5.0)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rbtrace (0.4.10)
|
||||
ffi (>= 1.0.6)
|
||||
msgpack (>= 0.4.3)
|
||||
trollop (>= 1.16.2)
|
||||
rchardet (1.8.0)
|
||||
redis (4.0.1)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
request_store (1.3.2)
|
||||
rinku (2.0.2)
|
||||
rotp (3.3.0)
|
||||
request_store (1.4.1)
|
||||
rack (>= 1.4)
|
||||
rinku (2.0.4)
|
||||
rotp (3.3.1)
|
||||
rqrcode (0.10.1)
|
||||
chunky_png (~> 1.0)
|
||||
rspec (3.6.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-core (3.7.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-html-matchers (0.9.1)
|
||||
nokogiri (~> 1)
|
||||
rspec (>= 3.0.0.a, < 4)
|
||||
rspec-mocks (3.6.0)
|
||||
rspec-mocks (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-rails (3.6.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rails (3.7.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-support (3.6.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-support (3.7.1)
|
||||
rtlit (0.0.5)
|
||||
rubocop (0.53.0)
|
||||
rubocop (0.57.2)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.5)
|
||||
powerpack (~> 0.1)
|
||||
@ -329,23 +365,30 @@ GEM
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
ruby-openid (2.7.0)
|
||||
ruby-prof (0.16.2)
|
||||
ruby-prof (0.17.0)
|
||||
ruby-progressbar (1.9.0)
|
||||
ruby-readability (0.7.0)
|
||||
guess_html_encoding (>= 0.0.4)
|
||||
nokogiri (>= 1.6.0)
|
||||
ruby_dep (1.5.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.6.4)
|
||||
sanitize (4.6.5)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4)
|
||||
sass (3.4.24)
|
||||
sassc (1.11.2)
|
||||
sass (3.5.6)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sassc (1.11.4)
|
||||
bundler
|
||||
ffi (~> 1.9.6)
|
||||
sass (>= 3.3.0)
|
||||
seed-fu (2.3.7)
|
||||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
seed-fu (2.3.9)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
shoulda (3.5.0)
|
||||
@ -363,29 +406,31 @@ GEM
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.0)
|
||||
sprockets-rails (3.2.1)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkey (1.9.0)
|
||||
stackprof (0.2.10)
|
||||
stackprof (0.2.11)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thor (0.19.4)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.7)
|
||||
tilt (2.0.8)
|
||||
trollop (2.1.2)
|
||||
tzinfo (1.2.3)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (3.2.0)
|
||||
uglifier (4.1.11)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.4)
|
||||
unicode-display_width (1.3.0)
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.0)
|
||||
unicorn (5.4.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
uniform_notifier (1.10.0)
|
||||
webmock (3.0.1)
|
||||
uniform_notifier (1.11.0)
|
||||
webmock (3.4.2)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff
|
||||
@ -397,13 +442,13 @@ PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
actionmailer (~> 5.1)
|
||||
actionpack (~> 5.1)
|
||||
actionview (~> 5.1)
|
||||
actionmailer (= 5.2)
|
||||
actionpack (= 5.2)
|
||||
actionview (= 5.2)
|
||||
active_model_serializers (~> 0.8.3)
|
||||
activemodel (~> 5.1)
|
||||
activerecord (~> 5.1)
|
||||
activesupport (~> 5.1)
|
||||
activemodel (= 5.2)
|
||||
activerecord (= 5.2)
|
||||
activesupport (= 5.2)
|
||||
annotate
|
||||
aws-sdk-s3
|
||||
barber
|
||||
@ -413,7 +458,9 @@ DEPENDENCIES
|
||||
bullet
|
||||
byebug
|
||||
certified
|
||||
colored2
|
||||
cppjieba_rb
|
||||
danger
|
||||
discourse_image_optim
|
||||
email_reply_trimmer (~> 0.1)
|
||||
ember-handlebars-template (= 0.7.5)
|
||||
@ -430,7 +477,7 @@ DEPENDENCIES
|
||||
flamegraph
|
||||
foreman
|
||||
gc_tracer
|
||||
highline
|
||||
highline (~> 1.7.0)
|
||||
hiredis
|
||||
htmlentities
|
||||
http_accept_language (~> 2.0.5)
|
||||
@ -445,6 +492,8 @@ DEPENDENCIES
|
||||
message_bus
|
||||
mini_mime
|
||||
mini_racer
|
||||
mini_scheduler
|
||||
mini_sql
|
||||
mini_suffix
|
||||
minitest
|
||||
mocha
|
||||
@ -461,9 +510,9 @@ DEPENDENCIES
|
||||
omniauth-oauth2
|
||||
omniauth-openid
|
||||
omniauth-twitter
|
||||
onebox (= 1.8.48)
|
||||
onebox (= 1.8.60)
|
||||
openid-redis-store
|
||||
pg (~> 0.21.0)
|
||||
pg
|
||||
pry-nav
|
||||
pry-rails
|
||||
puma
|
||||
@ -471,11 +520,12 @@ DEPENDENCIES
|
||||
rack-mini-profiler
|
||||
rack-protection
|
||||
rails_multisite
|
||||
railties (~> 5.1)
|
||||
railties (= 5.2)
|
||||
rake
|
||||
rb-fsevent
|
||||
rb-inotify (~> 0.9)
|
||||
rbtrace
|
||||
rchardet
|
||||
redis
|
||||
redis-namespace
|
||||
rinku
|
||||
@ -505,4 +555,4 @@ DEPENDENCIES
|
||||
webpush
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
||||
1.16.4
|
||||
|
||||
@ -26,7 +26,7 @@ Browse [lots more notable Discourse instances](https://www.discourse.org/custome
|
||||
|
||||
2. If you're familiar with how Rails works and are comfortable setting up your own environment, use our [**Discourse Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md).
|
||||
|
||||
Before you get started, ensure you have the following minimum versions: [Ruby 2.3+](http://www.ruby-lang.org/en/downloads/), [PostgreSQL 9.3+](http://www.postgresql.org/download/), [Redis 2.6+](http://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first!
|
||||
Before you get started, ensure you have the following minimum versions: [Ruby 2.4+](http://www.ruby-lang.org/en/downloads/), [PostgreSQL 9.3+](http://www.postgresql.org/download/), [Redis 2.6+](http://redis.io/download). If you're having trouble, please see our [**TROUBLESHOOTING GUIDE**](docs/TROUBLESHOOTING.md) first!
|
||||
|
||||
## Setting up Discourse
|
||||
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import RestAdapter from 'discourse/adapters/rest';
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default function buildPluginAdapter(pluginName) {
|
||||
return RestAdapter.extend({
|
||||
pathFor(store, type, findArgs) {
|
||||
return "/admin/plugins/" + pluginName + this._super(store, type, findArgs);
|
||||
return (
|
||||
"/admin/plugins/" + pluginName + this._super(store, type, findArgs)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import RestAdapter from 'discourse/adapters/rest';
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
basePath() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import RestAdapter from 'discourse/adapters/rest';
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
pathFor() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import RestAdapter from 'discourse/adapters/rest';
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
pathFor(store, type, findArgs) {
|
||||
@ -17,20 +17,20 @@ export default RestAdapter.extend({
|
||||
hasMore: pa.conversation.has_more,
|
||||
response: {
|
||||
excerpt: pa.conversation.response.excerpt,
|
||||
user: helper.lookup('user', pa.conversation.response.user_id)
|
||||
user: helper.lookup("user", pa.conversation.response.user_id)
|
||||
}
|
||||
};
|
||||
|
||||
if (pa.conversation.reply) {
|
||||
conversation.reply = {
|
||||
excerpt: pa.conversation.reply.excerpt,
|
||||
user: helper.lookup('user', pa.conversation.reply.user_id)
|
||||
user: helper.lookup("user", pa.conversation.reply.user_id)
|
||||
};
|
||||
}
|
||||
conversations.push(conversation);
|
||||
}
|
||||
});
|
||||
flag.set('conversations', conversations);
|
||||
flag.set("conversations", conversations);
|
||||
});
|
||||
|
||||
return results;
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
import CustomizationBase from 'admin/adapters/customization-base';
|
||||
import CustomizationBase from "admin/adapters/customization-base";
|
||||
export default CustomizationBase;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import RestAdapter from 'discourse/adapters/rest';
|
||||
import RestAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RestAdapter.extend({
|
||||
basePath() {
|
||||
@ -7,7 +7,9 @@ export default RestAdapter.extend({
|
||||
|
||||
afterFindAll(results) {
|
||||
let map = {};
|
||||
results.forEach(theme => {map[theme.id] = theme;});
|
||||
results.forEach(theme => {
|
||||
map[theme.id] = theme;
|
||||
});
|
||||
results.forEach(theme => {
|
||||
let mapped = theme.get("child_themes") || [];
|
||||
mapped = mapped.map(t => map[t.id]);
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
import CustomizationBase from 'admin/adapters/customization-base';
|
||||
import CustomizationBase from "admin/adapters/customization-base";
|
||||
export default CustomizationBase;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import RESTAdapter from 'discourse/adapters/rest';
|
||||
import RESTAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RESTAdapter.extend({
|
||||
basePath() {
|
||||
return '/admin/api/';
|
||||
return "/admin/api/";
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import RESTAdapter from 'discourse/adapters/rest';
|
||||
import RESTAdapter from "discourse/adapters/rest";
|
||||
|
||||
export default RESTAdapter.extend({
|
||||
basePath() {
|
||||
return '/admin/api/';
|
||||
return "/admin/api/";
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,37 +1,37 @@
|
||||
import loadScript from 'discourse/lib/load-script';
|
||||
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { observes } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const LOAD_ASYNC = !Ember.testing;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
mode: 'css',
|
||||
classNames: ['ace-wrapper'],
|
||||
mode: "css",
|
||||
classNames: ["ace-wrapper"],
|
||||
_editor: null,
|
||||
_skipContentChangeEvent: null,
|
||||
disabled: false,
|
||||
|
||||
@observes('editorId')
|
||||
@observes("editorId")
|
||||
editorIdChanged() {
|
||||
if (this.get('autofocus')) {
|
||||
this.send('focus');
|
||||
if (this.get("autofocus")) {
|
||||
this.send("focus");
|
||||
}
|
||||
},
|
||||
|
||||
@observes('content')
|
||||
@observes("content")
|
||||
contentChanged() {
|
||||
if (this._editor && !this._skipContentChangeEvent) {
|
||||
this._editor.getSession().setValue(this.get('content'));
|
||||
this._editor.getSession().setValue(this.get("content"));
|
||||
}
|
||||
},
|
||||
|
||||
@observes('mode')
|
||||
@observes("mode")
|
||||
modeChanged() {
|
||||
if (LOAD_ASYNC && this._editor && !this._skipContentChangeEvent) {
|
||||
this._editor.getSession().setMode("ace/mode/" + this.get('mode'));
|
||||
this._editor.getSession().setMode("ace/mode/" + this.get("mode"));
|
||||
}
|
||||
},
|
||||
|
||||
@observes('disabled')
|
||||
@observes("disabled")
|
||||
disabledStateChanged() {
|
||||
this.changeDisabledState();
|
||||
},
|
||||
@ -39,7 +39,7 @@ export default Ember.Component.extend({
|
||||
changeDisabledState() {
|
||||
const editor = this._editor;
|
||||
if (editor) {
|
||||
const disabled = this.get('disabled');
|
||||
const disabled = this.get("disabled");
|
||||
editor.setOptions({
|
||||
readOnly: disabled,
|
||||
highlightActiveLine: !disabled,
|
||||
@ -56,12 +56,11 @@ export default Ember.Component.extend({
|
||||
}
|
||||
if (this.appEvents) {
|
||||
// xxx: don't run during qunit tests
|
||||
this.appEvents.off('ace:resize', this, this.resize);
|
||||
this.appEvents.off("ace:resize", this, this.resize);
|
||||
}
|
||||
|
||||
$(window).off('ace:resize');
|
||||
|
||||
}.on('willDestroyElement'),
|
||||
$(window).off("ace:resize");
|
||||
}.on("willDestroyElement"),
|
||||
|
||||
resize() {
|
||||
if (this._editor) {
|
||||
@ -73,37 +72,41 @@ export default Ember.Component.extend({
|
||||
this._super();
|
||||
|
||||
loadScript("/javascripts/ace/ace.js", { scriptTag: true }).then(() => {
|
||||
window.ace.require(['ace/ace'], loadedAce => {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
|
||||
const editor = loadedAce.edit(this.$('.ace')[0]);
|
||||
window.ace.require(["ace/ace"], loadedAce => {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
const editor = loadedAce.edit(this.$(".ace")[0]);
|
||||
|
||||
if (LOAD_ASYNC) {
|
||||
editor.setTheme("ace/theme/chrome");
|
||||
}
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.setOptions({fontSize: "14px"});
|
||||
editor.setOptions({ fontSize: "14px" });
|
||||
if (LOAD_ASYNC) {
|
||||
editor.getSession().setMode("ace/mode/" + this.get('mode'));
|
||||
editor.getSession().setMode("ace/mode/" + this.get("mode"));
|
||||
}
|
||||
editor.on('change', () => {
|
||||
editor.on("change", () => {
|
||||
this._skipContentChangeEvent = true;
|
||||
this.set('content', editor.getSession().getValue());
|
||||
this.set("content", editor.getSession().getValue());
|
||||
this._skipContentChangeEvent = false;
|
||||
});
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.renderer.setScrollMargin(10,10);
|
||||
editor.renderer.setScrollMargin(10, 10);
|
||||
|
||||
this.$().data('editor', editor);
|
||||
this.$().data("editor", editor);
|
||||
this._editor = editor;
|
||||
this.changeDisabledState();
|
||||
|
||||
$(window).off('ace:resize').on('ace:resize', ()=>{
|
||||
this.appEvents.trigger('ace:resize');
|
||||
});
|
||||
$(window)
|
||||
.off("ace:resize")
|
||||
.on("ace:resize", () => {
|
||||
this.appEvents.trigger("ace:resize");
|
||||
});
|
||||
|
||||
if (this.appEvents) {
|
||||
// xxx: don't run during qunit tests
|
||||
this.appEvents.on('ace:resize', ()=>this.resize());
|
||||
this.appEvents.on("ace:resize", () => this.resize());
|
||||
}
|
||||
|
||||
if (this.get("autofocus")) {
|
||||
|
||||
@ -1,57 +1,64 @@
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import { renderSpinner } from 'discourse/helpers/loading-spinner';
|
||||
import { escapeExpression } from 'discourse/lib/utilities';
|
||||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import debounce from "discourse/lib/debounce";
|
||||
import { renderSpinner } from "discourse/helpers/loading-spinner";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
classNames: ["admin-backups-logs"],
|
||||
export default Ember.Component.extend(
|
||||
bufferedRender({
|
||||
classNames: ["admin-backups-logs"],
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
this._reset();
|
||||
},
|
||||
init() {
|
||||
this._super();
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_reset() {
|
||||
this.setProperties({ formattedLogs: "", index: 0 });
|
||||
},
|
||||
_reset() {
|
||||
this.setProperties({ formattedLogs: "", index: 0 });
|
||||
},
|
||||
|
||||
_scrollDown() {
|
||||
const $div = this.$()[0];
|
||||
$div.scrollTop = $div.scrollHeight;
|
||||
},
|
||||
_scrollDown() {
|
||||
const $div = this.$()[0];
|
||||
$div.scrollTop = $div.scrollHeight;
|
||||
},
|
||||
|
||||
_updateFormattedLogs: debounce(function() {
|
||||
const logs = this.get("logs");
|
||||
if (logs.length === 0) {
|
||||
this._reset(); // reset the cached logs whenever the model is reset
|
||||
} else {
|
||||
// do the log formatting only once for HELLish performance
|
||||
let formattedLogs = this.get("formattedLogs");
|
||||
for (let i = this.get("index"), length = logs.length; i < length; i++) {
|
||||
const date = logs[i].get("timestamp"),
|
||||
message = escapeExpression(logs[i].get("message"));
|
||||
formattedLogs += "[" + date + "] " + message + "\n";
|
||||
_updateFormattedLogs: debounce(function() {
|
||||
const logs = this.get("logs");
|
||||
if (logs.length === 0) {
|
||||
this._reset(); // reset the cached logs whenever the model is reset
|
||||
} else {
|
||||
// do the log formatting only once for HELLish performance
|
||||
let formattedLogs = this.get("formattedLogs");
|
||||
for (let i = this.get("index"), length = logs.length; i < length; i++) {
|
||||
const date = logs[i].get("timestamp"),
|
||||
message = escapeExpression(logs[i].get("message"));
|
||||
formattedLogs += "[" + date + "] " + message + "\n";
|
||||
}
|
||||
// update the formatted logs & cache index
|
||||
this.setProperties({
|
||||
formattedLogs: formattedLogs,
|
||||
index: logs.length
|
||||
});
|
||||
// force rerender
|
||||
this.rerenderBuffer();
|
||||
}
|
||||
// update the formatted logs & cache index
|
||||
this.setProperties({ formattedLogs: formattedLogs, index: logs.length });
|
||||
// force rerender
|
||||
this.rerenderBuffer();
|
||||
}
|
||||
Ember.run.scheduleOnce('afterRender', this, this._scrollDown);
|
||||
}, 150).observes("logs.[]").on('init'),
|
||||
Ember.run.scheduleOnce("afterRender", this, this._scrollDown);
|
||||
}, 150)
|
||||
.observes("logs.[]")
|
||||
.on("init"),
|
||||
|
||||
buildBuffer(buffer) {
|
||||
const formattedLogs = this.get("formattedLogs");
|
||||
if (formattedLogs && formattedLogs.length > 0) {
|
||||
buffer.push("<pre>");
|
||||
buffer.push(formattedLogs);
|
||||
buffer.push("</pre>");
|
||||
} else {
|
||||
buffer.push("<p>" + I18n.t("admin.backups.logs.none") + "</p>");
|
||||
buildBuffer(buffer) {
|
||||
const formattedLogs = this.get("formattedLogs");
|
||||
if (formattedLogs && formattedLogs.length > 0) {
|
||||
buffer.push("<pre>");
|
||||
buffer.push(formattedLogs);
|
||||
buffer.push("</pre>");
|
||||
} else {
|
||||
buffer.push("<p>" + I18n.t("admin.backups.logs.none") + "</p>");
|
||||
}
|
||||
// add a loading indicator
|
||||
if (this.get("status.isOperationRunning")) {
|
||||
buffer.push(renderSpinner("small"));
|
||||
}
|
||||
}
|
||||
// add a loading indicator
|
||||
if (this.get("status.isOperationRunning")) {
|
||||
buffer.push(renderSpinner('small'));
|
||||
}
|
||||
}
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
@ -1,33 +1,37 @@
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
tagName: 'th',
|
||||
classNames: ['sortable'],
|
||||
rerenderTriggers: ['order', 'ascending'],
|
||||
export default Ember.Component.extend(
|
||||
bufferedRender({
|
||||
tagName: "th",
|
||||
classNames: ["sortable"],
|
||||
rerenderTriggers: ["order", "ascending"],
|
||||
|
||||
buildBuffer(buffer) {
|
||||
const icon = this.get('icon');
|
||||
buildBuffer(buffer) {
|
||||
const icon = this.get("icon");
|
||||
|
||||
if (icon) {
|
||||
buffer.push(iconHTML(icon));
|
||||
if (icon) {
|
||||
buffer.push(iconHTML(icon));
|
||||
}
|
||||
|
||||
buffer.push(I18n.t(this.get("i18nKey")));
|
||||
|
||||
if (this.get("field") === this.get("order")) {
|
||||
buffer.push(
|
||||
iconHTML(this.get("ascending") ? "chevron-up" : "chevron-down")
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
click() {
|
||||
const currentOrder = this.get("order");
|
||||
const field = this.get("field");
|
||||
|
||||
if (currentOrder === field) {
|
||||
this.set("ascending", this.get("ascending") ? null : true);
|
||||
} else {
|
||||
this.setProperties({ order: field, ascending: null });
|
||||
}
|
||||
}
|
||||
|
||||
buffer.push(I18n.t(this.get('i18nKey')));
|
||||
|
||||
if (this.get('field') === this.get('order')) {
|
||||
buffer.push(iconHTML(this.get('ascending') ? 'chevron-up' : 'chevron-down'));
|
||||
}
|
||||
},
|
||||
|
||||
click() {
|
||||
const currentOrder = this.get('order');
|
||||
const field = this.get('field');
|
||||
|
||||
if (currentOrder === field) {
|
||||
this.set('ascending', this.get('ascending') ? null : true);
|
||||
} else {
|
||||
this.setProperties({ order: field, ascending: null });
|
||||
}
|
||||
}
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['row']
|
||||
classNames: ["row"]
|
||||
});
|
||||
|
||||
@ -1,49 +1,56 @@
|
||||
import loadScript from 'discourse/lib/load-script';
|
||||
import { number } from 'discourse/lib/formatter';
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { number } from "discourse/lib/formatter";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'canvas',
|
||||
refreshChart(){
|
||||
tagName: "canvas",
|
||||
refreshChart() {
|
||||
const ctx = this.$()[0].getContext("2d");
|
||||
const model = this.get("model");
|
||||
const rawData = this.get("model.data");
|
||||
|
||||
var data = {
|
||||
labels: rawData.map(r => r.x),
|
||||
datasets: [{
|
||||
data: rawData.map(r => r.y),
|
||||
label: model.get('title'),
|
||||
backgroundColor: "rgba(200,220,240,0.3)",
|
||||
borderColor: "#08C"
|
||||
}]
|
||||
datasets: [
|
||||
{
|
||||
data: rawData.map(r => r.y),
|
||||
label: model.get("title"),
|
||||
backgroundColor: "rgba(200,220,240,0.3)",
|
||||
borderColor: "#08C"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const config = {
|
||||
type: 'line',
|
||||
type: "line",
|
||||
data: data,
|
||||
options: {
|
||||
responsive: true,
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: (context) => moment(context[0].xLabel, "YYYY-MM-DD").format("LL")
|
||||
title: context =>
|
||||
moment(context[0].xLabel, "YYYY-MM-DD").format("LL")
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: true,
|
||||
ticks: {
|
||||
callback: (label) => number(label),
|
||||
suggestedMin: 0
|
||||
yAxes: [
|
||||
{
|
||||
display: true,
|
||||
ticks: {
|
||||
callback: label => number(label),
|
||||
suggestedMin: 0
|
||||
}
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
this._chart = new window.Chart(ctx, config);
|
||||
},
|
||||
|
||||
didInsertElement(){
|
||||
loadScript("/javascripts/Chart.min.js").then(() => this.refreshChart.apply(this));
|
||||
didInsertElement() {
|
||||
loadScript("/javascripts/Chart.min.js").then(() =>
|
||||
this.refreshChart.apply(this)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: ''
|
||||
tagName: ""
|
||||
});
|
||||
|
||||
@ -0,0 +1,116 @@
|
||||
import { number } from "discourse/lib/formatter";
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["admin-report-chart"],
|
||||
limit: 8,
|
||||
total: 0,
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._resetChart();
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
const $chartCanvas = this.$(".chart-canvas");
|
||||
if (!$chartCanvas || !$chartCanvas.length) return;
|
||||
|
||||
const context = $chartCanvas[0].getContext("2d");
|
||||
const model = this.get("model");
|
||||
const chartData = Ember.makeArray(
|
||||
model.get("chartData") || model.get("data")
|
||||
);
|
||||
const prevChartData = Ember.makeArray(
|
||||
model.get("prevChartData") || model.get("prev_data")
|
||||
);
|
||||
|
||||
const labels = chartData.map(d => d.x);
|
||||
|
||||
const data = {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
data: chartData.map(d => Math.round(parseFloat(d.y))),
|
||||
backgroundColor: prevChartData.length
|
||||
? "transparent"
|
||||
: model.secondary_color,
|
||||
borderColor: model.primary_color
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (prevChartData.length) {
|
||||
data.datasets.push({
|
||||
data: prevChartData.map(d => Math.round(parseFloat(d.y))),
|
||||
borderColor: model.primary_color,
|
||||
borderDash: [5, 5],
|
||||
backgroundColor: "transparent",
|
||||
borderWidth: 1,
|
||||
pointRadius: 0
|
||||
});
|
||||
}
|
||||
|
||||
loadScript("/javascripts/Chart.min.js").then(() => {
|
||||
this._resetChart();
|
||||
this._chart = new window.Chart(context, this._buildChartConfig(data));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_buildChartConfig(data) {
|
||||
return {
|
||||
type: "line",
|
||||
data,
|
||||
options: {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: tooltipItem =>
|
||||
moment(tooltipItem[0].xLabel, "YYYY-MM-DD").format("LL")
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: {
|
||||
padding: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
yAxes: [
|
||||
{
|
||||
display: true,
|
||||
ticks: { callback: label => number(label) }
|
||||
}
|
||||
],
|
||||
xAxes: [
|
||||
{
|
||||
display: true,
|
||||
gridLines: { display: false },
|
||||
type: "time",
|
||||
time: {
|
||||
parser: "YYYY-MM-DD"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_resetChart() {
|
||||
if (this._chart) {
|
||||
this._chart.destroy();
|
||||
this._chart = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["admin-report-counters"]
|
||||
});
|
||||
@ -1,6 +1,9 @@
|
||||
export default Ember.Component.extend({
|
||||
allTime: true,
|
||||
tagName: 'tr',
|
||||
reverseColors: Ember.computed.match('report.type', /^(time_to_first_response|topics_with_no_response)$/),
|
||||
classNameBindings: ['reverseColors']
|
||||
tagName: "tr",
|
||||
reverseColors: Ember.computed.match(
|
||||
"report.type",
|
||||
/^(time_to_first_response|topics_with_no_response)$/
|
||||
),
|
||||
classNameBindings: ["reverseColors"]
|
||||
});
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["admin-report-inline-table"]
|
||||
});
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr'
|
||||
tagName: "tr"
|
||||
});
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: "td",
|
||||
classNames: ["admin-report-table-cell"],
|
||||
classNameBindings: ["type", "property"],
|
||||
options: null,
|
||||
|
||||
@computed("label", "data", "options")
|
||||
computedLabel(label, data, options) {
|
||||
return label.compute(data, options || {});
|
||||
},
|
||||
|
||||
type: Ember.computed.alias("label.type"),
|
||||
property: Ember.computed.alias("label.mainProperty"),
|
||||
formatedValue: Ember.computed.alias("computedLabel.formatedValue"),
|
||||
value: Ember.computed.alias("computedLabel.value")
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: "th",
|
||||
classNames: ["admin-report-table-header"],
|
||||
classNameBindings: ["label.mainProperty", "label.type", "isCurrentSort"],
|
||||
attributeBindings: ["label.title:title"],
|
||||
|
||||
@computed("currentSortLabel.sortProperty", "label.sortProperty")
|
||||
isCurrentSort(currentSortField, labelSortField) {
|
||||
return currentSortField === labelSortField;
|
||||
},
|
||||
|
||||
@computed("currentSortDirection")
|
||||
sortIcon(currentSortDirection) {
|
||||
return currentSortDirection === 1 ? "caret-up" : "caret-down";
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,5 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: "tr",
|
||||
classNames: ["admin-report-table-row"],
|
||||
options: null
|
||||
});
|
||||
@ -0,0 +1,139 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const PAGES_LIMIT = 8;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: ["sortable", "twoColumns"],
|
||||
classNames: ["admin-report-table"],
|
||||
sortable: false,
|
||||
sortDirection: 1,
|
||||
perPage: Ember.computed.alias("options.perPage"),
|
||||
page: 0,
|
||||
|
||||
@computed("model.computedLabels.length")
|
||||
twoColumns(labelsLength) {
|
||||
return labelsLength === 2;
|
||||
},
|
||||
|
||||
@computed("totalsForSample", "options.total", "model.dates_filtering")
|
||||
showTotalForSample(totalsForSample, total, datesFiltering) {
|
||||
// check if we have at least one cell which contains a value
|
||||
const sum = totalsForSample
|
||||
.map(t => t.value)
|
||||
.compact()
|
||||
.reduce((s, v) => s + v, 0);
|
||||
|
||||
return sum >= 1 && total && datesFiltering;
|
||||
},
|
||||
|
||||
@computed("model.total", "options.total", "twoColumns")
|
||||
showTotal(reportTotal, total, twoColumns) {
|
||||
return reportTotal && total && twoColumns;
|
||||
},
|
||||
|
||||
@computed("model.data.length")
|
||||
showSortingUI(dataLength) {
|
||||
return dataLength >= 5;
|
||||
},
|
||||
|
||||
@computed("totalsForSampleRow", "model.computedLabels")
|
||||
totalsForSample(row, labels) {
|
||||
return labels.map(label => {
|
||||
const computedLabel = label.compute(row);
|
||||
computedLabel.type = label.type;
|
||||
computedLabel.property = label.mainProperty;
|
||||
return computedLabel;
|
||||
});
|
||||
},
|
||||
|
||||
@computed("model.data", "model.computedLabels")
|
||||
totalsForSampleRow(rows, labels) {
|
||||
if (!rows || !rows.length) return {};
|
||||
|
||||
let totalsRow = {};
|
||||
|
||||
labels.forEach(label => {
|
||||
const reducer = (sum, row) => {
|
||||
const computedLabel = label.compute(row);
|
||||
const value = computedLabel.value;
|
||||
|
||||
if (!["seconds", "number", "percent"].includes(label.type)) {
|
||||
return;
|
||||
} else {
|
||||
return sum + Math.round(value || 0);
|
||||
}
|
||||
};
|
||||
|
||||
const total = rows.reduce(reducer, 0);
|
||||
totalsRow[label.mainProperty] =
|
||||
label.type === "percent" ? Math.round(total / rows.length) : total;
|
||||
});
|
||||
|
||||
return totalsRow;
|
||||
},
|
||||
|
||||
@computed("sortLabel", "sortDirection", "model.data.[]")
|
||||
sortedData(sortLabel, sortDirection, data) {
|
||||
data = Ember.makeArray(data);
|
||||
|
||||
if (sortLabel) {
|
||||
const compare = (label, direction) => {
|
||||
return (a, b) => {
|
||||
let aValue = label.compute(a).value;
|
||||
let bValue = label.compute(b).value;
|
||||
const result = aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
|
||||
return result * direction;
|
||||
};
|
||||
};
|
||||
|
||||
return data.sort(compare(sortLabel, sortDirection));
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@computed("sortedData.[]", "perPage", "page")
|
||||
paginatedData(data, perPage, page) {
|
||||
if (perPage < data.length) {
|
||||
const start = perPage * page;
|
||||
return data.slice(start, start + perPage);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
@computed("model.data", "perPage", "page")
|
||||
pages(data, perPage, page) {
|
||||
if (!data || data.length <= perPage) return [];
|
||||
|
||||
let pages = [...Array(Math.ceil(data.length / perPage)).keys()].map(v => {
|
||||
return {
|
||||
page: v + 1,
|
||||
index: v,
|
||||
class: v === page ? "is-current" : null
|
||||
};
|
||||
});
|
||||
|
||||
if (pages.length > PAGES_LIMIT) {
|
||||
const before = Math.max(0, page - PAGES_LIMIT / 2);
|
||||
const after = Math.max(PAGES_LIMIT, page + PAGES_LIMIT / 2);
|
||||
pages = pages.slice(before, after);
|
||||
}
|
||||
|
||||
return pages;
|
||||
},
|
||||
|
||||
actions: {
|
||||
changePage(page) {
|
||||
this.set("page", page);
|
||||
},
|
||||
|
||||
sortByLabel(label) {
|
||||
if (this.get("sortLabel") === label) {
|
||||
this.set("sortDirection", this.get("sortDirection") === 1 ? -1 : 1);
|
||||
} else {
|
||||
this.set("sortLabel", label);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr'
|
||||
tagName: "tr"
|
||||
});
|
||||
|
||||
408
app/assets/javascripts/admin/components/admin-report.js.es6
Normal file
408
app/assets/javascripts/admin/components/admin-report.js.es6
Normal file
@ -0,0 +1,408 @@
|
||||
import ReportLoader from "discourse/lib/reports-loader";
|
||||
import Category from "discourse/models/category";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { SCHEMA_VERSION, default as Report } from "admin/models/report";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
registerHoverTooltip,
|
||||
unregisterHoverTooltip
|
||||
} from "discourse/lib/tooltip";
|
||||
|
||||
const TABLE_OPTIONS = {
|
||||
perPage: 8,
|
||||
total: true,
|
||||
limit: 20,
|
||||
formatNumbers: true
|
||||
};
|
||||
|
||||
const CHART_OPTIONS = {};
|
||||
|
||||
function collapseWeekly(data, average) {
|
||||
let aggregate = [];
|
||||
let bucket, i;
|
||||
let offset = data.length % 7;
|
||||
for (i = offset; i < data.length; i++) {
|
||||
if (bucket && i % 7 === offset) {
|
||||
if (average) {
|
||||
bucket.y = parseFloat((bucket.y / 7.0).toFixed(2));
|
||||
}
|
||||
aggregate.push(bucket);
|
||||
bucket = null;
|
||||
}
|
||||
|
||||
bucket = bucket || { x: data[i].x, y: 0 };
|
||||
bucket.y += data[i].y;
|
||||
}
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: ["isEnabled", "isLoading", "dasherizedDataSourceName"],
|
||||
classNames: ["admin-report"],
|
||||
isEnabled: true,
|
||||
disabledLabel: "admin.dashboard.disabled",
|
||||
isLoading: false,
|
||||
rateLimitationString: null,
|
||||
dataSourceName: null,
|
||||
report: null,
|
||||
model: null,
|
||||
reportOptions: null,
|
||||
forcedModes: null,
|
||||
showAllReportsLink: false,
|
||||
filters: null,
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
category: null,
|
||||
groupId: null,
|
||||
showTrend: false,
|
||||
showHeader: true,
|
||||
showTitle: true,
|
||||
showFilteringUI: false,
|
||||
showCategoryOptions: Ember.computed.alias("model.category_filtering"),
|
||||
showDatesOptions: Ember.computed.alias("model.dates_filtering"),
|
||||
showGroupOptions: Ember.computed.alias("model.group_filtering"),
|
||||
showExport: Ember.computed.not("model.onlyTable"),
|
||||
showRefresh: Ember.computed.or(
|
||||
"showCategoryOptions",
|
||||
"showDatesOptions",
|
||||
"showGroupOptions"
|
||||
),
|
||||
shouldDisplayTrend: Ember.computed.and("showTrend", "model.prev_period"),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._reports = [];
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
const state = this.get("filters") || {};
|
||||
|
||||
this.setProperties({
|
||||
category: Category.findById(state.categoryId),
|
||||
groupId: state.groupId,
|
||||
startDate: state.startDate,
|
||||
endDate: state.endDate
|
||||
});
|
||||
|
||||
if (this.get("report")) {
|
||||
this._renderReport(
|
||||
this.get("report"),
|
||||
this.get("forcedModes"),
|
||||
this.get("currentMode")
|
||||
);
|
||||
} else if (this.get("dataSourceName")) {
|
||||
this._fetchReport();
|
||||
}
|
||||
},
|
||||
|
||||
didRender() {
|
||||
this._super(...arguments);
|
||||
|
||||
registerHoverTooltip($(".info[data-tooltip]"));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
unregisterHoverTooltip($(".info[data-tooltip]"));
|
||||
},
|
||||
|
||||
showError: Ember.computed.or("showTimeoutError", "showExceptionError"),
|
||||
showTimeoutError: Ember.computed.equal("model.error", "timeout"),
|
||||
showExceptionError: Ember.computed.equal("model.error", "exception"),
|
||||
|
||||
hasData: Ember.computed.notEmpty("model.data"),
|
||||
|
||||
@computed("dataSourceName", "model.type")
|
||||
dasherizedDataSourceName(dataSourceName, type) {
|
||||
return (dataSourceName || type || "undefined").replace(/_/g, "-");
|
||||
},
|
||||
|
||||
@computed("dataSourceName", "model.type")
|
||||
dataSource(dataSourceName, type) {
|
||||
dataSourceName = dataSourceName || type;
|
||||
return `/admin/reports/${dataSourceName}`;
|
||||
},
|
||||
|
||||
@computed("displayedModes.length")
|
||||
showModes(displayedModesLength) {
|
||||
return displayedModesLength > 1;
|
||||
},
|
||||
|
||||
categoryId: Ember.computed.alias("category.id"),
|
||||
|
||||
@computed("currentMode", "model.modes", "forcedModes")
|
||||
displayedModes(currentMode, reportModes, forcedModes) {
|
||||
const modes = forcedModes ? forcedModes.split(",") : reportModes;
|
||||
|
||||
return Ember.makeArray(modes).map(mode => {
|
||||
const base = `mode-btn ${mode}`;
|
||||
const cssClass = currentMode === mode ? `${base} is-current` : base;
|
||||
|
||||
return {
|
||||
mode,
|
||||
cssClass,
|
||||
icon: mode === "table" ? "table" : "signal"
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@computed()
|
||||
groupOptions() {
|
||||
const arr = [
|
||||
{ name: I18n.t("admin.dashboard.reports.groups"), value: "all" }
|
||||
];
|
||||
return arr.concat(
|
||||
(this.site.groups || []).map(i => {
|
||||
return { name: i["name"], value: i["id"] };
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@computed("currentMode")
|
||||
modeComponent(currentMode) {
|
||||
return `admin-report-${currentMode}`;
|
||||
},
|
||||
|
||||
@computed("startDate")
|
||||
normalizedStartDate(startDate) {
|
||||
return startDate && typeof startDate.isValid === "function"
|
||||
? moment
|
||||
.utc(startDate.toISOString())
|
||||
.locale("en")
|
||||
.format("YYYYMMDD")
|
||||
: moment(startDate)
|
||||
.locale("en")
|
||||
.format("YYYYMMDD");
|
||||
},
|
||||
|
||||
@computed("endDate")
|
||||
normalizedEndDate(endDate) {
|
||||
return endDate && typeof endDate.isValid === "function"
|
||||
? moment
|
||||
.utc(endDate.toISOString())
|
||||
.locale("en")
|
||||
.format("YYYYMMDD")
|
||||
: moment(endDate)
|
||||
.locale("en")
|
||||
.format("YYYYMMDD");
|
||||
},
|
||||
|
||||
@computed(
|
||||
"dataSourceName",
|
||||
"categoryId",
|
||||
"groupId",
|
||||
"normalizedStartDate",
|
||||
"normalizedEndDate"
|
||||
)
|
||||
reportKey(dataSourceName, categoryId, groupId, startDate, endDate) {
|
||||
if (!dataSourceName || !startDate || !endDate) return null;
|
||||
|
||||
let reportKey = "reports:";
|
||||
reportKey += [
|
||||
dataSourceName,
|
||||
categoryId,
|
||||
startDate.replace(/-/g, ""),
|
||||
endDate.replace(/-/g, ""),
|
||||
groupId,
|
||||
"[:prev_period]",
|
||||
this.get("reportOptions.table.limit"),
|
||||
SCHEMA_VERSION
|
||||
]
|
||||
.filter(x => x)
|
||||
.map(x => x.toString())
|
||||
.join(":");
|
||||
|
||||
return reportKey;
|
||||
},
|
||||
|
||||
actions: {
|
||||
refreshReport() {
|
||||
this.attrs.onRefresh({
|
||||
categoryId: this.get("categoryId"),
|
||||
groupId: this.get("groupId"),
|
||||
startDate: this.get("startDate"),
|
||||
endDate: this.get("endDate")
|
||||
});
|
||||
},
|
||||
|
||||
exportCsv() {
|
||||
exportEntity("report", {
|
||||
name: this.get("model.type"),
|
||||
start_date: this.get("startDate"),
|
||||
end_date: this.get("endDate"),
|
||||
category_id:
|
||||
this.get("categoryId") === "all" ? undefined : this.get("categoryId"),
|
||||
group_id:
|
||||
this.get("groupId") === "all" ? undefined : this.get("groupId")
|
||||
}).then(outputExportResult);
|
||||
},
|
||||
|
||||
changeMode(mode) {
|
||||
this.set("currentMode", mode);
|
||||
}
|
||||
},
|
||||
|
||||
_computeReport() {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._reports || !this._reports.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// on a slow network _fetchReport could be called multiple times between
|
||||
// T and T+x, and all the ajax responses would occur after T+(x+y)
|
||||
// to avoid any inconsistencies we filter by period and make sure
|
||||
// the array contains only unique values
|
||||
let filteredReports = this._reports.uniqBy("report_key");
|
||||
let report;
|
||||
|
||||
const sort = r => {
|
||||
if (r.length > 1) {
|
||||
return r.findBy("type", this.get("dataSourceName"));
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
if (!this.get("startDate") || !this.get("endDate")) {
|
||||
report = sort(filteredReports)[0];
|
||||
} else {
|
||||
let reportKey = this.get("reportKey");
|
||||
|
||||
report = sort(
|
||||
filteredReports.filter(r => r.report_key.includes(reportKey))
|
||||
)[0];
|
||||
|
||||
if (!report) return;
|
||||
}
|
||||
|
||||
this._renderReport(
|
||||
report,
|
||||
this.get("forcedModes"),
|
||||
this.get("currentMode")
|
||||
);
|
||||
},
|
||||
|
||||
_renderReport(report, forcedModes, currentMode) {
|
||||
const modes = forcedModes ? forcedModes.split(",") : report.modes;
|
||||
currentMode = currentMode || (modes ? modes[0] : null);
|
||||
|
||||
this.setProperties({
|
||||
model: report,
|
||||
currentMode,
|
||||
options: this._buildOptions(currentMode)
|
||||
});
|
||||
},
|
||||
|
||||
_fetchReport() {
|
||||
this._super();
|
||||
|
||||
this.setProperties({ isLoading: true, rateLimitationString: null });
|
||||
|
||||
Ember.run.next(() => {
|
||||
let payload = this._buildPayload(["prev_period"]);
|
||||
|
||||
const callback = response => {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("isLoading", false);
|
||||
|
||||
if (response === 429) {
|
||||
this.set(
|
||||
"rateLimitationString",
|
||||
I18n.t("admin.dashboard.too_many_requests")
|
||||
);
|
||||
} else if (response === 500) {
|
||||
this.set("model.error", "exception");
|
||||
} else if (response) {
|
||||
this._reports.push(this._loadReport(response));
|
||||
this._computeReport();
|
||||
}
|
||||
};
|
||||
|
||||
ReportLoader.enqueue(this.get("dataSourceName"), payload.data, callback);
|
||||
});
|
||||
},
|
||||
|
||||
_buildPayload(facets) {
|
||||
let payload = { data: { cache: true, facets } };
|
||||
|
||||
if (this.get("startDate")) {
|
||||
payload.data.start_date = moment
|
||||
.utc(this.get("startDate"), "YYYY-MM-DD")
|
||||
.toISOString();
|
||||
}
|
||||
|
||||
if (this.get("endDate")) {
|
||||
payload.data.end_date = moment
|
||||
.utc(this.get("endDate"), "YYYY-MM-DD")
|
||||
.toISOString();
|
||||
}
|
||||
|
||||
if (this.get("groupId") && this.get("groupId") !== "all") {
|
||||
payload.data.group_id = this.get("groupId");
|
||||
}
|
||||
|
||||
if (this.get("categoryId") && this.get("categoryId") !== "all") {
|
||||
payload.data.category_id = this.get("categoryId");
|
||||
}
|
||||
|
||||
if (this.get("reportOptions.table.limit")) {
|
||||
payload.data.limit = this.get("reportOptions.table.limit");
|
||||
}
|
||||
|
||||
return payload;
|
||||
},
|
||||
|
||||
_buildOptions(mode) {
|
||||
if (mode === "table") {
|
||||
const tableOptions = JSON.parse(JSON.stringify(TABLE_OPTIONS));
|
||||
return Ember.Object.create(
|
||||
Object.assign(tableOptions, this.get("reportOptions.table") || {})
|
||||
);
|
||||
} else {
|
||||
const chartOptions = JSON.parse(JSON.stringify(CHART_OPTIONS));
|
||||
return Ember.Object.create(
|
||||
Object.assign(chartOptions, this.get("reportOptions.chart") || {})
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_loadReport(jsonReport) {
|
||||
Report.fillMissingDates(jsonReport, { filledField: "chartData" });
|
||||
|
||||
if (jsonReport.chartData && jsonReport.chartData.length > 40) {
|
||||
jsonReport.chartData = collapseWeekly(
|
||||
jsonReport.chartData,
|
||||
jsonReport.average
|
||||
);
|
||||
}
|
||||
|
||||
if (jsonReport.prev_data) {
|
||||
Report.fillMissingDates(jsonReport, {
|
||||
filledField: "prevChartData",
|
||||
dataField: "prev_data",
|
||||
starDate: jsonReport.prev_start_date,
|
||||
endDate: jsonReport.prev_end_date
|
||||
});
|
||||
|
||||
if (jsonReport.prevChartData && jsonReport.prevChartData.length > 40) {
|
||||
jsonReport.prevChartData = collapseWeekly(
|
||||
jsonReport.prevChartData,
|
||||
jsonReport.average
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Report.create(jsonReport);
|
||||
}
|
||||
});
|
||||
@ -1,97 +1,111 @@
|
||||
import UserField from 'admin/models/user-field';
|
||||
import { bufferedProperty } from 'discourse/mixins/buffered-content';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { propertyEqual } from 'discourse/lib/computed';
|
||||
import UserField from "admin/models/user-field";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
|
||||
export default Ember.Component.extend(bufferedProperty('userField'), {
|
||||
editing: Ember.computed.empty('userField.id'),
|
||||
classNameBindings: [':user-field'],
|
||||
export default Ember.Component.extend(bufferedProperty("userField"), {
|
||||
editing: Ember.computed.empty("userField.id"),
|
||||
classNameBindings: [":user-field"],
|
||||
|
||||
cantMoveUp: propertyEqual('userField', 'firstField'),
|
||||
cantMoveDown: propertyEqual('userField', 'lastField'),
|
||||
cantMoveUp: propertyEqual("userField", "firstField"),
|
||||
cantMoveDown: propertyEqual("userField", "lastField"),
|
||||
|
||||
userFieldsDescription: function() {
|
||||
return I18n.t('admin.user_fields.description');
|
||||
return I18n.t("admin.user_fields.description");
|
||||
}.property(),
|
||||
|
||||
bufferedFieldType: function() {
|
||||
return UserField.fieldTypeById(this.get('buffered.field_type'));
|
||||
}.property('buffered.field_type'),
|
||||
return UserField.fieldTypeById(this.get("buffered.field_type"));
|
||||
}.property("buffered.field_type"),
|
||||
|
||||
_focusOnEdit: function() {
|
||||
if (this.get('editing')) {
|
||||
Ember.run.scheduleOnce('afterRender', this, '_focusName');
|
||||
if (this.get("editing")) {
|
||||
Ember.run.scheduleOnce("afterRender", this, "_focusName");
|
||||
}
|
||||
}.observes('editing').on('didInsertElement'),
|
||||
}
|
||||
.observes("editing")
|
||||
.on("didInsertElement"),
|
||||
|
||||
_focusName: function() {
|
||||
$('.user-field-name').select();
|
||||
$(".user-field-name").select();
|
||||
},
|
||||
|
||||
fieldName: function() {
|
||||
return UserField.fieldTypeById(this.get('userField.field_type')).get('name');
|
||||
}.property('userField.field_type'),
|
||||
return UserField.fieldTypeById(this.get("userField.field_type")).get(
|
||||
"name"
|
||||
);
|
||||
}.property("userField.field_type"),
|
||||
|
||||
flags: function() {
|
||||
const ret = [];
|
||||
if (this.get('userField.editable')) {
|
||||
ret.push(I18n.t('admin.user_fields.editable.enabled'));
|
||||
if (this.get("userField.editable")) {
|
||||
ret.push(I18n.t("admin.user_fields.editable.enabled"));
|
||||
}
|
||||
if (this.get('userField.required')) {
|
||||
ret.push(I18n.t('admin.user_fields.required.enabled'));
|
||||
if (this.get("userField.required")) {
|
||||
ret.push(I18n.t("admin.user_fields.required.enabled"));
|
||||
}
|
||||
if (this.get('userField.show_on_profile')) {
|
||||
ret.push(I18n.t('admin.user_fields.show_on_profile.enabled'));
|
||||
if (this.get("userField.show_on_profile")) {
|
||||
ret.push(I18n.t("admin.user_fields.show_on_profile.enabled"));
|
||||
}
|
||||
if (this.get('userField.show_on_user_card')) {
|
||||
ret.push(I18n.t('admin.user_fields.show_on_user_card.enabled'));
|
||||
if (this.get("userField.show_on_user_card")) {
|
||||
ret.push(I18n.t("admin.user_fields.show_on_user_card.enabled"));
|
||||
}
|
||||
|
||||
return ret.join(', ');
|
||||
}.property('userField.editable', 'userField.required', 'userField.show_on_profile', 'userField.show_on_user_card'),
|
||||
return ret.join(", ");
|
||||
}.property(
|
||||
"userField.editable",
|
||||
"userField.required",
|
||||
"userField.show_on_profile",
|
||||
"userField.show_on_user_card"
|
||||
),
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
const self = this;
|
||||
const buffered = this.get('buffered');
|
||||
const attrs = buffered.getProperties('name',
|
||||
'description',
|
||||
'field_type',
|
||||
'editable',
|
||||
'required',
|
||||
'show_on_profile',
|
||||
'show_on_user_card',
|
||||
'options');
|
||||
const buffered = this.get("buffered");
|
||||
const attrs = buffered.getProperties(
|
||||
"name",
|
||||
"description",
|
||||
"field_type",
|
||||
"editable",
|
||||
"required",
|
||||
"show_on_profile",
|
||||
"show_on_user_card",
|
||||
"options"
|
||||
);
|
||||
|
||||
this.get('userField').save(attrs).then(function() {
|
||||
self.set('editing', false);
|
||||
self.commitBuffer();
|
||||
}).catch(popupAjaxError);
|
||||
this.get("userField")
|
||||
.save(attrs)
|
||||
.then(function() {
|
||||
self.set("editing", false);
|
||||
self.commitBuffer();
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
moveUp() {
|
||||
this.sendAction('moveUpAction', this.get('userField'));
|
||||
this.sendAction("moveUpAction", this.get("userField"));
|
||||
},
|
||||
|
||||
moveDown() {
|
||||
this.sendAction('moveDownAction', this.get('userField'));
|
||||
this.sendAction("moveDownAction", this.get("userField"));
|
||||
},
|
||||
|
||||
edit() {
|
||||
this.set('editing', true);
|
||||
this.set("editing", true);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this.sendAction('destroyAction', this.get('userField'));
|
||||
this.sendAction("destroyAction", this.get("userField"));
|
||||
},
|
||||
|
||||
cancel() {
|
||||
const id = this.get('userField.id');
|
||||
const id = this.get("userField.id");
|
||||
if (Ember.isEmpty(id)) {
|
||||
this.sendAction('destroyAction', this.get('userField'));
|
||||
this.sendAction("destroyAction", this.get("userField"));
|
||||
} else {
|
||||
this.rollbackBuffer();
|
||||
this.set('editing', false);
|
||||
this.set("editing", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
classNames: ['watched-word'],
|
||||
export default Ember.Component.extend(
|
||||
bufferedRender({
|
||||
classNames: ["watched-word"],
|
||||
|
||||
buildBuffer(buffer) {
|
||||
buffer.push(iconHTML('times'));
|
||||
buffer.push(' ' + this.get('word.word'));
|
||||
},
|
||||
buildBuffer(buffer) {
|
||||
buffer.push(iconHTML("times"));
|
||||
buffer.push(" " + this.get("word.word"));
|
||||
},
|
||||
|
||||
click() {
|
||||
this.get('word').destroy().then(() => {
|
||||
this.sendAction('action', this.get('word'));
|
||||
}).catch(e => {
|
||||
bootbox.alert(I18n.t("generic_error_with_reason", {error: `http: ${e.status} - ${e.body}`}));
|
||||
});;
|
||||
}
|
||||
}));
|
||||
click() {
|
||||
this.get("word")
|
||||
.destroy()
|
||||
.then(() => {
|
||||
this.sendAction("action", this.get("word"));
|
||||
})
|
||||
.catch(e => {
|
||||
bootbox.alert(
|
||||
I18n.t("generic_error_with_reason", {
|
||||
error: `http: ${e.status} - ${e.body}`
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@ -1,38 +1,40 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['hook-event'],
|
||||
typeName: Ember.computed.alias('type.name'),
|
||||
classNames: ["hook-event"],
|
||||
typeName: Ember.computed.alias("type.name"),
|
||||
|
||||
@computed('typeName')
|
||||
@computed("typeName")
|
||||
name(typeName) {
|
||||
return I18n.t(`admin.web_hooks.${typeName}_event.name`);
|
||||
},
|
||||
|
||||
@computed('typeName')
|
||||
@computed("typeName")
|
||||
details(typeName) {
|
||||
return I18n.t(`admin.web_hooks.${typeName}_event.details`);
|
||||
},
|
||||
|
||||
@computed('model.[]', 'typeName')
|
||||
@computed("model.[]", "typeName")
|
||||
eventTypeExists(eventTypes, typeName) {
|
||||
return eventTypes.any(event => event.name === typeName);
|
||||
},
|
||||
|
||||
@computed('eventTypeExists')
|
||||
@computed("eventTypeExists")
|
||||
enabled: {
|
||||
get(eventTypeExists) {
|
||||
return eventTypeExists;
|
||||
},
|
||||
set(value, eventTypeExists) {
|
||||
const type = this.get('type');
|
||||
const model = this.get('model');
|
||||
const type = this.get("type");
|
||||
const model = this.get("model");
|
||||
// add an association when not exists
|
||||
if (value !== eventTypeExists) {
|
||||
if (value) {
|
||||
model.addObject(type);
|
||||
} else {
|
||||
model.removeObjects(model.filter(eventType => eventType.name === type.name));
|
||||
model.removeObjects(
|
||||
model.filter(eventType => eventType.name === type.name)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,78 +1,92 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { ensureJSON, plainJSON, prettyJSON } from 'discourse/lib/formatter';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { ensureJSON, plainJSON, prettyJSON } from "discourse/lib/formatter";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'li',
|
||||
tagName: "li",
|
||||
expandDetails: null,
|
||||
|
||||
@computed('model.status')
|
||||
@computed("model.status")
|
||||
statusColorClasses(status) {
|
||||
if (!status) return '';
|
||||
if (!status) return "";
|
||||
|
||||
if (status >= 200 && status <= 299) {
|
||||
return 'text-successful';
|
||||
return "text-successful";
|
||||
} else {
|
||||
return 'text-danger';
|
||||
return "text-danger";
|
||||
}
|
||||
},
|
||||
|
||||
@computed('model.created_at')
|
||||
@computed("model.created_at")
|
||||
createdAt(createdAt) {
|
||||
return moment(createdAt).format('YYYY-MM-DD HH:mm:ss');
|
||||
return moment(createdAt).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
|
||||
@computed('model.duration')
|
||||
@computed("model.duration")
|
||||
completion(duration) {
|
||||
const seconds = Math.floor(duration / 10.0) / 100.0;
|
||||
return I18n.t('admin.web_hooks.events.completed_in', { count: seconds });
|
||||
return I18n.t("admin.web_hooks.events.completed_in", { count: seconds });
|
||||
},
|
||||
|
||||
actions: {
|
||||
redeliver() {
|
||||
return bootbox.confirm(I18n.t('admin.web_hooks.events.redeliver_confirm'), I18n.t('no_value'), I18n.t('yes_value'), result => {
|
||||
if (result) {
|
||||
ajax(`/admin/api/web_hooks/${this.get('model.web_hook_id')}/events/${this.get('model.id')}/redeliver`, { type: 'POST' }).then(json => {
|
||||
this.set('model', json.web_hook_event);
|
||||
}).catch(popupAjaxError);
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.web_hooks.events.redeliver_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
ajax(
|
||||
`/admin/api/web_hooks/${this.get(
|
||||
"model.web_hook_id"
|
||||
)}/events/${this.get("model.id")}/redeliver`,
|
||||
{ type: "POST" }
|
||||
)
|
||||
.then(json => {
|
||||
this.set("model", json.web_hook_event);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
toggleRequest() {
|
||||
const expandDetailsKey = 'request';
|
||||
const expandDetailsKey = "request";
|
||||
|
||||
if (this.get('expandDetails') !== expandDetailsKey) {
|
||||
let headers = _.extend({
|
||||
'Request URL': this.get('model.request_url'),
|
||||
'Request method': 'POST'
|
||||
}, ensureJSON(this.get('model.headers')));
|
||||
if (this.get("expandDetails") !== expandDetailsKey) {
|
||||
let headers = _.extend(
|
||||
{
|
||||
"Request URL": this.get("model.request_url"),
|
||||
"Request method": "POST"
|
||||
},
|
||||
ensureJSON(this.get("model.headers"))
|
||||
);
|
||||
this.setProperties({
|
||||
headers: plainJSON(headers),
|
||||
body: prettyJSON(this.get('model.payload')),
|
||||
body: prettyJSON(this.get("model.payload")),
|
||||
expandDetails: expandDetailsKey,
|
||||
bodyLabel: I18n.t('admin.web_hooks.events.payload')
|
||||
bodyLabel: I18n.t("admin.web_hooks.events.payload")
|
||||
});
|
||||
} else {
|
||||
this.set('expandDetails', null);
|
||||
this.set("expandDetails", null);
|
||||
}
|
||||
},
|
||||
|
||||
toggleResponse() {
|
||||
const expandDetailsKey = 'response';
|
||||
const expandDetailsKey = "response";
|
||||
|
||||
if (this.get('expandDetails') !== expandDetailsKey) {
|
||||
if (this.get("expandDetails") !== expandDetailsKey) {
|
||||
this.setProperties({
|
||||
headers: plainJSON(this.get('model.response_headers')),
|
||||
body: this.get('model.response_body'),
|
||||
headers: plainJSON(this.get("model.response_headers")),
|
||||
body: this.get("model.response_body"),
|
||||
expandDetails: expandDetailsKey,
|
||||
bodyLabel: I18n.t('admin.web_hooks.events.body')
|
||||
bodyLabel: I18n.t("admin.web_hooks.events.body")
|
||||
});
|
||||
} else {
|
||||
this.set('expandDetails', null);
|
||||
this.set("expandDetails", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
classes: ["text-muted", "text-danger", "text-successful"],
|
||||
icons: ["circle-o", "times-circle", "circle"],
|
||||
export default Ember.Component.extend(
|
||||
bufferedRender({
|
||||
classes: ["text-muted", "text-danger", "text-successful"],
|
||||
icons: ["circle-o", "times-circle", "circle"],
|
||||
|
||||
@computed('deliveryStatuses', 'model.last_delivery_status')
|
||||
status(deliveryStatuses, lastDeliveryStatus) {
|
||||
return deliveryStatuses.find(s => s.id === lastDeliveryStatus);
|
||||
},
|
||||
@computed("deliveryStatuses", "model.last_delivery_status")
|
||||
status(deliveryStatuses, lastDeliveryStatus) {
|
||||
return deliveryStatuses.find(s => s.id === lastDeliveryStatus);
|
||||
},
|
||||
|
||||
@computed('status.id', 'icons')
|
||||
icon(statusId, icons) {
|
||||
return icons[statusId - 1];
|
||||
},
|
||||
@computed("status.id", "icons")
|
||||
icon(statusId, icons) {
|
||||
return icons[statusId - 1];
|
||||
},
|
||||
|
||||
@computed('status.id', 'classes')
|
||||
class(statusId, classes) {
|
||||
return classes[statusId - 1];
|
||||
},
|
||||
@computed("status.id", "classes")
|
||||
class(statusId, classes) {
|
||||
return classes[statusId - 1];
|
||||
},
|
||||
|
||||
buildBuffer(buffer) {
|
||||
buffer.push(iconHTML(this.get('icon'), { class: this.get('class') }));
|
||||
buffer.push(I18n.t(`admin.web_hooks.delivery_status.${this.get('status.name')}`));
|
||||
}
|
||||
}));
|
||||
buildBuffer(buffer) {
|
||||
buffer.push(iconHTML(this.get("icon"), { class: this.get("class") }));
|
||||
buffer.push(
|
||||
I18n.t(`admin.web_hooks.delivery_status.${this.get("status.name")}`)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
export default Ember.Component.extend({
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
$('body').addClass('admin-interface');
|
||||
$("body").addClass("admin-interface");
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super();
|
||||
$('body').removeClass('admin-interface');
|
||||
$("body").removeClass("admin-interface");
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: ''
|
||||
tagName: ""
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {default as loadScript, loadCSS } from 'discourse/lib/load-script';
|
||||
import { default as loadScript, loadCSS } from "discourse/lib/load-script";
|
||||
|
||||
/**
|
||||
An input field for a color.
|
||||
@ -8,35 +8,43 @@ import {default as loadScript, loadCSS } from 'discourse/lib/load-script';
|
||||
@params valid is a boolean indicating if the input field is a valid color.
|
||||
**/
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['color-picker'],
|
||||
classNames: ["color-picker"],
|
||||
hexValueChanged: function() {
|
||||
var hex = this.get('hexValue');
|
||||
let $text = this.$('input.hex-input');
|
||||
var hex = this.get("hexValue");
|
||||
let $text = this.$("input.hex-input");
|
||||
|
||||
if (this.get('valid')) {
|
||||
$text.attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';');
|
||||
if (this.get("valid")) {
|
||||
$text.attr(
|
||||
"style",
|
||||
"color: " +
|
||||
(this.get("brightnessValue") > 125 ? "black" : "white") +
|
||||
"; background-color: #" +
|
||||
hex +
|
||||
";"
|
||||
);
|
||||
|
||||
if (this.get('pickerLoaded')) {
|
||||
this.$('.picker').spectrum({color: "#" + this.get('hexValue')});
|
||||
if (this.get("pickerLoaded")) {
|
||||
this.$(".picker").spectrum({ color: "#" + this.get("hexValue") });
|
||||
}
|
||||
} else {
|
||||
$text.attr('style', '');
|
||||
$text.attr("style", "");
|
||||
}
|
||||
}.observes('hexValue', 'brightnessValue', 'valid'),
|
||||
}.observes("hexValue", "brightnessValue", "valid"),
|
||||
|
||||
didInsertElement() {
|
||||
loadScript('/javascripts/spectrum.js').then(()=>{
|
||||
loadCSS('/javascripts/spectrum.css').then(()=>{
|
||||
Em.run.schedule('afterRender', ()=>{
|
||||
this.$('.picker').spectrum({color: "#" + this.get('hexValue')})
|
||||
.on("change.spectrum", (me, color)=>{
|
||||
this.set('hexValue', color.toHexString().replace("#",""));
|
||||
});
|
||||
this.set('pickerLoaded', true);
|
||||
loadScript("/javascripts/spectrum.js").then(() => {
|
||||
loadCSS("/javascripts/spectrum.css").then(() => {
|
||||
Em.run.schedule("afterRender", () => {
|
||||
this.$(".picker")
|
||||
.spectrum({ color: "#" + this.get("hexValue") })
|
||||
.on("change.spectrum", (me, color) => {
|
||||
this.set("hexValue", color.toHexString().replace("#", ""));
|
||||
});
|
||||
this.set("pickerLoaded", true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Em.run.schedule('afterRender', ()=>{
|
||||
Em.run.schedule("afterRender", () => {
|
||||
this.hexValueChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AsyncReport from "admin/mixins/async-report";
|
||||
|
||||
export default Ember.Component.extend(AsyncReport, {
|
||||
classNames: ["dashboard-inline-table"],
|
||||
|
||||
fetchReport() {
|
||||
this._super();
|
||||
|
||||
let payload = this.buildPayload(["total", "prev30Days"]);
|
||||
|
||||
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||
return ajax(dataSource, payload)
|
||||
.then(response => {
|
||||
this.get("reports").pushObject(this.loadReport(response.report));
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
@ -1,165 +0,0 @@
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AsyncReport from "admin/mixins/async-report";
|
||||
import Report from "admin/models/report";
|
||||
import { number } from 'discourse/lib/formatter';
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { registerTooltip, unregisterTooltip } from "discourse/lib/tooltip";
|
||||
|
||||
function collapseWeekly(data, average) {
|
||||
let aggregate = [];
|
||||
let bucket, i;
|
||||
let offset = data.length % 7;
|
||||
for(i = offset; i < data.length; i++) {
|
||||
|
||||
if (bucket && (i % 7 === offset)) {
|
||||
if (average) {
|
||||
bucket.y = parseFloat((bucket.y / 7.0).toFixed(2));
|
||||
}
|
||||
aggregate.push(bucket);
|
||||
bucket = null;
|
||||
}
|
||||
|
||||
bucket = bucket || { x: data[i].x, y: 0 };
|
||||
bucket.y += data[i].y;
|
||||
}
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
export default Ember.Component.extend(AsyncReport, {
|
||||
classNames: ["chart", "dashboard-mini-chart"],
|
||||
total: 0,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
|
||||
this._colorsPool = ["rgb(0,136,204)", "rgb(235,83,148)"];
|
||||
},
|
||||
|
||||
didRender() {
|
||||
this._super();
|
||||
registerTooltip($(this.element).find("[data-tooltip]"));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super();
|
||||
unregisterTooltip($(this.element).find("[data-tooltip]"));
|
||||
},
|
||||
|
||||
pickColorAtIndex(index) {
|
||||
return this._colorsPool[index] || this._colorsPool[0];
|
||||
},
|
||||
|
||||
fetchReport() {
|
||||
this._super();
|
||||
|
||||
let payload = this.buildPayload(["prev_period"]);
|
||||
|
||||
if (this._chart) {
|
||||
this._chart.destroy();
|
||||
this._chart = null;
|
||||
}
|
||||
|
||||
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||
return ajax(dataSource, payload)
|
||||
.then(response => {
|
||||
this.get("reports").pushObject(this.loadReport(response.report));
|
||||
});
|
||||
}));
|
||||
},
|
||||
|
||||
loadReport(report, previousReport) {
|
||||
Report.fillMissingDates(report);
|
||||
|
||||
if (report.data && report.data.length > 40) {
|
||||
report.data = collapseWeekly(report.data, report.average);
|
||||
}
|
||||
|
||||
if (previousReport && previousReport.color.length) {
|
||||
report.color = previousReport.color;
|
||||
} else {
|
||||
const dataSourceNameIndex = this.get("dataSourceNames").split(",").indexOf(report.type);
|
||||
report.color = this.pickColorAtIndex(dataSourceNameIndex);
|
||||
}
|
||||
|
||||
return Report.create(report);
|
||||
},
|
||||
|
||||
renderReport() {
|
||||
this._super();
|
||||
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
const $chartCanvas = this.$(".chart-canvas");
|
||||
if (!$chartCanvas.length) return;
|
||||
const context = $chartCanvas[0].getContext("2d");
|
||||
|
||||
const reportsForPeriod = this.get("reportsForPeriod");
|
||||
|
||||
const labels = Ember.makeArray(reportsForPeriod.get("firstObject.data")).map(d => d.x);
|
||||
|
||||
const data = {
|
||||
labels,
|
||||
datasets: reportsForPeriod.map(report => {
|
||||
return {
|
||||
data: Ember.makeArray(report.data).map(d => Math.round(parseFloat(d.y))),
|
||||
backgroundColor: "rgba(200,220,240,0.3)",
|
||||
borderColor: report.color
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
if (this._chart) {
|
||||
this._chart.destroy();
|
||||
this._chart = null;
|
||||
}
|
||||
|
||||
loadScript("/javascripts/Chart.min.js").then(() => {
|
||||
if (this._chart) {
|
||||
this._chart.destroy();
|
||||
}
|
||||
|
||||
this._chart = new window.Chart(context, this._buildChartConfig(data));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_buildChartConfig(data) {
|
||||
return {
|
||||
type: "line",
|
||||
data,
|
||||
options: {
|
||||
tooltips: {
|
||||
callbacks: {
|
||||
title: (context) => moment(context[0].xLabel, "YYYY-MM-DD").format("LL")
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
layout: {
|
||||
padding: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: true,
|
||||
ticks: { callback: (label) => number(label) }
|
||||
}],
|
||||
xAxes: [{
|
||||
display: true,
|
||||
gridLines: { display: false },
|
||||
type: "time",
|
||||
time: {
|
||||
parser: "YYYY-MM-DD"
|
||||
}
|
||||
}],
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
@ -1,19 +0,0 @@
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AsyncReport from "admin/mixins/async-report";
|
||||
|
||||
export default Ember.Component.extend(AsyncReport, {
|
||||
classNames: ["dashboard-table"],
|
||||
|
||||
fetchReport() {
|
||||
this._super();
|
||||
|
||||
let payload = this.buildPayload(["total", "prev30Days"]);
|
||||
|
||||
return Ember.RSVP.Promise.all(this.get("dataSources").map(dataSource => {
|
||||
return ajax(dataSource, payload)
|
||||
.then(response => {
|
||||
this.get("reports").pushObject(this.loadReport(response.report));
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
@ -1,63 +1,79 @@
|
||||
import { bufferedProperty } from 'discourse/mixins/buffered-content';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on, observes } from "ember-addons/ember-computed-decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Ember.Component.extend(bufferedProperty('host'), {
|
||||
export default Ember.Component.extend(bufferedProperty("host"), {
|
||||
editToggled: false,
|
||||
tagName: 'tr',
|
||||
tagName: "tr",
|
||||
categoryId: null,
|
||||
|
||||
editing: Ember.computed.or('host.isNew', 'editToggled'),
|
||||
editing: Ember.computed.or("host.isNew", "editToggled"),
|
||||
|
||||
@on('didInsertElement')
|
||||
@observes('editing')
|
||||
@on("didInsertElement")
|
||||
@observes("editing")
|
||||
_focusOnInput() {
|
||||
Ember.run.schedule('afterRender', () => { this.$('.host-name').focus(); });
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.$(".host-name").focus();
|
||||
});
|
||||
},
|
||||
|
||||
@computed('buffered.host', 'host.isSaving')
|
||||
@computed("buffered.host", "host.isSaving")
|
||||
cantSave(host, isSaving) {
|
||||
return isSaving || Ember.isEmpty(host);
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit() {
|
||||
this.set('categoryId', this.get('host.category.id'));
|
||||
this.set('editToggled', true);
|
||||
this.set("categoryId", this.get("host.category.id"));
|
||||
this.set("editToggled", true);
|
||||
},
|
||||
|
||||
save() {
|
||||
if (this.get('cantSave')) { return; }
|
||||
if (this.get("cantSave")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const props = this.get('buffered').getProperties('host', 'path_whitelist', 'class_name');
|
||||
props.category_id = this.get('categoryId');
|
||||
const props = this.get("buffered").getProperties(
|
||||
"host",
|
||||
"path_whitelist",
|
||||
"class_name"
|
||||
);
|
||||
props.category_id = this.get("categoryId");
|
||||
|
||||
const host = this.get('host');
|
||||
const host = this.get("host");
|
||||
|
||||
host.save(props).then(() => {
|
||||
host.set('category', Discourse.Category.findById(this.get('categoryId')));
|
||||
this.set('editToggled', false);
|
||||
}).catch(popupAjaxError);
|
||||
host
|
||||
.save(props)
|
||||
.then(() => {
|
||||
host.set(
|
||||
"category",
|
||||
Discourse.Category.findById(this.get("categoryId"))
|
||||
);
|
||||
this.set("editToggled", false);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
delete() {
|
||||
bootbox.confirm(I18n.t('admin.embedding.confirm_delete'), (result) => {
|
||||
bootbox.confirm(I18n.t("admin.embedding.confirm_delete"), result => {
|
||||
if (result) {
|
||||
this.get('host').destroyRecord().then(() => {
|
||||
this.sendAction('deleteHost', this.get('host'));
|
||||
});
|
||||
this.get("host")
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
this.sendAction("deleteHost", this.get("host"));
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
const host = this.get('host');
|
||||
if (host.get('isNew')) {
|
||||
this.sendAction('deleteHost', host);
|
||||
const host = this.get("host");
|
||||
if (host.get("isNew")) {
|
||||
this.sendAction("deleteHost", host);
|
||||
} else {
|
||||
this.rollbackBuffer();
|
||||
this.set('editToggled', false);
|
||||
this.set("editToggled", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,30 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['embed-setting'],
|
||||
classNames: ["embed-setting"],
|
||||
|
||||
@computed('field')
|
||||
inputId(field) { return field.dasherize(); },
|
||||
@computed("field")
|
||||
inputId(field) {
|
||||
return field.dasherize();
|
||||
},
|
||||
|
||||
@computed('field')
|
||||
translationKey(field) { return `admin.embedding.${field}`; },
|
||||
@computed("field")
|
||||
translationKey(field) {
|
||||
return `admin.embedding.${field}`;
|
||||
},
|
||||
|
||||
@computed('type')
|
||||
isCheckbox(type) { return type === "checkbox"; },
|
||||
@computed("type")
|
||||
isCheckbox(type) {
|
||||
return type === "checkbox";
|
||||
},
|
||||
|
||||
@computed('value')
|
||||
@computed("value")
|
||||
checked: {
|
||||
get(value) { return !!value; },
|
||||
get(value) {
|
||||
return !!value;
|
||||
},
|
||||
set(value) {
|
||||
this.set('value', value);
|
||||
this.set("value", value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['flag-user-lists']
|
||||
classNames: ["flag-user-lists"]
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['flagged-post-response']
|
||||
classNames: ["flagged-post-response"]
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'h3'
|
||||
tagName: "h3"
|
||||
});
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
adminTools: Ember.inject.service(),
|
||||
expanded: false,
|
||||
tagName: 'div',
|
||||
tagName: "div",
|
||||
classNameBindings: [
|
||||
':flagged-post',
|
||||
'flaggedPost.hidden:hidden-post',
|
||||
'flaggedPost.deleted'
|
||||
":flagged-post",
|
||||
"flaggedPost.hidden:hidden-post",
|
||||
"flaggedPost.deleted"
|
||||
],
|
||||
|
||||
canAct: Ember.computed.alias('actableFilter'),
|
||||
canAct: Ember.computed.alias("actableFilter"),
|
||||
|
||||
@computed('filter')
|
||||
@computed("filter")
|
||||
actableFilter(filter) {
|
||||
return filter === 'active';
|
||||
return filter === "active";
|
||||
},
|
||||
|
||||
removeAfter(promise) {
|
||||
@ -24,7 +24,7 @@ export default Ember.Component.extend({
|
||||
|
||||
_spawnModal(name, model, modalClass) {
|
||||
let controller = showModal(name, { model, admin: true, modalClass });
|
||||
controller.removeAfter = (p) => this.removeAfter(p);
|
||||
controller.removeAfter = p => this.removeAfter(p);
|
||||
},
|
||||
|
||||
actions: {
|
||||
@ -33,23 +33,25 @@ export default Ember.Component.extend({
|
||||
},
|
||||
|
||||
disagree() {
|
||||
this.removeAfter(this.get('flaggedPost').disagreeFlags());
|
||||
this.removeAfter(this.get("flaggedPost").disagreeFlags());
|
||||
},
|
||||
|
||||
defer() {
|
||||
this.removeAfter(this.get('flaggedPost').deferFlags());
|
||||
this.removeAfter(this.get("flaggedPost").deferFlags());
|
||||
},
|
||||
|
||||
expand() {
|
||||
this.get('flaggedPost').expandHidden().then(() => {
|
||||
this.set('expanded', true);
|
||||
});
|
||||
this.get("flaggedPost")
|
||||
.expandHidden()
|
||||
.then(() => {
|
||||
this.set("expanded", true);
|
||||
});
|
||||
},
|
||||
|
||||
showModerationHistory() {
|
||||
this.get('adminTools').showModerationHistory({
|
||||
filter: 'post',
|
||||
post_id: this.get('flaggedPost.id')
|
||||
this.get("adminTools").showModerationHistory({
|
||||
filter: "post",
|
||||
post_id: this.get("flaggedPost.id")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { on, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import highlightSyntax from 'discourse/lib/highlight-syntax';
|
||||
import { on, observes } from "ember-addons/ember-computed-decorators";
|
||||
import highlightSyntax from "discourse/lib/highlight-syntax";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
||||
@on('didInsertElement')
|
||||
@observes('code')
|
||||
@on("didInsertElement")
|
||||
@observes("code")
|
||||
_refresh: function() {
|
||||
highlightSyntax(this.$());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import {default as computed, observes} from "ember-addons/ember-computed-decorators";
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
init(){
|
||||
init() {
|
||||
this._super();
|
||||
this.set("checkedInternal", this.get("checked"));
|
||||
},
|
||||
|
||||
classNames: ['inline-edit'],
|
||||
classNames: ["inline-edit"],
|
||||
|
||||
@observes("checked")
|
||||
checkedChanged() {
|
||||
@ -20,15 +23,15 @@ export default Ember.Component.extend({
|
||||
|
||||
@computed("checked", "checkedInternal")
|
||||
changed(checked, checkedInternal) {
|
||||
return (!!checked) !== (!!checkedInternal);
|
||||
return !!checked !== !!checkedInternal;
|
||||
},
|
||||
|
||||
actions: {
|
||||
cancelled(){
|
||||
cancelled() {
|
||||
this.set("checkedInternal", this.get("checked"));
|
||||
},
|
||||
|
||||
finished(){
|
||||
finished() {
|
||||
this.set("checked", this.get("checkedInternal"));
|
||||
this.sendAction();
|
||||
}
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import AdminUser from 'admin/models/admin-user';
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import copyText from "discourse/lib/copy-text";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["ip-lookup"],
|
||||
|
||||
city: function () {
|
||||
city: function() {
|
||||
return [
|
||||
this.get("location.city"),
|
||||
this.get("location.region"),
|
||||
this.get("location.country")
|
||||
].filter(Boolean).join(", ");
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(", ");
|
||||
}.property("location.{city,region,country}"),
|
||||
|
||||
otherAccountsToDelete: function() {
|
||||
@ -20,14 +23,14 @@ export default Ember.Component.extend({
|
||||
}.property("other_accounts", "totalOthersWithSameIP"),
|
||||
|
||||
actions: {
|
||||
lookup: function () {
|
||||
lookup: function() {
|
||||
var self = this;
|
||||
this.set("show", true);
|
||||
|
||||
if (!this.get("location")) {
|
||||
ajax("/admin/users/ip-info", {
|
||||
data: { ip: this.get("ip") }
|
||||
}).then(function (location) {
|
||||
}).then(function(location) {
|
||||
self.set("location", Em.Object.create(location));
|
||||
});
|
||||
}
|
||||
@ -36,50 +39,92 @@ export default Ember.Component.extend({
|
||||
this.set("otherAccountsLoading", true);
|
||||
|
||||
var data = {
|
||||
"ip": this.get("ip"),
|
||||
"exclude": this.get("userId"),
|
||||
"order": "trust_level DESC"
|
||||
ip: this.get("ip"),
|
||||
exclude: this.get("userId"),
|
||||
order: "trust_level DESC"
|
||||
};
|
||||
|
||||
ajax("/admin/users/total-others-with-same-ip", { data }).then(function (result) {
|
||||
ajax("/admin/users/total-others-with-same-ip", { data }).then(function(
|
||||
result
|
||||
) {
|
||||
self.set("totalOthersWithSameIP", result.total);
|
||||
});
|
||||
|
||||
AdminUser.findAll("active", data).then(function (users) {
|
||||
AdminUser.findAll("active", data).then(function(users) {
|
||||
self.setProperties({
|
||||
other_accounts: users,
|
||||
otherAccountsLoading: false,
|
||||
otherAccountsLoading: false
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
hide: function() {
|
||||
this.set("show", false);
|
||||
},
|
||||
|
||||
copy: function() {
|
||||
let text = `IP: ${this.get("ip")}\n`;
|
||||
const location = this.get("location");
|
||||
if (location) {
|
||||
if (location.hostname) {
|
||||
text += `${I18n.t("ip_lookup.hostname")}: ${location.hostname}\n`;
|
||||
}
|
||||
|
||||
text += I18n.t("ip_lookup.location");
|
||||
if (location.loc) {
|
||||
text += `: ${location.loc} ${this.get("city")}\n`;
|
||||
} else {
|
||||
text += `: ${I18n.t("ip_lookup.location_not_found")}\n`;
|
||||
}
|
||||
|
||||
if (location.org) {
|
||||
text += I18n.t("ip_lookup.organisation");
|
||||
text += `: ${location.org}\n`;
|
||||
}
|
||||
|
||||
if (location.phone) {
|
||||
text += I18n.t("ip_lookup.phone");
|
||||
text += `: ${location.phone}\n`;
|
||||
}
|
||||
}
|
||||
const copyRange = $('<p id="copy-range"></p>');
|
||||
copyRange.html(text.trim().replace("\n", "<br>"));
|
||||
$(document.body).append(copyRange);
|
||||
if (copyText(text, copyRange[0])) {
|
||||
this.set("copied", true);
|
||||
Ember.run.later(() => this.set("copied", false), 2000);
|
||||
}
|
||||
copyRange.remove();
|
||||
},
|
||||
|
||||
deleteOtherAccounts: function() {
|
||||
var self = this;
|
||||
bootbox.confirm(I18n.t("ip_lookup.confirm_delete_other_accounts"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) {
|
||||
if (confirmed) {
|
||||
self.setProperties({
|
||||
other_accounts: null,
|
||||
otherAccountsLoading: true,
|
||||
totalOthersWithSameIP: null
|
||||
});
|
||||
bootbox.confirm(
|
||||
I18n.t("ip_lookup.confirm_delete_other_accounts"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
function(confirmed) {
|
||||
if (confirmed) {
|
||||
self.setProperties({
|
||||
other_accounts: null,
|
||||
otherAccountsLoading: true,
|
||||
totalOthersWithSameIP: null
|
||||
});
|
||||
|
||||
ajax("/admin/users/delete-others-with-same-ip.json", {
|
||||
type: "DELETE",
|
||||
data: {
|
||||
"ip": self.get("ip"),
|
||||
"exclude": self.get("userId"),
|
||||
"order": "trust_level DESC"
|
||||
}
|
||||
}).then(function() {
|
||||
self.send("lookup");
|
||||
});
|
||||
ajax("/admin/users/delete-others-with-same-ip.json", {
|
||||
type: "DELETE",
|
||||
data: {
|
||||
ip: self.get("ip"),
|
||||
exclude: self.get("userId"),
|
||||
order: "trust_level DESC"
|
||||
}
|
||||
}).then(function() {
|
||||
self.send("lookup");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr',
|
||||
tagName: "tr"
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const ACTIONS = ['delete', 'edit', 'none'];
|
||||
const ACTIONS = ["delete", "edit", "none"];
|
||||
export default Ember.Component.extend({
|
||||
postAction: null,
|
||||
postEdit: null,
|
||||
@ -12,19 +12,19 @@ export default Ember.Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
editing: Ember.computed.equal('postAction', 'edit'),
|
||||
editing: Ember.computed.equal("postAction", "edit"),
|
||||
|
||||
actions: {
|
||||
penaltyChanged() {
|
||||
let postAction = this.get('postAction');
|
||||
let postAction = this.get("postAction");
|
||||
|
||||
// If we switch to edit mode, jump to the edit textarea
|
||||
if (postAction === 'edit') {
|
||||
Ember.run.scheduleOnce('afterRender', () => {
|
||||
if (postAction === "edit") {
|
||||
Ember.run.scheduleOnce("afterRender", () => {
|
||||
let $elem = this.$();
|
||||
let body = $elem.closest('.modal-body');
|
||||
let body = $elem.closest(".modal-body");
|
||||
body.scrollTop(body.height());
|
||||
$elem.find('.post-editor').focus();
|
||||
$elem.find(".post-editor").focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,45 +1,58 @@
|
||||
import Permalink from 'admin/models/permalink';
|
||||
import Permalink from "admin/models/permalink";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['permalink-form'],
|
||||
classNames: ["permalink-form"],
|
||||
formSubmitted: false,
|
||||
permalinkType: 'topic_id',
|
||||
permalinkType: "topic_id",
|
||||
|
||||
permalinkTypes: function() {
|
||||
return [
|
||||
{id: 'topic_id', name: I18n.t('admin.permalink.topic_id')},
|
||||
{id: 'post_id', name: I18n.t('admin.permalink.post_id')},
|
||||
{id: 'category_id', name: I18n.t('admin.permalink.category_id')},
|
||||
{id: 'external_url', name: I18n.t('admin.permalink.external_url')}
|
||||
{ id: "topic_id", name: I18n.t("admin.permalink.topic_id") },
|
||||
{ id: "post_id", name: I18n.t("admin.permalink.post_id") },
|
||||
{ id: "category_id", name: I18n.t("admin.permalink.category_id") },
|
||||
{ id: "external_url", name: I18n.t("admin.permalink.external_url") }
|
||||
];
|
||||
}.property(),
|
||||
|
||||
permalinkTypePlaceholder: function() {
|
||||
return 'admin.permalink.' + this.get('permalinkType');
|
||||
}.property('permalinkType'),
|
||||
return "admin.permalink." + this.get("permalinkType");
|
||||
}.property("permalinkType"),
|
||||
|
||||
actions: {
|
||||
submit: function() {
|
||||
if (!this.get('formSubmitted')) {
|
||||
if (!this.get("formSubmitted")) {
|
||||
const self = this;
|
||||
self.set('formSubmitted', true);
|
||||
const permalink = Permalink.create({url: self.get('url'), permalink_type: self.get('permalinkType'), permalink_type_value: self.get('permalink_type_value')});
|
||||
permalink.save().then(function(result) {
|
||||
self.set('url', '');
|
||||
self.set('permalink_type_value', '');
|
||||
self.set('formSubmitted', false);
|
||||
self.sendAction('action', Permalink.create(result.permalink));
|
||||
Em.run.schedule('afterRender', function() { self.$('.permalink-url').focus(); });
|
||||
}, function(e) {
|
||||
self.set('formSubmitted', false);
|
||||
let error;
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
error = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')});
|
||||
} else {
|
||||
error = I18n.t("generic_error");
|
||||
}
|
||||
bootbox.alert(error, function() { self.$('.permalink-url').focus(); });
|
||||
self.set("formSubmitted", true);
|
||||
const permalink = Permalink.create({
|
||||
url: self.get("url"),
|
||||
permalink_type: self.get("permalinkType"),
|
||||
permalink_type_value: self.get("permalink_type_value")
|
||||
});
|
||||
permalink.save().then(
|
||||
function(result) {
|
||||
self.set("url", "");
|
||||
self.set("permalink_type_value", "");
|
||||
self.set("formSubmitted", false);
|
||||
self.sendAction("action", Permalink.create(result.permalink));
|
||||
Em.run.schedule("afterRender", function() {
|
||||
self.$(".permalink-url").focus();
|
||||
});
|
||||
},
|
||||
function(e) {
|
||||
self.set("formSubmitted", false);
|
||||
let error;
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
error = I18n.t("generic_error_with_reason", {
|
||||
error: e.responseJSON.errors.join(". ")
|
||||
});
|
||||
} else {
|
||||
error = I18n.t("generic_error");
|
||||
}
|
||||
bootbox.alert(error, function() {
|
||||
self.$(".permalink-url").focus();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -47,10 +60,11 @@ export default Ember.Component.extend({
|
||||
didInsertElement: function() {
|
||||
var self = this;
|
||||
self._super();
|
||||
Em.run.schedule('afterRender', function() {
|
||||
self.$('.external-url').keydown(function(e) {
|
||||
if (e.keyCode === 13) { // enter key
|
||||
self.send('submit');
|
||||
Em.run.schedule("afterRender", function() {
|
||||
self.$(".external-url").keydown(function(e) {
|
||||
if (e.keyCode === 13) {
|
||||
// enter key
|
||||
self.send("submit");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { iconHTML } from 'discourse-common/lib/icon-library';
|
||||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { bufferedRender } from "discourse-common/lib/buffered-render";
|
||||
|
||||
/*global Resumable:true */
|
||||
|
||||
@ -13,111 +13,119 @@ import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
uploadText="UPLOAD"
|
||||
}}
|
||||
**/
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
tagName: "button",
|
||||
classNames: ["btn", "ru"],
|
||||
classNameBindings: ["isUploading"],
|
||||
attributeBindings: ["translatedTitle:title"],
|
||||
export default Ember.Component.extend(
|
||||
bufferedRender({
|
||||
tagName: "button",
|
||||
classNames: ["btn", "ru"],
|
||||
classNameBindings: ["isUploading"],
|
||||
attributeBindings: ["translatedTitle:title"],
|
||||
|
||||
resumable: null,
|
||||
resumable: null,
|
||||
|
||||
isUploading: false,
|
||||
progress: 0,
|
||||
isUploading: false,
|
||||
progress: 0,
|
||||
|
||||
rerenderTriggers: ['isUploading', 'progress'],
|
||||
rerenderTriggers: ["isUploading", "progress"],
|
||||
|
||||
translatedTitle: function() {
|
||||
const title = this.get('title');
|
||||
return title ? I18n.t(title) : this.get('text');
|
||||
}.property('title', 'text'),
|
||||
translatedTitle: function() {
|
||||
const title = this.get("title");
|
||||
return title ? I18n.t(title) : this.get("text");
|
||||
}.property("title", "text"),
|
||||
|
||||
text: function() {
|
||||
if (this.get("isUploading")) {
|
||||
return this.get("progress") + " %";
|
||||
} else {
|
||||
return this.get("uploadText");
|
||||
}
|
||||
}.property("isUploading", "progress"),
|
||||
text: function() {
|
||||
if (this.get("isUploading")) {
|
||||
return this.get("progress") + " %";
|
||||
} else {
|
||||
return this.get("uploadText");
|
||||
}
|
||||
}.property("isUploading", "progress"),
|
||||
|
||||
buildBuffer(buffer) {
|
||||
const icon = this.get("isUploading") ? "times" : "upload";
|
||||
buffer.push(iconHTML(icon));
|
||||
buffer.push("<span class='ru-label'>" + this.get("text") + "</span>");
|
||||
buffer.push("<span class='ru-progress' style='width:" + this.get("progress") + "%'></span>");
|
||||
},
|
||||
buildBuffer(buffer) {
|
||||
const icon = this.get("isUploading") ? "times" : "upload";
|
||||
buffer.push(iconHTML(icon));
|
||||
buffer.push("<span class='ru-label'>" + this.get("text") + "</span>");
|
||||
buffer.push(
|
||||
"<span class='ru-progress' style='width:" +
|
||||
this.get("progress") +
|
||||
"%'></span>"
|
||||
);
|
||||
},
|
||||
|
||||
click: function() {
|
||||
if (this.get("isUploading")) {
|
||||
this.resumable.cancel();
|
||||
var self = this;
|
||||
Em.run.later(function() {
|
||||
self._reset();
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this.setProperties({ isUploading: false, progress: 0 });
|
||||
},
|
||||
|
||||
_initialize: function() {
|
||||
this.resumable = new Resumable({
|
||||
target: Discourse.getURL(this.get("target")),
|
||||
maxFiles: 1, // only 1 file at a time
|
||||
headers: {
|
||||
"X-CSRF-Token": $("meta[name='csrf-token']").attr("content")
|
||||
}
|
||||
});
|
||||
|
||||
click: function() {
|
||||
if (this.get("isUploading")) {
|
||||
this.resumable.cancel();
|
||||
var self = this;
|
||||
Em.run.later(function() { self._reset(); });
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this.setProperties({ isUploading: false, progress: 0 });
|
||||
},
|
||||
|
||||
_initialize: function() {
|
||||
this.resumable = new Resumable({
|
||||
target: Discourse.getURL(this.get("target")),
|
||||
maxFiles: 1, // only 1 file at a time
|
||||
headers: { "X-CSRF-Token": $("meta[name='csrf-token']").attr("content") }
|
||||
});
|
||||
|
||||
var self = this;
|
||||
|
||||
this.resumable.on("fileAdded", function() {
|
||||
// automatically upload the selected file
|
||||
self.resumable.upload();
|
||||
// mark as uploading
|
||||
Em.run.later(function() {
|
||||
self.set("isUploading", true);
|
||||
this.resumable.on("fileAdded", function() {
|
||||
// automatically upload the selected file
|
||||
self.resumable.upload();
|
||||
// mark as uploading
|
||||
Em.run.later(function() {
|
||||
self.set("isUploading", true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.resumable.on("fileProgress", function(file) {
|
||||
// update progress
|
||||
Em.run.later(function() {
|
||||
self.set("progress", parseInt(file.progress() * 100, 10));
|
||||
this.resumable.on("fileProgress", function(file) {
|
||||
// update progress
|
||||
Em.run.later(function() {
|
||||
self.set("progress", parseInt(file.progress() * 100, 10));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.resumable.on("fileSuccess", function(file) {
|
||||
Em.run.later(function() {
|
||||
// mark as not uploading anymore
|
||||
self._reset();
|
||||
// fire an event to allow the parent route to reload its model
|
||||
self.sendAction("success", file.fileName);
|
||||
this.resumable.on("fileSuccess", function(file) {
|
||||
Em.run.later(function() {
|
||||
// mark as not uploading anymore
|
||||
self._reset();
|
||||
// fire an event to allow the parent route to reload its model
|
||||
self.sendAction("success", file.fileName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.resumable.on("fileError", function(file, message) {
|
||||
Em.run.later(function() {
|
||||
// mark as not uploading anymore
|
||||
self._reset();
|
||||
// fire an event to allow the parent route to display the error message
|
||||
self.sendAction("error", file.fileName, message);
|
||||
this.resumable.on("fileError", function(file, message) {
|
||||
Em.run.later(function() {
|
||||
// mark as not uploading anymore
|
||||
self._reset();
|
||||
// fire an event to allow the parent route to display the error message
|
||||
self.sendAction("error", file.fileName, message);
|
||||
});
|
||||
});
|
||||
});
|
||||
}.on("init"),
|
||||
|
||||
}.on("init"),
|
||||
_assignBrowse: function() {
|
||||
var self = this;
|
||||
Em.run.schedule("afterRender", function() {
|
||||
self.resumable.assignBrowse(self.$());
|
||||
});
|
||||
}.on("didInsertElement"),
|
||||
|
||||
_assignBrowse: function() {
|
||||
var self = this;
|
||||
Em.run.schedule("afterRender", function() {
|
||||
self.resumable.assignBrowse(self.$());
|
||||
});
|
||||
}.on("didInsertElement"),
|
||||
|
||||
_teardown: function() {
|
||||
if (this.resumable) {
|
||||
this.resumable.cancel();
|
||||
this.resumable = null;
|
||||
}
|
||||
}.on("willDestroyElement")
|
||||
|
||||
}));
|
||||
_teardown: function() {
|
||||
if (this.resumable) {
|
||||
this.resumable.cancel();
|
||||
this.resumable = null;
|
||||
}
|
||||
}.on("willDestroyElement")
|
||||
})
|
||||
);
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['controls'],
|
||||
classNames: ["controls"],
|
||||
|
||||
buttonDisabled: Ember.computed.or('model.isSaving', 'saveDisabled'),
|
||||
buttonDisabled: Ember.computed.or("model.isSaving", "saveDisabled"),
|
||||
|
||||
@computed('model.isSaving')
|
||||
@computed("model.isSaving")
|
||||
savingText(saving) {
|
||||
return saving ? 'saving' : 'save';
|
||||
return saving ? "saving" : "save";
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
@ -9,14 +9,14 @@
|
||||
as an argument.
|
||||
**/
|
||||
|
||||
import ScreenedIpAddress from 'admin/models/screened-ip-address';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
import ScreenedIpAddress from "admin/models/screened-ip-address";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['screened-ip-address-form'],
|
||||
classNames: ["screened-ip-address-form"],
|
||||
formSubmitted: false,
|
||||
actionName: 'block',
|
||||
actionName: "block",
|
||||
|
||||
@computed
|
||||
adminWhitelistEnabled() {
|
||||
@ -27,51 +27,67 @@ export default Ember.Component.extend({
|
||||
actionNames(adminWhitelistEnabled) {
|
||||
if (adminWhitelistEnabled) {
|
||||
return [
|
||||
{id: 'block', name: I18n.t('admin.logs.screened_ips.actions.block')},
|
||||
{id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')},
|
||||
{id: 'allow_admin', name: I18n.t('admin.logs.screened_ips.actions.allow_admin')}
|
||||
{ id: "block", name: I18n.t("admin.logs.screened_ips.actions.block") },
|
||||
{
|
||||
id: "do_nothing",
|
||||
name: I18n.t("admin.logs.screened_ips.actions.do_nothing")
|
||||
},
|
||||
{
|
||||
id: "allow_admin",
|
||||
name: I18n.t("admin.logs.screened_ips.actions.allow_admin")
|
||||
}
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
{id: 'block', name: I18n.t('admin.logs.screened_ips.actions.block')},
|
||||
{id: 'do_nothing', name: I18n.t('admin.logs.screened_ips.actions.do_nothing')}
|
||||
{ id: "block", name: I18n.t("admin.logs.screened_ips.actions.block") },
|
||||
{
|
||||
id: "do_nothing",
|
||||
name: I18n.t("admin.logs.screened_ips.actions.do_nothing")
|
||||
}
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit() {
|
||||
if (!this.get('formSubmitted')) {
|
||||
this.set('formSubmitted', true);
|
||||
if (!this.get("formSubmitted")) {
|
||||
this.set("formSubmitted", true);
|
||||
const screenedIpAddress = ScreenedIpAddress.create({
|
||||
ip_address: this.get('ip_address'),
|
||||
action_name: this.get('actionName')
|
||||
});
|
||||
screenedIpAddress.save().then(result => {
|
||||
if (result.success) {
|
||||
this.setProperties({ ip_address: '', formSubmitted: false });
|
||||
this.sendAction('action', ScreenedIpAddress.create(result.screened_ip_address));
|
||||
Ember.run.schedule('afterRender', () => this.$('.ip-address-input').focus());
|
||||
} else {
|
||||
bootbox.alert(result.errors);
|
||||
}
|
||||
}).catch(e => {
|
||||
this.set('formSubmitted', false);
|
||||
const msg = (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) ?
|
||||
I18n.t("generic_error_with_reason", {error: e.jqXHR.responseJSON.errors.join('. ')}) :
|
||||
I18n.t("generic_error");
|
||||
bootbox.alert(msg, () => this.$('.ip-address-input').focus());
|
||||
ip_address: this.get("ip_address"),
|
||||
action_name: this.get("actionName")
|
||||
});
|
||||
screenedIpAddress
|
||||
.save()
|
||||
.then(result => {
|
||||
this.setProperties({ ip_address: "", formSubmitted: false });
|
||||
this.sendAction(
|
||||
"action",
|
||||
ScreenedIpAddress.create(result.screened_ip_address)
|
||||
);
|
||||
Ember.run.schedule("afterRender", () =>
|
||||
this.$(".ip-address-input").focus()
|
||||
);
|
||||
})
|
||||
.catch(e => {
|
||||
this.set("formSubmitted", false);
|
||||
const msg =
|
||||
e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors
|
||||
? I18n.t("generic_error_with_reason", {
|
||||
error: e.jqXHR.responseJSON.errors.join(". ")
|
||||
})
|
||||
: I18n.t("generic_error");
|
||||
bootbox.alert(msg, () => this.$(".ip-address-input").focus());
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_init() {
|
||||
Ember.run.schedule('afterRender', () => {
|
||||
this.$('.ip-address-input').keydown(e => {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.$(".ip-address-input").keydown(e => {
|
||||
if (e.keyCode === 13) {
|
||||
this.send('submit');
|
||||
this.send("submit");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: ''
|
||||
tagName: ""
|
||||
});
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import SiteSetting from 'admin/models/site-setting';
|
||||
import SettingComponent from 'admin/mixins/setting-component';
|
||||
import BufferedContent from "discourse/mixins/buffered-content";
|
||||
import SiteSetting from "admin/models/site-setting";
|
||||
import SettingComponent from "admin/mixins/setting-component";
|
||||
|
||||
export default Ember.Component.extend(BufferedContent, SettingComponent, {
|
||||
_save() {
|
||||
const setting = this.get('buffered');
|
||||
return SiteSetting.update(setting.get('setting'), setting.get('value'));
|
||||
const setting = this.get("buffered");
|
||||
return SiteSetting.update(setting.get("setting"), setting.get("value"));
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
||||
@computed("value")
|
||||
enabled: {
|
||||
get(value) {
|
||||
if (Ember.isEmpty(value)) { return false; }
|
||||
if (Ember.isEmpty(value)) {
|
||||
return false;
|
||||
}
|
||||
return value.toString() === "true";
|
||||
},
|
||||
set(value) {
|
||||
this.set("value", value ? "true" : "false");
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
|
||||
@computed("value")
|
||||
selectedCategories: {
|
||||
get(value) {
|
||||
@ -12,5 +11,4 @@ export default Ember.Component.extend({
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
actions: {
|
||||
showUploadModal({ value, setting }) {
|
||||
showModal("admin-uploaded-image-list", {
|
||||
admin: true,
|
||||
title: `admin.site_settings.${setting.setting}.title`,
|
||||
model: { value, setting }
|
||||
}).setProperties({
|
||||
save: v => this.set("value", v)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1,25 +1,27 @@
|
||||
import { on } from 'ember-addons/ember-computed-decorators';
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['site-text'],
|
||||
classNameBindings: ['siteText.overridden'],
|
||||
classNames: ["site-text"],
|
||||
classNameBindings: ["siteText.overridden"],
|
||||
|
||||
@on('didInsertElement')
|
||||
@on("didInsertElement")
|
||||
highlightTerm() {
|
||||
const term = this.get('term');
|
||||
const term = this.get("term");
|
||||
if (term) {
|
||||
this.$('.site-text-id, .site-text-value').highlight(term, {className: 'text-highlight'});
|
||||
this.$(".site-text-id, .site-text-value").highlight(term, {
|
||||
className: "text-highlight"
|
||||
});
|
||||
}
|
||||
this.$('.site-text-value').ellipsis();
|
||||
this.$(".site-text-value").ellipsis();
|
||||
},
|
||||
|
||||
click() {
|
||||
this.send('edit');
|
||||
this.send("edit");
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit() {
|
||||
this.sendAction('editAction', this.get('siteText'));
|
||||
this.sendAction("editAction", this.get("siteText"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
import DiscourseURL from 'discourse/lib/url';
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['table', 'staff-actions'],
|
||||
classNames: ["table", "staff-actions"],
|
||||
|
||||
willDestroyElement() {
|
||||
this.$().off('click.discourse-staff-logs');
|
||||
this.$().off("click.discourse-staff-logs");
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
this.$().on('click.discourse-staff-logs', '[data-link-post-id]', e => {
|
||||
let postId = $(e.target).attr('data-link-post-id');
|
||||
this.$().on("click.discourse-staff-logs", "[data-link-post-id]", e => {
|
||||
let postId = $(e.target).attr("data-link-post-id");
|
||||
|
||||
this.store.find('post', postId).then(p => {
|
||||
DiscourseURL.routeTo(p.get('url'));
|
||||
this.store.find("post", postId).then(p => {
|
||||
DiscourseURL.routeTo(p.get("url"));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: ''
|
||||
tagName: ""
|
||||
});
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import SettingComponent from 'admin/mixins/setting-component';
|
||||
import BufferedContent from "discourse/mixins/buffered-content";
|
||||
import SettingComponent from "admin/mixins/setting-component";
|
||||
|
||||
export default Ember.Component.extend(BufferedContent, SettingComponent, {
|
||||
layoutName: 'admin/templates/components/site-setting',
|
||||
layoutName: "admin/templates/components/site-setting",
|
||||
_save() {
|
||||
return this.get('model').saveSettings(this.get('setting.setting'), this.get('buffered.value'));
|
||||
return this.get("model").saveSettings(
|
||||
this.get("setting.setting"),
|
||||
this.get("buffered.value")
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
export default Ember.Component.extend({
|
||||
classNames: ["themes-list"],
|
||||
hasThemes: Ember.computed.gt("themes.length", 0)
|
||||
});
|
||||
@ -1,104 +1,95 @@
|
||||
import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNameBindings: [':value-list'],
|
||||
classNameBindings: [":value-list"],
|
||||
|
||||
_enableSorting: function() {
|
||||
const self = this;
|
||||
const placeholder = document.createElement("div");
|
||||
placeholder.className = "placeholder";
|
||||
inputInvalid: Ember.computed.empty("newValue"),
|
||||
|
||||
let dragging = null;
|
||||
let over = null;
|
||||
let nodePlacement;
|
||||
inputDelimiter: null,
|
||||
inputType: null,
|
||||
newValue: "",
|
||||
collection: null,
|
||||
values: null,
|
||||
noneKey: Ember.computed.alias("addKey"),
|
||||
|
||||
this.$().on('dragstart.discourse', '.values .value', function(e) {
|
||||
dragging = e.currentTarget;
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData("text/html", e.currentTarget);
|
||||
});
|
||||
|
||||
this.$().on('dragend.discourse', '.values .value', function() {
|
||||
Ember.run(function() {
|
||||
dragging.parentNode.removeChild(placeholder);
|
||||
dragging.style.display = 'block';
|
||||
|
||||
// Update data
|
||||
const from = Number(dragging.dataset.index);
|
||||
let to = Number(over.dataset.index);
|
||||
if (from < to) to--;
|
||||
if (nodePlacement === "after") to++;
|
||||
|
||||
const collection = self.get('collection');
|
||||
const fromObj = collection.objectAt(from);
|
||||
collection.replace(from, 1);
|
||||
collection.replace(to, 0, [fromObj]);
|
||||
self._saveValues();
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$().on('dragover.discourse', '.values', function(e) {
|
||||
e.preventDefault();
|
||||
dragging.style.display = 'none';
|
||||
if (e.target.className === "placeholder") { return; }
|
||||
over = e.target;
|
||||
|
||||
const relY = e.originalEvent.clientY - over.offsetTop;
|
||||
const height = over.offsetHeight / 2;
|
||||
const parent = e.target.parentNode;
|
||||
|
||||
if (relY > height) {
|
||||
nodePlacement = "after";
|
||||
parent.insertBefore(placeholder, e.target.nextElementSibling);
|
||||
} else if(relY < height) {
|
||||
nodePlacement = "before";
|
||||
parent.insertBefore(placeholder, e.target);
|
||||
}
|
||||
});
|
||||
}.on('didInsertElement'),
|
||||
|
||||
_removeSorting: function() {
|
||||
this.$().off('dragover.discourse').off('dragend.discourse').off('dragstart.discourse');
|
||||
}.on('willDestroyElement'),
|
||||
|
||||
_setupCollection: function() {
|
||||
const values = this.get('values');
|
||||
if (this.get('inputType') === "array") {
|
||||
this.set('collection', values || []);
|
||||
} else {
|
||||
this.set('collection', (values && values.length) ? values.split("\n") : []);
|
||||
@on("didReceiveAttrs")
|
||||
_setupCollection() {
|
||||
const values = this.get("values");
|
||||
if (this.get("inputType") === "array") {
|
||||
this.set("collection", values || []);
|
||||
return;
|
||||
}
|
||||
}.on('init').observes('values'),
|
||||
|
||||
_saveValues: function() {
|
||||
if (this.get('inputType') === "array") {
|
||||
this.set('values', this.get('collection'));
|
||||
} else {
|
||||
this.set('values', this.get('collection').join("\n"));
|
||||
}
|
||||
this.set(
|
||||
"collection",
|
||||
this._splitValues(values, this.get("inputDelimiter") || "\n")
|
||||
);
|
||||
},
|
||||
|
||||
inputInvalid: Ember.computed.empty('newValue'),
|
||||
@computed("choices.[]", "collection.[]")
|
||||
filteredChoices(choices, collection) {
|
||||
return Ember.makeArray(choices).filter(i => collection.indexOf(i) < 0);
|
||||
},
|
||||
|
||||
keyDown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.send('addValue');
|
||||
}
|
||||
keyDown(event) {
|
||||
if (event.keyCode === 13) this.send("addValue", this.get("newValue"));
|
||||
},
|
||||
|
||||
actions: {
|
||||
addValue() {
|
||||
if (this.get('inputInvalid')) { return; }
|
||||
changeValue(index, newValue) {
|
||||
this._replaceValue(index, newValue);
|
||||
},
|
||||
|
||||
this.get('collection').addObject(this.get('newValue'));
|
||||
this.set('newValue', '');
|
||||
addValue(newValue) {
|
||||
if (this.get("inputInvalid")) return;
|
||||
|
||||
this._saveValues();
|
||||
this.set("newValue", "");
|
||||
this._addValue(newValue);
|
||||
},
|
||||
|
||||
removeValue(value) {
|
||||
const collection = this.get('collection');
|
||||
collection.removeObject(value);
|
||||
this._saveValues();
|
||||
this._removeValue(value);
|
||||
},
|
||||
|
||||
selectChoice(choice) {
|
||||
this._addValue(choice);
|
||||
}
|
||||
},
|
||||
|
||||
_addValue(value) {
|
||||
this.get("collection").addObject(value);
|
||||
this._saveValues();
|
||||
},
|
||||
|
||||
_removeValue(value) {
|
||||
const collection = this.get("collection");
|
||||
collection.removeObject(value);
|
||||
this._saveValues();
|
||||
},
|
||||
|
||||
_replaceValue(index, newValue) {
|
||||
this.get("collection").replace(index, 1, [newValue]);
|
||||
this._saveValues();
|
||||
},
|
||||
|
||||
_saveValues() {
|
||||
if (this.get("inputType") === "array") {
|
||||
this.set("values", this.get("collection"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.set(
|
||||
"values",
|
||||
this.get("collection").join(this.get("inputDelimiter") || "\n")
|
||||
);
|
||||
},
|
||||
|
||||
_splitValues(values, delimiter) {
|
||||
if (values && values.length) {
|
||||
return values.split(delimiter).filter(x => x);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,65 +1,94 @@
|
||||
import WatchedWord from 'admin/models/watched-word';
|
||||
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import WatchedWord from "admin/models/watched-word";
|
||||
import {
|
||||
default as computed,
|
||||
on,
|
||||
observes
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['watched-word-form'],
|
||||
classNames: ["watched-word-form"],
|
||||
formSubmitted: false,
|
||||
actionKey: null,
|
||||
showMessage: false,
|
||||
|
||||
@computed('regularExpressions')
|
||||
@computed("regularExpressions")
|
||||
placeholderKey(regularExpressions) {
|
||||
return "admin.watched_words.form.placeholder" +
|
||||
(regularExpressions ? "_regexp" : "");
|
||||
return (
|
||||
"admin.watched_words.form.placeholder" +
|
||||
(regularExpressions ? "_regexp" : "")
|
||||
);
|
||||
},
|
||||
|
||||
@observes('word')
|
||||
@observes("word")
|
||||
removeMessage() {
|
||||
if (this.get('showMessage') && !Ember.isEmpty(this.get('word'))) {
|
||||
this.set('showMessage', false);
|
||||
if (this.get("showMessage") && !Ember.isEmpty(this.get("word"))) {
|
||||
this.set("showMessage", false);
|
||||
}
|
||||
},
|
||||
|
||||
@computed('word')
|
||||
@computed("word")
|
||||
isUniqueWord(word) {
|
||||
const words = this.get("filteredContent") || [];
|
||||
const filtered = words.filter(content => content.action === this.get("actionKey"));
|
||||
return filtered.every(content => content.word.toLowerCase() !== word.toLowerCase());
|
||||
const filtered = words.filter(
|
||||
content => content.action === this.get("actionKey")
|
||||
);
|
||||
return filtered.every(
|
||||
content => content.word.toLowerCase() !== word.toLowerCase()
|
||||
);
|
||||
},
|
||||
|
||||
actions: {
|
||||
submit() {
|
||||
if (!this.get("isUniqueWord")) {
|
||||
this.setProperties({ showMessage: true, message: I18n.t('admin.watched_words.form.exists') });
|
||||
this.setProperties({
|
||||
showMessage: true,
|
||||
message: I18n.t("admin.watched_words.form.exists")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.get('formSubmitted')) {
|
||||
this.set('formSubmitted', true);
|
||||
if (!this.get("formSubmitted")) {
|
||||
this.set("formSubmitted", true);
|
||||
|
||||
const watchedWord = WatchedWord.create({ word: this.get('word'), action: this.get('actionKey') });
|
||||
|
||||
watchedWord.save().then(result => {
|
||||
this.setProperties({ word: '', formSubmitted: false, showMessage: true, message: I18n.t('admin.watched_words.form.success') });
|
||||
this.sendAction('action', WatchedWord.create(result));
|
||||
Ember.run.schedule('afterRender', () => this.$('.watched-word-input').focus());
|
||||
}).catch(e => {
|
||||
this.set('formSubmitted', false);
|
||||
const msg = (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) ?
|
||||
I18n.t("generic_error_with_reason", {error: e.jqXHR.responseJSON.errors.join('. ')}) :
|
||||
I18n.t("generic_error");
|
||||
bootbox.alert(msg, () => this.$('.watched-word-input').focus());
|
||||
const watchedWord = WatchedWord.create({
|
||||
word: this.get("word"),
|
||||
action: this.get("actionKey")
|
||||
});
|
||||
|
||||
watchedWord
|
||||
.save()
|
||||
.then(result => {
|
||||
this.setProperties({
|
||||
word: "",
|
||||
formSubmitted: false,
|
||||
showMessage: true,
|
||||
message: I18n.t("admin.watched_words.form.success")
|
||||
});
|
||||
this.sendAction("action", WatchedWord.create(result));
|
||||
Ember.run.schedule("afterRender", () =>
|
||||
this.$(".watched-word-input").focus()
|
||||
);
|
||||
})
|
||||
.catch(e => {
|
||||
this.set("formSubmitted", false);
|
||||
const msg =
|
||||
e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors
|
||||
? I18n.t("generic_error_with_reason", {
|
||||
error: e.jqXHR.responseJSON.errors.join(". ")
|
||||
})
|
||||
: I18n.t("generic_error");
|
||||
bootbox.alert(msg, () => this.$(".watched-word-input").focus());
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@on("didInsertElement")
|
||||
_init() {
|
||||
Ember.run.schedule('afterRender', () => {
|
||||
this.$('.watched-word-input').keydown(e => {
|
||||
Ember.run.schedule("afterRender", () => {
|
||||
this.$(".watched-word-input").keydown(e => {
|
||||
if (e.keyCode === 13) {
|
||||
this.send('submit');
|
||||
this.send("submit");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,16 +2,16 @@ import computed from "ember-addons/ember-computed-decorators";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
|
||||
export default Em.Component.extend(UploadMixin, {
|
||||
type: 'csv',
|
||||
classNames: 'watched-words-uploader',
|
||||
uploadUrl: '/admin/logs/watched_words/upload',
|
||||
type: "csv",
|
||||
classNames: "watched-words-uploader",
|
||||
uploadUrl: "/admin/logs/watched_words/upload",
|
||||
addDisabled: Em.computed.alias("uploading"),
|
||||
|
||||
validateUploadedFilesOptions() {
|
||||
return { csvOnly: true };
|
||||
},
|
||||
|
||||
@computed('actionKey')
|
||||
@computed("actionKey")
|
||||
data(actionKey) {
|
||||
return { action_key: actionKey };
|
||||
},
|
||||
|
||||
@ -1,32 +1,40 @@
|
||||
import ApiKey from 'admin/models/api-key';
|
||||
import ApiKey from "admin/models/api-key";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
actions: {
|
||||
generateMasterKey() {
|
||||
ApiKey.generateMasterKey().then(key => this.get('model').pushObject(key));
|
||||
ApiKey.generateMasterKey().then(key => this.get("model").pushObject(key));
|
||||
},
|
||||
|
||||
regenerateKey(key) {
|
||||
bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
key.regenerate();
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.api.confirm_regen"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
key.regenerate();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
revokeKey(key) {
|
||||
bootbox.confirm(I18n.t("admin.api.confirm_revoke"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
key.revoke().then(() => this.get('model').removeObject(key));
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.api.confirm_revoke"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
key.revoke().then(() => this.get("model").removeObject(key));
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// Has a master key already been generated?
|
||||
hasMasterKey: function() {
|
||||
return !!this.get('model').findBy('user', null);
|
||||
}.property('model.[]')
|
||||
|
||||
return !!this.get("model").findBy("user", null);
|
||||
}.property("model.[]")
|
||||
});
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
adminBackups: Ember.inject.controller(),
|
||||
status: Ember.computed.alias('adminBackups.model'),
|
||||
status: Ember.computed.alias("adminBackups.model"),
|
||||
|
||||
uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(),
|
||||
uploadLabel: function() {
|
||||
return I18n.t("admin.backups.upload.label");
|
||||
}.property(),
|
||||
|
||||
restoreTitle: function() {
|
||||
if (!this.get('status.allowRestore')) {
|
||||
if (!this.get("status.allowRestore")) {
|
||||
return "admin.backups.operations.restore.is_disabled";
|
||||
} else if (this.get("status.isOperationRunning")) {
|
||||
return "admin.backups.operations.is_running";
|
||||
@ -17,7 +19,6 @@ export default Ember.Controller.extend({
|
||||
}.property("status.{allowRestore,isOperationRunning}"),
|
||||
|
||||
actions: {
|
||||
|
||||
toggleReadOnlyMode() {
|
||||
var self = this;
|
||||
if (!this.site.get("isReadOnly")) {
|
||||
@ -38,9 +39,8 @@ export default Ember.Controller.extend({
|
||||
},
|
||||
|
||||
download(backup) {
|
||||
let link = backup.get('filename');
|
||||
ajax("/admin/backups/" + link, { type: "PUT" })
|
||||
.then(() => {
|
||||
let link = backup.get("filename");
|
||||
ajax("/admin/backups/" + link, { type: "PUT" }).then(() => {
|
||||
bootbox.alert(I18n.t("admin.backups.operations.download.alert"));
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
export default Ember.Controller.extend({
|
||||
noOperationIsRunning: Ember.computed.not("model.isOperationRunning"),
|
||||
rollbackEnabled: Ember.computed.and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning"),
|
||||
rollbackEnabled: Ember.computed.and(
|
||||
"model.canRollback",
|
||||
"model.restoreEnabled",
|
||||
"noOperationIsRunning"
|
||||
),
|
||||
rollbackDisabled: Ember.computed.not("rollbackEnabled")
|
||||
});
|
||||
|
||||
@ -1,108 +1,139 @@
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import { propertyNotEqual } from 'discourse/lib/computed';
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import BufferedContent from "discourse/mixins/buffered-content";
|
||||
import { propertyNotEqual } from "discourse/lib/computed";
|
||||
|
||||
export default Ember.Controller.extend(BufferedContent, {
|
||||
adminBadges: Ember.inject.controller(),
|
||||
saving: false,
|
||||
savingStatus: '',
|
||||
savingStatus: "",
|
||||
|
||||
badgeTypes: Ember.computed.alias('adminBadges.badgeTypes'),
|
||||
badgeGroupings: Ember.computed.alias('adminBadges.badgeGroupings'),
|
||||
badgeTriggers: Ember.computed.alias('adminBadges.badgeTriggers'),
|
||||
protectedSystemFields: Ember.computed.alias('adminBadges.protectedSystemFields'),
|
||||
badgeTypes: Ember.computed.alias("adminBadges.badgeTypes"),
|
||||
badgeGroupings: Ember.computed.alias("adminBadges.badgeGroupings"),
|
||||
badgeTriggers: Ember.computed.alias("adminBadges.badgeTriggers"),
|
||||
protectedSystemFields: Ember.computed.alias(
|
||||
"adminBadges.protectedSystemFields"
|
||||
),
|
||||
|
||||
readOnly: Ember.computed.alias('buffered.system'),
|
||||
showDisplayName: propertyNotEqual('name', 'displayName'),
|
||||
readOnly: Ember.computed.alias("buffered.system"),
|
||||
showDisplayName: propertyNotEqual("name", "displayName"),
|
||||
|
||||
hasQuery: function() {
|
||||
const bQuery = this.get('buffered.query');
|
||||
const bQuery = this.get("buffered.query");
|
||||
if (bQuery) {
|
||||
return bQuery.trim().length > 0;
|
||||
}
|
||||
const mQuery = this.get('model.query');
|
||||
const mQuery = this.get("model.query");
|
||||
return mQuery && mQuery.trim().length > 0;
|
||||
}.property('model.query', 'buffered.query'),
|
||||
}.property("model.query", "buffered.query"),
|
||||
|
||||
_resetSaving: function() {
|
||||
this.set('saving', false);
|
||||
this.set('savingStatus', '');
|
||||
}.observes('model.id'),
|
||||
this.set("saving", false);
|
||||
this.set("savingStatus", "");
|
||||
}.observes("model.id"),
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
if (!this.get('saving')) {
|
||||
let fields = ['allow_title', 'multiple_grant',
|
||||
'listable', 'auto_revoke',
|
||||
'enabled', 'show_posts',
|
||||
'target_posts', 'name', 'description',
|
||||
'long_description',
|
||||
'icon', 'image', 'query', 'badge_grouping_id',
|
||||
'trigger', 'badge_type_id'];
|
||||
if (!this.get("saving")) {
|
||||
let fields = [
|
||||
"allow_title",
|
||||
"multiple_grant",
|
||||
"listable",
|
||||
"auto_revoke",
|
||||
"enabled",
|
||||
"show_posts",
|
||||
"target_posts",
|
||||
"name",
|
||||
"description",
|
||||
"long_description",
|
||||
"icon",
|
||||
"image",
|
||||
"query",
|
||||
"badge_grouping_id",
|
||||
"trigger",
|
||||
"badge_type_id"
|
||||
];
|
||||
|
||||
if (this.get('buffered.system')){
|
||||
var protectedFields = this.get('protectedSystemFields');
|
||||
fields = _.filter(fields, function(f){
|
||||
return !_.include(protectedFields,f);
|
||||
if (this.get("buffered.system")) {
|
||||
var protectedFields = this.get("protectedSystemFields");
|
||||
fields = _.filter(fields, function(f) {
|
||||
return !_.include(protectedFields, f);
|
||||
});
|
||||
}
|
||||
|
||||
this.set('saving', true);
|
||||
this.set('savingStatus', I18n.t('saving'));
|
||||
this.set("saving", true);
|
||||
this.set("savingStatus", I18n.t("saving"));
|
||||
|
||||
const boolFields = ['allow_title', 'multiple_grant',
|
||||
'listable', 'auto_revoke',
|
||||
'enabled', 'show_posts',
|
||||
'target_posts' ];
|
||||
const boolFields = [
|
||||
"allow_title",
|
||||
"multiple_grant",
|
||||
"listable",
|
||||
"auto_revoke",
|
||||
"enabled",
|
||||
"show_posts",
|
||||
"target_posts"
|
||||
];
|
||||
|
||||
const data = {};
|
||||
const buffered = this.get('buffered');
|
||||
fields.forEach(function(field){
|
||||
const buffered = this.get("buffered");
|
||||
fields.forEach(function(field) {
|
||||
var d = buffered.get(field);
|
||||
if (_.include(boolFields, field)) { d = !!d; }
|
||||
if (_.include(boolFields, field)) {
|
||||
d = !!d;
|
||||
}
|
||||
data[field] = d;
|
||||
});
|
||||
|
||||
const newBadge = !this.get('id');
|
||||
const model = this.get('model');
|
||||
this.get('model').save(data).then(() => {
|
||||
if (newBadge) {
|
||||
const adminBadges = this.get('adminBadges.model');
|
||||
if (!adminBadges.includes(model)) {
|
||||
adminBadges.pushObject(model);
|
||||
const newBadge = !this.get("id");
|
||||
const model = this.get("model");
|
||||
this.get("model")
|
||||
.save(data)
|
||||
.then(() => {
|
||||
if (newBadge) {
|
||||
const adminBadges = this.get("adminBadges.model");
|
||||
if (!adminBadges.includes(model)) {
|
||||
adminBadges.pushObject(model);
|
||||
}
|
||||
this.transitionToRoute("adminBadges.show", model.get("id"));
|
||||
} else {
|
||||
this.commitBuffer();
|
||||
this.set("savingStatus", I18n.t("saved"));
|
||||
}
|
||||
this.transitionToRoute('adminBadges.show', model.get('id'));
|
||||
} else {
|
||||
this.commitBuffer();
|
||||
this.set('savingStatus', I18n.t('saved'));
|
||||
}
|
||||
|
||||
}).catch(popupAjaxError).finally(() => {
|
||||
this.set('saving', false);
|
||||
this.set('savingStatus', '');
|
||||
});
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this.set("saving", false);
|
||||
this.set("savingStatus", "");
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy() {
|
||||
const adminBadges = this.get('adminBadges.model');
|
||||
const model = this.get('model');
|
||||
const adminBadges = this.get("adminBadges.model");
|
||||
const model = this.get("model");
|
||||
|
||||
if (!model.get('id')) {
|
||||
this.transitionToRoute('adminBadges.index');
|
||||
if (!model.get("id")) {
|
||||
this.transitionToRoute("adminBadges.index");
|
||||
return;
|
||||
}
|
||||
|
||||
return bootbox.confirm(I18n.t("admin.badges.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
model.destroy().then(() => {
|
||||
adminBadges.removeObject(model);
|
||||
this.transitionToRoute('adminBadges.index');
|
||||
}).catch(() => {
|
||||
bootbox.alert(I18n.t('generic_error'));
|
||||
});
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.badges.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
model
|
||||
.destroy()
|
||||
.then(() => {
|
||||
adminBadges.removeObject(model);
|
||||
this.transitionToRoute("adminBadges.index");
|
||||
})
|
||||
.catch(() => {
|
||||
bootbox.alert(I18n.t("generic_error"));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
@computed("model.colors","onlyOverridden")
|
||||
@computed("model.colors", "onlyOverridden")
|
||||
colors(allColors, onlyOverridden) {
|
||||
if (onlyOverridden) {
|
||||
return allColors.filter(color => color.get("overridden"));
|
||||
@ -11,7 +11,6 @@ export default Ember.Controller.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
revert: function(color) {
|
||||
color.revert();
|
||||
},
|
||||
@ -28,14 +27,20 @@ export default Ember.Controller.extend({
|
||||
let range = document.createRange();
|
||||
range.selectNode(area[0]);
|
||||
window.getSelection().addRange(range);
|
||||
let successful = document.execCommand('copy');
|
||||
let successful = document.execCommand("copy");
|
||||
if (successful) {
|
||||
this.set("model.savingStatus", I18n.t("admin.customize.copied_to_clipboard"));
|
||||
this.set(
|
||||
"model.savingStatus",
|
||||
I18n.t("admin.customize.copied_to_clipboard")
|
||||
);
|
||||
} else {
|
||||
this.set("model.savingStatus", I18n.t("admin.customize.copy_to_clipboard_error"));
|
||||
this.set(
|
||||
"model.savingStatus",
|
||||
I18n.t("admin.customize.copy_to_clipboard_error")
|
||||
);
|
||||
}
|
||||
|
||||
setTimeout(()=>{
|
||||
setTimeout(() => {
|
||||
this.set("model.savingStatus", null);
|
||||
}, 2000);
|
||||
|
||||
@ -46,29 +51,38 @@ export default Ember.Controller.extend({
|
||||
},
|
||||
|
||||
copy() {
|
||||
var newColorScheme = Em.copy(this.get('model'), true);
|
||||
newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + this.get('model.name'));
|
||||
newColorScheme.save().then(()=>{
|
||||
this.get('allColors').pushObject(newColorScheme);
|
||||
this.replaceRoute('adminCustomize.colors.show', newColorScheme);
|
||||
var newColorScheme = Em.copy(this.get("model"), true);
|
||||
newColorScheme.set(
|
||||
"name",
|
||||
I18n.t("admin.customize.colors.copy_name_prefix") +
|
||||
" " +
|
||||
this.get("model.name")
|
||||
);
|
||||
newColorScheme.save().then(() => {
|
||||
this.get("allColors").pushObject(newColorScheme);
|
||||
this.replaceRoute("adminCustomize.colors.show", newColorScheme);
|
||||
});
|
||||
},
|
||||
|
||||
save: function() {
|
||||
this.get('model').save();
|
||||
this.get("model").save();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
|
||||
const model = this.get('model');
|
||||
return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
model.destroy().then(()=>{
|
||||
this.get('allColors').removeObject(model);
|
||||
this.replaceRoute('adminCustomize.colors');
|
||||
});
|
||||
const model = this.get("model");
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.customize.colors.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
model.destroy().then(() => {
|
||||
this.get("allColors").removeObject(model);
|
||||
this.replaceRoute("adminCustomize.colors");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,41 +1,43 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
baseColorScheme: function() {
|
||||
return this.get('model').findBy('is_base', true);
|
||||
}.property('model.@each.id'),
|
||||
return this.get("model").findBy("is_base", true);
|
||||
}.property("model.@each.id"),
|
||||
|
||||
baseColorSchemes: function() {
|
||||
return this.get('model').filterBy('is_base', true);
|
||||
}.property('model.@each.id'),
|
||||
return this.get("model").filterBy("is_base", true);
|
||||
}.property("model.@each.id"),
|
||||
|
||||
baseColors: function() {
|
||||
var baseColorsHash = Em.Object.create({});
|
||||
_.each(this.get('baseColorScheme.colors'), function(color){
|
||||
baseColorsHash.set(color.get('name'), color);
|
||||
_.each(this.get("baseColorScheme.colors"), function(color) {
|
||||
baseColorsHash.set(color.get("name"), color);
|
||||
});
|
||||
return baseColorsHash;
|
||||
}.property('baseColorScheme'),
|
||||
}.property("baseColorScheme"),
|
||||
|
||||
actions: {
|
||||
|
||||
newColorSchemeWithBase(baseKey) {
|
||||
const base = this.get('baseColorSchemes').findBy('base_scheme_id', baseKey);
|
||||
const base = this.get("baseColorSchemes").findBy(
|
||||
"base_scheme_id",
|
||||
baseKey
|
||||
);
|
||||
const newColorScheme = Em.copy(base, true);
|
||||
newColorScheme.set('name', I18n.t('admin.customize.colors.new_name'));
|
||||
newColorScheme.set('base_scheme_id', base.get('base_scheme_id'));
|
||||
newColorScheme.save().then(()=>{
|
||||
this.get('model').pushObject(newColorScheme);
|
||||
newColorScheme.set('savingStatus', null);
|
||||
this.replaceRoute('adminCustomize.colors.show', newColorScheme);
|
||||
newColorScheme.set("name", I18n.t("admin.customize.colors.new_name"));
|
||||
newColorScheme.set("base_scheme_id", base.get("base_scheme_id"));
|
||||
newColorScheme.save().then(() => {
|
||||
this.get("model").pushObject(newColorScheme);
|
||||
newColorScheme.set("savingStatus", null);
|
||||
this.replaceRoute("adminCustomize.colors.show", newColorScheme);
|
||||
});
|
||||
},
|
||||
|
||||
newColorScheme() {
|
||||
showModal('admin-color-scheme-select-base', { model: this.get('baseColorSchemes'), admin: true});
|
||||
},
|
||||
|
||||
|
||||
showModal("admin-color-scheme-select-base", {
|
||||
model: this.get("baseColorSchemes"),
|
||||
admin: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,38 +1,47 @@
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { bufferedProperty } from 'discourse/mixins/buffered-content';
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
|
||||
export default Ember.Controller.extend(bufferedProperty('emailTemplate'), {
|
||||
export default Ember.Controller.extend(bufferedProperty("emailTemplate"), {
|
||||
saved: false,
|
||||
|
||||
hasMultipleSubjects: function() {
|
||||
const buffered = this.get('buffered');
|
||||
if (buffered.getProperties('subject')['subject']) {
|
||||
const buffered = this.get("buffered");
|
||||
if (buffered.getProperties("subject")["subject"]) {
|
||||
return false;
|
||||
} else {
|
||||
return buffered.getProperties('id')['id'];
|
||||
return buffered.getProperties("id")["id"];
|
||||
}
|
||||
}.property("buffered"),
|
||||
|
||||
actions: {
|
||||
saveChanges() {
|
||||
this.set('saved', false);
|
||||
const buffered = this.get('buffered');
|
||||
this.get('emailTemplate').save(buffered.getProperties('subject', 'body')).then(() => {
|
||||
this.set('saved', true);
|
||||
}).catch(popupAjaxError);
|
||||
this.set("saved", false);
|
||||
const buffered = this.get("buffered");
|
||||
this.get("emailTemplate")
|
||||
.save(buffered.getProperties("subject", "body"))
|
||||
.then(() => {
|
||||
this.set("saved", true);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
revertChanges() {
|
||||
this.set('saved', false);
|
||||
bootbox.confirm(I18n.t('admin.customize.email_templates.revert_confirm'), result => {
|
||||
if (result) {
|
||||
this.get('emailTemplate').revert().then(props => {
|
||||
const buffered = this.get('buffered');
|
||||
buffered.setProperties(props);
|
||||
this.commitBuffer();
|
||||
}).catch(popupAjaxError);
|
||||
this.set("saved", false);
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.email_templates.revert_confirm"),
|
||||
result => {
|
||||
if (result) {
|
||||
this.get("emailTemplate")
|
||||
.revert()
|
||||
.then(props => {
|
||||
const buffered = this.get("buffered");
|
||||
buffered.setProperties(props);
|
||||
this.commitBuffer();
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export default Ember.Controller.extend({
|
||||
titleSorting: ['title'],
|
||||
titleSorting: ["title"],
|
||||
emailTemplates: null,
|
||||
|
||||
sortedTemplates: Ember.computed.sort('emailTemplates', 'titleSorting')
|
||||
sortedTemplates: Ember.computed.sort("emailTemplates", "titleSorting")
|
||||
});
|
||||
|
||||
@ -1,66 +1,86 @@
|
||||
import { url } from 'discourse/lib/computed';
|
||||
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||
import { url } from "discourse/lib/computed";
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
maximized: false,
|
||||
section: null,
|
||||
|
||||
editRouteName: 'adminCustomizeThemes.edit',
|
||||
editRouteName: "adminCustomizeThemes.edit",
|
||||
|
||||
targets: [
|
||||
{ id: 0, name: 'common' },
|
||||
{ id: 1, name: 'desktop' },
|
||||
{ id: 2, name: 'mobile' },
|
||||
{ id: 3, name: 'settings' }
|
||||
{ id: 0, name: "common" },
|
||||
{ id: 1, name: "desktop" },
|
||||
{ id: 2, name: "mobile" },
|
||||
{ id: 3, name: "settings" }
|
||||
],
|
||||
|
||||
fieldsForTarget: function (target) {
|
||||
const common = ["scss", "head_tag", "header", "after_header", "body_tag", "footer"];
|
||||
switch(target) {
|
||||
case "common": return [...common, "embedded_scss"];
|
||||
case "desktop": return common;
|
||||
case "mobile": return common;
|
||||
case "settings": return ["yaml"];
|
||||
fieldsForTarget: function(target) {
|
||||
const common = [
|
||||
"scss",
|
||||
"head_tag",
|
||||
"header",
|
||||
"after_header",
|
||||
"body_tag",
|
||||
"footer"
|
||||
];
|
||||
switch (target) {
|
||||
case "common":
|
||||
return [...common, "embedded_scss"];
|
||||
case "desktop":
|
||||
return common;
|
||||
case "mobile":
|
||||
return common;
|
||||
case "settings":
|
||||
return ["yaml"];
|
||||
}
|
||||
},
|
||||
|
||||
@computed('onlyOverridden')
|
||||
@computed("onlyOverridden")
|
||||
showCommon() {
|
||||
return this.shouldShow('common');
|
||||
return this.shouldShow("common");
|
||||
},
|
||||
|
||||
@computed('onlyOverridden')
|
||||
@computed("onlyOverridden")
|
||||
showDesktop() {
|
||||
return this.shouldShow('desktop');
|
||||
return this.shouldShow("desktop");
|
||||
},
|
||||
|
||||
@computed('onlyOverridden')
|
||||
@computed("onlyOverridden")
|
||||
showMobile() {
|
||||
return this.shouldShow('mobile');
|
||||
return this.shouldShow("mobile");
|
||||
},
|
||||
|
||||
@computed('onlyOverridden', 'model.remote_theme')
|
||||
showSettings() {
|
||||
return false;
|
||||
},
|
||||
|
||||
@observes('onlyOverridden')
|
||||
@observes("onlyOverridden")
|
||||
onlyOverriddenChanged() {
|
||||
if (this.get('onlyOverridden')) {
|
||||
if (!this.get('model').hasEdited(this.get('currentTargetName'), this.get('fieldName'))) {
|
||||
let target = (this.get('showCommon') && 'common') ||
|
||||
(this.get('showDesktop') && 'desktop') ||
|
||||
(this.get('showMobile') && 'mobile');
|
||||
if (this.get("onlyOverridden")) {
|
||||
if (
|
||||
!this.get("model").hasEdited(
|
||||
this.get("currentTargetName"),
|
||||
this.get("fieldName")
|
||||
)
|
||||
) {
|
||||
let target =
|
||||
(this.get("showCommon") && "common") ||
|
||||
(this.get("showDesktop") && "desktop") ||
|
||||
(this.get("showMobile") && "mobile");
|
||||
|
||||
let fields = this.get('model.theme_fields');
|
||||
let field = fields && fields.find(f => (f.target === target));
|
||||
this.replaceRoute(this.get('editRouteName'), this.get('model.id'), target, field && field.name);
|
||||
let fields = this.get("model.theme_fields");
|
||||
let field = fields && fields.find(f => f.target === target);
|
||||
this.replaceRoute(
|
||||
this.get("editRouteName"),
|
||||
this.get("model.id"),
|
||||
target,
|
||||
field && field.name
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
shouldShow(target){
|
||||
if(!this.get("onlyOverridden")) {
|
||||
shouldShow(target) {
|
||||
if (!this.get("onlyOverridden")) {
|
||||
return true;
|
||||
}
|
||||
return this.get("model").hasEdited(target);
|
||||
@ -69,13 +89,13 @@ export default Ember.Controller.extend({
|
||||
currentTarget: 0,
|
||||
|
||||
setTargetName: function(name) {
|
||||
const target = this.get('targets').find(t => t.name === name);
|
||||
const target = this.get("targets").find(t => t.name === name);
|
||||
this.set("currentTarget", target && target.id);
|
||||
},
|
||||
|
||||
@computed("currentTarget")
|
||||
currentTargetName(id) {
|
||||
const target = this.get('targets').find(t => t.id === parseInt(id, 10));
|
||||
const target = this.get("targets").find(t => t.id === parseInt(id, 10));
|
||||
return target && target.name;
|
||||
},
|
||||
|
||||
@ -87,7 +107,7 @@ export default Ember.Controller.extend({
|
||||
|
||||
@computed("currentTargetName", "fieldName", "saving")
|
||||
error(target, fieldName) {
|
||||
return this.get('model').getError(target, fieldName);
|
||||
return this.get("model").getError(target, fieldName);
|
||||
},
|
||||
|
||||
@computed("fieldName", "currentTargetName")
|
||||
@ -116,9 +136,9 @@ export default Ember.Controller.extend({
|
||||
fields = fields.filter(name => model.hasEdited(targetName, name));
|
||||
}
|
||||
|
||||
return fields.map(name=>{
|
||||
return fields.map(name => {
|
||||
let hash = {
|
||||
key: (`admin.customize.theme.${name}.text`),
|
||||
key: `admin.customize.theme.${name}.text`,
|
||||
name: name
|
||||
};
|
||||
|
||||
@ -132,30 +152,36 @@ export default Ember.Controller.extend({
|
||||
});
|
||||
},
|
||||
|
||||
previewUrl: url('model.id', '/admin/themes/%@/preview'),
|
||||
previewUrl: url("model.id", "/admin/themes/%@/preview"),
|
||||
|
||||
maximizeIcon: function() {
|
||||
return this.get('maximized') ? 'compress' : 'expand';
|
||||
}.property('maximized'),
|
||||
return this.get("maximized") ? "compress" : "expand";
|
||||
}.property("maximized"),
|
||||
|
||||
saveButtonText: function() {
|
||||
return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save');
|
||||
}.property('model.isSaving'),
|
||||
return this.get("model.isSaving")
|
||||
? I18n.t("saving")
|
||||
: I18n.t("admin.customize.save");
|
||||
}.property("model.isSaving"),
|
||||
|
||||
saveDisabled: function() {
|
||||
return !this.get('model.changed') || this.get('model.isSaving');
|
||||
}.property('model.changed', 'model.isSaving'),
|
||||
return !this.get("model.changed") || this.get("model.isSaving");
|
||||
}.property("model.changed", "model.isSaving"),
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set('saving', true);
|
||||
this.get('model').saveChanges("theme_fields").finally(()=>{this.set('saving', false);});
|
||||
this.set("saving", true);
|
||||
this.get("model")
|
||||
.saveChanges("theme_fields")
|
||||
.finally(() => {
|
||||
this.set("saving", false);
|
||||
});
|
||||
},
|
||||
|
||||
toggleMaximize: function() {
|
||||
this.toggleProperty('maximized');
|
||||
Em.run.next(()=>{
|
||||
this.appEvents.trigger('ace:resize');
|
||||
this.toggleProperty("maximized");
|
||||
Em.run.next(() => {
|
||||
this.appEvents.trigger("ace:resize");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,78 +1,108 @@
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
import { url } from 'discourse/lib/computed';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import ThemeSettings from 'admin/models/theme-settings';
|
||||
import {
|
||||
default as computed,
|
||||
observes
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
import { url } from "discourse/lib/computed";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import ThemeSettings from "admin/models/theme-settings";
|
||||
|
||||
const THEME_UPLOAD_VAR = 2;
|
||||
const SETTINGS_TYPE_ID = 5;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
editRouteName: "adminCustomizeThemes.edit",
|
||||
|
||||
editRouteName: 'adminCustomizeThemes.edit',
|
||||
@observes("allowChildThemes")
|
||||
setSelectedThemeId() {
|
||||
const available = this.get("selectableChildThemes");
|
||||
if (
|
||||
!this.get("selectedChildThemeId") &&
|
||||
available &&
|
||||
available.length > 0
|
||||
) {
|
||||
this.set("selectedChildThemeId", available[0].get("id"));
|
||||
}
|
||||
},
|
||||
|
||||
@computed("model", "allThemes")
|
||||
@computed("model", "allThemes", "model.component")
|
||||
parentThemes(model, allThemes) {
|
||||
if (!model.get("component")) {
|
||||
return null;
|
||||
}
|
||||
let parents = allThemes.filter(theme =>
|
||||
_.contains(theme.get("childThemes"), model));
|
||||
_.contains(theme.get("childThemes"), model)
|
||||
);
|
||||
return parents.length === 0 ? null : parents;
|
||||
},
|
||||
|
||||
@computed("model.theme_fields.@each")
|
||||
hasEditedFields(fields) {
|
||||
return fields.any(f=>!Em.isBlank(f.value));
|
||||
return fields.any(
|
||||
f => !Em.isBlank(f.value) && f.type_id !== SETTINGS_TYPE_ID
|
||||
);
|
||||
},
|
||||
|
||||
@computed('model.theme_fields.@each')
|
||||
@computed("model.theme_fields.@each")
|
||||
editedDescriptions(fields) {
|
||||
let descriptions = [];
|
||||
let description = target => {
|
||||
let current = fields.filter(field => field.target === target && !Em.isBlank(field.value));
|
||||
let current = fields.filter(
|
||||
field => field.target === target && !Em.isBlank(field.value)
|
||||
);
|
||||
if (current.length > 0) {
|
||||
let text = I18n.t('admin.customize.theme.'+target);
|
||||
let localized = current.map(f=>I18n.t('admin.customize.theme.'+f.name + '.text'));
|
||||
let text = I18n.t("admin.customize.theme." + target);
|
||||
let localized = current.map(f =>
|
||||
I18n.t("admin.customize.theme." + f.name + ".text")
|
||||
);
|
||||
return text + ": " + localized.join(" , ");
|
||||
}
|
||||
};
|
||||
['common', 'desktop', 'mobile'].forEach(target => {
|
||||
["common", "desktop", "mobile"].forEach(target => {
|
||||
descriptions.push(description(target));
|
||||
});
|
||||
return descriptions.reject(d=>Em.isBlank(d));
|
||||
return descriptions.reject(d => Em.isBlank(d));
|
||||
},
|
||||
|
||||
previewUrl: url('model.id', '/admin/themes/%@/preview'),
|
||||
previewUrl: url("model.id", "/admin/themes/%@/preview"),
|
||||
|
||||
@computed("colorSchemeId", "model.color_scheme_id")
|
||||
colorSchemeChanged(colorSchemeId, existingId) {
|
||||
colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId);
|
||||
return colorSchemeId !== existingId;
|
||||
return colorSchemeId !== existingId;
|
||||
},
|
||||
|
||||
@computed("availableChildThemes", "model.childThemes.@each", "model", "allowChildThemes")
|
||||
selectableChildThemes(available, childThemes, model, allowChildThemes) {
|
||||
@computed(
|
||||
"availableChildThemes",
|
||||
"model.childThemes.@each",
|
||||
"model",
|
||||
"allowChildThemes"
|
||||
)
|
||||
selectableChildThemes(available, childThemes, allowChildThemes) {
|
||||
if (!allowChildThemes && (!childThemes || childThemes.length === 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let themes = [];
|
||||
available.forEach(t=> {
|
||||
if (!childThemes || (childThemes.indexOf(t) === -1)) {
|
||||
available.forEach(t => {
|
||||
if (!childThemes || childThemes.indexOf(t) === -1) {
|
||||
themes.push(t);
|
||||
};
|
||||
}
|
||||
});
|
||||
return themes.length === 0 ? null : themes;
|
||||
},
|
||||
|
||||
@computed("allThemes", "allThemes.length", "model")
|
||||
availableChildThemes(allThemes, count) {
|
||||
if (count === 1) {
|
||||
@computed("allThemes", "allThemes.length", "model.component", "model")
|
||||
availableChildThemes(allThemes, count, component) {
|
||||
if (count === 1 || component) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let excludeIds = [this.get("model.id")];
|
||||
const themeId = this.get("model.id");
|
||||
|
||||
let themes = [];
|
||||
allThemes.forEach(theme => {
|
||||
if (excludeIds.indexOf(theme.get("id")) === -1) {
|
||||
if (themeId !== theme.get("id") && theme.get("component")) {
|
||||
themes.push(theme);
|
||||
}
|
||||
});
|
||||
@ -80,6 +110,12 @@ export default Ember.Controller.extend({
|
||||
return themes;
|
||||
},
|
||||
|
||||
@computed("model.component")
|
||||
switchKey(component) {
|
||||
const type = component ? "component" : "theme";
|
||||
return `admin.customize.theme.switch_${type}`;
|
||||
},
|
||||
|
||||
@computed("model.settings")
|
||||
settings(settings) {
|
||||
return settings.map(setting => ThemeSettings.create(setting));
|
||||
@ -90,44 +126,48 @@ export default Ember.Controller.extend({
|
||||
return settings.length > 0;
|
||||
},
|
||||
|
||||
downloadUrl: url('model.id', '/admin/themes/%@'),
|
||||
downloadUrl: url("model.id", "/admin/themes/%@"),
|
||||
|
||||
actions: {
|
||||
|
||||
updateToLatest() {
|
||||
this.set("updatingRemote", true);
|
||||
this.get("model").updateToLatest()
|
||||
this.get("model")
|
||||
.updateToLatest()
|
||||
.catch(popupAjaxError)
|
||||
.finally(()=>{
|
||||
.finally(() => {
|
||||
this.set("updatingRemote", false);
|
||||
});
|
||||
},
|
||||
|
||||
checkForThemeUpdates() {
|
||||
this.set("updatingRemote", true);
|
||||
this.get("model").checkForUpdates()
|
||||
this.get("model")
|
||||
.checkForUpdates()
|
||||
.catch(popupAjaxError)
|
||||
.finally(()=>{
|
||||
.finally(() => {
|
||||
this.set("updatingRemote", false);
|
||||
});
|
||||
},
|
||||
|
||||
addUploadModal() {
|
||||
showModal('admin-add-upload', {admin: true, name: ''});
|
||||
showModal("admin-add-upload", { admin: true, name: "" });
|
||||
},
|
||||
|
||||
addUpload(info) {
|
||||
let model = this.get("model");
|
||||
model.setField('common', info.name, '', info.upload_id, THEME_UPLOAD_VAR);
|
||||
model.saveChanges('theme_fields').catch(e => popupAjaxError(e));
|
||||
model.setField("common", info.name, "", info.upload_id, THEME_UPLOAD_VAR);
|
||||
model.saveChanges("theme_fields").catch(e => popupAjaxError(e));
|
||||
},
|
||||
|
||||
cancelChangeScheme() {
|
||||
this.set("colorSchemeId", this.get("model.color_scheme_id"));
|
||||
},
|
||||
changeScheme(){
|
||||
changeScheme() {
|
||||
let schemeId = this.get("colorSchemeId");
|
||||
this.set("model.color_scheme_id", schemeId === null ? null : parseInt(schemeId));
|
||||
this.set(
|
||||
"model.color_scheme_id",
|
||||
schemeId === null ? null : parseInt(schemeId)
|
||||
);
|
||||
this.get("model").saveChanges("color_scheme_id");
|
||||
},
|
||||
startEditingName() {
|
||||
@ -144,14 +184,23 @@ export default Ember.Controller.extend({
|
||||
},
|
||||
|
||||
editTheme() {
|
||||
let edit = ()=>this.transitionToRoute(this.get('editRouteName'), this.get('model.id'), 'common', 'scss');
|
||||
let edit = () =>
|
||||
this.transitionToRoute(
|
||||
this.get("editRouteName"),
|
||||
this.get("model.id"),
|
||||
"common",
|
||||
"scss"
|
||||
);
|
||||
|
||||
if (this.get("model.remote_theme")) {
|
||||
bootbox.confirm(I18n.t("admin.customize.theme.edit_confirm"), result => {
|
||||
if (result) {
|
||||
edit();
|
||||
}
|
||||
});
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.theme.edit_confirm"),
|
||||
result => {
|
||||
if (result) {
|
||||
edit();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
edit();
|
||||
}
|
||||
@ -159,10 +208,10 @@ export default Ember.Controller.extend({
|
||||
|
||||
applyDefault() {
|
||||
const model = this.get("model");
|
||||
model.saveChanges("default").then(()=>{
|
||||
model.saveChanges("default").then(() => {
|
||||
if (model.get("default")) {
|
||||
this.get("allThemes").forEach(theme=>{
|
||||
if (theme !== model && theme.get('default')) {
|
||||
this.get("allThemes").forEach(theme => {
|
||||
if (theme !== model && theme.get("default")) {
|
||||
theme.set("default", false);
|
||||
}
|
||||
});
|
||||
@ -182,13 +231,15 @@ export default Ember.Controller.extend({
|
||||
|
||||
removeUpload(upload) {
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.customize.theme.delete_upload_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
this.get("model").removeField(upload);
|
||||
}
|
||||
});
|
||||
I18n.t("admin.customize.theme.delete_upload_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
this.get("model").removeField(upload);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
removeChildTheme(theme) {
|
||||
@ -196,17 +247,47 @@ export default Ember.Controller.extend({
|
||||
},
|
||||
|
||||
destroy() {
|
||||
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
const model = this.get('model');
|
||||
model.destroyRecord().then(() => {
|
||||
this.get('allThemes').removeObject(model);
|
||||
this.transitionToRoute('adminCustomizeThemes');
|
||||
});
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.customize.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
const model = this.get("model");
|
||||
model.destroyRecord().then(() => {
|
||||
this.get("allThemes").removeObject(model);
|
||||
this.transitionToRoute("adminCustomizeThemes");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
switchType() {
|
||||
return bootbox.confirm(
|
||||
I18n.t(`${this.get("switchKey")}_alert`),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
result => {
|
||||
if (result) {
|
||||
const model = this.get("model");
|
||||
model.set("component", !model.get("component"));
|
||||
model
|
||||
.saveChanges("component")
|
||||
.then(() => {
|
||||
this.set("colorSchemeId", null);
|
||||
model.setProperties({
|
||||
default: false,
|
||||
color_scheme_id: null,
|
||||
user_selectable: false,
|
||||
child_themes: [],
|
||||
childThemes: []
|
||||
});
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,10 +1,19 @@
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
@computed('model', 'model.@each')
|
||||
sortedThemes(themes) {
|
||||
return _.sortBy(themes.content, t => {
|
||||
return [!t.get("default"), !t.get("user_selectable"), t.get("name").toLowerCase()];
|
||||
@computed("model", "model.@each", "model.@each.component")
|
||||
fullThemes(themes) {
|
||||
return _.sortBy(themes.filter(t => !t.get("component")), t => {
|
||||
return [
|
||||
!t.get("default"),
|
||||
!t.get("user_selectable"),
|
||||
t.get("name").toLowerCase()
|
||||
];
|
||||
});
|
||||
},
|
||||
|
||||
@computed("model", "model.@each", "model.@each.component")
|
||||
childThemes(themes) {
|
||||
return themes.filter(t => t.get("component"));
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,134 @@
|
||||
import { setting } from "discourse/lib/computed";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import AdminDashboardNext from "admin/models/admin-dashboard-next";
|
||||
import Report from "admin/models/report";
|
||||
import PeriodComputationMixin from "admin/mixins/period-computation";
|
||||
|
||||
function staticReport(reportType) {
|
||||
return function() {
|
||||
return Ember.makeArray(this.get("reports")).find(
|
||||
report => report.type === reportType
|
||||
);
|
||||
}.property("reports.[]");
|
||||
}
|
||||
|
||||
export default Ember.Controller.extend(PeriodComputationMixin, {
|
||||
isLoading: false,
|
||||
dashboardFetchedAt: null,
|
||||
exceptionController: Ember.inject.controller("exception"),
|
||||
diskSpace: Ember.computed.alias("model.attributes.disk_space"),
|
||||
logSearchQueriesEnabled: setting("log_search_queries"),
|
||||
lastBackupTakenAt: Ember.computed.alias(
|
||||
"model.attributes.last_backup_taken_at"
|
||||
),
|
||||
shouldDisplayDurability: Ember.computed.and("diskSpace"),
|
||||
|
||||
@computed
|
||||
activityMetrics() {
|
||||
return [
|
||||
"page_view_total_reqs",
|
||||
"visits",
|
||||
"time_to_first_response",
|
||||
"likes",
|
||||
"flags",
|
||||
"user_to_user_private_messages_with_replies"
|
||||
];
|
||||
},
|
||||
|
||||
@computed
|
||||
activityMetricsFilters() {
|
||||
return {
|
||||
startDate: this.get("lastMonth"),
|
||||
endDate: this.get("today")
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
topReferredTopicsOptions() {
|
||||
return {
|
||||
table: { total: false, limit: 8 }
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
topReferredTopicsFilters() {
|
||||
return {
|
||||
startDate: moment()
|
||||
.subtract(6, "days")
|
||||
.startOf("day"),
|
||||
endDate: this.get("today")
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
trendingSearchFilters() {
|
||||
return {
|
||||
startDate: moment()
|
||||
.subtract(6, "days")
|
||||
.startOf("day"),
|
||||
endDate: this.get("today")
|
||||
};
|
||||
},
|
||||
|
||||
@computed
|
||||
trendingSearchOptions() {
|
||||
return {
|
||||
table: { total: false, limit: 8 }
|
||||
};
|
||||
},
|
||||
|
||||
usersByTypeReport: staticReport("users_by_type"),
|
||||
usersByTrustLevelReport: staticReport("users_by_trust_level"),
|
||||
|
||||
fetchDashboard() {
|
||||
if (this.get("isLoading")) return;
|
||||
|
||||
if (
|
||||
!this.get("dashboardFetchedAt") ||
|
||||
moment()
|
||||
.subtract(30, "minutes")
|
||||
.toDate() > this.get("dashboardFetchedAt")
|
||||
) {
|
||||
this.set("isLoading", true);
|
||||
|
||||
AdminDashboardNext.fetchGeneral()
|
||||
.then(adminDashboardNextModel => {
|
||||
this.setProperties({
|
||||
dashboardFetchedAt: new Date(),
|
||||
model: adminDashboardNextModel,
|
||||
reports: Ember.makeArray(adminDashboardNextModel.reports).map(x =>
|
||||
Report.create(x)
|
||||
)
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
this.get("exceptionController").set("thrown", e.jqXHR);
|
||||
this.replaceRoute("exception");
|
||||
})
|
||||
.finally(() => this.set("isLoading", false));
|
||||
}
|
||||
},
|
||||
|
||||
@computed("startDate", "endDate")
|
||||
filters(startDate, endDate) {
|
||||
return { startDate, endDate };
|
||||
},
|
||||
|
||||
@computed("model.attributes.updated_at")
|
||||
updatedTimestamp(updatedAt) {
|
||||
return moment(updatedAt)
|
||||
.tz(moment.tz.guess())
|
||||
.format("LLL");
|
||||
},
|
||||
|
||||
@computed("lastBackupTakenAt")
|
||||
backupTimestamp(lastBackupTakenAt) {
|
||||
return moment(lastBackupTakenAt)
|
||||
.tz(moment.tz.guess())
|
||||
.format("LLL");
|
||||
},
|
||||
|
||||
_reportsForPeriodURL(period) {
|
||||
return Discourse.getURL(`/admin?period=${period}`);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import PeriodComputationMixin from "admin/mixins/period-computation";
|
||||
|
||||
export default Ember.Controller.extend(PeriodComputationMixin, {
|
||||
@computed
|
||||
flagsStatusOptions() {
|
||||
return {
|
||||
table: {
|
||||
total: false,
|
||||
perPage: 10
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@computed("startDate", "endDate")
|
||||
filters(startDate, endDate) {
|
||||
return { startDate, endDate };
|
||||
},
|
||||
|
||||
@computed("lastWeek", "endDate")
|
||||
lastWeekfilters(startDate, endDate) {
|
||||
return { startDate, endDate };
|
||||
},
|
||||
|
||||
_reportsForPeriodURL(period) {
|
||||
return Discourse.getURL(`/admin/dashboard/moderation?period=${period}`);
|
||||
}
|
||||
});
|
||||
@ -1,127 +1,90 @@
|
||||
import { setting } from "discourse/lib/computed";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import AdminDashboardNext from "admin/models/admin-dashboard-next";
|
||||
import Report from "admin/models/report";
|
||||
import VersionCheck from "admin/models/version-check";
|
||||
|
||||
const PROBLEMS_CHECK_MINUTES = 1;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
queryParams: ["period"],
|
||||
period: "monthly",
|
||||
isLoading: false,
|
||||
dashboardFetchedAt: null,
|
||||
exceptionController: Ember.inject.controller("exception"),
|
||||
showVersionChecks: setting("version_checks"),
|
||||
diskSpace: Ember.computed.alias("model.attributes.disk_space"),
|
||||
logSearchQueriesEnabled: setting("log_search_queries"),
|
||||
availablePeriods: ["yearly", "quarterly", "monthly", "weekly"],
|
||||
|
||||
@computed("problems.length")
|
||||
foundProblems(problemsLength) {
|
||||
return this.currentUser.get("admin") && (problemsLength || 0) > 0;
|
||||
},
|
||||
|
||||
fetchDashboard() {
|
||||
if (this.get("isLoading")) return;
|
||||
fetchProblems() {
|
||||
if (this.get("isLoadingProblems")) return;
|
||||
|
||||
if (!this.get("dashboardFetchedAt") || moment().subtract(30, "minutes").toDate() > this.get("dashboardFetchedAt")) {
|
||||
this.set("isLoading", true);
|
||||
|
||||
const versionChecks = this.siteSettings.version_checks;
|
||||
|
||||
AdminDashboardNext.find().then(adminDashboardNextModel => {
|
||||
|
||||
if (versionChecks) {
|
||||
this.set("versionCheck", VersionCheck.create(adminDashboardNextModel.version_check));
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
dashboardFetchedAt: new Date(),
|
||||
model: adminDashboardNextModel,
|
||||
reports: adminDashboardNextModel.reports.map(x => Report.create(x))
|
||||
});
|
||||
}).catch(e => {
|
||||
this.get("exceptionController").set("thrown", e.jqXHR);
|
||||
this.replaceRoute("exception");
|
||||
}).finally(() => {
|
||||
this.set("isLoading", false);
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.get("problemsFetchedAt") || moment().subtract(PROBLEMS_CHECK_MINUTES, "minutes").toDate() > this.get("problemsFetchedAt")) {
|
||||
this.loadProblems();
|
||||
if (
|
||||
!this.get("problemsFetchedAt") ||
|
||||
moment()
|
||||
.subtract(PROBLEMS_CHECK_MINUTES, "minutes")
|
||||
.toDate() > this.get("problemsFetchedAt")
|
||||
) {
|
||||
this._loadProblems();
|
||||
}
|
||||
},
|
||||
|
||||
loadProblems() {
|
||||
this.set("loadingProblems", true);
|
||||
this.set("problemsFetchedAt", new Date());
|
||||
AdminDashboardNext.fetchProblems().then(d => {
|
||||
this.set("problems", d.problems);
|
||||
}).finally(() => {
|
||||
this.set("loadingProblems", false);
|
||||
fetchDashboard() {
|
||||
const versionChecks = this.siteSettings.version_checks;
|
||||
|
||||
if (this.get("isLoading") || !versionChecks) return;
|
||||
|
||||
if (
|
||||
!this.get("dashboardFetchedAt") ||
|
||||
moment()
|
||||
.subtract(30, "minutes")
|
||||
.toDate() > this.get("dashboardFetchedAt")
|
||||
) {
|
||||
this.set("isLoading", true);
|
||||
|
||||
AdminDashboardNext.fetch()
|
||||
.then(model => {
|
||||
let properties = {
|
||||
dashboardFetchedAt: new Date()
|
||||
};
|
||||
|
||||
if (versionChecks) {
|
||||
properties.versionCheck = VersionCheck.create(model.version_check);
|
||||
}
|
||||
|
||||
this.setProperties(properties);
|
||||
})
|
||||
.catch(e => {
|
||||
this.get("exceptionController").set("thrown", e.jqXHR);
|
||||
this.replaceRoute("exception");
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("isLoading", false);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_loadProblems() {
|
||||
this.setProperties({
|
||||
loadingProblems: true,
|
||||
problemsFetchedAt: new Date()
|
||||
});
|
||||
|
||||
AdminDashboardNext.fetchProblems()
|
||||
.then(model => this.set("problems", model.problems))
|
||||
.finally(() => this.set("loadingProblems", false));
|
||||
},
|
||||
|
||||
@computed("problemsFetchedAt")
|
||||
problemsTimestamp(problemsFetchedAt) {
|
||||
return moment(problemsFetchedAt).locale("en").format("LLL");
|
||||
},
|
||||
|
||||
@computed("period")
|
||||
startDate(period) {
|
||||
let fullDay = moment().locale("en").utc().subtract(1, "day");
|
||||
|
||||
switch (period) {
|
||||
case "yearly":
|
||||
return fullDay.subtract(1, "year").startOf("day");
|
||||
break;
|
||||
case "quarterly":
|
||||
return fullDay.subtract(3, "month").startOf("day");
|
||||
break;
|
||||
case "weekly":
|
||||
return fullDay.subtract(1, "week").startOf("day");
|
||||
break;
|
||||
case "monthly":
|
||||
return fullDay.subtract(1, "month").startOf("day");
|
||||
break;
|
||||
default:
|
||||
return fullDay.subtract(1, "month").startOf("day");
|
||||
}
|
||||
},
|
||||
|
||||
@computed()
|
||||
lastWeek() {
|
||||
return moment().locale("en").utc().endOf("day").subtract(1, "week");
|
||||
},
|
||||
|
||||
@computed()
|
||||
endDate() {
|
||||
return moment().locale("en").utc().subtract(1, "day").endOf("day");
|
||||
},
|
||||
|
||||
@computed("model.attributes.updated_at")
|
||||
updatedTimestamp(updatedAt) {
|
||||
return moment(updatedAt).format("LLL");
|
||||
},
|
||||
|
||||
@computed("model.attributes.last_backup_taken_at")
|
||||
backupTimestamp(lastBackupTakenAt) {
|
||||
return moment(lastBackupTakenAt).format("LLL");
|
||||
return moment(problemsFetchedAt)
|
||||
.locale("en")
|
||||
.format("LLL");
|
||||
},
|
||||
|
||||
actions: {
|
||||
changePeriod(period) {
|
||||
DiscourseURL.routeTo(this._reportsForPeriodURL(period));
|
||||
},
|
||||
refreshProblems() {
|
||||
this.loadProblems();
|
||||
},
|
||||
},
|
||||
|
||||
_reportsForPeriodURL(period) {
|
||||
return Discourse.getURL(`/admin?period=${period}`);
|
||||
this._loadProblems();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,50 +1,74 @@
|
||||
import AdminDashboard from 'admin/models/admin-dashboard';
|
||||
import Report from 'admin/models/report';
|
||||
import AdminUser from 'admin/models/admin-user';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import AdminDashboard from "admin/models/admin-dashboard";
|
||||
import Report from "admin/models/report";
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const ATTRIBUTES = [
|
||||
"disk_space",
|
||||
"admins",
|
||||
"moderators",
|
||||
"silenced",
|
||||
"suspended",
|
||||
"top_traffic_sources",
|
||||
"top_referred_topics",
|
||||
"updated_at"
|
||||
];
|
||||
|
||||
const ATTRIBUTES = [ 'disk_space','admins', 'moderators', 'silenced', 'suspended', 'top_traffic_sources',
|
||||
'top_referred_topics', 'updated_at'];
|
||||
|
||||
const REPORTS = [ 'global_reports', 'page_view_reports', 'private_message_reports', 'http_reports',
|
||||
'user_reports', 'mobile_reports'];
|
||||
const REPORTS = [
|
||||
"global_reports",
|
||||
"page_view_reports",
|
||||
"private_message_reports",
|
||||
"http_reports",
|
||||
"user_reports",
|
||||
"mobile_reports"
|
||||
];
|
||||
|
||||
// This controller supports the default interface when you enter the admin section.
|
||||
export default Ember.Controller.extend({
|
||||
loading: null,
|
||||
versionCheck: null,
|
||||
dashboardFetchedAt: null,
|
||||
exceptionController: Ember.inject.controller('exception'),
|
||||
exceptionController: Ember.inject.controller("exception"),
|
||||
|
||||
fetchDashboard() {
|
||||
if (!this.get('dashboardFetchedAt') || moment().subtract(30, 'minutes').toDate() > this.get('dashboardFetchedAt')) {
|
||||
this.set('loading', true);
|
||||
AdminDashboard.find().then(d => {
|
||||
this.set('dashboardFetchedAt', new Date());
|
||||
if (
|
||||
!this.get("dashboardFetchedAt") ||
|
||||
moment()
|
||||
.subtract(30, "minutes")
|
||||
.toDate() > this.get("dashboardFetchedAt")
|
||||
) {
|
||||
this.set("loading", true);
|
||||
AdminDashboard.find()
|
||||
.then(d => {
|
||||
this.set("dashboardFetchedAt", new Date());
|
||||
|
||||
REPORTS.forEach(name => this.set(name, d[name].map(r => Report.create(r))));
|
||||
REPORTS.forEach(name =>
|
||||
this.set(name, d[name].map(r => Report.create(r)))
|
||||
);
|
||||
|
||||
const topReferrers = d.top_referrers;
|
||||
if (topReferrers && topReferrers.data) {
|
||||
d.top_referrers.data = topReferrers.data.map(user => AdminUser.create(user));
|
||||
this.set('top_referrers', topReferrers);
|
||||
}
|
||||
const topReferrers = d.top_referrers;
|
||||
if (topReferrers && topReferrers.data) {
|
||||
d.top_referrers.data = topReferrers.data.map(user =>
|
||||
AdminUser.create(user)
|
||||
);
|
||||
this.set("top_referrers", topReferrers);
|
||||
}
|
||||
|
||||
ATTRIBUTES.forEach(a => this.set(a, d[a]));
|
||||
}).catch(e => {
|
||||
this.get('exceptionController').set('thrown', e.jqXHR);
|
||||
this.replaceRoute('exception');
|
||||
}).finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
ATTRIBUTES.forEach(a => this.set(a, d[a]));
|
||||
})
|
||||
.catch(e => {
|
||||
this.get("exceptionController").set("thrown", e.jqXHR);
|
||||
this.replaceRoute("exception");
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("loading", false);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@computed('updated_at')
|
||||
@computed("updated_at")
|
||||
updatedTimestamp(updatedAt) {
|
||||
return moment(updatedAt).format('LLL');
|
||||
return moment(updatedAt).format("LLL");
|
||||
},
|
||||
|
||||
actions: {
|
||||
@ -52,5 +76,4 @@ export default Ember.Controller.extend({
|
||||
this.set("showTrafficReport", true);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import AdminEmailLogsController from 'admin/controllers/admin-email-logs';
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import EmailLog from 'admin/models/email-log';
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import debounce from "discourse/lib/debounce";
|
||||
import EmailLog from "admin/models/email-log";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
filterEmailLogs: debounce(function() {
|
||||
EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs));
|
||||
}, 250).observes("filter.{user,address,type,skipped_reason}")
|
||||
}, 250).observes("filter.{user,address,type}")
|
||||
});
|
||||
|
||||
@ -1,21 +1,25 @@
|
||||
import IncomingEmail from 'admin/models/incoming-email';
|
||||
import IncomingEmail from "admin/models/incoming-email";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
|
||||
loadMore() {
|
||||
if (this.get("loading") || this.get("model.allLoaded")) { return; }
|
||||
this.set('loading', true);
|
||||
if (this.get("loading") || this.get("model.allLoaded")) {
|
||||
return;
|
||||
}
|
||||
this.set("loading", true);
|
||||
|
||||
IncomingEmail.findAll(this.get("filter"), this.get("model.length"))
|
||||
.then(incoming => {
|
||||
if (incoming.length < 50) { this.get("model").set("allLoaded", true); }
|
||||
this.get("model").addObjects(incoming);
|
||||
}).finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
.then(incoming => {
|
||||
if (incoming.length < 50) {
|
||||
this.get("model").set("allLoaded", true);
|
||||
}
|
||||
this.get("model").addObjects(incoming);
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("loading", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
/**
|
||||
Is the "send test email" button disabled?
|
||||
|
||||
@property sendTestEmailDisabled
|
||||
**/
|
||||
sendTestEmailDisabled: Em.computed.empty('testEmailAddress'),
|
||||
sendTestEmailDisabled: Em.computed.empty("testEmailAddress"),
|
||||
|
||||
/**
|
||||
Clears the 'sentTestEmail' property on successful send.
|
||||
@ -14,8 +13,8 @@ export default Ember.Controller.extend({
|
||||
@method testEmailAddressChanged
|
||||
**/
|
||||
testEmailAddressChanged: function() {
|
||||
this.set('sentTestEmail', false);
|
||||
}.observes('testEmailAddress'),
|
||||
this.set("sentTestEmail", false);
|
||||
}.observes("testEmailAddress"),
|
||||
|
||||
actions: {
|
||||
/**
|
||||
@ -29,23 +28,25 @@ export default Ember.Controller.extend({
|
||||
sentTestEmail: false
|
||||
});
|
||||
|
||||
var self = this;
|
||||
ajax("/admin/email/test", {
|
||||
type: 'POST',
|
||||
data: { email_address: this.get('testEmailAddress') }
|
||||
}).then(function () {
|
||||
self.set('sentTestEmail', true);
|
||||
}, function(e) {
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
bootbox.alert(I18n.t('admin.email.error', { server_error: e.responseJSON.errors[0] }));
|
||||
} else {
|
||||
bootbox.alert(I18n.t('admin.email.test_error'));
|
||||
}
|
||||
}).finally(function() {
|
||||
self.set('sendingEmail', false);
|
||||
});
|
||||
|
||||
type: "POST",
|
||||
data: { email_address: this.get("testEmailAddress") }
|
||||
})
|
||||
.then(response =>
|
||||
this.set("sentTestEmailMessage", response.send_test_email_message)
|
||||
)
|
||||
.catch(e => {
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
bootbox.alert(
|
||||
I18n.t("admin.email.error", {
|
||||
server_error: e.responseJSON.errors[0]
|
||||
})
|
||||
);
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.email.test_error"));
|
||||
}
|
||||
})
|
||||
.finally(() => this.set("sendingEmail", false));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
import EmailLog from 'admin/models/email-log';
|
||||
import EmailLog from "admin/models/email-log";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
if (this.get("loading") || this.get("model.allLoaded")) { return; }
|
||||
if (this.get("loading") || this.get("model.allLoaded")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set('loading', true);
|
||||
this.set("loading", true);
|
||||
return EmailLog.findAll(this.get("filter"), this.get("model.length"))
|
||||
.then(logs => {
|
||||
if (logs.length < 50) { this.get("model").set("allLoaded", true); }
|
||||
this.get("model").addObjects(logs);
|
||||
}).finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
.then(logs => {
|
||||
if (logs.length < 50) {
|
||||
this.get("model").set("allLoaded", true);
|
||||
}
|
||||
this.get("model").addObjects(logs);
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("loading", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,52 +1,60 @@
|
||||
import EmailPreview from 'admin/models/email-preview';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import EmailPreview from "admin/models/email-preview";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
username: null,
|
||||
lastSeen: null,
|
||||
|
||||
emailEmpty: Ember.computed.empty('email'),
|
||||
sendEmailDisabled: Ember.computed.or('emailEmpty', 'sendingEmail'),
|
||||
showSendEmailForm: Ember.computed.notEmpty('model.html_content'),
|
||||
htmlEmpty: Ember.computed.empty('model.html_content'),
|
||||
emailEmpty: Ember.computed.empty("email"),
|
||||
sendEmailDisabled: Ember.computed.or("emailEmpty", "sendingEmail"),
|
||||
showSendEmailForm: Ember.computed.notEmpty("model.html_content"),
|
||||
htmlEmpty: Ember.computed.empty("model.html_content"),
|
||||
|
||||
actions: {
|
||||
refresh() {
|
||||
const model = this.get('model');
|
||||
const model = this.get("model");
|
||||
|
||||
this.set('loading', true);
|
||||
this.set('sentEmail', false);
|
||||
this.set("loading", true);
|
||||
this.set("sentEmail", false);
|
||||
|
||||
let username = this.get('username');
|
||||
let username = this.get("username");
|
||||
if (!username) {
|
||||
username = this.currentUser.get('username');
|
||||
this.set('username', username);
|
||||
username = this.currentUser.get("username");
|
||||
this.set("username", username);
|
||||
}
|
||||
|
||||
EmailPreview.findDigest(username, this.get('lastSeen')).then(email => {
|
||||
model.setProperties(email.getProperties('html_content', 'text_content'));
|
||||
this.set('loading', false);
|
||||
EmailPreview.findDigest(username, this.get("lastSeen")).then(email => {
|
||||
model.setProperties(
|
||||
email.getProperties("html_content", "text_content")
|
||||
);
|
||||
this.set("loading", false);
|
||||
});
|
||||
},
|
||||
|
||||
toggleShowHtml() {
|
||||
this.toggleProperty('showHtml');
|
||||
this.toggleProperty("showHtml");
|
||||
},
|
||||
|
||||
sendEmail() {
|
||||
this.set('sendingEmail', true);
|
||||
this.set('sentEmail', false);
|
||||
this.set("sendingEmail", true);
|
||||
this.set("sentEmail", false);
|
||||
|
||||
EmailPreview.sendDigest(this.get('username'), this.get('lastSeen'), this.get('email')).then(result => {
|
||||
if (result.errors) {
|
||||
bootbox.alert(result.errors);
|
||||
} else {
|
||||
this.set('sentEmail', true);
|
||||
}
|
||||
}).catch(popupAjaxError).finally(() => {
|
||||
this.set('sendingEmail', false);
|
||||
});
|
||||
EmailPreview.sendDigest(
|
||||
this.get("username"),
|
||||
this.get("lastSeen"),
|
||||
this.get("email")
|
||||
)
|
||||
.then(result => {
|
||||
if (result.errors) {
|
||||
bootbox.alert(result.errors);
|
||||
} else {
|
||||
this.set("sentEmail", true);
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this.set("sendingEmail", false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings';
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import IncomingEmail from 'admin/models/incoming-email';
|
||||
import AdminEmailIncomingsController from "admin/controllers/admin-email-incomings";
|
||||
import debounce from "discourse/lib/debounce";
|
||||
import IncomingEmail from "admin/models/incoming-email";
|
||||
|
||||
export default AdminEmailIncomingsController.extend({
|
||||
filterIncomingEmails: debounce(function() {
|
||||
IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings));
|
||||
IncomingEmail.findAll(this.get("filter")).then(incomings =>
|
||||
this.set("model", incomings)
|
||||
);
|
||||
}, 250).observes("filter.{from,to,subject}")
|
||||
});
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings';
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import IncomingEmail from 'admin/models/incoming-email';
|
||||
import AdminEmailIncomingsController from "admin/controllers/admin-email-incomings";
|
||||
import debounce from "discourse/lib/debounce";
|
||||
import IncomingEmail from "admin/models/incoming-email";
|
||||
|
||||
export default AdminEmailIncomingsController.extend({
|
||||
filterIncomingEmails: debounce(function() {
|
||||
IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings));
|
||||
IncomingEmail.findAll(this.get("filter")).then(incomings =>
|
||||
this.set("model", incomings)
|
||||
);
|
||||
}, 250).observes("filter.{from,to,subject,error}")
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import AdminEmailLogsController from 'admin/controllers/admin-email-logs';
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import EmailLog from 'admin/models/email-log';
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import debounce from "discourse/lib/debounce";
|
||||
import EmailLog from "admin/models/email-log";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
filterEmailLogs: debounce(function() {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import AdminEmailLogsController from 'admin/controllers/admin-email-logs';
|
||||
import debounce from 'discourse/lib/debounce';
|
||||
import EmailLog from 'admin/models/email-log';
|
||||
import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import debounce from "discourse/lib/debounce";
|
||||
import EmailLog from "admin/models/email-log";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
filterEmailLogs: debounce(function() {
|
||||
EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs));
|
||||
}, 250).observes("filter.{user,address,type,skipped_reason}")
|
||||
}, 250).observes("filter.{user,address,type}")
|
||||
});
|
||||
|
||||
@ -1,22 +1,20 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
saved: false,
|
||||
embedding: null,
|
||||
|
||||
// show settings if we have at least one created host
|
||||
@computed('embedding.embeddable_hosts.@each.isCreated')
|
||||
@computed("embedding.embeddable_hosts.@each.isCreated")
|
||||
showSecondary() {
|
||||
const hosts = this.get('embedding.embeddable_hosts');
|
||||
return hosts.length && hosts.findBy('isCreated');
|
||||
const hosts = this.get("embedding.embeddable_hosts");
|
||||
return hosts.length && hosts.findBy("isCreated");
|
||||
},
|
||||
|
||||
@computed('embedding.base_url')
|
||||
@computed("embedding.base_url")
|
||||
embeddingCode(baseUrl) {
|
||||
|
||||
const html =
|
||||
`<div id='discourse-comments'></div>
|
||||
const html = `<div id='discourse-comments'></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
DiscourseEmbed = { discourseUrl: '${baseUrl}/',
|
||||
@ -34,20 +32,23 @@ export default Ember.Controller.extend({
|
||||
|
||||
actions: {
|
||||
saveChanges() {
|
||||
const embedding = this.get('embedding');
|
||||
const updates = embedding.getProperties(embedding.get('fields'));
|
||||
const embedding = this.get("embedding");
|
||||
const updates = embedding.getProperties(embedding.get("fields"));
|
||||
|
||||
this.set('saved', false);
|
||||
this.get('embedding').update(updates).then(() => this.set('saved', true)).catch(popupAjaxError);
|
||||
this.set("saved", false);
|
||||
this.get("embedding")
|
||||
.update(updates)
|
||||
.then(() => this.set("saved", true))
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
addHost() {
|
||||
const host = this.store.createRecord('embeddable-host');
|
||||
this.get('embedding.embeddable_hosts').pushObject(host);
|
||||
const host = this.store.createRecord("embeddable-host");
|
||||
this.get("embedding.embeddable_hosts").pushObject(host);
|
||||
},
|
||||
|
||||
deleteHost(host) {
|
||||
this.get('embedding.embeddable_hosts').removeObject(host);
|
||||
this.get("embedding.embeddable_hosts").removeObject(host);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user