Version bump
This commit is contained in:
commit
fcbfd7eccd
37
.eslintrc
37
.eslintrc
@ -5,14 +5,41 @@
|
||||
"eol-last": 2
|
||||
},
|
||||
"globals": {
|
||||
"_": "off",
|
||||
"acceptance": "off",
|
||||
"asyncRender": "off",
|
||||
"bootbox": "off",
|
||||
"click": "off",
|
||||
"count": "off",
|
||||
"currentPath": "off",
|
||||
"currentRouteName": "off",
|
||||
"currentURL": "off",
|
||||
"currentUser": "off",
|
||||
"Discourse": "off",
|
||||
"exists": "off",
|
||||
"fillIn": "off",
|
||||
"find": "off",
|
||||
"getSettledState": "off",
|
||||
"hasModule": "off",
|
||||
"invisible": "off",
|
||||
"jQuery": "off",
|
||||
"keyboardHelper": "off",
|
||||
"keyEvent": "off",
|
||||
"moduleFor": "off",
|
||||
"moduleForComponent": "off",
|
||||
"testStart": "off",
|
||||
"testDone": "off",
|
||||
"pauseTest": "off",
|
||||
"Pretender": "off",
|
||||
"query": "off",
|
||||
"queryAll": "off",
|
||||
"QUnit": "off",
|
||||
"sandbox": "off",
|
||||
"sinon": "off",
|
||||
"currentURL": "off",
|
||||
"invisible": "off",
|
||||
"test": "off",
|
||||
"testDone": "off",
|
||||
"testStart": "off",
|
||||
"triggerEvent": "off",
|
||||
"visible": "off",
|
||||
"count": "off"
|
||||
"visit": "off",
|
||||
"waitUntil": "off"
|
||||
}
|
||||
}
|
||||
|
||||
8
.github/workflows/linting.yml
vendored
8
.github/workflows/linting.yml
vendored
@ -61,11 +61,11 @@ jobs:
|
||||
|
||||
- name: ESLint (core)
|
||||
if: ${{ always() }}
|
||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern app/assets/javascripts
|
||||
run: yarn eslint app/assets/javascripts
|
||||
|
||||
- name: ESLint (core plugins)
|
||||
if: ${{ always() }}
|
||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern plugins/**/{test,assets}/javascripts
|
||||
run: yarn eslint plugins
|
||||
|
||||
- name: Prettier
|
||||
if: ${{ always() }}
|
||||
@ -73,9 +73,9 @@ jobs:
|
||||
yarn prettier -v
|
||||
yarn prettier --list-different \
|
||||
"app/assets/stylesheets/**/*.scss" \
|
||||
"app/assets/javascripts/**/*.{js,es6}" \
|
||||
"app/assets/javascripts/**/*.js" \
|
||||
"plugins/**/assets/stylesheets/**/*.scss" \
|
||||
"plugins/**/assets/javascripts/**/*.{js,es6}"
|
||||
"plugins/**/assets/javascripts/**/*.js"
|
||||
|
||||
- name: Ember template lint
|
||||
if: ${{ always() }}
|
||||
|
||||
7
Gemfile
7
Gemfile
@ -181,9 +181,14 @@ group :development do
|
||||
gem 'yaml-lint'
|
||||
end
|
||||
|
||||
group ENV["ALLOW_DEV_POPULATE"] == "1" ? :production : :development do
|
||||
if ENV["ALLOW_DEV_POPULATE"] == "1"
|
||||
gem 'discourse_dev_assets'
|
||||
gem 'faker', "~> 2.16"
|
||||
else
|
||||
group :development do
|
||||
gem 'discourse_dev_assets'
|
||||
gem 'faker', "~> 2.16"
|
||||
end
|
||||
end
|
||||
|
||||
# this is an optional gem, it provides a high performance replacement
|
||||
|
||||
56
Gemfile.lock
56
Gemfile.lock
@ -129,7 +129,7 @@ GEM
|
||||
sprockets (>= 3.3, < 4.1)
|
||||
ember-source (2.18.2)
|
||||
erubi (1.10.0)
|
||||
excon (0.87.0)
|
||||
excon (0.88.0)
|
||||
execjs (2.8.1)
|
||||
exifr (1.3.9)
|
||||
fabrication (2.22.0)
|
||||
@ -171,23 +171,23 @@ GEM
|
||||
hkdf (0.3.0)
|
||||
htmlentities (4.3.4)
|
||||
http_accept_language (2.1.1)
|
||||
i18n (1.8.10)
|
||||
i18n (1.8.11)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_optim (0.31.0)
|
||||
image_optim (0.31.1)
|
||||
exifr (~> 1.2, >= 1.2.2)
|
||||
fspath (~> 3.0)
|
||||
image_size (>= 1.5, < 3)
|
||||
image_size (>= 1.5, < 4)
|
||||
in_threads (~> 1.3)
|
||||
progress (~> 3.0, >= 3.0.1)
|
||||
image_size (2.1.2)
|
||||
image_size (3.0.1)
|
||||
in_threads (1.5.4)
|
||||
ipaddr (1.2.2)
|
||||
ipaddr (1.2.3)
|
||||
jmespath (1.4.0)
|
||||
jquery-rails (4.4.0)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.6.0)
|
||||
json (2.6.1)
|
||||
json-schema (2.8.1)
|
||||
addressable (>= 2.4)
|
||||
json_schemer (0.2.18)
|
||||
@ -197,12 +197,10 @@ GEM
|
||||
uri_template (~> 0.7)
|
||||
jwt (2.3.0)
|
||||
kgio (2.11.4)
|
||||
libv8-node (15.14.0.1)
|
||||
libv8-node (15.14.0.1-arm64-darwin-20)
|
||||
libv8-node (15.14.0.1-x86_64-darwin-18)
|
||||
libv8-node (15.14.0.1-x86_64-darwin-19)
|
||||
libv8-node (15.14.0.1-x86_64-darwin-20)
|
||||
libv8-node (15.14.0.1-x86_64-linux)
|
||||
libv8-node (16.10.0.0)
|
||||
libv8-node (16.10.0.0-x86_64-darwin)
|
||||
libv8-node (16.10.0.0-x86_64-darwin-19)
|
||||
libv8-node (16.10.0.0-x86_64-linux)
|
||||
listen (3.7.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
@ -215,7 +213,7 @@ GEM
|
||||
logstash-event (1.2.02)
|
||||
logstash-logger (0.26.1)
|
||||
logstash-event (~> 1.2)
|
||||
logster (2.9.7)
|
||||
logster (2.9.8)
|
||||
loofah (2.12.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
@ -228,8 +226,8 @@ GEM
|
||||
method_source (1.0.0)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.6.1)
|
||||
mini_racer (0.4.0)
|
||||
libv8-node (~> 15.14.0.0)
|
||||
mini_racer (0.5.0)
|
||||
libv8-node (~> 16.10.0.0)
|
||||
mini_scheduler (0.13.0)
|
||||
sidekiq (>= 4.2.3)
|
||||
mini_sql (1.1.3)
|
||||
@ -254,7 +252,7 @@ GEM
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.12.5-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
oauth (0.5.6)
|
||||
oauth (0.5.8)
|
||||
oauth2 (1.4.7)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
@ -265,7 +263,7 @@ GEM
|
||||
omniauth (1.9.1)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-facebook (8.0.0)
|
||||
omniauth-facebook (9.0.0)
|
||||
omniauth-oauth2 (~> 1.2)
|
||||
omniauth-github (1.4.0)
|
||||
omniauth (~> 1.5)
|
||||
@ -278,7 +276,7 @@ GEM
|
||||
omniauth-oauth (1.2.0)
|
||||
oauth
|
||||
omniauth (>= 1.0, < 3)
|
||||
omniauth-oauth2 (1.7.1)
|
||||
omniauth-oauth2 (1.7.2)
|
||||
oauth2 (~> 1.4)
|
||||
omniauth (>= 1.9, < 3)
|
||||
omniauth-twitter (1.4.0)
|
||||
@ -325,7 +323,7 @@ GEM
|
||||
activerecord (~> 6.0)
|
||||
concurrent-ruby
|
||||
railties (~> 6.0)
|
||||
rails_multisite (3.1.0)
|
||||
rails_multisite (4.0.0)
|
||||
activerecord (> 5.0, < 7)
|
||||
railties (> 5.0, < 7)
|
||||
railties (6.1.4.1)
|
||||
@ -381,7 +379,7 @@ GEM
|
||||
rspec-expectations (~> 3.10)
|
||||
rspec-mocks (~> 3.10)
|
||||
rspec-support (~> 3.10)
|
||||
rspec-support (3.10.2)
|
||||
rspec-support (3.10.3)
|
||||
rss (0.2.9)
|
||||
rexml
|
||||
rswag-specs (2.4.0)
|
||||
@ -389,7 +387,7 @@ GEM
|
||||
json-schema (~> 2.2)
|
||||
railties (>= 3.1, < 7.0)
|
||||
rtlit (0.0.5)
|
||||
rubocop (1.22.1)
|
||||
rubocop (1.22.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.0.0.0)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
@ -398,12 +396,12 @@ GEM
|
||||
rubocop-ast (>= 1.12.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.12.0)
|
||||
rubocop-ast (1.13.0)
|
||||
parser (>= 3.0.1.1)
|
||||
rubocop-discourse (2.4.2)
|
||||
rubocop (>= 1.1.0)
|
||||
rubocop-rspec (>= 2.0.0)
|
||||
rubocop-rspec (2.5.0)
|
||||
rubocop-rspec (2.6.0)
|
||||
rubocop (~> 1.19)
|
||||
ruby-prof (1.4.3)
|
||||
ruby-progressbar (1.11.0)
|
||||
@ -429,7 +427,7 @@ GEM
|
||||
activesupport (>= 3.1)
|
||||
shoulda-matchers (5.0.0)
|
||||
activesupport (>= 5.2.0)
|
||||
sidekiq (6.2.2)
|
||||
sidekiq (6.3.1)
|
||||
connection_pool (>= 2.2.2)
|
||||
rack (~> 2.0)
|
||||
redis (>= 4.2.0)
|
||||
@ -442,9 +440,9 @@ GEM
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.2)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets-rails (3.3.0)
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkey (2.0.0)
|
||||
stackprof (0.2.17)
|
||||
@ -473,7 +471,7 @@ GEM
|
||||
jwt (~> 2.0)
|
||||
xorcist (1.1.2)
|
||||
yaml-lint (0.0.10)
|
||||
zeitwerk (2.4.2)
|
||||
zeitwerk (2.5.1)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin-20
|
||||
|
||||
@ -33,7 +33,7 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_updateFormattedLogsFunc: function () {
|
||||
_updateFormattedLogsFunc() {
|
||||
const logs = this.logs;
|
||||
if (logs.length === 0) {
|
||||
return;
|
||||
@ -48,7 +48,7 @@ export default Component.extend({
|
||||
}
|
||||
// update the formatted logs & cache index
|
||||
this.setProperties({
|
||||
formattedLogs: formattedLogs,
|
||||
formattedLogs,
|
||||
index: logs.length,
|
||||
});
|
||||
// force rerender
|
||||
|
||||
@ -24,7 +24,7 @@ export default Component.extend({
|
||||
|
||||
const config = {
|
||||
type: this.type,
|
||||
data: data,
|
||||
data,
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
|
||||
@ -114,7 +114,7 @@ export default Component.extend({
|
||||
this.fieldAdded(this.currentTargetName, name);
|
||||
},
|
||||
|
||||
toggleMaximize: function () {
|
||||
toggleMaximize() {
|
||||
this.toggleProperty("maximized");
|
||||
next(() => this.appEvents.trigger("ace:resize"));
|
||||
},
|
||||
|
||||
@ -40,7 +40,7 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@observes("hexValue", "brightnessValue", "valid")
|
||||
hexValueChanged: function () {
|
||||
hexValueChanged() {
|
||||
const hex = this.hexValue;
|
||||
let text = this.element.querySelector("input.hex-input");
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
_addValue(value, secret) {
|
||||
this.collection.addObject({ key: value, secret: secret });
|
||||
this.collection.addObject({ key: value, secret });
|
||||
this._saveValues();
|
||||
},
|
||||
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
import UppyUploadMixin from "discourse/mixins/uppy-upload";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import bootbox from "bootbox";
|
||||
|
||||
export default Component.extend(UploadMixin, {
|
||||
export default Component.extend(UppyUploadMixin, {
|
||||
type: "csv",
|
||||
uploadUrl: "/tags/upload",
|
||||
addDisabled: alias("uploading"),
|
||||
elementId: "tag-uploader",
|
||||
preventDirectS3Uploads: true,
|
||||
|
||||
validateUploadedFilesOptions() {
|
||||
return { csvOnly: true };
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { and, gt } from "@ember/object/computed";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import { escape } from "pretty-text/sanitizer";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import { schedule } from "@ember/runloop";
|
||||
|
||||
const MAX_COMPONENTS = 4;
|
||||
|
||||
@ -22,36 +20,6 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.scheduleAnimation();
|
||||
},
|
||||
|
||||
@observes("theme.selected")
|
||||
triggerAnimation() {
|
||||
this.animate();
|
||||
},
|
||||
|
||||
scheduleAnimation() {
|
||||
schedule("afterRender", () => {
|
||||
this.animate(true);
|
||||
});
|
||||
},
|
||||
|
||||
animate(isInitial) {
|
||||
const $container = $(this.element);
|
||||
const $list = $(this.element.querySelector(".components-list"));
|
||||
if ($list.length === 0 || isTesting()) {
|
||||
return;
|
||||
}
|
||||
const duration = 300;
|
||||
if (this.get("theme.selected")) {
|
||||
this.collapseComponentsList($container, $list, duration);
|
||||
} else if (!isInitial) {
|
||||
this.expandComponentsList($container, $list, duration);
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"theme.component",
|
||||
"theme.childThemes.@each.name",
|
||||
@ -91,54 +59,6 @@ export default Component.extend({
|
||||
return childrenCount - MAX_COMPONENTS;
|
||||
},
|
||||
|
||||
expandComponentsList($container, $list, duration) {
|
||||
$container.css("height", `${$container.height()}px`);
|
||||
$list.css("display", "");
|
||||
$container.animate(
|
||||
{
|
||||
height: `${$container.height() + $list.outerHeight(true)}px`,
|
||||
},
|
||||
{
|
||||
duration,
|
||||
done: () => {
|
||||
$list.css("display", "");
|
||||
$container.css("height", "");
|
||||
},
|
||||
}
|
||||
);
|
||||
$list.animate(
|
||||
{
|
||||
opacity: 1,
|
||||
},
|
||||
{
|
||||
duration,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
collapseComponentsList($container, $list, duration) {
|
||||
$container.animate(
|
||||
{
|
||||
height: `${$container.height() - $list.outerHeight(true)}px`,
|
||||
},
|
||||
{
|
||||
duration,
|
||||
done: () => {
|
||||
$list.css("display", "none");
|
||||
$container.css("height", "");
|
||||
},
|
||||
}
|
||||
);
|
||||
$list.animate(
|
||||
{
|
||||
opacity: 0,
|
||||
},
|
||||
{
|
||||
duration,
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleChildrenExpanded() {
|
||||
this.toggleProperty("childrenExpanded");
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
import UppyUploadMixin from "discourse/mixins/uppy-upload";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import bootbox from "bootbox";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend(UploadMixin, {
|
||||
export default Component.extend(UppyUploadMixin, {
|
||||
type: "txt",
|
||||
classNames: "watched-words-uploader",
|
||||
uploadUrl: "/admin/customize/watched_words/upload",
|
||||
addDisabled: alias("uploading"),
|
||||
preventDirectS3Uploads: true,
|
||||
|
||||
validateUploadedFilesOptions() {
|
||||
return { skipValidation: true };
|
||||
|
||||
@ -1,14 +1,39 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Controller.extend({
|
||||
actions: {
|
||||
revokeKey(key) {
|
||||
key.revoke().catch(popupAjaxError);
|
||||
},
|
||||
loading: false,
|
||||
|
||||
undoRevokeKey(key) {
|
||||
key.undoRevoke().catch(popupAjaxError);
|
||||
},
|
||||
@action
|
||||
revokeKey(key) {
|
||||
key.revoke().catch(popupAjaxError);
|
||||
},
|
||||
|
||||
@action
|
||||
undoRevokeKey(key) {
|
||||
key.undoRevoke().catch(popupAjaxError);
|
||||
},
|
||||
|
||||
@action
|
||||
loadMore() {
|
||||
if (this.loading || this.model.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limit = 50;
|
||||
|
||||
this.set("loading", true);
|
||||
this.store
|
||||
.findAll("api-key", { offset: this.model.length, limit })
|
||||
.then((keys) => {
|
||||
this.model.addObjects(keys);
|
||||
if (keys.length < limit) {
|
||||
this.model.set("loaded", true);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.set("loading", false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -10,7 +10,8 @@ import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
export default Controller.extend({
|
||||
userModes: null,
|
||||
useGlobalKey: false,
|
||||
scopeModes: null,
|
||||
globalScopes: null,
|
||||
scopes: null,
|
||||
|
||||
init() {
|
||||
@ -20,6 +21,13 @@ export default Controller.extend({
|
||||
{ id: "all", name: I18n.t("admin.api.all_users") },
|
||||
{ id: "single", name: I18n.t("admin.api.single_user") },
|
||||
]);
|
||||
|
||||
this.set("scopeModes", [
|
||||
{ id: "granular", name: I18n.t("admin.api.scopes.granular") },
|
||||
{ id: "read_only", name: I18n.t("admin.api.scopes.read_only") },
|
||||
{ id: "global", name: I18n.t("admin.api.scopes.global") },
|
||||
]);
|
||||
|
||||
this._loadScopes();
|
||||
},
|
||||
|
||||
@ -49,14 +57,23 @@ export default Controller.extend({
|
||||
this.set("userMode", userMode);
|
||||
},
|
||||
|
||||
@action
|
||||
changeScopeMode(scopeMode) {
|
||||
this.set("scopeMode", scopeMode);
|
||||
},
|
||||
|
||||
@action
|
||||
save() {
|
||||
if (!this.useGlobalKey) {
|
||||
if (this.scopeMode === "granular") {
|
||||
const selectedScopes = Object.values(this.scopes)
|
||||
.flat()
|
||||
.filterBy("selected");
|
||||
|
||||
this.model.set("scopes", selectedScopes);
|
||||
} else if (this.scopeMode === "read_only") {
|
||||
this.model.set("scopes", [this.globalScopes.findBy("key", "read")]);
|
||||
} else if (this.scopeMode === "all") {
|
||||
this.model.set("scopes", null);
|
||||
}
|
||||
|
||||
return this.model.save().catch(popupAjaxError);
|
||||
@ -78,6 +95,10 @@ export default Controller.extend({
|
||||
_loadScopes() {
|
||||
return ajax("/admin/api/keys/scopes.json")
|
||||
.then((data) => {
|
||||
// remove global scopes because there is a different dropdown
|
||||
this.set("globalScopes", data.scopes.global);
|
||||
delete data.scopes.global;
|
||||
|
||||
this.set("scopes", data.scopes);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
|
||||
@ -12,6 +12,9 @@ export default Controller.extend({
|
||||
uploadLabel: i18n("admin.backups.upload.label"),
|
||||
backupLocation: setting("backup_location"),
|
||||
localBackupStorage: equal("backupLocation", "local"),
|
||||
enableExperimentalBackupUploader: setting(
|
||||
"enable_experimental_backup_uploader"
|
||||
),
|
||||
|
||||
@discourseComputed("status.allowRestore", "status.isOperationRunning")
|
||||
restoreTitle(allowRestore, isOperationRunning) {
|
||||
|
||||
@ -70,7 +70,7 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||
},
|
||||
|
||||
@observes("model.id")
|
||||
_resetSaving: function () {
|
||||
_resetSaving() {
|
||||
this.set("saving", false);
|
||||
this.set("savingStatus", "");
|
||||
},
|
||||
|
||||
@ -15,11 +15,11 @@ export default Controller.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
revert: function (color) {
|
||||
revert(color) {
|
||||
color.revert();
|
||||
},
|
||||
|
||||
undo: function (color) {
|
||||
undo(color) {
|
||||
color.undo();
|
||||
},
|
||||
|
||||
@ -68,7 +68,7 @@ export default Controller.extend({
|
||||
});
|
||||
},
|
||||
|
||||
save: function () {
|
||||
save() {
|
||||
this.model.save();
|
||||
},
|
||||
|
||||
@ -76,7 +76,7 @@ export default Controller.extend({
|
||||
this.model.updateUserSelectable(this.get("model.user_selectable"));
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
destroy() {
|
||||
const model = this.model;
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.customize.colors.delete_confirm"),
|
||||
|
||||
@ -12,7 +12,7 @@ export default Controller.extend({
|
||||
editRouteName: "adminCustomizeThemes.edit",
|
||||
showRouteName: "adminCustomizeThemes.show",
|
||||
|
||||
setTargetName: function (name) {
|
||||
setTargetName(name) {
|
||||
const target = this.get("model.targets").find((t) => t.name === name);
|
||||
this.set("currentTarget", target && target.id);
|
||||
},
|
||||
|
||||
@ -19,7 +19,7 @@ export default Controller.extend({
|
||||
@method testEmailAddressChanged
|
||||
**/
|
||||
@observes("testEmailAddress")
|
||||
testEmailAddressChanged: function () {
|
||||
testEmailAddressChanged() {
|
||||
this.set("sentTestEmail", false);
|
||||
},
|
||||
|
||||
@ -29,7 +29,7 @@ export default Controller.extend({
|
||||
|
||||
@method sendTestEmail
|
||||
**/
|
||||
sendTestEmail: function () {
|
||||
sendTestEmail() {
|
||||
this.setProperties({
|
||||
sendingEmail: true,
|
||||
sentTestEmail: false,
|
||||
|
||||
@ -121,7 +121,7 @@ export default Controller.extend({
|
||||
},
|
||||
|
||||
filterBySubject(subject) {
|
||||
this.changeFilters({ subject: subject });
|
||||
this.changeFilters({ subject });
|
||||
},
|
||||
|
||||
exportStaffActionLogs() {
|
||||
|
||||
@ -37,7 +37,7 @@ export default Controller.extend({
|
||||
textArea.remove();
|
||||
},
|
||||
|
||||
destroy: function (record) {
|
||||
destroy(record) {
|
||||
return bootbox.confirm(
|
||||
I18n.t("admin.permalink.delete_confirm"),
|
||||
I18n.t("no_value"),
|
||||
|
||||
@ -3,7 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Controller.extend({
|
||||
@discourseComputed
|
||||
adminRoutes: function () {
|
||||
adminRoutes() {
|
||||
return this.model
|
||||
.map((p) => {
|
||||
if (p.get("enabled")) {
|
||||
|
||||
@ -49,7 +49,7 @@ export default Controller.extend(GrantBadgeController, {
|
||||
let result = {
|
||||
badge: badges[0].badge,
|
||||
granted_at: lastGranted,
|
||||
badges: badges,
|
||||
badges,
|
||||
count: badges.length,
|
||||
grouped: true,
|
||||
};
|
||||
@ -61,7 +61,7 @@ export default Controller.extend(GrantBadgeController, {
|
||||
},
|
||||
|
||||
actions: {
|
||||
expandGroup: function (userBadge) {
|
||||
expandGroup(userBadge) {
|
||||
const model = this.model;
|
||||
model.set("expandedBadges", model.get("expandedBadges") || []);
|
||||
model.get("expandedBadges").pushObject(userBadge.badge.id);
|
||||
|
||||
@ -592,7 +592,7 @@ export default Controller.extend(CanCheckEmails, {
|
||||
(deletedPosts * 100) / user.get("post_count")
|
||||
);
|
||||
progressModal.setProperties({
|
||||
deletedPercentage: deletedPercentage,
|
||||
deletedPercentage,
|
||||
});
|
||||
performDelete(progressModal);
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { ajax } from "discourse/lib/ajax";
|
||||
const EmailSettings = EmberObject.extend({});
|
||||
|
||||
EmailSettings.reopenClass({
|
||||
find: function () {
|
||||
find() {
|
||||
return ajax("/admin/email.json").then(function (settings) {
|
||||
return EmailSettings.create(settings);
|
||||
});
|
||||
|
||||
@ -5,7 +5,7 @@ import { ajax } from "discourse/lib/ajax";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
const Permalink = EmberObject.extend({
|
||||
save: function () {
|
||||
save() {
|
||||
return ajax("/admin/permalinks.json", {
|
||||
type: "POST",
|
||||
data: {
|
||||
@ -17,16 +17,16 @@ const Permalink = EmberObject.extend({
|
||||
},
|
||||
|
||||
@discourseComputed("category_id")
|
||||
category: function (category_id) {
|
||||
category(category_id) {
|
||||
return Category.findById(category_id);
|
||||
},
|
||||
|
||||
@discourseComputed("external_url")
|
||||
linkIsExternal: function (external_url) {
|
||||
linkIsExternal(external_url) {
|
||||
return !DiscourseURL.isInternal(external_url);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
destroy() {
|
||||
return ajax("/admin/permalinks/" + this.id + ".json", {
|
||||
type: "DELETE",
|
||||
});
|
||||
@ -34,12 +34,12 @@ const Permalink = EmberObject.extend({
|
||||
});
|
||||
|
||||
Permalink.reopenClass({
|
||||
findAll: function (filter) {
|
||||
return ajax("/admin/permalinks.json", { data: { filter: filter } }).then(
|
||||
function (permalinks) {
|
||||
return permalinks.map((p) => Permalink.create(p));
|
||||
}
|
||||
);
|
||||
findAll(filter) {
|
||||
return ajax("/admin/permalinks.json", { data: { filter } }).then(function (
|
||||
permalinks
|
||||
) {
|
||||
return permalinks.map((p) => Permalink.create(p));
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -672,7 +672,7 @@ Report.reopenClass({
|
||||
Report.fillMissingDates(json.report);
|
||||
}
|
||||
|
||||
const model = Report.create({ type: type });
|
||||
const model = Report.create({ type });
|
||||
model.setProperties(json.report);
|
||||
|
||||
if (json.report.related_report) {
|
||||
|
||||
@ -9,7 +9,7 @@ const ScreenedEmail = EmberObject.extend({
|
||||
return I18n.t("admin.logs.screened_actions." + action);
|
||||
},
|
||||
|
||||
clearBlock: function () {
|
||||
clearBlock() {
|
||||
return ajax("/admin/logs/screened_emails/" + this.id, {
|
||||
type: "DELETE",
|
||||
});
|
||||
@ -17,7 +17,7 @@ const ScreenedEmail = EmberObject.extend({
|
||||
});
|
||||
|
||||
ScreenedEmail.reopenClass({
|
||||
findAll: function () {
|
||||
findAll() {
|
||||
return ajax("/admin/logs/screened_emails.json").then(function (
|
||||
screened_emails
|
||||
) {
|
||||
|
||||
@ -42,7 +42,7 @@ const ScreenedIpAddress = EmberObject.extend({
|
||||
ScreenedIpAddress.reopenClass({
|
||||
findAll(filter) {
|
||||
return ajax("/admin/logs/screened_ip_addresses.json", {
|
||||
data: { filter: filter },
|
||||
data: { filter },
|
||||
}).then((screened_ips) =>
|
||||
screened_ips.map((b) => ScreenedIpAddress.create(b))
|
||||
);
|
||||
|
||||
@ -11,7 +11,7 @@ const ScreenedUrl = EmberObject.extend({
|
||||
});
|
||||
|
||||
ScreenedUrl.reopenClass({
|
||||
findAll: function () {
|
||||
findAll() {
|
||||
return ajax("/admin/logs/screened_urls.json").then(function (
|
||||
screened_urls
|
||||
) {
|
||||
|
||||
@ -44,7 +44,7 @@ export default RestModel.extend({
|
||||
},
|
||||
|
||||
groupFinder(term) {
|
||||
return Group.findAll({ term: term, ignore_automatic: false });
|
||||
return Group.findAll({ term, ignore_automatic: false });
|
||||
},
|
||||
|
||||
@discourseComputed("wildcard_web_hook", "web_hook_event_types.[]")
|
||||
|
||||
@ -32,7 +32,7 @@ export default DiscourseRoute.extend({
|
||||
});
|
||||
|
||||
controller.setProperties({
|
||||
badgeGroupings: badgeGroupings,
|
||||
badgeGroupings,
|
||||
badgeTypes: json.badge_types,
|
||||
protectedSystemFields: json.admin_badges.protected_system_fields,
|
||||
badgeTriggers,
|
||||
|
||||
@ -2,6 +2,7 @@ import { COMPONENTS, THEMES } from "admin/models/theme";
|
||||
import I18n from "I18n";
|
||||
import Route from "@ember/routing/route";
|
||||
import { scrollTop } from "discourse/mixins/scroll-top";
|
||||
import bootbox from "bootbox";
|
||||
|
||||
export function showUnassignedComponentWarning(theme, callback) {
|
||||
bootbox.confirm(
|
||||
@ -39,8 +40,8 @@ export default Route.extend({
|
||||
});
|
||||
|
||||
controller.setProperties({
|
||||
model: model,
|
||||
parentController: parentController,
|
||||
model,
|
||||
parentController,
|
||||
allThemes: parentController.get("model"),
|
||||
colorSchemeId: model.get("color_scheme_id"),
|
||||
colorSchemes: parentController.get("model.extras.color_schemes"),
|
||||
|
||||
@ -3,7 +3,7 @@ import EmberObject from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model: function () {
|
||||
model() {
|
||||
return ajax("/admin/customize/emojis.json").then(function (emojis) {
|
||||
return emojis.map(function (emoji) {
|
||||
return EmberObject.create(emoji);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
redirect: function () {
|
||||
redirect() {
|
||||
this.transitionTo("adminLogs.staffActionLogs");
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
renderTemplate: function () {
|
||||
renderTemplate() {
|
||||
this.render("admin/templates/logs/screened-emails", { into: "adminLogs" });
|
||||
},
|
||||
|
||||
setupController: function () {
|
||||
setupController() {
|
||||
return this.controllerFor("adminLogsScreenedEmails").show();
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
renderTemplate: function () {
|
||||
renderTemplate() {
|
||||
this.render("admin/templates/logs/screened-urls", { into: "adminLogs" });
|
||||
},
|
||||
|
||||
setupController: function () {
|
||||
setupController() {
|
||||
return this.controllerFor("adminLogsScreenedUrls").show();
|
||||
},
|
||||
});
|
||||
|
||||
@ -24,7 +24,7 @@ export default Route.extend({
|
||||
controller.setProperties({
|
||||
siteText,
|
||||
saved: false,
|
||||
localeFullName: localeFullName,
|
||||
localeFullName,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -2,11 +2,11 @@ import DiscourseRoute from "discourse/routes/discourse";
|
||||
import UserField from "admin/models/user-field";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model: function () {
|
||||
model() {
|
||||
return this.store.findAll("user-field");
|
||||
},
|
||||
|
||||
setupController: function (controller, model) {
|
||||
setupController(controller, model) {
|
||||
controller.setProperties({ model, fieldTypes: UserField.fieldTypes() });
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
redirect: function () {
|
||||
redirect() {
|
||||
this.transitionTo("adminUsersList");
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
beforeModel: function () {
|
||||
beforeModel() {
|
||||
this.transitionTo("adminUsersList.show", "active");
|
||||
},
|
||||
});
|
||||
|
||||
@ -5,67 +5,71 @@
|
||||
label="admin.api.new_key"}}
|
||||
|
||||
{{#if model}}
|
||||
<table class="api-keys grid">
|
||||
<thead>
|
||||
<th>{{i18n "admin.api.key"}}</th>
|
||||
<th>{{i18n "admin.api.description"}}</th>
|
||||
<th>{{i18n "admin.api.user"}}</th>
|
||||
<th>{{i18n "admin.api.created"}}</th>
|
||||
<th>{{i18n "admin.api.last_used"}}</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each model as |k|}}
|
||||
<tr class={{if k.revoked_at "revoked"}}>
|
||||
<td class="key">
|
||||
{{#if k.revoked_at}}{{d-icon "times-circle"}}{{/if}}
|
||||
{{k.truncatedKey}}
|
||||
</td>
|
||||
<td class="key-description">
|
||||
{{k.shortDescription}}
|
||||
</td>
|
||||
<td class="key-user">
|
||||
<div class="label">{{i18n "admin.api.user"}}</div>
|
||||
{{#if k.user}}
|
||||
{{#link-to "adminUser" k.user}}
|
||||
{{avatar k.user imageSize="small"}}
|
||||
{{/link-to}}
|
||||
{{else}}
|
||||
{{i18n "admin.api.all_users"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="key-created">
|
||||
<div class="label">{{i18n "admin.api.created"}}</div>
|
||||
{{format-date k.created_at}}
|
||||
</td>
|
||||
<td class="key-last-used">
|
||||
<div class="label">{{i18n "admin.api.last_used"}}</div>
|
||||
{{#if k.last_used_at}}
|
||||
{{format-date k.last_used_at}}
|
||||
{{else}}
|
||||
{{i18n "admin.api.never_used"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="key-controls">
|
||||
{{d-button action=(route-action "show" k) icon="far-eye" title="admin.api.show_details"}}
|
||||
{{#if k.revoked_at}}
|
||||
{{d-button
|
||||
action=(action "undoRevokeKey")
|
||||
actionParam=k icon="undo"
|
||||
title="admin.api.undo_revoke"}}
|
||||
{{else}}
|
||||
{{d-button
|
||||
class="btn-danger"
|
||||
action=(action "revokeKey")
|
||||
actionParam=k
|
||||
icon="times"
|
||||
title="admin.api.revoke"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{#load-more selector=".api-keys tr" action=(action "loadMore")}}
|
||||
<table class="api-keys grid">
|
||||
<thead>
|
||||
<th>{{i18n "admin.api.key"}}</th>
|
||||
<th>{{i18n "admin.api.description"}}</th>
|
||||
<th>{{i18n "admin.api.user"}}</th>
|
||||
<th>{{i18n "admin.api.created"}}</th>
|
||||
<th>{{i18n "admin.api.last_used"}}</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each model as |k|}}
|
||||
<tr class={{if k.revoked_at "revoked"}}>
|
||||
<td class="key">
|
||||
{{#if k.revoked_at}}{{d-icon "times-circle"}}{{/if}}
|
||||
{{k.truncatedKey}}
|
||||
</td>
|
||||
<td class="key-description">
|
||||
{{k.shortDescription}}
|
||||
</td>
|
||||
<td class="key-user">
|
||||
<div class="label">{{i18n "admin.api.user"}}</div>
|
||||
{{#if k.user}}
|
||||
{{#link-to "adminUser" k.user}}
|
||||
{{avatar k.user imageSize="small"}}
|
||||
{{/link-to}}
|
||||
{{else}}
|
||||
{{i18n "admin.api.all_users"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="key-created">
|
||||
<div class="label">{{i18n "admin.api.created"}}</div>
|
||||
{{format-date k.created_at}}
|
||||
</td>
|
||||
<td class="key-last-used">
|
||||
<div class="label">{{i18n "admin.api.last_used"}}</div>
|
||||
{{#if k.last_used_at}}
|
||||
{{format-date k.last_used_at}}
|
||||
{{else}}
|
||||
{{i18n "admin.api.never_used"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="key-controls">
|
||||
{{d-button action=(route-action "show" k) icon="far-eye" title="admin.api.show_details"}}
|
||||
{{#if k.revoked_at}}
|
||||
{{d-button
|
||||
action=(action "undoRevokeKey")
|
||||
actionParam=k icon="undo"
|
||||
title="admin.api.undo_revoke"}}
|
||||
{{else}}
|
||||
{{d-button
|
||||
class="btn-danger"
|
||||
action=(action "revokeKey")
|
||||
actionParam=k
|
||||
icon="times"
|
||||
title="admin.api.revoke"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{/load-more}}
|
||||
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
{{else}}
|
||||
<p>{{i18n "admin.api.none"}}</p>
|
||||
{{/if}}
|
||||
|
||||
@ -36,12 +36,18 @@
|
||||
{{/admin-form-row}}
|
||||
{{/if}}
|
||||
|
||||
{{#admin-form-row label="admin.api.use_global_key"}}
|
||||
{{input type="checkbox" checked=useGlobalKey}}
|
||||
{{#admin-form-row label="admin.api.scope_mode"}}
|
||||
{{combo-box content=scopeModes value=scopeMode onChange=(action "changeScopeMode")}}
|
||||
|
||||
{{#if (eq scopeMode "read_only")}}
|
||||
<p>{{i18n "admin.api.scopes.descriptions.global.read"}}</p>
|
||||
{{else if (eq scopeMode "global")}}
|
||||
<p>{{i18n "admin.api.scopes.global_description"}}</p>
|
||||
{{/if}}
|
||||
{{/admin-form-row}}
|
||||
|
||||
{{#unless useGlobalKey}}
|
||||
<div class="scopes-title">{{i18n "admin.api.scopes.title"}}</div>
|
||||
{{#if (eq scopeMode "granular")}}
|
||||
<h2 class="scopes-title">{{i18n "admin.api.scopes.title"}}</h2>
|
||||
<p>{{i18n "admin.api.scopes.description"}}</p>
|
||||
<table class="scopes-table grid">
|
||||
<thead>
|
||||
@ -82,7 +88,7 @@
|
||||
{{/each-in}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
{{d-button icon="check" label="admin.api.save" action=(action "save") class="btn-primary" disabled=saveDisabled}}
|
||||
{{/if}}
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
{{/admin-form-row}}
|
||||
|
||||
{{#if model.api_key_scopes.length}}
|
||||
<div class="scopes-title">{{i18n "admin.api.scopes.title"}}</div>
|
||||
<h2 class="scopes-title">{{i18n "admin.api.scopes.title"}}</h2>
|
||||
|
||||
<table class="scopes-table grid">
|
||||
<thead>
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
title="admin.backups.upload.title"
|
||||
class="btn-default"}}
|
||||
{{else}}
|
||||
{{backup-uploader done=(route-action "remoteUploadSuccess")}}
|
||||
{{#if enableExperimentalBackupUploader}}
|
||||
{{uppy-backup-uploader done=(route-action "remoteUploadSuccess")}}
|
||||
{{else}}
|
||||
{{backup-uploader done=(route-action "remoteUploadSuccess")}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if site.isReadOnly}}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
class="watched-word-input-field"
|
||||
tags=selectedTags
|
||||
onChange=(action "changeSelectedTags")
|
||||
everyTag=true
|
||||
options=(hash
|
||||
allowAny=true
|
||||
disabled=formSubmitted
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
{{emoji-uploader
|
||||
emojiGroups=emojiGroups
|
||||
done=(action "emojiUploaded")
|
||||
id="emoji-uploader"
|
||||
}}
|
||||
|
||||
<hr>
|
||||
|
||||
@ -1,17 +1,21 @@
|
||||
<div class="permalink-title">
|
||||
<h2>{{i18n "admin.permalink.title"}}</h2>
|
||||
</div>
|
||||
<h1>{{i18n "admin.permalink.title"}}</h1>
|
||||
|
||||
<div class="permalink-description">
|
||||
<span>{{i18n "admin.permalink.description"}}</span>
|
||||
</div>
|
||||
<div class="permalink-search">
|
||||
{{text-field value=filter class="url-input" placeholderKey="admin.permalink.form.filter" autocorrect="off" autocapitalize="off"}}
|
||||
</div>
|
||||
{{permalink-form action=(action "recordAdded")}}
|
||||
<br>
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
{{#if model.length}}
|
||||
<div class="permalink-search">
|
||||
{{text-field
|
||||
value=filter
|
||||
class="url-input"
|
||||
placeholderKey="admin.permalink.form.filter"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
}}
|
||||
</div>
|
||||
<table class="admin-logs-table permalinks grid">
|
||||
<thead class="heading-container">
|
||||
<th class="col heading first url">{{i18n "admin.permalink.url"}}</th>
|
||||
@ -21,7 +25,14 @@
|
||||
<tbody>
|
||||
{{#each model as |pl|}}
|
||||
<tr class="admin-list-item">
|
||||
<td class="col first url">{{d-button title="admin.permalink.copy_to_clipboard" icon="far-clipboard" action=(action "copyUrl" pl)}} <span id="admin-permalink-{{pl.id}}" title={{pl.url}}>{{pl.url}}</span></td>
|
||||
<td class="col first url">
|
||||
{{flat-button
|
||||
title="admin.permalink.copy_to_clipboard"
|
||||
icon="far-clipboard"
|
||||
action=(action "copyUrl" pl)
|
||||
}}
|
||||
<span id="admin-permalink-{{pl.id}}" title={{pl.url}}>{{pl.url}}</span>
|
||||
</td>
|
||||
<td class="col destination">
|
||||
{{#if pl.topic_id}}
|
||||
<a href={{pl.topic_url}}>{{pl.topic_title}}</a>
|
||||
|
||||
@ -9,7 +9,11 @@
|
||||
icon="download"
|
||||
label="admin.watched_words.download"}}
|
||||
|
||||
{{watched-word-uploader uploading=uploading actionKey=actionNameKey done=(action "uploadComplete")}}
|
||||
{{watched-word-uploader
|
||||
id="watched-word-uploader"
|
||||
uploading=uploading
|
||||
actionKey=actionNameKey
|
||||
done=(action "uploadComplete")}}
|
||||
|
||||
{{d-button
|
||||
class="watched-word-test"
|
||||
|
||||
@ -20,6 +20,8 @@ const REPLACEMENTS = {
|
||||
"d-drop-collapsed": "caret-right",
|
||||
"d-unliked": "far-heart",
|
||||
"d-liked": "heart",
|
||||
"d-post-share": "link",
|
||||
"d-topic-share": "link",
|
||||
"notification.mentioned": "at",
|
||||
"notification.group_mentioned": "users",
|
||||
"notification.quoted": "quote-right",
|
||||
|
||||
@ -11,7 +11,7 @@ export default function decoratorAlias(fn, errorMessage) {
|
||||
enumerable: desc.enumerable,
|
||||
configurable: desc.configurable,
|
||||
writable: desc.writable,
|
||||
initializer: function () {
|
||||
initializer() {
|
||||
let value = extractValue(desc);
|
||||
return fn.apply(null, params.concat(value));
|
||||
},
|
||||
|
||||
@ -50,7 +50,7 @@ export function readOnly(target, name, desc) {
|
||||
writable: false,
|
||||
enumerable: desc.enumerable,
|
||||
configurable: desc.configurable,
|
||||
initializer: function () {
|
||||
initializer() {
|
||||
let value = extractValue(desc);
|
||||
return value.readOnly();
|
||||
},
|
||||
|
||||
@ -6,7 +6,7 @@ export default function handleDescriptor(target, key, desc, params = []) {
|
||||
enumerable: desc.enumerable,
|
||||
configurable: desc.configurable,
|
||||
writeable: desc.writeable,
|
||||
initializer: function () {
|
||||
initializer() {
|
||||
let computedDescriptor;
|
||||
|
||||
if (desc.writable) {
|
||||
|
||||
@ -5,7 +5,7 @@ function handleDescriptor(target, property, desc, fn, params = []) {
|
||||
enumerable: desc.enumerable,
|
||||
configurable: desc.configurable,
|
||||
writable: desc.writable,
|
||||
initializer: function () {
|
||||
initializer() {
|
||||
return fn(...params);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import Component from "@ember/component";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
import UppyUploadMixin from "discourse/mixins/uppy-upload";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend(UploadMixin, {
|
||||
export default Component.extend(UppyUploadMixin, {
|
||||
type: "avatar",
|
||||
tagName: "span",
|
||||
imageIsNotASquare: false,
|
||||
|
||||
@ -19,7 +19,7 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@observes("topicList.[]")
|
||||
_topicListChanged: function () {
|
||||
_topicListChanged() {
|
||||
this._initFromTopicList(this.topicList);
|
||||
},
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import I18n from "I18n";
|
||||
import { Promise } from "rsvp";
|
||||
import { action } from "@ember/object";
|
||||
import bootbox from "bootbox";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { openBookmarkModal } from "discourse/controllers/bookmark";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["bookmark-list-wrapper"],
|
||||
@ -19,6 +19,11 @@ export default Component.extend({
|
||||
bookmark
|
||||
.destroy()
|
||||
.then(() => {
|
||||
this.appEvents.trigger(
|
||||
"bookmarks:changed",
|
||||
null,
|
||||
bookmark.attachedTo()
|
||||
);
|
||||
this._removeBookmarkFromList(bookmark);
|
||||
resolve(true);
|
||||
})
|
||||
@ -51,17 +56,19 @@ export default Component.extend({
|
||||
|
||||
@action
|
||||
editBookmark(bookmark) {
|
||||
let controller = showModal("bookmark", {
|
||||
model: {
|
||||
postId: bookmark.post_id,
|
||||
id: bookmark.id,
|
||||
reminderAt: bookmark.reminder_at,
|
||||
name: bookmark.name,
|
||||
openBookmarkModal(bookmark, {
|
||||
onAfterSave: (savedData) => {
|
||||
this.appEvents.trigger(
|
||||
"bookmarks:changed",
|
||||
savedData,
|
||||
bookmark.attachedTo()
|
||||
);
|
||||
this.reload();
|
||||
},
|
||||
onAfterDelete: () => {
|
||||
this.reload();
|
||||
},
|
||||
title: "post.bookmarks.edit",
|
||||
modalClass: "bookmark-with-reminder",
|
||||
});
|
||||
controller.set("afterSave", this.reload);
|
||||
},
|
||||
|
||||
@action
|
||||
|
||||
@ -17,7 +17,7 @@ import { TIME_SHORTCUT_TYPES } from "discourse/lib/time-shortcut";
|
||||
import { action } from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { bind, on } from "discourse-common/utils/decorators";
|
||||
import { formattedReminderTime } from "discourse/lib/bookmark";
|
||||
import { and, notEmpty } from "@ember/object/computed";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
@ -65,7 +65,7 @@ export default Component.extend({
|
||||
_itsatrap: new ItsATrap(),
|
||||
});
|
||||
|
||||
this.registerOnCloseHandler(this._onModalClose.bind(this));
|
||||
this.registerOnCloseHandler(this._onModalClose);
|
||||
|
||||
this._loadBookmarkOptions();
|
||||
this._bindKeyboardShortcuts();
|
||||
@ -201,6 +201,7 @@ export default Component.extend({
|
||||
post_id: this.model.postId,
|
||||
id: this.model.id || response.id,
|
||||
name: this.model.name,
|
||||
topic_id: this.model.topicId,
|
||||
});
|
||||
},
|
||||
|
||||
@ -238,12 +239,15 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
_onModalClose(initiatedByCloseButton) {
|
||||
@bind
|
||||
_onModalClose(closeOpts) {
|
||||
// we want to close without saving if the user already saved
|
||||
// manually or deleted the bookmark, as well as when the modal
|
||||
// is just closed with the X button
|
||||
this._closeWithoutSaving =
|
||||
this._closeWithoutSaving || initiatedByCloseButton;
|
||||
this._closeWithoutSaving ||
|
||||
closeOpts.initiatedByCloseButton ||
|
||||
closeOpts.initiatedByESC;
|
||||
|
||||
if (!this._closeWithoutSaving && !this._savingBookmarkManually) {
|
||||
this._saveBookmark().catch((e) => this._handleSaveError(e));
|
||||
|
||||
@ -207,7 +207,6 @@ export default Component.extend(KeyEnterEscape, {
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.appEvents.off("composer:resize", this, this.resize);
|
||||
if (this._visualViewportResizing()) {
|
||||
window.visualViewport.removeEventListener("resize", this.viewportResize);
|
||||
}
|
||||
|
||||
@ -38,6 +38,18 @@ import { loadOneboxes } from "discourse/lib/load-oneboxes";
|
||||
import putCursorAtEnd from "discourse/lib/put-cursor-at-end";
|
||||
import userSearch from "discourse/lib/user-search";
|
||||
|
||||
// original string ``
|
||||
// group 1 `image|foo=bar`
|
||||
// group 2 `690x220`
|
||||
// group 3 `, 50%`
|
||||
// group 4 '|bar=baz'
|
||||
// group 5 'upload://1TjaobgKObzpU7xRMw2HuUc87vO.png "image title"'
|
||||
|
||||
// Notes:
|
||||
// Group 3 is optional. group 4 can match images with or without a markdown title.
|
||||
// All matches are whitespace tolerant as long it's still valid markdown.
|
||||
// If the image is inside a code block, we'll ignore it `(?!(.*`))`.
|
||||
const IMAGE_MARKDOWN_REGEX = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
|
||||
const REBUILD_SCROLL_MAP_EVENTS = ["composer:resized", "composer:typed-reply"];
|
||||
|
||||
let uploadHandlers = [];
|
||||
@ -48,7 +60,14 @@ export function addComposerUploadHandler(extensions, method) {
|
||||
});
|
||||
}
|
||||
export function cleanUpComposerUploadHandler() {
|
||||
uploadHandlers = [];
|
||||
// we cannot set this to uploadHandlers = [] because that messes with
|
||||
// the references to the original array that the component has. this only
|
||||
// really affects tests, but without doing this you could addComposerUploadHandler
|
||||
// in a beforeEach function in a test but then it's not adding to the
|
||||
// existing reference that the component has, because an earlier test ran
|
||||
// cleanUpComposerUploadHandler and lost it. setting the length to 0 empties
|
||||
// the array but keeps the reference
|
||||
uploadHandlers.length = 0;
|
||||
}
|
||||
|
||||
let uploadProcessorQueue = [];
|
||||
@ -499,41 +518,40 @@ export default Component.extend(ComposerUpload, {
|
||||
$input.stop(true).animate({ scrollTop }, 100, "linear");
|
||||
},
|
||||
|
||||
_renderUnseenMentions($preview, unseen) {
|
||||
_renderUnseenMentions(preview, unseen) {
|
||||
// 'Create a New Topic' scenario is not supported (per conversation with codinghorror)
|
||||
// https://meta.discourse.org/t/taking-another-1-7-release-task/51986/7
|
||||
fetchUnseenMentions(unseen, this.get("composer.topic.id")).then(() => {
|
||||
linkSeenMentions($preview, this.siteSettings);
|
||||
this._warnMentionedGroups($preview);
|
||||
this._warnCannotSeeMention($preview);
|
||||
linkSeenMentions(preview, this.siteSettings);
|
||||
this._warnMentionedGroups(preview);
|
||||
this._warnCannotSeeMention(preview);
|
||||
});
|
||||
},
|
||||
|
||||
_renderUnseenHashtags($preview) {
|
||||
const unseen = linkSeenHashtags($preview);
|
||||
_renderUnseenHashtags(preview) {
|
||||
const unseen = linkSeenHashtags(preview);
|
||||
if (unseen.length > 0) {
|
||||
fetchUnseenHashtags(unseen).then(() => {
|
||||
linkSeenHashtags($preview);
|
||||
linkSeenHashtags(preview);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_warnMentionedGroups($preview) {
|
||||
_warnMentionedGroups(preview) {
|
||||
schedule("afterRender", () => {
|
||||
let found = this.warnedGroupMentions || [];
|
||||
$preview.find(".mention-group.notify").each((idx, e) => {
|
||||
if (this._isInQuote(e)) {
|
||||
preview?.querySelectorAll(".mention-group.notify")?.forEach((mention) => {
|
||||
if (this._isInQuote(mention)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $e = $(e);
|
||||
let name = $e.data("name");
|
||||
let name = mention.dataset.name;
|
||||
if (found.indexOf(name) === -1) {
|
||||
this.groupsMentioned([
|
||||
{
|
||||
name: name,
|
||||
user_count: $e.data("mentionable-user-count"),
|
||||
max_mentions: $e.data("max-mentions"),
|
||||
name,
|
||||
user_count: mention.dataset.mentionableUserCount,
|
||||
max_mentions: mention.dataset.maxMentions,
|
||||
},
|
||||
]);
|
||||
found.push(name);
|
||||
@ -544,7 +562,7 @@ export default Component.extend(ComposerUpload, {
|
||||
});
|
||||
},
|
||||
|
||||
_warnCannotSeeMention($preview) {
|
||||
_warnCannotSeeMention(preview) {
|
||||
const composerDraftKey = this.get("composer.draftKey");
|
||||
|
||||
if (composerDraftKey === Composer.NEW_PRIVATE_MESSAGE_KEY) {
|
||||
@ -554,9 +572,8 @@ export default Component.extend(ComposerUpload, {
|
||||
schedule("afterRender", () => {
|
||||
let found = this.warnedCannotSeeMentions || [];
|
||||
|
||||
$preview.find(".mention.cannot-see").each((idx, e) => {
|
||||
const $e = $(e);
|
||||
let name = $e.data("name");
|
||||
preview?.querySelectorAll(".mention.cannot-see")?.forEach((mention) => {
|
||||
let name = mention.dataset.name;
|
||||
|
||||
if (found.indexOf(name) === -1) {
|
||||
// add a delay to allow for typing, so you don't open the warning right away
|
||||
@ -565,8 +582,9 @@ export default Component.extend(ComposerUpload, {
|
||||
this,
|
||||
() => {
|
||||
if (
|
||||
$preview.find('.mention.cannot-see[data-name="' + name + '"]')
|
||||
.length > 0
|
||||
preview?.querySelectorAll(
|
||||
`.mention.cannot-see[data-name="${name}"]`
|
||||
)?.length > 0
|
||||
) {
|
||||
this.cannotSeeMention([{ name }]);
|
||||
found.push(name);
|
||||
@ -582,24 +600,15 @@ export default Component.extend(ComposerUpload, {
|
||||
},
|
||||
|
||||
_registerImageScaleButtonClick($preview) {
|
||||
// original string ``
|
||||
// group 1 `image|foo=bar`
|
||||
// group 2 `690x220`
|
||||
// group 3 `, 50%`
|
||||
// group 4 '|bar=baz'
|
||||
// group 5 'upload://1TjaobgKObzpU7xRMw2HuUc87vO.png "image title"'
|
||||
|
||||
// Notes:
|
||||
// Group 3 is optional. group 4 can match images with or without a markdown title.
|
||||
// All matches are whitespace tolerant as long it's still valid markdown.
|
||||
// If the image is inside a code block, we'll ignore it `(?!(.*`))`.
|
||||
const imageScaleRegex = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
|
||||
$preview.off("click", ".scale-btn").on("click", ".scale-btn", (e) => {
|
||||
const index = parseInt($(e.target).parent().attr("data-image-index"), 10);
|
||||
const index = parseInt(
|
||||
$(e.target).closest(".button-wrapper").attr("data-image-index"),
|
||||
10
|
||||
);
|
||||
|
||||
const scale = e.target.attributes["data-scale"].value;
|
||||
const matchingPlaceholder = this.get("composer.reply").match(
|
||||
imageScaleRegex
|
||||
IMAGE_MARKDOWN_REGEX
|
||||
);
|
||||
|
||||
if (matchingPlaceholder) {
|
||||
@ -607,7 +616,7 @@ export default Component.extend(ComposerUpload, {
|
||||
|
||||
if (match) {
|
||||
const replacement = match.replace(
|
||||
imageScaleRegex,
|
||||
IMAGE_MARKDOWN_REGEX,
|
||||
``
|
||||
);
|
||||
|
||||
@ -615,7 +624,7 @@ export default Component.extend(ComposerUpload, {
|
||||
"composer:replace-text",
|
||||
matchingPlaceholder[index],
|
||||
replacement,
|
||||
{ regex: imageScaleRegex, index }
|
||||
{ regex: IMAGE_MARKDOWN_REGEX, index }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -625,6 +634,58 @@ export default Component.extend(ComposerUpload, {
|
||||
});
|
||||
},
|
||||
|
||||
_registerImageAltTextButtonClick($preview) {
|
||||
$preview
|
||||
.off("click", ".alt-text-edit-btn")
|
||||
.on("click", ".alt-text-edit-btn", (e) => {
|
||||
const parentContainer = $(e.target).closest(
|
||||
".alt-text-readonly-container"
|
||||
);
|
||||
const altText = parentContainer.find(".alt-text");
|
||||
const correspondingInput = parentContainer.find(".alt-text-input");
|
||||
|
||||
$(e.target).hide();
|
||||
altText.hide();
|
||||
correspondingInput.val(altText.text());
|
||||
correspondingInput.show();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$preview
|
||||
.off("keypress", ".alt-text-input")
|
||||
.on("keypress", ".alt-text-input", (e) => {
|
||||
if (e.key === "[" || e.key === "]") {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (e.key === "Enter") {
|
||||
const index = parseInt(
|
||||
$(e.target).closest(".button-wrapper").attr("data-image-index"),
|
||||
10
|
||||
);
|
||||
const matchingPlaceholder = this.get("composer.reply").match(
|
||||
IMAGE_MARKDOWN_REGEX
|
||||
);
|
||||
const match = matchingPlaceholder[index];
|
||||
const replacement = match.replace(
|
||||
IMAGE_MARKDOWN_REGEX,
|
||||
``
|
||||
);
|
||||
|
||||
this.appEvents.trigger("composer:replace-text", match, replacement);
|
||||
|
||||
const parentContainer = $(e.target).closest(
|
||||
".alt-text-readonly-container"
|
||||
);
|
||||
const altText = parentContainer.find(".alt-text");
|
||||
const altTextButton = parentContainer.find(".alt-text-edit-btn");
|
||||
altText.show();
|
||||
altTextButton.show();
|
||||
$(e.target).hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@on("willDestroyElement")
|
||||
_composerClosed() {
|
||||
this._unbindMobileUploadButton();
|
||||
@ -688,6 +749,14 @@ export default Component.extend(ComposerUpload, {
|
||||
}
|
||||
},
|
||||
|
||||
_findMatchingUploadHandler(fileName) {
|
||||
return this.uploadHandlers.find((handler) => {
|
||||
const ext = handler.extensions.join("|");
|
||||
const regex = new RegExp(`\\.(${ext})$`, "i");
|
||||
return regex.test(fileName);
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
importQuote(toolbarEvent) {
|
||||
this.importQuote(toolbarEvent);
|
||||
@ -732,26 +801,29 @@ export default Component.extend(ComposerUpload, {
|
||||
});
|
||||
},
|
||||
|
||||
previewUpdated($preview) {
|
||||
previewUpdated(preview) {
|
||||
// cache jquery objects for functions still using jquery
|
||||
const $preview = $(preview);
|
||||
|
||||
// Paint mentions
|
||||
const unseenMentions = linkSeenMentions($preview, this.siteSettings);
|
||||
const unseenMentions = linkSeenMentions(preview, this.siteSettings);
|
||||
if (unseenMentions.length) {
|
||||
discourseDebounce(
|
||||
this,
|
||||
this._renderUnseenMentions,
|
||||
$preview,
|
||||
preview,
|
||||
unseenMentions,
|
||||
450
|
||||
);
|
||||
}
|
||||
|
||||
this._warnMentionedGroups($preview);
|
||||
this._warnCannotSeeMention($preview);
|
||||
this._warnMentionedGroups(preview);
|
||||
this._warnCannotSeeMention(preview);
|
||||
|
||||
// Paint category and tag hashtags
|
||||
const unseenHashtags = linkSeenHashtags($preview);
|
||||
const unseenHashtags = linkSeenHashtags(preview);
|
||||
if (unseenHashtags.length > 0) {
|
||||
discourseDebounce(this, this._renderUnseenHashtags, $preview, 450);
|
||||
discourseDebounce(this, this._renderUnseenHashtags, preview, 450);
|
||||
}
|
||||
|
||||
// Paint oneboxes
|
||||
@ -765,7 +837,7 @@ export default Component.extend(ComposerUpload, {
|
||||
}
|
||||
|
||||
const paintedCount = loadOneboxes(
|
||||
$preview[0],
|
||||
preview,
|
||||
ajax,
|
||||
this.get("composer.topic.id"),
|
||||
this.get("composer.category.id"),
|
||||
@ -781,7 +853,7 @@ export default Component.extend(ComposerUpload, {
|
||||
discourseDebounce(this, paintFunc, 450);
|
||||
|
||||
// Short upload urls need resolution
|
||||
resolveAllShortUrls(ajax, this.siteSettings, $preview[0]);
|
||||
resolveAllShortUrls(ajax, this.siteSettings, preview);
|
||||
|
||||
if (this._enableAdvancedEditorPreviewSync()) {
|
||||
this._syncScroll(
|
||||
@ -792,8 +864,9 @@ export default Component.extend(ComposerUpload, {
|
||||
}
|
||||
|
||||
this._registerImageScaleButtonClick($preview);
|
||||
this._registerImageAltTextButtonClick($preview);
|
||||
|
||||
this.trigger("previewRefreshed", $preview[0]);
|
||||
this.trigger("previewRefreshed", preview);
|
||||
this.afterRefresh($preview);
|
||||
},
|
||||
},
|
||||
|
||||
@ -83,7 +83,7 @@ export default Component.extend({
|
||||
!topic.archived &&
|
||||
!topic.closed &&
|
||||
!topic.deleted,
|
||||
topic: topic,
|
||||
topic,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -319,7 +319,7 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
}
|
||||
|
||||
if (isTesting()) {
|
||||
this.element.addEventListener("paste", this.paste.bind(this));
|
||||
this.element.addEventListener("paste", this.paste);
|
||||
}
|
||||
},
|
||||
|
||||
@ -341,6 +341,8 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
if (isTesting()) {
|
||||
this.element.removeEventListener("paste", this.paste);
|
||||
}
|
||||
|
||||
this._cachedCookFunction = null;
|
||||
},
|
||||
|
||||
@discourseComputed()
|
||||
@ -394,8 +396,8 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
const cookedElement = document.createElement("div");
|
||||
cookedElement.innerHTML = cooked;
|
||||
|
||||
linkSeenHashtags($(cookedElement));
|
||||
linkSeenMentions($(cookedElement), this.siteSettings);
|
||||
linkSeenHashtags(cookedElement);
|
||||
linkSeenMentions(cookedElement, this.siteSettings);
|
||||
resolveCachedShortUrls(this.siteSettings, cookedElement);
|
||||
loadOneboxes(
|
||||
cookedElement,
|
||||
@ -427,7 +429,7 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
}
|
||||
|
||||
if (this.previewUpdated) {
|
||||
this.previewUpdated($(preview));
|
||||
this.previewUpdated(preview);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,7 +10,7 @@ export default Component.extend({
|
||||
this._super(...arguments);
|
||||
this._modalAlertElement = document.getElementById("modal-alert");
|
||||
if (this._modalAlertElement) {
|
||||
this._modalAlertElement.innerHTML = "";
|
||||
this._clearFlash();
|
||||
}
|
||||
|
||||
let fixedParent = $(this.element).closest(".d-modal.fixed-modal");
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
/* global Pikaday:true */
|
||||
import discourseComputed, { on } from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import { Promise } from "rsvp";
|
||||
import { action } from "@ember/object";
|
||||
/* global Pikaday:true */
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { schedule } from "@ember/runloop";
|
||||
|
||||
@ -144,9 +144,16 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed()
|
||||
placeholder() {
|
||||
return I18n.t("dates.placeholder");
|
||||
@discourseComputed("_placeholder")
|
||||
placeholder: {
|
||||
get(_placeholder) {
|
||||
return _placeholder || I18n.t("dates.placeholder");
|
||||
},
|
||||
|
||||
set(value) {
|
||||
this.set("_placeholder", value);
|
||||
return value;
|
||||
},
|
||||
},
|
||||
|
||||
_opts() {
|
||||
|
||||
@ -39,7 +39,7 @@ export default Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_addToCollection: function () {
|
||||
_addToCollection() {
|
||||
this.panels.addObject(this.tabClassName);
|
||||
},
|
||||
|
||||
@ -50,7 +50,7 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
select: function () {
|
||||
select() {
|
||||
this.set("selectedTab", this.tab);
|
||||
|
||||
if (!this.newCategory) {
|
||||
|
||||
@ -8,7 +8,7 @@ export default buildCategoryPanel("topic-template", {
|
||||
showInsertLinkButton: false,
|
||||
|
||||
@observes("activeTab")
|
||||
_activeTabChanged: function () {
|
||||
_activeTabChanged() {
|
||||
if (this.activeTab) {
|
||||
schedule("afterRender", () =>
|
||||
this.element.querySelector(".d-editor-input").focus()
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import Component from "@ember/component";
|
||||
import UploadMixin from "discourse/mixins/upload";
|
||||
import UppyUploadMixin from "discourse/mixins/uppy-upload";
|
||||
import { action } from "@ember/object";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { notEmpty } from "@ember/object/computed";
|
||||
|
||||
const DEFAULT_GROUP = "default";
|
||||
|
||||
export default Component.extend(UploadMixin, {
|
||||
export default Component.extend(UppyUploadMixin, {
|
||||
type: "emoji",
|
||||
uploadUrl: "/admin/customize/emojis",
|
||||
hasName: notEmpty("name"),
|
||||
@ -15,10 +15,10 @@ export default Component.extend(UploadMixin, {
|
||||
emojiGroups: null,
|
||||
newEmojiGroups: null,
|
||||
tagName: null,
|
||||
preventDirectS3Uploads: true,
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.set("newEmojiGroups", this.emojiGroups);
|
||||
},
|
||||
|
||||
@ -27,10 +27,6 @@ export default Component.extend(UploadMixin, {
|
||||
return !this.hasName || this.uploading;
|
||||
},
|
||||
|
||||
uploadOptions() {
|
||||
return { sequentialUploads: true };
|
||||
},
|
||||
|
||||
@action
|
||||
createEmojiGroup(group) {
|
||||
this.setProperties({
|
||||
|
||||
@ -7,7 +7,7 @@ export default Component.extend({
|
||||
|
||||
@on("didInsertElement")
|
||||
@observes("highlight")
|
||||
_highlightOnInsert: function () {
|
||||
_highlightOnInsert() {
|
||||
const term = this.highlight;
|
||||
highlightSearch(this.element, term);
|
||||
},
|
||||
|
||||
@ -351,12 +351,11 @@ export default Component.extend({
|
||||
if (this.isInviteeGroup) {
|
||||
return this.inviteModel
|
||||
.createGroupInvite(this.invitee.trim())
|
||||
.then((data) => {
|
||||
.then(() => {
|
||||
model.setProperties({ saving: false, finished: true });
|
||||
this.get("inviteModel.details.allowed_groups").pushObject(
|
||||
EmberObject.create(data.group)
|
||||
);
|
||||
this.appEvents.trigger("post-stream:refresh");
|
||||
this.inviteModel.reload().then(() => {
|
||||
this.appEvents.trigger("post-stream:refresh");
|
||||
});
|
||||
})
|
||||
.catch(onerror);
|
||||
} else {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { empty } from "@ember/object/computed";
|
||||
import { bind, default as computed } from "discourse-common/utils/decorators";
|
||||
import computed, { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["pick-files-button"],
|
||||
|
||||
@ -4,7 +4,7 @@ import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { dasherize } from "@ember/string";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import optionalService from "discourse/lib/optional-service";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { set } from "@ember/object";
|
||||
@ -113,6 +113,7 @@ export default Component.extend({
|
||||
return _components[type];
|
||||
},
|
||||
|
||||
@bind
|
||||
_performConfirmed(action) {
|
||||
let reviewable = this.reviewable;
|
||||
|
||||
@ -264,7 +265,7 @@ export default Component.extend({
|
||||
title: "review.reject_reason.title",
|
||||
model: this.reviewable,
|
||||
}).setProperties({
|
||||
performConfirmed: this._performConfirmed.bind(this),
|
||||
performConfirmed: this._performConfirmed,
|
||||
action,
|
||||
});
|
||||
} else if (customModal) {
|
||||
@ -272,7 +273,7 @@ export default Component.extend({
|
||||
title: `review.${customModal}.title`,
|
||||
model: this.reviewable,
|
||||
}).setProperties({
|
||||
performConfirmed: this._performConfirmed.bind(this),
|
||||
performConfirmed: this._performConfirmed,
|
||||
action,
|
||||
});
|
||||
} else {
|
||||
|
||||
@ -77,7 +77,12 @@ export default MountWidget.extend({
|
||||
if (this.isDestroyed || this.isDestroying) {
|
||||
return;
|
||||
}
|
||||
if (isWorkaroundActive()) {
|
||||
|
||||
if (
|
||||
isWorkaroundActive() ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.fullscreenElement
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import Component from "@ember/component";
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
|
||||
actions: {
|
||||
share: function (source) {
|
||||
share(source) {
|
||||
this.action(source);
|
||||
},
|
||||
},
|
||||
|
||||
@ -37,12 +37,12 @@ export default Component.extend({
|
||||
const inboxFilter = suggestedGroupName ? "group" : "user";
|
||||
|
||||
const unreadCount = this.pmTopicTrackingState.lookupCount("unread", {
|
||||
inboxFilter: inboxFilter,
|
||||
inboxFilter,
|
||||
groupName: suggestedGroupName,
|
||||
});
|
||||
|
||||
const newCount = this.pmTopicTrackingState.lookupCount("new", {
|
||||
inboxFilter: inboxFilter,
|
||||
inboxFilter,
|
||||
groupName: suggestedGroupName,
|
||||
});
|
||||
|
||||
@ -54,7 +54,7 @@ export default Component.extend({
|
||||
BOTH: hasBoth,
|
||||
UNREAD: unreadCount,
|
||||
NEW: newCount,
|
||||
username: username,
|
||||
username,
|
||||
groupName: suggestedGroupName,
|
||||
groupLink: this._groupLink(username, suggestedGroupName),
|
||||
basePath: getURL(""),
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { alias, and, or } from "@ember/object/computed";
|
||||
import { computed } from "@ember/object";
|
||||
import Component from "@ember/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { getTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
import { getTopicFooterDropdowns } from "discourse/lib/register-topic-footer-dropdown";
|
||||
|
||||
export default Component.extend({
|
||||
elementId: "topic-footer-buttons",
|
||||
@ -18,17 +20,25 @@ export default Component.extend({
|
||||
return this.siteSettings.enable_personal_messages && isPM;
|
||||
},
|
||||
|
||||
buttons: getTopicFooterButtons(),
|
||||
inlineButtons: getTopicFooterButtons(),
|
||||
inlineDropdowns: getTopicFooterDropdowns(),
|
||||
|
||||
@discourseComputed("buttons.[]")
|
||||
inlineButtons(buttons) {
|
||||
return buttons.filter((button) => !button.dropdown);
|
||||
},
|
||||
inlineActionables: computed(
|
||||
"inlineButtons.[]",
|
||||
"inlineDropdowns.[]",
|
||||
function () {
|
||||
return this.inlineButtons
|
||||
.filterBy("dropdown", false)
|
||||
.concat(this.inlineDropdowns)
|
||||
.sortBy("priority")
|
||||
.reverse();
|
||||
}
|
||||
),
|
||||
|
||||
// topic.assigned_to_user is for backward plugin support
|
||||
@discourseComputed("buttons.[]", "topic.assigned_to_user")
|
||||
dropdownButtons(buttons) {
|
||||
return buttons.filter((button) => button.dropdown);
|
||||
@discourseComputed("inlineButtons.[]", "topic.assigned_to_user")
|
||||
dropdownButtons(inlineButtons) {
|
||||
return inlineButtons.filter((button) => button.dropdown);
|
||||
},
|
||||
|
||||
@discourseComputed("topic.isPrivateMessage")
|
||||
|
||||
@ -159,16 +159,16 @@ export default Component.extend({
|
||||
return classes.join(" ");
|
||||
},
|
||||
|
||||
hasLikes: function () {
|
||||
hasLikes() {
|
||||
return this.get("topic.like_count") > 0;
|
||||
},
|
||||
|
||||
hasOpLikes: function () {
|
||||
hasOpLikes() {
|
||||
return this.get("topic.op_like_count") > 0;
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
expandPinned: function () {
|
||||
expandPinned() {
|
||||
const pinned = this.get("topic.pinned");
|
||||
if (!pinned) {
|
||||
return false;
|
||||
|
||||
@ -13,6 +13,7 @@ const MIN_WIDTH_TIMELINE = 924,
|
||||
MIN_HEIGHT_TIMELINE = 325;
|
||||
|
||||
export default Component.extend(PanEvents, {
|
||||
classNameBindings: ["info.topicProgressExpanded:topic-progress-expanded"],
|
||||
composerOpen: null,
|
||||
info: null,
|
||||
isPanning: false,
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { later, scheduleOnce } from "@ember/runloop";
|
||||
|
||||
export default Component.extend({
|
||||
elementId: "topic-progress-wrapper",
|
||||
classNameBindings: ["docked"],
|
||||
classNameBindings: ["docked", "withTransitions"],
|
||||
docked: false,
|
||||
withTransitions: null,
|
||||
progressPosition: null,
|
||||
postStream: alias("topic.postStream"),
|
||||
_streamPercentage: null,
|
||||
@ -68,128 +69,119 @@ export default Component.extend({
|
||||
return readPos < stream.length - 1 && readPos > position;
|
||||
},
|
||||
|
||||
@observes("postStream.stream.[]")
|
||||
_updateBar() {
|
||||
scheduleOnce("afterRender", this, this._updateProgressBar);
|
||||
},
|
||||
|
||||
_topicScrolled(event) {
|
||||
if (this.docked) {
|
||||
this.set("progressPosition", this.get("postStream.filteredPostsCount"));
|
||||
this._streamPercentage = 1.0;
|
||||
this.setProperties({
|
||||
progressPosition: this.get("postStream.filteredPostsCount"),
|
||||
_streamPercentage: 100,
|
||||
});
|
||||
} else {
|
||||
this.set("progressPosition", event.postIndex);
|
||||
this._streamPercentage = event.percent;
|
||||
this.setProperties({
|
||||
progressPosition: event.postIndex,
|
||||
_streamPercentage: (event.percent * 100).toFixed(2),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
this._updateBar();
|
||||
@discourseComputed("_streamPercentage")
|
||||
progressStyle(_streamPercentage) {
|
||||
return `--progress-bg-width: ${_streamPercentage || 0}%`;
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.appEvents
|
||||
.on("composer:will-open", this, this._dock)
|
||||
.on("composer:resized", this, this._dock)
|
||||
.on("composer:closed", this, this._dock)
|
||||
.on("topic:scrolled", this, this._dock)
|
||||
.on("composer:resized", this, this._composerEvent)
|
||||
.on("topic:current-post-scrolled", this, this._topicScrolled);
|
||||
|
||||
const prevEvent = this.prevEvent;
|
||||
if (prevEvent) {
|
||||
scheduleOnce("afterRender", this, this._topicScrolled, prevEvent);
|
||||
} else {
|
||||
scheduleOnce("afterRender", this, this._updateProgressBar);
|
||||
if (this.prevEvent) {
|
||||
scheduleOnce("afterRender", this, this._topicScrolled, this.prevEvent);
|
||||
}
|
||||
scheduleOnce("afterRender", this, this._dock);
|
||||
scheduleOnce("afterRender", this, this._startObserver);
|
||||
|
||||
// start CSS transitions a tiny bit later
|
||||
// to avoid jumpiness on initial topic load
|
||||
later(this._addCssTransitions, 500);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this._topicBottomObserver?.disconnect();
|
||||
this.appEvents
|
||||
.off("composer:will-open", this, this._dock)
|
||||
.off("composer:resized", this, this._dock)
|
||||
.off("composer:closed", this, this._dock)
|
||||
.off("topic:scrolled", this, this._dock)
|
||||
.off("composer:resized", this, this._composerEvent)
|
||||
.off("topic:current-post-scrolled", this, this._topicScrolled);
|
||||
},
|
||||
|
||||
_updateProgressBar() {
|
||||
if (this.isDestroyed || this.isDestroying) {
|
||||
@bind
|
||||
_addCssTransitions() {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
this.set("withTransitions", true);
|
||||
},
|
||||
|
||||
const $topicProgress = $(this.element.querySelector("#topic-progress"));
|
||||
// speeds up stuff, bypass jquery slowness and extra checks
|
||||
if (!this._totalWidth) {
|
||||
this._totalWidth = $topicProgress[0].offsetWidth;
|
||||
}
|
||||
|
||||
// Only show percentage once we have one
|
||||
if (!this._streamPercentage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const totalWidth = this._totalWidth;
|
||||
const progressWidth = (this._streamPercentage || 0) * totalWidth;
|
||||
const borderSize = progressWidth === totalWidth ? "0px" : "1px";
|
||||
|
||||
const $bg = $topicProgress.find(".bg");
|
||||
if ($bg.length === 0) {
|
||||
const style = `border-right-width: ${borderSize}; width: ${progressWidth}px`;
|
||||
$topicProgress.append(`<div class='bg' style="${style}"> </div>`);
|
||||
} else {
|
||||
$bg.css("border-right-width", borderSize).width(progressWidth - 2);
|
||||
_startObserver() {
|
||||
if ("IntersectionObserver" in window) {
|
||||
this._topicBottomObserver = this._setupObserver();
|
||||
this._topicBottomObserver.observe(
|
||||
document.querySelector("#topic-bottom")
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_dock() {
|
||||
const $wrapper = $(this.element);
|
||||
if (!$wrapper || $wrapper.length === 0) {
|
||||
return;
|
||||
}
|
||||
_setupObserver() {
|
||||
// minimum 50px here ensures element is not docked when
|
||||
// scrolling down quickly, it causes post stream refresh loop
|
||||
// on Android
|
||||
const bottomIntersectionMargin =
|
||||
document.querySelector("#reply-control")?.clientHeight || 50;
|
||||
|
||||
const $html = $("html");
|
||||
const offset = window.pageYOffset || $html.scrollTop();
|
||||
const maximumOffset = $("#topic-bottom").offset().top;
|
||||
const windowHeight = $(window).height();
|
||||
let composerHeight = $("#reply-control").height() || 0;
|
||||
const isDocked = offset >= maximumOffset - windowHeight + composerHeight;
|
||||
let bottom = $("body").height() - maximumOffset;
|
||||
|
||||
const $iPadFooterNav = $(".footer-nav-ipad .footer-nav");
|
||||
if ($iPadFooterNav && $iPadFooterNav.length > 0) {
|
||||
bottom += $iPadFooterNav.outerHeight();
|
||||
}
|
||||
|
||||
const draftComposerHeight = 40;
|
||||
|
||||
if (composerHeight > 0) {
|
||||
const $iPhoneFooterNav = $(".footer-nav-visible .footer-nav");
|
||||
const $replyDraft = $("#reply-control.draft");
|
||||
if ($iPhoneFooterNav.outerHeight() && $replyDraft.outerHeight()) {
|
||||
composerHeight =
|
||||
$replyDraft.outerHeight() + $iPhoneFooterNav.outerHeight();
|
||||
}
|
||||
$wrapper.css("bottom", isDocked ? bottom : composerHeight);
|
||||
} else {
|
||||
$wrapper.css("bottom", isDocked ? bottom : "");
|
||||
}
|
||||
|
||||
this.set("docked", isDocked);
|
||||
|
||||
$wrapper.css(
|
||||
"margin-bottom",
|
||||
!isDocked && composerHeight > draftComposerHeight ? "0px" : ""
|
||||
);
|
||||
this.appEvents.trigger("topic-progress:docked-status-changed", {
|
||||
docked: isDocked,
|
||||
element: this.element,
|
||||
return new IntersectionObserver(this._intersectionHandler, {
|
||||
threshold: 1,
|
||||
rootMargin: `0px 0px -${bottomIntersectionMargin}px 0px`,
|
||||
});
|
||||
},
|
||||
|
||||
_composerEvent() {
|
||||
// reinitializing needed to account for composer height
|
||||
// might be no longer necessary if IntersectionObserver API supports dynamic rootMargin
|
||||
// see https://github.com/w3c/IntersectionObserver/issues/428
|
||||
if ("IntersectionObserver" in window) {
|
||||
this._topicBottomObserver?.disconnect();
|
||||
this._startObserver();
|
||||
}
|
||||
},
|
||||
|
||||
@bind
|
||||
_intersectionHandler(entries) {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entries[0].isIntersecting === true) {
|
||||
this.set("docked", true);
|
||||
} else {
|
||||
if (entries[0].boundingClientRect.top > 0) {
|
||||
this.set("docked", false);
|
||||
const wrapper = document.querySelector("#topic-progress-wrapper");
|
||||
const composerH =
|
||||
document.querySelector("#reply-control")?.clientHeight || 0;
|
||||
if (composerH === 0) {
|
||||
const filteredPostsHeight =
|
||||
document.querySelector(".posts-filtered-notice")?.clientHeight || 0;
|
||||
filteredPostsHeight === 0
|
||||
? wrapper.style.removeProperty("bottom")
|
||||
: wrapper.style.setProperty("bottom", `${filteredPostsHeight}px`);
|
||||
} else {
|
||||
wrapper.style.setProperty("bottom", `${composerH}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
click(e) {
|
||||
if ($(e.target).closest("#topic-progress").length) {
|
||||
if (e.target.closest("#topic-progress")) {
|
||||
this.send("toggleExpansion");
|
||||
}
|
||||
},
|
||||
|
||||
@ -5,7 +5,7 @@ export default Component.extend({
|
||||
tagName: "span",
|
||||
|
||||
@observes("selected")
|
||||
selectionChanged: function () {
|
||||
selectionChanged() {
|
||||
const selected = this.selected;
|
||||
const list = this.selectedList;
|
||||
const id = this.selectedId;
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import UppyUploadMixin from "discourse/mixins/uppy-upload";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend(UppyUploadMixin, {
|
||||
tagName: "span",
|
||||
type: "backup",
|
||||
useMultipartUploadsIfAvailable: true,
|
||||
|
||||
@discourseComputed("uploading", "uploadProgress")
|
||||
uploadButtonText(uploading, progress) {
|
||||
return uploading
|
||||
? I18n.t("admin.backups.upload.uploading_progress", { progress })
|
||||
: I18n.t("admin.backups.upload.label");
|
||||
},
|
||||
|
||||
validateUploadedFilesOptions() {
|
||||
return { skipValidation: true };
|
||||
},
|
||||
|
||||
uploadDone() {
|
||||
this.done();
|
||||
},
|
||||
});
|
||||
@ -31,7 +31,7 @@ export default Component.extend(LoadMore, {
|
||||
classNames: ["user-stream"],
|
||||
|
||||
@observes("stream.user.id")
|
||||
_scrollTopOnModelChange: function () {
|
||||
_scrollTopOnModelChange() {
|
||||
schedule("afterRender", () => $(document).scrollTop(0));
|
||||
},
|
||||
|
||||
|
||||
@ -1,6 +1,62 @@
|
||||
import Controller from "@ember/controller";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { action } from "@ember/object";
|
||||
import { Promise } from "rsvp";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export function openBookmarkModal(
|
||||
bookmark,
|
||||
callbacks = {
|
||||
onCloseWithoutSaving: null,
|
||||
onAfterSave: null,
|
||||
onAfterDelete: null,
|
||||
}
|
||||
) {
|
||||
return new Promise((resolve) => {
|
||||
const modalTitle = () => {
|
||||
if (bookmark.for_topic) {
|
||||
return bookmark.id
|
||||
? "post.bookmarks.edit_for_topic"
|
||||
: "post.bookmarks.create_for_topic";
|
||||
}
|
||||
return bookmark.id ? "post.bookmarks.edit" : "post.bookmarks.create";
|
||||
};
|
||||
let modalController = showModal("bookmark", {
|
||||
model: {
|
||||
postId: bookmark.post_id,
|
||||
topicId: bookmark.topic_id,
|
||||
id: bookmark.id,
|
||||
reminderAt: bookmark.reminder_at,
|
||||
autoDeletePreference: bookmark.auto_delete_preference,
|
||||
name: bookmark.name,
|
||||
forTopic: bookmark.for_topic,
|
||||
},
|
||||
title: modalTitle(),
|
||||
modalClass: "bookmark-with-reminder",
|
||||
});
|
||||
modalController.setProperties({
|
||||
onCloseWithoutSaving: () => {
|
||||
if (callbacks.onCloseWithoutSaving) {
|
||||
callbacks.onCloseWithoutSaving();
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
afterSave: (savedData) => {
|
||||
let resolveData;
|
||||
if (callbacks.onAfterSave) {
|
||||
resolveData = callbacks.onAfterSave(savedData);
|
||||
}
|
||||
resolve(resolveData);
|
||||
},
|
||||
afterDelete: (topicBookmarked, bookmarkId) => {
|
||||
if (callbacks.onAfterDelete) {
|
||||
callbacks.onAfterDelete(topicBookmarked, bookmarkId);
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
onShow() {
|
||||
@ -21,7 +77,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
*/
|
||||
onClose(opts = {}) {
|
||||
if (this.onCloseHandler) {
|
||||
this.onCloseHandler(opts.initiatedByCloseButton);
|
||||
this.onCloseHandler(opts);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -50,7 +50,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
);
|
||||
},
|
||||
() => {
|
||||
this.flash(I18n.t("topic.change_owner.error"), "alert-error");
|
||||
this.flash(I18n.t("topic.change_owner.error"), "error");
|
||||
this.set("saving", false);
|
||||
}
|
||||
);
|
||||
|
||||
@ -54,7 +54,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
next(() => DiscourseURL.routeTo(topic.url));
|
||||
})
|
||||
.catch(() =>
|
||||
this.flash(I18n.t("topic.change_timestamp.error"), "alert-error")
|
||||
this.flash(I18n.t("topic.change_timestamp.error"), "error")
|
||||
)
|
||||
.finally(() => this.set("saving", false));
|
||||
|
||||
|
||||
@ -192,10 +192,13 @@ export default Controller.extend({
|
||||
|
||||
@discourseComputed("model.canEditTitle", "model.creatingPrivateMessage")
|
||||
canEditTags(canEditTitle, creatingPrivateMessage) {
|
||||
if (creatingPrivateMessage && (this.site.mobileView || !this.isStaffUser)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
this.site.can_tag_topics &&
|
||||
canEditTitle &&
|
||||
!creatingPrivateMessage &&
|
||||
(!this.get("model.topic.isPrivateMessage") || this.site.can_tag_pms)
|
||||
);
|
||||
},
|
||||
@ -502,6 +505,11 @@ export default Controller.extend({
|
||||
$links.each((idx, l) => {
|
||||
const href = l.href;
|
||||
if (href && href.length) {
|
||||
// skip links added by watched words
|
||||
if (l.dataset.word !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// skip links in quotes and oneboxes
|
||||
for (let element = l; element; element = element.parentElement) {
|
||||
if (
|
||||
@ -632,7 +640,9 @@ export default Controller.extend({
|
||||
},
|
||||
|
||||
save(ignore, event) {
|
||||
this.save(false, { jump: !(event && event.shiftKey) });
|
||||
this.save(false, {
|
||||
jump: !event?.shiftKey && !this.skipJumpOnSave,
|
||||
});
|
||||
},
|
||||
|
||||
displayEditReason() {
|
||||
@ -665,17 +675,19 @@ export default Controller.extend({
|
||||
groups.forEach((group) => {
|
||||
let body;
|
||||
const groupLink = getURL(`/g/${group.name}/members`);
|
||||
const maxMentions = parseInt(group.max_mentions, 10);
|
||||
const userCount = parseInt(group.user_count, 10);
|
||||
|
||||
if (group.max_mentions < group.user_count) {
|
||||
if (maxMentions < userCount) {
|
||||
body = I18n.t("composer.group_mentioned_limit", {
|
||||
group: `@${group.name}`,
|
||||
count: group.max_mentions,
|
||||
count: maxMentions,
|
||||
group_link: groupLink,
|
||||
});
|
||||
} else if (group.user_count > 0) {
|
||||
body = I18n.t("composer.group_mentioned", {
|
||||
group: `@${group.name}`,
|
||||
count: group.user_count,
|
||||
count: userCount,
|
||||
group_link: groupLink,
|
||||
});
|
||||
}
|
||||
@ -956,6 +968,7 @@ export default Controller.extend({
|
||||
@param {Number} [opts.prioritizedCategoryId]
|
||||
@param {String} [opts.draftSequence]
|
||||
@param {Boolean} [opts.skipDraftCheck]
|
||||
@param {Boolean} [opts.skipJumpOnSave] Option to skip navigating to the post when saved in this composer session
|
||||
**/
|
||||
open(opts) {
|
||||
opts = opts || {};
|
||||
@ -982,6 +995,8 @@ export default Controller.extend({
|
||||
skipAutoSave: true,
|
||||
});
|
||||
|
||||
this.set("skipJumpOnSave", !!opts.skipJumpOnSave);
|
||||
|
||||
// Scope the categories drop down to the category we opened the composer with.
|
||||
if (opts.categoryId && !opts.disableScopedCategory) {
|
||||
const category = this.site.categories.findBy("id", opts.categoryId);
|
||||
|
||||
@ -260,7 +260,7 @@ export default Controller.extend(
|
||||
},
|
||||
|
||||
@observes("emailValidation", "accountEmail")
|
||||
prefillUsername: function () {
|
||||
prefillUsername() {
|
||||
if (this.prefilledUsername) {
|
||||
// If username field has been filled automatically, and email field just changed,
|
||||
// then remove the username.
|
||||
|
||||
@ -29,7 +29,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
.destroy(this.currentUser)
|
||||
.then(() => this.send("closeModal"))
|
||||
.catch(() => {
|
||||
this.flash(I18n.t("post.controls.delete_topic_error"), "alert-error");
|
||||
this.flash(I18n.t("post.controls.delete_topic_error"), "error");
|
||||
this.set("deletingTopic", false);
|
||||
});
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ export default Controller.extend({
|
||||
loadedAllItems: not("discoveryTopics.model.canLoadMore"),
|
||||
|
||||
@observes("loadedAllItems")
|
||||
_showFooter: function () {
|
||||
_showFooter() {
|
||||
this.set("application.showFooter", this.loadedAllItems);
|
||||
},
|
||||
|
||||
|
||||
@ -74,6 +74,7 @@ const controllerOpts = {
|
||||
// router and ember throws an error due to missing `handlerInfos`.
|
||||
// Lesson learned: Don't call `loading` yourself.
|
||||
this.set("discovery.loading", true);
|
||||
this.set("model.canLoadMore", true);
|
||||
|
||||
this.topicTrackingState.resetTracking();
|
||||
|
||||
|
||||
@ -185,7 +185,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
) {
|
||||
this.flash(
|
||||
I18n.t("topic.topic_status_update.time_frame_required"),
|
||||
"alert-error"
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -195,19 +195,13 @@ export default Controller.extend(ModalFunctionality, {
|
||||
!this.get("topicTimer.updateTime")
|
||||
) {
|
||||
if (this.get("topicTimer.duration_minutes") <= 0) {
|
||||
this.flash(
|
||||
I18n.t("topic.topic_status_update.min_duration"),
|
||||
"alert-error"
|
||||
);
|
||||
this.flash(I18n.t("topic.topic_status_update.min_duration"), "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// cannot be more than 20 years
|
||||
if (this.get("topicTimer.duration_minutes") > 20 * 365 * 1440) {
|
||||
this.flash(
|
||||
I18n.t("topic.topic_status_update.max_duration"),
|
||||
"alert-error"
|
||||
);
|
||||
this.flash(I18n.t("topic.topic_status_update.max_duration"), "error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export default Controller.extend({
|
||||
ajax({
|
||||
url: `/session/email-login/${this.model.token}`,
|
||||
type: "POST",
|
||||
data: data,
|
||||
data,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
|
||||
@ -258,8 +258,9 @@ export default Controller.extend(ModalFunctionality, {
|
||||
let params = this.get("selected.is_custom_flag")
|
||||
? { message: this.message }
|
||||
: {};
|
||||
|
||||
if (opts) {
|
||||
params = $.extend(params, opts);
|
||||
params = Object.assign(params, opts);
|
||||
}
|
||||
|
||||
this.appEvents.trigger(
|
||||
|
||||
@ -5,6 +5,7 @@ import { getWebauthnCredential } from "discourse/lib/webauthn";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
showSecondFactor: false,
|
||||
|
||||
@ -48,8 +48,8 @@ export default Controller.extend(ModalFunctionality, GrantBadgeController, {
|
||||
UserBadge.findByUsername(this.get("post.username")),
|
||||
]).then(([allBadges, userBadges]) => {
|
||||
this.setProperties({
|
||||
allBadges: allBadges,
|
||||
userBadges: userBadges,
|
||||
allBadges,
|
||||
userBadges,
|
||||
loading: false,
|
||||
});
|
||||
});
|
||||
|
||||
@ -13,7 +13,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
if (!this.ignoredUntil || !this.ignoredUsername) {
|
||||
this.flash(
|
||||
I18n.t("user.user_notifications.ignore_duration_time_frame_required"),
|
||||
"alert-error"
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
if (!this.ignoredUntil) {
|
||||
this.flash(
|
||||
I18n.t("user.user_notifications.ignore_duration_time_frame_required"),
|
||||
"alert-error"
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ export default Controller.extend(
|
||||
|
||||
@discourseComputed("email")
|
||||
yourEmailMessage(email) {
|
||||
return I18n.t("invites.your_email", { email: email });
|
||||
return I18n.t("invites.your_email", { email });
|
||||
},
|
||||
|
||||
@discourseComputed
|
||||
|
||||
@ -273,9 +273,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
}
|
||||
|
||||
this.set("loggingIn", true);
|
||||
loginMethod
|
||||
.doLogin({ signup: signup })
|
||||
.catch(() => this.set("loggingIn", false));
|
||||
loginMethod.doLogin({ signup }).catch(() => this.set("loggingIn", false));
|
||||
},
|
||||
|
||||
createAccount() {
|
||||
|
||||
@ -121,7 +121,7 @@ export default Controller.extend(PasswordValidation, {
|
||||
this.setProperties({
|
||||
securityKeyRequired: true,
|
||||
password: null,
|
||||
errorMessage: errorMessage,
|
||||
errorMessage,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@ -73,7 +73,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
name: this.model.username_lower,
|
||||
},
|
||||
pubKeyCredParams: this.supported_algorithms.map((alg) => {
|
||||
return { type: "public-key", alg: alg };
|
||||
return { type: "public-key", alg };
|
||||
}),
|
||||
excludeCredentials: this.existing_active_credential_ids.map(
|
||||
(credentialId) => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user