mirror of
https://github.com/discourse/discourse.git
synced 2025-02-20 11:06:32 +08:00
Quite a few Ember-CLI / Upgrade related changes (#11867)
* Quite a few Ember-CLI / Upgrade related changes They should all be backwards compatible. This is all to help merge our branches. * REFACTOR: DRY up username validation Also avoids overwriting computed properties for compatibility with newer Ember releases.
This commit is contained in:
parent
0bd0358158
commit
11c812f042
|
@ -2,6 +2,7 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
|||
import Component from "@ember/component";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import I18n from "I18n";
|
||||
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
||||
import { on } from "@ember/object/evented";
|
||||
|
@ -49,7 +50,10 @@ export default Component.extend({
|
|||
renderTopicListItem() {
|
||||
const template = findRawTemplate("list/topic-list-item");
|
||||
if (template) {
|
||||
this.set("topicListItemContents", template(this).htmlSafe());
|
||||
this.set(
|
||||
"topicListItemContents",
|
||||
template(this, RUNTIME_OPTIONS).htmlSafe()
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
import BufferedMixin from "ember-buffered-proxy/mixin";
|
||||
import BufferedProxy from "ember-buffered-proxy/proxy";
|
||||
import Controller from "@ember/controller";
|
||||
import EmberObjectProxy from "@ember/object/proxy";
|
||||
|
@ -11,14 +12,13 @@ import { sort } from "@ember/object/computed";
|
|||
export default Controller.extend(ModalFunctionality, Evented, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.categoriesSorting = ["position"];
|
||||
},
|
||||
|
||||
@discourseComputed("site.categories.[]")
|
||||
categoriesBuffered(categories) {
|
||||
const bufProxy = EmberObjectProxy.extend(BufferedProxy);
|
||||
return categories.map((c) => bufProxy.create({ content: c }));
|
||||
const bufProxy = EmberObjectProxy.extend(BufferedMixin || BufferedProxy);
|
||||
return (categories || []).map((c) => bufProxy.create({ content: c }));
|
||||
},
|
||||
|
||||
categoriesOrdered: sort("categoriesBuffered", "categoriesSorting"),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { helperContext, registerUnbound } from "discourse-common/lib/helpers";
|
||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
|
||||
|
||||
function renderRaw(ctx, template, templateName, params) {
|
||||
params = jQuery.extend({}, params);
|
||||
|
@ -21,7 +22,7 @@ function renderRaw(ctx, template, templateName, params) {
|
|||
}
|
||||
}
|
||||
|
||||
return htmlSafe(template(params));
|
||||
return htmlSafe(template(params, RUNTIME_OPTIONS));
|
||||
}
|
||||
|
||||
registerUnbound("raw", function (templateName, params) {
|
||||
|
|
|
@ -63,6 +63,8 @@ export default {
|
|||
name: "copy-codeblocks",
|
||||
|
||||
initialize(container) {
|
||||
const siteSettings = container.lookup("site-settings:main");
|
||||
|
||||
withPluginApi("0.8.7", (api) => {
|
||||
function _cleanUp() {
|
||||
Object.values(_copyCodeblocksClickHandlers || {}).forEach((handler) =>
|
||||
|
@ -112,7 +114,6 @@ export default {
|
|||
return;
|
||||
}
|
||||
|
||||
const siteSettings = container.lookup("site-settings:main");
|
||||
if (!siteSettings.show_copy_button_on_codeblocks) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import bootbox from "bootbox";
|
|||
|
||||
export default {
|
||||
name: "jquery-plugins",
|
||||
initialize: function () {
|
||||
initialize() {
|
||||
// Settings for bootbox
|
||||
bootbox.animate(false);
|
||||
bootbox.backdrop(true);
|
||||
|
|
|
@ -1223,8 +1223,6 @@ class PluginApi {
|
|||
}
|
||||
}
|
||||
|
||||
let _pluginv01;
|
||||
|
||||
// from http://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number
|
||||
function cmpVersions(a, b) {
|
||||
let i, diff;
|
||||
|
@ -1244,16 +1242,24 @@ function cmpVersions(a, b) {
|
|||
|
||||
function getPluginApi(version) {
|
||||
version = version.toString();
|
||||
|
||||
if (cmpVersions(version, PLUGIN_API_VERSION) <= 0) {
|
||||
if (!_pluginv01) {
|
||||
_pluginv01 = new PluginApi(version, getOwner(this));
|
||||
const owner = getOwner(this);
|
||||
let pluginApi = owner.lookup("plugin-api:main");
|
||||
|
||||
if (!pluginApi) {
|
||||
pluginApi = new PluginApi(version, owner);
|
||||
owner.registry.register("plugin-api:main", pluginApi, {
|
||||
instantiate: false,
|
||||
});
|
||||
}
|
||||
|
||||
// We are recycling the compatible object, but let's update to the higher version
|
||||
if (_pluginv01.version < version) {
|
||||
_pluginv01.version = version;
|
||||
if (pluginApi.version < version) {
|
||||
pluginApi.version = version;
|
||||
}
|
||||
return _pluginv01;
|
||||
|
||||
return pluginApi;
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`Plugin API v${version} is not supported`);
|
||||
|
@ -1306,7 +1312,3 @@ function decorate(klass, evt, cb, id) {
|
|||
});
|
||||
klass.reopen(mixin);
|
||||
}
|
||||
|
||||
export function resetPluginApi() {
|
||||
_pluginv01 = null;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import BufferedMixin from "ember-buffered-proxy/mixin";
|
||||
import BufferedProxy from "ember-buffered-proxy/proxy";
|
||||
import EmberObjectProxy from "@ember/object/proxy";
|
||||
import Mixin from "@ember/object/mixin";
|
||||
|
@ -6,7 +7,7 @@ import { computed } from "@ember/object";
|
|||
export function bufferedProperty(property) {
|
||||
const mixin = {
|
||||
buffered: computed(property, function () {
|
||||
return EmberObjectProxy.extend(BufferedProxy).create({
|
||||
return EmberObjectProxy.extend(BufferedMixin || BufferedProxy).create({
|
||||
content: this.get(property),
|
||||
});
|
||||
}),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Mixin from "@ember/object/mixin";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
|
||||
// Small buffer so that very tiny scrolls don't trigger mobile header switch
|
||||
const MOBILE_SCROLL_TOLERANCE = 5;
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ const Scrolling = Mixin.create({
|
|||
let debouncedScrollMethod = () => {
|
||||
discourseDebounce(this, onScrollMethod, opts.debounce);
|
||||
};
|
||||
|
||||
ScrollingDOMMethods.bindOnScroll(debouncedScrollMethod, opts.name);
|
||||
} else {
|
||||
ScrollingDOMMethods.bindOnScroll(onScrollMethod, opts.name);
|
||||
|
|
|
@ -2,16 +2,30 @@ import EmberObject from "@ember/object";
|
|||
import I18n from "I18n";
|
||||
import Mixin from "@ember/object/mixin";
|
||||
import User from "discourse/models/user";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { setting } from "discourse/lib/computed";
|
||||
|
||||
function failedResult(attrs) {
|
||||
let result = EmberObject.create({
|
||||
shouldCheck: false,
|
||||
failed: true,
|
||||
element: document.querySelector("#new-account-username"),
|
||||
});
|
||||
result.setProperties(attrs);
|
||||
return result;
|
||||
}
|
||||
|
||||
function validResult(attrs) {
|
||||
let result = EmberObject.create({ ok: true });
|
||||
result.setProperties(attrs);
|
||||
return result;
|
||||
}
|
||||
|
||||
export default Mixin.create({
|
||||
uniqueUsernameValidation: null,
|
||||
|
||||
maxUsernameLength: setting("max_username_length"),
|
||||
|
||||
minUsernameLength: setting("min_username_length"),
|
||||
|
||||
fetchExistingUsername() {
|
||||
|
@ -35,125 +49,78 @@ export default Mixin.create({
|
|||
);
|
||||
},
|
||||
|
||||
@discourseComputed("accountUsername")
|
||||
basicUsernameValidation(accountUsername) {
|
||||
const failedAttrs = {
|
||||
failed: true,
|
||||
element: document.querySelector("#new-account-username"),
|
||||
};
|
||||
this.set("uniqueUsernameValidation", null);
|
||||
@observes("accountUsername")
|
||||
triggerValidation() {
|
||||
let { accountUsername } = this;
|
||||
|
||||
if (accountUsername && accountUsername === this.prefilledUsername) {
|
||||
return EmberObject.create({
|
||||
ok: true,
|
||||
reason: I18n.t("user.username.prefilled"),
|
||||
});
|
||||
let result = this.basicUsernameValidation(accountUsername);
|
||||
if (result.shouldCheck) {
|
||||
this.checkUsernameAvailability();
|
||||
}
|
||||
|
||||
// If blank, fail without a reason
|
||||
if (isEmpty(accountUsername)) {
|
||||
return EmberObject.create(
|
||||
Object.assign(failedAttrs, {
|
||||
message: I18n.t("user.username.required"),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// If too short
|
||||
if (accountUsername.length < this.siteSettings.min_username_length) {
|
||||
return EmberObject.create(
|
||||
Object.assign(failedAttrs, {
|
||||
reason: I18n.t("user.username.too_short"),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// If too long
|
||||
if (accountUsername.length > this.maxUsernameLength) {
|
||||
return EmberObject.create(
|
||||
Object.assign(failedAttrs, {
|
||||
reason: I18n.t("user.username.too_long"),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.checkUsernameAvailability();
|
||||
// Let's check it out asynchronously
|
||||
return EmberObject.create(
|
||||
Object.assign(failedAttrs, {
|
||||
reason: I18n.t("user.username.checking"),
|
||||
})
|
||||
);
|
||||
this.set("usernameValidation", result);
|
||||
},
|
||||
|
||||
shouldCheckUsernameAvailability() {
|
||||
return (
|
||||
!isEmpty(this.accountUsername) &&
|
||||
this.accountUsername.length >= this.minUsernameLength
|
||||
);
|
||||
basicUsernameValidation(username) {
|
||||
if (username && username === this.prefilledUsername) {
|
||||
return validResult({ reason: I18n.t("user.username.prefilled") });
|
||||
}
|
||||
|
||||
if (isEmpty(username)) {
|
||||
return failedResult({ message: I18n.t("user.username.required") });
|
||||
}
|
||||
|
||||
if (username.length < this.siteSettings.min_username_length) {
|
||||
return failedResult({ reason: I18n.t("user.username.too_short") });
|
||||
}
|
||||
|
||||
if (username.length > this.maxUsernameLength) {
|
||||
return failedResult({ reason: I18n.t("user.username.too_long") });
|
||||
}
|
||||
|
||||
return failedResult({
|
||||
shouldCheck: true,
|
||||
reason: I18n.t("user.username.checking"),
|
||||
});
|
||||
},
|
||||
|
||||
checkUsernameAvailability() {
|
||||
discourseDebounce(
|
||||
this,
|
||||
function () {
|
||||
if (this.shouldCheckUsernameAvailability()) {
|
||||
return User.checkUsername(
|
||||
this.accountUsername,
|
||||
this.accountEmail
|
||||
).then((result) => {
|
||||
return User.checkUsername(this.accountUsername, this.accountEmail).then(
|
||||
(result) => {
|
||||
this.set("isDeveloper", false);
|
||||
if (result.available) {
|
||||
if (result.is_developer) {
|
||||
this.set("isDeveloper", true);
|
||||
}
|
||||
return this.set(
|
||||
"uniqueUsernameValidation",
|
||||
EmberObject.create({
|
||||
ok: true,
|
||||
reason: I18n.t("user.username.available"),
|
||||
})
|
||||
"usernameValidation",
|
||||
validResult({ reason: I18n.t("user.username.available") })
|
||||
);
|
||||
} else {
|
||||
const failedAttrs = {
|
||||
failed: true,
|
||||
element: document.querySelector("#new-account-username"),
|
||||
};
|
||||
|
||||
if (result.suggestion) {
|
||||
return this.set(
|
||||
"uniqueUsernameValidation",
|
||||
EmberObject.create(
|
||||
Object.assign(failedAttrs, {
|
||||
reason: I18n.t("user.username.not_available", result),
|
||||
})
|
||||
)
|
||||
"usernameValidation",
|
||||
failedResult({
|
||||
reason: I18n.t("user.username.not_available", result),
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return this.set(
|
||||
"uniqueUsernameValidation",
|
||||
EmberObject.create(
|
||||
Object.assign(failedAttrs, {
|
||||
reason: result.errors
|
||||
? result.errors.join(" ")
|
||||
: I18n.t("user.username.not_available_no_suggestion"),
|
||||
})
|
||||
)
|
||||
"usernameValidation",
|
||||
failedResult({
|
||||
reason: result.errors
|
||||
? result.errors.join(" ")
|
||||
: I18n.t("user.username.not_available_no_suggestion"),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
500
|
||||
);
|
||||
},
|
||||
|
||||
// Actually wait for the async name check before we're 100% sure we're good to go
|
||||
@discourseComputed("uniqueUsernameValidation", "basicUsernameValidation")
|
||||
usernameValidation() {
|
||||
const basicValidation = this.basicUsernameValidation;
|
||||
const uniqueUsername = this.uniqueUsernameValidation;
|
||||
return uniqueUsername ? uniqueUsername : basicValidation;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -21,6 +21,15 @@ export function clearAppEventsCache(container) {
|
|||
}
|
||||
|
||||
export default Service.extend(Evented, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
// A hack because we don't make `current user` properly via container in testing mode
|
||||
if (this.currentUser) {
|
||||
this.currentUser.appEvents = this;
|
||||
}
|
||||
},
|
||||
|
||||
on() {
|
||||
if (arguments.length === 2) {
|
||||
let [name, fn] = arguments;
|
||||
|
|
|
@ -28,7 +28,6 @@ import { moduleFor } from "ember-qunit";
|
|||
import { resetCustomPostMessageCallbacks } from "discourse/controllers/topic";
|
||||
import { resetDecorators } from "discourse/widgets/widget";
|
||||
import { resetCache as resetOneboxCache } from "pretty-text/oneboxer";
|
||||
import { resetPluginApi } from "discourse/lib/plugin-api";
|
||||
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
|
||||
import { resetDecorators as resetPostCookedDecorators } from "discourse/widgets/post-cooked";
|
||||
import { resetTopicTitleDecorators } from "discourse/components/topic-title";
|
||||
|
@ -197,7 +196,6 @@ export function acceptance(name, optionsOrCallback) {
|
|||
|
||||
clearOutletCache();
|
||||
clearHTMLCache();
|
||||
resetPluginApi();
|
||||
|
||||
if (siteChanges) {
|
||||
resetSite(currentSettings(), siteChanges);
|
||||
|
@ -231,7 +229,6 @@ export function acceptance(name, optionsOrCallback) {
|
|||
resetExtraClasses();
|
||||
clearOutletCache();
|
||||
clearHTMLCache();
|
||||
resetPluginApi();
|
||||
clearRewrites();
|
||||
initSearchData();
|
||||
resetDecorators();
|
||||
|
|
|
@ -8,13 +8,10 @@ discourseModule("Unit | Controller | create-account", function () {
|
|||
const controller = await this.owner.lookup("controller:create-account");
|
||||
controller.set("accountUsername", username);
|
||||
|
||||
let validation = controller.basicUsernameValidation(username);
|
||||
assert.ok(validation.failed, "username should be invalid: " + username);
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.failed"),
|
||||
true,
|
||||
"username should be invalid: " + username
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.reason"),
|
||||
validation.reason,
|
||||
expectedReason,
|
||||
"username validation reason: " + username + ", " + expectedReason
|
||||
);
|
||||
|
@ -33,13 +30,10 @@ discourseModule("Unit | Controller | create-account", function () {
|
|||
prefilledUsername: "porkchops",
|
||||
});
|
||||
|
||||
let validation = controller.basicUsernameValidation("porkchops");
|
||||
assert.ok(validation.ok, "Prefilled username is valid");
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.ok"),
|
||||
true,
|
||||
"Prefilled username is valid"
|
||||
);
|
||||
assert.equal(
|
||||
controller.get("basicUsernameValidation.reason"),
|
||||
validation.reason,
|
||||
I18n.t("user.username.prefilled"),
|
||||
"Prefilled username is valid"
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue
Block a user