diff --git a/.eslintrc b/.eslintrc
index a656e80d38..7898fbf829 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,96 +1,3 @@
{
- "env": {
- "browser": true,
- "builtin": true,
- "es6": true,
- "jasmine": true,
- "mocha": true,
- "node": true
- },
- "parserOptions": {
- "ecmaVersion": 7,
- "sourceType": "module"
- },
- "globals": {
- "$": true,
- "_": true,
- "andThen": true,
- "asyncRender": true,
- "Blob": true,
- "bootbox": true,
- "click": true,
- "waitUntil": true,
- "getSettledState": true,
- "count": true,
- "currentPath": true,
- "currentRouteName": true,
- "currentURL": true,
- "define": true,
- "Discourse": true,
- "Ember": true,
- "exists": true,
- "File": true,
- "fillIn": true,
- "find": true,
- "Handlebars": true,
- "hasModule": true,
- "I18n": true,
- "invisible": true,
- "jQuery": true,
- "keyboardHelper": true,
- "keyEvent": true,
- "moduleFor": true,
- "moduleForComponent": true,
- "moment": true,
- "Pretender": true,
- "QUnit": true,
- "require": true,
- "requirejs": true,
- "RSVP": true,
- "sandbox": true,
- "sinon": true,
- "test": true,
- "triggerEvent": true,
- "visible": true,
- "visit": true,
- "pauseTest": true
- },
- "rules": {
- "block-scoped-var": 2,
- "dot-notation": 0,
- "eqeqeq": [2, "allow-null"],
- "guard-for-in": 2,
- "no-alert": 2,
- "no-bitwise": 2,
- "no-caller": 2,
- "no-cond-assign": 0,
- "no-console": 2,
- "no-debugger": 2,
- "no-empty": 0,
- "no-eval": 2,
- "no-extend-native": 2,
- "no-extra-parens": 0,
- "no-inner-declarations": 2,
- "no-irregular-whitespace": 2,
- "no-iterator": 2,
- "no-loop-func": 2,
- "no-mixed-spaces-and-tabs": 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-this-before-super": 2,
- "no-trailing-spaces": 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"
+ "extends": "eslint-config-discourse"
}
diff --git a/Gemfile b/Gemfile
index 98e165f3ac..fe9faf36a4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -26,6 +26,10 @@ else
gem 'sprockets-rails'
end
+# this will eventually be added to rails,
+# allows us to precompile all our templates in the unicorn master
+gem 'actionview_precompiler', require: false
+
gem 'seed-fu'
gem 'mail', require: false
@@ -46,7 +50,7 @@ gem 'redis-namespace'
gem 'active_model_serializers', '~> 0.8.3'
-gem 'onebox', '1.9.13'
+gem 'onebox', '1.9.17'
gem 'http_accept_language', '~>2.0.5', require: false
@@ -114,6 +118,8 @@ gem 'execjs', require: false
gem 'mini_racer'
gem 'highline', '~> 1.7.0', require: false
gem 'rack-protection' # security
+gem 'cbor', require: false
+gem 'cose', require: false
# Gems used only for assets and not required in production environments by default.
# Allow everywhere for now cause we are allowing asset debugging in production
diff --git a/Gemfile.lock b/Gemfile.lock
index e859be90ef..89de47af23 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -20,6 +20,8 @@ GEM
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ actionview_precompiler (0.2.1)
+ actionview (>= 6.0.a)
active_model_serializers (0.8.4)
activemodel (>= 3.0)
activejob (6.0.0)
@@ -77,12 +79,15 @@ GEM
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.0.1)
+ cbor (0.5.9.6)
certified (1.0.0)
chunky_png (1.3.11)
coderay (1.1.2)
colored2 (3.1.2)
concurrent-ruby (1.1.5)
connection_pool (2.2.2)
+ cose (0.9.0)
+ cbor (~> 0.5.9)
cppjieba_rb (0.3.3)
crack (0.4.3)
safe_yaml (~> 1.0.0)
@@ -92,7 +97,7 @@ GEM
debug_inspector (0.0.3)
diff-lcs (1.3)
diffy (3.3.0)
- discourse-ember-source (3.10.0.1)
+ discourse-ember-source (3.10.0.2)
discourse_image_optim (0.26.2)
exifr (~> 1.2, >= 1.2.2)
fspath (~> 3.0)
@@ -168,7 +173,7 @@ GEM
logstash-event (1.2.02)
logstash-logger (0.26.1)
logstash-event (~> 1.2)
- logster (2.3.2)
+ logster (2.3.3)
loofah (2.2.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
@@ -240,7 +245,7 @@ GEM
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
- onebox (1.9.13)
+ onebox (1.9.17)
htmlentities (~> 4.3)
moneta (~> 1.0)
multi_json (~> 1.11)
@@ -355,7 +360,7 @@ GEM
ruby_dep (1.5.0)
rubyzip (2.0.0)
safe_yaml (1.0.5)
- sanitize (5.0.0)
+ sanitize (5.1.0)
crass (~> 1.0.2)
nokogiri (>= 1.8.0)
nokogumbo (~> 2.0)
@@ -425,6 +430,7 @@ DEPENDENCIES
actionmailer (= 6.0.0)
actionpack (= 6.0.0)
actionview (= 6.0.0)
+ actionview_precompiler
active_model_serializers (~> 0.8.3)
activemodel (= 6.0.0)
activerecord (= 6.0.0)
@@ -438,8 +444,10 @@ DEPENDENCIES
bootsnap
bullet
byebug
+ cbor
certified
colored2
+ cose
cppjieba_rb
css_parser
diffy
@@ -493,7 +501,7 @@ DEPENDENCIES
omniauth-oauth2
omniauth-openid
omniauth-twitter
- onebox (= 1.9.13)
+ onebox (= 1.9.17)
openid-redis-store
parallel_tests
pg
diff --git a/app/assets/javascripts/admin-login/admin-login.js.es6 b/app/assets/javascripts/admin-login/admin-login.js.es6
new file mode 100644
index 0000000000..e7404b386a
--- /dev/null
+++ b/app/assets/javascripts/admin-login/admin-login.js.es6
@@ -0,0 +1,46 @@
+import { getWebauthnCredential } from "discourse/lib/webauthn";
+
+export default function() {
+ document.getElementById(
+ "activate-security-key-alternative"
+ ).onclick = function() {
+ document.getElementById("second-factor-forms").style.display = "block";
+ document.getElementById("primary-security-key-form").style.display = "none";
+ };
+
+ document.getElementById("submit-security-key").onclick = function(e) {
+ e.preventDefault();
+ getWebauthnCredential(
+ document.getElementById("security-key-challenge").value,
+ document
+ .getElementById("security-key-allowed-credential-ids")
+ .value.split(","),
+ credentialData => {
+ document.getElementById(
+ "security-key-credential"
+ ).value = JSON.stringify(credentialData);
+ e.target.parentElement.submit();
+ },
+ errorMessage => {
+ document.getElementById("security-key-error").innerText = errorMessage;
+ }
+ );
+ };
+
+ const useTotp = I18n.t("login.second_factor_toggle.totp");
+ const useBackup = I18n.t("login.second_factor_toggle.backup_code");
+ const backupForm = document.getElementById("backup-second-factor-form");
+ const primaryForm = document.getElementById("primary-second-factor-form");
+ document.getElementById("toggle-form").onclick = function(event) {
+ event.preventDefault();
+ if (backupForm.style.display === "none") {
+ backupForm.style.display = "block";
+ primaryForm.style.display = "none";
+ document.getElementById("toggle-form").innerHTML = useTotp;
+ } else {
+ backupForm.style.display = "none";
+ primaryForm.style.display = "block";
+ document.getElementById("toggle-form").innerHTML = useBackup;
+ }
+ };
+}
diff --git a/app/assets/javascripts/admin-login/admin-login.no-module.js.es6 b/app/assets/javascripts/admin-login/admin-login.no-module.js.es6
new file mode 100644
index 0000000000..4de268433d
--- /dev/null
+++ b/app/assets/javascripts/admin-login/admin-login.no-module.js.es6
@@ -0,0 +1 @@
+require("admin-login/admin-login").default();
diff --git a/app/assets/javascripts/admin/components/admin-report-stacked-chart.js.es6 b/app/assets/javascripts/admin/components/admin-report-stacked-chart.js.es6
index ce0dfd9021..852bc92b3d 100644
--- a/app/assets/javascripts/admin/components/admin-report-stacked-chart.js.es6
+++ b/app/assets/javascripts/admin/components/admin-report-stacked-chart.js.es6
@@ -33,6 +33,10 @@ export default Ember.Component.extend({
_scheduleChartRendering() {
Ember.run.schedule("afterRender", () => {
+ if (!this.element) {
+ return;
+ }
+
this._renderChart(
this.model,
this.element.querySelector(".chart-canvas")
diff --git a/app/assets/javascripts/admin/models/admin-user.js.es6 b/app/assets/javascripts/admin/models/admin-user.js.es6
index bd830be687..8a8ba62481 100644
--- a/app/assets/javascripts/admin/models/admin-user.js.es6
+++ b/app/assets/javascripts/admin/models/admin-user.js.es6
@@ -243,12 +243,12 @@ const AdminUser = Discourse.User.extend({
this.set("originalTrustLevel", this.trust_level);
},
- dirty: propertyNotEqual("originalTrustLevel", "trustLevel.id"),
+ dirty: propertyNotEqual("originalTrustLevel", "trust_level"),
saveTrustLevel() {
return ajax(`/admin/users/${this.id}/trust_level`, {
type: "PUT",
- data: { level: this.get("trustLevel.id") }
+ data: { level: this.trust_level }
})
.then(() => window.location.reload())
.catch(e => {
@@ -266,7 +266,7 @@ const AdminUser = Discourse.User.extend({
},
restoreTrustLevel() {
- this.set("trustLevel.id", this.originalTrustLevel);
+ this.set("trust_level", this.originalTrustLevel);
},
lockTrustLevel(locked) {
diff --git a/app/assets/javascripts/admin/templates/api-keys.hbs b/app/assets/javascripts/admin/templates/api-keys.hbs
index d631e4188d..a1254f3042 100644
--- a/app/assets/javascripts/admin/templates/api-keys.hbs
+++ b/app/assets/javascripts/admin/templates/api-keys.hbs
@@ -3,6 +3,8 @@
{{i18n "admin.api.key"}}
{{i18n "admin.api.user"}}
+ {{i18n "admin.api.created"}}
+ {{i18n "admin.api.last_used"}}
{{c.description}}