mirror of
https://github.com/discourse/discourse.git
synced 2025-03-06 03:09:43 +00:00
Merge branch 'master' into fix-by-external
Conflicts: app/controllers/users_controller.rb
This commit is contained in:
commit
14d2b76354
@ -12,6 +12,7 @@ lib/javascripts/locale/
|
||||
lib/javascripts/messageformat.js
|
||||
lib/javascripts/moment.js
|
||||
lib/javascripts/moment_locale/
|
||||
lib/highlight_js/
|
||||
lib/es6_module_transpiler/support/es6-module-transpiler.js
|
||||
public/javascripts/
|
||||
spec/phantom_js/smoke_test.js
|
||||
@ -19,4 +20,5 @@ vendor/
|
||||
test/javascripts/helpers/
|
||||
test/javascripts/test_helper.js
|
||||
test/javascripts/test_helper.js
|
||||
app/assets/javascripts/ember-addons/
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
"$",
|
||||
"RSVP",
|
||||
"Discourse",
|
||||
"$LAB",
|
||||
"Em",
|
||||
"PreloadStore",
|
||||
"Handlebars",
|
||||
@ -15,7 +14,6 @@
|
||||
"moduleForComponent",
|
||||
"Pretender",
|
||||
"sandbox",
|
||||
"integration",
|
||||
"controllerFor",
|
||||
"test",
|
||||
"ok",
|
||||
@ -35,6 +33,10 @@
|
||||
"triggerEvent",
|
||||
"count",
|
||||
"exists",
|
||||
"visible",
|
||||
"invisible",
|
||||
"asyncRender",
|
||||
"selectDropdown",
|
||||
"asyncTestDiscourse",
|
||||
"fixture",
|
||||
"find",
|
||||
@ -47,6 +49,7 @@
|
||||
"parseHTML",
|
||||
"deepEqual",
|
||||
"notEqual",
|
||||
"define",
|
||||
"require",
|
||||
"requirejs",
|
||||
"hasModule",
|
||||
|
21
.travis.yml
21
.travis.yml
@ -5,20 +5,25 @@ env:
|
||||
- DISCOURSE_HOSTNAME=www.example.com
|
||||
- RUBY_GC_MALLOC_LIMIT=50000000
|
||||
matrix:
|
||||
- "RAILS42=1"
|
||||
- "RAILS_MASTER=1"
|
||||
- "RAILS_MASTER=0"
|
||||
|
||||
addons:
|
||||
postgresql: 9.3
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rvm: 2.0.0
|
||||
env: "RAILS_MASTER=1"
|
||||
- rvm: 2.1
|
||||
env: "RAILS_MASTER=1"
|
||||
- env: "RAILS_MASTER=1"
|
||||
- env: "RAILS42=1"
|
||||
- rvm: rbx-2
|
||||
fast_finish: true
|
||||
|
||||
rvm:
|
||||
- 2.0.0
|
||||
- 2.1
|
||||
- 2.2
|
||||
- rbx-2
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
@ -32,9 +37,11 @@ before_install:
|
||||
- jshint .
|
||||
|
||||
before_script:
|
||||
- psql -c 'create database discourse_test;' -U postgres
|
||||
- bundle exec rake db:migrate
|
||||
- bundle exec rake db:create db:migrate
|
||||
|
||||
bundler_args: --without development --deployment --retry=3 --jobs=3
|
||||
install:
|
||||
- bash -c "if [ '$RAILS42' == '1' ]; then bundle update --retry=3 --jobs=3 rails rails-observers; fi"
|
||||
- bash -c "if [ '$RAILS_MASTER' == '1' ]; then bundle update --retry=3 --jobs=3 arel rails rails-observers seed-fu; fi"
|
||||
- bash -c "if [ '$RAILS_MASTER' == '0' ]; then bundle install --without development --deployment --retry=3 --jobs=3; fi"
|
||||
|
||||
script: 'bundle exec rspec && bundle exec rake plugin:spec && bundle exec rake qunit:test'
|
||||
|
37
.tx/config
37
.tx/config
@ -1,92 +1,57 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = es_ES: es, fr_FR: fr, ko_KR: ko, pt_PT: pt
|
||||
|
||||
[discourse-org.clientenyml]
|
||||
file_filter = config/locales/client.<lang>.yml
|
||||
source_file = config/locales/client.en.yml
|
||||
source_lang = en
|
||||
trans.es_ES = config/locales/client.es.yml
|
||||
trans.fr_FR = config/locales/client.fr.yml
|
||||
trans.ko_KR = config/locales/client.ko.yml
|
||||
trans.pt_PT = config/locales/client.pt.yml
|
||||
type = YML
|
||||
|
||||
[discourse-org.serverenyml]
|
||||
file_filter = config/locales/server.<lang>.yml
|
||||
source_file = config/locales/server.en.yml
|
||||
source_lang = en
|
||||
trans.es_ES = config/locales/server.es.yml
|
||||
trans.fr_FR = config/locales/server.fr.yml
|
||||
trans.ko_KR = config/locales/server.ko.yml
|
||||
trans.pt_PT = config/locales/server.pt.yml
|
||||
type = YML
|
||||
|
||||
[discourse-org.pollclientenyml]
|
||||
file_filter = plugins/poll/config/locales/client.<lang>.yml
|
||||
source_file = plugins/poll/config/locales/client.en.yml
|
||||
source_lang = en
|
||||
trans.es_ES = plugins/poll/config/locales/client.es.yml
|
||||
trans.fr_FR = plugins/poll/config/locales/client.fr.yml
|
||||
trans.ko_KR = plugins/poll/config/locales/client.ko.yml
|
||||
trans.pt_PT = plugins/poll/config/locales/client.pt.yml
|
||||
type = YML
|
||||
|
||||
[discourse-org.pollserverenyml]
|
||||
file_filter = plugins/poll/config/locales/server.<lang>.yml
|
||||
source_file = plugins/poll/config/locales/server.en.yml
|
||||
source_lang = en
|
||||
trans.es_ES = plugins/poll/config/locales/server.es.yml
|
||||
trans.fr_FR = plugins/poll/config/locales/server.fr.yml
|
||||
trans.ko_KR = plugins/poll/config/locales/server.ko.yml
|
||||
trans.pt_PT = plugins/poll/config/locales/server.pt.yml
|
||||
type = YML
|
||||
|
||||
[discourse-org.imgurserverenyml]
|
||||
file_filter = vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.<lang>.yml
|
||||
source_file = vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.en.yml
|
||||
source_lang = en
|
||||
trans.es_ES = vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.es.yml
|
||||
trans.fr_FR = vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.fr.yml
|
||||
trans.ko_KR = vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.ko.yml
|
||||
trans.pt_PT = vendor/gems/discourse_imgur/lib/discourse_imgur/locale/server.pt.yml
|
||||
type = YML
|
||||
|
||||
[discourse-org.403html]
|
||||
file_filter = public/403.<lang>.html
|
||||
source_file = public/403.html
|
||||
source_lang = en
|
||||
trans.es_ES = public/403.es.html
|
||||
trans.fr_FR = public/403.fr.html
|
||||
trans.ko_KR = public/403.ko.html
|
||||
trans.pt_PT = public/403.pt.html
|
||||
type = HTML
|
||||
|
||||
[discourse-org.422html]
|
||||
file_filter = public/422.<lang>.html
|
||||
source_file = public/422.html
|
||||
source_lang = en
|
||||
trans.es_ES = public/422.es.html
|
||||
trans.fr_FR = public/422.fr.html
|
||||
trans.ko_KR = public/422.ko.html
|
||||
trans.pt_PT = public/422.pt.html
|
||||
type = HTML
|
||||
|
||||
[discourse-org.500html]
|
||||
file_filter = public/500.<lang>.html
|
||||
source_file = public/500.html
|
||||
source_lang = en
|
||||
trans.es_ES = public/500.es.html
|
||||
trans.fr_FR = public/500.fr.html
|
||||
trans.ko_KR = public/500.ko.html
|
||||
trans.pt_PT = public/500.pt.html
|
||||
type = HTML
|
||||
|
||||
[discourse-org.503html]
|
||||
file_filter = public/503.<lang>.html
|
||||
source_file = public/503.html
|
||||
source_lang = en
|
||||
trans.es_ES = public/503.es.html
|
||||
trans.fr_FR = public/503.fr.html
|
||||
trans.ko_KR = public/503.ko.html
|
||||
trans.pt_PT = public/503.pt.html
|
||||
type = HTML
|
||||
|
123
Gemfile
123
Gemfile
@ -2,81 +2,27 @@ source 'https://rubygems.org'
|
||||
# if there is a super emergency and rubygems is playing up, try
|
||||
#source 'http://production.cf.rubygems.org'
|
||||
|
||||
module ::Kernel
|
||||
def rails_master?
|
||||
ENV["RAILS_MASTER"] == '1'
|
||||
end
|
||||
def rails_master?
|
||||
ENV["RAILS_MASTER"] == '1'
|
||||
end
|
||||
|
||||
if rails_master?
|
||||
# monkey patching to support dual booting
|
||||
module Bundler::SharedHelpers
|
||||
def default_lockfile=(path)
|
||||
@default_lockfile = path
|
||||
end
|
||||
def default_lockfile
|
||||
@default_lockfile ||= Pathname.new("#{default_gemfile}.lock")
|
||||
end
|
||||
end
|
||||
|
||||
Bundler::SharedHelpers.default_lockfile = Pathname.new("#{Bundler::SharedHelpers.default_gemfile}_master.lock")
|
||||
|
||||
# Bundler::Dsl.evaluate already called with an incorrect lockfile ... fix it
|
||||
class Bundler::Dsl
|
||||
# A bit messy, this can be called multiple times by bundler, avoid blowing the stack
|
||||
unless self.method_defined? :to_definition_unpatched
|
||||
alias_method :to_definition_unpatched, :to_definition
|
||||
end
|
||||
def to_definition(bad_lockfile, unlock)
|
||||
to_definition_unpatched(Bundler::SharedHelpers.default_lockfile, unlock)
|
||||
end
|
||||
end
|
||||
|
||||
def rails_42?
|
||||
ENV["RAILS42"] == '1'
|
||||
end
|
||||
|
||||
# Monkey patch bundler to support mri_21
|
||||
unless Bundler::Dependency::PLATFORM_MAP.include? :mri_21
|
||||
STDERR.puts
|
||||
STDERR.puts "WARNING: --------------------------------------------------------------------------"
|
||||
STDERR.puts "You are running an old version of bundler, please update by running: gem install bundler"
|
||||
STDERR.puts
|
||||
map = Bundler::Dependency::PLATFORM_MAP.dup
|
||||
map[:mri_21] = Gem::Platform::RUBY
|
||||
map.freeze
|
||||
Bundler::Dependency.send(:remove_const, "PLATFORM_MAP")
|
||||
Bundler::Dependency.const_set("PLATFORM_MAP", map)
|
||||
|
||||
Bundler::Dsl.send(:remove_const, "VALID_PLATFORMS")
|
||||
Bundler::Dsl.const_set("VALID_PLATFORMS", map.keys.freeze)
|
||||
class ::Bundler::CurrentRuby
|
||||
def on_21?
|
||||
RUBY_VERSION =~ /^2\.1/
|
||||
end
|
||||
def mri_21?
|
||||
mri? && on_21?
|
||||
end
|
||||
end
|
||||
class ::Bundler::Dependency
|
||||
private
|
||||
def on_21?
|
||||
RUBY_VERSION =~ /^2\.1/
|
||||
end
|
||||
def mri_21?
|
||||
mri? && on_21?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if rails_master?
|
||||
gem 'arel', git: 'https://github.com/rails/arel.git'
|
||||
gem 'rails', git: 'https://github.com/rails/rails.git'
|
||||
gem 'rails-observers', git: 'https://github.com/SamSaffron/rails-observers.git'
|
||||
gem 'rails-observers', git: 'https://github.com/rails/rails-observers.git'
|
||||
gem 'seed-fu', git: 'https://github.com/SamSaffron/seed-fu.git', branch: 'discourse'
|
||||
elsif rails_42?
|
||||
gem 'rails', '~> 4.2.1'
|
||||
gem 'rails-observers', git: 'https://github.com/rails/rails-observers.git'
|
||||
gem 'seed-fu', '~> 2.3.5'
|
||||
else
|
||||
gem 'seed-fu', '~> 2.3.3'
|
||||
gem 'rails'
|
||||
gem 'rails', '~> 4.1.10'
|
||||
gem 'rails-observers'
|
||||
gem 'seed-fu', '~> 2.3.3'
|
||||
end
|
||||
|
||||
gem 'actionpack-action_caching'
|
||||
@ -91,22 +37,15 @@ gem 'mail', '~> 2.5.4'
|
||||
gem 'hiredis'
|
||||
gem 'redis', require: ["redis", "redis/connection/hiredis"]
|
||||
|
||||
# We use some ams 0.8.0 features, need to amend code
|
||||
# to support 0.9 etc, bench needs to run and ensure no
|
||||
# perf regressions
|
||||
if rails_master?
|
||||
gem 'active_model_serializers', github: 'rails-api/active_model_serializers', branch: '0-8-stable'
|
||||
else
|
||||
gem 'active_model_serializers', '~> 0.8.0'
|
||||
end
|
||||
|
||||
gem 'active_model_serializers', '~> 0.8.3'
|
||||
|
||||
gem 'onebox'
|
||||
|
||||
gem 'ember-rails'
|
||||
gem 'ember-source', '1.9.0.beta.4'
|
||||
gem 'ember-source', '1.11.3.1'
|
||||
gem 'handlebars-source', '2.0.0'
|
||||
gem 'barber'
|
||||
gem 'babel-transpiler'
|
||||
|
||||
gem 'message_bus'
|
||||
gem 'rails_multisite', path: 'vendor/gems/rails_multisite'
|
||||
@ -116,7 +55,9 @@ gem 'eventmachine'
|
||||
gem 'fast_xs'
|
||||
|
||||
gem 'fast_xor'
|
||||
gem 'fastimage'
|
||||
|
||||
# while we sort out https://github.com/sdsykes/fastimage/pull/46
|
||||
gem 'fastimage_discourse', require: 'fastimage'
|
||||
gem 'fog', '1.26.0', require: false
|
||||
gem 'unf', require: false
|
||||
|
||||
@ -144,6 +85,7 @@ gem 'omniauth-google-oauth2'
|
||||
gem 'oj'
|
||||
gem 'pg'
|
||||
gem 'pry-rails', require: false
|
||||
gem 'r2', '~> 0.2.5', require: false
|
||||
gem 'rake'
|
||||
|
||||
|
||||
@ -165,14 +107,7 @@ gem 'rack-protection' # security
|
||||
# in production environments by default.
|
||||
# allow everywhere for now cause we are allowing asset debugging in prd
|
||||
group :assets do
|
||||
|
||||
if rails_master?
|
||||
gem 'sass-rails', git: 'https://github.com/rails/sass-rails.git'
|
||||
else
|
||||
# later is breaking our asset compliation extensions
|
||||
gem 'sass-rails', '4.0.2'
|
||||
end
|
||||
|
||||
gem 'sass-rails', '~> 4.0.5'
|
||||
gem 'uglifier'
|
||||
gem 'rtlit', require: false # for css rtling
|
||||
end
|
||||
@ -183,14 +118,13 @@ group :test do
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
# while upgrading to 3
|
||||
gem 'rspec', '2.99.0'
|
||||
gem 'rspec', '~> 3.2.0'
|
||||
gem 'mock_redis'
|
||||
gem 'listen', '0.7.3', require: false
|
||||
gem 'certified', require: false
|
||||
# later appears to break Fabricate(:topic, category: category)
|
||||
gem 'fabrication', '2.9.8', require: false
|
||||
gem 'qunit-rails'
|
||||
gem 'discourse-qunit-rails', require: 'qunit-rails'
|
||||
gem 'mocha', require: false
|
||||
gem 'rb-fsevent', require: RUBY_PLATFORM =~ /darwin/i ? 'rb-fsevent' : false
|
||||
gem 'rb-inotify', '~> 0.9', require: RUBY_PLATFORM =~ /linux/i ? 'rb-inotify' : false
|
||||
@ -238,16 +172,19 @@ gem 'ruby-readability', require: false
|
||||
|
||||
gem 'simple-rss', require: false
|
||||
|
||||
# TODO mri_22 should be here, but bundler was real slow to pick it up
|
||||
# not even in production bundler yet, monkey patching it in feels bad
|
||||
gem 'gctools', require: false, platform: :mri_21
|
||||
gem 'stackprof', require: false, platform: :mri_21
|
||||
gem 'memory_profiler', require: false, platform: :mri_21
|
||||
|
||||
begin
|
||||
gem 'stackprof', require: false, platform: [:mri_21, :mri_22]
|
||||
gem 'memory_profiler', require: false, platform: [:mri_21, :mri_22]
|
||||
rescue Bundler::GemfileError
|
||||
STDERR.puts "You are running an old version of bundler, please upgrade bundler ASAP, if you are using Discourse docker, rebuild your container."
|
||||
gem 'stackprof', require: false, platform: [:mri_21]
|
||||
gem 'memory_profiler', require: false, platform: [:mri_21]
|
||||
end
|
||||
|
||||
gem 'rmmseg-cpp', require: false
|
||||
|
||||
gem 'stringex', require: false
|
||||
|
||||
gem 'logster'
|
||||
|
||||
# perftools only works on 1.9 atm
|
||||
|
291
Gemfile.lock
291
Gemfile.lock
@ -7,87 +7,89 @@ GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (2.2.8)
|
||||
actionmailer (4.1.8)
|
||||
actionpack (= 4.1.8)
|
||||
actionview (= 4.1.8)
|
||||
actionmailer (4.1.10)
|
||||
actionpack (= 4.1.10)
|
||||
actionview (= 4.1.10)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
actionpack (4.1.8)
|
||||
actionview (= 4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
actionpack (4.1.10)
|
||||
actionview (= 4.1.10)
|
||||
activesupport (= 4.1.10)
|
||||
rack (~> 1.5.2)
|
||||
rack-test (~> 0.6.2)
|
||||
actionpack-action_caching (1.1.1)
|
||||
actionpack (>= 4.0.0, < 5.0)
|
||||
actionview (4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
actionview (4.1.10)
|
||||
activesupport (= 4.1.10)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
active_model_serializers (0.8.2)
|
||||
active_model_serializers (0.8.3)
|
||||
activemodel (>= 3.0)
|
||||
activemodel (4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
activemodel (4.1.10)
|
||||
activesupport (= 4.1.10)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.1.8)
|
||||
activemodel (= 4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
activerecord (4.1.10)
|
||||
activemodel (= 4.1.10)
|
||||
activesupport (= 4.1.10)
|
||||
arel (~> 5.0.0)
|
||||
activesupport (4.1.8)
|
||||
activesupport (4.1.10)
|
||||
i18n (~> 0.6, >= 0.6.9)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.6)
|
||||
annotate (2.6.5)
|
||||
annotate (2.6.6)
|
||||
activerecord (>= 2.3.0)
|
||||
rake (>= 0.8.7)
|
||||
rake (~> 10.4.2, >= 10.4.2)
|
||||
arel (5.0.1.20140414130214)
|
||||
barber (0.5.0)
|
||||
ember-source
|
||||
execjs
|
||||
handlebars-source (>= 1.0.0.rc.4)
|
||||
better_errors (2.1.0)
|
||||
babel-source (4.6.6)
|
||||
babel-transpiler (0.6.0)
|
||||
babel-source (>= 4.0, < 5)
|
||||
execjs (~> 2.0)
|
||||
barber (0.9.0)
|
||||
ember-source (>= 1.0, < 2)
|
||||
execjs (>= 1.2, < 3)
|
||||
better_errors (2.1.1)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
builder (3.2.2)
|
||||
celluloid (0.15.2)
|
||||
timers (~> 1.1.0)
|
||||
celluloid (0.16.0)
|
||||
timers (~> 4.0.0)
|
||||
certified (1.0.0)
|
||||
coderay (1.1.0)
|
||||
connection_pool (2.0.0)
|
||||
crass (0.2.1)
|
||||
daemons (1.1.9)
|
||||
connection_pool (2.1.2)
|
||||
crass (1.0.1)
|
||||
daemons (1.2.2)
|
||||
debug_inspector (0.0.2)
|
||||
diff-lcs (1.2.5)
|
||||
discourse-qunit-rails (0.0.8)
|
||||
railties
|
||||
docile (1.1.5)
|
||||
dotenv (0.11.1)
|
||||
dotenv-deployment (~> 0.0.2)
|
||||
dotenv-deployment (0.0.2)
|
||||
dotenv (1.0.2)
|
||||
email_reply_parser (0.5.8)
|
||||
ember-data-source (0.14)
|
||||
ember-source
|
||||
ember-rails (0.14.1)
|
||||
ember-data-source (1.0.0.beta.16.1)
|
||||
ember-source (~> 1.8)
|
||||
ember-handlebars-template (0.1.5)
|
||||
barber (>= 0.9.0)
|
||||
sprockets (>= 2.1, < 3.1)
|
||||
ember-rails (0.18.2)
|
||||
active_model_serializers
|
||||
barber (>= 0.4.1)
|
||||
ember-data-source
|
||||
ember-source
|
||||
execjs (>= 1.2)
|
||||
handlebars-source
|
||||
ember-data-source (>= 1.0.0.beta.5)
|
||||
ember-handlebars-template (>= 0.1.1, < 1.0)
|
||||
ember-source (>= 1.1.0)
|
||||
jquery-rails (>= 1.0.17)
|
||||
railties (>= 3.1)
|
||||
ember-source (1.9.0.beta.4)
|
||||
handlebars-source (~> 2.0)
|
||||
ember-source (1.11.3.1)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.4)
|
||||
excon (0.42.1)
|
||||
execjs (2.2.2)
|
||||
eventmachine (1.0.7)
|
||||
excon (0.44.4)
|
||||
execjs (2.5.2)
|
||||
exifr (1.1.3)
|
||||
fabrication (2.9.8)
|
||||
fakeweb (1.3.0)
|
||||
faraday (0.9.0)
|
||||
faraday (0.9.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
fast_blank (0.0.2)
|
||||
fast_stack (0.1.0)
|
||||
@ -97,8 +99,7 @@ GEM
|
||||
rake
|
||||
rake-compiler
|
||||
fast_xs (0.8.0)
|
||||
fastimage (1.6.3)
|
||||
addressable (~> 2.3, >= 2.3.5)
|
||||
fastimage_discourse (1.6.6)
|
||||
ffi (1.9.6)
|
||||
fission (0.5.0)
|
||||
CFPropertyList (~> 2.2)
|
||||
@ -169,22 +170,23 @@ GEM
|
||||
fog-xml (0.1.1)
|
||||
fog-core
|
||||
nokogiri (~> 1.5, >= 1.5.11)
|
||||
foreman (0.75.0)
|
||||
dotenv (~> 0.11.1)
|
||||
foreman (0.77.0)
|
||||
dotenv (~> 1.0.2)
|
||||
thor (~> 0.19.1)
|
||||
formatador (0.2.5)
|
||||
fspath (2.0.6)
|
||||
gctools (0.2.3)
|
||||
given_core (3.5.4)
|
||||
sorcerer (>= 0.3.7)
|
||||
guess_html_encoding (0.0.9)
|
||||
guess_html_encoding (0.0.11)
|
||||
handlebars-source (2.0.0)
|
||||
hashie (3.3.1)
|
||||
highline (1.6.21)
|
||||
hashie (3.4.0)
|
||||
highline (1.7.1)
|
||||
hike (1.2.3)
|
||||
hiredis (0.5.2)
|
||||
hiredis (0.6.0)
|
||||
hitimes (1.2.2)
|
||||
htmlentities (4.3.3)
|
||||
i18n (0.6.11)
|
||||
i18n (0.7.0)
|
||||
image_optim (0.9.1)
|
||||
exifr (~> 1.1.3)
|
||||
fspath (~> 2.0.5)
|
||||
@ -198,45 +200,44 @@ GEM
|
||||
jquery-rails (3.1.2)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (1.8.1)
|
||||
jwt (1.0.0)
|
||||
kgio (2.9.2)
|
||||
json (1.8.2)
|
||||
jwt (1.3.0)
|
||||
kgio (2.9.3)
|
||||
librarian (0.1.2)
|
||||
highline
|
||||
thor (~> 0.15)
|
||||
libv8 (3.16.14.7)
|
||||
listen (0.7.3)
|
||||
logster (0.1.6)
|
||||
lru_redux (0.8.1)
|
||||
logster (0.8.1)
|
||||
lru_redux (0.8.4)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memory_profiler (0.0.4)
|
||||
message_bus (1.0.5)
|
||||
eventmachine
|
||||
memory_profiler (0.9.0)
|
||||
message_bus (1.0.11)
|
||||
rack (>= 1.1.3)
|
||||
redis
|
||||
metaclass (0.0.4)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.1)
|
||||
minitest (5.4.2)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.6.1)
|
||||
mocha (1.1.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mock_redis (0.13.2)
|
||||
mock_redis (0.14.0)
|
||||
moneta (0.8.0)
|
||||
msgpack (0.5.10)
|
||||
multi_json (1.10.1)
|
||||
msgpack (0.5.11)
|
||||
multi_json (1.11.0)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (2.0.0)
|
||||
mustache (0.99.6)
|
||||
mustache (0.99.8)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.9.1)
|
||||
netrc (0.7.7)
|
||||
nokogiri (1.6.5)
|
||||
net-ssh (2.9.2)
|
||||
netrc (0.10.3)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
nokogumbo (1.1.12)
|
||||
nokogumbo (1.2.0)
|
||||
nokogiri
|
||||
oauth (0.4.7)
|
||||
oauth2 (1.0.0)
|
||||
@ -245,7 +246,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (~> 1.2)
|
||||
oj (2.11.1)
|
||||
oj (2.12.0)
|
||||
omniauth (1.2.2)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (~> 1.0)
|
||||
@ -271,7 +272,7 @@ GEM
|
||||
omniauth-twitter (1.0.1)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
onebox (1.5.10)
|
||||
onebox (1.5.18)
|
||||
moneta (~> 0.7)
|
||||
multi_json (~> 1.7)
|
||||
mustache (~> 0.99)
|
||||
@ -279,46 +280,45 @@ GEM
|
||||
openid-redis-store (0.0.2)
|
||||
redis
|
||||
ruby-openid
|
||||
pg (0.18.0)
|
||||
pg (0.18.1)
|
||||
polyglot (0.3.5)
|
||||
progress (3.0.1)
|
||||
progress (3.0.2)
|
||||
pry (0.10.1)
|
||||
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.2)
|
||||
pry-rails (0.3.3)
|
||||
pry (>= 0.9.10)
|
||||
puma (2.9.1)
|
||||
puma (2.11.1)
|
||||
rack (>= 1.1, < 2.0)
|
||||
qunit-rails (0.0.7)
|
||||
railties
|
||||
rack (1.5.2)
|
||||
rack-mini-profiler (0.9.2)
|
||||
r2 (0.2.5)
|
||||
rack (1.5.3)
|
||||
rack-mini-profiler (0.9.3)
|
||||
rack (>= 1.1.3)
|
||||
rack-openid (1.3.1)
|
||||
rack (>= 1.1.0)
|
||||
ruby-openid (>= 2.1.8)
|
||||
rack-protection (1.5.3)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.1.8)
|
||||
actionmailer (= 4.1.8)
|
||||
actionpack (= 4.1.8)
|
||||
actionview (= 4.1.8)
|
||||
activemodel (= 4.1.8)
|
||||
activerecord (= 4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
rails (4.1.10)
|
||||
actionmailer (= 4.1.10)
|
||||
actionpack (= 4.1.10)
|
||||
actionview (= 4.1.10)
|
||||
activemodel (= 4.1.10)
|
||||
activerecord (= 4.1.10)
|
||||
activesupport (= 4.1.10)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.1.8)
|
||||
railties (= 4.1.10)
|
||||
sprockets-rails (~> 2.0)
|
||||
rails-observers (0.1.2)
|
||||
activemodel (~> 4.0)
|
||||
railties (4.1.8)
|
||||
actionpack (= 4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
railties (4.1.10)
|
||||
actionpack (= 4.1.10)
|
||||
activesupport (= 4.1.10)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
raindrops (0.13.0)
|
||||
@ -328,12 +328,12 @@ GEM
|
||||
rb-fsevent (0.9.4)
|
||||
rb-inotify (0.9.5)
|
||||
ffi (>= 0.5.0)
|
||||
rbtrace (0.4.6)
|
||||
rbtrace (0.4.7)
|
||||
ffi (>= 1.0.6)
|
||||
msgpack (>= 0.4.3)
|
||||
trollop (>= 1.16.2)
|
||||
redcarpet (3.1.2)
|
||||
redis (3.2.0)
|
||||
redcarpet (3.2.2)
|
||||
redis (3.2.1)
|
||||
redis-namespace (1.5.1)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
ref (1.0.5)
|
||||
@ -342,55 +342,57 @@ GEM
|
||||
netrc (~> 0.7)
|
||||
rinku (1.7.3)
|
||||
rmmseg-cpp (0.2.9)
|
||||
rspec (2.99.0)
|
||||
rspec-core (~> 2.99.0)
|
||||
rspec-expectations (~> 2.99.0)
|
||||
rspec-mocks (~> 2.99.0)
|
||||
rspec-collection_matchers (1.0.0)
|
||||
rspec-expectations (>= 2.99.0.beta1)
|
||||
rspec-core (2.99.2)
|
||||
rspec-expectations (2.99.2)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
rspec (3.2.0)
|
||||
rspec-core (~> 3.2.0)
|
||||
rspec-expectations (~> 3.2.0)
|
||||
rspec-mocks (~> 3.2.0)
|
||||
rspec-core (3.2.3)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-expectations (3.2.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-given (3.5.4)
|
||||
given_core (= 3.5.4)
|
||||
rspec (>= 2.12)
|
||||
rspec-mocks (2.99.2)
|
||||
rspec-rails (2.99.0)
|
||||
actionpack (>= 3.0)
|
||||
activemodel (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-collection_matchers
|
||||
rspec-core (~> 2.99.0)
|
||||
rspec-expectations (~> 2.99.0)
|
||||
rspec-mocks (~> 2.99.0)
|
||||
rspec-mocks (3.2.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-rails (3.2.1)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.2.0)
|
||||
rspec-expectations (~> 3.2.0)
|
||||
rspec-mocks (~> 3.2.0)
|
||||
rspec-support (~> 3.2.0)
|
||||
rspec-support (3.2.2)
|
||||
rtlit (0.0.5)
|
||||
ruby-openid (2.5.0)
|
||||
ruby-readability (0.7.0)
|
||||
guess_html_encoding (>= 0.0.4)
|
||||
nokogiri (>= 1.6.0)
|
||||
sanitize (3.0.2)
|
||||
crass (~> 0.2.0)
|
||||
sanitize (3.1.2)
|
||||
crass (~> 1.0.1)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (= 1.1.12)
|
||||
nokogumbo (= 1.2.0)
|
||||
sass (3.2.19)
|
||||
sass-rails (4.0.2)
|
||||
sass-rails (4.0.5)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.2.0)
|
||||
sprockets (~> 2.8, <= 2.11.0)
|
||||
sprockets-rails (~> 2.0.0)
|
||||
seed-fu (2.3.3)
|
||||
activerecord (>= 3.1, < 4.2)
|
||||
activesupport (>= 3.1, < 4.2)
|
||||
seed-fu (2.3.5)
|
||||
activerecord (>= 3.1, < 4.3)
|
||||
activesupport (>= 3.1, < 4.3)
|
||||
shoulda (3.5.0)
|
||||
shoulda-context (~> 1.0, >= 1.0.1)
|
||||
shoulda-matchers (>= 1.4.1, < 3.0)
|
||||
shoulda-context (1.2.1)
|
||||
shoulda-matchers (2.7.0)
|
||||
activesupport (>= 3.0.0)
|
||||
sidekiq (3.2.5)
|
||||
celluloid (= 0.15.2)
|
||||
connection_pool (>= 2.0.0)
|
||||
sidekiq (3.3.2)
|
||||
celluloid (>= 0.16.0)
|
||||
connection_pool (>= 2.1.1)
|
||||
json
|
||||
redis (>= 3.0.6)
|
||||
redis-namespace (>= 1.3.1)
|
||||
@ -420,26 +422,26 @@ GEM
|
||||
activesupport (>= 3.0)
|
||||
sprockets (~> 2.8)
|
||||
stackprof (0.2.7)
|
||||
stringex (2.5.2)
|
||||
therubyracer (0.12.1)
|
||||
therubyracer (0.12.2)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thin (1.6.2)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 1.0.0)
|
||||
rack (>= 1.0.0)
|
||||
thin (1.6.3)
|
||||
daemons (~> 1.0, >= 1.0.9)
|
||||
eventmachine (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.4)
|
||||
thread_safe (0.3.5)
|
||||
tilt (1.4.1)
|
||||
timecop (0.7.1)
|
||||
timers (1.1.0)
|
||||
timecop (0.7.3)
|
||||
timers (4.0.1)
|
||||
hitimes
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
trollop (2.1.1)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.6.0)
|
||||
uglifier (2.7.1)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unf (0.1.4)
|
||||
@ -455,22 +457,24 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
actionpack-action_caching
|
||||
active_model_serializers (~> 0.8.0)
|
||||
active_model_serializers (~> 0.8.3)
|
||||
annotate
|
||||
babel-transpiler
|
||||
barber
|
||||
better_errors
|
||||
binding_of_caller
|
||||
certified
|
||||
discourse-qunit-rails
|
||||
email_reply_parser
|
||||
ember-rails
|
||||
ember-source (= 1.9.0.beta.4)
|
||||
ember-source (= 1.11.3.1)
|
||||
eventmachine
|
||||
fabrication (= 2.9.8)
|
||||
fakeweb (~> 1.3.0)
|
||||
fast_blank
|
||||
fast_xor
|
||||
fast_xs
|
||||
fastimage
|
||||
fastimage_discourse
|
||||
flamegraph
|
||||
fog (= 1.26.0)
|
||||
foreman
|
||||
@ -507,10 +511,10 @@ DEPENDENCIES
|
||||
pry-nav
|
||||
pry-rails
|
||||
puma
|
||||
qunit-rails
|
||||
r2 (~> 0.2.5)
|
||||
rack-mini-profiler
|
||||
rack-protection
|
||||
rails
|
||||
rails (~> 4.1.10)
|
||||
rails-observers
|
||||
rails_multisite!
|
||||
rake
|
||||
@ -522,14 +526,14 @@ DEPENDENCIES
|
||||
rest-client
|
||||
rinku
|
||||
rmmseg-cpp
|
||||
rspec (= 2.99.0)
|
||||
rspec (~> 3.2.0)
|
||||
rspec-given
|
||||
rspec-rails
|
||||
rtlit
|
||||
ruby-readability
|
||||
sanitize
|
||||
sass
|
||||
sass-rails (= 4.0.2)
|
||||
sass-rails (~> 4.0.5)
|
||||
seed-fu (~> 2.3.3)
|
||||
shoulda
|
||||
sidekiq
|
||||
@ -538,7 +542,6 @@ DEPENDENCIES
|
||||
sinatra
|
||||
spork-rails
|
||||
stackprof
|
||||
stringex
|
||||
therubyracer
|
||||
thin
|
||||
timecop
|
||||
|
@ -1,552 +0,0 @@
|
||||
GIT
|
||||
remote: git://github.com/rails-api/active_model_serializers.git
|
||||
revision: b6b01d0b7396f3deaa6e661cedf4bc5efe2f4525
|
||||
branch: 0-8-stable
|
||||
specs:
|
||||
active_model_serializers (0.8.2)
|
||||
activemodel (>= 3.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/SamSaffron/rails-observers.git
|
||||
revision: 7d2222d758603a004f6599f82a7068ffeb2d7ebf
|
||||
specs:
|
||||
rails-observers (0.1.2)
|
||||
activemodel (> 4.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/SamSaffron/seed-fu.git
|
||||
revision: d93df3b6364ea938d87c5629bf950b0d1ffe037e
|
||||
branch: discourse
|
||||
specs:
|
||||
seed-fu (2.3.3)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/rails/arel.git
|
||||
revision: 98fc25991137ee09b6800578117f8c1c322680f2
|
||||
specs:
|
||||
arel (6.0.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/rails/sass-rails.git
|
||||
revision: b4b5f32a2928ef203f4b442bc538a572645de8e3
|
||||
specs:
|
||||
sass-rails (5.0.0.beta1)
|
||||
railties (>= 4.0.0, < 5.0)
|
||||
sass (~> 3.2, >= 3.2.2)
|
||||
sprockets (~> 2.12)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
|
||||
PATH
|
||||
remote: ../rails
|
||||
specs:
|
||||
actionmailer (5.0.0.alpha)
|
||||
actionpack (= 5.0.0.alpha)
|
||||
actionview (= 5.0.0.alpha)
|
||||
activejob (= 5.0.0.alpha)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (5.0.0.alpha)
|
||||
actionview (= 5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
rack (~> 1.6.0.beta2)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
||||
actionview (5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
||||
activejob (5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
builder (~> 3.1)
|
||||
activerecord (5.0.0.alpha)
|
||||
activemodel (= 5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
arel (~> 6.0)
|
||||
activesupport (5.0.0.alpha)
|
||||
i18n (>= 0.7.0.beta1, < 0.8)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 1.1)
|
||||
rails (5.0.0.alpha)
|
||||
actionmailer (= 5.0.0.alpha)
|
||||
actionpack (= 5.0.0.alpha)
|
||||
actionview (= 5.0.0.alpha)
|
||||
activejob (= 5.0.0.alpha)
|
||||
activemodel (= 5.0.0.alpha)
|
||||
activerecord (= 5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 5.0.0.alpha)
|
||||
sprockets-rails
|
||||
railties (5.0.0.alpha)
|
||||
actionpack (= 5.0.0.alpha)
|
||||
activesupport (= 5.0.0.alpha)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/rails_multisite
|
||||
specs:
|
||||
rails_multisite (0.0.1)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionpack-action_caching (1.1.1)
|
||||
actionpack (>= 4.0.0, < 5.0)
|
||||
addressable (2.3.6)
|
||||
annotate (2.6.5)
|
||||
activerecord (>= 2.3.0)
|
||||
rake (>= 0.8.7)
|
||||
barber (0.4.2)
|
||||
ember-source
|
||||
execjs
|
||||
handlebars-source
|
||||
better_errors (2.0.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
builder (3.2.2)
|
||||
celluloid (0.15.2)
|
||||
timers (~> 1.1.0)
|
||||
certified (1.0.0)
|
||||
coderay (1.1.0)
|
||||
connection_pool (2.0.0)
|
||||
crass (0.2.1)
|
||||
daemons (1.1.9)
|
||||
debug_inspector (0.0.2)
|
||||
diff-lcs (1.2.5)
|
||||
docile (1.1.5)
|
||||
dotenv (0.11.1)
|
||||
dotenv-deployment (~> 0.0.2)
|
||||
dotenv-deployment (0.0.2)
|
||||
email_reply_parser-discourse (0.6)
|
||||
ember-data-source (0.14)
|
||||
ember-source
|
||||
ember-rails (0.14.1)
|
||||
active_model_serializers
|
||||
barber (>= 0.4.1)
|
||||
ember-data-source
|
||||
ember-source
|
||||
execjs (>= 1.2)
|
||||
handlebars-source
|
||||
jquery-rails (>= 1.0.17)
|
||||
railties (>= 3.1)
|
||||
ember-source (1.6.0.beta.2)
|
||||
handlebars-source (~> 1.0)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.3)
|
||||
excon (0.39.6)
|
||||
execjs (2.2.1)
|
||||
exifr (1.1.3)
|
||||
fabrication (2.9.8)
|
||||
fakeweb (1.3.0)
|
||||
faraday (0.9.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
fast_blank (0.0.2)
|
||||
fast_stack (0.1.0)
|
||||
rake
|
||||
rake-compiler
|
||||
fast_xor (1.1.3)
|
||||
rake
|
||||
rake-compiler
|
||||
fast_xs (0.8.0)
|
||||
fastimage (1.6.3)
|
||||
addressable (~> 2.3, >= 2.3.5)
|
||||
ffi (1.9.5)
|
||||
flamegraph (0.0.9)
|
||||
fast_stack
|
||||
fog (1.22.1)
|
||||
fog-brightbox
|
||||
fog-core (~> 1.22)
|
||||
fog-json
|
||||
ipaddress (~> 0.5)
|
||||
nokogiri (~> 1.5, >= 1.5.11)
|
||||
fog-brightbox (0.5.1)
|
||||
fog-core (~> 1.22)
|
||||
fog-json
|
||||
inflecto
|
||||
fog-core (1.24.0)
|
||||
builder
|
||||
excon (~> 0.38)
|
||||
formatador (~> 0.2)
|
||||
mime-types
|
||||
net-scp (~> 1.1)
|
||||
net-ssh (>= 2.1.3)
|
||||
fog-json (1.0.0)
|
||||
multi_json (~> 1.0)
|
||||
foreman (0.75.0)
|
||||
dotenv (~> 0.11.1)
|
||||
thor (~> 0.19.1)
|
||||
formatador (0.2.5)
|
||||
fspath (2.0.6)
|
||||
gctools (0.2.3)
|
||||
given_core (3.5.4)
|
||||
sorcerer (>= 0.3.7)
|
||||
globalid (0.3.0)
|
||||
activesupport (>= 4.1.0)
|
||||
guess_html_encoding (0.0.9)
|
||||
handlebars-source (1.3.0)
|
||||
hashie (3.3.1)
|
||||
highline (1.6.21)
|
||||
hike (1.2.3)
|
||||
hiredis (0.5.2)
|
||||
htmlentities (4.3.2)
|
||||
i18n (0.7.0.beta1)
|
||||
image_optim (0.9.1)
|
||||
exifr (~> 1.1.3)
|
||||
fspath (~> 2.0.5)
|
||||
image_size (~> 1.1.2)
|
||||
in_threads (~> 1.2.0)
|
||||
progress (~> 3.0.0)
|
||||
image_size (1.1.5)
|
||||
in_threads (1.2.2)
|
||||
inflecto (0.0.2)
|
||||
ipaddress (0.8.0)
|
||||
jquery-rails (3.1.2)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (1.8.1)
|
||||
jwt (1.0.0)
|
||||
kgio (2.9.2)
|
||||
librarian (0.1.2)
|
||||
highline
|
||||
thor (~> 0.15)
|
||||
libv8 (3.16.14.7)
|
||||
listen (0.7.3)
|
||||
logster (0.1.6)
|
||||
loofah (2.0.1)
|
||||
nokogiri (>= 1.5.9)
|
||||
lru_redux (0.8.1)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memory_profiler (0.0.4)
|
||||
message_bus (1.0.5)
|
||||
eventmachine
|
||||
rack (>= 1.1.3)
|
||||
redis
|
||||
metaclass (0.0.4)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.1)
|
||||
minitest (5.4.3)
|
||||
mocha (1.1.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mock_redis (0.13.2)
|
||||
moneta (0.8.0)
|
||||
msgpack (0.5.8)
|
||||
multi_json (1.10.1)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (2.0.0)
|
||||
mustache (0.99.6)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.9.1)
|
||||
netrc (0.7.7)
|
||||
nokogiri (1.6.4.1)
|
||||
mini_portile (~> 0.6.0)
|
||||
nokogumbo (1.1.12)
|
||||
nokogiri
|
||||
oauth (0.4.7)
|
||||
oauth2 (1.0.0)
|
||||
faraday (>= 0.8, < 0.10)
|
||||
jwt (~> 1.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (~> 1.2)
|
||||
oj (2.10.2)
|
||||
omniauth (1.2.2)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (~> 1.0)
|
||||
omniauth-facebook (2.0.0)
|
||||
omniauth-oauth2 (~> 1.2)
|
||||
omniauth-github-discourse (1.1.2)
|
||||
omniauth (~> 1.0)
|
||||
omniauth-oauth2 (~> 1.1)
|
||||
omniauth-google-oauth2 (0.2.5)
|
||||
omniauth (> 1.0)
|
||||
omniauth-oauth2 (~> 1.1)
|
||||
omniauth-oauth (1.0.1)
|
||||
oauth
|
||||
omniauth (~> 1.0)
|
||||
omniauth-oauth2 (1.2.0)
|
||||
faraday (>= 0.8, < 0.10)
|
||||
multi_json (~> 1.3)
|
||||
oauth2 (~> 1.0)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-openid (1.0.1)
|
||||
omniauth (~> 1.0)
|
||||
rack-openid (~> 1.3.1)
|
||||
omniauth-twitter (1.0.1)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
onebox (1.5.3)
|
||||
moneta (~> 0.7)
|
||||
multi_json (~> 1.7)
|
||||
mustache (~> 0.99)
|
||||
nokogiri (~> 1.6.1)
|
||||
openid-redis-store (0.0.2)
|
||||
redis
|
||||
ruby-openid
|
||||
pg (0.18.0.pre20141117110243)
|
||||
polyglot (0.3.5)
|
||||
progress (3.0.1)
|
||||
pry (0.10.1)
|
||||
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.2)
|
||||
pry (>= 0.9.10)
|
||||
puma (2.9.1)
|
||||
rack (>= 1.1, < 2.0)
|
||||
qunit-rails (0.0.7)
|
||||
railties
|
||||
rack (1.6.0.beta2)
|
||||
rack-mini-profiler (0.9.2)
|
||||
rack (>= 1.1.3)
|
||||
rack-openid (1.3.1)
|
||||
rack (>= 1.1.0)
|
||||
ruby-openid (>= 2.1.8)
|
||||
rack-protection (1.5.3)
|
||||
rack
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.5)
|
||||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.1)
|
||||
loofah (~> 2.0)
|
||||
raindrops (0.13.0)
|
||||
rake (10.4.0)
|
||||
rake-compiler (0.9.3)
|
||||
rake
|
||||
rb-fsevent (0.9.4)
|
||||
rb-inotify (0.9.5)
|
||||
ffi (>= 0.5.0)
|
||||
rbtrace (0.4.5)
|
||||
ffi (>= 1.0.6)
|
||||
msgpack (>= 0.4.3)
|
||||
trollop (>= 1.16.2)
|
||||
redcarpet (3.1.2)
|
||||
redis (3.1.0)
|
||||
redis-namespace (1.5.1)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
ref (1.0.5)
|
||||
rest-client (1.7.2)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rinku (1.7.3)
|
||||
rmmseg-cpp (0.2.9)
|
||||
rspec (2.99.0)
|
||||
rspec-core (~> 2.99.0)
|
||||
rspec-expectations (~> 2.99.0)
|
||||
rspec-mocks (~> 2.99.0)
|
||||
rspec-collection_matchers (1.0.0)
|
||||
rspec-expectations (>= 2.99.0.beta1)
|
||||
rspec-core (2.99.2)
|
||||
rspec-expectations (2.99.2)
|
||||
diff-lcs (>= 1.1.3, < 2.0)
|
||||
rspec-given (3.5.4)
|
||||
given_core (= 3.5.4)
|
||||
rspec (>= 2.12)
|
||||
rspec-mocks (2.99.2)
|
||||
rspec-rails (2.99.0)
|
||||
actionpack (>= 3.0)
|
||||
activemodel (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-collection_matchers
|
||||
rspec-core (~> 2.99.0)
|
||||
rspec-expectations (~> 2.99.0)
|
||||
rspec-mocks (~> 2.99.0)
|
||||
rtlit (0.0.5)
|
||||
ruby-openid (2.5.0)
|
||||
ruby-readability (0.7.0)
|
||||
guess_html_encoding (>= 0.0.4)
|
||||
nokogiri (>= 1.6.0)
|
||||
sanitize (3.0.2)
|
||||
crass (~> 0.2.0)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (= 1.1.12)
|
||||
sass (3.2.19)
|
||||
shoulda (3.5.0)
|
||||
shoulda-context (~> 1.0, >= 1.0.1)
|
||||
shoulda-matchers (>= 1.4.1, < 3.0)
|
||||
shoulda-context (1.2.1)
|
||||
shoulda-matchers (2.7.0)
|
||||
activesupport (>= 3.0.0)
|
||||
sidekiq (3.2.5)
|
||||
celluloid (= 0.15.2)
|
||||
connection_pool (>= 2.0.0)
|
||||
json
|
||||
redis (>= 3.0.6)
|
||||
redis-namespace (>= 1.3.1)
|
||||
simple-rss (1.3.1)
|
||||
simplecov (0.9.1)
|
||||
docile (~> 1.1.0)
|
||||
multi_json (~> 1.0)
|
||||
simplecov-html (~> 0.8.0)
|
||||
simplecov-html (0.8.0)
|
||||
sinatra (1.4.5)
|
||||
rack (~> 1.4)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
slop (3.6.0)
|
||||
sorcerer (1.0.2)
|
||||
spork (1.0.0rc4)
|
||||
spork-rails (4.0.0)
|
||||
rails (>= 3.0.0, < 5)
|
||||
spork (>= 1.0rc0)
|
||||
sprockets (2.12.3)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-rails (3.0.0.beta1)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (~> 2.8)
|
||||
stackprof (0.2.7)
|
||||
stringex (2.5.2)
|
||||
therubyracer (0.12.1)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thin (1.6.2)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 1.0.0)
|
||||
rack (>= 1.0.0)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.4)
|
||||
tilt (1.4.1)
|
||||
timecop (0.7.1)
|
||||
timers (1.1.0)
|
||||
treetop (1.4.15)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
trollop (2.0)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (2.5.3)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.6)
|
||||
unicorn (4.8.3)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
raindrops (~> 0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
actionpack-action_caching
|
||||
active_model_serializers!
|
||||
annotate
|
||||
arel!
|
||||
barber
|
||||
better_errors
|
||||
binding_of_caller
|
||||
certified
|
||||
email_reply_parser-discourse
|
||||
ember-rails
|
||||
ember-source (= 1.6.0.beta.2)
|
||||
eventmachine
|
||||
fabrication (= 2.9.8)
|
||||
fakeweb (~> 1.3.0)
|
||||
fast_blank
|
||||
fast_xor
|
||||
fast_xs
|
||||
fastimage
|
||||
flamegraph
|
||||
fog (= 1.22.1)
|
||||
foreman
|
||||
gctools
|
||||
handlebars-source (= 1.3.0)
|
||||
highline
|
||||
hiredis
|
||||
htmlentities
|
||||
image_optim (= 0.9.1)
|
||||
librarian (>= 0.0.25)
|
||||
listen (= 0.7.3)
|
||||
logster
|
||||
lru_redux
|
||||
mail (~> 2.5.4)
|
||||
memory_profiler
|
||||
message_bus
|
||||
minitest
|
||||
mocha
|
||||
mock_redis
|
||||
multi_json
|
||||
mustache
|
||||
nokogiri
|
||||
oj
|
||||
omniauth
|
||||
omniauth-facebook
|
||||
omniauth-github-discourse
|
||||
omniauth-google-oauth2
|
||||
omniauth-oauth2
|
||||
omniauth-openid
|
||||
omniauth-twitter
|
||||
onebox
|
||||
openid-redis-store
|
||||
pg (= 0.18.0.pre20141117110243)
|
||||
pry-nav
|
||||
pry-rails
|
||||
puma
|
||||
qunit-rails
|
||||
rack-mini-profiler
|
||||
rack-protection
|
||||
rails!
|
||||
rails-observers!
|
||||
rails_multisite!
|
||||
rake
|
||||
rb-fsevent
|
||||
rb-inotify (~> 0.9)
|
||||
rbtrace
|
||||
redcarpet
|
||||
redis
|
||||
rest-client
|
||||
rinku
|
||||
rmmseg-cpp
|
||||
rspec (= 2.99.0)
|
||||
rspec-given
|
||||
rspec-rails
|
||||
rtlit
|
||||
ruby-readability
|
||||
sanitize
|
||||
sass
|
||||
sass-rails!
|
||||
seed-fu!
|
||||
shoulda
|
||||
sidekiq
|
||||
simple-rss
|
||||
simplecov
|
||||
sinatra
|
||||
spork-rails
|
||||
stackprof
|
||||
stringex
|
||||
therubyracer
|
||||
thin
|
||||
timecop
|
||||
uglifier
|
||||
unf
|
||||
unicorn
|
15
README.md
15
README.md
@ -10,13 +10,14 @@ To learn more about the philosophy and goals of the project, [visit **discourse.
|
||||
|
||||
## Screenshots
|
||||
|
||||
[](http://bbs.boingboing.net)
|
||||
[](http://discuss.howtogeek.com)
|
||||
[](http://discuss.newrelic.com)
|
||||
[](https://talk.turtlerockstudios.com/)
|
||||
[](http://discuss.atom.io)
|
||||
[](http://discourse.soylent.me)
|
||||
[](http://bbs.boingboing.net)
|
||||
[](http://discuss.howtogeek.com)
|
||||
[](http://discuss.newrelic.com)
|
||||
[](https://talk.turtlerockstudios.com/)
|
||||
[](http://discuss.atom.io)
|
||||
[](http://discourse.soylent.me)
|
||||
|
||||
Browse [lots more notable Discourse instances](http://www.discourse.org/faq/customers/).
|
||||
|
||||
## Development
|
||||
|
||||
@ -82,7 +83,7 @@ The original Discourse code contributors can be found in [**AUTHORS.MD**](docs/A
|
||||
|
||||
## Copyright / License
|
||||
|
||||
Copyright 2014 Civilized Discourse Construction Kit, Inc.
|
||||
Copyright 2014 - 2015 Civilized Discourse Construction Kit, Inc.
|
||||
|
||||
Licensed under the GNU General Public License Version 2.0 (or later);
|
||||
you may not use this work except in compliance with the License.
|
||||
|
21
Vagrantfile
vendored
21
Vagrantfile
vendored
@ -3,8 +3,8 @@
|
||||
# See https://github.com/discourse/discourse/blob/master/docs/VAGRANT.md
|
||||
#
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box= "discourse/discourse-0.9.9.15.box"
|
||||
config.vm.box_url = "https://vagrantcloud.com/discourse/discourse-0.9.9.15.box"
|
||||
config.vm.box = 'discourse/discourse-1.3.0'
|
||||
config.vm.box_url = "http://discourse-vms.s3.amazonaws.com/discourse-1.3.0.box"
|
||||
|
||||
# Make this VM reachable on the host network as well, so that other
|
||||
# VM's running other browsers can access our dev server.
|
||||
@ -45,21 +45,4 @@ Vagrant.configure("2") do |config|
|
||||
nfs_setting = RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/
|
||||
config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", :nfs => nfs_setting
|
||||
|
||||
config.vm.provision :shell, :inline => "apt-get -qq update && apt-get -qq -y install ruby1.9.3 build-essential && gem install chef --no-rdoc --no-ri --conservative"
|
||||
|
||||
chef_cookbooks_path = ["chef/cookbooks"]
|
||||
|
||||
# This run uses the updated chef-solo and does normal configuration
|
||||
config.vm.provision :chef_solo do |chef|
|
||||
chef.binary_env = "GEM_HOME=/opt/chef/embedded/lib/ruby/gems/1.9.1/ GEM_PATH= "
|
||||
chef.binary_path = "/opt/chef/bin/"
|
||||
chef.cookbooks_path = chef_cookbooks_path
|
||||
|
||||
chef.add_recipe "recipe[apt]"
|
||||
chef.add_recipe "recipe[build-essential]"
|
||||
chef.add_recipe "recipe[vim]"
|
||||
chef.add_recipe "recipe[java]"
|
||||
chef.add_recipe "recipe[imagemagick]"
|
||||
chef.add_recipe "discourse"
|
||||
end
|
||||
end
|
||||
|
Binary file not shown.
BIN
app/assets/fonts/fontawesome-webfont.eot
Executable file → Normal file
BIN
app/assets/fonts/fontawesome-webfont.eot
Executable file → Normal file
Binary file not shown.
989
app/assets/fonts/fontawesome-webfont.svg
Executable file → Normal file
989
app/assets/fonts/fontawesome-webfont.svg
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 306 KiB |
BIN
app/assets/fonts/fontawesome-webfont.ttf
Executable file → Normal file
BIN
app/assets/fonts/fontawesome-webfont.ttf
Executable file → Normal file
Binary file not shown.
BIN
app/assets/fonts/fontawesome-webfont.woff
Executable file → Normal file
BIN
app/assets/fonts/fontawesome-webfont.woff
Executable file → Normal file
Binary file not shown.
BIN
app/assets/fonts/fontawesome-webfont.woff2
Normal file
BIN
app/assets/fonts/fontawesome-webfont.woff2
Normal file
Binary file not shown.
53
app/assets/javascripts/admin/components/ace-editor.js.es6
Normal file
53
app/assets/javascripts/admin/components/ace-editor.js.es6
Normal file
@ -0,0 +1,53 @@
|
||||
/* global ace:true */
|
||||
import loadScript from 'discourse/lib/load-script';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
mode: 'css',
|
||||
classNames: ['ace-wrapper'],
|
||||
_editor: null,
|
||||
_skipContentChangeEvent: null,
|
||||
|
||||
contentChanged: function() {
|
||||
if (this._editor && !this._skipContentChangeEvent) {
|
||||
this._editor.getSession().setValue(this.get('content'));
|
||||
}
|
||||
}.observes('content'),
|
||||
|
||||
render(buffer) {
|
||||
buffer.push("<div class='ace'>");
|
||||
if (this.get('content')) {
|
||||
buffer.push(Handlebars.Utils.escapeExpression(this.get('content')));
|
||||
}
|
||||
buffer.push("</div>");
|
||||
},
|
||||
|
||||
_destroyEditor: function() {
|
||||
if (this._editor) {
|
||||
this._editor.destroy();
|
||||
this._editor = null;
|
||||
}
|
||||
}.on('willDestroyElement'),
|
||||
|
||||
_initEditor: function() {
|
||||
const self = this;
|
||||
|
||||
loadScript("/javascripts/ace/ace.js", { scriptTag: true }).then(function() {
|
||||
ace.require(['ace/ace'], function(loadedAce) {
|
||||
const editor = loadedAce.edit(self.$('.ace')[0]);
|
||||
|
||||
editor.setTheme("ace/theme/chrome");
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.getSession().setMode("ace/mode/" + self.get('mode'));
|
||||
editor.on('change', function() {
|
||||
self._skipContentChangeEvent = true;
|
||||
self.set('content', editor.getSession().getValue());
|
||||
self._skipContentChangeEvent = false;
|
||||
});
|
||||
|
||||
self.$().data('editor', editor);
|
||||
self._editor = editor;
|
||||
});
|
||||
});
|
||||
|
||||
}.on('didInsertElement')
|
||||
});
|
@ -1,31 +1,36 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'div',
|
||||
|
||||
didInsertElement: function(){
|
||||
_init: function(){
|
||||
this.$("input").select2({
|
||||
multiple: true,
|
||||
width: '100%',
|
||||
query: function(opts){
|
||||
opts.callback({
|
||||
results: this.get("available").map(this._format)
|
||||
});
|
||||
query: function(opts) {
|
||||
opts.callback({ results: this.get("available").map(this._format) });
|
||||
}.bind(this)
|
||||
}).on("change", function(evt) {
|
||||
if (evt.added){
|
||||
this.triggerAction({action: "groupAdded",
|
||||
actionContext: this.get("available"
|
||||
).findBy("id", evt.added.id)});
|
||||
this.triggerAction({
|
||||
action: "groupAdded",
|
||||
actionContext: this.get("available").findBy("id", evt.added.id)
|
||||
});
|
||||
} else if (evt.removed) {
|
||||
this.triggerAction({action:"groupRemoved",
|
||||
actionContext: this.get("selected"
|
||||
).findBy("id", evt.removed.id)});
|
||||
this.triggerAction({
|
||||
action:"groupRemoved",
|
||||
actionContext: evt.removed.id
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
this._refreshOnReset();
|
||||
},
|
||||
|
||||
_format: function(item){
|
||||
return {"text": item.name, "id": item.id, "locked": item.automatic};
|
||||
this._refreshOnReset();
|
||||
}.on("didInsertElement"),
|
||||
|
||||
_format(item) {
|
||||
return {
|
||||
"text": item.name,
|
||||
"id": item.id,
|
||||
"locked": item.automatic
|
||||
};
|
||||
},
|
||||
|
||||
_refreshOnReset: function() {
|
||||
|
@ -0,0 +1,18 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'li',
|
||||
classNameBindings: ['active'],
|
||||
|
||||
router: function() {
|
||||
return this.container.lookup('router:main');
|
||||
}.property(),
|
||||
|
||||
active: function() {
|
||||
const route = this.get('route');
|
||||
if (!route) { return; }
|
||||
|
||||
const routeParam = this.get('routeParam'),
|
||||
router = this.get('router');
|
||||
|
||||
return routeParam ? router.isActive(route, routeParam) : router.isActive(route);
|
||||
}.property('router.url', 'route')
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr'
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr'
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr'
|
||||
});
|
@ -21,7 +21,8 @@ export default Ember.Component.extend({
|
||||
tokenSeparators: ["|"],
|
||||
tags : this.get("choices") || [],
|
||||
width: 'off',
|
||||
dropdownCss: this.get("choices") ? {} : {display: 'none'}
|
||||
dropdownCss: this.get("choices") ? {} : {display: 'none'},
|
||||
selectOnBlur: this.get("choices") ? false : true
|
||||
};
|
||||
|
||||
var settingName = this.get('settingName');
|
||||
|
@ -14,6 +14,7 @@ Discourse.ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuff
|
||||
tagName: "button",
|
||||
classNames: ["btn", "ru"],
|
||||
classNameBindings: ["isUploading"],
|
||||
attributeBindings: ["translatedTitle:title"],
|
||||
|
||||
resumable: null,
|
||||
|
||||
@ -22,6 +23,11 @@ Discourse.ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuff
|
||||
|
||||
rerenderTriggers: ['isUploading', 'progress'],
|
||||
|
||||
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") + " %";
|
||||
|
91
app/assets/javascripts/admin/components/site-setting.js.es6
Normal file
91
app/assets/javascripts/admin/components/site-setting.js.es6
Normal file
@ -0,0 +1,91 @@
|
||||
import BufferedContent from 'discourse/mixins/buffered-content';
|
||||
import SiteSetting from 'admin/models/site-setting';
|
||||
|
||||
const CustomTypes = ['bool', 'enum', 'list', 'url_list'];
|
||||
|
||||
export default Ember.Component.extend(BufferedContent, Discourse.ScrollTop, {
|
||||
classNameBindings: [':row', ':setting', 'setting.overridden', 'typeClass'],
|
||||
content: Ember.computed.alias('setting'),
|
||||
dirty: Discourse.computed.propertyNotEqual('buffered.value', 'setting.value'),
|
||||
validationMessage: null,
|
||||
|
||||
preview: function() {
|
||||
const preview = this.get('setting.preview');
|
||||
if (preview) {
|
||||
return new Handlebars.SafeString("<div class='preview'>" +
|
||||
preview.replace("{{value}}", this.get('buffered.value')) +
|
||||
"</div>");
|
||||
}
|
||||
}.property('buffered.value'),
|
||||
|
||||
typeClass: function() {
|
||||
return this.get('partialType').replace("_", "-");
|
||||
}.property('partialType'),
|
||||
|
||||
enabled: function(key, value) {
|
||||
if (arguments.length > 1) {
|
||||
this.set('buffered.value', value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
const bufferedValue = this.get('buffered.value');
|
||||
if (Ember.isEmpty(bufferedValue)) { return false; }
|
||||
return bufferedValue === 'true';
|
||||
}.property('buffered.value'),
|
||||
|
||||
settingName: function() {
|
||||
return this.get('setting.setting').replace(/\_/g, ' ');
|
||||
}.property('setting.setting'),
|
||||
|
||||
partialType: function() {
|
||||
let type = this.get('setting.type');
|
||||
return (CustomTypes.indexOf(type) !== -1) ? type : 'string';
|
||||
}.property('setting.type'),
|
||||
|
||||
partialName: function() {
|
||||
return 'admin/templates/site-settings/' + this.get('partialType');
|
||||
}.property('partialType'),
|
||||
|
||||
_watchEnterKey: function() {
|
||||
const self = this;
|
||||
this.$().on("keydown.site-setting-enter", ".input-setting-string", function (e) {
|
||||
if (e.keyCode === 13) { // enter key
|
||||
self._save();
|
||||
}
|
||||
});
|
||||
}.on('didInsertElement'),
|
||||
|
||||
_removeBindings: function() {
|
||||
this.$().off("keydown.site-setting-enter");
|
||||
}.on("willDestroyElement"),
|
||||
|
||||
_save() {
|
||||
const setting = this.get('buffered');
|
||||
const self = this;
|
||||
SiteSetting.update(setting.get('setting'), setting.get('value')).then(function() {
|
||||
self.set('validationMessage', null);
|
||||
self.commitBuffer();
|
||||
}).catch(function(e) {
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
self.set('validationMessage', e.responseJSON.errors[0]);
|
||||
} else {
|
||||
self.set('validationMessage', I18n.t('generic_error'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this._save();
|
||||
},
|
||||
|
||||
resetDefault() {
|
||||
this.set('buffered.value', this.get('setting.default'));
|
||||
this._save();
|
||||
},
|
||||
|
||||
cancel() {
|
||||
this.rollbackBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
32
app/assets/javascripts/admin/components/url-list.js.es6
Normal file
32
app/assets/javascripts/admin/components/url-list.js.es6
Normal file
@ -0,0 +1,32 @@
|
||||
export default Ember.Component.extend({
|
||||
_setupUrls: function() {
|
||||
const value = this.get('value');
|
||||
this.set('urls', (value && value.length) ? value.split("\n") : []);
|
||||
}.on('init').observes('value'),
|
||||
|
||||
_urlsChanged: function() {
|
||||
this.set('value', this.get('urls').join("\n"));
|
||||
}.observes('urls.@each'),
|
||||
|
||||
urlInvalid: Ember.computed.empty('newUrl'),
|
||||
|
||||
keyDown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
this.send('addUrl');
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
addUrl() {
|
||||
if (this.get('urlInvalid')) { return; }
|
||||
|
||||
this.get('urls').addObject(this.get('newUrl'));
|
||||
this.set('newUrl', '');
|
||||
},
|
||||
|
||||
removeUrl(url) {
|
||||
const urls = this.get('urls');
|
||||
urls.removeObject(url);
|
||||
}
|
||||
}
|
||||
});
|
@ -1,49 +1,24 @@
|
||||
export default Ember.ArrayController.extend({
|
||||
needs: ["adminBackups"],
|
||||
status: Em.computed.alias("controllers.adminBackups"),
|
||||
|
||||
uploadText: function() { return I18n.t("admin.backups.upload.text"); }.property(),
|
||||
|
||||
readOnlyModeDisabled: Em.computed.alias("status.isOperationRunning"),
|
||||
|
||||
isOperationRunning: Em.computed.alias("status.isOperationRunning"),
|
||||
restoreDisabled: Em.computed.alias("status.restoreDisabled"),
|
||||
|
||||
uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(),
|
||||
|
||||
restoreTitle: function() {
|
||||
if (!this.get('status.allowRestore')) {
|
||||
return I18n.t("admin.backups.operations.restore.is_disabled");
|
||||
return "admin.backups.operations.restore.is_disabled";
|
||||
} else if (this.get("status.isOperationRunning")) {
|
||||
return I18n.t("admin.backups.operation_already_running");
|
||||
return "admin.backups.operations.is_running";
|
||||
} else {
|
||||
return I18n.t("admin.backups.operations.restore.title");
|
||||
return "admin.backups.operations.restore.title";
|
||||
}
|
||||
}.property("status.isOperationRunning"),
|
||||
|
||||
destroyDisabled: Em.computed.alias("status.isOperationRunning"),
|
||||
|
||||
destroyTitle: function() {
|
||||
if (this.get("status.isOperationRunning")) {
|
||||
return I18n.t("admin.backups.operation_already_running");
|
||||
} else {
|
||||
return I18n.t("admin.backups.operations.destroy.title");
|
||||
}
|
||||
}.property("status.isOperationRunning"),
|
||||
|
||||
readOnlyModeTitle: function() { return this._readOnlyModeI18n("title"); }.property("site.isReadOnly"),
|
||||
readOnlyModeText: function() { return this._readOnlyModeI18n("text"); }.property("site.isReadOnly"),
|
||||
|
||||
_readOnlyModeI18n: function(value) {
|
||||
var action = this.site.get("isReadOnly") ? "disable" : "enable";
|
||||
return I18n.t("admin.backups.read_only." + action + "." + value);
|
||||
},
|
||||
}.property("status.{allowRestore,isOperationRunning}"),
|
||||
|
||||
actions: {
|
||||
|
||||
/**
|
||||
Toggle read-only mode
|
||||
|
||||
@method toggleReadOnlyMode
|
||||
**/
|
||||
toggleReadOnlyMode: function() {
|
||||
toggleReadOnlyMode() {
|
||||
var self = this;
|
||||
if (!this.site.get("isReadOnly")) {
|
||||
bootbox.confirm(
|
||||
@ -64,7 +39,7 @@ export default Ember.ArrayController.extend({
|
||||
|
||||
},
|
||||
|
||||
_toggleReadOnlyMode: function(enable) {
|
||||
_toggleReadOnlyMode(enable) {
|
||||
var site = this.site;
|
||||
Discourse.ajax("/admin/backups/readonly", {
|
||||
type: "PUT",
|
||||
|
@ -53,6 +53,9 @@ export default Ember.Controller.extend({
|
||||
actions: {
|
||||
refreshProblems: function() {
|
||||
this.loadProblems();
|
||||
},
|
||||
showTrafficReport: function() {
|
||||
this.set("showTrafficReport", true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ export default Ember.ArrayController.extend({
|
||||
adminActiveFlagsView: Em.computed.equal("query", "active"),
|
||||
|
||||
actions: {
|
||||
disagreeFlags: function (flaggedPost) {
|
||||
disagreeFlags(flaggedPost) {
|
||||
var self = this;
|
||||
flaggedPost.disagreeFlags().then(function () {
|
||||
self.removeObject(flaggedPost);
|
||||
@ -14,7 +14,7 @@ export default Ember.ArrayController.extend({
|
||||
});
|
||||
},
|
||||
|
||||
deferFlags: function (flaggedPost) {
|
||||
deferFlags(flaggedPost) {
|
||||
var self = this;
|
||||
flaggedPost.deferFlags().then(function () {
|
||||
self.removeObject(flaggedPost);
|
||||
@ -23,12 +23,12 @@ export default Ember.ArrayController.extend({
|
||||
});
|
||||
},
|
||||
|
||||
doneTopicFlags: function(item) {
|
||||
doneTopicFlags(item) {
|
||||
this.send("disagreeFlags", item);
|
||||
},
|
||||
},
|
||||
|
||||
loadMore: function(){
|
||||
loadMore(){
|
||||
var flags = this.get("model");
|
||||
return Discourse.FlaggedPost.findAll(this.get("query"),flags.length+1).then(function(data){
|
||||
if(data.length===0){
|
||||
|
@ -3,12 +3,12 @@ export default Em.ObjectController.extend({
|
||||
disableSave: false,
|
||||
|
||||
currentPage: function() {
|
||||
if (this.get("user_count") == 0) { return 0; }
|
||||
if (this.get("user_count") === 0) { return 0; }
|
||||
return Math.floor(this.get("offset") / this.get("limit")) + 1;
|
||||
}.property("limit", "offset", "user_count"),
|
||||
|
||||
totalPages: function() {
|
||||
if (this.get("user_count") == 0) { return 0; }
|
||||
if (this.get("user_count") === 0) { return 0; }
|
||||
return Math.floor(this.get("user_count") / this.get("limit")) + 1;
|
||||
}.property("limit", "user_count"),
|
||||
|
||||
@ -25,31 +25,31 @@ export default Em.ObjectController.extend({
|
||||
}.property(),
|
||||
|
||||
actions: {
|
||||
next: function() {
|
||||
next() {
|
||||
if (this.get("showingLast")) { return; }
|
||||
|
||||
var group = this.get("model"),
|
||||
offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count"));
|
||||
const group = this.get("model"),
|
||||
offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count"));
|
||||
|
||||
group.set("offset", offset);
|
||||
|
||||
return group.findMembers();
|
||||
},
|
||||
|
||||
previous: function() {
|
||||
previous() {
|
||||
if (this.get("showingFirst")) { return; }
|
||||
|
||||
var group = this.get("model"),
|
||||
offset = Math.max(group.get("offset") - group.get("limit"), 0);
|
||||
const group = this.get("model"),
|
||||
offset = Math.max(group.get("offset") - group.get("limit"), 0);
|
||||
|
||||
group.set("offset", offset);
|
||||
|
||||
return group.findMembers();
|
||||
},
|
||||
|
||||
removeMember: function(member) {
|
||||
var self = this,
|
||||
message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("name") });
|
||||
removeMember(member) {
|
||||
const self = this,
|
||||
message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("name") });
|
||||
return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), function(confirm) {
|
||||
if (confirm) {
|
||||
self.get("model").removeMember(member);
|
||||
@ -57,57 +57,49 @@ export default Em.ObjectController.extend({
|
||||
});
|
||||
},
|
||||
|
||||
addMembers: function() {
|
||||
addMembers() {
|
||||
if (Em.isEmpty(this.get("usernames"))) { return; }
|
||||
this.get("model").addMembers(this.get("usernames"));
|
||||
// clear the user selector
|
||||
this.set("usernames", null);
|
||||
},
|
||||
|
||||
save: function() {
|
||||
var self = this,
|
||||
group = this.get('model'),
|
||||
groupsController = this.get("controllers.adminGroupsType");
|
||||
save() {
|
||||
const group = this.get('model'),
|
||||
groupsController = this.get("controllers.adminGroupsType");
|
||||
|
||||
this.set('disableSave', true);
|
||||
|
||||
var promise;
|
||||
if (group.get("id")) {
|
||||
promise = group.save();
|
||||
} else {
|
||||
promise = group.create().then(function() {
|
||||
groupsController.addObject(group);
|
||||
});
|
||||
}
|
||||
promise.then(function() {
|
||||
self.transitionToRoute("adminGroup", group);
|
||||
}, function(e) {
|
||||
var message = $.parseJSON(e.responseText).errors;
|
||||
bootbox.alert(message);
|
||||
}).finally(function() {
|
||||
self.set('disableSave', false);
|
||||
});
|
||||
let promise = group.get("id") ? group.save() : group.create().then(() => groupsController.addObject(group));
|
||||
|
||||
promise.then(() => this.transitionToRoute("adminGroup", group))
|
||||
.catch(e => bootbox.alert($.parseJSON(e.responseText).errors))
|
||||
.finally(() => this.set('disableSave', false));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
var group = this.get('model'),
|
||||
groupsController = this.get('controllers.adminGroupsType'),
|
||||
self = this;
|
||||
destroy() {
|
||||
const group = this.get('model'),
|
||||
groupsController = this.get('controllers.adminGroupsType'),
|
||||
self = this;
|
||||
|
||||
this.set('disableSave', true);
|
||||
|
||||
bootbox.confirm(I18n.t("admin.groups.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(confirmed) {
|
||||
if (confirmed) {
|
||||
group.destroy().then(function() {
|
||||
groupsController.get('model').removeObject(group);
|
||||
self.transitionToRoute('adminGroups.index');
|
||||
}, function() {
|
||||
bootbox.alert(I18n.t("admin.groups.delete_failed"));
|
||||
}).finally(function() {
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.groups.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
function(confirmed) {
|
||||
if (confirmed) {
|
||||
group.destroy().then(() => {
|
||||
groupsController.get('model').removeObject(group);
|
||||
self.transitionToRoute('adminGroups.index');
|
||||
}).catch(() => bootbox.alert(I18n.t("admin.groups.delete_failed")))
|
||||
.finally(() => self.set('disableSave', false));
|
||||
} else {
|
||||
self.set('disableSave', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,9 @@
|
||||
export default Ember.ArrayController.extend({
|
||||
sortProperties: ['name'],
|
||||
refreshingAutoGroups: false,
|
||||
isAuto: function(){
|
||||
return this.get('type') === 'automatic';
|
||||
}.property('type'),
|
||||
|
||||
actions: {
|
||||
refreshAutoGroups: function(){
|
||||
|
@ -3,8 +3,8 @@ export default Ember.ObjectController.extend({
|
||||
savedIpAddress: null,
|
||||
|
||||
isRange: function() {
|
||||
return this.get("ip_address").indexOf("/") > 0;
|
||||
}.property("ip_address"),
|
||||
return this.get("model.ip_address").indexOf("/") > 0;
|
||||
}.property("model.ip_address"),
|
||||
|
||||
actions: {
|
||||
allow: function(record) {
|
||||
@ -19,14 +19,14 @@ export default Ember.ObjectController.extend({
|
||||
|
||||
edit: function() {
|
||||
if (!this.get('editing')) {
|
||||
this.savedIpAddress = this.get('ip_address');
|
||||
this.savedIpAddress = this.get('model.ip_address');
|
||||
}
|
||||
this.set('editing', true);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this.get('savedIpAddress') && this.get('editing')) {
|
||||
this.set('ip_address', this.get('savedIpAddress'));
|
||||
this.set('model.ip_address', this.get('savedIpAddress'));
|
||||
}
|
||||
this.set('editing', false);
|
||||
},
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { outputExportResult } from 'discourse/lib/export-result';
|
||||
|
||||
export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
export default Ember.ArrayController.extend({
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
clearBlock: function(row){
|
||||
clearBlock(row){
|
||||
row.clearBlock().then(function(){
|
||||
// feeling lazy
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
|
||||
exportScreenedEmailList: function(subject) {
|
||||
exportScreenedEmailList() {
|
||||
Discourse.ExportCsv.exportScreenedEmailList().then(outputExportResult);
|
||||
}
|
||||
},
|
||||
|
||||
show: function() {
|
||||
show() {
|
||||
var self = this;
|
||||
self.set('loading', true);
|
||||
Discourse.ScreenedEmail.findAll().then(function(result) {
|
||||
|
@ -1,28 +1,29 @@
|
||||
import { outputExportResult } from 'discourse/lib/export-result';
|
||||
|
||||
export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
export default Ember.ArrayController.extend({
|
||||
loading: false,
|
||||
itemController: 'admin-log-screened-ip-address',
|
||||
filter: null,
|
||||
|
||||
show: function() {
|
||||
show: Discourse.debounce(function() {
|
||||
var self = this;
|
||||
self.set('loading', true);
|
||||
Discourse.ScreenedIpAddress.findAll().then(function(result) {
|
||||
Discourse.ScreenedIpAddress.findAll(this.get("filter")).then(function(result) {
|
||||
self.set('model', result);
|
||||
self.set('loading', false);
|
||||
});
|
||||
},
|
||||
}, 250).observes("filter"),
|
||||
|
||||
actions: {
|
||||
recordAdded: function(arg) {
|
||||
recordAdded(arg) {
|
||||
this.get("model").unshiftObject(arg);
|
||||
},
|
||||
|
||||
rollUp: function() {
|
||||
var self = this;
|
||||
rollUp() {
|
||||
const self = this;
|
||||
return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) {
|
||||
if (confirmed) {
|
||||
self.set("loading", true)
|
||||
self.set("loading", true);
|
||||
return Discourse.ScreenedIpAddress.rollUp().then(function(results) {
|
||||
if (results && results.subnets) {
|
||||
if (results.subnets.length > 0) {
|
||||
@ -38,7 +39,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
});
|
||||
},
|
||||
|
||||
exportScreenedIpList: function(subject) {
|
||||
exportScreenedIpList() {
|
||||
Discourse.ExportCsv.exportScreenedIpList().then(outputExportResult);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { outputExportResult } from 'discourse/lib/export-result';
|
||||
|
||||
export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
export default Ember.ArrayController.extend({
|
||||
loading: false,
|
||||
|
||||
show: function() {
|
||||
var self = this;
|
||||
show() {
|
||||
const self = this;
|
||||
self.set('loading', true);
|
||||
Discourse.ScreenedUrl.findAll().then(function(result) {
|
||||
self.set('model', result);
|
||||
@ -13,7 +13,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
exportScreenedUrlList: function(subject) {
|
||||
exportScreenedUrlList() {
|
||||
Discourse.ExportCsv.exportScreenedUrlList().then(outputExportResult);
|
||||
}
|
||||
}
|
||||
|
@ -1,70 +1,97 @@
|
||||
/**
|
||||
This controller supports the interface for listing staff action logs in the admin section.
|
||||
|
||||
@class AdminLogsStaffActionLogsController
|
||||
@extends Ember.ArrayController
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
import { outputExportResult } from 'discourse/lib/export-result';
|
||||
|
||||
export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
export default Ember.ArrayController.extend({
|
||||
loading: false,
|
||||
filters: {},
|
||||
filters: null,
|
||||
|
||||
show: function() {
|
||||
var self = this;
|
||||
this.set('loading', true);
|
||||
Discourse.URL.set('queryParams', this.get('filters')); // TODO: doesn't work
|
||||
Discourse.StaffActionLog.findAll(this.get('filters')).then(function(result) {
|
||||
self.set('model', result);
|
||||
self.set('loading', false);
|
||||
});
|
||||
}.observes('filters.action_name', 'filters.acting_user', 'filters.target_user', 'filters.subject'),
|
||||
|
||||
filtersExists: function() {
|
||||
return (_.size(this.get('filters')) > 0);
|
||||
}.property('filters.action_name', 'filters.acting_user', 'filters.target_user', 'filters.subject'),
|
||||
filtersExists: Ember.computed.gt('filterCount', 0),
|
||||
|
||||
actionFilter: function() {
|
||||
if (this.get('filters.action_name')) {
|
||||
return I18n.t("admin.logs.staff_actions.actions." + this.get('filters.action_name'));
|
||||
var name = this.get('filters.action_name');
|
||||
if (name) {
|
||||
return I18n.t("admin.logs.staff_actions.actions." + name);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}.property('filters.action_name'),
|
||||
|
||||
showInstructions: function() {
|
||||
return this.get('model.length') > 0;
|
||||
}.property('loading', 'model.length'),
|
||||
showInstructions: Ember.computed.gt('model.length', 0),
|
||||
|
||||
refresh: function() {
|
||||
var self = this;
|
||||
this.set('loading', true);
|
||||
|
||||
var filters = this.get('filters'),
|
||||
params = {},
|
||||
count = 0;
|
||||
|
||||
// Don't send null values
|
||||
Object.keys(filters).forEach(function(k) {
|
||||
var val = filters.get(k);
|
||||
if (val) {
|
||||
params[k] = val;
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
this.set('filterCount', count);
|
||||
|
||||
Discourse.StaffActionLog.findAll(params).then(function(result) {
|
||||
self.set('model', result);
|
||||
}).finally(function() {
|
||||
self.set('loading', false);
|
||||
});
|
||||
},
|
||||
|
||||
resetFilters: function() {
|
||||
this.set('filters', Ember.Object.create());
|
||||
this.refresh();
|
||||
}.on('init'),
|
||||
|
||||
_changeFilters: function(props) {
|
||||
this.get('filters').setProperties(props);
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
actions: {
|
||||
clearFilter: function(key) {
|
||||
delete this.get('filters')[key];
|
||||
this.notifyPropertyChange('filters');
|
||||
var changed = {};
|
||||
|
||||
// Special case, clear all action related stuff
|
||||
if (key === 'actionFilter') {
|
||||
changed.action_name = null;
|
||||
changed.action_id = null;
|
||||
changed.custom_type = null;
|
||||
} else {
|
||||
changed[key] = null;
|
||||
}
|
||||
this._changeFilters(changed);
|
||||
},
|
||||
|
||||
clearAllFilters: function() {
|
||||
this.set('filters', {});
|
||||
this.resetFilters();
|
||||
},
|
||||
|
||||
filterByAction: function(action) {
|
||||
this.set('filters.action_name', action);
|
||||
filterByAction: function(logItem) {
|
||||
this._changeFilters({
|
||||
action_name: logItem.get('action_name'),
|
||||
action_id: logItem.get('action'),
|
||||
custom_type: logItem.get('custom_type')
|
||||
});
|
||||
},
|
||||
|
||||
filterByStaffUser: function(acting_user) {
|
||||
this.set('filters.acting_user', acting_user.username);
|
||||
this._changeFilters({ acting_user: acting_user.username });
|
||||
},
|
||||
|
||||
filterByTargetUser: function(target_user) {
|
||||
this.set('filters.target_user', target_user.username);
|
||||
this._changeFilters({ target_user: target_user.username });
|
||||
},
|
||||
|
||||
filterBySubject: function(subject) {
|
||||
this.set('filters.subject', subject);
|
||||
this._changeFilters({ subject: subject });
|
||||
},
|
||||
|
||||
exportStaffActionLogs: function(subject) {
|
||||
exportStaffActionLogs: function() {
|
||||
Discourse.ExportCsv.exportStaffActionLogs().then(outputExportResult);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
export default Ember.ArrayController.extend({
|
||||
|
||||
adminRoutes: function() {
|
||||
return this.get('model').map(p => p.admin_route).compact();
|
||||
}.property()
|
||||
});
|
@ -5,46 +5,12 @@ export default Ember.ObjectController.extend({
|
||||
filteredContent: function() {
|
||||
if (!this.get('categoryNameKey')) { return []; }
|
||||
|
||||
var category = this.get('controllers.adminSiteSettings.content').findProperty('nameKey', this.get('categoryNameKey'));
|
||||
const category = this.get('controllers.adminSiteSettings.content').findProperty('nameKey', this.get('categoryNameKey'));
|
||||
if (category) {
|
||||
return category.siteSettings;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}.property('controllers.adminSiteSettings.content', 'categoryNameKey'),
|
||||
|
||||
actions: {
|
||||
|
||||
/**
|
||||
Reset a setting to its default value
|
||||
|
||||
@method resetDefault
|
||||
@param {Discourse.SiteSetting} setting The setting we want to revert
|
||||
**/
|
||||
resetDefault: function(setting) {
|
||||
setting.set('value', setting.get('default'));
|
||||
setting.save();
|
||||
},
|
||||
|
||||
/**
|
||||
Save changes to a site setting
|
||||
|
||||
@method save
|
||||
@param {Discourse.SiteSetting} setting The setting we've changed
|
||||
**/
|
||||
save: function(setting) {
|
||||
setting.save();
|
||||
},
|
||||
|
||||
/**
|
||||
Cancel changes to a site setting
|
||||
|
||||
@method cancel
|
||||
@param {Discourse.SiteSetting} setting The setting we've changed but want to revert
|
||||
**/
|
||||
cancel: function(setting) {
|
||||
setting.resetValue();
|
||||
}
|
||||
}
|
||||
}.property('controllers.adminSiteSettings.content', 'categoryNameKey')
|
||||
|
||||
});
|
||||
|
@ -1,19 +1,16 @@
|
||||
export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
import Presence from 'discourse/mixins/presence';
|
||||
|
||||
export default Ember.ArrayController.extend(Presence, {
|
||||
filter: null,
|
||||
onlyOverridden: false,
|
||||
filtered: Ember.computed.notEmpty('filter'),
|
||||
|
||||
/**
|
||||
The list of settings based on the current filters
|
||||
|
||||
@property filterContent
|
||||
**/
|
||||
filterContent: Discourse.debounce(function() {
|
||||
|
||||
// If we have no content, don't bother filtering anything
|
||||
if (!this.present('allSiteSettings')) return;
|
||||
|
||||
var filter;
|
||||
let filter;
|
||||
if (this.get('filter')) {
|
||||
filter = this.get('filter').toLowerCase();
|
||||
}
|
||||
@ -24,12 +21,11 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
matches,
|
||||
matchesGroupedByCategory = Em.A([{nameKey: 'all_results', name: I18n.t('admin.site_settings.categories.all_results'), siteSettings: []}]);
|
||||
const self = this,
|
||||
matchesGroupedByCategory = [{nameKey: 'all_results', name: I18n.t('admin.site_settings.categories.all_results'), siteSettings: []}];
|
||||
|
||||
_.each(this.get('allSiteSettings'), function(settingsCategory) {
|
||||
matches = settingsCategory.siteSettings.filter(function(item) {
|
||||
this.get('allSiteSettings').forEach(function(settingsCategory) {
|
||||
const matches = settingsCategory.siteSettings.filter(function(item) {
|
||||
if (self.get('onlyOverridden') && !item.get('overridden')) return false;
|
||||
if (filter) {
|
||||
if (item.get('setting').toLowerCase().indexOf(filter) > -1) return true;
|
||||
@ -51,7 +47,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
|
||||
}, 250).observes('filter', 'onlyOverridden'),
|
||||
|
||||
actions: {
|
||||
clearFilter: function() {
|
||||
clearFilter() {
|
||||
this.setProperties({
|
||||
filter: '',
|
||||
onlyOverridden: false
|
||||
|
@ -13,6 +13,42 @@ export default Ember.ArrayController.extend({
|
||||
sortProperties: ['granted_at'],
|
||||
sortAscending: false,
|
||||
|
||||
groupedBadges: function(){
|
||||
const badges = this.get('model');
|
||||
|
||||
var grouped = _.groupBy(badges, badge => badge.badge_id);
|
||||
|
||||
var expanded = [];
|
||||
const expandedBadges = badges.get('expandedBadges');
|
||||
|
||||
_(grouped).each(function(badges){
|
||||
var lastGranted = badges[0].granted_at;
|
||||
|
||||
_.each(badges, function(badge) {
|
||||
lastGranted = lastGranted < badge.granted_at ? badge.granted_at : lastGranted;
|
||||
});
|
||||
|
||||
if(badges.length===1 || _.include(expandedBadges, badges[0].badge.id)){
|
||||
_.each(badges, badge => expanded.push(badge));
|
||||
return;
|
||||
}
|
||||
|
||||
var result = {
|
||||
badge: badges[0].badge,
|
||||
granted_at: lastGranted,
|
||||
badges: badges,
|
||||
count: badges.length,
|
||||
grouped: true
|
||||
};
|
||||
|
||||
expanded.push(result);
|
||||
});
|
||||
|
||||
return _(expanded).sortBy(group => group.granted_at).reverse().value();
|
||||
|
||||
|
||||
}.property('model', 'model.@each', 'model.expandedBadges.@each'),
|
||||
|
||||
/**
|
||||
Array of badges that have not been granted to this user.
|
||||
|
||||
@ -32,7 +68,7 @@ export default Ember.ArrayController.extend({
|
||||
}
|
||||
});
|
||||
|
||||
return badges;
|
||||
return _.sortBy(badges, "name");
|
||||
}.property('badges.@each', 'model.@each'),
|
||||
|
||||
/**
|
||||
@ -45,6 +81,12 @@ export default Ember.ArrayController.extend({
|
||||
|
||||
actions: {
|
||||
|
||||
expandGroup: function(userBadge){
|
||||
const model = this.get('model');
|
||||
model.set('expandedBadges', model.get('expandedBadges') || []);
|
||||
model.get('expandedBadges').pushObject(userBadge.badge.id);
|
||||
},
|
||||
|
||||
/**
|
||||
Grant the selected badge to the user.
|
||||
|
||||
@ -53,7 +95,8 @@ export default Ember.ArrayController.extend({
|
||||
**/
|
||||
grantBadge: function(badgeId) {
|
||||
var self = this;
|
||||
Discourse.UserBadge.grant(badgeId, this.get('user.username')).then(function(userBadge) {
|
||||
Discourse.UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) {
|
||||
self.set('badgeReason', '');
|
||||
self.pushObject(userBadge);
|
||||
Ember.run.next(function() {
|
||||
// Update the selected badge ID after the combobox has re-rendered.
|
||||
|
@ -11,58 +11,61 @@ export default ObjectController.extend(CanCheckEmails, {
|
||||
|
||||
primaryGroupDirty: Discourse.computed.propertyNotEqual('originalPrimaryGroupId', 'primary_group_id'),
|
||||
|
||||
custom_groups: Ember.computed.filter("model.groups", function(g){
|
||||
return (!g.automatic && g.visible);
|
||||
}),
|
||||
automaticGroups: function() {
|
||||
return this.get("model.automaticGroups").map((g) => g.name).join(", ");
|
||||
}.property("model.automaticGroups"),
|
||||
|
||||
userFields: function() {
|
||||
var siteUserFields = this.site.get('user_fields'),
|
||||
userFields = this.get('user_fields');
|
||||
const siteUserFields = this.site.get('user_fields'),
|
||||
userFields = this.get('user_fields');
|
||||
|
||||
if (!Ember.isEmpty(siteUserFields)) {
|
||||
return siteUserFields.map(function(uf) {
|
||||
var value = userFields ? userFields[uf.get('id').toString()] : null;
|
||||
return {name: uf.get('name'), value: value};
|
||||
let value = userFields ? userFields[uf.get('id').toString()] : null;
|
||||
return { name: uf.get('name'), value: value };
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}.property('user_fields.@each'),
|
||||
|
||||
actions: {
|
||||
toggleTitleEdit: function() {
|
||||
toggleTitleEdit() {
|
||||
this.toggleProperty('editingTitle');
|
||||
},
|
||||
|
||||
saveTitle: function() {
|
||||
Discourse.ajax("/users/" + this.get('username').toLowerCase(), {
|
||||
saveTitle() {
|
||||
const self = this;
|
||||
|
||||
return Discourse.ajax("/users/" + this.get('username').toLowerCase(), {
|
||||
data: {title: this.get('title')},
|
||||
type: 'PUT'
|
||||
}).then(null, function(e){
|
||||
}).catch(function(e) {
|
||||
bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body}));
|
||||
}).finally(function() {
|
||||
self.send('toggleTitleEdit');
|
||||
});
|
||||
|
||||
this.send('toggleTitleEdit');
|
||||
},
|
||||
|
||||
generateApiKey: function() {
|
||||
generateApiKey() {
|
||||
this.get('model').generateApiKey();
|
||||
},
|
||||
|
||||
groupAdded: function(added){
|
||||
groupAdded(added) {
|
||||
this.get('model').groupAdded(added).catch(function() {
|
||||
bootbox.alert(I18n.t('generic_error'));
|
||||
});
|
||||
},
|
||||
|
||||
groupRemoved: function(removed){
|
||||
this.get('model').groupRemoved(removed).catch(function() {
|
||||
groupRemoved(groupId) {
|
||||
this.get('model').groupRemoved(groupId).catch(function() {
|
||||
bootbox.alert(I18n.t('generic_error'));
|
||||
});
|
||||
},
|
||||
|
||||
savePrimaryGroup: function() {
|
||||
var self = this;
|
||||
Discourse.ajax("/admin/users/" + this.get('id') + "/primary_group", {
|
||||
savePrimaryGroup() {
|
||||
const self = this;
|
||||
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/primary_group", {
|
||||
type: 'PUT',
|
||||
data: {primary_group_id: this.get('primary_group_id')}
|
||||
}).then(function () {
|
||||
@ -72,26 +75,42 @@ export default ObjectController.extend(CanCheckEmails, {
|
||||
});
|
||||
},
|
||||
|
||||
resetPrimaryGroup: function() {
|
||||
resetPrimaryGroup() {
|
||||
this.set('primary_group_id', this.get('originalPrimaryGroupId'));
|
||||
},
|
||||
|
||||
regenerateApiKey: function() {
|
||||
var self = this;
|
||||
bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
||||
if (result) {
|
||||
self.get('model').generateApiKey();
|
||||
regenerateApiKey() {
|
||||
const self = this;
|
||||
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.api.confirm_regen"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
function(result) {
|
||||
if (result) { self.get('model').generateApiKey(); }
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
revokeApiKey: function() {
|
||||
var self = this;
|
||||
bootbox.confirm(I18n.t("admin.api.confirm_revoke"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
||||
if (result) {
|
||||
self.get('model').revokeApiKey();
|
||||
revokeApiKey() {
|
||||
const self = this;
|
||||
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.api.confirm_revoke"),
|
||||
I18n.t("no_value"),
|
||||
I18n.t("yes_value"),
|
||||
function(result) {
|
||||
if (result) { self.get('model').revokeApiKey(); }
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
anonymize() {
|
||||
this.get('model').anonymize();
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this.get('model').destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import Controller from 'discourse/controllers/controller';
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
|
||||
needs: ["adminBackupsLogs"],
|
||||
|
||||
_startBackup: function (withUploads) {
|
||||
@ -17,11 +16,11 @@ export default Controller.extend(ModalFunctionality, {
|
||||
actions: {
|
||||
|
||||
startBackup: function () {
|
||||
return this._startBackup();
|
||||
this._startBackup();
|
||||
},
|
||||
|
||||
startBackupWithoutUpload: function () {
|
||||
return this._startBackup(false);
|
||||
this._startBackup(false);
|
||||
},
|
||||
|
||||
cancel: function () {
|
@ -1,5 +1,4 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
|
||||
import ObjectController from 'discourse/controllers/object';
|
||||
|
||||
export default ObjectController.extend(ModalFunctionality, {
|
||||
@ -19,7 +18,7 @@ export default ObjectController.extend(ModalFunctionality, {
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
var error = I18n.t('admin.user.suspend_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error, function() { self.send('showModal'); });
|
||||
bootbox.alert(error, function() { self.send('reopenModal'); });
|
||||
});
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
|
||||
import ObjectController from 'discourse/controllers/object';
|
||||
|
||||
export default ObjectController.extend(ModalFunctionality, {
|
||||
@ -7,14 +6,16 @@ export default ObjectController.extend(ModalFunctionality, {
|
||||
newSelected: Ember.computed.equal('selectedTab', 'new'),
|
||||
|
||||
onShow: function() {
|
||||
this.selectNew();
|
||||
this.send("selectNew");
|
||||
},
|
||||
|
||||
selectNew: function() {
|
||||
this.set('selectedTab', 'new');
|
||||
},
|
||||
actions: {
|
||||
selectNew: function() {
|
||||
this.set('selectedTab', 'new');
|
||||
},
|
||||
|
||||
selectPrevious: function() {
|
||||
this.set('selectedTab', 'previous');
|
||||
selectPrevious: function() {
|
||||
this.set('selectedTab', 'previous');
|
||||
}
|
||||
}
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
import ChangeSiteCustomizationDetailsController from "admin/controllers/change-site-customization-details";
|
||||
import ChangeSiteCustomizationDetailsController from "admin/controllers/modals/change-site-customization-details";
|
||||
|
||||
export default ChangeSiteCustomizationDetailsController.extend({
|
||||
onShow: function() {
|
||||
this.selectPrevious();
|
||||
this.send("selectPrevious");
|
||||
}
|
||||
});
|
545
app/assets/javascripts/admin/models/admin-user.js.es6
Normal file
545
app/assets/javascripts/admin/models/admin-user.js.es6
Normal file
@ -0,0 +1,545 @@
|
||||
const AdminUser = Discourse.User.extend({
|
||||
|
||||
customGroups: Em.computed.filter("groups", (g) => !g.automatic && Discourse.Group.create(g)),
|
||||
automaticGroups: Em.computed.filter("groups", (g) => g.automatic && Discourse.Group.create(g)),
|
||||
|
||||
generateApiKey() {
|
||||
const self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/generate_api_key", {
|
||||
type: 'POST'
|
||||
}).then(function (result) {
|
||||
const apiKey = Discourse.ApiKey.create(result.api_key);
|
||||
self.set('api_key', apiKey);
|
||||
return apiKey;
|
||||
});
|
||||
},
|
||||
|
||||
groupAdded(added) {
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/groups", {
|
||||
type: 'POST',
|
||||
data: { group_id: added.id }
|
||||
}).then(() => this.get('groups').pushObject(added));
|
||||
},
|
||||
|
||||
groupRemoved(groupId) {
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/groups/" + groupId, {
|
||||
type: 'DELETE'
|
||||
}).then(() => this.set('groups.[]', this.get('groups').rejectBy("id", groupId)));
|
||||
},
|
||||
|
||||
revokeApiKey() {
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_api_key", {
|
||||
type: 'DELETE'
|
||||
}).then(() => this.set('api_key', null));
|
||||
},
|
||||
|
||||
deleteAllPostsExplanation: function() {
|
||||
if (!this.get('can_delete_all_posts')) {
|
||||
if (this.get('deleteForbidden') && this.get('staff')) {
|
||||
return I18n.t('admin.user.delete_posts_forbidden_because_staff');
|
||||
}
|
||||
if (this.get('post_count') > Discourse.SiteSettings.delete_all_posts_max) {
|
||||
return I18n.t('admin.user.cant_delete_all_too_many_posts', {count: Discourse.SiteSettings.delete_all_posts_max});
|
||||
} else {
|
||||
return I18n.t('admin.user.cant_delete_all_posts', {count: Discourse.SiteSettings.delete_user_max_post_age});
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}.property('can_delete_all_posts', 'deleteForbidden'),
|
||||
|
||||
deleteAllPosts() {
|
||||
const user = this,
|
||||
message = I18n.t('admin.user.delete_all_posts_confirm', { posts: user.get('post_count'), topics: user.get('topic_count') }),
|
||||
buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel-inline",
|
||||
"link": true
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("admin.user.delete_all_posts"),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function() {
|
||||
Discourse.ajax("/admin/users/" + user.get('id') + "/delete_all_posts", {
|
||||
type: 'PUT'
|
||||
}).then(() => user.set('post_count', 0));
|
||||
}
|
||||
}];
|
||||
bootbox.dialog(message, buttons, { "classes": "delete-all-posts" });
|
||||
},
|
||||
|
||||
revokeAdmin() {
|
||||
const self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_admin", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
self.setProperties({
|
||||
admin: false,
|
||||
can_grant_admin: true,
|
||||
can_revoke_admin: false
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
grantAdmin() {
|
||||
const self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/grant_admin", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
self.setProperties({
|
||||
admin: true,
|
||||
can_grant_admin: false,
|
||||
can_revoke_admin: true
|
||||
});
|
||||
}).catch(function(e) {
|
||||
let error;
|
||||
if (e.responseJSON && e.responseJSON.error) {
|
||||
error = e.responseJSON.error;
|
||||
}
|
||||
error = error || I18n.t('admin.user.grant_admin_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
revokeModeration() {
|
||||
const self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_moderation", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
self.setProperties({
|
||||
moderator: false,
|
||||
can_grant_moderation: true,
|
||||
can_revoke_moderation: false
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
grantModeration() {
|
||||
const self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/grant_moderation", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
self.setProperties({
|
||||
moderator: true,
|
||||
can_grant_moderation: false,
|
||||
can_revoke_moderation: true
|
||||
});
|
||||
}).catch(function(e) {
|
||||
let error;
|
||||
if (e.responseJSON && e.responseJSON.error) {
|
||||
error = e.responseJSON.error;
|
||||
}
|
||||
error = error || I18n.t('admin.user.grant_moderation_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
refreshBrowsers() {
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/refresh_browsers", {
|
||||
type: 'POST'
|
||||
}).finally(() => bootbox.alert(I18n.t("admin.user.refresh_browsers_message")));
|
||||
},
|
||||
|
||||
approve() {
|
||||
const self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/approve", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
self.setProperties({
|
||||
can_approve: false,
|
||||
approved: true,
|
||||
approved_by: Discourse.User.current()
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setOriginalTrustLevel() {
|
||||
this.set('originalTrustLevel', this.get('trust_level'));
|
||||
},
|
||||
|
||||
trustLevels: function() {
|
||||
return Discourse.Site.currentProp('trustLevels');
|
||||
}.property(),
|
||||
|
||||
dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
|
||||
|
||||
saveTrustLevel() {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/trust_level", {
|
||||
type: 'PUT',
|
||||
data: { level: this.get('trustLevel.id') }
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
let error;
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
error = e.responseJSON.errors[0];
|
||||
}
|
||||
error = error || I18n.t('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
restoreTrustLevel() {
|
||||
this.set('trustLevel.id', this.get('originalTrustLevel'));
|
||||
},
|
||||
|
||||
lockTrustLevel(locked) {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/trust_level_lock", {
|
||||
type: 'PUT',
|
||||
data: { locked: !!locked }
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
let error;
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
error = e.responseJSON.errors[0];
|
||||
}
|
||||
error = error || I18n.t('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
canLockTrustLevel: function() {
|
||||
return this.get('trust_level') < 4;
|
||||
}.property('trust_level'),
|
||||
|
||||
isSuspended: Em.computed.equal('suspended', true),
|
||||
canSuspend: Em.computed.not('staff'),
|
||||
|
||||
suspendDuration: function() {
|
||||
const suspended_at = moment(this.suspended_at),
|
||||
suspended_till = moment(this.suspended_till);
|
||||
return suspended_at.format('L') + " - " + suspended_till.format('L');
|
||||
}.property('suspended_till', 'suspended_at'),
|
||||
|
||||
suspend(duration, reason) {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/suspend", {
|
||||
type: 'PUT',
|
||||
data: { duration: duration, reason: reason }
|
||||
});
|
||||
},
|
||||
|
||||
unsuspend() {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/unsuspend", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.unsuspend_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
log_out() {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/log_out", {
|
||||
type: 'POST',
|
||||
data: { username_or_email: this.get('username') }
|
||||
}).then(function() {
|
||||
bootbox.alert(I18n.t("admin.user.logged_out"));
|
||||
});
|
||||
},
|
||||
|
||||
impersonate() {
|
||||
return Discourse.ajax("/admin/impersonate", {
|
||||
type: 'POST',
|
||||
data: { username_or_email: this.get('username') }
|
||||
}).then(function() {
|
||||
document.location = "/";
|
||||
}).catch(function(e) {
|
||||
if (e.status === 404) {
|
||||
bootbox.alert(I18n.t('admin.impersonate.not_found'));
|
||||
} else {
|
||||
bootbox.alert(I18n.t('admin.impersonate.invalid'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
activate() {
|
||||
return Discourse.ajax('/admin/users/' + this.id + '/activate', {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.activate_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
return Discourse.ajax('/admin/users/' + this.id + '/deactivate', {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.deactivate_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
unblock() {
|
||||
return Discourse.ajax('/admin/users/' + this.id + '/unblock', {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.unblock_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
block() {
|
||||
return Discourse.ajax('/admin/users/' + this.id + '/block', {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
window.location.reload();
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.block_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
sendActivationEmail() {
|
||||
return Discourse.ajax('/users/action/send_activation_email', {
|
||||
type: 'POST',
|
||||
data: { username: this.get('username') }
|
||||
}).then(function() {
|
||||
bootbox.alert( I18n.t('admin.user.activation_email_sent') );
|
||||
}).catch(function(e) {
|
||||
var error = I18n.t('admin.user.send_activation_email_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
anonymizeForbidden: Em.computed.not("can_be_anonymized"),
|
||||
|
||||
anonymize() {
|
||||
const user = this,
|
||||
message = I18n.t("admin.user.anonymize_confirm");
|
||||
|
||||
const performAnonymize = function() {
|
||||
return Discourse.ajax("/admin/users/" + user.get('id') + '/anonymize.json', {
|
||||
type: 'PUT'
|
||||
}).then(function(data) {
|
||||
if (data.success) {
|
||||
if (data.username) {
|
||||
document.location = "/admin/users/" + data.username;
|
||||
} else {
|
||||
document.location = "/admin/users/list/active";
|
||||
}
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.anonymize_failed"));
|
||||
if (data.user) {
|
||||
user.setProperties(data.user);
|
||||
}
|
||||
}
|
||||
}).catch(function() {
|
||||
bootbox.alert(I18n.t("admin.user.anonymize_failed"));
|
||||
});
|
||||
};
|
||||
|
||||
const buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel",
|
||||
"link": true
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.anonymize_yes'),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function() { performAnonymize(); }
|
||||
}];
|
||||
|
||||
bootbox.dialog(message, buttons, { "classes": "delete-user-modal" });
|
||||
},
|
||||
|
||||
deleteForbidden: Em.computed.not("canBeDeleted"),
|
||||
|
||||
deleteExplanation: function() {
|
||||
if (this.get('deleteForbidden')) {
|
||||
if (this.get('staff')) {
|
||||
return I18n.t('admin.user.delete_forbidden_because_staff');
|
||||
} else {
|
||||
return I18n.t('admin.user.delete_forbidden', {count: Discourse.SiteSettings.delete_user_max_post_age});
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}.property('deleteForbidden'),
|
||||
|
||||
destroy(opts) {
|
||||
const user = this,
|
||||
message = I18n.t("admin.user.delete_confirm"),
|
||||
location = document.location.pathname;
|
||||
|
||||
const performDestroy = function(block) {
|
||||
let formData = { context: location };
|
||||
if (block) {
|
||||
formData["block_email"] = true;
|
||||
formData["block_urls"] = true;
|
||||
formData["block_ip"] = true;
|
||||
}
|
||||
if (opts && opts.deletePosts) {
|
||||
formData["delete_posts"] = true;
|
||||
}
|
||||
return Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
|
||||
type: 'DELETE',
|
||||
data: formData
|
||||
}).then(function(data) {
|
||||
if (data.deleted) {
|
||||
if (/^\/admin\/users\/list\//.test(location)) {
|
||||
document.location = location;
|
||||
} else {
|
||||
document.location = "/admin/users/list/active";
|
||||
}
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
if (data.user) {
|
||||
user.setProperties(data.user);
|
||||
}
|
||||
}
|
||||
}).catch(function() {
|
||||
Discourse.AdminUser.find( user.get('username') ).then(function(u){ user.setProperties(u); });
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
});
|
||||
};
|
||||
|
||||
const buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel",
|
||||
"link": true
|
||||
}, {
|
||||
"label": I18n.t('admin.user.delete_dont_block'),
|
||||
"class": "btn",
|
||||
"callback": function(){ performDestroy(false); }
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.delete_and_block'),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function(){ performDestroy(true); }
|
||||
}];
|
||||
|
||||
bootbox.dialog(message, buttons, { "classes": "delete-user-modal" });
|
||||
},
|
||||
|
||||
deleteAsSpammer(successCallback) {
|
||||
const user = this;
|
||||
|
||||
user.checkEmail().then(function() {
|
||||
const data = {
|
||||
posts: user.get('post_count'),
|
||||
topics: user.get('topic_count'),
|
||||
email: user.get('email') || I18n.t("flagging.hidden_email_address"),
|
||||
ip_address: user.get('ip_address') || I18n.t("flagging.ip_address_missing")
|
||||
};
|
||||
|
||||
const message = I18n.t('flagging.delete_confirm', data),
|
||||
buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel-inline",
|
||||
"link": true
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("flagging.yes_delete_spammer"),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function() {
|
||||
return Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
|
||||
type: 'DELETE',
|
||||
data: {
|
||||
delete_posts: true,
|
||||
block_email: true,
|
||||
block_urls: true,
|
||||
block_ip: true,
|
||||
delete_as_spammer: true,
|
||||
context: window.location.pathname
|
||||
}
|
||||
}).then(function(result) {
|
||||
if (result.deleted) {
|
||||
if (successCallback) successCallback();
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
}
|
||||
}).catch(function() {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
bootbox.dialog(message, buttons, {"classes": "flagging-delete-spammer"});
|
||||
});
|
||||
},
|
||||
|
||||
loadDetails() {
|
||||
const user = this;
|
||||
|
||||
if (user.get('loadedDetails')) { return Ember.RSVP.resolve(user); }
|
||||
|
||||
return Discourse.AdminUser.find(user.get('username_lower')).then(function (result) {
|
||||
user.setProperties(result);
|
||||
user.set('loadedDetails', true);
|
||||
});
|
||||
},
|
||||
|
||||
tl3Requirements: function() {
|
||||
if (this.get('tl3_requirements')) {
|
||||
return Discourse.TL3Requirements.create(this.get('tl3_requirements'));
|
||||
}
|
||||
}.property('tl3_requirements'),
|
||||
|
||||
suspendedBy: function() {
|
||||
if (this.get('suspended_by')) {
|
||||
return Discourse.AdminUser.create(this.get('suspended_by'));
|
||||
}
|
||||
}.property('suspended_by'),
|
||||
|
||||
approvedBy: function() {
|
||||
if (this.get('approved_by')) {
|
||||
return Discourse.AdminUser.create(this.get('approved_by'));
|
||||
}
|
||||
}.property('approved_by')
|
||||
|
||||
});
|
||||
|
||||
AdminUser.reopenClass({
|
||||
|
||||
bulkApprove(users) {
|
||||
_.each(users, function(user) {
|
||||
user.setProperties({
|
||||
approved: true,
|
||||
can_approve: false,
|
||||
selected: false
|
||||
});
|
||||
});
|
||||
|
||||
return Discourse.ajax("/admin/users/approve-bulk", {
|
||||
type: 'PUT',
|
||||
data: { users: users.map((u) => u.id) }
|
||||
}).finally(() => bootbox.alert(I18n.t("admin.user.approve_bulk_success")));
|
||||
},
|
||||
|
||||
bulkReject(users) {
|
||||
_.each(users, function(user) {
|
||||
user.set('can_approve', false);
|
||||
user.set('selected', false);
|
||||
});
|
||||
|
||||
return Discourse.ajax("/admin/users/reject-bulk", {
|
||||
type: 'DELETE',
|
||||
data: {
|
||||
users: users.map((u) => u.id),
|
||||
context: window.location.pathname
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
find(username) {
|
||||
return Discourse.ajax("/admin/users/" + username + ".json").then(function (result) {
|
||||
result.loadedDetails = true;
|
||||
return Discourse.AdminUser.create(result);
|
||||
});
|
||||
},
|
||||
|
||||
findAll(query, filter) {
|
||||
return Discourse.ajax("/admin/users/list/" + query + ".json", {
|
||||
data: filter
|
||||
}).then(function(users) {
|
||||
return users.map((u) => Discourse.AdminUser.create(u));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default AdminUser;
|
@ -1,501 +0,0 @@
|
||||
/**
|
||||
Our data model for dealing with users from the admin section.
|
||||
|
||||
@class AdminUser
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.AdminUser = Discourse.User.extend({
|
||||
|
||||
/**
|
||||
Generates an API key for the user. Will regenerate if they already have one.
|
||||
|
||||
@method generateApiKey
|
||||
@returns {Promise} a promise that resolves to the newly generated API key
|
||||
**/
|
||||
generateApiKey: function() {
|
||||
var self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/generate_api_key", {type: 'POST'}).then(function (result) {
|
||||
var apiKey = Discourse.ApiKey.create(result.api_key);
|
||||
self.set('api_key', apiKey);
|
||||
return apiKey;
|
||||
});
|
||||
},
|
||||
|
||||
groupAdded: function(added){
|
||||
var self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/groups", {
|
||||
type: 'POST',
|
||||
data: {group_id: added.id}
|
||||
}).then(function () {
|
||||
self.get('groups').pushObject(added);
|
||||
});
|
||||
},
|
||||
|
||||
groupRemoved: function(removed){
|
||||
var self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/groups/" + removed.id, {
|
||||
type: 'DELETE'
|
||||
}).then(function () {
|
||||
self.set('groups.[]', self.get('groups').rejectBy("id", removed.id));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
Revokes a user's current API key
|
||||
|
||||
@method revokeApiKey
|
||||
@returns {Promise} a promise that resolves when the API key has been deleted
|
||||
**/
|
||||
revokeApiKey: function() {
|
||||
var self = this;
|
||||
return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_api_key", {type: 'DELETE'}).then(function () {
|
||||
self.set('api_key', null);
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllPostsExplanation: function() {
|
||||
if (!this.get('can_delete_all_posts')) {
|
||||
if (this.get('post_count') > Discourse.SiteSettings.delete_all_posts_max) {
|
||||
return I18n.t('admin.user.cant_delete_all_too_many_posts', {count: Discourse.SiteSettings.delete_all_posts_max});
|
||||
} else {
|
||||
return I18n.t('admin.user.cant_delete_all_posts', {count: Discourse.SiteSettings.delete_user_max_post_age});
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}.property('can_delete_all_posts'),
|
||||
|
||||
deleteAllPosts: function() {
|
||||
var user = this;
|
||||
var message = I18n.t('admin.user.delete_all_posts_confirm', {posts: user.get('post_count'), topics: user.get('topic_count')});
|
||||
var buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel-inline",
|
||||
"link": true
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("admin.user.delete_all_posts"),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function() {
|
||||
Discourse.ajax("/admin/users/" + (user.get('id')) + "/delete_all_posts", {type: 'PUT'}).then(function(){
|
||||
user.set('post_count', 0);
|
||||
});
|
||||
}
|
||||
}];
|
||||
bootbox.dialog(message, buttons, {"classes": "delete-all-posts"});
|
||||
},
|
||||
|
||||
// Revoke the user's admin access
|
||||
revokeAdmin: function() {
|
||||
this.set('admin', false);
|
||||
this.set('can_grant_admin', true);
|
||||
this.set('can_revoke_admin', false);
|
||||
return Discourse.ajax("/admin/users/" + (this.get('id')) + "/revoke_admin", {type: 'PUT'});
|
||||
},
|
||||
|
||||
grantAdmin: function() {
|
||||
this.set('admin', true);
|
||||
this.set('can_grant_admin', false);
|
||||
this.set('can_revoke_admin', true);
|
||||
Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_admin", {type: 'PUT'});
|
||||
},
|
||||
|
||||
// Revoke the user's moderation access
|
||||
revokeModeration: function() {
|
||||
this.set('moderator', false);
|
||||
this.set('can_grant_moderation', true);
|
||||
this.set('can_revoke_moderation', false);
|
||||
return Discourse.ajax("/admin/users/" + (this.get('id')) + "/revoke_moderation", {type: 'PUT'});
|
||||
},
|
||||
|
||||
grantModeration: function() {
|
||||
this.set('moderator', true);
|
||||
this.set('can_grant_moderation', false);
|
||||
this.set('can_revoke_moderation', true);
|
||||
Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_moderation", {type: 'PUT'});
|
||||
},
|
||||
|
||||
refreshBrowsers: function() {
|
||||
Discourse.ajax("/admin/users/" + (this.get('id')) + "/refresh_browsers", {type: 'POST'});
|
||||
bootbox.alert(I18n.t("admin.user.refresh_browsers_message"));
|
||||
},
|
||||
|
||||
approve: function() {
|
||||
this.set('can_approve', false);
|
||||
this.set('approved', true);
|
||||
this.set('approved_by', Discourse.User.current());
|
||||
Discourse.ajax("/admin/users/" + (this.get('id')) + "/approve", {type: 'PUT'});
|
||||
},
|
||||
|
||||
username_lower: (function() {
|
||||
return this.get('username').toLowerCase();
|
||||
}).property('username'),
|
||||
|
||||
setOriginalTrustLevel: function() {
|
||||
this.set('originalTrustLevel', this.get('trust_level'));
|
||||
},
|
||||
|
||||
trustLevels: function() {
|
||||
return Discourse.Site.currentProp('trustLevels');
|
||||
}.property(),
|
||||
|
||||
dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
|
||||
|
||||
saveTrustLevel: function() {
|
||||
Discourse.ajax("/admin/users/" + this.id + "/trust_level", {
|
||||
type: 'PUT',
|
||||
data: {level: this.get('trustLevel.id')}
|
||||
}).then(function () {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failure
|
||||
var error;
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
error = e.responseJSON.errors[0];
|
||||
}
|
||||
error = error || I18n.t('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
restoreTrustLevel: function() {
|
||||
this.set('trustLevel.id', this.get('originalTrustLevel'));
|
||||
},
|
||||
|
||||
lockTrustLevel: function(locked) {
|
||||
Discourse.ajax("/admin/users/" + this.id + "/trust_level_lock", {
|
||||
type: 'PUT',
|
||||
data: { locked: !!locked }
|
||||
}).then(function() {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failure
|
||||
var error;
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
error = e.responseJSON.errors[0];
|
||||
}
|
||||
error = error || I18n.t('admin.user.trust_level_change_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
canLockTrustLevel: function(){
|
||||
return this.get('trust_level') < 4;
|
||||
}.property('trust_level'),
|
||||
|
||||
isSuspended: Em.computed.equal('suspended', true),
|
||||
canSuspend: Em.computed.not('staff'),
|
||||
|
||||
suspendDuration: function() {
|
||||
var suspended_at = moment(this.suspended_at);
|
||||
var suspended_till = moment(this.suspended_till);
|
||||
return suspended_at.format('L') + " - " + suspended_till.format('L');
|
||||
}.property('suspended_till', 'suspended_at'),
|
||||
|
||||
suspend: function(duration, reason) {
|
||||
return Discourse.ajax("/admin/users/" + this.id + "/suspend", {
|
||||
type: 'PUT',
|
||||
data: {duration: duration, reason: reason}
|
||||
});
|
||||
},
|
||||
|
||||
unsuspend: function() {
|
||||
Discourse.ajax("/admin/users/" + this.id + "/unsuspend", {
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failed
|
||||
var error = I18n.t('admin.user.unsuspend_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
log_out: function(){
|
||||
Discourse.ajax("/admin/users/" + this.id + "/log_out", {
|
||||
type: 'POST',
|
||||
data: { username_or_email: this.get('username') }
|
||||
}).then(
|
||||
function(){
|
||||
bootbox.alert(I18n.t("admin.user.logged_out"));
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
impersonate: function() {
|
||||
Discourse.ajax("/admin/impersonate", {
|
||||
type: 'POST',
|
||||
data: { username_or_email: this.get('username') }
|
||||
}).then(function() {
|
||||
// succeeded
|
||||
document.location = "/";
|
||||
}, function(e) {
|
||||
// failed
|
||||
if (e.status === 404) {
|
||||
bootbox.alert(I18n.t('admin.impersonate.not_found'));
|
||||
} else {
|
||||
bootbox.alert(I18n.t('admin.impersonate.invalid'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
Discourse.ajax('/admin/users/' + this.id + '/activate', {type: 'PUT'}).then(function() {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failed
|
||||
var error = I18n.t('admin.user.activate_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
Discourse.ajax('/admin/users/' + this.id + '/deactivate', {type: 'PUT'}).then(function() {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failed
|
||||
var error = I18n.t('admin.user.deactivate_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
unblock: function() {
|
||||
Discourse.ajax('/admin/users/' + this.id + '/unblock', {type: 'PUT'}).then(function() {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failed
|
||||
var error = I18n.t('admin.user.unblock_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
block: function() {
|
||||
Discourse.ajax('/admin/users/' + this.id + '/block', {type: 'PUT'}).then(function() {
|
||||
// succeeded
|
||||
window.location.reload();
|
||||
}, function(e) {
|
||||
// failed
|
||||
var error = I18n.t('admin.user.block_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
sendActivationEmail: function() {
|
||||
Discourse.ajax('/users/action/send_activation_email', {data: {username: this.get('username')}, type: 'POST'}).then(function() {
|
||||
// succeeded
|
||||
bootbox.alert( I18n.t('admin.user.activation_email_sent') );
|
||||
}, function(e) {
|
||||
// failed
|
||||
var error = I18n.t('admin.user.send_activation_email_failed', { error: "http: " + e.status + " - " + e.body });
|
||||
bootbox.alert(error);
|
||||
});
|
||||
},
|
||||
|
||||
deleteForbidden: Em.computed.not("canBeDeleted"),
|
||||
|
||||
deleteExplanation: function() {
|
||||
if (this.get('deleteForbidden')) {
|
||||
if (this.get('staff')) {
|
||||
return I18n.t('admin.user.delete_forbidden_because_staff');
|
||||
} else {
|
||||
return I18n.t('admin.user.delete_forbidden', {count: Discourse.SiteSettings.delete_user_max_post_age});
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}.property('deleteForbidden'),
|
||||
|
||||
destroy: function(opts) {
|
||||
var user = this;
|
||||
var location = document.location.pathname;
|
||||
|
||||
var performDestroy = function(block) {
|
||||
var formData = { context: location };
|
||||
if (block) {
|
||||
formData["block_email"] = true;
|
||||
formData["block_urls"] = true;
|
||||
formData["block_ip"] = true;
|
||||
}
|
||||
if (opts && opts.deletePosts) {
|
||||
formData["delete_posts"] = true;
|
||||
}
|
||||
Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
|
||||
type: 'DELETE',
|
||||
data: formData
|
||||
}).then(function(data) {
|
||||
if (data.deleted) {
|
||||
if (/^\/admin\/users\/list\//.test(location)) {
|
||||
document.location = location;
|
||||
} else {
|
||||
document.location = "/admin/users/list/active";
|
||||
}
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
if (data.user) {
|
||||
user.setProperties(data.user);
|
||||
}
|
||||
}
|
||||
}, function() {
|
||||
Discourse.AdminUser.find( user.get('username') ).then(function(u){ user.setProperties(u); });
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
});
|
||||
};
|
||||
|
||||
var message = I18n.t("admin.user.delete_confirm");
|
||||
|
||||
var buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel",
|
||||
"link": true
|
||||
}, {
|
||||
"label": I18n.t('admin.user.delete_dont_block'),
|
||||
"class": "btn",
|
||||
"callback": function(){
|
||||
performDestroy(false);
|
||||
}
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.delete_and_block'),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function(){
|
||||
performDestroy(true);
|
||||
}
|
||||
}];
|
||||
|
||||
bootbox.dialog(message, buttons, {"classes": "delete-user-modal"});
|
||||
},
|
||||
|
||||
deleteAsSpammer: function(successCallback) {
|
||||
var user = this;
|
||||
|
||||
user.checkEmail().then(function() {
|
||||
var data = {
|
||||
posts: user.get('post_count'),
|
||||
topics: user.get('topic_count'),
|
||||
email: user.get('email') || I18n.t("flagging.hidden_email_address"),
|
||||
ip_address: user.get('ip_address') || I18n.t("flagging.ip_address_missing")
|
||||
};
|
||||
var message = I18n.t('flagging.delete_confirm', data);
|
||||
var buttons = [{
|
||||
"label": I18n.t("composer.cancel"),
|
||||
"class": "cancel-inline",
|
||||
"link": true
|
||||
}, {
|
||||
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("flagging.yes_delete_spammer"),
|
||||
"class": "btn btn-danger",
|
||||
"callback": function() {
|
||||
Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
|
||||
type: 'DELETE',
|
||||
data: {
|
||||
delete_posts: true,
|
||||
block_email: true,
|
||||
block_urls: true,
|
||||
block_ip: true,
|
||||
delete_as_spammer: true,
|
||||
context: window.location.pathname
|
||||
}
|
||||
}).then(function(result) {
|
||||
if (result.deleted) {
|
||||
if (successCallback) successCallback();
|
||||
} else {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
}
|
||||
}, function() {
|
||||
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||
});
|
||||
}
|
||||
}];
|
||||
bootbox.dialog(message, buttons, {"classes": "flagging-delete-spammer"});
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
loadDetails: function() {
|
||||
var model = this;
|
||||
if (model.get('loadedDetails')) { return Ember.RSVP.resolve(model); }
|
||||
|
||||
return Discourse.AdminUser.find(model.get('username_lower')).then(function (result) {
|
||||
model.setProperties(result);
|
||||
model.set('loadedDetails', true);
|
||||
});
|
||||
},
|
||||
|
||||
tl3Requirements: function() {
|
||||
if (this.get('tl3_requirements')) {
|
||||
return Discourse.TL3Requirements.create(this.get('tl3_requirements'));
|
||||
}
|
||||
}.property('tl3_requirements'),
|
||||
|
||||
suspendedBy: function() {
|
||||
if (this.get('suspended_by')) {
|
||||
return Discourse.AdminUser.create(this.get('suspended_by'));
|
||||
}
|
||||
}.property('suspended_by'),
|
||||
|
||||
approvedBy: function() {
|
||||
if (this.get('approved_by')) {
|
||||
return Discourse.AdminUser.create(this.get('approved_by'));
|
||||
}
|
||||
}.property('approved_by')
|
||||
|
||||
});
|
||||
|
||||
Discourse.AdminUser.reopenClass({
|
||||
|
||||
bulkApprove: function(users) {
|
||||
_.each(users, function(user) {
|
||||
user.set('approved', true);
|
||||
user.set('can_approve', false);
|
||||
return user.set('selected', false);
|
||||
});
|
||||
|
||||
bootbox.alert(I18n.t("admin.user.approve_bulk_success"));
|
||||
|
||||
return Discourse.ajax("/admin/users/approve-bulk", {
|
||||
type: 'PUT',
|
||||
data: {
|
||||
users: users.map(function(u) {
|
||||
return u.id;
|
||||
})
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
bulkReject: function(users) {
|
||||
_.each(users, function(user){
|
||||
user.set('can_approve', false);
|
||||
user.set('selected', false);
|
||||
});
|
||||
|
||||
return Discourse.ajax("/admin/users/reject-bulk", {
|
||||
type: 'DELETE',
|
||||
data: {
|
||||
users: users.map(function(u) { return u.id; }),
|
||||
context: window.location.pathname
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
find: function(username) {
|
||||
return Discourse.ajax("/admin/users/" + username + ".json").then(function (result) {
|
||||
result.loadedDetails = true;
|
||||
return Discourse.AdminUser.create(result);
|
||||
});
|
||||
},
|
||||
|
||||
findAll: function(query, filter) {
|
||||
return Discourse.ajax("/admin/users/list/" + query + ".json", {
|
||||
data: filter
|
||||
}).then(function(users) {
|
||||
return users.map(function(u) {
|
||||
return Discourse.AdminUser.create(u);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
@ -92,9 +92,9 @@ Discourse.Report = Discourse.Model.extend({
|
||||
icon: function() {
|
||||
switch( this.get('type') ) {
|
||||
case 'flags':
|
||||
return 'fa-flag';
|
||||
return 'flag';
|
||||
case 'likes':
|
||||
return 'fa-heart';
|
||||
return 'heart';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -1,11 +1,6 @@
|
||||
/**
|
||||
Represents an IP address that is watched for during account registration
|
||||
(and possibly other times), and an action is taken.
|
||||
|
||||
@class ScreenedIpAddress
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ScreenedIpAddress = Discourse.Model.extend({
|
||||
actionName: function() {
|
||||
@ -17,21 +12,9 @@ Discourse.ScreenedIpAddress = Discourse.Model.extend({
|
||||
}.property('action_name'),
|
||||
|
||||
actionIcon: function() {
|
||||
if (this.get('action_name') === 'block') {
|
||||
return this.get('blockIcon');
|
||||
} else {
|
||||
return this.get('doNothingIcon');
|
||||
}
|
||||
return (this.get('action_name') === 'block') ? 'ban' : 'check';
|
||||
}.property('action_name'),
|
||||
|
||||
blockIcon: function() {
|
||||
return 'fa-ban';
|
||||
}.property(),
|
||||
|
||||
doNothingIcon: function() {
|
||||
return 'fa-check';
|
||||
}.property(),
|
||||
|
||||
save: function() {
|
||||
return Discourse.ajax("/admin/logs/screened_ip_addresses" + (this.id ? '/' + this.id : '') + ".json", {
|
||||
type: this.id ? 'PUT' : 'POST',
|
||||
@ -45,8 +28,8 @@ Discourse.ScreenedIpAddress = Discourse.Model.extend({
|
||||
});
|
||||
|
||||
Discourse.ScreenedIpAddress.reopenClass({
|
||||
findAll: function() {
|
||||
return Discourse.ajax("/admin/logs/screened_ip_addresses.json").then(function(screened_ips) {
|
||||
findAll: function(filter) {
|
||||
return Discourse.ajax("/admin/logs/screened_ip_addresses.json", { data: { filter: filter } }).then(function(screened_ips) {
|
||||
return screened_ips.map(function(b) {
|
||||
return Discourse.ScreenedIpAddress.create(b);
|
||||
});
|
||||
|
54
app/assets/javascripts/admin/models/site-setting.js.es6
Normal file
54
app/assets/javascripts/admin/models/site-setting.js.es6
Normal file
@ -0,0 +1,54 @@
|
||||
const SiteSetting = Discourse.Model.extend({
|
||||
overridden: function() {
|
||||
let val = this.get('value'),
|
||||
defaultVal = this.get('default');
|
||||
|
||||
if (val === null) val = '';
|
||||
if (defaultVal === null) defaultVal = '';
|
||||
|
||||
return val.toString() !== defaultVal.toString();
|
||||
}.property('value', 'default'),
|
||||
|
||||
validValues: function() {
|
||||
const vals = [],
|
||||
translateNames = this.get('translate_names');
|
||||
|
||||
this.get('valid_values').forEach(function(v) {
|
||||
if (v.name && v.name.length > 0) {
|
||||
vals.addObject(translateNames ? {name: I18n.t(v.name), value: v.value} : v);
|
||||
}
|
||||
});
|
||||
return vals;
|
||||
}.property('valid_values'),
|
||||
|
||||
allowsNone: function() {
|
||||
if ( _.indexOf(this.get('valid_values'), '') >= 0 ) return 'admin.site_settings.none';
|
||||
}.property('valid_values')
|
||||
});
|
||||
|
||||
SiteSetting.reopenClass({
|
||||
findAll() {
|
||||
return Discourse.ajax("/admin/site_settings").then(function (settings) {
|
||||
// Group the results by category
|
||||
const categories = {};
|
||||
settings.site_settings.forEach(function(s) {
|
||||
if (!categories[s.category]) {
|
||||
categories[s.category] = [];
|
||||
}
|
||||
categories[s.category].pushObject(Discourse.SiteSetting.create(s));
|
||||
});
|
||||
|
||||
return Object.keys(categories).map(function(n) {
|
||||
return {nameKey: n, name: I18n.t('admin.site_settings.categories.' + n), siteSettings: categories[n]};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
update(key, value) {
|
||||
const data = {};
|
||||
data[key] = value;
|
||||
return Discourse.ajax("/admin/site_settings/" + key, { type: 'PUT', data });
|
||||
}
|
||||
});
|
||||
|
||||
export default SiteSetting;
|
@ -1,136 +0,0 @@
|
||||
Discourse.SiteSetting = Discourse.Model.extend({
|
||||
|
||||
validationMessage: null,
|
||||
|
||||
/**
|
||||
Is the boolean setting true?
|
||||
|
||||
@property enabled
|
||||
**/
|
||||
enabled: function(key, value) {
|
||||
|
||||
if (arguments.length > 1) {
|
||||
this.set('value', value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
if (this.blank('value')) return false;
|
||||
return this.get('value') === 'true';
|
||||
|
||||
}.property('value'),
|
||||
|
||||
/**
|
||||
The name of the setting. Basically, underscores in the setting key are replaced with spaces.
|
||||
|
||||
@property settingName
|
||||
**/
|
||||
settingName: function() {
|
||||
return this.get('setting').replace(/\_/g, ' ');
|
||||
}.property('setting'),
|
||||
|
||||
/**
|
||||
Has the user changed the setting? If so we should save it.
|
||||
|
||||
@property dirty
|
||||
**/
|
||||
dirty: function() {
|
||||
return this.get('originalValue') !== this.get('value');
|
||||
}.property('originalValue', 'value'),
|
||||
|
||||
overridden: function() {
|
||||
var val = this.get('value'),
|
||||
defaultVal = this.get('default');
|
||||
|
||||
if (val === null) val = '';
|
||||
if (defaultVal === null) defaultVal = '';
|
||||
|
||||
return val.toString() !== defaultVal.toString();
|
||||
}.property('value', 'default'),
|
||||
|
||||
/**
|
||||
Reset the setting to its original value.
|
||||
|
||||
@method resetValue
|
||||
**/
|
||||
resetValue: function() {
|
||||
this.set('value', this.get('originalValue'));
|
||||
this.set('validationMessage', null);
|
||||
},
|
||||
|
||||
/**
|
||||
Save the setting's value.
|
||||
|
||||
@method save
|
||||
**/
|
||||
save: function() {
|
||||
// Update the setting
|
||||
var self = this, data = {};
|
||||
data[this.get('setting')] = this.get('value');
|
||||
return Discourse.ajax("/admin/site_settings/" + this.get('setting'), {
|
||||
data: data,
|
||||
type: 'PUT'
|
||||
}).then(function() {
|
||||
self.set('originalValue', self.get('value'));
|
||||
self.set('validationMessage', null);
|
||||
}, function(e) {
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
self.set('validationMessage', e.responseJSON.errors[0]);
|
||||
} else {
|
||||
self.set('validationMessage', I18n.t('generic_error'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
validValues: function() {
|
||||
var vals, setting;
|
||||
vals = Em.A();
|
||||
setting = this;
|
||||
_.each(this.get('valid_values'), function(v) {
|
||||
if (v.name && v.name.length > 0) {
|
||||
if (setting.translate_names) {
|
||||
vals.addObject({name: I18n.t(v.name), value: v.value});
|
||||
} else {
|
||||
vals.addObject(v);
|
||||
}
|
||||
}
|
||||
});
|
||||
return vals;
|
||||
}.property('valid_values'),
|
||||
|
||||
allowsNone: function() {
|
||||
if ( _.indexOf(this.get('valid_values'), '') >= 0 ) return 'admin.site_settings.none';
|
||||
}.property('valid_values')
|
||||
});
|
||||
|
||||
Discourse.SiteSetting.reopenClass({
|
||||
|
||||
findAll: function() {
|
||||
return Discourse.ajax("/admin/site_settings").then(function (settings) {
|
||||
// Group the results by category
|
||||
var categoryNames = [],
|
||||
categories = {},
|
||||
result = Em.A();
|
||||
_.each(settings.site_settings,function(s) {
|
||||
s.originalValue = s.value;
|
||||
if (!categoryNames.contains(s.category)) {
|
||||
categoryNames.pushObject(s.category);
|
||||
categories[s.category] = Em.A();
|
||||
}
|
||||
categories[s.category].pushObject(Discourse.SiteSetting.create(s));
|
||||
});
|
||||
_.each(categoryNames, function(n) {
|
||||
result.pushObject({nameKey: n, name: I18n.t('admin.site_settings.categories.' + n), siteSettings: categories[n]});
|
||||
});
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
update: function(key, value) {
|
||||
return Discourse.ajax("/admin/site_settings/" + key, {
|
||||
type: 'PUT',
|
||||
data: { value: value }
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
@ -1,11 +1,3 @@
|
||||
/**
|
||||
Represents an action taken by a staff member that has been logged.
|
||||
|
||||
@class StaffActionLog
|
||||
@extends Discourse.Model
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.StaffActionLog = Discourse.Model.extend({
|
||||
showFullDetails: false,
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
LOG_CHANNEL: "/admin/backups/logs",
|
||||
const LOG_CHANNEL = "/admin/backups/logs";
|
||||
|
||||
activate: function() {
|
||||
Discourse.MessageBus.subscribe(this.LOG_CHANNEL, this._processLogMessage.bind(this));
|
||||
export default Discourse.Route.extend({
|
||||
|
||||
activate() {
|
||||
this.messageBus.subscribe(LOG_CHANNEL, this._processLogMessage.bind(this));
|
||||
},
|
||||
|
||||
_processLogMessage: function(log) {
|
||||
_processLogMessage(log) {
|
||||
if (log.message === "[STARTED]") {
|
||||
this.controllerFor("adminBackups").set("isOperationRunning", true);
|
||||
this.controllerFor("adminBackupsLogs").clear();
|
||||
@ -25,7 +27,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
}
|
||||
},
|
||||
|
||||
model: function() {
|
||||
model() {
|
||||
return PreloadStore.getAndRemove("operations_status", function() {
|
||||
return Discourse.ajax("/admin/backups/status.json");
|
||||
}).then(function (status) {
|
||||
@ -37,35 +39,24 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
});
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
Discourse.MessageBus.unsubscribe(this.LOG_CHANNEL);
|
||||
deactivate() {
|
||||
this.messageBus.unsubscribe(LOG_CHANNEL);
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
Starts a backup and redirect the user to the logs tab
|
||||
|
||||
@method startBackup
|
||||
**/
|
||||
startBackup: function() {
|
||||
Discourse.Route.showModal(this, 'admin_start_backup');
|
||||
startBackup() {
|
||||
showModal('modals/admin-start-backup');
|
||||
this.controllerFor('modal').set('modalClass', 'start-backup-modal');
|
||||
},
|
||||
|
||||
backupStarted: function () {
|
||||
backupStarted() {
|
||||
this.modelFor("adminBackups").set("isOperationRunning", true);
|
||||
this.transitionTo("admin.backups.logs");
|
||||
this.send("closeModal");
|
||||
},
|
||||
|
||||
/**
|
||||
Destroys a backup
|
||||
|
||||
@method destroyBackup
|
||||
@param {Discourse.Backup} backup the backup to destroy
|
||||
**/
|
||||
destroyBackup: function(backup) {
|
||||
var self = this;
|
||||
destroyBackup(backup) {
|
||||
const self = this;
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.backups.operations.destroy.confirm"),
|
||||
I18n.t("no_value"),
|
||||
@ -80,14 +71,8 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
Start a restore and redirect the user to the logs tab
|
||||
|
||||
@method startRestore
|
||||
@param {Discourse.Backup} backup the backup to restore
|
||||
**/
|
||||
startRestore: function(backup) {
|
||||
var self = this;
|
||||
startRestore(backup) {
|
||||
const self = this;
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.backups.operations.restore.confirm"),
|
||||
I18n.t("no_value"),
|
||||
@ -105,13 +90,8 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
Cancels the current operation
|
||||
|
||||
@method cancelOperation
|
||||
**/
|
||||
cancelOperation: function() {
|
||||
var self = this;
|
||||
cancelOperation() {
|
||||
const self = this;
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.backups.operations.cancel.confirm"),
|
||||
I18n.t("no_value"),
|
||||
@ -126,12 +106,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
Rollback to previous working state
|
||||
|
||||
@method rollback
|
||||
**/
|
||||
rollback: function() {
|
||||
rollback() {
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.backups.operations.rollback.confirm"),
|
||||
I18n.t("no_value"),
|
||||
@ -142,8 +117,8 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
);
|
||||
},
|
||||
|
||||
uploadSuccess: function(filename) {
|
||||
var self = this;
|
||||
uploadSuccess(filename) {
|
||||
const self = this;
|
||||
bootbox.alert(I18n.t("admin.backups.upload.success", { filename: filename }), function() {
|
||||
Discourse.Backup.find().then(function (backups) {
|
||||
self.controllerFor("adminBackupsIndex").set("model", backups);
|
||||
@ -151,7 +126,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
|
||||
});
|
||||
},
|
||||
|
||||
uploadError: function(filename, message) {
|
||||
uploadError(filename, message) {
|
||||
bootbox.alert(I18n.t("admin.backups.upload.error", { filename: filename, message: message }));
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
serialize: function(m) {
|
||||
serialize(m) {
|
||||
return {badge_id: Em.get(m, 'id') || 'new'};
|
||||
},
|
||||
|
||||
model: function(params) {
|
||||
model(params) {
|
||||
if (params.badge_id === "new") {
|
||||
return Discourse.Badge.create({
|
||||
name: I18n.t('admin.badges.new_badge')
|
||||
@ -13,22 +15,20 @@ export default Ember.Route.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
saveError: function(e) {
|
||||
var msg = I18n.t("generic_error");
|
||||
saveError(e) {
|
||||
let msg = I18n.t("generic_error");
|
||||
if (e.responseJSON && e.responseJSON.errors) {
|
||||
msg = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')});
|
||||
}
|
||||
bootbox.alert(msg);
|
||||
},
|
||||
|
||||
editGroupings: function() {
|
||||
var groupings = this.controllerFor('admin-badges').get('badgeGroupings');
|
||||
Discourse.Route.showModal(this, 'admin_edit_badge_groupings', groupings);
|
||||
editGroupings() {
|
||||
const model = this.controllerFor('admin-badges').get('badgeGroupings');
|
||||
showModal('modals/admin-edit-badge-groupings', { model });
|
||||
},
|
||||
|
||||
preview: function(badge, explain) {
|
||||
var self = this;
|
||||
|
||||
preview(badge, explain) {
|
||||
badge.set('preview_loading', true);
|
||||
Discourse.ajax('/admin/badges/preview.json', {
|
||||
method: 'post',
|
||||
@ -36,11 +36,11 @@ export default Ember.Route.extend({
|
||||
sql: badge.get('query'),
|
||||
target_posts: !!badge.get('target_posts'),
|
||||
trigger: badge.get('trigger'),
|
||||
explain: explain
|
||||
explain
|
||||
}
|
||||
}).then(function(json) {
|
||||
}).then(function(model) {
|
||||
badge.set('preview_loading', false);
|
||||
Discourse.Route.showModal(self, 'admin_badge_preview', json);
|
||||
showModal('modals/admin-badge-preview', { model });
|
||||
}).catch(function(error) {
|
||||
badge.set('preview_loading', false);
|
||||
Em.Logger.error(error);
|
||||
|
@ -24,7 +24,8 @@ export default Discourse.Route.extend({
|
||||
c.set('top_referrers', topReferrers);
|
||||
}
|
||||
|
||||
['admins', 'moderators', 'blocked', 'suspended', 'top_traffic_sources', 'top_referred_topics', 'updated_at'].forEach(function(x) {
|
||||
[ 'disk_space','admins', 'moderators', 'blocked', 'suspended',
|
||||
'top_traffic_sources', 'top_referred_topics', 'updated_at'].forEach(function(x) {
|
||||
c.set(x, d[x]);
|
||||
});
|
||||
|
||||
|
@ -1,22 +1,24 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
model: function(params) {
|
||||
model(params) {
|
||||
this.filter = params.filter;
|
||||
return Discourse.FlaggedPost.findAll(params.filter);
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
setupController(controller, model) {
|
||||
controller.set('model', model);
|
||||
controller.set('query', this.filter);
|
||||
},
|
||||
|
||||
actions: {
|
||||
showAgreeFlagModal: function (flaggedPost) {
|
||||
Discourse.Route.showModal(this, 'admin_agree_flag', flaggedPost);
|
||||
showAgreeFlagModal(model) {
|
||||
showModal('modals/admin-agree-flag', { model });
|
||||
this.controllerFor('modal').set('modalClass', 'agree-flag-modal');
|
||||
},
|
||||
|
||||
showDeleteFlagModal: function (flaggedPost) {
|
||||
Discourse.Route.showModal(this, 'admin_delete_flag', flaggedPost);
|
||||
showDeleteFlagModal(model) {
|
||||
showModal('modals/admin-delete-flag', { model });
|
||||
this.controllerFor('modal').set('modalClass', 'delete-flag-modal');
|
||||
}
|
||||
|
||||
|
@ -2,4 +2,4 @@ export default Discourse.Route.extend({
|
||||
redirect: function() {
|
||||
this.transitionTo("adminGroupsType", "custom");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -1,17 +1,23 @@
|
||||
export default Discourse.Route.extend({
|
||||
model: function(params) {
|
||||
model(params) {
|
||||
this.set("type", params.type);
|
||||
return Discourse.Group.findAll().then(function(groups) {
|
||||
return groups.filterBy("type", params.type);
|
||||
});
|
||||
},
|
||||
|
||||
setupController(controller, model){
|
||||
controller.set("type", this.get("type"));
|
||||
controller.set("model", model);
|
||||
},
|
||||
|
||||
actions: {
|
||||
newGroup: function() {
|
||||
var self = this;
|
||||
newGroup() {
|
||||
const self = this;
|
||||
this.transitionTo("adminGroupsType", "custom").then(function() {
|
||||
var group = Discourse.Group.create({ automatic: false, visible: true });
|
||||
self.transitionTo("adminGroup", group);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,26 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
// TODO: make this automatic using an `{{outlet}}`
|
||||
renderTemplate: function() {
|
||||
this.render('admin/templates/logs/staff_action_logs', {into: 'adminLogs'});
|
||||
},
|
||||
|
||||
setupController: function(controller) {
|
||||
controller.resetFilters();
|
||||
controller.refresh();
|
||||
},
|
||||
|
||||
actions: {
|
||||
showDetailsModal(model) {
|
||||
showModal('modals/admin-staff-action-log-details', { model });
|
||||
this.controllerFor('modal').set('modalClass', 'log-details-modal');
|
||||
},
|
||||
|
||||
showCustomDetailsModal(model) {
|
||||
const modalName = "modals/" + (model.action_name + '_details').replace("_", "-");
|
||||
showModal(modalName, { model });
|
||||
this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal');
|
||||
}
|
||||
}
|
||||
});
|
12
app/assets/javascripts/admin/routes/admin-plugins.js.es6
Normal file
12
app/assets/javascripts/admin/routes/admin-plugins.js.es6
Normal file
@ -0,0 +1,12 @@
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return this.store.findAll('plugin');
|
||||
},
|
||||
|
||||
actions: {
|
||||
showSettings() {
|
||||
this.transitionTo('adminSiteSettingsCategory', 'plugins');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
export default {
|
||||
resource: 'admin',
|
||||
|
||||
map: function() {
|
||||
map() {
|
||||
this.route('dashboard', { path: '/' });
|
||||
this.resource('adminSiteSettings', { path: '/site_settings' }, function() {
|
||||
this.resource('adminSiteSettingsCategory', { path: 'category/:category_id'} );
|
||||
|
@ -1,16 +1,8 @@
|
||||
/**
|
||||
Handles routes related to viewing and editing site settings within one category.
|
||||
|
||||
@class AdminSiteSettingCategoryRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.AdminSiteSettingsCategoryRoute = Discourse.Route.extend({
|
||||
model: function(params) {
|
||||
export default Discourse.Route.extend({
|
||||
model(params) {
|
||||
// The model depends on user input, so let the controller do the work:
|
||||
this.controllerFor('adminSiteSettingsCategory').set('categoryNameKey', params.category_id);
|
||||
return Em.Object.create({
|
||||
return Ember.Object.create({
|
||||
nameKey: params.category_id,
|
||||
name: I18n.t('admin.site_settings.categories.' + params.category_id),
|
||||
siteSettings: this.controllerFor('adminSiteSettingsCategory').get('filteredContent')
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
Handles when you click the Site Settings tab in admin, but haven't
|
||||
chosen a category. It will redirect to the first category.
|
||||
**/
|
||||
export default Discourse.Route.extend({
|
||||
beforeModel() {
|
||||
this.replaceWith('adminSiteSettingsCategory', this.modelFor('adminSiteSettings')[0].nameKey);
|
||||
}
|
||||
});
|
@ -0,0 +1,11 @@
|
||||
import SiteSetting from 'admin/models/site-setting';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
model() {
|
||||
return SiteSetting.findAll();
|
||||
},
|
||||
|
||||
afterModel(siteSettings) {
|
||||
this.controllerFor('adminSiteSettings').set('allSiteSettings', siteSettings);
|
||||
}
|
||||
});
|
32
app/assets/javascripts/admin/routes/admin-user-index.js.es6
Normal file
32
app/assets/javascripts/admin/routes/admin-user-index.js.es6
Normal file
@ -0,0 +1,32 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
export default Discourse.Route.extend({
|
||||
model() {
|
||||
return this.modelFor('adminUser');
|
||||
},
|
||||
|
||||
afterModel(model) {
|
||||
if (this.currentUser.get('admin')) {
|
||||
const self = this;
|
||||
return Discourse.Group.findAll().then(function(groups){
|
||||
self._availableGroups = groups.filterBy('automatic', false);
|
||||
return model;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.setProperties({
|
||||
originalPrimaryGroupId: model.get('primary_group_id'),
|
||||
availableGroups: this._availableGroups,
|
||||
model
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
showSuspendModal(model) {
|
||||
showModal('modals/admin-suspend-user', { model });
|
||||
this.controllerFor('modal').set('modalClass', 'suspend-user-modal');
|
||||
}
|
||||
}
|
||||
});
|
20
app/assets/javascripts/admin/routes/admin-user.js.es6
Normal file
20
app/assets/javascripts/admin/routes/admin-user.js.es6
Normal file
@ -0,0 +1,20 @@
|
||||
export default Discourse.Route.extend({
|
||||
serialize(model) {
|
||||
return { username: model.get('username').toLowerCase() };
|
||||
},
|
||||
|
||||
model(params) {
|
||||
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render({into: 'admin'});
|
||||
},
|
||||
|
||||
afterModel(adminUser) {
|
||||
return adminUser.loadDetails().then(function () {
|
||||
adminUser.setOriginalTrustLevel();
|
||||
return adminUser;
|
||||
});
|
||||
}
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
export default Discourse.Route.extend({
|
||||
redirect: function() {
|
||||
beforeModel: function() {
|
||||
this.replaceWith('adminUsersList.show', 'active');
|
||||
}
|
||||
});
|
||||
|
@ -7,6 +7,10 @@ export default Discourse.Route.extend({
|
||||
Discourse.ExportCsv.exportUserList().then(outputExportResult);
|
||||
},
|
||||
|
||||
sendInvites: function() {
|
||||
this.transitionTo('user.invited', Discourse.User.current());
|
||||
},
|
||||
|
||||
deleteUser: function(user) {
|
||||
Discourse.AdminUser.create(user).destroy({ deletePosts: true });
|
||||
}
|
||||
|
@ -12,47 +12,6 @@ Discourse.AdminLogsIndexRoute = Discourse.Route.extend({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
The route that lists staff actions that were logged.
|
||||
|
||||
@class AdminLogsStaffActionLogsRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.AdminLogsStaffActionLogsRoute = Discourse.Route.extend({
|
||||
renderTemplate: function() {
|
||||
this.render('admin/templates/logs/staff_action_logs', {into: 'adminLogs'});
|
||||
},
|
||||
|
||||
setupController: function(controller) {
|
||||
var queryParams = Discourse.URL.get('queryParams');
|
||||
if (queryParams) {
|
||||
controller.set('filters', queryParams);
|
||||
}
|
||||
return controller.show();
|
||||
},
|
||||
|
||||
actions: {
|
||||
showDetailsModal: function(logRecord) {
|
||||
Discourse.Route.showModal(this, 'admin_staff_action_log_details', logRecord);
|
||||
this.controllerFor('modal').set('modalClass', 'log-details-modal');
|
||||
},
|
||||
|
||||
showCustomDetailsModal: function(logRecord) {
|
||||
Discourse.Route.showModal(this, logRecord.action_name + '_details', logRecord);
|
||||
this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal');
|
||||
}
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
this._super();
|
||||
|
||||
// Clear any filters when we leave the route
|
||||
Discourse.URL.set('queryParams', null);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
The route that lists blocked email addresses.
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
Discourse.AdminRoute = Discourse.Route.extend({
|
||||
renderTemplate: function() {
|
||||
this.render('admin/templates/admin');
|
||||
},
|
||||
|
||||
titleToken: function() {
|
||||
return I18n.t('admin_title');
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
Handles routes related to viewing and editing site settings.
|
||||
|
||||
@class AdminSiteSettingsRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.AdminSiteSettingsRoute = Discourse.Route.extend({
|
||||
model: function() {
|
||||
return Discourse.SiteSetting.findAll();
|
||||
},
|
||||
|
||||
afterModel: function(siteSettings) {
|
||||
this.controllerFor('adminSiteSettings').set('allSiteSettings', siteSettings);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
Handles when you click the Site Settings tab in admin, but haven't
|
||||
chosen a category. It will redirect to the first category.
|
||||
**/
|
||||
Discourse.AdminSiteSettingsIndexRoute = Discourse.Route.extend({
|
||||
model: function() {
|
||||
this.replaceWith('adminSiteSettingsCategory', this.modelFor('adminSiteSettings')[0].nameKey);
|
||||
}
|
||||
});
|
@ -1,51 +0,0 @@
|
||||
Discourse.AdminUserRoute = Discourse.Route.extend({
|
||||
serialize: function(model) {
|
||||
return { username: model.get('username').toLowerCase() };
|
||||
},
|
||||
|
||||
model: function(params) {
|
||||
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({into: 'admin/templates/admin'});
|
||||
},
|
||||
|
||||
afterModel: function(adminUser) {
|
||||
return adminUser.loadDetails().then(function () {
|
||||
adminUser.setOriginalTrustLevel();
|
||||
return adminUser;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Discourse.AdminUserIndexRoute = Discourse.Route.extend({
|
||||
model: function() {
|
||||
return this.modelFor('adminUser');
|
||||
},
|
||||
|
||||
afterModel: function(model) {
|
||||
if(Discourse.User.currentProp('admin')) {
|
||||
var self = this;
|
||||
return Discourse.Group.findAll().then(function(groups){
|
||||
self._availableGroups = groups.filterBy('automatic', false);
|
||||
return model;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setProperties({
|
||||
originalPrimaryGroupId: model.get('primary_group_id'),
|
||||
availableGroups: this._availableGroups,
|
||||
model: model
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
showSuspendModal: function(user) {
|
||||
Discourse.Route.showModal(this, 'admin_suspend_user', user);
|
||||
this.controllerFor('modal').set('modalClass', 'suspend-user-modal');
|
||||
}
|
||||
}
|
||||
});
|
@ -4,25 +4,26 @@
|
||||
<div class="full-width">
|
||||
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to 'admin.dashboard'}}{{i18n 'admin.dashboard.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='admin.dashboard' label='admin.dashboard.title'}}
|
||||
{{#if currentUser.admin}}
|
||||
<li>{{#link-to 'adminSiteSettings'}}{{i18n 'admin.site_settings.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='adminSiteSettings' label='admin.site_settings.title'}}
|
||||
{{/if}}
|
||||
<li>{{#link-to 'adminUsersList'}}{{i18n 'admin.users.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='adminUsersList.show' routeParam='active' label='admin.users.title'}}
|
||||
{{#if showBadges}}
|
||||
<li>{{#link-to 'adminBadges.index'}}{{i18n 'admin.badges.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='adminBadges.index' label='admin.badges.title'}}
|
||||
{{/if}}
|
||||
{{#if currentUser.admin}}
|
||||
<li>{{#link-to 'adminGroups'}}{{i18n 'admin.groups.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='adminGroups' label='admin.groups.title'}}
|
||||
{{/if}}
|
||||
<li>{{#link-to 'adminEmail'}}{{i18n 'admin.email.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminFlags'}}{{i18n 'admin.flags.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminLogs'}}{{i18n 'admin.logs.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='adminEmail' label='admin.email.title'}}
|
||||
{{admin-nav-item route='adminFlags' label='admin.flags.title'}}
|
||||
{{admin-nav-item route='adminLogs' label='admin.logs.title'}}
|
||||
{{#if currentUser.admin}}
|
||||
<li>{{#link-to 'adminCustomize.colors'}}{{i18n 'admin.customize.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'admin.api'}}{{i18n 'admin.api.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'admin.backups'}}{{i18n 'admin.backups.title'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='adminCustomize.colors' label='admin.customize.title'}}
|
||||
{{admin-nav-item route='admin.api' label='admin.api.title'}}
|
||||
{{admin-nav-item route='admin.backups' label='admin.backups.title'}}
|
||||
{{/if}}
|
||||
{{admin-nav-item route='adminPlugins' label='admin.plugins.title'}}
|
||||
{{plugin-outlet "admin-menu" tagName="li"}}
|
||||
</ul>
|
||||
|
||||
|
@ -1,18 +1,31 @@
|
||||
<div class="admin-controls">
|
||||
<div class="span15">
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to "admin.backups.index"}}{{i18n 'admin.backups.menu.backups'}}{{/link-to}}</li>
|
||||
<li>{{#link-to "admin.backups.logs"}}{{i18n 'admin.backups.menu.logs'}}{{/link-to}}</li>
|
||||
{{admin-nav-item route='admin.backups.index' label='admin.backups.menu.backups'}}
|
||||
{{admin-nav-item route='admin.backups.logs' label='admin.backups.menu.logs'}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{{#if canRollback}}
|
||||
<button {{action "rollback"}} class="btn btn-rollback" title="{{i18n 'admin.backups.operations.rollback.title'}}" {{bind-attr disabled="rollbackDisabled"}}><i class="fa fa-ambulance fa-flip-horizontal"></i>{{i18n 'admin.backups.operations.rollback.text'}}</button>
|
||||
{{d-button action="rollback"
|
||||
class="btn-rollback"
|
||||
label="admin.backups.operations.rollback.label"
|
||||
title="admin.backups.operations.rollback.title"
|
||||
icon="ambulance"
|
||||
disabled=rollbackDisabled}}
|
||||
{{/if}}
|
||||
{{#if isOperationRunning}}
|
||||
<button {{action "cancelOperation"}} class="btn btn-danger" title="{{i18n 'admin.backups.operations.cancel.title'}}"><i class="fa fa-times"></i>{{i18n 'admin.backups.operations.cancel.text'}}</button>
|
||||
{{d-button action="cancelOperation"
|
||||
class="btn-danger"
|
||||
title="admin.backups.operations.cancel.title"
|
||||
label="admin.backups.operations.cancel.label"
|
||||
icon="times"}}
|
||||
{{else}}
|
||||
<button {{action "startBackup"}} class="btn btn-primary" title="{{i18n 'admin.backups.operations.backup.title'}}"><i class="fa fa-rocket"></i>{{i18n 'admin.backups.operations.backup.text'}}</button>
|
||||
{{d-button action="startBackup"
|
||||
class="btn-primary"
|
||||
title="admin.backups.operations.backup.title"
|
||||
label="admin.backups.operations.backup.label"
|
||||
icon="rocket"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,8 +4,12 @@
|
||||
<th width="10%">{{i18n 'admin.backups.columns.size'}}</th>
|
||||
<th>
|
||||
<div class="pull-right">
|
||||
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadText}}
|
||||
<button {{action "toggleReadOnlyMode"}} class="btn" {{bind-attr disabled="readOnlyModeDisabled" title="readOnlyModeTitle"}}><i class="fa fa-eye"></i>{{readOnlyModeText}}</button>
|
||||
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadLabel title="admin.backups.upload.title"}}
|
||||
{{#if site.isReadOnly}}
|
||||
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}}
|
||||
{{else}}
|
||||
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
@ -15,9 +19,14 @@
|
||||
<td>{{human-size backup.size}}</td>
|
||||
<td>
|
||||
<div class="pull-right">
|
||||
<a {{bind-attr href="backup.link"}} class="btn download" title="{{i18n 'admin.backups.operations.download.title'}}"><i class="fa fa-download"></i>{{i18n 'admin.backups.operations.download.text'}}</a>
|
||||
<button {{action "destroyBackup" backup}} class="btn btn-danger no-text" {{bind-attr disabled="destroyDisabled" title="destroyTitle"}}><i class="fa fa-trash-o"></i></button>
|
||||
<button {{action "startRestore" backup}} class="btn" {{bind-attr disabled="restoreDisabled" title="restoreTitle"}}><i class="fa fa-play"></i>{{i18n 'admin.backups.operations.restore.text'}}</button>
|
||||
<a {{bind-attr href="backup.link"}} class="btn download" title="{{i18n 'admin.backups.operations.download.title'}}">{{fa-icon "download"}}{{i18n 'admin.backups.operations.download.label'}}</a>
|
||||
{{#if isOperationRunning}}
|
||||
{{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger no-text" disabled="true" title="admin.backups.operations.is_running"}}
|
||||
{{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
|
||||
{{else}}
|
||||
{{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger no-text" title="admin.backups.operations.destroy.title"}}
|
||||
{{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class='span13'>
|
||||
<div class='current-badge span13'>
|
||||
<p>{{i18n 'admin.badges.none_selected'}}</p>
|
||||
|
||||
<div>
|
||||
|
@ -0,0 +1,9 @@
|
||||
{{#if routeParam}}
|
||||
{{#link-to route routeParam}}{{i18n label}}{{/link-to}}
|
||||
{{else}}
|
||||
{{#if route}}
|
||||
{{#link-to route}}{{i18n label}}{{/link-to}}
|
||||
{{else}}
|
||||
<a href="{{unbound href}}" data-auto-route="true">{{i18n label}}</a>
|
||||
{{/if}}
|
||||
{{/if}}
|
@ -0,0 +1,7 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
{{yield}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
<td class="title">
|
||||
{{#if report.icon}}
|
||||
{{fa-icon report.icon}}
|
||||
{{/if}}
|
||||
<a href="{{report.reportUrl}}">{{report.title}}</a>
|
||||
</td>
|
||||
<td class="value">{{report.todayCount}}</td>
|
||||
<td {{bind-attr class=":value report.yesterdayTrend"}} title="{{report.yesterdayCountTitle}}">{{report.yesterdayCount}} {{fa-icon "caret-up" class="up"}} {{fa-icon "caret-down" class="down"}}</td>
|
||||
<td {{bind-attr class=":value report.sevenDayTrend"}} title="{{report.sevenDayCountTitle}}">{{report.lastSevenDaysCount}} {{fa-icon "caret-up" class="up"}} {{fa-icon "caret-down" class="down"}}</td>
|
||||
<td {{bind-attr class=":value report.thirtyDayTrend"}} title="{{report.thirtyDayCountTitle}}">{{report.lastThirtyDaysCount}} {{fa-icon "caret-up" class="up"}} {{fa-icon "caret-down" class="down"}}</td>
|
||||
<td class="value">{{report.total}}</td>
|
@ -0,0 +1,6 @@
|
||||
<td class="title"><a href="report.reportUrl}}">{{report.title}}</a></td>
|
||||
<td class="value">{{report.todayCount}}</td>
|
||||
<td class="value">{{report.yesterdayCount}}</td>
|
||||
<td class="value">{{report.sevenDaysAgoCount}}</td>
|
||||
<td class="value">{{report.thirtyDaysAgoCount}}</td>
|
||||
<td class="value"></td>
|
@ -0,0 +1,6 @@
|
||||
<td class="title">{{report.title}}</td>
|
||||
<td class="value">{{#link-to 'adminUsersList.show' 'newuser'}}{{value-at-tl report.data level="0"}}{{/link-to}}</td>
|
||||
<td class="value">{{#link-to 'adminUsersList.show' 'basic'}}{{value-at-tl report.data level="1"}}{{/link-to}}</td>
|
||||
<td class="value">{{#link-to 'adminUsersList.show' 'regular'}}{{value-at-tl report.data level="2"}}{{/link-to}}</td>
|
||||
<td class="value">{{#link-to 'adminUsersList.show' 'leader'}}{{value-at-tl report.data level="3"}}{{/link-to}}</td>
|
||||
<td class="value">{{#link-to 'adminUsersList.show' 'elder'}}{{value-at-tl report.data level="4"}}{{/link-to}}</td>
|
@ -0,0 +1,4 @@
|
||||
<div {{bind-attr class=":validation-error message::hidden"}}>
|
||||
{{fa-icon "times"}}
|
||||
{{message}}
|
||||
</div>
|
@ -0,0 +1,16 @@
|
||||
<div class='setting-label'>
|
||||
<h3>{{unbound settingName}}</h3>
|
||||
</div>
|
||||
<div class="setting-value">
|
||||
{{partial partialName}}
|
||||
</div>
|
||||
{{#if dirty}}
|
||||
<div class='setting-controls'>
|
||||
{{d-button class="ok no-text" action="save" icon="check"}}
|
||||
{{d-button class="cancel no-text" action="cancel" icon="times"}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if setting.overridden}}
|
||||
{{d-button action="resetDefault" icon="undo" label="admin.site_settings.reset"}}
|
||||
{{/if}}
|
||||
{{/if}}
|
@ -0,0 +1,18 @@
|
||||
{{#if urls}}
|
||||
<div class='urls'>
|
||||
{{#each url in urls}}
|
||||
<div class='url'>
|
||||
{{d-button action="removeUrl"
|
||||
actionParam=url
|
||||
icon="times"
|
||||
class="btn-small no-text"}}
|
||||
<a href="{{unbound url}}" target="_blank">{{url}}</a>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='input'>
|
||||
{{text-field value=newUrl placeholderKey="admin.site_settings.add_url"}}
|
||||
{{d-button action="addUrl" icon="plus" class="btn-primary btn-small no-text" disabled=urlInvalid}}
|
||||
</div>
|
@ -1,14 +1,10 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to 'adminCustomize.colors'}}{{i18n 'admin.customize.colors.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomize.css_html'}}{{i18n 'admin.customize.css_html.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminSiteText'}}{{i18n 'admin.site_text.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminUserFields'}}{{i18n 'admin.user_fields.title'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminEmojis'}}{{i18n 'admin.emoji.title'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{#admin-nav}}
|
||||
{{admin-nav-item route='adminCustomize.colors' label='admin.customize.colors.title'}}
|
||||
{{admin-nav-item route='adminCustomize.css_html' label='admin.customize.css_html.title'}}
|
||||
{{admin-nav-item route='adminSiteText' label='admin.site_text.title'}}
|
||||
{{admin-nav-item route='adminUserFields' label='admin.user_fields.title'}}
|
||||
{{admin-nav-item route='adminEmojis' label='admin.emoji.title'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
{{outlet}}
|
||||
|
@ -33,27 +33,29 @@
|
||||
<li class='toggle-mobile'>
|
||||
<a {{bind-attr class="view.mobile:active"}} {{action "toggleMobile" target="view"}}>{{fa-icon "mobile"}}</a>
|
||||
</li>
|
||||
<li class='toggle-maximize'><a {{action "toggleMaximize" target="view"}}>
|
||||
{{#if view.maximized}}
|
||||
{{fa-icon "compress"}}
|
||||
{{else}}
|
||||
{{fa-icon "expand"}}
|
||||
{{/if}}
|
||||
</a>
|
||||
<li class='toggle-maximize'>
|
||||
<a {{action "toggleMaximize" target="view"}}>
|
||||
{{#if view.maximized}}
|
||||
{{fa-icon "compress"}}
|
||||
{{else}}
|
||||
{{fa-icon "expand"}}
|
||||
{{/if}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="admin-container">
|
||||
{{#if view.stylesheetActive}}{{aceEditor content=selectedItem.stylesheet mode="scss"}}{{/if}}
|
||||
{{#if view.headerActive}}{{aceEditor content=selectedItem.header mode="html"}}{{/if}}
|
||||
{{#if view.topActive}}{{aceEditor content=selectedItem.top mode="html"}}{{/if}}
|
||||
{{#if view.footerActive}}{{aceEditor content=selectedItem.footer mode="html"}}{{/if}}
|
||||
{{#if view.headTagActive}}{{aceEditor content=selectedItem.head_tag mode="html"}}{{/if}}
|
||||
{{#if view.bodyTagActive}}{{aceEditor content=selectedItem.body_tag mode="html"}}{{/if}}
|
||||
{{#if view.mobileStylesheetActive}}{{aceEditor content=selectedItem.mobile_stylesheet mode="scss"}}{{/if}}
|
||||
{{#if view.mobileHeaderActive}}{{aceEditor content=selectedItem.mobile_header mode="html"}}{{/if}}
|
||||
{{#if view.mobileTopActive}}{{aceEditor content=selectedItem.mobile_top mode="html"}}{{/if}}
|
||||
{{#if view.mobileFooterActive}}{{aceEditor content=selectedItem.mobile_footer mode="html"}}{{/if}}
|
||||
{{#if view.stylesheetActive}}{{ace-editor content=selectedItem.stylesheet mode="scss"}}{{/if}}
|
||||
{{#if view.headerActive}}{{ace-editor content=selectedItem.header mode="html"}}{{/if}}
|
||||
{{#if view.topActive}}{{ace-editor content=selectedItem.top mode="html"}}{{/if}}
|
||||
{{#if view.footerActive}}{{ace-editor content=selectedItem.footer mode="html"}}{{/if}}
|
||||
{{#if view.headTagActive}}{{ace-editor content=selectedItem.head_tag mode="html"}}{{/if}}
|
||||
{{#if view.bodyTagActive}}{{ace-editor content=selectedItem.body_tag mode="html"}}{{/if}}
|
||||
{{#if view.mobileStylesheetActive}}{{ace-editor content=selectedItem.mobile_stylesheet mode="scss"}}{{/if}}
|
||||
{{#if view.mobileHeaderActive}}{{ace-editor content=selectedItem.mobile_header mode="html"}}{{/if}}
|
||||
{{#if view.mobileTopActive}}{{ace-editor content=selectedItem.mobile_top mode="html"}}{{/if}}
|
||||
{{#if view.mobileFooterActive}}{{ace-editor content=selectedItem.mobile_footer mode="html"}}{{/if}}
|
||||
</div>
|
||||
<div class='admin-footer'>
|
||||
<div class='status-actions'>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="dashboard-left">
|
||||
{{#if showVersionChecks}}
|
||||
{{partial 'admin/templates/version_checks'}}
|
||||
{{partial 'admin/templates/version-checks'}}
|
||||
{{/if}}
|
||||
|
||||
<div class="dashboard-stats trust-levels">
|
||||
@ -15,9 +15,11 @@
|
||||
<th>4</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{ render 'admin/templates/reports/trust_levels_report' users_by_trust_level tagName="tbody" }}
|
||||
{{/unless}}
|
||||
<tbody>
|
||||
{{#unless loading}}
|
||||
{{admin-report-trust-level-counts report=users_by_trust_level}}
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -50,18 +52,45 @@
|
||||
<th>{{i18n 'admin.dashboard.reports.all'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{ render 'admin_report_counts' signups }}
|
||||
{{ render 'admin_report_counts' topics }}
|
||||
{{ render 'admin_report_counts' posts }}
|
||||
{{ render 'admin_report_counts' likes }}
|
||||
{{ render 'admin_report_counts' flags }}
|
||||
{{ render 'admin_report_counts' bookmarks }}
|
||||
{{ render 'admin_report_counts' emails }}
|
||||
{{/unless}}
|
||||
<tbody>
|
||||
{{#unless loading}}
|
||||
{{admin-report-per-day-counts report=visits}}
|
||||
{{admin-report-counts report=signups}}
|
||||
{{admin-report-counts report=topics}}
|
||||
{{admin-report-counts report=posts}}
|
||||
{{admin-report-counts report=likes}}
|
||||
{{admin-report-counts report=flags}}
|
||||
{{admin-report-counts report=bookmarks}}
|
||||
{{admin-report-counts report=emails}}
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title" title="{{i18n 'admin.dashboard.page_views'}}">{{i18n 'admin.dashboard.page_views_short'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.today'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.yesterday'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.last_7_days'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.last_30_days'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.all'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#unless loading}}
|
||||
{{admin-report-counts report=page_view_anon_reqs}}
|
||||
{{admin-report-counts report=page_view_logged_in_reqs}}
|
||||
{{admin-report-counts report=page_view_crawler_reqs}}
|
||||
{{admin-report-counts report=page_view_total_reqs}}
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
@ -74,13 +103,15 @@
|
||||
<th>{{i18n 'admin.dashboard.reports.all'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{ render 'admin_report_counts' user_to_user_private_messages }}
|
||||
{{ render 'admin_report_counts' system_private_messages }}
|
||||
{{ render 'admin_report_counts' notify_moderators_private_messages }}
|
||||
{{ render 'admin_report_counts' notify_user_private_messages }}
|
||||
{{ render 'admin_report_counts' moderator_warning_private_messages }}
|
||||
{{/unless}}
|
||||
<tbody>
|
||||
{{#unless loading}}
|
||||
{{admin-report-counts report=user_to_user_private_messages}}
|
||||
{{admin-report-counts report=system_private_messages}}
|
||||
{{admin-report-counts report=notify_moderators_private_messages}}
|
||||
{{admin-report-counts report=notify_user_private_messages}}
|
||||
{{admin-report-counts report=moderator_warning_private_messages}}
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -89,17 +120,56 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>{{i18n 'admin.dashboard.reports.today'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.yesterday'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.7_days_ago'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.30_days_ago'}}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#unless loading}}
|
||||
{{ render 'admin/templates/reports/per_day_counts_report' visits tagName="tbody"}}
|
||||
{{/unless}}
|
||||
<tbody>
|
||||
{{#unless loading}}
|
||||
<tr>
|
||||
<td>{{i18n 'admin.dashboard.uploads'}}</td>
|
||||
<td>{{disk_space.uploads_used}} ({{i18n 'admin.dashboard.space_free' size=disk_space.uploads_free}})</td>
|
||||
<td><a href="/admin/backups">{{i18n 'admin.dashboard.backups'}}</a></td>
|
||||
<td>{{disk_space.backups_used}} ({{i18n 'admin.dashboard.space_free' size=disk_space.backups_free}})</td>
|
||||
</tr>
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{#unless loading}}
|
||||
{{#if showTrafficReport}}
|
||||
<div class="dashboard-stats">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title" title="{{i18n 'admin.dashboard.traffic'}}">{{i18n 'admin.dashboard.traffic_short'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.today'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.yesterday'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.last_7_days'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.last_30_days'}}</th>
|
||||
<th>{{i18n 'admin.dashboard.reports.all'}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#unless loading}}
|
||||
{{admin-report-counts report=http_2xx_reqs}}
|
||||
{{admin-report-counts report=http_3xx_reqs}}
|
||||
{{admin-report-counts report=http_4xx_reqs}}
|
||||
{{admin-report-counts report=http_5xx_reqs}}
|
||||
{{admin-report-counts report=http_background_reqs}}
|
||||
{{admin-report-counts report=http_total_reqs}}
|
||||
{{/unless}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="dashboard-stats">
|
||||
<a href {{action 'showTrafficReport'}}>{{i18n 'admin.dashboard.show_traffic_report'}}</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
<div class="dashboard-right">
|
||||
|
@ -1,14 +1,10 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to 'adminEmail.index'}}{{i18n 'admin.email.settings'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminEmail.all'}}{{i18n 'admin.email.all'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminEmail.sent'}}{{i18n 'admin.email.sent'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminEmail.skipped'}}{{i18n 'admin.email.skipped'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminEmail.previewDigest'}}{{i18n 'admin.email.preview_digest'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{#admin-nav}}
|
||||
{{admin-nav-item route='adminEmail.index' label='admin.email.settings'}}
|
||||
{{admin-nav-item route='adminEmail.all' label='admin.email.all'}}
|
||||
{{admin-nav-item route='adminEmail.sent' label='admin.email.sent'}}
|
||||
{{admin-nav-item route='adminEmail.skipped' label='admin.email.skipped'}}
|
||||
{{admin-nav-item route='adminEmail.previewDigest' label='admin.email.preview_digest'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
{{outlet}}
|
||||
|
@ -18,10 +18,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#loading-spinner condition=loading}}
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{#if showHtml}}
|
||||
{{{html_content}}}
|
||||
{{else}}
|
||||
<pre>{{{text_content}}}</pre>
|
||||
{{/if}}
|
||||
{{/loading-spinner}}
|
||||
{{/conditional-loading-spinner}}
|
||||
|
@ -147,7 +147,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{loading-spinner condition=view.loading}}
|
||||
{{conditional-loading-spinner condition=view.loading}}
|
||||
{{else}}
|
||||
<p>{{i18n 'admin.flags.no_results'}}</p>
|
||||
{{/if}}
|
||||
|
@ -1,11 +1,7 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to 'adminFlags.list' 'active'}}{{i18n 'admin.flags.active'}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminFlags.list' 'old'}}{{i18n 'admin.flags.old'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{#admin-nav}}
|
||||
{{admin-nav-item route='adminFlags.list' routeParam='active' label='admin.flags.active'}}
|
||||
{{admin-nav-item route='adminFlags.list' routeParam='old' label='admin.flags.old'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
{{outlet}}
|
||||
|
@ -38,6 +38,15 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{#unless automatic}}
|
||||
<div>
|
||||
<label for="primary_group">
|
||||
{{input type="checkbox" checked=primary_group}}
|
||||
{{i18n 'admin.groups.primary_group'}}
|
||||
</label>
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div>
|
||||
<label for="alias">{{i18n 'groups.alias_levels.title'}}</label>
|
||||
{{combo-box name="alias" valueAttribute="value" value=alias_level content=aliasLevelOptions}}
|
||||
@ -52,6 +61,13 @@
|
||||
{{i18n 'admin.groups.automatic_membership_retroactive'}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="title">
|
||||
{{i18n 'admin.groups.default_title'}}
|
||||
</label>
|
||||
{{input value=title}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class='buttons'>
|
||||
|
@ -1 +1 @@
|
||||
{{avatar member imageSize="small"}} {{member.username}} {{#unless automatic}}<a class='remove' {{action "removeMember" member}}>{{fa-icon "times"}}</a>{{/unless}}
|
||||
<a href='{{unbound member.adminPath}}'>{{avatar member imageSize="small"}}</a> {{member.username}} {{#unless automatic}}<a class='remove' {{action "removeMember" member}}>{{fa-icon "times"}}</a>{{/unless}}
|
||||
|
@ -1,11 +1,8 @@
|
||||
<div class="admin-controls">
|
||||
<div class="span15">
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to "adminGroupsType" "custom"}}{{i18n 'admin.groups.custom'}}{{/link-to}}</li>
|
||||
<li>{{#link-to "adminGroupsType" "automatic"}}{{i18n 'admin.groups.automatic'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{#admin-nav}}
|
||||
{{admin-nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}}
|
||||
{{admin-nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}}
|
||||
{{/admin-nav}}
|
||||
|
||||
<div class="admin-container">
|
||||
{{outlet}}
|
||||
</div>
|
||||
|
@ -10,7 +10,9 @@
|
||||
</ul>
|
||||
<div class='controls'>
|
||||
{{d-button action="newGroup" icon="plus" label="admin.groups.new"}}
|
||||
{{#if controller.isAuto}}
|
||||
{{d-button action="refreshAutoGroups" icon="refresh" label="admin.groups.refresh" disabled=refreshingAutoGroups}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user