diff --git a/.jshintignore b/.eslintignore similarity index 92% rename from .jshintignore rename to .eslintignore index ac11baa97e..5b61bf6c51 100644 --- a/.jshintignore +++ b/.eslintignore @@ -7,6 +7,7 @@ app/assets/javascripts/vendor.js app/assets/javascripts/locales/i18n.js app/assets/javascripts/defer/html-sanitizer-bundle.js app/assets/javascripts/discourse/lib/Markdown.Editor.js +app/assets/javascripts/ember-addons/ jsapp/lib/Markdown.Editor.js lib/javascripts/locale/ lib/javascripts/messageformat.js @@ -20,5 +21,5 @@ vendor/ test/javascripts/helpers/ test/javascripts/test_helper.js test/javascripts/test_helper.js +test/javascripts/fixtures app/assets/javascripts/ember-addons/ - diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..d2a13ae262 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,102 @@ +{ + "env": { + "jasmine": true, + "node": true, + "mocha": true, + "browser": true, + "builtin": true + }, + ecmaVersion: 7, + "globals": + {"Ember":true, + "jQuery":true, + "$":true, + "RSVP":true, + "Discourse":true, + "Em":true, + "PreloadStore":true, + "Handlebars":true, + "I18n":true, + "bootbox":true, + "module":true, + "moduleFor":true, + "moduleForComponent":true, + "Pretender":true, + "sandbox":true, + "controllerFor":true, + "test":true, + "ok":true, + "not":true, + "expect":true, + "equal":true, + "visit":true, + "andThen":true, + "click":true, + "currentPath":true, + "currentRouteName":true, + "currentURL":true, + "fillIn":true, + "keyEvent":true, + "triggerEvent":true, + "count":true, + "exists":true, + "visible":true, + "invisible":true, + "asyncRender":true, + "selectDropdown":true, + "asyncTestDiscourse":true, + "fixture":true, + "find":true, + "sinon":true, + "moment":true, + "start":true, + "_":true, + "alert":true, + "containsInstance":true, + "deepEqual":true, + "notEqual":true, + "define":true, + "require":true, + "requirejs":true, + "hasModule":true, + "Blob":true, + "File":true}, + "rules": { + "block-scoped-var": 2, + "dot-notation": 0, + "eqeqeq": [ + 2, + "allow-null" + ], + "guard-for-in": 2, + "no-bitwise": 2, + "no-caller": 2, + "no-cond-assign": 0, + "no-debugger": 2, + "no-empty": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-parens": 0, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-loop-func": 2, + "no-multi-str": 2, + "no-new": 2, + "no-plusplus": 0, + "no-proto": 2, + "no-script-url": 2, + "no-sequences": 2, + "no-shadow": 2, + "no-undef": 2, + "no-unused-vars": 2, + "no-with": 2, + "semi": 2, + "strict": 0, + "valid-typeof": 2, + "wrap-iife": [ + 2, + "inside" + ] + }, + "parser": "babel-eslint" +} diff --git a/.image_optim.yml b/.image_optim.yml index c7196cbb29..4a9ad0555c 100644 --- a/.image_optim.yml +++ b/.image_optim.yml @@ -1,11 +1,11 @@ skip_missing_workers: true -allow_lossy: true +allow_lossy: false # PNG advpng: false -optipng: false +optipng: + level: 2 pngcrush: false pngout: false -pngquant: - quality: !ruby/range 10..90 +pngquant: false # JPG jpegrecompress: false diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 55cc59d193..0000000000 --- a/.jshintrc +++ /dev/null @@ -1,86 +0,0 @@ -{ - "predef":["Ember", - "jQuery", - "$", - "RSVP", - "Discourse", - "Em", - "PreloadStore", - "Handlebars", - "I18n", - "bootbox", - "module", - "moduleFor", - "moduleForComponent", - "Pretender", - "sandbox", - "controllerFor", - "test", - "ok", - "not", - "expect", - "equal", - "blank", - "present", - "visit", - "andThen", - "click", - "currentPath", - "currentRouteName", - "currentURL", - "fillIn", - "keyEvent", - "triggerEvent", - "count", - "exists", - "visible", - "invisible", - "asyncRender", - "selectDropdown", - "asyncTestDiscourse", - "fixture", - "find", - "sinon", - "moment", - "start", - "_", - "alert", - "containsInstance", - "parseHTML", - "deepEqual", - "notEqual", - "define", - "require", - "requirejs", - "hasModule", - "Blob", - "File"], - "node" : false, - "browser" : true, - "boss" : true, - "curly": false, - "debug": false, - "devel": false, - "eqeqeq": true, - "evil": true, - "forin": false, - "immed": false, - "laxbreak": false, - "newcap": true, - "noarg": true, - "noempty": false, - "nonew": false, - "nomen": false, - "onevar": false, - "plusplus": false, - "regexp": false, - "undef": true, - "unused": true, - "sub": true, - "strict": false, - "white": false, - "eqnull": true, - "quotmark": false, - "lastsemic": true, - "esnext": true -} diff --git a/.travis.yml b/.travis.yml index 9c2e282d89..62961abc4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,12 +5,18 @@ env: - DISCOURSE_HOSTNAME=www.example.com - RUBY_GC_MALLOC_LIMIT=50000000 matrix: + - "RAILS_MASTER=0" - "RAILS42=1" - "RAILS_MASTER=1" - - "RAILS_MASTER=0" addons: postgresql: 9.3 + apt: + packages: + - gifsicle + - jpegoptim + - optipng + - jhead matrix: allow_failures: @@ -23,18 +29,23 @@ rvm: - 2.0.0 - 2.1 - 2.2 - - rbx-2 services: - redis-server sudo: false -cache: bundler +cache: + directories: + - vendor/bundle before_install: - - npm i -g jshint - - jshint . + - gem install bundler + - npm i -g eslint babel-eslint + - eslint app/assets/javascripts + - eslint --ext .es6 app/assets/javascripts + - eslint --ext .es6 test/javascripts + - eslint test/javascripts before_script: - bundle exec rake db:create db:migrate diff --git a/Brewfile b/Brewfile index 2f0e8f764e..ee7378b71c 100644 --- a/Brewfile +++ b/Brewfile @@ -1,22 +1,19 @@ # Install development dependencies on Mac OS X using Homebrew (http://mxcl.github.com/homebrew) -# ensure that Homebrew's sources are up to date -update - # add this repo to Homebrew's sources -tap homebrew/dupes +tap 'homebrew/dupes' # install the gcc compiler required for ruby -install apple-gcc42 +brew 'apple-gcc42' # you probably already have git installed; ensure that it is the latest version -install git +brew 'git' # install the PostgreSQL database -install postgresql +brew 'postgresql' # install the Redis datastore -install redis +brew 'redis' # install headless Javascript testing library -install phantomjs +brew 'phantomjs' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f00d5d078e..98aa05e4ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,129 +1,27 @@ # Contributing to Discourse -## Before You Start +## Important note for Developers -Anyone wishing to contribute to the **[Discourse/Discourse](https://github.com/discourse/discourse)** project **MUST read & sign the [Electronic Discourse Forums Contribution License Agreement](http://www.discourse.org/cla)**. The Discourse team is legally prevented from accepting any pull requests from users who have not signed the CLA first. +Anyone wishing to contribute to the [github.com/discourse/discourse](https://github.com/discourse/discourse) project **must read & sign our [Contributor License Agreement](http://www.discourse.org/cla)**. The Discourse team is legally prevented from accepting any pull requests from users who have not signed the CLA first. -## Reporting Bugs +For more information on -1. Always update to the most recent master release; the bug may already be resolved. +- how to set up your development environment +- first-time project suggestions +- code conventions +- step-by-step guide for GitHub commits -2. Search for similar issues on the [Discourse meta forum][m]; it may already be an identified problem. +**please read our [Discourse Development Contribution Guidelines](https://meta.discourse.org/t/discourse-development-contribution-guidelines/3823)** -3. Make sure you can reproduce your problem on our sandbox at [try.discourse.org](http://try.discourse.org) +## Everything Else -4. If this is a bug or problem that **requires any kind of extended discussion -- open [a topic on meta][m] about it**. +There are many other ways to contribute to Discourse besides code. We've outlined the most common ones below. -5. If possible, submit a Pull Request with a failing test. If you'd rather take matters into your own hands, fix the bug yourself (jump down to the "Contributing (Step-by-step)" section). +- [Reporting Bugs](https://meta.discourse.org/t/how-to-make-bug-reports-for-discourse/33070) +- [Requesting Features](https://meta.discourse.org/t/how-to-request-new-features-for-discourse/32986) +- [Translation](https://meta.discourse.org/t/contribute-a-translation-to-discourse/14882) +- Documentation (TBA) -6. When the bug is fixed, we will do our best to update the Discourse topic. +For anything else, just start a new topic on [Meta](https://meta.discourse.org/) and let us know what you're interested in working on. -## Requesting New Features - -1. Do not submit a feature request on GitHub; all feature requests on GitHub will be closed. Instead, visit the **[Discourse meta forum, features category](http://meta.discourse.org/category/feature)**, and search this list for similar feature requests. It's possible somebody has already asked for this feature or provided a pull request that we're still discussing. - -2. Provide a clear and detailed explanation of the feature you want and why it's important to add. The feature must apply to a wide array of users of Discourse; for smaller, more targeted "one-off" features, you might consider writing a plugin for Discourse. You may also want to provide us with some advance documentation on the feature, which will help the community to better understand where it will fit. - -3. If you're a Rock Star programmer, build the feature yourself (refer to the "Contributing (Step-by-step)" section below). - -## Contributing (Step-by-step) - -1. Clone the Repo: - - git clone git://github.com/discourse/discourse.git - -2. Create a new Branch: - - cd discourse - git checkout -b new_discourse_branch - - > Please keep your code clean: one feature or bug-fix per branch. If you find another bug, you want to fix while being in a new branch, please fix it in a separated branch instead. - -3. Code - * Adhere to common conventions you see in the existing code - * Include tests, and ensure they pass - * Search to see if your new functionality has been discussed on [the Discourse meta forum](http://meta.discourse.org), and include updates as appropriate - -4. Follow the Coding Conventions - * two spaces, no tabs - * no trailing whitespaces, blank lines should have no spaces - * use spaces around operators, after commas, colons, semicolons, around `{` and before `}` - * no space after `(`, `[` or before `]`, `)` - * use Ruby 1.9 hash syntax: prefer `{ a: 1 }` over `{ :a => 1 }` - * prefer `class << self; def method; end` over `def self.method` for class methods - * prefer `{ ... }` over `do ... end` for single-line blocks, avoid using `{ ... }` for multi-line blocks - * avoid `return` when not required - - > However, please note that **pull requests consisting entirely of style changes are not welcome on this project**. Style changes in the context of pull requests that also refactor code, fix bugs, improve functionality *are* welcome. - -5. Commit - - For every commit please write a short (max 72 characters) summary in the first line followed with a blank line and then more detailed descriptions of the change. Use markdown syntax for simple styling. - - **NEVER leave the commit message blank!** Provide a detailed, clear, and complete description of your commit! - - -6. Update your branch - - ``` - git fetch origin - git rebase origin/master - ``` - -7. Fork - - ``` - git remote add mine git@github.com:/discourse.git - ``` - -8. Push to your remote - - ``` - git push mine new_discourse_branch - ``` - -9. Issue a Pull Request - - Before submitting a pull-request, clean up the history, go over your commits and squash together minor changes and fixes into the corresponding commits. You can squash commits with the interactive rebase command: - - ``` - git fetch origin - git checkout new_discourse_branch - git rebase origin/master - git rebase -i - - < the editor opens and allows you to change the commit history > - < follow the instructions on the bottom of the editor > - - git push -f mine new_discourse_branch - ``` - - - In order to make a pull request, - * Navigate to the Discourse repository you just pushed to (e.g. https://github.com/your-user-name/discourse) - * Click "Pull Request". - * Write your branch name in the branch field (this is filled with "master" by default) - * Click "Update Commit Range". - * Ensure the changesets you introduced are included in the "Commits" tab. - * Ensure that the "Files Changed" incorporate all of your changes. - * Fill in some details about your potential patch including a meaningful title. - * Click "Send pull request". - - Thanks for that -- we'll get to your pull request ASAP, we love pull requests! - -10. Responding to Feedback - - The Discourse team may recommend adjustments to your code. Part of interacting with a healthy open-source community requires you to be open to learning new techniques and strategies; *don't get discouraged!* Remember: if the Discourse team suggest changes to your code, **they care enough about your work that they want to include it**, and hope that you can assist by implementing those revisions on your own. - - > Though we ask you to clean your history and squash commit before submitting a pull-request, please do not change any commits you've submitted already (as other work might be build on top). - -## Translations - -Translators can do their work in our [Transifex project](https://www.transifex.com/projects/p/discourse-org/). For more information, please see these how-to topics: - -* [Contributing a translation to Discourse](https://meta.discourse.org/t/contribute-a-translation-to-discourse/14882) -* [How to add a new language](https://meta.discourse.org/t/how-to-add-a-new-language/14970) - - - -[m]: http://meta.discourse.org +*Thanks for contributing!* diff --git a/Gemfile b/Gemfile index 03750100f4..2332689189 100644 --- a/Gemfile +++ b/Gemfile @@ -25,8 +25,6 @@ else gem 'seed-fu', '~> 2.3.3' end -gem 'actionpack-action_caching' - # Rails 4.1.6+ will relax the mail gem version requirement to `~> 2.5, >= 2.5.4`. # However, mail gem 2.6.x currently does not work with discourse because of the # reference to `Mail::RFC2822Parser` in `lib/email.rb`. This ensure discourse @@ -42,7 +40,7 @@ gem 'active_model_serializers', '~> 0.8.3' gem 'onebox' gem 'ember-rails' -gem 'ember-source', '1.11.3.1' +gem 'ember-source', '1.12.1' gem 'handlebars-source', '2.0.0' gem 'barber' gem 'babel-transpiler' @@ -65,7 +63,8 @@ gem 'email_reply_parser' # note: for image_optim to correctly work you need to follow # https://github.com/toy/image_optim -gem 'image_optim' +# pinned due to https://github.com/toy/image_optim/pull/75, docker image must be upgraded to upgrade +gem 'image_optim', '0.20.2' gem 'multi_json' gem 'mustache' gem 'nokogiri' @@ -92,6 +91,7 @@ gem 'rinku' gem 'sanitize' gem 'sass' gem 'sidekiq' +gem 'sidekiq-statistic' # for sidekiq web gem 'sinatra', require: false @@ -133,6 +133,7 @@ group :test, :development do gem 'rspec-given' gem 'pry-nav' gem 'spork-rails' + gem 'byebug' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 562f3a9197..46f0d552f3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,8 +15,6 @@ GEM 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.10) activesupport (= 4.1.10) builder (~> 3.1) @@ -48,9 +46,9 @@ GEM multi_json (~> 1.0) aws-sdk-resources (2.0.45) aws-sdk-core (= 2.0.45) - babel-source (4.6.6) - babel-transpiler (0.6.0) - babel-source (>= 4.0, < 5) + babel-source (5.8.19) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) execjs (~> 2.0) barber (0.9.0) ember-source (>= 1.0, < 2) @@ -62,11 +60,14 @@ GEM binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) + byebug (5.0.0) + columnize (= 0.9.0) celluloid (0.16.0) timers (~> 4.0.0) certified (1.0.0) coderay (1.1.0) - connection_pool (2.1.2) + columnize (0.9.0) + connection_pool (2.2.0) crass (1.0.1) daemons (1.2.2) debug_inspector (0.0.2) @@ -88,7 +89,7 @@ GEM ember-source (>= 1.1.0) jquery-rails (>= 1.0.17) railties (>= 3.1) - ember-source (1.11.3.1) + ember-source (1.12.1) erubis (2.7.0) eventmachine (1.0.7) excon (0.45.3) @@ -139,7 +140,7 @@ GEM jquery-rails (3.1.2) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) - json (1.8.2) + json (1.8.3) jwt (1.3.0) kgio (2.9.3) librarian (0.1.2) @@ -147,13 +148,13 @@ GEM thor (~> 0.15) libv8 (3.16.14.7) listen (0.7.3) - logster (0.8.1) + logster (1.0.0.3.pre) lru_redux (1.1.0) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) memory_profiler (0.9.3) - message_bus (1.0.12) + message_bus (1.0.16) rack (>= 1.1.3) redis metaclass (0.0.4) @@ -166,10 +167,10 @@ GEM mock_redis (0.14.0) moneta (0.8.0) msgpack (0.5.11) - multi_json (1.11.0) + multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) - mustache (0.99.8) + mustache (1.0.2) netrc (0.10.3) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) @@ -208,11 +209,11 @@ GEM omniauth-twitter (1.0.1) multi_json (~> 1.3) omniauth-oauth (~> 1.0) - onebox (1.5.19) - moneta (~> 0.7) - multi_json (~> 1.7) - mustache (~> 0.99) - nokogiri (~> 1.6.1) + onebox (1.5.26) + moneta (~> 0.8) + multi_json (~> 1.11) + mustache + nokogiri (~> 1.6.6) openid-redis-store (0.0.2) redis ruby-openid @@ -230,8 +231,8 @@ GEM puma (2.11.1) rack (>= 1.1, < 2.0) r2 (0.2.5) - rack (1.5.3) - rack-mini-profiler (0.9.3) + rack (1.5.5) + rack-mini-profiler (0.9.6) rack (>= 1.1.3) rack-openid (1.3.1) rack (>= 1.1.0) @@ -270,7 +271,7 @@ GEM trollop (>= 1.16.2) redcarpet (3.2.2) redis (3.2.1) - redis-namespace (1.5.1) + redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) ref (1.0.5) rest-client (1.7.2) @@ -326,12 +327,14 @@ GEM shoulda-context (1.2.1) shoulda-matchers (2.7.0) activesupport (>= 3.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) + sidekiq (3.4.2) + celluloid (~> 0.16.0) + connection_pool (~> 2.2, >= 2.2.0) + json (~> 1.0) + redis (~> 3.2, >= 3.2.1) + redis-namespace (~> 1.5, >= 1.5.2) + sidekiq-statistic (1.1.0) + sidekiq (~> 3.3, >= 3.3.4) simple-rss (1.3.1) simplecov (0.9.1) docile (~> 1.1.0) @@ -392,7 +395,6 @@ PLATFORMS ruby DEPENDENCIES - actionpack-action_caching active_model_serializers (~> 0.8.3) annotate aws-sdk @@ -400,11 +402,12 @@ DEPENDENCIES barber better_errors binding_of_caller + byebug certified discourse-qunit-rails email_reply_parser ember-rails - ember-source (= 1.11.3.1) + ember-source (= 1.12.1) excon fabrication (= 2.9.8) fakeweb (~> 1.3.0) @@ -419,7 +422,7 @@ DEPENDENCIES highline hiredis htmlentities - image_optim + image_optim (= 0.20.2) librarian (>= 0.0.25) listen (= 0.7.3) logster @@ -473,6 +476,7 @@ DEPENDENCIES seed-fu (~> 2.3.3) shoulda sidekiq + sidekiq-statistic simple-rss simplecov sinatra @@ -486,4 +490,4 @@ DEPENDENCIES unicorn BUNDLED WITH - 1.10.3 + 1.10.6 diff --git a/README.md b/README.md index 3b1bf88d06..5659382d31 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Discourse is built for the *next* 10 years of the Internet, so our requirements | Browsers | Tablets | Smartphones | | -------- | ------- | ----------- | -| Safari 5.1+| iPad 2+ | iOS 6+ | +| Safari 5.1+| iPad 2+ | iOS 7+ | | Google Chrome 23+ | Android 4.1+ | Android 4.1+ | | Internet Explorer 10+ | Windows 8 | Windows Phone 8 | | Firefox 16+ | | diff --git a/app/assets/fonts/FontAwesome.otf b/app/assets/fonts/FontAwesome.otf index f7936cc1e7..681bdd4d4c 100644 Binary files a/app/assets/fonts/FontAwesome.otf and b/app/assets/fonts/FontAwesome.otf differ diff --git a/app/assets/fonts/fontawesome-webfont.eot b/app/assets/fonts/fontawesome-webfont.eot index 33b2bb8005..a30335d748 100644 Binary files a/app/assets/fonts/fontawesome-webfont.eot and b/app/assets/fonts/fontawesome-webfont.eot differ diff --git a/app/assets/fonts/fontawesome-webfont.svg b/app/assets/fonts/fontawesome-webfont.svg index 1ee89d4368..6fd19abcb9 100644 --- a/app/assets/fonts/fontawesome-webfont.svg +++ b/app/assets/fonts/fontawesome-webfont.svg @@ -399,7 +399,7 @@ - + @@ -411,8 +411,8 @@ - - + + @@ -459,7 +459,7 @@ - + @@ -483,13 +483,13 @@ - + - + @@ -523,7 +523,7 @@ - + @@ -531,18 +531,18 @@ - + - + - + - + @@ -556,10 +556,85 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/fonts/fontawesome-webfont.ttf b/app/assets/fonts/fontawesome-webfont.ttf index ed9372f8ea..d7994e1308 100644 Binary files a/app/assets/fonts/fontawesome-webfont.ttf and b/app/assets/fonts/fontawesome-webfont.ttf differ diff --git a/app/assets/fonts/fontawesome-webfont.woff b/app/assets/fonts/fontawesome-webfont.woff index 8b280b98fa..6fd4ede0f3 100644 Binary files a/app/assets/fonts/fontawesome-webfont.woff and b/app/assets/fonts/fontawesome-webfont.woff differ diff --git a/app/assets/fonts/fontawesome-webfont.woff2 b/app/assets/fonts/fontawesome-webfont.woff2 index 3311d58514..5560193ccc 100644 Binary files a/app/assets/fonts/fontawesome-webfont.woff2 and b/app/assets/fonts/fontawesome-webfont.woff2 differ diff --git a/app/assets/fonts/zocial-regular-webfont.eot b/app/assets/fonts/zocial-regular-webfont.eot deleted file mode 100644 index 5db5d21666..0000000000 Binary files a/app/assets/fonts/zocial-regular-webfont.eot and /dev/null differ diff --git a/app/assets/fonts/zocial-regular-webfont.svg b/app/assets/fonts/zocial-regular-webfont.svg deleted file mode 100644 index 130d83ca10..0000000000 --- a/app/assets/fonts/zocial-regular-webfont.svg +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/assets/fonts/zocial-regular-webfont.ttf b/app/assets/fonts/zocial-regular-webfont.ttf deleted file mode 100644 index a043566360..0000000000 Binary files a/app/assets/fonts/zocial-regular-webfont.ttf and /dev/null differ diff --git a/app/assets/fonts/zocial-regular-webfont.woff b/app/assets/fonts/zocial-regular-webfont.woff deleted file mode 100644 index 5a91cf3056..0000000000 Binary files a/app/assets/fonts/zocial-regular-webfont.woff and /dev/null differ diff --git a/app/assets/javascripts/admin.js.erb b/app/assets/javascripts/admin.js.erb index ce47046a21..41f0315deb 100644 --- a/app/assets/javascripts/admin.js.erb +++ b/app/assets/javascripts/admin.js.erb @@ -2,4 +2,12 @@ require_asset("main_include_admin.js") DiscoursePluginRegistry.admin_javascripts.each { |js| require_asset(js) } + +DiscoursePluginRegistry.each_globbed_asset(admin: true) do |f, ext| + if File.directory?(f) + depend_on(f) + elsif f.to_s.end_with?(".#{ext}") + require_asset(f) + end +end %> diff --git a/app/assets/javascripts/discourse/adapters/build-plugin.js.es6 b/app/assets/javascripts/admin/adapters/build-plugin.js.es6 similarity index 79% rename from app/assets/javascripts/discourse/adapters/build-plugin.js.es6 rename to app/assets/javascripts/admin/adapters/build-plugin.js.es6 index 218a8d8d8c..b73bf7ecb1 100644 --- a/app/assets/javascripts/discourse/adapters/build-plugin.js.es6 +++ b/app/assets/javascripts/admin/adapters/build-plugin.js.es6 @@ -2,8 +2,8 @@ import RestAdapter from 'discourse/adapters/rest'; export default function buildPluginAdapter(pluginName) { return RestAdapter.extend({ - pathFor(store, type) { - return "/admin/plugins/" + pluginName + this._super(store, type); + pathFor(store, type, findArgs) { + return "/admin/plugins/" + pluginName + this._super(store, type, findArgs); } }); } diff --git a/app/assets/javascripts/admin/adapters/customization-base.js.es6 b/app/assets/javascripts/admin/adapters/customization-base.js.es6 new file mode 100644 index 0000000000..f57240a116 --- /dev/null +++ b/app/assets/javascripts/admin/adapters/customization-base.js.es6 @@ -0,0 +1,7 @@ +import RestAdapter from 'discourse/adapters/rest'; + +export default RestAdapter.extend({ + basePath() { + return "/admin/customize/"; + } +}); diff --git a/app/assets/javascripts/admin/adapters/embedding.js.es6 b/app/assets/javascripts/admin/adapters/embedding.js.es6 new file mode 100644 index 0000000000..c8985cfdca --- /dev/null +++ b/app/assets/javascripts/admin/adapters/embedding.js.es6 @@ -0,0 +1,7 @@ +import RestAdapter from 'discourse/adapters/rest'; + +export default RestAdapter.extend({ + pathFor() { + return "/admin/customize/embedding"; + } +}); diff --git a/app/assets/javascripts/admin/adapters/site-text-type.js.es6 b/app/assets/javascripts/admin/adapters/site-text-type.js.es6 new file mode 100644 index 0000000000..b547b06f3c --- /dev/null +++ b/app/assets/javascripts/admin/adapters/site-text-type.js.es6 @@ -0,0 +1,2 @@ +import CustomizationBase from 'admin/adapters/customization-base'; +export default CustomizationBase; diff --git a/app/assets/javascripts/admin/adapters/site-text.js.es6 b/app/assets/javascripts/admin/adapters/site-text.js.es6 new file mode 100644 index 0000000000..b547b06f3c --- /dev/null +++ b/app/assets/javascripts/admin/adapters/site-text.js.es6 @@ -0,0 +1,2 @@ +import CustomizationBase from 'admin/adapters/customization-base'; +export default CustomizationBase; diff --git a/app/assets/javascripts/admin/adapters/user-field.js.es6 b/app/assets/javascripts/admin/adapters/user-field.js.es6 new file mode 100644 index 0000000000..b547b06f3c --- /dev/null +++ b/app/assets/javascripts/admin/adapters/user-field.js.es6 @@ -0,0 +1,2 @@ +import CustomizationBase from 'admin/adapters/customization-base'; +export default CustomizationBase; diff --git a/app/assets/javascripts/admin/components/ace-editor.js.es6 b/app/assets/javascripts/admin/components/ace-editor.js.es6 index d1813bcf35..b660d48859 100644 --- a/app/assets/javascripts/admin/components/ace-editor.js.es6 +++ b/app/assets/javascripts/admin/components/ace-editor.js.es6 @@ -26,8 +26,18 @@ export default Ember.Component.extend({ this._editor.destroy(); this._editor = null; } + if (this.appEvents) { + // xxx: don't run during qunit tests + this.appEvents.off('ace:resize', this, this.resize); + } }.on('willDestroyElement'), + resize() { + if (this._editor) { + this._editor.resize(); + } + }, + _initEditor: function() { const self = this; @@ -43,9 +53,14 @@ export default Ember.Component.extend({ self.set('content', editor.getSession().getValue()); self._skipContentChangeEvent = false; }); + editor.$blockScrolling = Infinity; self.$().data('editor', editor); self._editor = editor; + if (self.appEvents) { + // xxx: don't run during qunit tests + self.appEvents.on('ace:resize', self, self.resize); + } }); }); diff --git a/app/assets/javascripts/admin/components/admin-form-row.js.es6 b/app/assets/javascripts/admin/components/admin-form-row.js.es6 new file mode 100644 index 0000000000..e7cef2edb0 --- /dev/null +++ b/app/assets/javascripts/admin/components/admin-form-row.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + classNames: ['row'] +}); diff --git a/app/assets/javascripts/admin/components/admin-report-counts.js.es6 b/app/assets/javascripts/admin/components/admin-report-counts.js.es6 index 25bf94db21..46ab32f609 100644 --- a/app/assets/javascripts/admin/components/admin-report-counts.js.es6 +++ b/app/assets/javascripts/admin/components/admin-report-counts.js.es6 @@ -1,3 +1,5 @@ export default Ember.Component.extend({ - tagName: 'tr' + tagName: 'tr', + reverseColors: Ember.computed.match('report.type', /^(time_to_first_response|topics_with_no_response)$/), + classNameBindings: ['reverseColors'] }); diff --git a/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 new file mode 100644 index 0000000000..d10280daf7 --- /dev/null +++ b/app/assets/javascripts/admin/components/admin-user-field-item.js.es6 @@ -0,0 +1,94 @@ +import UserField from 'admin/models/user-field'; +import { bufferedProperty } from 'discourse/mixins/buffered-content'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; +import { propertyEqual } from 'discourse/lib/computed'; + +export default Ember.Component.extend(bufferedProperty('userField'), { + editing: Ember.computed.empty('userField.id'), + classNameBindings: [':user-field'], + + cantMoveUp: propertyEqual('userField', 'firstField'), + cantMoveDown: propertyEqual('userField', 'lastField'), + + userFieldsDescription: function() { + return I18n.t('admin.user_fields.description'); + }.property(), + + bufferedFieldType: function() { + return UserField.fieldTypeById(this.get('buffered.field_type')); + }.property('buffered.field_type'), + + _focusOnEdit: function() { + if (this.get('editing')) { + Ember.run.scheduleOnce('afterRender', this, '_focusName'); + } + }.observes('editing').on('didInsertElement'), + + _focusName: function() { + $('.user-field-name').select(); + }, + + fieldName: function() { + return UserField.fieldTypeById(this.get('userField.field_type')).get('name'); + }.property('userField.field_type'), + + flags: function() { + const ret = []; + if (this.get('userField.editable')) { + ret.push(I18n.t('admin.user_fields.editable.enabled')); + } + if (this.get('userField.required')) { + ret.push(I18n.t('admin.user_fields.required.enabled')); + } + if (this.get('userField.show_on_profile')) { + ret.push(I18n.t('admin.user_fields.show_on_profile.enabled')); + } + + return ret.join(', '); + }.property('userField.editable', 'userField.required', 'userField.show_on_profile'), + + actions: { + save() { + const self = this; + const buffered = this.get('buffered'); + const attrs = buffered.getProperties('name', + 'description', + 'field_type', + 'editable', + 'required', + 'show_on_profile', + 'options'); + + this.get('userField').save(attrs).then(function() { + self.set('editing', false); + self.commitBuffer(); + }).catch(popupAjaxError); + }, + + moveUp() { + this.sendAction('moveUpAction', this.get('userField')); + }, + + moveDown() { + this.sendAction('moveDownAction', this.get('userField')); + }, + + edit() { + this.set('editing', true); + }, + + destroy() { + this.sendAction('destroyAction', this.get('userField')); + }, + + cancel() { + const id = this.get('userField.id'); + if (Ember.isEmpty(id)) { + this.sendAction('destroyAction', this.get('userField')); + } else { + this.rollbackBuffer(); + this.set('editing', false); + } + } + } +}); diff --git a/app/assets/javascripts/admin/components/customize-link.js.es6 b/app/assets/javascripts/admin/components/customize-link.js.es6 new file mode 100644 index 0000000000..79d8cc26f4 --- /dev/null +++ b/app/assets/javascripts/admin/components/customize-link.js.es6 @@ -0,0 +1,10 @@ +export default Ember.Component.extend({ + router: function() { + return this.container.lookup('router:main'); + }.property(), + + active: function() { + const id = this.get('customization.id'); + return this.get('router.url').indexOf(`/customize/css_html/${id}/css`) !== -1; + }.property('router.url', 'customization.id') +}); diff --git a/app/assets/javascripts/admin/components/embeddable-host.js.es6 b/app/assets/javascripts/admin/components/embeddable-host.js.es6 new file mode 100644 index 0000000000..f33c750965 --- /dev/null +++ b/app/assets/javascripts/admin/components/embeddable-host.js.es6 @@ -0,0 +1,63 @@ +import { bufferedProperty } from 'discourse/mixins/buffered-content'; +import computed from 'ember-addons/ember-computed-decorators'; +import { on, observes } from 'ember-addons/ember-computed-decorators'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; + +export default Ember.Component.extend(bufferedProperty('host'), { + editToggled: false, + tagName: 'tr', + categoryId: null, + + editing: Ember.computed.or('host.isNew', 'editToggled'), + + @on('didInsertElement') + @observes('editing') + _focusOnInput() { + Ember.run.schedule('afterRender', () => { this.$('.host-name').focus(); }); + }, + + @computed('buffered.host', 'host.isSaving') + cantSave(host, isSaving) { + return isSaving || Ember.isEmpty(host); + }, + + actions: { + edit() { + this.set('categoryId', this.get('host.category.id')); + this.set('editToggled', true); + }, + + save() { + if (this.get('cantSave')) { return; } + + const props = this.get('buffered').getProperties('host'); + props.category_id = this.get('categoryId'); + + const host = this.get('host'); + host.save(props).then(() => { + host.set('category', Discourse.Category.findById(this.get('categoryId'))); + this.set('editToggled', false); + }).catch(popupAjaxError); + }, + + delete() { + bootbox.confirm(I18n.t('admin.embedding.confirm_delete'), (result) => { + if (result) { + this.get('host').destroyRecord().then(() => { + this.sendAction('deleteHost', this.get('host')); + }); + } + }); + }, + + cancel() { + const host = this.get('host'); + if (host.get('isNew')) { + this.sendAction('deleteHost', host); + } else { + this.rollbackBuffer(); + this.set('editToggled', false); + } + } + } +}); diff --git a/app/assets/javascripts/admin/components/embedding-setting.js.es6 b/app/assets/javascripts/admin/components/embedding-setting.js.es6 new file mode 100644 index 0000000000..904afacfee --- /dev/null +++ b/app/assets/javascripts/admin/components/embedding-setting.js.es6 @@ -0,0 +1,23 @@ +import computed from 'ember-addons/ember-computed-decorators'; + +export default Ember.Component.extend({ + classNames: ['embed-setting'], + + @computed('field') + inputId(field) { return field.dasherize(); }, + + @computed('field') + translationKey(field) { return `admin.embedding.${field}`; }, + + @computed('type') + isCheckbox(type) { return type === "checkbox"; }, + + @computed('value') + checked: { + get(value) { return !!value; }, + set(value) { + this.set('value', value); + return value; + } + } +}); diff --git a/app/assets/javascripts/admin/components/group-member.js.es6 b/app/assets/javascripts/admin/components/group-member.js.es6 new file mode 100644 index 0000000000..abf0f2984a --- /dev/null +++ b/app/assets/javascripts/admin/components/group-member.js.es6 @@ -0,0 +1,9 @@ +export default Ember.Component.extend({ + classNames: ["item"], + + actions: { + remove() { + this.sendAction('removeAction', this.get('member')); + } + } +}); diff --git a/app/assets/javascripts/admin/components/highlighted-code.js.es6 b/app/assets/javascripts/admin/components/highlighted-code.js.es6 new file mode 100644 index 0000000000..4fc413fd89 --- /dev/null +++ b/app/assets/javascripts/admin/components/highlighted-code.js.es6 @@ -0,0 +1,12 @@ +import { on, observes } from 'ember-addons/ember-computed-decorators'; +import highlightSyntax from 'discourse/lib/highlight-syntax'; + +export default Ember.Component.extend({ + + @on('didInsertElement') + @observes('code') + _refresh: function() { + highlightSyntax(this.$()); + } + +}); diff --git a/app/assets/javascripts/admin/components/ip-lookup.js.es6 b/app/assets/javascripts/admin/components/ip-lookup.js.es6 index bf46c9a2da..f1fe7a391c 100644 --- a/app/assets/javascripts/admin/components/ip-lookup.js.es6 +++ b/app/assets/javascripts/admin/components/ip-lookup.js.es6 @@ -22,7 +22,7 @@ export default Ember.Component.extend({ this.set("show", true); if (!this.get("location")) { - Discourse.ajax("/admin/users/ip-info.json", { + Discourse.ajax("/admin/users/ip-info", { data: { ip: this.get("ip") } }).then(function (location) { self.set("location", Em.Object.create(location)); @@ -38,7 +38,7 @@ export default Ember.Component.extend({ "order": "trust_level DESC" }; - Discourse.ajax("/admin/users/total-others-with-same-ip.json", { data: data }).then(function (result) { + Discourse.ajax("/admin/users/total-others-with-same-ip", { data }).then(function (result) { self.set("totalOthersWithSameIP", result.total); }); diff --git a/app/assets/javascripts/admin/components/permalink-form.js.es6 b/app/assets/javascripts/admin/components/permalink-form.js.es6 new file mode 100644 index 0000000000..1bb29e52eb --- /dev/null +++ b/app/assets/javascripts/admin/components/permalink-form.js.es6 @@ -0,0 +1,56 @@ +export default Ember.Component.extend({ + classNames: ['permalink-form'], + formSubmitted: false, + permalinkType: 'topic_id', + + permalinkTypes: function() { + return [ + {id: 'topic_id', name: I18n.t('admin.permalink.topic_id')}, + {id: 'post_id', name: I18n.t('admin.permalink.post_id')}, + {id: 'category_id', name: I18n.t('admin.permalink.category_id')}, + {id: 'external_url', name: I18n.t('admin.permalink.external_url')} + ]; + }.property(), + + permalinkTypePlaceholder: function() { + return 'admin.permalink.' + this.get('permalinkType'); + }.property('permalinkType'), + + actions: { + submit: function() { + if (!this.get('formSubmitted')) { + const self = this; + self.set('formSubmitted', true); + const permalink = Discourse.Permalink.create({url: self.get('url'), permalink_type: self.get('permalinkType'), permalink_type_value: self.get('permalink_type_value')}); + permalink.save().then(function(result) { + self.set('url', ''); + self.set('permalink_type_value', ''); + self.set('formSubmitted', false); + self.sendAction('action', Discourse.Permalink.create(result.permalink)); + Em.run.schedule('afterRender', function() { self.$('.permalink-url').focus(); }); + }, function(e) { + self.set('formSubmitted', false); + let error; + if (e.responseJSON && e.responseJSON.errors) { + error = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')}); + } else { + error = I18n.t("generic_error"); + } + bootbox.alert(error, function() { self.$('.permalink-url').focus(); }); + }); + } + } + }, + + didInsertElement: function() { + var self = this; + self._super(); + Em.run.schedule('afterRender', function() { + self.$('.external-url').keydown(function(e) { + if (e.keyCode === 13) { // enter key + self.send('submit'); + } + }); + }); + } +}); diff --git a/app/assets/javascripts/admin/components/site-setting.js.es6 b/app/assets/javascripts/admin/components/site-setting.js.es6 index f8e3cc6b2c..8b6601b4d3 100644 --- a/app/assets/javascripts/admin/components/site-setting.js.es6 +++ b/app/assets/javascripts/admin/components/site-setting.js.es6 @@ -1,50 +1,43 @@ import BufferedContent from 'discourse/mixins/buffered-content'; import ScrollTop from 'discourse/mixins/scroll-top'; import SiteSetting from 'admin/models/site-setting'; +import { propertyNotEqual } from 'discourse/lib/computed'; +import computed from 'ember-addons/ember-computed-decorators'; -const CustomTypes = ['bool', 'enum', 'list', 'url_list']; +const CustomTypes = ['bool', 'enum', 'list', 'url_list', 'host_list', 'category_list', 'value_list']; export default Ember.Component.extend(BufferedContent, ScrollTop, { classNameBindings: [':row', ':setting', 'setting.overridden', 'typeClass'], content: Ember.computed.alias('setting'), - dirty: Discourse.computed.propertyNotEqual('buffered.value', 'setting.value'), + dirty: propertyNotEqual('buffered.value', 'setting.value'), validationMessage: null, - preview: function() { - const preview = this.get('setting.preview'); + @computed("setting.preview", "buffered.value") + preview(preview, value) { if (preview) { - return new Handlebars.SafeString("
" + - preview.replace("{{value}}", this.get('buffered.value')) + - "
"); + return new Handlebars.SafeString("
" + preview.replace(/\{\{value\}\}/g, value) + "
"); } - }.property('buffered.value'), + }, - typeClass: function() { - return this.get('partialType').replace("_", "-"); - }.property('partialType'), + @computed('componentType') + typeClass(componentType) { + return componentType.replace(/\_/g, '-'); + }, - enabled: function(key, value) { - if (arguments.length > 1) { - this.set('buffered.value', value ? 'true' : 'false'); - } + @computed("setting.setting") + settingName(setting) { + return setting.replace(/\_/g, ' '); + }, - const bufferedValue = this.get('buffered.value'); - if (Ember.isEmpty(bufferedValue)) { return false; } - return bufferedValue === 'true'; - }.property('buffered.value'), + @computed("setting.type") + componentType(type) { + return CustomTypes.indexOf(type) !== -1 ? type : 'string'; + }, - 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'), + @computed("typeClass") + componentName(typeClass) { + return "site-settings/" + typeClass; + }, _watchEnterKey: function() { const self = this; @@ -60,8 +53,8 @@ export default Ember.Component.extend(BufferedContent, ScrollTop, { }.on("willDestroyElement"), _save() { - const setting = this.get('buffered'); - const self = this; + const self = this, + setting = this.get('buffered'); SiteSetting.update(setting.get('setting'), setting.get('value')).then(function() { self.set('validationMessage', null); self.commitBuffer(); diff --git a/app/assets/javascripts/admin/components/site-settings/bool.js.es6 b/app/assets/javascripts/admin/components/site-settings/bool.js.es6 new file mode 100644 index 0000000000..40fcfb354b --- /dev/null +++ b/app/assets/javascripts/admin/components/site-settings/bool.js.es6 @@ -0,0 +1,17 @@ +import computed from "ember-addons/ember-computed-decorators"; + +export default Ember.Component.extend({ + + @computed("value") + enabled: { + get(value) { + if (Ember.isEmpty(value)) { return false; } + return value === "true"; + }, + set(value) { + this.set("value", value ? "true" : "false"); + return value; + } + }, + +}); diff --git a/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 b/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 new file mode 100644 index 0000000000..487239b78f --- /dev/null +++ b/app/assets/javascripts/admin/components/site-settings/category-list.js.es6 @@ -0,0 +1,16 @@ +import computed from "ember-addons/ember-computed-decorators"; + +export default Ember.Component.extend({ + + @computed("value") + selectedCategories: { + get(value) { + return Discourse.Category.findByIds(value.split("|")); + }, + set(value) { + this.set("value", value.mapBy("id").join("|")); + return value; + } + } + +}); diff --git a/app/assets/javascripts/admin/components/url-list.js.es6 b/app/assets/javascripts/admin/components/url-list.js.es6 deleted file mode 100644 index 4562c4b20e..0000000000 --- a/app/assets/javascripts/admin/components/url-list.js.es6 +++ /dev/null @@ -1,32 +0,0 @@ -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); - } - } -}); diff --git a/app/assets/javascripts/admin/components/value-list.js.es6 b/app/assets/javascripts/admin/components/value-list.js.es6 new file mode 100644 index 0000000000..3aa66ff187 --- /dev/null +++ b/app/assets/javascripts/admin/components/value-list.js.es6 @@ -0,0 +1,104 @@ +export default Ember.Component.extend({ + classNameBindings: [':value-list'], + + _enableSorting: function() { + const self = this; + const placeholder = document.createElement("div"); + placeholder.className = "placeholder"; + + let dragging = null; + let over = null; + let nodePlacement; + + this.$().on('dragstart.discourse', '.values .value', function(e) { + dragging = e.currentTarget; + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData("text/html", e.currentTarget); + }); + + this.$().on('dragend.discourse', '.values .value', function() { + Ember.run(function() { + dragging.parentNode.removeChild(placeholder); + dragging.style.display = 'block'; + + // Update data + const from = Number(dragging.dataset.index); + let to = Number(over.dataset.index); + if (from < to) to--; + if (nodePlacement === "after") to++; + + const collection = self.get('collection'); + const fromObj = collection.objectAt(from); + collection.replace(from, 1); + collection.replace(to, 0, [fromObj]); + self._saveValues(); + }); + return false; + }); + + this.$().on('dragover.discourse', '.values', function(e) { + e.preventDefault(); + dragging.style.display = 'none'; + if (e.target.className === "placeholder") { return; } + over = e.target; + + const relY = e.originalEvent.clientY - over.offsetTop; + const height = over.offsetHeight / 2; + const parent = e.target.parentNode; + + if (relY > height) { + nodePlacement = "after"; + parent.insertBefore(placeholder, e.target.nextElementSibling); + } else if(relY < height) { + nodePlacement = "before"; + parent.insertBefore(placeholder, e.target); + } + }); + }.on('didInsertElement'), + + _removeSorting: function() { + this.$().off('dragover.discourse').off('dragend.discourse').off('dragstart.discourse'); + }.on('willDestroyElement'), + + _setupCollection: function() { + const values = this.get('values'); + if (this.get('inputType') === "array") { + this.set('collection', values || []); + } else { + this.set('collection', (values && values.length) ? values.split("\n") : []); + } + }.on('init').observes('values'), + + _saveValues: function() { + if (this.get('inputType') === "array") { + this.set('values', this.get('collection')); + } else { + this.set('values', this.get('collection').join("\n")); + } + }, + + inputInvalid: Ember.computed.empty('newValue'), + + keyDown(e) { + if (e.keyCode === 13) { + this.send('addValue'); + } + }, + + actions: { + addValue() { + if (this.get('inputInvalid')) { return; } + + this.get('collection').addObject(this.get('newValue')); + this.set('newValue', ''); + + this._saveValues(); + }, + + removeValue(value) { + const collection = this.get('collection'); + collection.removeObject(value); + this._saveValues(); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 index 8eceb0bb7d..1617e6133a 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups-index.js.es6 @@ -1,20 +1,18 @@ export default Ember.ArrayController.extend({ needs: ["adminBackups"], - status: Em.computed.alias("controllers.adminBackups"), - isOperationRunning: Em.computed.alias("status.isOperationRunning"), - restoreDisabled: Em.computed.alias("status.restoreDisabled"), + status: Ember.computed.alias("controllers.adminBackups"), uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(), restoreTitle: function() { - if (!this.get('status.allowRestore')) { + if (!this.get('status.model.allowRestore')) { return "admin.backups.operations.restore.is_disabled"; - } else if (this.get("status.isOperationRunning")) { + } else if (this.get("status.model.isOperationRunning")) { return "admin.backups.operations.is_running"; } else { return "admin.backups.operations.restore.title"; } - }.property("status.{allowRestore,isOperationRunning}"), + }.property("status.model.{allowRestore,isOperationRunning}"), actions: { diff --git a/app/assets/javascripts/admin/controllers/admin-backups.js.es6 b/app/assets/javascripts/admin/controllers/admin-backups.js.es6 index 64bb7ad105..a429883378 100644 --- a/app/assets/javascripts/admin/controllers/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-backups.js.es6 @@ -1,5 +1,5 @@ -export default Ember.ObjectController.extend({ - noOperationIsRunning: Em.computed.not("isOperationRunning"), - rollbackEnabled: Em.computed.and("canRollback", "restoreEnabled", "noOperationIsRunning"), - rollbackDisabled: Em.computed.not("rollbackEnabled") +export default Ember.Controller.extend({ + noOperationIsRunning: Ember.computed.not("model.isOperationRunning"), + rollbackEnabled: Ember.computed.and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning"), + rollbackDisabled: Ember.computed.not("rollbackEnabled") }); diff --git a/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 index ac648462b2..96547435d8 100644 --- a/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-badges-show.js.es6 @@ -1,7 +1,8 @@ import { popupAjaxError } from 'discourse/lib/ajax-error'; import BufferedContent from 'discourse/mixins/buffered-content'; +import { propertyNotEqual } from 'discourse/lib/computed'; -export default Ember.ObjectController.extend(BufferedContent, { +export default Ember.Controller.extend(BufferedContent, { needs: ['admin-badges'], saving: false, savingStatus: '', @@ -12,9 +13,18 @@ export default Ember.ObjectController.extend(BufferedContent, { protectedSystemFields: Em.computed.alias('controllers.admin-badges.protectedSystemFields'), readOnly: Ember.computed.alias('buffered.system'), - showDisplayName: Discourse.computed.propertyNotEqual('name', 'displayName'), + showDisplayName: propertyNotEqual('name', 'displayName'), canEditDescription: Em.computed.none('buffered.translatedDescription'), + hasQuery: function() { + const bQuery = this.get('buffered.query'); + if (bQuery) { + return bQuery.trim().length > 0; + } + const mQuery = this.get('model.query'); + return mQuery && mQuery.trim().length > 0; + }.property('model.query', 'buffered.query'), + _resetSaving: function() { this.set('saving', false); this.set('savingStatus', ''); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 index 636905c009..4a6a58021b 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-colors.js.es6 @@ -1,13 +1,4 @@ -/** - This controller supports interface for creating custom CSS skins in Discourse. - - @class AdminCustomizeColorsController - @extends Ember.Controller - @namespace Discourse - @module Discourse -**/ export default Ember.ArrayController.extend({ - onlyOverridden: false, baseColorScheme: function() { diff --git a/app/assets/javascripts/admin/controllers/admin-customize-css-html-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-css-html-show.js.es6 new file mode 100644 index 0000000000..03c855a969 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-customize-css-html-show.js.es6 @@ -0,0 +1,79 @@ +import { url } from 'discourse/lib/computed'; + +const sections = ['css', 'header', 'top', 'footer', 'head-tag', 'body-tag', + 'mobile-css', 'mobile-header', 'mobile-top', 'mobile-footer', + 'embedded-css']; + +const activeSections = {}; +sections.forEach(function(s) { + activeSections[Ember.String.camelize(s) + "Active"] = Ember.computed.equal('section', s); +}); + + +export default Ember.Controller.extend(activeSections, { + maximized: false, + section: null, + + previewUrl: url("model.key", "/?preview-style=%@"), + downloadUrl: url('model.id', '/admin/site_customizations/%@'), + + mobile: function() { + return this.get('section').indexOf('mobile-') === 0; + }.property('section'), + + maximizeIcon: function() { + return this.get('maximized') ? 'compress' : 'expand'; + }.property('maximized'), + + saveButtonText: function() { + return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save'); + }.property('model.isSaving'), + + saveDisabled: function() { + return !this.get('model.changed') || this.get('model.isSaving'); + }.property('model.changed', 'model.isSaving'), + + needs: ['adminCustomizeCssHtml'], + + undoPreviewUrl: url('/?preview-style='), + defaultStyleUrl: url('/?preview-style=default'), + + actions: { + save() { + this.get('model').saveChanges(); + }, + + destroy() { + const self = this; + return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { + if (result) { + const model = self.get('model'); + model.destroyRecord().then(function() { + self.get('controllers.adminCustomizeCssHtml').get('model').removeObject(model); + self.transitionToRoute('adminCustomizeCssHtml'); + }); + } + }); + }, + + toggleMaximize: function() { + this.toggleProperty('maximized'); + }, + + toggleMobile: function() { + const section = this.get('section'); + + // Try to send to the same tab as before + let dest; + if (this.get('mobile')) { + dest = section.replace('mobile-', ''); + if (sections.indexOf(dest) === -1) { dest = 'css'; } + } else { + dest = 'mobile-' + section; + if (sections.indexOf(dest) === -1) { dest = 'mobile-css'; } + } + this.replaceRoute('adminCustomizeCssHtml.show', this.get('model.id'), dest); + } + } + +}); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 deleted file mode 100644 index 266f06c3e8..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-customize-css-html.js.es6 +++ /dev/null @@ -1,63 +0,0 @@ -/** - This controller supports interface for creating custom CSS skins in Discourse. - - @class AdminCustomizeCssHtmlController - @extends Ember.Controller - @namespace Discourse - @module Discourse -**/ -export default Ember.ArrayController.extend({ - - actions: { - - /** - Create a new customization style - - @method newCustomization - **/ - newCustomization: function() { - var item = Discourse.SiteCustomization.create({name: I18n.t("admin.customize.new_style")}); - this.pushObject(item); - this.set('selectedItem', item); - }, - - /** - Select a given style - - @method selectStyle - @param {Discourse.SiteCustomization} style The style we are selecting - **/ - selectStyle: function(style) { - this.set('selectedItem', style); - }, - - /** - Save the current customization - - @method save - **/ - save: function() { - this.get('selectedItem').save(); - }, - - /** - Destroy the current customization - - @method destroy - **/ - destroy: function() { - var _this = this; - return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { - var selected; - if (result) { - selected = _this.get('selectedItem'); - selected.destroy(); - _this.set('selectedItem', null); - return _this.removeObject(selected); - } - }); - } - - } - -}); diff --git a/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 b/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 index ad572337e0..c712a8ac5a 100644 --- a/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-dashboard.js.es6 @@ -1,24 +1,19 @@ -/** - This controller supports the default interface when you enter the admin section. +import { setting } from 'discourse/lib/computed'; - @class AdminDashboardController - @extends Ember.Controller - @namespace Discourse - @module Discourse -**/ +// This controller supports the default interface when you enter the admin section. export default Ember.Controller.extend({ loading: true, versionCheck: null, problemsCheckMinutes: 1, - showVersionChecks: Discourse.computed.setting('version_checks'), + showVersionChecks: setting('version_checks'), foundProblems: function() { return(Discourse.User.currentProp('admin') && this.get('problems') && this.get('problems').length > 0); }.property('problems'), thereWereProblems: function() { - if(!Discourse.User.currentProp('admin')) { return false } + if(!Discourse.User.currentProp('admin')) { return false; } if( this.get('foundProblems') ) { this.set('hadProblems', true); return true; diff --git a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 index 341ca4f652..b4006391b2 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-index.js.es6 @@ -1,6 +1,4 @@ -import DiscourseController from 'discourse/controllers/controller'; - -export default DiscourseController.extend({ +export default Ember.Controller.extend({ /** Is the "send test email" button disabled? diff --git a/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 index 84d1f3e4b5..f259acba84 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-preview-digest.js.es6 @@ -1,20 +1,17 @@ -import ObjectController from 'discourse/controllers/object'; - -export default ObjectController.extend({ +export default Ember.Controller.extend({ actions: { - refresh: function() { - var model = this.get('model'), - self = this; + refresh() { + const model = this.get('model'); - self.set('loading', true); - Discourse.EmailPreview.findDigest(this.get('lastSeen')).then(function (email) { + this.set('loading', true); + Discourse.EmailPreview.findDigest(this.get('lastSeen')).then(email => { model.setProperties(email.getProperties('html_content', 'text_content')); - self.set('loading', false); + this.set('loading', false); }); }, - toggleShowHtml: function() { + toggleShowHtml() { this.toggleProperty('showHtml'); } } diff --git a/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 index 0b23e90268..a03bd8212d 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-sent.js.es6 @@ -1,8 +1,8 @@ -import DiscourseController from 'discourse/controllers/controller'; +import debounce from 'discourse/lib/debounce'; -export default DiscourseController.extend({ +export default Ember.Controller.extend({ - filterEmailLogs: Discourse.debounce(function() { + filterEmailLogs: debounce(function() { var self = this; Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) { self.set("model", logs); diff --git a/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 b/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 index 9e3affff6a..1d83f14c6d 100644 --- a/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-email-skipped.js.es6 @@ -1,7 +1,7 @@ -import DiscourseController from 'discourse/controllers/controller'; +import debounce from 'discourse/lib/debounce'; -export default DiscourseController.extend({ - filterEmailLogs: Discourse.debounce(function() { +export default Ember.Controller.extend({ + filterEmailLogs: debounce(function() { var self = this; Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) { self.set("model", logs); diff --git a/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 b/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 new file mode 100644 index 0000000000..780c61f3f9 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-embedding.js.es6 @@ -0,0 +1,55 @@ +import computed from 'ember-addons/ember-computed-decorators'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; + +export default Ember.Controller.extend({ + saved: false, + embedding: null, + + // show settings if we have at least one created host + @computed('embedding.embeddable_hosts.@each.isCreated') + showSecondary() { + const hosts = this.get('embedding.embeddable_hosts'); + return hosts.length && hosts.findProperty('isCreated'); + }, + + @computed('embedding.base_url') + embeddingCode(baseUrl) { + + const html = +`
+ +`; + + return html; + }, + + actions: { + saveChanges() { + const embedding = this.get('embedding'); + const updates = embedding.getProperties(embedding.get('fields')); + + this.set('saved', false); + this.get('embedding').update(updates).then(() => { + this.set('saved', true); + }).catch(popupAjaxError); + }, + + addHost() { + const host = this.store.createRecord('embeddable-host'); + this.get('embedding.embeddable_hosts').pushObject(host); + }, + + deleteHost(host) { + this.get('embedding.embeddable_hosts').removeObject(host); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-group.js.es6 b/app/assets/javascripts/admin/controllers/admin-group.js.es6 index 2256714084..dbe31af85e 100644 --- a/app/assets/javascripts/admin/controllers/admin-group.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-group.js.es6 @@ -1,21 +1,22 @@ import { popupAjaxError } from 'discourse/lib/ajax-error'; +import { propertyEqual } from 'discourse/lib/computed'; -export default Em.ObjectController.extend({ +export default Ember.Controller.extend({ needs: ['adminGroupsType'], disableSave: false, currentPage: function() { - if (this.get("user_count") === 0) { return 0; } - return Math.floor(this.get("offset") / this.get("limit")) + 1; - }.property("limit", "offset", "user_count"), + if (this.get("model.user_count") === 0) { return 0; } + return Math.floor(this.get("model.offset") / this.get("model.limit")) + 1; + }.property("model.limit", "model.offset", "model.user_count"), totalPages: function() { - if (this.get("user_count") === 0) { return 0; } - return Math.floor(this.get("user_count") / this.get("limit")) + 1; - }.property("limit", "user_count"), + if (this.get("model.user_count") === 0) { return 0; } + return Math.floor(this.get("model.user_count") / this.get("model.limit")) + 1; + }.property("model.limit", "model.user_count"), showingFirst: Em.computed.lte("currentPage", 1), - showingLast: Discourse.computed.propertyEqual("currentPage", "totalPages"), + showingLast: propertyEqual("currentPage", "totalPages"), aliasLevelOptions: function() { return [ @@ -26,6 +27,13 @@ export default Em.ObjectController.extend({ ]; }.property(), + trustLevelOptions: function() { + return [ + { name: I18n.t("groups.trust_levels.none"), value: 0 }, + { name: 1, value: 1 }, { name: 2, value: 2 }, { name: 3, value: 3 }, { name: 4, value: 4 } + ]; + }.property(), + actions: { next() { if (this.get("showingLast")) { return; } @@ -51,7 +59,7 @@ export default Em.ObjectController.extend({ removeMember(member) { const self = this, - message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("name") }); + message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("model.name") }); return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), function(confirm) { if (confirm) { self.get("model").removeMember(member); @@ -60,21 +68,21 @@ export default Em.ObjectController.extend({ }, addMembers() { - if (Em.isEmpty(this.get("usernames"))) { return; } - this.get("model").addMembers(this.get("usernames")); - // clear the user selector - this.set("usernames", null); + if (Em.isEmpty(this.get("model.usernames"))) { return; } + this.get("model").addMembers(this.get("model.usernames")).catch(popupAjaxError); + this.set("model.usernames", null); }, save() { const group = this.get('model'), - groupsController = this.get("controllers.adminGroupsType"); + groupsController = this.get("controllers.adminGroupsType"), + groupType = groupsController.get("type"); this.set('disableSave', true); let promise = group.get("id") ? group.save() : group.create().then(() => groupsController.addObject(group)); - promise.then(() => this.transitionToRoute("adminGroup", group)) + promise.then(() => this.transitionToRoute("adminGroup", groupType, group.get('name'))) .catch(popupAjaxError) .finally(() => this.set('disableSave', false)); }, @@ -84,6 +92,11 @@ export default Em.ObjectController.extend({ groupsController = this.get('controllers.adminGroupsType'), self = this; + if (!group.get('id')) { + self.transitionToRoute('adminGroupsType.index', 'custom'); + return; + } + this.set('disableSave', true); bootbox.confirm( diff --git a/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 b/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 index 81486978a4..199296d823 100644 --- a/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-log-screened-ip-address.js.es6 @@ -1,4 +1,4 @@ -export default Ember.ObjectController.extend({ +export default Ember.Controller.extend({ editing: false, savedIpAddress: null, diff --git a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 index 0ae12ff2c5..987b07a3b6 100644 --- a/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-logs-screened-ip-addresses.js.es6 @@ -1,3 +1,4 @@ +import debounce from 'discourse/lib/debounce'; import { outputExportResult } from 'discourse/lib/export-result'; import { exportEntity } from 'discourse/lib/export-csv'; @@ -6,7 +7,7 @@ export default Ember.ArrayController.extend({ itemController: 'admin-log-screened-ip-address', filter: null, - show: Discourse.debounce(function() { + show: debounce(function() { var self = this; self.set('loading', true); Discourse.ScreenedIpAddress.findAll(this.get("filter")).then(function(result) { diff --git a/app/assets/javascripts/admin/controllers/admin-permalinks.js.es6 b/app/assets/javascripts/admin/controllers/admin-permalinks.js.es6 new file mode 100644 index 0000000000..a0d38e7b8c --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-permalinks.js.es6 @@ -0,0 +1,38 @@ +import debounce from 'discourse/lib/debounce'; + +export default Ember.ArrayController.extend({ + loading: false, + filter: null, + + show: debounce(function() { + var self = this; + self.set('loading', true); + Discourse.Permalink.findAll(self.get("filter")).then(function(result) { + self.set('model', result); + self.set('loading', false); + }); + }, 250).observes("filter"), + + actions: { + recordAdded(arg) { + this.get("model").unshiftObject(arg); + }, + + destroy: function(record) { + const self = this; + return bootbox.confirm(I18n.t("admin.permalink.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { + if (result) { + record.destroy().then(function(deleted) { + if (deleted) { + self.removeObject(record); + } else { + bootbox.alert(I18n.t("generic_error")); + } + }, function(){ + bootbox.alert(I18n.t("generic_error")); + }); + } + }); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 b/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 index 52b7cb6d8b..b2eb1335ed 100644 --- a/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-plugins.js.es6 @@ -1,6 +1,10 @@ export default Ember.ArrayController.extend({ adminRoutes: function() { - return this.get('model').map(p => p.admin_route).compact(); + return this.get('model').map(function(p) { + if (p.get('enabled')) { + return p.admin_route; + } + }).compact(); }.property() }); diff --git a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 index 6bb36989b7..d11e67305a 100644 --- a/app/assets/javascripts/admin/controllers/admin-reports.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-reports.js.es6 @@ -1,28 +1,47 @@ -export default Ember.ObjectController.extend({ +import { exportEntity } from 'discourse/lib/export-csv'; +import { outputExportResult } from 'discourse/lib/export-result'; + +export default Ember.Controller.extend({ viewMode: 'table', viewingTable: Em.computed.equal('viewMode', 'table'), viewingBarChart: Em.computed.equal('viewMode', 'barChart'), startDate: null, endDate: null, + categoryId: null, refreshing: false, + categoryOptions: function() { + var arr = [{name: I18n.t('category.all'), value: 'all'}]; + return arr.concat( Discourse.Site.currentProp('sortedCategories').map(function(i) { return {name: i.get('name'), value: i.get('id') }; }) ); + }.property(), + actions: { - refreshReport: function() { - var self = this; - this.set('refreshing', true); - Discourse.Report.find(this.get('type'), this.get('startDate'), this.get('endDate')).then(function(r) { - self.set('model', r); - }).finally(function() { - self.set('refreshing', false); - }); + refreshReport() { + var q; + this.set("refreshing", true); + if (this.get('categoryId') === "all") { + q = Discourse.Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate")); + } else { + q = Discourse.Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId")); + } + q.then(m => this.set("model", m)).finally(() => this.set("refreshing", false)); }, - viewAsTable: function() { + viewAsTable() { this.set('viewMode', 'table'); }, - viewAsBarChart: function() { + viewAsBarChart() { this.set('viewMode', 'barChart'); + }, + + exportCsv() { + exportEntity('report', { + name: this.get("model.type"), + start_date: this.get('startDate'), + end_date: this.get('endDate'), + category_id: this.get('categoryId') === 'all' ? undefined : this.get('categoryId') + }).then(outputExportResult); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 index 37bc9b3765..b16db34e3e 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-settings-category.js.es6 @@ -1,4 +1,4 @@ -export default Ember.ObjectController.extend({ +export default Ember.Controller.extend({ categoryNameKey: null, needs: ['adminSiteSettings'], diff --git a/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 index 9e67e638b5..97b1c9ace9 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-settings.js.es6 @@ -1,14 +1,13 @@ -import Presence from 'discourse/mixins/presence'; +import debounce from 'discourse/lib/debounce'; -export default Ember.ArrayController.extend(Presence, { +export default Ember.ArrayController.extend({ filter: null, onlyOverridden: false, filtered: Ember.computed.notEmpty('filter'), - filterContent: Discourse.debounce(function() { - + filterContentNow: function(category) { // If we have no content, don't bother filtering anything - if (!this.present('allSiteSettings')) return; + if (!!Ember.isEmpty(this.get('allSiteSettings'))) return; let filter; if (this.get('filter')) { @@ -39,11 +38,24 @@ export default Ember.ArrayController.extend(Presence, { }); if (matches.length > 0) { matchesGroupedByCategory[0].siteSettings.pushObjects(matches); + matchesGroupedByCategory.pushObject({ + nameKey: settingsCategory.nameKey, + name: I18n.t('admin.site_settings.categories.' + settingsCategory.nameKey), + siteSettings: matches + }); } }); this.set('model', matchesGroupedByCategory); - this.transitionToRoute("adminSiteSettingsCategory", "all_results"); + this.transitionToRoute("adminSiteSettingsCategory", category || "all_results"); + }, + + filterContent: debounce(function() { + if (this.get("_skipBounce")) { + this.set("_skipBounce", false); + } else { + this.filterContentNow(); + } }, 250).observes('filter', 'onlyOverridden'), actions: { @@ -52,6 +64,10 @@ export default Ember.ArrayController.extend(Presence, { filter: '', onlyOverridden: false }); + }, + + toggleMenu() { + $('.admin-detail').toggleClass('mobile-closed mobile-open'); } } diff --git a/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 index c95db1e8b8..943317b1bf 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-text-edit.js.es6 @@ -1,20 +1,16 @@ -export default Ember.ObjectController.extend({ - saving: false, +export default Ember.Controller.extend({ saved: false, saveDisabled: function() { - if (this.get('saving')) { return true; } - if ((!this.get('allow_blank')) && Ember.isEmpty(this.get('value'))) { return true; } + if (this.get('model.isSaving')) { return true; } + if ((!this.get('allow_blank')) && Ember.isEmpty(this.get('model.value'))) { return true; } return false; - }.property('saving', 'value'), + }.property('model.iSaving', 'model.value'), actions: { - saveChanges: function() { - var self = this; - self.setProperties({saving: true, saved: false}); - self.get('model').save().then(function () { - self.setProperties({saving: false, saved: true}); - }); + saveChanges() { + const model = this.get('model'); + model.save(model.getProperties('value')).then(() => this.set('saved', true)); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 index 5e6bb325fd..b85fbaf069 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-badges.js.es6 @@ -1,25 +1,18 @@ -/** - This controller supports the interface for granting and revoking badges from - individual users. +import UserBadge from 'discourse/models/user-badge'; - @class AdminUserBadgesController - @extends Ember.ArrayController - @namespace Discourse - @module Discourse -**/ export default Ember.ArrayController.extend({ needs: ["adminUser"], - user: Em.computed.alias('controllers.adminUser'), + user: Em.computed.alias('controllers.adminUser.model'), sortProperties: ['granted_at'], sortAscending: false, groupedBadges: function(){ - const badges = this.get('model'); + const allBadges = this.get('model'); - var grouped = _.groupBy(badges, badge => badge.badge_id); + var grouped = _.groupBy(allBadges, badge => badge.badge_id); var expanded = []; - const expandedBadges = badges.get('expandedBadges'); + const expandedBadges = allBadges.get('expandedBadges'); _(grouped).each(function(badges){ var lastGranted = badges[0].granted_at; @@ -95,7 +88,7 @@ export default Ember.ArrayController.extend({ **/ grantBadge: function(badgeId) { var self = this; - Discourse.UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) { + UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) { self.set('badgeReason', ''); self.pushObject(userBadge); Ember.run.next(function() { @@ -111,12 +104,6 @@ export default Ember.ArrayController.extend({ }); }, - /** - Revoke the selected userBadge. - - @method revokeBadge - @param {Discourse.UserBadge} userBadge the `Discourse.UserBadge` instance that needs to be revoked. - **/ revokeBadge: function(userBadge) { var self = this; return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) { diff --git a/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 deleted file mode 100644 index f4b9ba2a79..0000000000 --- a/app/assets/javascripts/admin/controllers/admin-user-field-item.js.es6 +++ /dev/null @@ -1,64 +0,0 @@ -import UserField from 'admin/models/user-field'; -import BufferedContent from 'discourse/mixins/buffered-content'; - -export default Ember.ObjectController.extend(BufferedContent, { - needs: ['admin-user-fields'], - editing: Ember.computed.empty('id'), - - fieldName: function() { - return UserField.fieldTypeById(this.get('field_type')).get('name'); - }.property('field_type'), - - flags: function() { - var ret = []; - if (this.get('editable')) { - ret.push(I18n.t('admin.user_fields.editable.enabled')); - } - if (this.get('required')) { - ret.push(I18n.t('admin.user_fields.required.enabled')); - } - if (this.get('show_on_profile')) { - ret.push(I18n.t('admin.user_fields.show_on_profile.enabled')); - } - - return ret.join(', '); - }.property('editable', 'required', 'show_on_profile'), - - actions: { - save: function() { - var self = this; - - var attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable', 'required', 'show_on_profile'); - - this.get('model').save(attrs).then(function(res) { - self.set('model.id', res.user_field.id); - self.set('editing', false); - self.commitBuffer(); - }).catch(function(e) { - var 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); - }); - }, - - edit: function() { - this.set('editing', true); - }, - - destroy: function() { - this.get('controllers.admin-user-fields').send('destroy', this.get('model')); - }, - - cancel: function() { - var id = this.get('id'); - if (Ember.isEmpty(id)) { - this.get('controllers.admin-user-fields').send('destroy', this.get('model')); - } else { - this.rollbackBuffer(); - this.set('editing', false); - } - } - } -}); diff --git a/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 index 0eb041c55b..70963a147e 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-fields.js.es6 @@ -1,39 +1,60 @@ -import UserField from 'admin/models/user-field'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; -export default Ember.ArrayController.extend({ +const MAX_FIELDS = 20; + +export default Ember.Controller.extend({ fieldTypes: null, - createDisabled: Em.computed.gte('model.length', 20), + createDisabled: Em.computed.gte('model.length', MAX_FIELDS), - userFieldsDescription: function() { - return I18n.t('admin.user_fields.description'); - }.property(), - - userFieldsName: function() { - return I18n.t('admin.user_fields.name'); - }.property(), - - _performDestroy: function(f, model) { - return f.destroy().then(function() { - model.removeObject(f); + arrangedContent: function() { + return Ember.ArrayProxy.extend(Ember.SortableMixin).create({ + sortProperties: ['position'], + content: this.get('model') }); - }, + }.property('model'), actions: { - createField: function() { - this.pushObject(UserField.create({ field_type: 'text' })); + createField() { + const f = this.store.createRecord('user-field', { field_type: 'text', position: MAX_FIELDS }); + this.get('model').pushObject(f); }, - destroy: function(f) { - var model = this.get('model'), - self = this; + moveUp(f) { + const idx = this.get('arrangedContent').indexOf(f); + if (idx) { + const prev = this.get('arrangedContent').objectAt(idx-1); + const prevPos = prev.get('position'); + + prev.update({ position: f.get('position') }); + f.update({ position: prevPos }); + } + }, + + moveDown(f) { + const idx = this.get('arrangedContent').indexOf(f); + if (idx > -1) { + const next = this.get('arrangedContent').objectAt(idx+1); + const nextPos = next.get('position'); + + next.update({ position: f.get('position') }); + f.update({ position: nextPos }); + } + }, + + destroy(f) { + const model = this.get('model'); // Only confirm if we already been saved if (f.get('id')) { bootbox.confirm(I18n.t("admin.user_fields.delete_confirm"), function(result) { - if (result) { self._performDestroy(f, model); } + if (result) { + f.destroyRecord().then(function() { + model.removeObject(f); + }).catch(popupAjaxError); + } }); } else { - self._performDestroy(f, model); + model.removeObject(f); } } } diff --git a/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 index b544aafc63..fdea272ab1 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user-index.js.es6 @@ -1,15 +1,15 @@ -import ObjectController from 'discourse/controllers/object'; import CanCheckEmails from 'discourse/mixins/can-check-emails'; +import { propertyNotEqual, setting } from 'discourse/lib/computed'; -export default ObjectController.extend(CanCheckEmails, { +export default Ember.Controller.extend(CanCheckEmails, { editingTitle: false, originalPrimaryGroupId: null, availableGroups: null, - showApproval: Discourse.computed.setting('must_approve_users'), - showBadges: Discourse.computed.setting('enable_badges'), + showApproval: setting('must_approve_users'), + showBadges: setting('enable_badges'), - primaryGroupDirty: Discourse.computed.propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'), + primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'), automaticGroups: function() { return this.get("model.automaticGroups").map((g) => g.name).join(", "); @@ -36,8 +36,8 @@ export default ObjectController.extend(CanCheckEmails, { saveTitle() { const self = this; - return Discourse.ajax("/users/" + this.get('username').toLowerCase(), { - data: {title: this.get('title')}, + return Discourse.ajax("/users/" + this.get('model.username').toLowerCase(), { + data: {title: this.get('model.title')}, type: 'PUT' }).catch(function(e) { bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body})); @@ -65,7 +65,7 @@ export default ObjectController.extend(CanCheckEmails, { savePrimaryGroup() { const self = this; - return Discourse.ajax("/admin/users/" + this.get('id') + "/primary_group", { + return Discourse.ajax("/admin/users/" + this.get('model.id') + "/primary_group", { type: 'PUT', data: {primary_group_id: this.get('model.primary_group_id')} }).then(function () { diff --git a/app/assets/javascripts/admin/controllers/admin-user.js.es6 b/app/assets/javascripts/admin/controllers/admin-user.js.es6 index 33f6459f8e..77c79b724a 100644 --- a/app/assets/javascripts/admin/controllers/admin-user.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-user.js.es6 @@ -1,3 +1 @@ -import ObjectController from 'discourse/controllers/object'; - -export default ObjectController.extend(); +export default Ember.Controller.extend(); diff --git a/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 index 708207f84a..a0eaf6e162 100644 --- a/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-users-list-show.js.es6 @@ -1,3 +1,6 @@ +import debounce from 'discourse/lib/debounce'; +import { i18n } from 'discourse/lib/computed'; + export default Ember.ArrayController.extend({ query: null, showEmails: false, @@ -9,7 +12,7 @@ export default Ember.ArrayController.extend({ queryPending: Em.computed.equal('query', 'pending'), queryHasApproval: Em.computed.or('queryNew', 'queryPending'), showApproval: Em.computed.and('siteSettings.must_approve_users', 'queryHasApproval'), - searchHint: Discourse.computed.i18n('search_hint'), + searchHint: i18n('search_hint'), hasSelection: Em.computed.gt('selectedCount', 0), selectedCount: function() { @@ -31,7 +34,7 @@ export default Ember.ArrayController.extend({ return I18n.t('admin.users.titles.' + this.get('query')); }.property('query'), - _filterUsers: Discourse.debounce(function() { + _filterUsers: debounce(function() { this._refreshUsers(); }, 250).observes('listFilter'), diff --git a/app/assets/javascripts/admin/controllers/admin.js.es6 b/app/assets/javascripts/admin/controllers/admin.js.es6 index ae2b81f087..3bdfbe6b50 100644 --- a/app/assets/javascripts/admin/controllers/admin.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin.js.es6 @@ -1,6 +1,4 @@ -import DiscourseController from 'discourse/controllers/controller'; - -export default DiscourseController.extend({ +export default Ember.Controller.extend({ showBadges: function() { return this.get('currentUser.admin') && this.siteSettings.enable_badges; }.property() diff --git a/app/assets/javascripts/admin/controllers/modals/admin-agree-flag.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-agree-flag.js.es6 index 635b8c87fe..2924eb6699 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-agree-flag.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-agree-flag.js.es6 @@ -1,7 +1,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; -export default ObjectController.extend(ModalFunctionality, { +export default Ember.Controller.extend(ModalFunctionality, { needs: ["admin-flags-list"], _agreeFlag: function (actionOnPost) { diff --git a/app/assets/javascripts/admin/controllers/modals/admin-delete-flag.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-delete-flag.js.es6 index de26828eca..284b8bfd09 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-delete-flag.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-delete-flag.js.es6 @@ -1,8 +1,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; - -export default ObjectController.extend(ModalFunctionality, { +export default Ember.Controller.extend(ModalFunctionality, { needs: ["admin-flags-list"], actions: { diff --git a/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 index 2034d10368..74c4f79e6c 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6 @@ -56,8 +56,8 @@ export default Ember.Controller.extend({ saveAll: function(){ var self = this; var items = this.get('workingCopy'); - var groupIds = items.map(function(i){return i.get("id") || -1}); - var names = items.map(function(i){return i.get("name")}); + var groupIds = items.map(function(i){return i.get("id") || -1;}); + var names = items.map(function(i){return i.get("name");}); Discourse.ajax('/admin/badges/badge_groupings',{ data: {ids: groupIds, names: names}, diff --git a/app/assets/javascripts/admin/controllers/modals/admin-staff-action-log-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-staff-action-log-details.js.es6 index 9d525e9827..1e2ae5adca 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-staff-action-log-details.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-staff-action-log-details.js.es6 @@ -1,5 +1,3 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; - -export default ObjectController.extend(ModalFunctionality); +export default Ember.Controller.extend(ModalFunctionality); diff --git a/app/assets/javascripts/admin/controllers/modals/admin-start-backup.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-start-backup.js.es6 index 1af962e41c..682b1ba25d 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-start-backup.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-start-backup.js.es6 @@ -1,7 +1,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import Controller from 'discourse/controllers/controller'; -export default Controller.extend(ModalFunctionality, { +export default Ember.Controller.extend(ModalFunctionality, { needs: ["adminBackupsLogs"], _startBackup: function (withUploads) { diff --git a/app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6 index 49999aa092..d3e19de569 100644 --- a/app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6 @@ -1,7 +1,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; -export default ObjectController.extend(ModalFunctionality, { +export default Ember.Controller.extend(ModalFunctionality, { submitDisabled: function() { return (!this.get('reason') || this.get('reason').length < 1); diff --git a/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 index 1ef3e217d5..ca6ac31db1 100644 --- a/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 +++ b/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6 @@ -1,7 +1,6 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; -import ObjectController from 'discourse/controllers/object'; -export default ObjectController.extend(ModalFunctionality, { +export default Ember.Controller.extend(ModalFunctionality, { previousSelected: Ember.computed.equal('selectedTab', 'previous'), newSelected: Ember.computed.equal('selectedTab', 'new'), diff --git a/app/assets/javascripts/admin/models/admin-user.js.es6 b/app/assets/javascripts/admin/models/admin-user.js.es6 index 8416f53585..7cda05c820 100644 --- a/app/assets/javascripts/admin/models/admin-user.js.es6 +++ b/app/assets/javascripts/admin/models/admin-user.js.es6 @@ -1,3 +1,4 @@ +import { propertyNotEqual } from 'discourse/lib/computed'; import { popupAjaxError } from 'discourse/lib/ajax-error'; const AdminUser = Discourse.User.extend({ @@ -144,7 +145,7 @@ const AdminUser = Discourse.User.extend({ this.set('originalTrustLevel', this.get('trust_level')); }, - dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'), + dirty: propertyNotEqual('originalTrustLevel', 'trustLevel.id'), saveTrustLevel() { return Discourse.ajax("/admin/users/" + this.id + "/trust_level", { diff --git a/app/assets/javascripts/admin/models/backup-status.js.es6 b/app/assets/javascripts/admin/models/backup-status.js.es6 new file mode 100644 index 0000000000..2de301f955 --- /dev/null +++ b/app/assets/javascripts/admin/models/backup-status.js.es6 @@ -0,0 +1,12 @@ +import computed from "ember-addons/ember-computed-decorators"; + +export default Discourse.Model.extend({ + + restoreDisabled: Em.computed.not("restoreEnabled"), + + @computed("allowRestore", "isOperationRunning") + restoreEnabled(allowRestore, isOperationRunning) { + return allowRestore && !isOperationRunning; + } + +}); diff --git a/app/assets/javascripts/admin/models/backup.js b/app/assets/javascripts/admin/models/backup.js deleted file mode 100644 index 8c11c9fe33..0000000000 --- a/app/assets/javascripts/admin/models/backup.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - Data model for representing a backup - - @class Backup - @extends Discourse.Model - @namespace Discourse - @module Discourse -**/ -Discourse.Backup = Discourse.Model.extend({ - - /** - Destroys the current backup - - @method destroy - @returns {Promise} a promise that resolves when the backup has been destroyed - **/ - destroy: function() { - return Discourse.ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" }); - }, - - /** - Starts the restoration of the current backup - - @method restore - @returns {Promise} a promise that resolves when the backup has started being restored - **/ - restore: function() { - return Discourse.ajax("/admin/backups/" + this.get("filename") + "/restore", { type: "POST" }); - } - -}); - -Discourse.Backup.reopenClass({ - - /** - Finds a list of backups - - @method find - @returns {Promise} a promise that resolves to the array of {Discourse.Backup} backup - **/ - find: function() { - return PreloadStore.getAndRemove("backups", function() { - return Discourse.ajax("/admin/backups.json"); - }).then(function(backups) { - return backups.map(function (backup) { return Discourse.Backup.create(backup); }); - }); - }, - - /** - Starts a backup - - @method start - @returns {Promise} a promise that resolves when the backup has started - **/ - start: function (withUploads) { - if (withUploads === undefined) { withUploads = true; } - return Discourse.ajax("/admin/backups", { type: "POST", data: { with_uploads: withUploads } }).then(function(result) { - if (!result.success) { bootbox.alert(result.message); } - }); - }, - - /** - Cancels a backup - - @method cancel - @returns {Promise} a promise that resolves when the backup has been cancelled - **/ - cancel: function() { - return Discourse.ajax("/admin/backups/cancel.json").then(function(result) { - if (!result.success) { bootbox.alert(result.message); } - }); - }, - - /** - Rollbacks the database to the previous working state - - @method rollback - @returns {Promise} a promise that resolves when the rollback is done - **/ - rollback: function() { - return Discourse.ajax("/admin/backups/rollback.json").then(function(result) { - if (!result.success) { - bootbox.alert(result.message); - } else { - // redirect to homepage (session might be lost) - window.location.pathname = Discourse.getURL("/"); - } - }); - } -}); diff --git a/app/assets/javascripts/admin/models/backup.js.es6 b/app/assets/javascripts/admin/models/backup.js.es6 new file mode 100644 index 0000000000..8b4991b728 --- /dev/null +++ b/app/assets/javascripts/admin/models/backup.js.es6 @@ -0,0 +1,56 @@ +const Backup = Discourse.Model.extend({ + + destroy() { + return Discourse.ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" }); + }, + + restore() { + return Discourse.ajax("/admin/backups/" + this.get("filename") + "/restore", { + type: "POST", + data: { client_id: window.MessageBus.clientId } + }); + } + +}); + +Backup.reopenClass({ + + find() { + return PreloadStore.getAndRemove("backups", () => Discourse.ajax("/admin/backups.json")) + .then(backups => backups.map(backup => Backup.create(backup))); + }, + + start(withUploads) { + if (withUploads === undefined) { withUploads = true; } + return Discourse.ajax("/admin/backups", { + type: "POST", + data: { + with_uploads: withUploads, + client_id: window.MessageBus.clientId + } + }).then(result => { + if (!result.success) { bootbox.alert(result.message); } + }); + }, + + cancel() { + return Discourse.ajax("/admin/backups/cancel.json") + .then(result => { + if (!result.success) { bootbox.alert(result.message); } + }); + }, + + rollback() { + return Discourse.ajax("/admin/backups/rollback.json") + .then(result => { + if (!result.success) { + bootbox.alert(result.message); + } else { + // redirect to homepage (session might be lost) + window.location.pathname = Discourse.getURL("/"); + } + }); + } +}); + +export default Backup; diff --git a/app/assets/javascripts/admin/models/backup_status.js b/app/assets/javascripts/admin/models/backup_status.js deleted file mode 100644 index e67a34bc3f..0000000000 --- a/app/assets/javascripts/admin/models/backup_status.js +++ /dev/null @@ -1,9 +0,0 @@ -Discourse.BackupStatus = Discourse.Model.extend({ - - restoreDisabled: Em.computed.not("restoreEnabled"), - - restoreEnabled: function() { - return this.get('allowRestore') && !this.get("isOperationRunning"); - }.property("isOperationRunning", "allowRestore") - -}); diff --git a/app/assets/javascripts/admin/models/flagged_post.js b/app/assets/javascripts/admin/models/flagged_post.js index 0696067806..e3a01948ca 100644 --- a/app/assets/javascripts/admin/models/flagged_post.js +++ b/app/assets/javascripts/admin/models/flagged_post.js @@ -47,7 +47,7 @@ Discourse.FlaggedPost = Discourse.Post.extend({ }, wasEdited: function () { - if (this.blank("last_revised_at")) { return false; } + if (Ember.isEmpty(this.get("last_revised_at"))) { return false; } var lastRevisedAt = Date.parse(this.get("last_revised_at")); return _.some(this.get("post_actions"), function (postAction) { return Date.parse(postAction.created_at) < lastRevisedAt; diff --git a/app/assets/javascripts/admin/models/permalink.js.es6 b/app/assets/javascripts/admin/models/permalink.js.es6 new file mode 100644 index 0000000000..761cd4ab51 --- /dev/null +++ b/app/assets/javascripts/admin/models/permalink.js.es6 @@ -0,0 +1,22 @@ +const Permalink = Discourse.Model.extend({ + save: function() { + return Discourse.ajax("/admin/permalinks.json", { + type: 'POST', + data: {url: this.get('url'), permalink_type: this.get('permalink_type'), permalink_type_value: this.get('permalink_type_value')} + }); + }, + + destroy: function() { + return Discourse.ajax("/admin/permalinks/" + this.get('id') + ".json", {type: 'DELETE'}); + } +}); + +Permalink.reopenClass({ + findAll: function(filter) { + return Discourse.ajax("/admin/permalinks.json", { data: { filter: filter } }).then(function(permalinks) { + return permalinks.map(p => Discourse.Permalink.create(p)); + }); + } +}); + +export default Permalink; diff --git a/app/assets/javascripts/admin/models/report.js b/app/assets/javascripts/admin/models/report.js deleted file mode 100644 index 6f8bfdfff3..0000000000 --- a/app/assets/javascripts/admin/models/report.js +++ /dev/null @@ -1,164 +0,0 @@ -Discourse.Report = Discourse.Model.extend({ - reportUrl: function() { - return("/admin/reports/" + this.get('type')); - }.property('type'), - - valueAt: function(numDaysAgo) { - if (this.data) { - var wantedDate = moment().subtract(numDaysAgo, 'days').format('YYYY-MM-DD'); - var item = this.data.find( function(d) { return d.x === wantedDate; } ); - if (item) { - return item.y; - } - } - return 0; - }, - - sumDays: function(startDaysAgo, endDaysAgo) { - if (this.data) { - var earliestDate = moment().subtract(endDaysAgo, 'days').startOf('day'); - var latestDate = moment().subtract(startDaysAgo, 'days').startOf('day'); - var d, sum = 0; - _.each(this.data,function(datum){ - d = moment(datum.x); - if(d >= earliestDate && d <= latestDate) { - sum += datum.y; - } - }); - return sum; - } - }, - - todayCount: function() { - return this.valueAt(0); - }.property('data'), - - yesterdayCount: function() { - return this.valueAt(1); - }.property('data'), - - lastSevenDaysCount: function() { - return this.sumDays(1,7); - }.property('data'), - - lastThirtyDaysCount: function() { - return this.sumDays(1,30); - }.property('data'), - - sevenDaysAgoCount: function() { - return this.valueAt(7); - }.property('data'), - - thirtyDaysAgoCount: function() { - return this.valueAt(30); - }.property('data'), - - yesterdayTrend: function() { - var yesterdayVal = this.valueAt(1); - var twoDaysAgoVal = this.valueAt(2); - if ( yesterdayVal > twoDaysAgoVal ) { - return 'trending-up'; - } else if ( yesterdayVal < twoDaysAgoVal ) { - return 'trending-down'; - } else { - return 'no-change'; - } - }.property('data'), - - sevenDayTrend: function() { - var currentPeriod = this.sumDays(1,7); - var prevPeriod = this.sumDays(8,14); - if ( currentPeriod > prevPeriod ) { - return 'trending-up'; - } else if ( currentPeriod < prevPeriod ) { - return 'trending-down'; - } else { - return 'no-change'; - } - }.property('data'), - - thirtyDayTrend: function() { - if( this.get('prev30Days') ) { - var currentPeriod = this.sumDays(1,30); - if( currentPeriod > this.get('prev30Days') ) { - return 'trending-up'; - } else if ( currentPeriod < this.get('prev30Days') ) { - return 'trending-down'; - } - } - return 'no-change'; - }.property('data', 'prev30Days'), - - icon: function() { - switch( this.get('type') ) { - case 'flags': - return 'flag'; - case 'likes': - return 'heart'; - default: - return null; - } - }.property('type'), - - percentChangeString: function(val1, val2) { - var val = ((val1 - val2) / val2) * 100; - if( isNaN(val) || !isFinite(val) ) { - return null; - } else if( val > 0 ) { - return '+' + val.toFixed(0) + '%'; - } else { - return val.toFixed(0) + '%'; - } - }, - - changeTitle: function(val1, val2, prevPeriodString) { - var title = ''; - var percentChange = this.percentChangeString(val1, val2); - if( percentChange ) { - title += percentChange + ' change. '; - } - title += 'Was ' + val2 + ' ' + prevPeriodString + '.'; - return title; - }, - - yesterdayCountTitle: function() { - return this.changeTitle( this.valueAt(1), this.valueAt(2),'two days ago'); - }.property('data'), - - sevenDayCountTitle: function() { - return this.changeTitle( this.sumDays(1,7), this.sumDays(8,14), 'two weeks ago'); - }.property('data'), - - thirtyDayCountTitle: function() { - return this.changeTitle( this.sumDays(1,30), this.get('prev30Days'), 'in the previous 30 day period'); - }.property('data'), - - dataReversed: function() { - return this.get('data').toArray().reverse(); - }.property('data') - -}); - -Discourse.Report.reopenClass({ - find: function(type, startDate, endDate) { - - return Discourse.ajax("/admin/reports/" + type, {data: { - start_date: startDate, - end_date: endDate - }}).then(function (json) { - // Add a percent field to each tuple - var maxY = 0; - json.report.data.forEach(function (row) { - if (row.y > maxY) maxY = row.y; - }); - if (maxY > 0) { - json.report.data.forEach(function (row) { - row.percentage = Math.round((row.y / maxY) * 100); - }); - } - var model = Discourse.Report.create({type: type}); - model.setProperties(json.report); - return model; - }); - } -}); diff --git a/app/assets/javascripts/admin/models/report.js.es6 b/app/assets/javascripts/admin/models/report.js.es6 new file mode 100644 index 0000000000..a28601d0ce --- /dev/null +++ b/app/assets/javascripts/admin/models/report.js.es6 @@ -0,0 +1,157 @@ +import round from "discourse/lib/round"; +import { fmt } from 'discourse/lib/computed'; + +const Report = Discourse.Model.extend({ + reportUrl: fmt("type", "/admin/reports/%@"), + + valueAt(numDaysAgo) { + if (this.data) { + const wantedDate = moment().subtract(numDaysAgo, "days").format("YYYY-MM-DD"); + const item = this.data.find(d => d.x === wantedDate); + if (item) { + return item.y; + } + } + return 0; + }, + + valueFor(startDaysAgo, endDaysAgo) { + if (this.data) { + const earliestDate = moment().subtract(endDaysAgo, "days").startOf("day"); + const latestDate = moment().subtract(startDaysAgo, "days").startOf("day"); + var d, sum = 0, count = 0; + _.each(this.data, datum => { + d = moment(datum.x); + if (d >= earliestDate && d <= latestDate) { + sum += datum.y; + count++; + } + }); + if (this.get("method") === "average" && count > 0) { sum /= count; } + return round(sum, -2); + } + }, + + todayCount: function() { return this.valueAt(0); }.property("data"), + yesterdayCount: function() { return this.valueAt(1); }.property("data"), + sevenDaysAgoCount: function() { return this.valueAt(7); }.property("data"), + thirtyDaysAgoCount: function() { return this.valueAt(30); }.property("data"), + + lastSevenDaysCount: function() { return this.valueFor(1, 7); }.property("data"), + lastThirtyDaysCount: function() { return this.valueFor(1, 30); }.property("data"), + + yesterdayTrend: function() { + const yesterdayVal = this.valueAt(1); + const twoDaysAgoVal = this.valueAt(2); + if (yesterdayVal > twoDaysAgoVal) { + return "trending-up"; + } else if (yesterdayVal < twoDaysAgoVal) { + return "trending-down"; + } else { + return "no-change"; + } + }.property("data"), + + sevenDayTrend: function() { + const currentPeriod = this.valueFor(1, 7); + const prevPeriod = this.valueFor(8, 14); + if (currentPeriod > prevPeriod) { + return "trending-up"; + } else if (currentPeriod < prevPeriod) { + return "trending-down"; + } else { + return "no-change"; + } + }.property("data"), + + thirtyDayTrend: function() { + if (this.get("prev30Days")) { + const currentPeriod = this.valueFor(1, 30); + if (currentPeriod > this.get("prev30Days")) { + return "trending-up"; + } else if (currentPeriod < this.get("prev30Days")) { + return "trending-down"; + } + } + return "no-change"; + }.property("data", "prev30Days"), + + icon: function() { + switch (this.get("type")) { + case "flags": return "flag"; + case "likes": return "heart"; + default: return null; + } + }.property("type"), + + method: function() { + if (this.get("type") === "time_to_first_response") { + return "average"; + } else { + return "sum"; + } + }.property("type"), + + percentChangeString(val1, val2) { + const val = ((val1 - val2) / val2) * 100; + if (isNaN(val) || !isFinite(val)) { + return null; + } else if (val > 0) { + return "+" + val.toFixed(0) + "%"; + } else { + return val.toFixed(0) + "%"; + } + }, + + changeTitle(val1, val2, prevPeriodString) { + const percentChange = this.percentChangeString(val1, val2); + var title = ""; + if (percentChange) { title += percentChange + " change. "; } + title += "Was " + val2 + " " + prevPeriodString + "."; + return title; + }, + + yesterdayCountTitle: function() { + return this.changeTitle(this.valueAt(1), this.valueAt(2), "two days ago"); + }.property("data"), + + sevenDayCountTitle: function() { + return this.changeTitle(this.valueFor(1, 7), this.valueFor(8, 14), "two weeks ago"); + }.property("data"), + + thirtyDayCountTitle: function() { + return this.changeTitle(this.valueFor(1, 30), this.get("prev30Days"), "in the previous 30 day period"); + }.property("data"), + + dataReversed: function() { + return this.get("data").toArray().reverse(); + }.property("data") + +}); + +Report.reopenClass({ + + find(type, startDate, endDate, categoryId) { + return Discourse.ajax("/admin/reports/" + type, { + data: { + start_date: startDate, + end_date: endDate, + category_id: categoryId + } + }).then(json => { + // Add a percent field to each tuple + let maxY = 0; + json.report.data.forEach(row => { + if (row.y > maxY) maxY = row.y; + }); + if (maxY > 0) { + json.report.data.forEach(row => row.percentage = Math.round((row.y / maxY) * 100)); + } + const model = Discourse.Report.create({ type: type }); + model.setProperties(json.report); + return model; + }); + } +}); + +export default Report; diff --git a/app/assets/javascripts/admin/models/site-customization.js.es6 b/app/assets/javascripts/admin/models/site-customization.js.es6 new file mode 100644 index 0000000000..fe2176bf11 --- /dev/null +++ b/app/assets/javascripts/admin/models/site-customization.js.es6 @@ -0,0 +1,31 @@ +import RestModel from 'discourse/models/rest'; + +const trackedProperties = [ + 'enabled', 'name', 'stylesheet', 'header', 'top', 'footer', 'mobile_stylesheet', + 'mobile_header', 'mobile_top', 'mobile_footer', 'head_tag', 'body_tag', 'embedded_css' +]; + +function changed() { + const originals = this.get('originals'); + if (!originals) { return false; } + return _.some(trackedProperties, (p) => originals[p] !== this.get(p)); +} + +const SiteCustomization = RestModel.extend({ + description: function() { + return "" + this.name + (this.enabled ? ' (*)' : ''); + }.property('selected', 'name', 'enabled'), + + changed: changed.property.apply(changed, trackedProperties.concat('originals')), + + startTrackingChanges: function() { + this.set('originals', this.getProperties(trackedProperties)); + }.on('init'), + + saveChanges() { + return this.save(this.getProperties(trackedProperties)).then(() => this.startTrackingChanges()); + }, + +}); + +export default SiteCustomization; diff --git a/app/assets/javascripts/admin/models/site-text-type.js.es6 b/app/assets/javascripts/admin/models/site-text-type.js.es6 new file mode 100644 index 0000000000..7cb1171e90 --- /dev/null +++ b/app/assets/javascripts/admin/models/site-text-type.js.es6 @@ -0,0 +1,2 @@ +import RestModel from 'discourse/models/rest'; +export default RestModel.extend(); diff --git a/app/assets/javascripts/admin/models/site-text.js.es6 b/app/assets/javascripts/admin/models/site-text.js.es6 new file mode 100644 index 0000000000..edbaf2a444 --- /dev/null +++ b/app/assets/javascripts/admin/models/site-text.js.es6 @@ -0,0 +1,8 @@ +import RestModel from 'discourse/models/rest'; + +export default RestModel.extend({ + markdown: Em.computed.equal('format', 'markdown'), + plainText: Em.computed.equal('format', 'plain'), + html: Em.computed.equal('format', 'html'), + css: Em.computed.equal('format', 'css'), +}); diff --git a/app/assets/javascripts/admin/models/site_customization.js b/app/assets/javascripts/admin/models/site_customization.js deleted file mode 100644 index 27b6ddbdd9..0000000000 --- a/app/assets/javascripts/admin/models/site_customization.js +++ /dev/null @@ -1,111 +0,0 @@ -/** - Our data model for interacting with site customizations. - - @class SiteCustomization - @extends Discourse.Model - @namespace Discourse - @module Discourse -**/ -Discourse.SiteCustomization = Discourse.Model.extend({ - trackedProperties: [ - 'enabled', 'name', - 'stylesheet', 'header', 'top', 'footer', - 'mobile_stylesheet', 'mobile_header', 'mobile_top', 'mobile_footer', - 'head_tag', 'body_tag' - ], - - description: function() { - return "" + this.name + (this.enabled ? ' (*)' : ''); - }.property('selected', 'name', 'enabled'), - - changed: function() { - var self = this; - - if (!this.originals) { return false; } - - var changed = _.some(this.trackedProperties, function (p) { - return self.originals[p] !== self.get(p); - }); - - if (changed) { this.set('savingStatus', ''); } - - return changed; - }.property('enabled', 'name', 'originals', - 'stylesheet', 'header', 'top', 'footer', - 'mobile_stylesheet', 'mobile_header', 'mobile_top', 'mobile_footer', - 'head_tag', 'body_tag'), - - startTrackingChanges: function() { - var self = this; - var originals = {}; - _.each(this.trackedProperties, function (prop) { - originals[prop] = self.get(prop); - }); - this.set('originals', originals); - }.on('init'), - - previewUrl: function() { return "/?preview-style=" + this.get('key'); }.property('key'), - disableSave: function() { return !this.get('changed') || this.get('saving'); }.property('changed'), - - save: function() { - this.set('savingStatus', I18n.t('saving')); - this.set('saving',true); - - var data = { - name: this.name, - enabled: this.enabled, - stylesheet: this.stylesheet, - header: this.header, - top: this.top, - footer: this.footer, - mobile_stylesheet: this.mobile_stylesheet, - mobile_header: this.mobile_header, - mobile_top: this.mobile_top, - mobile_footer: this.mobile_footer, - head_tag: this.head_tag, - body_tag: this.body_tag - }; - - var siteCustomization = this; - return Discourse.ajax("/admin/site_customizations" + (this.id ? '/' + this.id : ''), { - data: { site_customization: data }, - type: this.id ? 'PUT' : 'POST' - }).then(function (result) { - if (!siteCustomization.id) { - siteCustomization.set('id', result.id); - siteCustomization.set('key', result.key); - } - siteCustomization.set('savingStatus', I18n.t('saved')); - siteCustomization.set('saving',false); - siteCustomization.startTrackingChanges(); - }); - }, - - destroy: function() { - if (!this.id) return; - return Discourse.ajax("/admin/site_customizations/" + this.id, { type: 'DELETE' }); - } -}); - -var SiteCustomizations = Ember.ArrayProxy.extend({ - selectedItemChanged: function() { - var selected = this.get('selectedItem'); - _.each(this.get('content'), function (i) { - i.set('selected', selected === i); - }); - }.observes('selectedItem') -}); - -Discourse.SiteCustomization.reopenClass({ - findAll: function() { - return Discourse.ajax("/admin/site_customizations").then(function (data) { - var content = []; - if (data) { - content = data.site_customizations.map(function(c) { - return Discourse.SiteCustomization.create(c); - }); - } - return SiteCustomizations.create({ content: content }); - }); - } -}); diff --git a/app/assets/javascripts/admin/models/site_text.js b/app/assets/javascripts/admin/models/site_text.js deleted file mode 100644 index 0d671c4334..0000000000 --- a/app/assets/javascripts/admin/models/site_text.js +++ /dev/null @@ -1,21 +0,0 @@ -Discourse.SiteText = Discourse.Model.extend({ - markdown: Em.computed.equal('format', 'markdown'), - plainText: Em.computed.equal('format', 'plain'), - html: Em.computed.equal('format', 'html'), - css: Em.computed.equal('format', 'css'), - - save: function() { - return Discourse.ajax("/admin/customize/site_text/" + this.get('text_type'), { - type: 'PUT', - data: {value: this.get('value')} - }); - } -}); - -Discourse.SiteText.reopenClass({ - find: function(type) { - return Discourse.ajax("/admin/customize/site_text/" + type).then(function (data) { - return Discourse.SiteText.create(data.site_text); - }); - } -}); diff --git a/app/assets/javascripts/admin/models/site_text_type.js b/app/assets/javascripts/admin/models/site_text_type.js deleted file mode 100644 index 1a181bd8e8..0000000000 --- a/app/assets/javascripts/admin/models/site_text_type.js +++ /dev/null @@ -1,11 +0,0 @@ -Discourse.SiteTextType = Discourse.Model.extend(); - -Discourse.SiteTextType.reopenClass({ - findAll: function() { - return Discourse.ajax("/admin/customize/site_text_types").then(function(data) { - return data.map(function(ct) { - return Discourse.SiteTextType.create(ct); - }); - }); - } -}); diff --git a/app/assets/javascripts/admin/models/user-field.js.es6 b/app/assets/javascripts/admin/models/user-field.js.es6 index e5c3348efd..9e657f2204 100644 --- a/app/assets/javascripts/admin/models/user-field.js.es6 +++ b/app/assets/javascripts/admin/models/user-field.js.es6 @@ -1,54 +1,26 @@ -var UserField = Ember.Object.extend({ - destroy: function() { - var self = this; - return new Ember.RSVP.Promise(function(resolve) { - var id = self.get('id'); - if (id) { - return Discourse.ajax("/admin/customize/user_fields/" + id, { type: 'DELETE' }).then(function() { - resolve(); - }); - } - resolve(); - }); - }, +import RestModel from 'discourse/models/rest'; +import { i18n } from 'discourse/lib/computed'; - save: function(attrs) { - var id = this.get('id'); - if (!id) { - return Discourse.ajax("/admin/customize/user_fields", { - type: "POST", - data: { user_field: attrs } - }); - } else { - return Discourse.ajax("/admin/customize/user_fields/" + id, { - type: "PUT", - data: { user_field: attrs } - }); - } - } +const UserField = RestModel.extend(); + +const UserFieldType = Ember.Object.extend({ + name: i18n('id', 'admin.user_fields.field_types.%@') }); UserField.reopenClass({ - findAll: function() { - return Discourse.ajax("/admin/customize/user_fields").then(function(result) { - return result.user_fields.map(function(uf) { - return UserField.create(uf); - }); - }); - }, - - fieldTypes: function() { + fieldTypes() { if (!this._fieldTypes) { this._fieldTypes = [ - Ember.Object.create({id: 'text', name: I18n.t('admin.user_fields.field_types.text') }), - Ember.Object.create({id: 'confirm', name: I18n.t('admin.user_fields.field_types.confirm') }) + UserFieldType.create({ id: 'text' }), + UserFieldType.create({ id: 'confirm' }), + UserFieldType.create({ id: 'dropdown', hasOptions: true }) ]; } return this._fieldTypes; }, - fieldTypeById: function(id) { + fieldTypeById(id) { return this.fieldTypes().findBy('id', id); } }); diff --git a/app/assets/javascripts/admin/routes/admin-api.js.es6 b/app/assets/javascripts/admin/routes/admin-api.js.es6 new file mode 100644 index 0000000000..82212c9657 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-api.js.es6 @@ -0,0 +1,5 @@ +export default Ember.Route.extend({ + model() { + return Discourse.ApiKey.find(); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-backups-index.js.es6 b/app/assets/javascripts/admin/routes/admin-backups-index.js.es6 new file mode 100644 index 0000000000..651551f857 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-backups-index.js.es6 @@ -0,0 +1,5 @@ +export default Ember.Route.extend({ + model() { + return Discourse.Backup.find(); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin_backups_logs_route.js b/app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 similarity index 71% rename from app/assets/javascripts/admin/routes/admin_backups_logs_route.js rename to app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 index e1f3101d69..8a6d32fddf 100644 --- a/app/assets/javascripts/admin/routes/admin_backups_logs_route.js +++ b/app/assets/javascripts/admin/routes/admin-backups-logs.js.es6 @@ -1,15 +1,15 @@ -Discourse.AdminBackupsLogsRoute = Discourse.Route.extend({ +export default Ember.Route.extend({ // since the logs are pushed via the message bus // we only want to preload them (hence the beforeModel hook) - beforeModel: function() { - var logsController = this.controllerFor("adminBackupsLogs"); + beforeModel() { + const logsController = this.controllerFor("adminBackupsLogs"); // preload the logs if any PreloadStore.getAndRemove("logs").then(function (preloadedLogs) { if (preloadedLogs && preloadedLogs.length) { // we need to filter out message like: "[SUCCESS]" // and convert POJOs to Ember Objects - var logs = _.chain(preloadedLogs) + const logs = _.chain(preloadedLogs) .reject(function (log) { return log.message.length === 0 || log.message[0] === "["; }) .map(function (log) { return Em.Object.create(log); }) .value(); @@ -18,6 +18,6 @@ Discourse.AdminBackupsLogsRoute = Discourse.Route.extend({ }); }, - setupController: function() { /* prevent default behavior */ } + setupController() { /* prevent default behavior */ } }); diff --git a/app/assets/javascripts/admin/routes/admin-backups.js.es6 b/app/assets/javascripts/admin/routes/admin-backups.js.es6 index 981dc5394b..ac7f963cd4 100644 --- a/app/assets/javascripts/admin/routes/admin-backups.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-backups.js.es6 @@ -10,14 +10,14 @@ export default Discourse.Route.extend({ _processLogMessage(log) { if (log.message === "[STARTED]") { - this.controllerFor("adminBackups").set("isOperationRunning", true); + this.controllerFor("adminBackups").set("model.isOperationRunning", true); this.controllerFor("adminBackupsLogs").clear(); } else if (log.message === "[FAILED]") { - this.controllerFor("adminBackups").set("isOperationRunning", false); + this.controllerFor("adminBackups").set("model.isOperationRunning", false); bootbox.alert(I18n.t("admin.backups.operations.failed", { operation: log.operation })); } else if (log.message === "[SUCCESS]") { Discourse.User.currentProp("hideReadOnlyAlert", false); - this.controllerFor("adminBackups").set("isOperationRunning", false); + this.controllerFor("adminBackups").set("model.isOperationRunning", false); if (log.operation === "restore") { // redirect to homepage when the restore is done (session might be lost) window.location.pathname = Discourse.getURL("/"); @@ -30,7 +30,7 @@ export default Discourse.Route.extend({ model() { return PreloadStore.getAndRemove("operations_status", function() { return Discourse.ajax("/admin/backups/status.json"); - }).then(function (status) { + }).then(status => { return Discourse.BackupStatus.create({ isOperationRunning: status.is_operation_running, canRollback: status.can_rollback, @@ -50,7 +50,7 @@ export default Discourse.Route.extend({ }, backupStarted() { - this.modelFor("adminBackups").set("isOperationRunning", true); + this.controllerFor("adminBackups").set("isOperationRunning", true); this.transitionTo("admin.backups.logs"); this.send("closeModal"); }, @@ -82,7 +82,7 @@ export default Discourse.Route.extend({ Discourse.User.currentProp("hideReadOnlyAlert", true); backup.restore().then(function() { self.controllerFor("adminBackupsLogs").clear(); - self.modelFor("adminBackups").set("isOperationRunning", true); + self.controllerFor("adminBackups").set("model.isOperationRunning", true); self.transitionTo("admin.backups.logs"); }); } @@ -99,7 +99,7 @@ export default Discourse.Route.extend({ function(confirmed) { if (confirmed) { Discourse.Backup.cancel().then(function() { - self.controllerFor("adminBackups").set("isOperationRunning", false); + self.controllerFor("adminBackups").set("model.isOperationRunning", false); }); } } diff --git a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 index 648730d9b0..c283ff737b 100644 --- a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 @@ -1,3 +1,4 @@ +import Badge from 'discourse/models/badge'; import showModal from 'discourse/lib/show-modal'; export default Ember.Route.extend({ @@ -7,7 +8,7 @@ export default Ember.Route.extend({ model(params) { if (params.badge_id === "new") { - return Discourse.Badge.create({ + return Badge.create({ name: I18n.t('admin.badges.new_badge') }); } diff --git a/app/assets/javascripts/admin/routes/admin-badges.js.es6 b/app/assets/javascripts/admin/routes/admin-badges.js.es6 index 3d417f53c2..5927437669 100644 --- a/app/assets/javascripts/admin/routes/admin-badges.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-badges.js.es6 @@ -1,3 +1,5 @@ +import Badge from 'discourse/models/badge'; + export default Discourse.Route.extend({ _json: null, @@ -5,7 +7,7 @@ export default Discourse.Route.extend({ var self = this; return Discourse.ajax('/admin/badges.json').then(function(json) { self._json = json; - return Discourse.Badge.createFromJson(json); + return Badge.createFromJson(json); }); }, diff --git a/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 new file mode 100644 index 0000000000..b0f4745a38 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-colors.js.es6 @@ -0,0 +1,12 @@ +export default Ember.Route.extend({ + + model() { + return Discourse.ColorScheme.findAll(); + }, + + deactivate() { + this._super(); + this.controllerFor('adminCustomizeColors').set('selectedItem', null); + }, + +}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-css-html-show.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-css-html-show.js.es6 new file mode 100644 index 0000000000..dc38f3c51f --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-css-html-show.js.es6 @@ -0,0 +1,11 @@ +export default Ember.Route.extend({ + model(params) { + const all = this.modelFor('adminCustomizeCssHtml'); + const model = all.findProperty('id', parseInt(params.site_customization_id)); + return model ? { model, section: params.section } : this.replaceWith('adminCustomizeCssHtml.index'); + }, + + setupController(controller, hash) { + controller.setProperties(hash); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-css-html.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-css-html.js.es6 new file mode 100644 index 0000000000..5bbb460959 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-css-html.js.es6 @@ -0,0 +1,26 @@ +import showModal from 'discourse/lib/show-modal'; +import { popupAjaxError } from 'discourse/lib/ajax-error'; + +export default Ember.Route.extend({ + model() { + return this.store.findAll('site-customization'); + }, + + actions: { + importModal() { + showModal('upload-customization'); + }, + + newCustomization(obj) { + obj = obj || {name: I18n.t("admin.customize.new_style")}; + const item = this.store.createRecord('site-customization'); + + const all = this.modelFor('adminCustomizeCssHtml'); + const self = this; + item.save(obj).then(function() { + all.pushObject(item); + self.transitionTo('adminCustomizeCssHtml.show', item.get('id'), 'css'); + }).catch(popupAjaxError); + } + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 new file mode 100644 index 0000000000..725b9fa8dd --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-customize-index.js.es6 @@ -0,0 +1,5 @@ +export default Ember.Route.extend({ + beforeModel() { + this.replaceWith('adminCustomize.colors'); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 b/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 index bbe4c79845..7bdb274e43 100644 --- a/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-dashboard.js.es6 @@ -12,8 +12,9 @@ export default Discourse.Route.extend({ if (versionChecks) { c.set('versionCheck', Discourse.VersionCheck.create(d.version_check)); } - _.each(d.reports,function(report){ - c.set(report.type, Discourse.Report.create(report)); + + ['global_reports', 'page_view_reports', 'private_message_reports', 'http_reports', 'user_reports', 'mobile_reports'].forEach(name => { + c.set(name, d[name].map(r => Discourse.Report.create(r))); }); var topReferrers = d.top_referrers; diff --git a/app/assets/javascripts/admin/routes/admin-email-preview-digest.js.es6 b/app/assets/javascripts/admin/routes/admin-email-preview-digest.js.es6 new file mode 100644 index 0000000000..94d48e400d --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-email-preview-digest.js.es6 @@ -0,0 +1,16 @@ +export default Discourse.Route.extend({ + + model() { + return Discourse.EmailPreview.findDigest(); + }, + + afterModel(model) { + const controller = this.controllerFor('adminEmailPreviewDigest'); + controller.setProperties({ + model: model, + lastSeen: moment().subtract(7, 'days').format('YYYY-MM-DD'), + showHtml: true + }); + } + +}); diff --git a/app/assets/javascripts/admin/routes/admin-embedding.js.es6 b/app/assets/javascripts/admin/routes/admin-embedding.js.es6 new file mode 100644 index 0000000000..d9a249c60c --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-embedding.js.es6 @@ -0,0 +1,9 @@ +export default Ember.Route.extend({ + model() { + return this.store.find('embedding'); + }, + + setupController(controller, model) { + controller.set('embedding', model); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-group.js.es6 b/app/assets/javascripts/admin/routes/admin-group.js.es6 index f397297767..b46022a646 100644 --- a/app/assets/javascripts/admin/routes/admin-group.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-group.js.es6 @@ -1,8 +1,17 @@ +import Group from 'discourse/models/group'; + export default Discourse.Route.extend({ model: function(params) { - var groups = this.modelFor('adminGroupsType'), - group = groups.findProperty('name', params.name); + var groups = this.modelFor('adminGroupsType'); + if (params.name === 'new') { + return Group.create({ + automatic: false, + visible: true + }); + } + + var group = groups.findProperty('name', params.name); if (!group) { return this.transitionTo('adminGroups.index'); } @@ -11,9 +20,7 @@ export default Discourse.Route.extend({ setupController: function(controller, model) { controller.set("model", model); - // clear the user selector - controller.set("usernames", null); - // load the members of the group + controller.set("model.usernames", null); model.findMembers(); } diff --git a/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 b/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 index c8226f04dc..def99aa7c1 100644 --- a/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-groups-type.js.es6 @@ -9,15 +9,5 @@ export default Discourse.Route.extend({ setupController(controller, model){ controller.set("type", this.get("type")); controller.set("model", model); - }, - - actions: { - newGroup() { - const self = this; - this.transitionTo("adminGroupsType", "custom").then(function() { - var group = Discourse.Group.create({ automatic: false, visible: true }); - self.transitionTo("adminGroup", group); - }); - } } }); diff --git a/app/assets/javascripts/admin/routes/admin-permalinks.js.es6 b/app/assets/javascripts/admin/routes/admin-permalinks.js.es6 new file mode 100644 index 0000000000..72b7f444d2 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-permalinks.js.es6 @@ -0,0 +1,9 @@ +export default Discourse.Route.extend({ + model() { + return Discourse.Permalink.findAll(); + }, + + setupController(controller, model) { + controller.set('model', model); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-plugins.js.es6 b/app/assets/javascripts/admin/routes/admin-plugins.js.es6 index ae7f961fd1..820c5207d4 100644 --- a/app/assets/javascripts/admin/routes/admin-plugins.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-plugins.js.es6 @@ -4,8 +4,21 @@ export default Ember.Route.extend({ }, actions: { - showSettings() { - this.transitionTo('adminSiteSettingsCategory', 'plugins'); + showSettings(plugin) { + const controller = this.controllerFor('adminSiteSettings'); + this.transitionTo('adminSiteSettingsCategory', 'plugins').then(() => { + if (plugin) { + const match = /^(.*)_enabled/.exec(plugin.get('enabled_setting')); + if (match[1]) { + // filterContent() is normally on a debounce from typing. + // Because we don't want the default of "All Results", we tell it + // to skip the next debounce. + controller.set('filter', match[1]); + controller.set('_skipBounce', true); + controller.filterContentNow('plugins'); + } + } + }); } } }); diff --git a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 index ab5f2e16c9..e01d0f8f0d 100644 --- a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 @@ -16,12 +16,18 @@ export default { this.resource('adminCustomize', { path: '/customize' } ,function() { this.route('colors'); - this.route('css_html'); - this.resource('adminSiteText', { path: '/site_text' }, function() { + + this.resource('adminCustomizeCssHtml', { path: 'css_html' }, function() { + this.route('show', {path: '/:site_customization_id/:section'}); + }); + + this.resource('adminSiteText', { path: '/site_texts' }, function() { this.route('edit', {path: '/:text_type'}); }); this.resource('adminUserFields', { path: '/user_fields' }); this.resource('adminEmojis', { path: '/emojis' }); + this.resource('adminPermalinks', { path: '/permalinks' }); + this.resource('adminEmbedding', { path: '/embedding' }); }); this.route('api'); diff --git a/app/assets/javascripts/admin/routes/admin-site-text-edit.js.es6 b/app/assets/javascripts/admin/routes/admin-site-text-edit.js.es6 index 6eba6e76e1..847746d039 100644 --- a/app/assets/javascripts/admin/routes/admin-site-text-edit.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-site-text-edit.js.es6 @@ -1,5 +1,5 @@ export default Discourse.Route.extend({ - model: function(params) { - return Discourse.SiteText.find(params.text_type); + model(params) { + return this.store.find('site-text', params.text_type); } }); diff --git a/app/assets/javascripts/admin/routes/admin-site-text.js.es6 b/app/assets/javascripts/admin/routes/admin-site-text.js.es6 index e850fd6c6c..f69c3c3954 100644 --- a/app/assets/javascripts/admin/routes/admin-site-text.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-site-text.js.es6 @@ -1,5 +1,5 @@ export default Discourse.Route.extend({ - model: function() { - return Discourse.SiteTextType.findAll(); + model() { + return this.store.findAll('site-text-type'); } }); diff --git a/app/assets/javascripts/admin/routes/admin-user-badges.js.es6 b/app/assets/javascripts/admin/routes/admin-user-badges.js.es6 new file mode 100644 index 0000000000..205c1647c9 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-user-badges.js.es6 @@ -0,0 +1,26 @@ +import UserBadge from 'discourse/models/user-badge'; +import Badge from 'discourse/models/badge'; + +export default Discourse.Route.extend({ + model() { + const username = this.modelFor('adminUser').get('username'); + return UserBadge.findByUsername(username); + }, + + setupController(controller, model) { + // Find all badges. + controller.set('loading', true); + Badge.findAll().then(function(badges) { + controller.set('badges', badges); + if (badges.length > 0) { + var grantableBadges = controller.get('grantableBadges'); + if (grantableBadges.length > 0) { + controller.set('selectedBadgeId', grantableBadges[0].get('id')); + } + } + controller.set('loading', false); + }); + // Set the model. + controller.set('model', model); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 b/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 index a540f343cc..5770165ff7 100644 --- a/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-user-fields.js.es6 @@ -2,13 +2,10 @@ import UserField from 'admin/models/user-field'; export default Discourse.Route.extend({ model: function() { - return UserField.findAll(); + return this.store.findAll('user-field'); }, setupController: function(controller, model) { - controller.setProperties({ - model: model, - fieldTypes: UserField.fieldTypes() - }); + controller.setProperties({ model, fieldTypes: UserField.fieldTypes() }); } }); diff --git a/app/assets/javascripts/admin/routes/admin-users-list.js.es6 b/app/assets/javascripts/admin/routes/admin-users-list.js.es6 index 0472077f69..1a2b08d29e 100644 --- a/app/assets/javascripts/admin/routes/admin-users-list.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-users-list.js.es6 @@ -9,7 +9,7 @@ export default Discourse.Route.extend({ }, sendInvites: function() { - this.transitionTo('user.invited', Discourse.User.current()); + this.transitionTo('userInvited', Discourse.User.current()); }, deleteUser: function(user) { diff --git a/app/assets/javascripts/admin/routes/admin.js.es6 b/app/assets/javascripts/admin/routes/admin.js.es6 new file mode 100644 index 0000000000..c1b80b2df5 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin.js.es6 @@ -0,0 +1,16 @@ +export default Discourse.Route.extend({ + titleToken() { + return I18n.t('admin_title'); + }, + + activate() { + this.controllerFor("application").setProperties({ + showTop: false, + showFooter: false, + }); + }, + + deactivate() { + this.controllerFor("application").set("showTop", true); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin_api_route.js b/app/assets/javascripts/admin/routes/admin_api_route.js deleted file mode 100644 index e13085fa1a..0000000000 --- a/app/assets/javascripts/admin/routes/admin_api_route.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - Handles routes related to api - - @class AdminApiRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminApiRoute = Discourse.Route.extend({ - - model: function() { - return Discourse.ApiKey.find(); - } - -}); diff --git a/app/assets/javascripts/admin/routes/admin_backups_index_route.js b/app/assets/javascripts/admin/routes/admin_backups_index_route.js deleted file mode 100644 index 8ba5e6539f..0000000000 --- a/app/assets/javascripts/admin/routes/admin_backups_index_route.js +++ /dev/null @@ -1,7 +0,0 @@ -Discourse.AdminBackupsIndexRoute = Discourse.Route.extend({ - - model: function() { - return Discourse.Backup.find(); - } - -}); diff --git a/app/assets/javascripts/admin/routes/admin_customize_colors_route.js b/app/assets/javascripts/admin/routes/admin_customize_colors_route.js deleted file mode 100644 index 953031ab40..0000000000 --- a/app/assets/javascripts/admin/routes/admin_customize_colors_route.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - Handles routes related to colors customization - - @class AdminCustomizeColorsRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminCustomizeColorsRoute = Discourse.Route.extend({ - - model: function() { - return Discourse.ColorScheme.findAll(); - }, - - deactivate: function() { - this._super(); - this.controllerFor('adminCustomizeColors').set('selectedItem', null); - }, - -}); diff --git a/app/assets/javascripts/admin/routes/admin_customize_css_html_route.js b/app/assets/javascripts/admin/routes/admin_customize_css_html_route.js deleted file mode 100644 index a9684add0f..0000000000 --- a/app/assets/javascripts/admin/routes/admin_customize_css_html_route.js +++ /dev/null @@ -1,5 +0,0 @@ -Discourse.AdminCustomizeCssHtmlRoute = Discourse.Route.extend({ - model: function() { - return Discourse.SiteCustomization.findAll(); - } -}); diff --git a/app/assets/javascripts/admin/routes/admin_customize_route.js b/app/assets/javascripts/admin/routes/admin_customize_route.js deleted file mode 100644 index 5b6d853170..0000000000 --- a/app/assets/javascripts/admin/routes/admin_customize_route.js +++ /dev/null @@ -1,5 +0,0 @@ -Discourse.AdminCustomizeIndexRoute = Discourse.Route.extend({ - beforeModel: function() { - this.replaceWith('adminCustomize.colors'); - } -}); diff --git a/app/assets/javascripts/admin/routes/admin_email_preview_digest_route.js b/app/assets/javascripts/admin/routes/admin_email_preview_digest_route.js deleted file mode 100644 index 511b56bbfe..0000000000 --- a/app/assets/javascripts/admin/routes/admin_email_preview_digest_route.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - Previews the Email Digests - - @class AdminEmailPreviewDigest - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ - -Discourse.AdminEmailPreviewDigestRoute = Discourse.Route.extend({ - - model: function() { - return Discourse.EmailPreview.findDigest(); - }, - - afterModel: function(model) { - var controller = this.controllerFor('adminEmailPreviewDigest'); - controller.setProperties({ - model: model, - lastSeen: moment().subtract(7, 'days').format('YYYY-MM-DD'), - showHtml: true - }); - } - -}); diff --git a/app/assets/javascripts/admin/routes/admin_reports_route.js b/app/assets/javascripts/admin/routes/admin_reports_route.js index 21b842a9f8..2e11a0d39e 100644 --- a/app/assets/javascripts/admin/routes/admin_reports_route.js +++ b/app/assets/javascripts/admin/routes/admin_reports_route.js @@ -14,6 +14,7 @@ Discourse.AdminReportsRoute = Discourse.Route.extend({ setupController: function(controller, model) { controller.setProperties({ model: model, + categoryId: 'all', startDate: moment(model.get('start_date')).format('YYYY-MM-DD'), endDate: moment(model.get('end_date')).format('YYYY-MM-DD') }); diff --git a/app/assets/javascripts/admin/routes/admin_route.js b/app/assets/javascripts/admin/routes/admin_route.js deleted file mode 100644 index 07fc597f03..0000000000 --- a/app/assets/javascripts/admin/routes/admin_route.js +++ /dev/null @@ -1,5 +0,0 @@ -Discourse.AdminRoute = Discourse.Route.extend({ - titleToken: function() { - return I18n.t('admin_title'); - } -}); diff --git a/app/assets/javascripts/admin/routes/admin_user_badges_route.js b/app/assets/javascripts/admin/routes/admin_user_badges_route.js deleted file mode 100644 index f681e6f919..0000000000 --- a/app/assets/javascripts/admin/routes/admin_user_badges_route.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - Shows all of the badges that have been granted to a user, and allow granting and - revoking badges. - - @class AdminUserBadgesRoute - @extends Discourse.Route - @namespace Discourse - @module Discourse -**/ -Discourse.AdminUserBadgesRoute = Discourse.Route.extend({ - model: function() { - var username = this.modelFor('adminUser').get('username'); - return Discourse.UserBadge.findByUsername(username); - }, - - setupController: function(controller, model) { - // Find all badges. - controller.set('loading', true); - Discourse.Badge.findAll().then(function(badges) { - controller.set('badges', badges); - if (badges.length > 0) { - var grantableBadges = controller.get('grantableBadges'); - if (grantableBadges.length > 0) { - controller.set('selectedBadgeId', grantableBadges[0].get('id')); - } - } - controller.set('loading', false); - }); - // Set the model. - controller.set('model', model); - } -}); diff --git a/app/assets/javascripts/admin/templates/admin.hbs b/app/assets/javascripts/admin/templates/admin.hbs index df56d57bf8..c7746a6d4a 100644 --- a/app/assets/javascripts/admin/templates/admin.hbs +++ b/app/assets/javascripts/admin/templates/admin.hbs @@ -1,29 +1,28 @@
- {{global-notice}}
diff --git a/app/assets/javascripts/admin/templates/backups.hbs b/app/assets/javascripts/admin/templates/backups.hbs index 510b965308..7562754c08 100644 --- a/app/assets/javascripts/admin/templates/backups.hbs +++ b/app/assets/javascripts/admin/templates/backups.hbs @@ -1,12 +1,12 @@
- {{#if canRollback}} + {{#if model.canRollback}} {{d-button action="rollback" class="btn-rollback" label="admin.backups.operations.rollback.label" @@ -14,7 +14,7 @@ icon="ambulance" disabled=rollbackDisabled}} {{/if}} - {{#if isOperationRunning}} + {{#if model.isOperationRunning}} {{d-button action="cancelOperation" class="btn-danger" title="admin.backups.operations.cancel.title" diff --git a/app/assets/javascripts/admin/templates/backups_index.hbs b/app/assets/javascripts/admin/templates/backups_index.hbs index ca8830a0e8..09bf83bc63 100644 --- a/app/assets/javascripts/admin/templates/backups_index.hbs +++ b/app/assets/javascripts/admin/templates/backups_index.hbs @@ -6,9 +6,9 @@
{{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"}} + {{d-button icon="eye" action="toggleReadOnlyMode" disabled=status.model.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"}} + {{d-button icon="eye" action="toggleReadOnlyMode" disabled=status.model.isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}} {{/if}}
@@ -20,12 +20,12 @@
{{fa-icon "download"}}{{i18n 'admin.backups.operations.download.label'}} - {{#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"}} + {{#if status.model.isOperationRunning}} + {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" disabled="true" title="admin.backups.operations.is_running"}} + {{d-button icon="play" action="startRestore" actionParam=backup disabled=status.model.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"}} + {{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" title="admin.backups.operations.destroy.title"}} + {{d-button icon="play" action="startRestore" actionParam=backup disabled=status.model.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}} {{/if}}
diff --git a/app/assets/javascripts/admin/templates/components/admin-form-row.hbs b/app/assets/javascripts/admin/templates/components/admin-form-row.hbs new file mode 100644 index 0000000000..aee95b6701 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/admin-form-row.hbs @@ -0,0 +1,14 @@ +
+ {{#if label}} + + {{else}} +   + {{/if}} +
+
+ {{#if wrapLabel}} + + {{else}} + {{yield}} + {{/if}} +
diff --git a/app/assets/javascripts/admin/templates/components/admin-nav-item.hbs b/app/assets/javascripts/admin/templates/components/admin-nav-item.hbs deleted file mode 100644 index d6341fdce8..0000000000 --- a/app/assets/javascripts/admin/templates/components/admin-nav-item.hbs +++ /dev/null @@ -1,9 +0,0 @@ -{{#if routeParam}} - {{#link-to route routeParam}}{{i18n label}}{{/link-to}} -{{else}} - {{#if route}} - {{#link-to route}}{{i18n label}}{{/link-to}} - {{else}} - {{i18n label}} - {{/if}} -{{/if}} diff --git a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs new file mode 100644 index 0000000000..36fa8c3b81 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs @@ -0,0 +1,53 @@ +{{#if editing}} + {{#admin-form-row label="admin.user_fields.type"}} + {{combo-box content=fieldTypes valueAttribute="id" value=buffered.field_type}} + {{/admin-form-row}} + + {{#admin-form-row label="admin.user_fields.name"}} + {{input value=buffered.name class="user-field-name"}} + {{/admin-form-row}} + + {{#admin-form-row label="admin.user_fields.description"}} + {{input value=buffered.description class="user-field-desc"}} + {{/admin-form-row}} + + {{#if bufferedFieldType.hasOptions}} + {{#admin-form-row label="admin.user_fields.options"}} + {{value-list values=buffered.options inputType="array"}} + {{/admin-form-row}} + {{/if}} + + {{#admin-form-row wrapLabel="true"}} + {{input type="checkbox" checked=buffered.editable}} {{i18n 'admin.user_fields.editable.title'}} + {{/admin-form-row}} + + {{#admin-form-row wrapLabel="true"}} + {{input type="checkbox" checked=buffered.required}} {{i18n 'admin.user_fields.required.title'}} + {{/admin-form-row}} + + {{#admin-form-row wrapLabel="true"}} + {{input type="checkbox" checked=buffered.show_on_profile}} {{i18n 'admin.user_fields.show_on_profile.title'}} + {{/admin-form-row}} + + {{#admin-form-row}} + {{d-button action="save" class="btn-primary" icon="check" label="admin.user_fields.save"}} + {{d-button action="cancel" class="btn-danger" icon="times" label="admin.user_fields.cancel"}} + {{/admin-form-row}} +{{else}} +
+
+ {{userField.name}} +
+ {{{userField.description}}} +
+
{{fieldName}}
+
+ {{d-button action="edit" class="btn-default" icon="pencil" label="admin.user_fields.edit"}} + {{d-button action="destroy" class="btn-danger" icon="trash-o" label="admin.user_fields.delete"}} + {{d-button action="moveUp" icon="arrow-up" disabled=cantMoveUp}} + {{d-button action="moveDown" icon="arrow-down" disabled=cantMoveDown}} +
+
+
{{flags}}
+{{/if}} +
diff --git a/app/assets/javascripts/admin/templates/components/customize-link.hbs b/app/assets/javascripts/admin/templates/components/customize-link.hbs new file mode 100644 index 0000000000..dd3c4104c7 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/customize-link.hbs @@ -0,0 +1,5 @@ +
  • + + {{customization.description}} + +
  • diff --git a/app/assets/javascripts/admin/templates/components/embeddable-host.hbs b/app/assets/javascripts/admin/templates/components/embeddable-host.hbs new file mode 100644 index 0000000000..c35d40e1d6 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/embeddable-host.hbs @@ -0,0 +1,19 @@ +{{#if editing}} + + {{input value=buffered.host placeholder="example.com" enter="save" class="host-name"}} + + + {{category-chooser value=categoryId}} + + + {{d-button icon="check" action="save" class="btn-primary" disabled=cantSave}} + {{d-button icon="times" action="cancel" class="btn-danger" disabled=host.isSaving}} + +{{else}} + {{host.host}} + {{category-badge host.category}} + + {{d-button icon="pencil" action="edit"}} + {{d-button icon="trash-o" action="delete" class='btn-danger'}} + +{{/if}} diff --git a/app/assets/javascripts/admin/templates/components/embedding-setting.hbs b/app/assets/javascripts/admin/templates/components/embedding-setting.hbs new file mode 100644 index 0000000000..36dbb88692 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/embedding-setting.hbs @@ -0,0 +1,11 @@ +{{#if isCheckbox}} + +{{else}} + + {{input value=value id=inputId}} +{{/if}} + +
    diff --git a/app/assets/javascripts/admin/templates/components/group-member.hbs b/app/assets/javascripts/admin/templates/components/group-member.hbs new file mode 100644 index 0000000000..d476fdf033 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/group-member.hbs @@ -0,0 +1 @@ +{{avatar member imageSize="small"}} {{member.username}} {{#unless automatic}}{{fa-icon "times"}}{{/unless}} diff --git a/app/assets/javascripts/admin/templates/components/highlighted-code.hbs b/app/assets/javascripts/admin/templates/components/highlighted-code.hbs new file mode 100644 index 0000000000..4d67dd6fd2 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/highlighted-code.hbs @@ -0,0 +1 @@ +
    {{code}}
    diff --git a/app/assets/javascripts/admin/templates/components/permalink-form.hbs b/app/assets/javascripts/admin/templates/components/permalink-form.hbs new file mode 100644 index 0000000000..f5155c27c2 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/permalink-form.hbs @@ -0,0 +1,5 @@ +{{i18n 'admin.permalink.form.label'}} +{{text-field value=url disabled=formSubmitted class="permalink-url" placeholderKey="admin.permalink.url" autocorrect="off" autocapitalize="off"}} +{{combo-box content=permalinkTypes value=permalinkType}} +{{text-field value=permalink_type_value disabled=formSubmitted class="external-url" placeholderKey=permalinkTypePlaceholder autocorrect="off" autocapitalize="off"}} + diff --git a/app/assets/javascripts/admin/templates/components/site-setting.hbs b/app/assets/javascripts/admin/templates/components/site-setting.hbs index eb117c0f4d..be0d4d8a08 100644 --- a/app/assets/javascripts/admin/templates/components/site-setting.hbs +++ b/app/assets/javascripts/admin/templates/components/site-setting.hbs @@ -2,12 +2,12 @@

    {{unbound settingName}}

    - {{partial partialName}} +{{component componentName setting=setting value=buffered.value validationMessage=validationMessage}}
    {{#if dirty}}
    - {{d-button class="ok no-text" action="save" icon="check"}} - {{d-button class="cancel no-text" action="cancel" icon="times"}} + {{d-button class="ok" action="save" icon="check"}} + {{d-button class="cancel" action="cancel" icon="times"}}
    {{else if setting.overridden}} {{d-button action="resetDefault" icon="undo" label="admin.site_settings.reset"}} diff --git a/app/assets/javascripts/admin/templates/site-settings/bool.hbs b/app/assets/javascripts/admin/templates/components/site-settings/bool.hbs similarity index 62% rename from app/assets/javascripts/admin/templates/site-settings/bool.hbs rename to app/assets/javascripts/admin/templates/components/site-settings/bool.hbs index 1f441c421c..f1ac669900 100644 --- a/app/assets/javascripts/admin/templates/site-settings/bool.hbs +++ b/app/assets/javascripts/admin/templates/components/site-settings/bool.hbs @@ -1,4 +1,5 @@ diff --git a/app/assets/javascripts/admin/templates/components/site-settings/category-list.hbs b/app/assets/javascripts/admin/templates/components/site-settings/category-list.hbs new file mode 100644 index 0000000000..621f3fa70e --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/site-settings/category-list.hbs @@ -0,0 +1,3 @@ +{{category-group categories=selectedCategories blacklist=selectedCategories}} +
    {{{unbound setting.description}}}
    +{{setting-validation-message message=validationMessage}} diff --git a/app/assets/javascripts/admin/templates/site-settings/enum.hbs b/app/assets/javascripts/admin/templates/components/site-settings/enum.hbs similarity index 80% rename from app/assets/javascripts/admin/templates/site-settings/enum.hbs rename to app/assets/javascripts/admin/templates/components/site-settings/enum.hbs index 67cbfc92ac..765a0e20d1 100644 --- a/app/assets/javascripts/admin/templates/site-settings/enum.hbs +++ b/app/assets/javascripts/admin/templates/components/site-settings/enum.hbs @@ -1,4 +1,4 @@ -{{combo-box valueAttribute="value" content=setting.validValues value=buffered.value none=setting.allowsNone}} +{{combo-box valueAttribute="value" content=setting.validValues value=value none=setting.allowsNone}} {{preview}} {{setting-validation-message message=validationMessage}}
    {{{unbound setting.description}}}
    diff --git a/app/assets/javascripts/admin/templates/components/site-settings/host-list.hbs b/app/assets/javascripts/admin/templates/components/site-settings/host-list.hbs new file mode 100644 index 0000000000..5107f5b4af --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/site-settings/host-list.hbs @@ -0,0 +1,3 @@ +{{value-list values=value addKey="admin.site_settings.add_host"}} +{{setting-validation-message message=validationMessage}} +
    {{{unbound setting.description}}}
    diff --git a/app/assets/javascripts/admin/templates/site-settings/list.hbs b/app/assets/javascripts/admin/templates/components/site-settings/list.hbs similarity index 54% rename from app/assets/javascripts/admin/templates/site-settings/list.hbs rename to app/assets/javascripts/admin/templates/components/site-settings/list.hbs index bc1cf2b51e..e741bea5ed 100644 --- a/app/assets/javascripts/admin/templates/site-settings/list.hbs +++ b/app/assets/javascripts/admin/templates/components/site-settings/list.hbs @@ -1,3 +1,3 @@ -{{list-setting settingValue=buffered.value choices=setting.choices settingName=setting.setting}} +{{list-setting settingValue=value choices=setting.choices settingName=setting.setting}} {{setting-validation-message message=validationMessage}}
    {{{unbound setting.description}}}
    diff --git a/app/assets/javascripts/admin/templates/site-settings/string.hbs b/app/assets/javascripts/admin/templates/components/site-settings/string.hbs similarity index 62% rename from app/assets/javascripts/admin/templates/site-settings/string.hbs rename to app/assets/javascripts/admin/templates/components/site-settings/string.hbs index f8427094ab..71d7216f27 100644 --- a/app/assets/javascripts/admin/templates/site-settings/string.hbs +++ b/app/assets/javascripts/admin/templates/components/site-settings/string.hbs @@ -1,3 +1,3 @@ -{{text-field value=buffered.value classNames="input-setting-string"}} +{{text-field value=value classNames="input-setting-string"}} {{setting-validation-message message=validationMessage}}
    {{{unbound setting.description}}}
    diff --git a/app/assets/javascripts/admin/templates/components/site-settings/url-list.hbs b/app/assets/javascripts/admin/templates/components/site-settings/url-list.hbs new file mode 100644 index 0000000000..41213777e3 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/site-settings/url-list.hbs @@ -0,0 +1,3 @@ +{{value-list values=value addKey="admin.site_settings.add_url"}} +{{setting-validation-message message=validationMessage}} +
    {{{unbound setting.description}}}
    diff --git a/app/assets/javascripts/admin/templates/site-settings/url_list.hbs b/app/assets/javascripts/admin/templates/components/site-settings/value-list.hbs similarity index 77% rename from app/assets/javascripts/admin/templates/site-settings/url_list.hbs rename to app/assets/javascripts/admin/templates/components/site-settings/value-list.hbs index bd22e1b033..caaf3739d7 100644 --- a/app/assets/javascripts/admin/templates/site-settings/url_list.hbs +++ b/app/assets/javascripts/admin/templates/components/site-settings/value-list.hbs @@ -1,3 +1,3 @@ -{{url-list value=buffered.value}} +{{value-list values=value}} {{setting-validation-message message=validationMessage}}
    {{{unbound setting.description}}}
    diff --git a/app/assets/javascripts/admin/templates/components/url-list.hbs b/app/assets/javascripts/admin/templates/components/url-list.hbs deleted file mode 100644 index d313c5b30a..0000000000 --- a/app/assets/javascripts/admin/templates/components/url-list.hbs +++ /dev/null @@ -1,18 +0,0 @@ -{{#if urls}} -
    - {{#each url in urls}} -
    - {{d-button action="removeUrl" - actionParam=url - icon="times" - class="btn-small no-text"}} - {{url}} -
    - {{/each}} -
    -{{/if}} - -
    - {{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}} -
    diff --git a/app/assets/javascripts/admin/templates/components/value-list.hbs b/app/assets/javascripts/admin/templates/components/value-list.hbs new file mode 100644 index 0000000000..6f6e1aa194 --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/value-list.hbs @@ -0,0 +1,18 @@ +{{#if collection}} +
    + {{#each collection as |value index|}} +
    + {{d-button action="removeValue" + actionParam=value + icon="times" + class="btn-small"}} + {{value}} +
    + {{/each}} +
    +{{/if}} + +
    + {{text-field value=newValue placeholderKey=addKey}} + {{d-button action="addValue" icon="plus" class="btn-primary btn-small" disabled=inputInvalid}} +
    diff --git a/app/assets/javascripts/admin/templates/customize-css-html-index.hbs b/app/assets/javascripts/admin/templates/customize-css-html-index.hbs new file mode 100644 index 0000000000..781d063c7f --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-css-html-index.hbs @@ -0,0 +1 @@ +

    {{i18n 'admin.customize.about'}}

    diff --git a/app/assets/javascripts/admin/templates/customize-css-html-show.hbs b/app/assets/javascripts/admin/templates/customize-css-html-show.hbs new file mode 100644 index 0000000000..95b82ba97b --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-css-html-show.hbs @@ -0,0 +1,75 @@ +
    +
    + {{text-field class="style-name" value=model.name}} + {{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}} + +
    + +
    + +
    + {{#if cssActive}}{{ace-editor content=model.stylesheet mode="scss"}}{{/if}} + {{#if headerActive}}{{ace-editor content=model.header mode="html"}}{{/if}} + {{#if topActive}}{{ace-editor content=model.top mode="html"}}{{/if}} + {{#if footerActive}}{{ace-editor content=model.footer mode="html"}}{{/if}} + {{#if headTagActive}}{{ace-editor content=model.head_tag mode="html"}}{{/if}} + {{#if bodyTagActive}}{{ace-editor content=model.body_tag mode="html"}}{{/if}} + {{#if embeddedCssActive}}{{ace-editor content=model.embedded_css mode="css"}}{{/if}} + {{#if mobileCssActive}}{{ace-editor content=model.mobile_stylesheet mode="scss"}}{{/if}} + {{#if mobileHeaderActive}}{{ace-editor content=model.mobile_header mode="html"}}{{/if}} + {{#if mobileTopActive}}{{ace-editor content=model.mobile_top mode="html"}}{{/if}} + {{#if mobileFooterActive}}{{ace-editor content=model.mobile_footer mode="html"}}{{/if}} +
    + + +
    +
    diff --git a/app/assets/javascripts/admin/templates/customize-css-html.hbs b/app/assets/javascripts/admin/templates/customize-css-html.hbs new file mode 100644 index 0000000000..73b8e22c9f --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-css-html.hbs @@ -0,0 +1,13 @@ +
    +

    {{i18n 'admin.customize.css_html.long_title'}}

    +
      + {{#each model as |c|}} + {{customize-link customization=c}} + {{/each}} +
    + + {{d-button label="admin.customize.new" icon="plus" action="newCustomization" class="btn-primary"}} + {{d-button action="importModal" icon="upload" label="admin.customize.import"}} +
    + +{{outlet}} diff --git a/app/assets/javascripts/admin/templates/customize.hbs b/app/assets/javascripts/admin/templates/customize.hbs index 75dcdbab9f..8ab4c662e7 100644 --- a/app/assets/javascripts/admin/templates/customize.hbs +++ b/app/assets/javascripts/admin/templates/customize.hbs @@ -1,9 +1,11 @@ {{#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'}} + {{nav-item route='adminCustomize.colors' label='admin.customize.colors.title'}} + {{nav-item route='adminCustomizeCssHtml.index' label='admin.customize.css_html.title'}} + {{nav-item route='adminSiteText' label='admin.site_text.title'}} + {{nav-item route='adminUserFields' label='admin.user_fields.title'}} + {{nav-item route='adminEmojis' label='admin.emoji.title'}} + {{nav-item route='adminPermalinks' label='admin.permalink.title'}} + {{nav-item route='adminEmbedding' label='admin.embedding.title'}} {{/admin-nav}}
    diff --git a/app/assets/javascripts/admin/templates/customize_css_html.hbs b/app/assets/javascripts/admin/templates/customize_css_html.hbs deleted file mode 100644 index 004318e1eb..0000000000 --- a/app/assets/javascripts/admin/templates/customize_css_html.hbs +++ /dev/null @@ -1,82 +0,0 @@ -
    -

    {{i18n 'admin.customize.css_html.long_title'}}

    - - -
    - -{{#if selectedItem}} -
    -
    - {{text-field class="style-name" value=selectedItem.name}} - - - -
    - {{#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}} -
    - -
    -
    -{{else}} -

    {{i18n 'admin.customize.about'}}

    -{{/if}} diff --git a/app/assets/javascripts/admin/templates/dashboard.hbs b/app/assets/javascripts/admin/templates/dashboard.hbs index 32ce15f9c2..9a3d98496a 100644 --- a/app/assets/javascripts/admin/templates/dashboard.hbs +++ b/app/assets/javascripts/admin/templates/dashboard.hbs @@ -17,7 +17,9 @@ {{#unless loading}} - {{admin-report-trust-level-counts report=users_by_trust_level}} + {{#each r in user_reports}} + {{admin-report-trust-level-counts report=r}} + {{/each}} {{/unless}} @@ -26,15 +28,15 @@
    - + - + - + - +
    {{i18n 'admin.dashboard.admins'}}{{fa-icon "shield"}} {{i18n 'admin.dashboard.admins'}} {{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}} {{i18n 'admin.dashboard.suspended'}}{{fa-icon "ban"}} {{i18n 'admin.dashboard.suspended'}} {{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}
    {{i18n 'admin.dashboard.moderators'}}{{fa-icon "shield"}} {{i18n 'admin.dashboard.moderators'}} {{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}} {{i18n 'admin.dashboard.blocked'}}{{fa-icon "ban"}} {{i18n 'admin.dashboard.blocked'}} {{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}
    @@ -54,14 +56,9 @@ {{#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}} + {{#each r in global_reports}} + {{admin-report-counts report=r}} + {{/each}} {{/unless}} @@ -81,21 +78,19 @@ {{#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}} + {{#each r in page_view_reports}} + {{admin-report-counts report=r}} + {{/each}} {{/unless}}
    -
    - + @@ -105,11 +100,31 @@ {{#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}} + {{#each r in private_message_reports}} + {{admin-report-counts report=r}} + {{/each}} + {{/unless}} + +
    {{i18n 'admin.dashboard.private_messages_short'}}{{fa-icon "envelope"}} {{i18n 'admin.dashboard.private_messages_short'}} {{i18n 'admin.dashboard.reports.today'}} {{i18n 'admin.dashboard.reports.yesterday'}} {{i18n 'admin.dashboard.reports.last_7_days'}}
    +
    + +
    + + + + + + + + + + + + + {{#unless loading}} + {{#each r in mobile_reports}} + {{admin-report-counts report=r}} + {{/each}} {{/unless}}
    {{i18n 'admin.dashboard.mobile_title'}}{{i18n 'admin.dashboard.reports.today'}}{{i18n 'admin.dashboard.reports.yesterday'}}{{i18n 'admin.dashboard.reports.last_7_days'}}{{i18n 'admin.dashboard.reports.last_30_days'}}{{i18n 'admin.dashboard.reports.all'}}
    @@ -154,12 +169,9 @@ {{#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}} + {{#each r in http_reports}} + {{admin-report-counts report=r}} + {{/each}} {{/unless}} @@ -176,7 +188,7 @@ {{#if foundProblems}}
    -
    +
    {{fa-icon "exclamation-triangle"}}

    {{i18n 'admin.dashboard.problems_found'}} diff --git a/app/assets/javascripts/admin/templates/email.hbs b/app/assets/javascripts/admin/templates/email.hbs index 0ec147207e..7388653777 100644 --- a/app/assets/javascripts/admin/templates/email.hbs +++ b/app/assets/javascripts/admin/templates/email.hbs @@ -1,9 +1,9 @@ {{#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'}} + {{nav-item route='adminEmail.index' label='admin.email.settings'}} + {{nav-item route='adminEmail.all' label='admin.email.all'}} + {{nav-item route='adminEmail.sent' label='admin.email.sent'}} + {{nav-item route='adminEmail.skipped' label='admin.email.skipped'}} + {{nav-item route='adminEmail.previewDigest' label='admin.email.preview_digest'}} {{/admin-nav}}

    diff --git a/app/assets/javascripts/admin/templates/email_preview_digest.hbs b/app/assets/javascripts/admin/templates/email_preview_digest.hbs index 7670d6194b..83d99db35f 100644 --- a/app/assets/javascripts/admin/templates/email_preview_digest.hbs +++ b/app/assets/javascripts/admin/templates/email_preview_digest.hbs @@ -4,24 +4,22 @@
    {{input type="date" value=lastSeen id="last-seen"}} -
    -
    -
    -
    - - {{#if showHtml}} - {{i18n 'admin.email.html'}} | {{i18n 'admin.email.text'}} - {{else}} - {{i18n 'admin.email.html'}} | {{i18n 'admin.email.text'}} - {{/if}} +
    + + {{#if showHtml}} + {{i18n 'admin.email.html'}} | {{i18n 'admin.email.text'}} + {{else}} + {{i18n 'admin.email.html'}} | {{i18n 'admin.email.text'}} + {{/if}} +
    {{#conditional-loading-spinner condition=loading}} {{#if showHtml}} - {{{html_content}}} + {{{model.html_content}}} {{else}} -
    {{{text_content}}}
    +
    {{{model.text_content}}}
    {{/if}} {{/conditional-loading-spinner}} diff --git a/app/assets/javascripts/admin/templates/embedding.hbs b/app/assets/javascripts/admin/templates/embedding.hbs new file mode 100644 index 0000000000..15d021a9c1 --- /dev/null +++ b/app/assets/javascripts/admin/templates/embedding.hbs @@ -0,0 +1,61 @@ +
    + {{#if embedding.embeddable_hosts}} + + + + + + + {{#each embedding.embeddable_hosts as |host|}} + {{embeddable-host host=host deleteHost="deleteHost"}} + {{/each}} +
    {{i18n "admin.embedding.host"}}{{i18n "admin.embedding.category"}} 
    + {{else}} +

    {{i18n "admin.embedding.get_started"}}

    + {{/if}} + + {{d-button label="admin.embedding.add_host" action="addHost" icon="plus" class="btn-primary add-host"}} +
    + +{{#if showSecondary}} +
    +

    {{{i18n "admin.embedding.sample"}}}

    + {{highlighted-code code=embeddingCode lang="html"}} +
    + +
    + +
    +

    {{i18n "admin.embedding.settings"}}

    + + {{embedding-setting field="embed_by_username" value=embedding.embed_by_username}} + {{embedding-setting field="embed_post_limit" value=embedding.embed_post_limit}} + {{embedding-setting field="embed_truncate" value=embedding.embed_truncate type="checkbox"}} +
    + +
    +

    {{i18n "admin.embedding.feed_settings"}}

    +

    {{i18n "admin.embedding.feed_description"}}

    + + {{embedding-setting field="feed_polling_enabled" value=embedding.feed_polling_enabled type="checkbox"}} + {{embedding-setting field="feed_polling_url" value=embedding.feed_polling_url}} + {{embedding-setting field="embed_username_key_from_feed" value=embedding.embed_username_key_from_feed}} +
    + +
    +

    {{i18n "admin.embedding.crawling_settings"}}

    +

    {{i18n "admin.embedding.crawling_description"}}

    + + {{embedding-setting field="embed_whitelist_selector" value=embedding.embed_whitelist_selector}} + {{embedding-setting field="embed_blacklist_selector" value=embedding.embed_blacklist_selector}} +
    + +
    + {{d-button label="admin.embedding.save" + action="saveChanges" + class="btn-primary embed-save" + disabled=embedding.isSaving}} + + {{#if saved}}{{i18n "saved"}}{{/if}} +
    +{{/if}} diff --git a/app/assets/javascripts/admin/templates/flags-list.hbs b/app/assets/javascripts/admin/templates/flags-list.hbs index e001e92ae8..9b404b9929 100644 --- a/app/assets/javascripts/admin/templates/flags-list.hbs +++ b/app/assets/javascripts/admin/templates/flags-list.hbs @@ -32,7 +32,7 @@ {{fa-icon "envelope"}} {{/if}} {{topic-status topic=flaggedPost.topic}} - {{flaggedPost.topic.title}} + {{{unbound flaggedPost.topic.fancyTitle}}} {{#if flaggedPost.postAuthorFlagged}} {{{flaggedPost.excerpt}}} diff --git a/app/assets/javascripts/admin/templates/flags.hbs b/app/assets/javascripts/admin/templates/flags.hbs index a1ecf47875..b19e8e2276 100644 --- a/app/assets/javascripts/admin/templates/flags.hbs +++ b/app/assets/javascripts/admin/templates/flags.hbs @@ -1,6 +1,6 @@ {{#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'}} + {{nav-item route='adminFlags.list' routeParam='active' label='admin.flags.active'}} + {{nav-item route='adminFlags.list' routeParam='old' label='admin.flags.old'}} {{/admin-nav}}
    diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index d17167a66f..ac1f979691 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -1,47 +1,49 @@
    - {{#if automatic}} -

    {{name}}

    + {{#if model.automatic}} +

    {{model.name}}

    {{else}} - {{text-field name="name" value=name placeholderKey="admin.groups.name_placeholder"}} + {{text-field name="name" value=model.name placeholderKey="admin.groups.name_placeholder"}} {{/if}}
    - {{#if id}} + {{#if model.id}}
    - +
    - {{each member in members itemView="group-member"}} + {{#each model.members as |member|}} + {{group-member member=member automatic=model.automatic removeAction="removeMember"}} + {{/each}}
    - {{#unless automatic}} + {{#unless model.automatic}}
    - {{user-selector usernames=usernames placeholderKey="admin.groups.selector_placeholder" id="user-selector"}} - + {{user-selector usernames=model.usernames placeholderKey="admin.groups.selector_placeholder" id="user-selector"}} + {{d-button action="addMembers" class="add" icon="plus" label="admin.groups.add"}}
    {{/unless}} {{/if}}
    - {{#unless automatic}} + {{#unless model.automatic}}
    @@ -49,15 +51,15 @@
    - {{combo-box name="alias" valueAttribute="value" value=alias_level content=aliasLevelOptions}} + {{combo-box name="alias" valueAttribute="value" value=model.alias_level content=aliasLevelOptions}}
    - {{#unless automatic}} + {{#unless model.automatic}}
    - {{list-setting name="automatic_membership" settingValue=emailDomains}} + {{list-setting name="automatic_membership" settingValue=model.emailDomains}}
    @@ -66,13 +68,18 @@ - {{input value=title}} + {{input value=model.title}} +
    + +
    + + {{combo-box name="grant_trust_level" valueAttribute="value" value=model.grant_trust_level content=trustLevelOptions}}
    {{/unless}}
    - {{#unless automatic}} + {{#unless model.automatic}} {{/unless}}
    diff --git a/app/assets/javascripts/admin/templates/group_member.hbs b/app/assets/javascripts/admin/templates/group_member.hbs deleted file mode 100644 index 1c344ba739..0000000000 --- a/app/assets/javascripts/admin/templates/group_member.hbs +++ /dev/null @@ -1 +0,0 @@ -{{avatar member imageSize="small"}} {{member.username}} {{#unless automatic}}{{fa-icon "times"}}{{/unless}} diff --git a/app/assets/javascripts/admin/templates/groups.hbs b/app/assets/javascripts/admin/templates/groups.hbs index 0de3fc5c79..2d767c3844 100644 --- a/app/assets/javascripts/admin/templates/groups.hbs +++ b/app/assets/javascripts/admin/templates/groups.hbs @@ -1,6 +1,6 @@ {{#admin-nav}} - {{admin-nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}} - {{admin-nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}} + {{nav-item route='adminGroupsType' routeParam='custom' label='admin.groups.custom'}} + {{nav-item route='adminGroupsType' routeParam='automatic' label='admin.groups.automatic'}} {{/admin-nav}}
    diff --git a/app/assets/javascripts/admin/templates/groups_type.hbs b/app/assets/javascripts/admin/templates/groups_type.hbs index 8188752a9a..980ca08c8f 100644 --- a/app/assets/javascripts/admin/templates/groups_type.hbs +++ b/app/assets/javascripts/admin/templates/groups_type.hbs @@ -9,7 +9,9 @@ {{/each}}
    - {{d-button action="newGroup" icon="plus" label="admin.groups.new"}} + {{#link-to 'adminGroup' 'new' class="btn"}} + {{fa-icon "plus"}} {{i18n 'admin.groups.new'}} + {{/link-to}} {{#if controller.isAuto}} {{d-button action="refreshAutoGroups" icon="refresh" label="admin.groups.refresh" disabled=refreshingAutoGroups}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/logs.hbs b/app/assets/javascripts/admin/templates/logs.hbs index 38654fefca..5c412949fa 100644 --- a/app/assets/javascripts/admin/templates/logs.hbs +++ b/app/assets/javascripts/admin/templates/logs.hbs @@ -1,10 +1,10 @@ {{#admin-nav}} - {{admin-nav-item route='adminLogs.staffActionLogs' label='admin.logs.staff_actions.title'}} - {{admin-nav-item route='adminLogs.screenedEmails' label='admin.logs.screened_emails.title'}} - {{admin-nav-item route='adminLogs.screenedIpAddresses' label='admin.logs.screened_ips.title'}} - {{admin-nav-item route='adminLogs.screenedUrls' label='admin.logs.screened_urls.title'}} + {{nav-item route='adminLogs.staffActionLogs' label='admin.logs.staff_actions.title'}} + {{nav-item route='adminLogs.screenedEmails' label='admin.logs.screened_emails.title'}} + {{nav-item route='adminLogs.screenedIpAddresses' label='admin.logs.screened_ips.title'}} + {{nav-item route='adminLogs.screenedUrls' label='admin.logs.screened_urls.title'}} {{#if currentUser.admin}} - {{admin-nav-item href='/logs' label='admin.logs.logster.title'}} + {{nav-item path='/logs' label='admin.logs.logster.title'}} {{/if}} {{/admin-nav}} diff --git a/app/assets/javascripts/admin/templates/logs/details_modal.hbs b/app/assets/javascripts/admin/templates/logs/details_modal.hbs index d789dd9cc8..874e4ea858 100644 --- a/app/assets/javascripts/admin/templates/logs/details_modal.hbs +++ b/app/assets/javascripts/admin/templates/logs/details_modal.hbs @@ -1,5 +1,5 @@