Version bump
This commit is contained in:
commit
ce44c05e83
5
.github/workflows/licenses.yml
vendored
5
.github/workflows/licenses.yml
vendored
@ -48,10 +48,7 @@ jobs:
|
||||
|
||||
- name: Setup licensed
|
||||
run: |
|
||||
# gem install licensed
|
||||
# Workaround for https://github.com/github/licensed/issues/521
|
||||
gem install specific_install
|
||||
gem specific_install https://github.com/CvX/licensed.git -b bundler-compat
|
||||
gem install licensed
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
|
||||
@ -1 +1 @@
|
||||
2.7.2
|
||||
2.7.6
|
||||
|
||||
@ -3,6 +3,9 @@ module.exports = {
|
||||
extends: "discourse:recommended",
|
||||
|
||||
rules: {
|
||||
"no-action-modifiers": true,
|
||||
"no-args-paths": true,
|
||||
"no-attrs-in-components": true,
|
||||
"no-capital-arguments": false, // TODO: we extensively use `args` argument name
|
||||
"no-curly-component-invocation": {
|
||||
allow: [
|
||||
|
||||
16
Gemfile
16
Gemfile
@ -39,7 +39,7 @@ gem 'sprockets', '3.7.2'
|
||||
# allows us to precompile all our templates in the unicorn master
|
||||
gem 'actionview_precompiler', require: false
|
||||
|
||||
gem 'seed-fu'
|
||||
gem 'discourse-seed-fu'
|
||||
|
||||
gem 'mail', git: 'https://github.com/discourse/mail.git'
|
||||
gem 'mini_mime'
|
||||
@ -134,13 +134,10 @@ gem 'cose', require: false
|
||||
gem 'addressable'
|
||||
gem 'json_schemer'
|
||||
|
||||
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.1")
|
||||
# net-smtp, net-imap and net-pop were removed from default gems in Ruby 3.1
|
||||
gem "net-smtp", "~> 0.2.1", require: false
|
||||
gem "net-imap", "~> 0.2.1", require: false
|
||||
gem "net-pop", "~> 0.1.1", require: false
|
||||
gem "digest", "3.0.0", require: false
|
||||
end
|
||||
gem "net-smtp", require: false
|
||||
gem "net-imap", require: false
|
||||
gem "net-pop", require: false
|
||||
gem "digest", require: false
|
||||
|
||||
# Gems used only for assets and not required in production environments by default.
|
||||
# Allow everywhere for now cause we are allowing asset debugging in production
|
||||
@ -272,6 +269,9 @@ gem 'maxminddb'
|
||||
|
||||
gem 'rails_failover', require: false
|
||||
|
||||
gem 'faraday'
|
||||
gem 'faraday-retry'
|
||||
|
||||
# workaround for faraday-net_http, see
|
||||
# https://github.com/ruby/net-imap/issues/16#issuecomment-803086765
|
||||
gem 'net-http'
|
||||
|
||||
128
Gemfile.lock
128
Gemfile.lock
@ -106,7 +106,7 @@ GEM
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.3.0)
|
||||
cose (1.2.1)
|
||||
cose (1.3.0)
|
||||
cbor (~> 0.5.9)
|
||||
openssl-signature_algorithm (~> 1.0)
|
||||
cppjieba_rb (0.4.2)
|
||||
@ -118,6 +118,7 @@ GEM
|
||||
debug_inspector (1.1.0)
|
||||
diff-lcs (1.5.0)
|
||||
diffy (3.4.2)
|
||||
digest (3.1.0)
|
||||
discourse-ember-rails (0.18.6)
|
||||
active_model_serializers
|
||||
ember-data-source (>= 1.0.0.beta.5)
|
||||
@ -127,6 +128,9 @@ GEM
|
||||
railties (>= 3.1)
|
||||
discourse-ember-source (3.12.2.3)
|
||||
discourse-fonts (0.0.9)
|
||||
discourse-seed-fu (2.3.12)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
discourse_dev_assets (0.0.4)
|
||||
faker (~> 2.16)
|
||||
literate_randomizer
|
||||
@ -141,17 +145,19 @@ GEM
|
||||
sprockets (>= 3.3, < 4.1)
|
||||
ember-source (2.18.2)
|
||||
erubi (1.11.0)
|
||||
excon (0.92.5)
|
||||
excon (0.93.1)
|
||||
execjs (2.8.1)
|
||||
exifr (1.3.9)
|
||||
exifr (1.3.10)
|
||||
fabrication (2.30.0)
|
||||
faker (2.23.0)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
fakeweb (1.3.0)
|
||||
faraday (2.5.2)
|
||||
faraday (2.6.0)
|
||||
faraday-net_http (>= 2.0, < 3.1)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-net_http (3.0.0)
|
||||
faraday-net_http (3.0.1)
|
||||
faraday-retry (2.0.0)
|
||||
faraday (~> 2.0)
|
||||
fast_blank (1.0.1)
|
||||
fast_xs (0.8.0)
|
||||
fastimage (2.2.6)
|
||||
@ -184,9 +190,9 @@ GEM
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.6.2)
|
||||
json-schema (2.8.1)
|
||||
addressable (>= 2.4)
|
||||
json_schemer (0.2.21)
|
||||
json-schema (3.0.0)
|
||||
addressable (>= 2.8)
|
||||
json_schemer (0.2.22)
|
||||
ecma-re-validator (~> 0.3)
|
||||
hana (~> 1.3)
|
||||
regexp_parser (~> 2.0)
|
||||
@ -219,7 +225,7 @@ GEM
|
||||
lz4-ruby (0.3.3)
|
||||
matrix (0.4.2)
|
||||
maxminddb (0.1.22)
|
||||
memory_profiler (1.0.0)
|
||||
memory_profiler (1.0.1)
|
||||
message_bus (4.2.0)
|
||||
rack (>= 1.1.3)
|
||||
method_source (1.0.0)
|
||||
@ -233,39 +239,39 @@ GEM
|
||||
mini_suffix (0.3.3)
|
||||
ffi (~> 1.9)
|
||||
minitest (5.16.3)
|
||||
mocha (1.15.0)
|
||||
msgpack (1.5.6)
|
||||
mocha (1.16.0)
|
||||
msgpack (1.6.0)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
mustache (1.1.1)
|
||||
net-http (0.2.2)
|
||||
uri
|
||||
net-imap (0.3.0)
|
||||
net-imap (0.3.1)
|
||||
net-protocol
|
||||
net-pop (0.1.2)
|
||||
net-protocol
|
||||
net-protocol (0.1.3)
|
||||
timeout
|
||||
net-smtp (0.3.2)
|
||||
net-smtp (0.3.3)
|
||||
net-protocol
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.8)
|
||||
nokogiri (1.13.9)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.8-aarch64-linux)
|
||||
nokogiri (1.13.9-aarch64-linux)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.8-arm64-darwin)
|
||||
nokogiri (1.13.9-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.8-x86_64-darwin)
|
||||
nokogiri (1.13.9-x86_64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.13.8-x86_64-linux)
|
||||
nokogiri (1.13.9-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
oauth (1.1.0)
|
||||
oauth-tty (~> 1.0, >= 1.0.1)
|
||||
snaky_hash (~> 2.0)
|
||||
version_gem (~> 1.1)
|
||||
oauth-tty (1.0.3)
|
||||
version_gem (~> 1.1)
|
||||
oauth-tty (1.0.5)
|
||||
version_gem (~> 1.1, >= 1.1.1)
|
||||
oauth2 (1.4.11)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
@ -304,7 +310,7 @@ GEM
|
||||
parallel
|
||||
parser (3.1.2.1)
|
||||
ast (~> 2.4.1)
|
||||
pg (1.4.3)
|
||||
pg (1.4.4)
|
||||
progress (3.6.0)
|
||||
pry (0.14.1)
|
||||
coderay (~> 1.1)
|
||||
@ -322,7 +328,7 @@ GEM
|
||||
rack (2.2.4)
|
||||
rack-mini-profiler (3.0.0)
|
||||
rack (>= 1.2.0)
|
||||
rack-protection (3.0.1)
|
||||
rack-protection (3.0.2)
|
||||
rack
|
||||
rack-test (2.0.2)
|
||||
rack (>= 1.3)
|
||||
@ -369,52 +375,53 @@ GEM
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 1.0)
|
||||
rqrcode_core (1.2.0)
|
||||
rspec (3.11.0)
|
||||
rspec-core (~> 3.11.0)
|
||||
rspec-expectations (~> 3.11.0)
|
||||
rspec-mocks (~> 3.11.0)
|
||||
rspec-core (3.11.0)
|
||||
rspec-support (~> 3.11.0)
|
||||
rspec-expectations (3.11.1)
|
||||
rspec (3.12.0)
|
||||
rspec-core (~> 3.12.0)
|
||||
rspec-expectations (~> 3.12.0)
|
||||
rspec-mocks (~> 3.12.0)
|
||||
rspec-core (3.12.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-expectations (3.12.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.11.0)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-html-matchers (0.10.0)
|
||||
nokogiri (~> 1)
|
||||
rspec (>= 3.0.0.a)
|
||||
rspec-mocks (3.11.1)
|
||||
rspec-mocks (3.12.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.11.0)
|
||||
rspec-rails (5.1.2)
|
||||
actionpack (>= 5.2)
|
||||
activesupport (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rspec-core (~> 3.10)
|
||||
rspec-expectations (~> 3.10)
|
||||
rspec-mocks (~> 3.10)
|
||||
rspec-support (~> 3.10)
|
||||
rspec-support (3.11.1)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-rails (6.0.1)
|
||||
actionpack (>= 6.1)
|
||||
activesupport (>= 6.1)
|
||||
railties (>= 6.1)
|
||||
rspec-core (~> 3.11)
|
||||
rspec-expectations (~> 3.11)
|
||||
rspec-mocks (~> 3.11)
|
||||
rspec-support (~> 3.11)
|
||||
rspec-support (3.12.0)
|
||||
rss (0.2.9)
|
||||
rexml
|
||||
rswag-specs (2.6.0)
|
||||
rswag-specs (2.7.0)
|
||||
activesupport (>= 3.1, < 7.1)
|
||||
json-schema (~> 2.2)
|
||||
json-schema (>= 2.2, < 4.0)
|
||||
railties (>= 3.1, < 7.1)
|
||||
rubocop (1.36.0)
|
||||
rspec-core (>= 2.14)
|
||||
rubocop (1.37.1)
|
||||
json (~> 2.3)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 3.1.2.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.20.1, < 2.0)
|
||||
rubocop-ast (>= 1.23.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 3.0)
|
||||
rubocop-ast (1.21.0)
|
||||
rubocop-ast (1.23.0)
|
||||
parser (>= 3.1.1.0)
|
||||
rubocop-discourse (3.0)
|
||||
rubocop (>= 1.1.0)
|
||||
rubocop-rspec (>= 2.0.0)
|
||||
rubocop-rspec (2.13.2)
|
||||
rubocop-rspec (2.14.2)
|
||||
rubocop (~> 1.33)
|
||||
ruby-prof (1.4.3)
|
||||
ruby-progressbar (1.11.0)
|
||||
@ -435,9 +442,6 @@ GEM
|
||||
sprockets (> 3.0)
|
||||
sprockets-rails
|
||||
tilt
|
||||
seed-fu (2.3.9)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
selenium-webdriver (4.5.0)
|
||||
childprocess (>= 0.5, < 5.0)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
@ -455,9 +459,9 @@ GEM
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov_json_formatter (0.1.4)
|
||||
snaky_hash (2.0.0)
|
||||
snaky_hash (2.0.1)
|
||||
hashie
|
||||
version_gem (~> 1.1)
|
||||
version_gem (~> 1.1, >= 1.1.1)
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
@ -466,8 +470,8 @@ GEM
|
||||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkey (2.0.0)
|
||||
stackprof (0.2.21)
|
||||
test-prof (1.0.10)
|
||||
stackprof (0.2.22)
|
||||
test-prof (1.0.11)
|
||||
thor (1.2.1)
|
||||
tilt (2.0.11)
|
||||
timeout (0.3.0)
|
||||
@ -485,8 +489,8 @@ GEM
|
||||
uniform_notifier (1.16.0)
|
||||
uri (0.11.0)
|
||||
uri_template (0.7.0)
|
||||
version_gem (1.1.0)
|
||||
webdrivers (5.1.0)
|
||||
version_gem (1.1.1)
|
||||
webdrivers (5.2.0)
|
||||
nokogiri (~> 1.6)
|
||||
rubyzip (>= 1.3.0)
|
||||
selenium-webdriver (~> 4.0)
|
||||
@ -502,7 +506,7 @@ GEM
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
yaml-lint (0.0.10)
|
||||
zeitwerk (2.6.0)
|
||||
zeitwerk (2.6.3)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux
|
||||
@ -540,9 +544,11 @@ DEPENDENCIES
|
||||
cppjieba_rb
|
||||
css_parser
|
||||
diffy
|
||||
digest
|
||||
discourse-ember-rails (= 0.18.6)
|
||||
discourse-ember-source (~> 3.12.2)
|
||||
discourse-fonts
|
||||
discourse-seed-fu
|
||||
discourse_dev_assets
|
||||
email_reply_trimmer
|
||||
ember-handlebars-template (= 0.8.0)
|
||||
@ -551,6 +557,8 @@ DEPENDENCIES
|
||||
fabrication
|
||||
faker (~> 2.16)
|
||||
fakeweb
|
||||
faraday
|
||||
faraday-retry
|
||||
fast_blank
|
||||
fast_xs
|
||||
fastimage
|
||||
@ -583,6 +591,9 @@ DEPENDENCIES
|
||||
multi_json
|
||||
mustache
|
||||
net-http
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
nokogiri
|
||||
oj (= 3.13.14)
|
||||
omniauth
|
||||
@ -624,7 +635,6 @@ DEPENDENCIES
|
||||
sanitize
|
||||
sassc (= 2.0.1)
|
||||
sassc-rails
|
||||
seed-fu
|
||||
selenium-webdriver
|
||||
shoulda-matchers
|
||||
sidekiq
|
||||
@ -645,4 +655,4 @@ DEPENDENCIES
|
||||
yaml-lint
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.18
|
||||
2.3.22
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
|
||||
@ -10,12 +12,14 @@ export default Component.extend({
|
||||
this.set("editing", false);
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit() {
|
||||
this.set("buffer", this.value);
|
||||
this.toggleProperty("editing");
|
||||
},
|
||||
@action
|
||||
edit(event) {
|
||||
event?.preventDefault();
|
||||
this.set("buffer", this.value);
|
||||
this.toggleProperty("editing");
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
// Action has to toggle 'editing' property.
|
||||
this.action(this.buffer);
|
||||
|
||||
@ -3,6 +3,7 @@ import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { fmt } from "discourse/lib/computed";
|
||||
import { isDocumentRTL } from "discourse/lib/text-direction";
|
||||
import { action } from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
|
||||
export default Component.extend({
|
||||
@ -91,15 +92,26 @@ export default Component.extend({
|
||||
return this.theme.getError(target, fieldName);
|
||||
},
|
||||
|
||||
@action
|
||||
toggleShowAdvanced(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("showAdvanced");
|
||||
},
|
||||
|
||||
@action
|
||||
toggleAddField(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("addingField");
|
||||
},
|
||||
|
||||
@action
|
||||
toggleMaximize(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("maximized");
|
||||
next(() => this.appEvents.trigger("ace:resize"));
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleShowAdvanced() {
|
||||
this.toggleProperty("showAdvanced");
|
||||
},
|
||||
|
||||
toggleAddField() {
|
||||
this.toggleProperty("addingField");
|
||||
},
|
||||
|
||||
cancelAddField() {
|
||||
this.set("addingField", false);
|
||||
},
|
||||
@ -114,11 +126,6 @@ export default Component.extend({
|
||||
this.fieldAdded(this.currentTargetName, name);
|
||||
},
|
||||
|
||||
toggleMaximize() {
|
||||
this.toggleProperty("maximized");
|
||||
next(() => this.appEvents.trigger("ace:resize"));
|
||||
},
|
||||
|
||||
onlyOverriddenChanged(value) {
|
||||
this.onlyOverriddenChanged(value);
|
||||
},
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import Category from "discourse/models/category";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { or } from "@ember/object/computed";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
@ -13,6 +13,7 @@ export default Component.extend(bufferedProperty("host"), {
|
||||
tagName: "tr",
|
||||
categoryId: null,
|
||||
category: null,
|
||||
dialog: service(),
|
||||
|
||||
editing: or("host.isNew", "editToggled"),
|
||||
|
||||
@ -61,12 +62,13 @@ export default Component.extend(bufferedProperty("host"), {
|
||||
},
|
||||
|
||||
delete() {
|
||||
bootbox.confirm(I18n.t("admin.embedding.confirm_delete"), (result) => {
|
||||
if (result) {
|
||||
this.host.destroyRecord().then(() => {
|
||||
return this.dialog.confirm({
|
||||
message: I18n.t("admin.embedding.confirm_delete"),
|
||||
didConfirm: () => {
|
||||
return this.host.destroyRecord().then(() => {
|
||||
this.deleteHost(this.host);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import AdminUser from "admin/models/admin-user";
|
||||
import Component from "@ember/component";
|
||||
import EmberObject from "@ember/object";
|
||||
import EmberObject, { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import copyText from "discourse/lib/copy-text";
|
||||
@ -21,6 +21,12 @@ export default Component.extend({
|
||||
return Math.max(visible, total);
|
||||
},
|
||||
|
||||
@action
|
||||
hide(event) {
|
||||
event?.preventDefault();
|
||||
this.set("show", false);
|
||||
},
|
||||
|
||||
actions: {
|
||||
lookup() {
|
||||
this.set("show", true);
|
||||
@ -55,10 +61,6 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
hide() {
|
||||
this.set("show", false);
|
||||
},
|
||||
|
||||
copy() {
|
||||
let text = `IP: ${this.ip}\n`;
|
||||
const location = this.location;
|
||||
|
||||
@ -3,6 +3,7 @@ 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 { action } from "@ember/object";
|
||||
|
||||
const MAX_COMPONENTS = 4;
|
||||
|
||||
@ -59,9 +60,9 @@ export default Component.extend({
|
||||
return childrenCount - MAX_COMPONENTS;
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleChildrenExpanded() {
|
||||
this.toggleProperty("childrenExpanded");
|
||||
},
|
||||
@action
|
||||
toggleChildrenExpanded(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("childrenExpanded");
|
||||
},
|
||||
});
|
||||
|
||||
@ -133,6 +133,12 @@ export default class AdminBadgesShowController extends Controller.extend(
|
||||
this.buffered.set("image_url", null);
|
||||
}
|
||||
|
||||
@action
|
||||
showPreview(badge, explain, event) {
|
||||
event?.preventDefault();
|
||||
this.send("preview", badge, explain);
|
||||
}
|
||||
|
||||
@action
|
||||
save() {
|
||||
if (!this.saving) {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { action } from "@ember/object";
|
||||
import bootbox from "bootbox";
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Controller.extend(bufferedProperty("emailTemplate"), {
|
||||
adminCustomizeEmailTemplates: controller(),
|
||||
dialog: service(),
|
||||
emailTemplate: null,
|
||||
saved: false,
|
||||
|
||||
@ -42,20 +43,19 @@ export default Controller.extend(bufferedProperty("emailTemplate"), {
|
||||
@action
|
||||
revertChanges() {
|
||||
this.set("saved", false);
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.email_templates.revert_confirm"),
|
||||
(result) => {
|
||||
if (result) {
|
||||
this.emailTemplate
|
||||
.revert()
|
||||
.then((props) => {
|
||||
const buffered = this.buffered;
|
||||
buffered.setProperties(props);
|
||||
this.commitBuffer();
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.dialog.yesNoConfirm({
|
||||
title: I18n.t("admin.customize.email_templates.revert_confirm"),
|
||||
didConfirm: () => {
|
||||
return this.emailTemplate
|
||||
.revert()
|
||||
.then((props) => {
|
||||
const buffered = this.buffered;
|
||||
buffered.setProperties(props);
|
||||
this.commitBuffer();
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -10,7 +10,6 @@ import Controller from "@ember/controller";
|
||||
import EmberObject from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import ThemeSettings from "admin/models/theme-settings";
|
||||
import bootbox from "bootbox";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { makeArray } from "discourse-common/lib/helpers";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
@ -306,14 +305,10 @@ export default Controller.extend({
|
||||
|
||||
editTheme() {
|
||||
if (this.get("model.remote_theme.is_git")) {
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.theme.edit_confirm"),
|
||||
(result) => {
|
||||
if (result) {
|
||||
this.transitionToEditRoute();
|
||||
}
|
||||
}
|
||||
);
|
||||
this.dialog.confirm({
|
||||
message: I18n.t("admin.customize.theme.edit_confirm"),
|
||||
didConfirm: () => this.transitionToEditRoute(),
|
||||
});
|
||||
} else {
|
||||
this.transitionToEditRoute();
|
||||
}
|
||||
|
||||
@ -2,8 +2,15 @@ import AdminEmailLogsController from "admin/controllers/admin-email-logs";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@action
|
||||
handleShowIncomingEmail(id, event) {
|
||||
event?.preventDefault();
|
||||
this.send("showIncomingEmail", id);
|
||||
},
|
||||
|
||||
@observes("filter.{status,user,address,type}")
|
||||
filterEmailLogs() {
|
||||
discourseDebounce(this, this.loadLogs, INPUT_DELAY);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { empty, notEmpty, or } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import EmailPreview from "admin/models/email-preview";
|
||||
import { get } from "@ember/object";
|
||||
import { action, get } from "@ember/object";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
@ -14,6 +14,12 @@ export default Controller.extend({
|
||||
showSendEmailForm: notEmpty("model.html_content"),
|
||||
htmlEmpty: empty("model.html_content"),
|
||||
|
||||
@action
|
||||
toggleShowHtml(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("showHtml");
|
||||
},
|
||||
|
||||
actions: {
|
||||
updateUsername(selected) {
|
||||
this.set("username", get(selected, "firstObject"));
|
||||
@ -39,10 +45,6 @@ export default Controller.extend({
|
||||
});
|
||||
},
|
||||
|
||||
toggleShowHtml() {
|
||||
this.toggleProperty("showHtml");
|
||||
},
|
||||
|
||||
sendEmail() {
|
||||
this.set("sendingEmail", true);
|
||||
this.set("sentEmail", false);
|
||||
|
||||
@ -3,6 +3,7 @@ import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import IncomingEmail from "admin/models/incoming-email";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default AdminEmailLogsController.extend({
|
||||
@observes("filter.{status,from,to,subject,error}")
|
||||
@ -10,6 +11,12 @@ export default AdminEmailLogsController.extend({
|
||||
discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY);
|
||||
},
|
||||
|
||||
@action
|
||||
handleShowIncomingEmail(id, event) {
|
||||
event?.preventDefault();
|
||||
this.send("showIncomingEmail", id);
|
||||
},
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
this.loadLogs(IncomingEmail, true);
|
||||
|
||||
@ -6,6 +6,7 @@ import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Controller.extend({
|
||||
@ -26,6 +27,15 @@ export default Controller.extend({
|
||||
discourseDebounce(this, this._debouncedShow, INPUT_DELAY);
|
||||
},
|
||||
|
||||
@action
|
||||
edit(record, event) {
|
||||
event?.preventDefault();
|
||||
if (!record.get("editing")) {
|
||||
this.set("savedIpAddress", record.get("ip_address"));
|
||||
}
|
||||
record.set("editing", true);
|
||||
},
|
||||
|
||||
actions: {
|
||||
allow(record) {
|
||||
record.set("action_name", "do_nothing");
|
||||
@ -37,13 +47,6 @@ export default Controller.extend({
|
||||
record.save();
|
||||
},
|
||||
|
||||
edit(record) {
|
||||
if (!record.get("editing")) {
|
||||
this.set("savedIpAddress", record.get("ip_address"));
|
||||
}
|
||||
record.set("editing", true);
|
||||
},
|
||||
|
||||
cancel(record) {
|
||||
const savedIpAddress = this.savedIpAddress;
|
||||
if (savedIpAddress && record.get("editing")) {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import Controller from "@ember/controller";
|
||||
import EmberObject from "@ember/object";
|
||||
import EmberObject, { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: ["filters"],
|
||||
@ -31,11 +32,13 @@ export default Controller.extend({
|
||||
this.set(
|
||||
"userHistoryActions",
|
||||
result.extras.user_history_actions
|
||||
.map((action) => ({
|
||||
id: action.id,
|
||||
action_id: action.action_id,
|
||||
name: I18n.t("admin.logs.staff_actions.actions." + action.id),
|
||||
name_raw: action.id,
|
||||
.map((historyAction) => ({
|
||||
id: historyAction.id,
|
||||
action_id: historyAction.action_id,
|
||||
name: I18n.t(
|
||||
"admin.logs.staff_actions.actions." + historyAction.id
|
||||
),
|
||||
name_raw: historyAction.id,
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
);
|
||||
@ -75,61 +78,95 @@ export default Controller.extend({
|
||||
this.scheduleRefresh();
|
||||
},
|
||||
|
||||
actions: {
|
||||
filterActionIdChanged(filterActionId) {
|
||||
if (filterActionId) {
|
||||
this.changeFilters({
|
||||
action_name: filterActionId,
|
||||
action_id: this.userHistoryActions.findBy("id", filterActionId)
|
||||
.action_id,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
clearFilter(key) {
|
||||
if (key === "actionFilter") {
|
||||
this.set("filterActionId", null);
|
||||
this.changeFilters({
|
||||
action_name: null,
|
||||
action_id: null,
|
||||
custom_type: null,
|
||||
});
|
||||
} else {
|
||||
this.changeFilters({ [key]: null });
|
||||
}
|
||||
},
|
||||
|
||||
clearAllFilters() {
|
||||
this.set("filterActionId", null);
|
||||
this.resetFilters();
|
||||
},
|
||||
|
||||
filterByAction(logItem) {
|
||||
@action
|
||||
filterActionIdChanged(filterActionId) {
|
||||
if (filterActionId) {
|
||||
this.changeFilters({
|
||||
action_name: logItem.get("action_name"),
|
||||
action_id: logItem.get("action"),
|
||||
custom_type: logItem.get("custom_type"),
|
||||
action_name: filterActionId,
|
||||
action_id: this.userHistoryActions.findBy("id", filterActionId)
|
||||
.action_id,
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
filterByStaffUser(acting_user) {
|
||||
this.changeFilters({ acting_user: acting_user.username });
|
||||
},
|
||||
@action
|
||||
clearFilter(key, event) {
|
||||
event?.preventDefault();
|
||||
if (key === "actionFilter") {
|
||||
this.set("filterActionId", null);
|
||||
this.changeFilters({
|
||||
action_name: null,
|
||||
action_id: null,
|
||||
custom_type: null,
|
||||
});
|
||||
} else {
|
||||
this.changeFilters({ [key]: null });
|
||||
}
|
||||
},
|
||||
|
||||
filterByTargetUser(target_user) {
|
||||
this.changeFilters({ target_user: target_user.username });
|
||||
},
|
||||
@action
|
||||
clearAllFilters(event) {
|
||||
event?.preventDefault();
|
||||
this.set("filterActionId", null);
|
||||
this.resetFilters();
|
||||
},
|
||||
|
||||
filterBySubject(subject) {
|
||||
this.changeFilters({ subject });
|
||||
},
|
||||
@action
|
||||
filterByAction(logItem, event) {
|
||||
event?.preventDefault();
|
||||
this.changeFilters({
|
||||
action_name: logItem.get("action_name"),
|
||||
action_id: logItem.get("action"),
|
||||
custom_type: logItem.get("custom_type"),
|
||||
});
|
||||
},
|
||||
|
||||
exportStaffActionLogs() {
|
||||
exportEntity("staff_action").then(outputExportResult);
|
||||
},
|
||||
@action
|
||||
filterByStaffUser(acting_user, event) {
|
||||
event?.preventDefault();
|
||||
this.changeFilters({ acting_user: acting_user.username });
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
this.model.loadMore();
|
||||
},
|
||||
@action
|
||||
filterByTargetUser(target_user, event) {
|
||||
event?.preventDefault();
|
||||
this.changeFilters({ target_user: target_user.username });
|
||||
},
|
||||
|
||||
@action
|
||||
filterBySubject(subject, event) {
|
||||
event?.preventDefault();
|
||||
this.changeFilters({ subject });
|
||||
},
|
||||
|
||||
@action
|
||||
exportStaffActionLogs() {
|
||||
exportEntity("staff_action").then(outputExportResult);
|
||||
},
|
||||
|
||||
@action
|
||||
loadMore() {
|
||||
this.model.loadMore();
|
||||
},
|
||||
|
||||
@action
|
||||
showDetailsModal(model, event) {
|
||||
event?.preventDefault();
|
||||
showModal("admin-staff-action-log-details", {
|
||||
model,
|
||||
admin: true,
|
||||
modalClass: "log-details-modal",
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
showCustomDetailsModal(model, event) {
|
||||
event?.preventDefault();
|
||||
let modal = showModal("admin-theme-change", {
|
||||
model,
|
||||
admin: true,
|
||||
modalClass: "history-modal",
|
||||
});
|
||||
modal.loadDiff();
|
||||
},
|
||||
});
|
||||
|
||||
@ -2,9 +2,8 @@ import Controller from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { INPUT_DELAY } from "discourse-common/config/environment";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { debounce, observes } from "discourse-common/utils/decorators";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Controller.extend({
|
||||
@ -113,18 +112,13 @@ export default Controller.extend({
|
||||
},
|
||||
|
||||
@observes("filter", "onlyOverridden", "model")
|
||||
@debounce(INPUT_DELAY)
|
||||
filterContent() {
|
||||
discourseDebounce(
|
||||
this,
|
||||
() => {
|
||||
if (this._skipBounce) {
|
||||
this.set("_skipBounce", false);
|
||||
} else {
|
||||
this.filterContentNow(this.categoryNameKey);
|
||||
}
|
||||
},
|
||||
INPUT_DELAY
|
||||
);
|
||||
if (this._skipBounce) {
|
||||
this.set("_skipBounce", false);
|
||||
} else {
|
||||
this.filterContentNow(this.categoryNameKey);
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { gte, sort } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
const MAX_FIELDS = 30;
|
||||
|
||||
export default Controller.extend({
|
||||
dialog: service(),
|
||||
fieldTypes: null,
|
||||
createDisabled: gte("model.length", MAX_FIELDS),
|
||||
sortedFields: sort("model", "fieldSortOrder"),
|
||||
@ -53,18 +54,17 @@ export default Controller.extend({
|
||||
|
||||
// Only confirm if we already been saved
|
||||
if (f.get("id")) {
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.user_fields.delete_confirm"),
|
||||
function (result) {
|
||||
if (result) {
|
||||
f.destroyRecord()
|
||||
.then(function () {
|
||||
model.removeObject(f);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
);
|
||||
this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.user_fields.delete_confirm"),
|
||||
didConfirm: () => {
|
||||
return f
|
||||
.destroyRecord()
|
||||
.then(function () {
|
||||
model.removeObject(f);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
model.removeObject(f);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import Controller from "@ember/controller";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { action } from "@ember/object";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
@ -43,6 +44,23 @@ export default Controller.extend({
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
showInserted(event) {
|
||||
event?.preventDefault();
|
||||
const webHookId = this.get("model.extras.web_hook_id");
|
||||
|
||||
ajax(`/admin/api/web_hooks/${webHookId}/events/bulk`, {
|
||||
type: "GET",
|
||||
data: { ids: this.incomingEventIds },
|
||||
}).then((data) => {
|
||||
const objects = data.map((webHookEvent) =>
|
||||
this.store.createRecord("web-hook-event", webHookEvent)
|
||||
);
|
||||
this.model.unshiftObjects(objects);
|
||||
this.set("incomingEventIds", []);
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
this.model.loadMore();
|
||||
@ -61,20 +79,5 @@ export default Controller.extend({
|
||||
popupAjaxError(error);
|
||||
});
|
||||
},
|
||||
|
||||
showInserted() {
|
||||
const webHookId = this.get("model.extras.web_hook_id");
|
||||
|
||||
ajax(`/admin/api/web_hooks/${webHookId}/events/bulk`, {
|
||||
type: "GET",
|
||||
data: { ids: this.incomingEventIds },
|
||||
}).then((data) => {
|
||||
const objects = data.map((event) =>
|
||||
this.store.createRecord("web-hook-event", event)
|
||||
);
|
||||
this.model.unshiftObjects(objects);
|
||||
this.set("incomingEventIds", []);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -3,8 +3,6 @@ import EmberObject from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { extractDomainFromUrl } from "discourse/lib/utilities";
|
||||
import { isAbsoluteURL } from "discourse-common/lib/get-url";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
@ -89,38 +87,20 @@ export default Controller.extend({
|
||||
actions: {
|
||||
save() {
|
||||
this.set("saved", false);
|
||||
const url = this.get("model.payload_url");
|
||||
const domain = extractDomainFromUrl(url);
|
||||
const model = this.model;
|
||||
const isNew = model.get("isNew");
|
||||
|
||||
const saveWebHook = () => {
|
||||
return model
|
||||
.save()
|
||||
.then(() => {
|
||||
this.set("saved", true);
|
||||
this.adminWebHooks.get("model").addObject(model);
|
||||
return model
|
||||
.save()
|
||||
.then(() => {
|
||||
this.set("saved", true);
|
||||
this.adminWebHooks.get("model").addObject(model);
|
||||
|
||||
if (isNew) {
|
||||
this.transitionToRoute("adminWebHooks.show", model.get("id"));
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
};
|
||||
|
||||
if (
|
||||
domain === "localhost" ||
|
||||
domain.match(/192\.168\.\d+\.\d+/) ||
|
||||
domain.match(/127\.\d+\.\d+\.\d+/) ||
|
||||
isAbsoluteURL(url)
|
||||
) {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.web_hooks.warn_local_payload_url"),
|
||||
didConfirm: () => saveWebHook(),
|
||||
});
|
||||
}
|
||||
|
||||
return saveWebHook();
|
||||
if (isNew) {
|
||||
this.transitionToRoute("adminWebHooks.show", model.get("id"));
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
|
||||
@ -31,6 +31,7 @@ export default Controller.extend(ModalFunctionality, {
|
||||
advancedVisible: false,
|
||||
selectedType: alias("themesController.currentTab"),
|
||||
component: equal("selectedType", COMPONENTS),
|
||||
urlPlaceholder: "https://github.com/discourse/sample_theme",
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
@ -79,29 +80,6 @@ export default Controller.extend(ModalFunctionality, {
|
||||
);
|
||||
},
|
||||
|
||||
@discourseComputed("privateChecked")
|
||||
urlPlaceholder(privateChecked) {
|
||||
return privateChecked
|
||||
? "git@github.com:discourse/sample_theme.git"
|
||||
: "https://github.com/discourse/sample_theme";
|
||||
},
|
||||
|
||||
@observes("privateChecked")
|
||||
privateWasChecked() {
|
||||
const checked = this.privateChecked;
|
||||
if (checked && !this._keyLoading) {
|
||||
this._keyLoading = true;
|
||||
ajax(this.keyGenUrl, { type: "POST" })
|
||||
.then((pair) => {
|
||||
this.set("publicKey", pair.public_key);
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this._keyLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("name")
|
||||
nameTooShort(name) {
|
||||
return !name || name.length < MIN_NAME_LENGTH;
|
||||
@ -116,6 +94,22 @@ export default Controller.extend(ModalFunctionality, {
|
||||
}
|
||||
},
|
||||
|
||||
@observes("checkPrivate")
|
||||
privateWasChecked() {
|
||||
const checked = this.checkPrivate;
|
||||
if (checked && !this._keyLoading && !this.publicKey) {
|
||||
this._keyLoading = true;
|
||||
ajax(this.keyGenUrl, { type: "POST" })
|
||||
.then((pair) => {
|
||||
this.set("publicKey", pair.public_key);
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => {
|
||||
this._keyLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("selection", "themeCannotBeInstalled")
|
||||
submitLabel(selection, themeCannotBeInstalled) {
|
||||
if (themeCannotBeInstalled) {
|
||||
@ -127,15 +121,14 @@ export default Controller.extend(ModalFunctionality, {
|
||||
}`;
|
||||
},
|
||||
|
||||
@discourseComputed("privateChecked", "checkPrivate", "publicKey")
|
||||
showPublicKey(privateChecked, checkPrivate, publicKey) {
|
||||
return privateChecked && checkPrivate && publicKey;
|
||||
@discourseComputed("checkPrivate", "publicKey")
|
||||
showPublicKey(checkPrivate, publicKey) {
|
||||
return checkPrivate && publicKey;
|
||||
},
|
||||
|
||||
onClose() {
|
||||
this.setProperties({
|
||||
duplicateRemoteThemeWarning: null,
|
||||
privateChecked: false,
|
||||
localFile: null,
|
||||
uploadUrl: null,
|
||||
publicKey: null,
|
||||
@ -209,11 +202,8 @@ export default Controller.extend(ModalFunctionality, {
|
||||
options.data = {
|
||||
remote: this.uploadUrl,
|
||||
branch: this.branch,
|
||||
public_key: this.publicKey,
|
||||
};
|
||||
|
||||
if (this.privateChecked) {
|
||||
options.data.public_key = this.publicKey;
|
||||
}
|
||||
}
|
||||
|
||||
// User knows that theme cannot be installed, but they want to continue
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { observes, on } from "discourse-common/utils/decorators";
|
||||
import Controller from "@ember/controller";
|
||||
import { action } from "@ember/object";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
@ -10,15 +11,17 @@ export default Controller.extend(ModalFunctionality, {
|
||||
this.set("images", value && value.length ? value.split("|") : []);
|
||||
},
|
||||
|
||||
@action
|
||||
remove(url, event) {
|
||||
event?.preventDefault();
|
||||
this.images.removeObject(url);
|
||||
},
|
||||
|
||||
actions: {
|
||||
uploadDone({ url }) {
|
||||
this.images.addObject(url);
|
||||
},
|
||||
|
||||
remove(url) {
|
||||
this.images.removeObject(url);
|
||||
},
|
||||
|
||||
close() {
|
||||
this.save(this.images.join("|"));
|
||||
this.send("closeModal");
|
||||
|
||||
@ -2,11 +2,12 @@ import I18n from "I18n";
|
||||
import Mixin from "@ember/object/mixin";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { Promise } from "rsvp";
|
||||
import bootbox from "bootbox";
|
||||
import { extractError } from "discourse/lib/ajax-error";
|
||||
import { next } from "@ember/runloop";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Mixin.create(ModalFunctionality, {
|
||||
dialog: service(),
|
||||
errorMessage: null,
|
||||
reason: null,
|
||||
message: null,
|
||||
@ -40,15 +41,15 @@ export default Mixin.create(ModalFunctionality, {
|
||||
(this.message && this.message.length > 1))
|
||||
) {
|
||||
this.send("hideModal");
|
||||
bootbox.confirm(I18n.t("admin.user.confirm_cancel_penalty"), (result) => {
|
||||
if (result) {
|
||||
this.dialog.confirm({
|
||||
message: I18n.t("admin.user.confirm_cancel_penalty"),
|
||||
didConfirm: () => {
|
||||
next(() => {
|
||||
this.set("confirmClose", true);
|
||||
this.send("closeModal");
|
||||
});
|
||||
} else {
|
||||
next(() => this.send("reopenModal"));
|
||||
}
|
||||
},
|
||||
didCancel: () => this.send("reopenModal"),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -157,7 +157,10 @@ export default Mixin.create({
|
||||
"default_tags_watching_first_post",
|
||||
"default_text_size",
|
||||
"default_title_count_mode",
|
||||
"default_sidebar_categories",
|
||||
"default_sidebar_tags",
|
||||
];
|
||||
|
||||
const key = this.buffered.get("setting");
|
||||
|
||||
if (defaultUserPreferences.includes(key)) {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import I18n from "I18n";
|
||||
import Route from "@ember/routing/route";
|
||||
import bootbox from "bootbox";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Route.extend({
|
||||
dialog: service(),
|
||||
model(params) {
|
||||
return {
|
||||
model: this.modelFor("adminCustomizeEmailStyle"),
|
||||
@ -26,17 +27,15 @@ export default Route.extend({
|
||||
transition.intent.name !== this.routeName
|
||||
) {
|
||||
transition.abort();
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.theme.unsaved_changes_alert"),
|
||||
I18n.t("admin.customize.theme.discard"),
|
||||
I18n.t("admin.customize.theme.stay"),
|
||||
(result) => {
|
||||
if (!result) {
|
||||
this._shouldAlertUnsavedChanges = false;
|
||||
transition.retry();
|
||||
}
|
||||
}
|
||||
);
|
||||
this.dialog.confirm({
|
||||
message: I18n.t("admin.customize.theme.unsaved_changes_alert"),
|
||||
confirmButtonLabel: "admin.customize.theme.discard",
|
||||
cancelButtonLabel: "admin.customize.theme.stay",
|
||||
didConfirm: () => {
|
||||
this._shouldAlertUnsavedChanges = false;
|
||||
transition.retry();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import I18n from "I18n";
|
||||
import Route from "@ember/routing/route";
|
||||
import bootbox from "bootbox";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Route.extend({
|
||||
dialog: service(),
|
||||
|
||||
model(params) {
|
||||
const all = this.modelFor("adminCustomizeThemes");
|
||||
const model = all.findBy("id", parseInt(params.theme_id, 10));
|
||||
@ -56,17 +58,16 @@ export default Route.extend({
|
||||
transition.intent.name !== this.routeName
|
||||
) {
|
||||
transition.abort();
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.theme.unsaved_changes_alert"),
|
||||
I18n.t("admin.customize.theme.discard"),
|
||||
I18n.t("admin.customize.theme.stay"),
|
||||
(result) => {
|
||||
if (!result) {
|
||||
this.set("shouldAlertUnsavedChanges", false);
|
||||
transition.retry();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.dialog.confirm({
|
||||
message: I18n.t("admin.customize.theme.unsaved_changes_alert"),
|
||||
confirmButtonLabel: "admin.customize.theme.discard",
|
||||
cancelButtonLabel: "admin.customize.theme.stay",
|
||||
didConfirm: () => {
|
||||
this.set("shouldAlertUnsavedChanges", false);
|
||||
transition.retry();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -2,23 +2,11 @@ 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(
|
||||
I18n.t("admin.customize.theme.unsaved_parent_themes"),
|
||||
I18n.t("admin.customize.theme.discard"),
|
||||
I18n.t("admin.customize.theme.stay"),
|
||||
(result) => {
|
||||
if (!result) {
|
||||
theme.set("recentlyInstalled", false);
|
||||
}
|
||||
callback(result);
|
||||
}
|
||||
);
|
||||
}
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Route.extend({
|
||||
dialog: service(),
|
||||
|
||||
serialize(model) {
|
||||
return { theme_id: model.get("id") };
|
||||
},
|
||||
@ -72,10 +60,14 @@ export default Route.extend({
|
||||
const model = this.controller.model;
|
||||
if (model.warnUnassignedComponent) {
|
||||
transition.abort();
|
||||
showUnassignedComponentWarning(model, (result) => {
|
||||
if (!result) {
|
||||
|
||||
this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.customize.theme.unsaved_parent_themes"),
|
||||
didConfirm: () => {
|
||||
model.set("recentlyInstalled", false);
|
||||
transition.retry();
|
||||
}
|
||||
},
|
||||
didCancel: () => model.set("recentlyInstalled", false),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import Route from "@ember/routing/route";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import I18n from "I18n";
|
||||
import { next } from "@ember/runloop";
|
||||
import { showUnassignedComponentWarning } from "admin/routes/admin-customize-themes-show";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Route.extend({
|
||||
dialog: service(),
|
||||
|
||||
queryParams: {
|
||||
repoUrl: null,
|
||||
repoName: null,
|
||||
@ -35,11 +38,13 @@ export default Route.extend({
|
||||
const currentTheme = this.controllerFor(
|
||||
"adminCustomizeThemes.show"
|
||||
).model;
|
||||
if (currentTheme && currentTheme.warnUnassignedComponent) {
|
||||
showUnassignedComponentWarning(currentTheme, (result) => {
|
||||
if (!result) {
|
||||
if (currentTheme?.warnUnassignedComponent) {
|
||||
this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.customize.theme.unsaved_parent_themes"),
|
||||
didConfirm: () => {
|
||||
currentTheme.set("recentlyInstalled", false);
|
||||
showModal("admin-install-theme", { admin: true });
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
showModal("admin-install-theme", { admin: true });
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import EmberObject from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
queryParams: {
|
||||
@ -43,17 +42,6 @@ export default DiscourseRoute.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
showDetailsModal(model) {
|
||||
showModal("admin-staff-action-log-details", { model, admin: true });
|
||||
this.controllerFor("modal").set("modalClass", "log-details-modal");
|
||||
},
|
||||
|
||||
showCustomDetailsModal(model) {
|
||||
let modal = showModal("admin-theme-change", { model, admin: true });
|
||||
this.controllerFor("modal").set("modalClass", "history-modal");
|
||||
modal.loadDiff();
|
||||
},
|
||||
|
||||
onFiltersChange(filters) {
|
||||
if (filters && Object.keys(filters) === 0) {
|
||||
this.transitionTo("adminLogs.staffActionLogs");
|
||||
|
||||
@ -3,10 +3,9 @@ import I18n from "I18n";
|
||||
import { Promise } from "rsvp";
|
||||
import Service, { inject as service } from "@ember/service";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { getOwner } from "discourse-common/lib/get-owner";
|
||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
// A service that can act as a bridge between the front end Discourse application
|
||||
// and the admin application. Use this if you need front end code to access admin
|
||||
@ -79,58 +78,50 @@ export default Service.extend({
|
||||
: Promise.resolve();
|
||||
|
||||
return tryEmail.then(() => {
|
||||
let message = I18n.messageFormat("flagging.delete_confirm_MF", {
|
||||
POSTS: adminUser.get("post_count"),
|
||||
TOPICS: adminUser.get("topic_count"),
|
||||
email:
|
||||
adminUser.get("email") || I18n.t("flagging.hidden_email_address"),
|
||||
ip_address:
|
||||
adminUser.get("ip_address") || I18n.t("flagging.ip_address_missing"),
|
||||
});
|
||||
let message = htmlSafe(
|
||||
I18n.messageFormat("flagging.delete_confirm_MF", {
|
||||
POSTS: adminUser.get("post_count"),
|
||||
TOPICS: adminUser.get("topic_count"),
|
||||
email:
|
||||
adminUser.get("email") || I18n.t("flagging.hidden_email_address"),
|
||||
ip_address:
|
||||
adminUser.get("ip_address") ||
|
||||
I18n.t("flagging.ip_address_missing"),
|
||||
})
|
||||
);
|
||||
|
||||
let userId = adminUser.get("id");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const buttons = [
|
||||
{
|
||||
label: I18n.t("composer.cancel"),
|
||||
class: "d-modal-cancel",
|
||||
link: true,
|
||||
},
|
||||
{
|
||||
label:
|
||||
`${iconHTML("exclamation-triangle")} ` +
|
||||
I18n.t("flagging.yes_delete_spammer"),
|
||||
class: "btn btn-danger confirm-delete",
|
||||
callback() {
|
||||
return ajax(`/admin/users/${userId}.json`, {
|
||||
type: "DELETE",
|
||||
data: {
|
||||
delete_posts: true,
|
||||
block_email: true,
|
||||
block_urls: true,
|
||||
block_ip: true,
|
||||
delete_as_spammer: true,
|
||||
context: window.location.pathname,
|
||||
},
|
||||
this.dialog.deleteConfirm({
|
||||
message,
|
||||
class: "flagging-delete-spammer",
|
||||
confirmButtonLabel: "flagging.yes_delete_spammer",
|
||||
confirmButtonIcon: "exclamation-triangle",
|
||||
didConfirm: () => {
|
||||
return ajax(`/admin/users/${userId}.json`, {
|
||||
type: "DELETE",
|
||||
data: {
|
||||
delete_posts: true,
|
||||
block_email: true,
|
||||
block_urls: true,
|
||||
block_ip: true,
|
||||
delete_as_spammer: true,
|
||||
context: window.location.pathname,
|
||||
},
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.deleted) {
|
||||
resolve();
|
||||
} else {
|
||||
throw new Error("failed to delete");
|
||||
}
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.deleted) {
|
||||
resolve();
|
||||
} else {
|
||||
throw new Error("failed to delete");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.dialog.alert(I18n.t("admin.user.delete_failed"));
|
||||
reject();
|
||||
});
|
||||
},
|
||||
.catch(() => {
|
||||
this.dialog.alert(I18n.t("admin.user.delete_failed"));
|
||||
reject();
|
||||
});
|
||||
},
|
||||
];
|
||||
|
||||
bootbox.dialog(message, buttons, {
|
||||
classes: "flagging-delete-spammer",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -88,9 +88,9 @@
|
||||
</div>
|
||||
|
||||
{{#if this.hasQuery}}
|
||||
<a href {{action "preview" this.buffered "false"}}>{{i18n "admin.badges.preview.link_text"}}</a>
|
||||
<a href {{on "click" (fn this.showPreview this.buffered "false")}}>{{i18n "admin.badges.preview.link_text"}}</a>
|
||||
|
|
||||
<a href {{action "preview" this.buffered "true"}}>{{i18n "admin.badges.preview.plan_text"}}</a>
|
||||
<a href {{on "click" (fn this.showPreview this.buffered "true")}}>{{i18n "admin.badges.preview.plan_text"}}</a>
|
||||
{{#if this.preview_loading}}
|
||||
{{i18n "loading"}}
|
||||
{{/if}}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{{#if this.editing}}
|
||||
<TextField @value={{this.buffer}} @autofocus="autofocus" @autocomplete="off" />
|
||||
{{else}}
|
||||
<a href {{action "edit"}} class="inline-editable-field">
|
||||
<a href {{on "click" this.edit}} class="inline-editable-field">
|
||||
<span>{{this.value}}</span>
|
||||
</a>
|
||||
{{/if}}
|
||||
@ -11,7 +11,7 @@
|
||||
<div class="controls">
|
||||
{{#if this.editing}}
|
||||
<DButton @class="btn-default" @action={{action "save"}} @label="admin.user_fields.save" />
|
||||
<a href {{action "edit"}}>{{i18n "cancel"}}</a>
|
||||
<a href {{on "click" this.edit}}>{{i18n "cancel"}}</a>
|
||||
{{else}}
|
||||
<DButton @class="btn-default" @action={{action "edit"}} @icon="pencil-alt" />
|
||||
{{/if}}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
{{#if this.allowAdvanced}}
|
||||
<li>
|
||||
<a {{action "toggleShowAdvanced"}}
|
||||
<a {{on "click" this.toggleShowAdvanced}}
|
||||
href
|
||||
title={{i18n (concat "admin.customize.theme." (if this.showAdvanced "hide_advanced" "show_advanced"))}}
|
||||
class="no-text">
|
||||
@ -52,7 +52,7 @@
|
||||
<DButton @class="ok" @action={{action "addField" this.newFieldName}} @icon="check" />
|
||||
<DButton @class="cancel" @action={{action "cancelAddField"}} @icon="times" />
|
||||
{{else}}
|
||||
<a href {{action "toggleAddField" this.currentTargetName}} class="no-text">
|
||||
<a href {{on "click" this.toggleAddField}} class="no-text">
|
||||
{{d-icon "plus"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
@ -61,7 +61,7 @@
|
||||
|
||||
<li class="spacer"></li>
|
||||
<li>
|
||||
<a href {{action "toggleMaximize"}} class="no-text">
|
||||
<a href {{on "click" this.toggleMaximize}} class="no-text">
|
||||
{{d-icon this.maximizeIcon}}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{{/if}}
|
||||
{{#if this.show}}
|
||||
<div class="location-box">
|
||||
<a href class="close pull-right" {{action "hide"}}>{{d-icon "times"}}</a>
|
||||
<a href class="close pull-right" {{on "click" this.hide}}>{{d-icon "times"}}</a>
|
||||
{{#if this.copied}}
|
||||
<DButton @class="btn-hover pull-right" @icon="copy" @label="ip_lookup.copied" />
|
||||
{{else}}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<span class="components">{{html-safe this.childrenString}}</span>
|
||||
|
||||
{{#if this.displayHasMore}}
|
||||
<a href {{action "toggleChildrenExpanded"}} class="others-count">
|
||||
<a href {{on "click" this.toggleChildrenExpanded}} class="others-count">
|
||||
{{#if this.childrenExpanded}}
|
||||
{{i18n "admin.customize.theme.collapse"}}
|
||||
{{else}}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<div class="themes-list-header">
|
||||
<DButton @action={{action "changeView"}} @actionParam={{this.THEMES}} @class={{concat "themes-tab " "tab " (if this.themesTabActive "btn-danger active" "")}} @label="admin.customize.theme.title" />
|
||||
<DButton @action={{action "changeView"}} @actionParam={{this.COMPONENTS}} @class={{concat "components-tab " "tab " (if this.componentsTabActive "btn-danger active" "")}} @label="admin.customize.theme.components" @icon="puzzle-piece" />
|
||||
<DButton @action={{action "changeView"}} @actionParam={{this.THEMES}} @class={{concat "themes-tab " "tab " (if this.themesTabActive "active" "")}} @label="admin.customize.theme.title" />
|
||||
<DButton @action={{action "changeView"}} @actionParam={{this.COMPONENTS}} @class={{concat "components-tab " "tab " (if this.componentsTabActive "active" "")}} @label="admin.customize.theme.components" @icon="puzzle-piece" />
|
||||
</div>
|
||||
|
||||
<div class="themes-list-container">
|
||||
|
||||
@ -282,6 +282,7 @@
|
||||
{{#if this.hasSettings}}
|
||||
<div class="control-unit">
|
||||
<div class="mini-title">{{i18n "admin.customize.theme.theme_settings"}}</div>
|
||||
<p><i>{{i18n "admin.customize.theme.overriden_settings_explanation"}}</i></p>
|
||||
<DSection @class="form-horizontal theme settings control-unit">
|
||||
{{#each this.settings as |setting|}}
|
||||
<ThemeSettingEditor @setting={{setting}} @model={{this.model}} @class="theme-setting control-unit" />
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
<td class="email-address"><a href="mailto:{{l.to_address}}">{{l.to_address}}</a></td>
|
||||
<td>
|
||||
{{#if l.has_bounce_key}}
|
||||
<a href {{action "showIncomingEmail" l.id}}>
|
||||
<a href {{on "click" (fn this.handleShowIncomingEmail l.id)}}>
|
||||
{{l.email_type}}
|
||||
</a>
|
||||
{{else}}
|
||||
@ -39,7 +39,7 @@
|
||||
</td>
|
||||
<td class="email-details">
|
||||
{{#if l.has_bounce_key}}
|
||||
<a href {{action "showIncomingEmail" l.id}} title={{i18n "admin.email.details_title"}}>
|
||||
<a href {{on "click" (fn this.handleShowIncomingEmail l.id)}} title={{i18n "admin.email.details_title"}}>
|
||||
{{d-icon "info-circle"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
@ -15,11 +15,11 @@
|
||||
{{#if this.showHtml}}
|
||||
<span>{{i18n "admin.email.html"}}</span>
|
||||
|
|
||||
<a href {{action "toggleShowHtml"}}>
|
||||
<a href {{on "click" this.toggleShowHtml}}>
|
||||
{{i18n "admin.email.text"}}
|
||||
</a>
|
||||
{{else}}
|
||||
<a href {{action "toggleShowHtml"}}>{{i18n "admin.email.html"}}</a> |
|
||||
<a href {{on "click" this.toggleShowHtml}}>{{i18n "admin.email.html"}}</a> |
|
||||
<span>{{i18n "admin.email.text"}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
@ -48,10 +48,10 @@
|
||||
</td>
|
||||
<td>{{email.subject}}</td>
|
||||
<td class="error">
|
||||
<a href {{action "showIncomingEmail" email.id}}>{{email.error}}</a>
|
||||
<a href {{on "click" (fn this.handleShowIncomingEmail email.id)}}>{{email.error}}</a>
|
||||
</td>
|
||||
<td class="email-details">
|
||||
<a href {{action "showIncomingEmail" email.id}} title={{i18n "admin.email.details_title"}}>
|
||||
<a href {{on "click" (fn this.handleShowIncomingEmail email.id)}} title={{i18n "admin.email.details_title"}}>
|
||||
{{d-icon "info-circle"}}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
{{#if item.editing}}
|
||||
<TextField @value={{item.ip_address}} @autofocus="autofocus" />
|
||||
{{else}}
|
||||
<a href {{action "edit" item}} class="inline-editable-field">
|
||||
<a href {{on "click" (fn this.edit item)}} class="inline-editable-field">
|
||||
{{#if item.isRange}}
|
||||
<strong>{{item.ip_address}}</strong>
|
||||
{{else}}
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
<div class="staff-action-logs-controls">
|
||||
{{#if this.filtersExists}}
|
||||
<div class="staff-action-logs-filters">
|
||||
<a href {{action "clearAllFilters"}} class="clear-filters filter btn">
|
||||
<a href {{on "click" this.clearAllFilters}} class="clear-filters filter btn">
|
||||
<span class="label">{{i18n "admin.logs.staff_actions.clear_filters"}}</span>
|
||||
</a>
|
||||
{{#if this.actionFilter}}
|
||||
<a href {{action "clearFilter" "actionFilter"}} class="filter btn">
|
||||
<a href {{on "click" (fn this.clearFilter "actionFilter")}} class="filter btn">
|
||||
<span class="label">{{i18n "admin.logs.action"}}</span>: {{this.actionFilter}}
|
||||
{{d-icon "times-circle"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if this.filters.acting_user}}
|
||||
<a href {{action "clearFilter" "acting_user"}} class="filter btn">
|
||||
<a href {{on "click" (fn this.clearFilter "acting_user")}} class="filter btn">
|
||||
<span class="label">{{i18n "admin.logs.staff_actions.staff_user"}}</span>: {{this.filters.acting_user}}
|
||||
{{d-icon "times-circle"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if this.filters.target_user}}
|
||||
<a href {{action "clearFilter" "target_user"}} class="filter btn">
|
||||
<a href {{on "click" (fn this.clearFilter "target_user")}} class="filter btn">
|
||||
<span class="label">{{i18n "admin.logs.staff_actions.target_user"}}</span>: {{this.filters.target_user}}
|
||||
{{d-icon "times-circle"}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if this.filters.subject}}
|
||||
<a href {{action "clearFilter" "subject"}} class="filter btn">
|
||||
<a href {{on "click" (fn this.clearFilter "subject")}} class="filter btn">
|
||||
<span class="label">{{i18n "admin.logs.staff_actions.subject"}}</span>: {{this.filters.subject}}
|
||||
{{d-icon "times-circle"}}
|
||||
</a>
|
||||
@ -71,16 +71,16 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="col value action">
|
||||
<a href {{action "filterByAction" item}}>{{item.actionName}}</a>
|
||||
<a href {{on "click" (fn this.filterByAction item)}}>{{item.actionName}}</a>
|
||||
</td>
|
||||
<td class="col value subject">
|
||||
<div class="subject">
|
||||
{{#if item.target_user}}
|
||||
<LinkTo @route="adminUser" @model={{item.target_user}}>{{avatar item.target_user imageSize="tiny"}}</LinkTo>
|
||||
<a href {{action "filterByTargetUser" item.target_user}}>{{item.target_user.username}}</a>
|
||||
<a href {{on "click" (fn this.filterByTargetUser item.target_user)}}>{{item.target_user.username}}</a>
|
||||
{{/if}}
|
||||
{{#if item.subject}}
|
||||
<a href {{action "filterBySubject" item.subject}} title={{item.subject}}>{{item.subject}}</a>
|
||||
<a href {{on "click" (fn this.filterBySubject item.subject)}} title={{item.subject}}>{{item.subject}}</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
@ -89,10 +89,10 @@
|
||||
<div>
|
||||
{{html-safe item.formattedDetails}}
|
||||
{{#if item.useCustomModalForDetails}}
|
||||
<a href {{action "showCustomDetailsModal" item}}>{{d-icon "info-circle"}} {{i18n "admin.logs.staff_actions.show"}}</a>
|
||||
<a href {{on "click" (fn this.showCustomDetailsModal item)}}>{{d-icon "info-circle"}} {{i18n "admin.logs.staff_actions.show"}}</a>
|
||||
{{/if}}
|
||||
{{#if item.useModalForDetails}}
|
||||
<a href {{action "showDetailsModal" item}}>{{d-icon "info-circle"}} {{i18n "admin.logs.staff_actions.show"}}</a>
|
||||
<a href {{on "click" (fn this.showDetailsModal item)}}>{{d-icon "info-circle"}} {{i18n "admin.logs.staff_actions.show"}}</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@ -61,25 +61,15 @@
|
||||
<div class="label">{{i18n "admin.customize.theme.remote_branch"}}</div>
|
||||
<Input @value={{this.branch}} placeholder="main" />
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="check-private">
|
||||
<label>
|
||||
<Input @type="checkbox" @checked={{this.privateChecked}} />
|
||||
{{i18n "admin.customize.theme.is_private"}}
|
||||
</label>
|
||||
</div>
|
||||
{{#if this.showPublicKey}}
|
||||
<div class="public-key">
|
||||
<div class="label">{{i18n "admin.customize.theme.public_key"}}</div>
|
||||
<div class="public-key-text-wrapper">
|
||||
<Textarea class="public-key-value" readonly={{true}} @value={{this.publicKey}} /> <CopyButton @selector="textarea.public-key-value" />
|
||||
</div>
|
||||
{{#if this.showPublicKey}}
|
||||
<div class="public-key">
|
||||
<div class="label">{{i18n "admin.customize.theme.public_key"}}</div>
|
||||
<div class="public-key-text-wrapper">
|
||||
<Textarea class="public-key-value" readonly={{true}} @value={{this.publicKey}} /> <CopyButton @selector="textarea.public-key-value" />
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if this.privateChecked}}
|
||||
<div class="public-key-note">{{i18n "admin.customize.theme.public_key_note"}}</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<DModalBody @class="uploaded-image-list">
|
||||
<div class="selectable-avatars">
|
||||
{{#each this.images as |image|}}
|
||||
<a href class="selectable-avatar" {{action "remove" image}}>
|
||||
<a href class="selectable-avatar" {{on "click" (fn this.remove image)}}>
|
||||
{{bound-avatar-template image "huge"}}
|
||||
</a>
|
||||
{{else}}
|
||||
|
||||
@ -15,20 +15,24 @@
|
||||
|
||||
<LoadMore @class="users-list-container" @selector=".users-list tr" @action={{action "loadMore"}}>
|
||||
{{#if this.model}}
|
||||
<table class="table users-list grid">
|
||||
<table class="table users-list grid" role="table" aria-label={{this.title}}>
|
||||
<thead>
|
||||
<TableHeaderToggle @field="username" @labelKey="username" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @class={{if this.showEmails "" "hidden"}} @field="email" @labelKey="email" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="last_emailed" @labelKey="admin.users.last_emailed" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="seen" @labelKey="last_seen" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="topics_viewed" @labelKey="admin.user.topics_entered" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="posts_read" @labelKey="admin.user.posts_read_count" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="read_time" @labelKey="admin.user.time_read" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="created" @labelKey="created" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
{{#if this.siteSettings.must_approve_users}}
|
||||
<th>{{i18n "admin.users.approved"}}</th>
|
||||
{{/if}}
|
||||
<th> </th>
|
||||
<tr>
|
||||
<TableHeaderToggle @field="username" @labelKey="username" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @class={{if this.showEmails "" "hidden"}} @field="email" @labelKey="email" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="last_emailed" @labelKey="admin.users.last_emailed" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="seen" @labelKey="last_seen" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="topics_viewed" @labelKey="admin.user.topics_entered" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="posts_read" @labelKey="admin.user.posts_read_count" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="read_time" @labelKey="admin.user.time_read" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<TableHeaderToggle @field="created" @labelKey="created" @order={{this.order}} @asc={{this.asc}} @automatic={{true}} />
|
||||
<PluginOutlet @name="admin-users-list-thead-after" @args={{hash order=this.order asc=this.asc}} />
|
||||
|
||||
{{#if this.siteSettings.must_approve_users}}
|
||||
<th>{{i18n "admin.users.approved"}}</th>
|
||||
{{/if}}
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.model as |user|}}
|
||||
@ -80,6 +84,8 @@
|
||||
<div>{{format-duration user.created_at_age}}</div>
|
||||
</td>
|
||||
|
||||
<PluginOutlet @name="admin-users-list-td-after" @args={{hash user=user query=this.query}} />
|
||||
|
||||
{{#if this.siteSettings.must_approve_users}}
|
||||
<td>{{i18n-yes-no user.approved}}</td>
|
||||
{{/if}}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{{#if this.hasIncoming}}
|
||||
<a href tabindex="0" {{action "showInserted"}} class="alert alert-info clickable">
|
||||
<a href tabindex="0" {{on "click" this.showInserted}} class="alert alert-info clickable">
|
||||
<CountI18n @key="admin.web_hooks.events.incoming" @count={{this.incomingCount}} />
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"start": "ember serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"ember-auto-import": "^2.4.2",
|
||||
"ember-auto-import": "^2.4.3",
|
||||
"ember-cli-babel": "^7.26.10",
|
||||
"ember-cli-htmlbars": "^6.1.1",
|
||||
"webpack": "^5.74.0",
|
||||
|
||||
@ -13,18 +13,15 @@ export function setEnvironment(e) {
|
||||
}
|
||||
|
||||
export function isTesting() {
|
||||
// eslint-disable-next-line no-undef
|
||||
return Ember.testing || environment === "testing";
|
||||
return environment === "testing";
|
||||
}
|
||||
|
||||
// Generally means "before we migrated to Ember CLI"
|
||||
// eslint-disable-next-line no-undef
|
||||
let _isLegacy = Ember.VERSION.startsWith("3.12");
|
||||
export function isLegacyEmber() {
|
||||
deprecated("`isLegacyEmber()` is now deprecated and always returns true", {
|
||||
deprecated("`isLegacyEmber()` is now deprecated and always returns false", {
|
||||
dropFrom: "3.0.0.beta1",
|
||||
});
|
||||
return _isLegacy;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isDevelopment() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* global Ember */
|
||||
import Ember from "ember";
|
||||
import { classify, dasherize, decamelize } from "@ember/string";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
import { findHelper } from "discourse-common/lib/helpers";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/* global Ember */
|
||||
import Ember from "ember";
|
||||
import { dasherize, decamelize } from "@ember/string";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
import { findHelper } from "discourse-common/lib/helpers";
|
||||
@ -119,8 +119,19 @@ export function clearResolverOptions() {
|
||||
function lookupModuleBySuffix(suffix) {
|
||||
if (!moduleSuffixTrie) {
|
||||
moduleSuffixTrie = new SuffixTrie("/");
|
||||
const searchPaths = [
|
||||
"discourse/", // Includes themes/plugins
|
||||
"discourse-common/",
|
||||
"select-kit/",
|
||||
"admin/",
|
||||
"wizard/",
|
||||
"truth-helpers/",
|
||||
];
|
||||
Object.keys(requirejs.entries).forEach((name) => {
|
||||
if (!name.includes("/templates/")) {
|
||||
if (
|
||||
searchPaths.some((s) => name.startsWith(s)) &&
|
||||
!name.includes("/templates/")
|
||||
) {
|
||||
moduleSuffixTrie.add(name);
|
||||
}
|
||||
});
|
||||
|
||||
@ -36,6 +36,7 @@ import extractValue from "discourse-common/utils/extract-value";
|
||||
import handleDescriptor from "discourse-common/utils/handle-descriptor";
|
||||
import isDescriptor from "discourse-common/utils/is-descriptor";
|
||||
import macroAlias from "discourse-common/utils/macro-alias";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
|
||||
export default function discourseComputedDecorator(...params) {
|
||||
// determine if user called as @discourseComputed('blah', 'blah') or @discourseComputed
|
||||
@ -87,6 +88,24 @@ export function readOnly(target, name, desc) {
|
||||
};
|
||||
}
|
||||
|
||||
export function debounce(delay) {
|
||||
return function (target, name, descriptor) {
|
||||
return {
|
||||
enumerable: descriptor.enumerable,
|
||||
configurable: descriptor.configurable,
|
||||
writable: descriptor.writable,
|
||||
initializer() {
|
||||
const originalFunction = descriptor.value;
|
||||
const debounced = function (...args) {
|
||||
return discourseDebounce(this, originalFunction, ...args, delay);
|
||||
};
|
||||
|
||||
return debounced;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const on = decoratorAlias(emberOn, "Can not `on` without event names");
|
||||
export const observes = decoratorAlias(
|
||||
observer,
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
"@uppy/drop-target": "^1.1.3",
|
||||
"@uppy/utils": "^4.1.0",
|
||||
"@uppy/xhr-upload": "^2.1.2",
|
||||
"ember-auto-import": "^2.4.2",
|
||||
"ember-auto-import": "^2.4.3",
|
||||
"ember-cli-babel": "^7.26.10",
|
||||
"ember-cli-htmlbars": "^6.1.1",
|
||||
"ember-resolver": "^8.0.3",
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"start": "ember serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"ember-auto-import": "^2.4.2",
|
||||
"ember-auto-import": "^2.4.3",
|
||||
"ember-cli-babel": "^7.26.10",
|
||||
"ember-cli-htmlbars": "^6.1.1",
|
||||
"handlebars": "^4.7.6",
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
/* global Babel:true */
|
||||
/* global Babel:true Terser:true */
|
||||
|
||||
// This is executed in mini_racer to provide the JS logic for lib/discourse_js_processor.rb
|
||||
|
||||
const makeEmberTemplateCompilerPlugin =
|
||||
require("babel-plugin-ember-template-compilation").default;
|
||||
const colocatedBabelPlugin = require("colocated-babel-plugin").default;
|
||||
const precompile = require("ember-template-compiler").precompile;
|
||||
const Handlebars = require("handlebars").default;
|
||||
|
||||
@ -50,6 +51,7 @@ function buildTemplateCompilerBabelPlugins({ themeId }) {
|
||||
}
|
||||
|
||||
return [
|
||||
colocatedBabelPlugin,
|
||||
require("widget-hbs-compiler").WidgetHbsCompiler,
|
||||
[
|
||||
makeEmberTemplateCompilerPlugin(() => compileFunction),
|
||||
@ -108,3 +110,29 @@ exports.transpile = function (
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// mini_racer doesn't have native support for getting the result of an async operation.
|
||||
// To work around that, we provide a getMinifyResult which can be used to fetch the result
|
||||
// in a followup method call.
|
||||
let lastMinifyError, lastMinifyResult;
|
||||
|
||||
exports.minify = async function (sources, options) {
|
||||
lastMinifyError = lastMinifyResult = null;
|
||||
try {
|
||||
lastMinifyResult = await Terser.minify(sources, options);
|
||||
} catch (e) {
|
||||
lastMinifyError = e;
|
||||
}
|
||||
};
|
||||
|
||||
exports.getMinifyResult = function () {
|
||||
const error = lastMinifyError;
|
||||
const result = lastMinifyResult;
|
||||
|
||||
lastMinifyError = lastMinifyResult = null;
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
const ColocatedTemplateProcessor = require("ember-cli-htmlbars/lib/colocated-broccoli-plugin");
|
||||
|
||||
module.exports = class DiscoursePluginColocatedTemplateProcessor extends (
|
||||
ColocatedTemplateProcessor
|
||||
) {
|
||||
constructor(tree, discoursePluginName) {
|
||||
super(tree);
|
||||
this.discoursePluginName = discoursePluginName;
|
||||
}
|
||||
|
||||
detectRootName() {
|
||||
return `discourse/plugins/${this.discoursePluginName}/discourse`;
|
||||
}
|
||||
};
|
||||
@ -7,6 +7,7 @@ const mergeTrees = require("broccoli-merge-trees");
|
||||
const fs = require("fs");
|
||||
const concat = require("broccoli-concat");
|
||||
const RawHandlebarsCompiler = require("discourse-hbr/raw-handlebars-compiler");
|
||||
const DiscoursePluginColocatedTemplateProcessor = require("./colocated-template-compiler");
|
||||
|
||||
function fixLegacyExtensions(tree) {
|
||||
return new Funnel(tree, {
|
||||
@ -169,6 +170,8 @@ module.exports = {
|
||||
tree = namespaceModules(tree, pluginName);
|
||||
|
||||
tree = RawHandlebarsCompiler(tree);
|
||||
|
||||
tree = new DiscoursePluginColocatedTemplateProcessor(tree, pluginName);
|
||||
tree = this.compileTemplates(tree);
|
||||
|
||||
tree = this.processedAddonJsFiles(tree);
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
],
|
||||
"repository": "",
|
||||
"dependencies": {
|
||||
"ember-auto-import": "^2.4.2",
|
||||
"ember-auto-import": "^2.4.3",
|
||||
"ember-cli-babel": "^7.26.10",
|
||||
"ember-cli-htmlbars": "^6.1.1",
|
||||
"discourse-widget-hbs": "1.0"
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
"start": "ember serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"ember-auto-import": "^2.4.2",
|
||||
"ember-auto-import": "^2.4.3",
|
||||
"ember-cli-babel": "^7.26.10",
|
||||
"ember-cli-htmlbars": "^6.1.1",
|
||||
"handlebars": "^4.7.6",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Application from "@ember/application";
|
||||
import { buildResolver } from "discourse-common/resolver";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import { normalizeEmberEventHandling } from "./lib/ember-events";
|
||||
|
||||
const _pluginCallbacks = [];
|
||||
let _unhandledThemeErrors = [];
|
||||
@ -54,6 +55,10 @@ const Discourse = Application.extend({
|
||||
start() {
|
||||
document.querySelector("noscript")?.remove();
|
||||
|
||||
// Rewire event handling to eliminate event delegation for better compat
|
||||
// between Glimmer and Classic components.
|
||||
normalizeEmberEventHandling(this);
|
||||
|
||||
if (Error.stackTraceLimit) {
|
||||
// We need Errors to have full stack traces for `lib/source-identifier`
|
||||
Error.stackTraceLimit = Infinity;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { next, schedule } from "@ember/runloop";
|
||||
import bootbox from "bootbox";
|
||||
import { openBookmarkModal } from "discourse/controllers/bookmark";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import {
|
||||
@ -11,8 +10,10 @@ import {
|
||||
import Scrolling from "discourse/mixins/scrolling";
|
||||
import I18n from "I18n";
|
||||
import { Promise } from "rsvp";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Component.extend(Scrolling, {
|
||||
dialog: service(),
|
||||
classNames: ["bookmark-list-wrapper"],
|
||||
|
||||
didInsertElement() {
|
||||
@ -64,12 +65,10 @@ export default Component.extend(Scrolling, {
|
||||
if (!bookmark.reminder_at) {
|
||||
return deleteBookmark();
|
||||
}
|
||||
bootbox.confirm(I18n.t("bookmarks.confirm_delete"), (result) => {
|
||||
if (result) {
|
||||
deleteBookmark();
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
this.dialog.deleteConfirm({
|
||||
message: I18n.t("bookmarks.confirm_delete"),
|
||||
didConfirm: () => deleteBookmark(),
|
||||
didCancel: () => resolve(false),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
} from "discourse/lib/time-shortcut";
|
||||
import { action } from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import { formattedReminderTime } from "discourse/lib/bookmark";
|
||||
import { and, notEmpty } from "@ember/object/computed";
|
||||
@ -261,7 +260,11 @@ export default Component.extend({
|
||||
KeyboardShortcuts.unpause();
|
||||
},
|
||||
|
||||
showExistingReminderAt: notEmpty("model.reminderAt"),
|
||||
@discourseComputed("model.reminderAt")
|
||||
showExistingReminderAt(reminderAt) {
|
||||
return reminderAt && Date.parse(reminderAt) > new Date().getTime();
|
||||
},
|
||||
|
||||
showDelete: notEmpty("model.id"),
|
||||
userHasTimezoneSet: notEmpty("userTimezone"),
|
||||
editingExistingBookmark: and("model", "model.id"),
|
||||
@ -373,10 +376,9 @@ export default Component.extend({
|
||||
};
|
||||
|
||||
if (this.existingBookmarkHasReminder) {
|
||||
bootbox.confirm(I18n.t("bookmarks.confirm_delete"), (result) => {
|
||||
if (result) {
|
||||
deleteAction();
|
||||
}
|
||||
this.dialog.deleteConfirm({
|
||||
message: I18n.t("bookmarks.confirm_delete"),
|
||||
didConfirm: () => deleteAction(),
|
||||
});
|
||||
} else {
|
||||
deleteAction();
|
||||
|
||||
@ -50,7 +50,8 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@action
|
||||
toggleShowMuted() {
|
||||
toggleShowMuted(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("showMuted");
|
||||
},
|
||||
});
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default class CategoryLogo extends Component {
|
||||
@service session;
|
||||
|
||||
get defaultCategoryLogo() {
|
||||
// use dark logo by default in edge case
|
||||
// when scheme is dark and dark logo is present
|
||||
if (
|
||||
this.session.defaultColorSchemeIsDark &&
|
||||
this.args.category.uploaded_logo_dark
|
||||
) {
|
||||
return this.args.category.uploaded_logo_dark;
|
||||
}
|
||||
|
||||
return this.args.category.uploaded_logo;
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import { action } from "@ember/object";
|
||||
import { alias, equal } from "@ember/object/computed";
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
@ -92,11 +93,13 @@ export default Component.extend({
|
||||
this.category.updatePermission(this.group_name, type);
|
||||
},
|
||||
|
||||
actions: {
|
||||
removeRow() {
|
||||
this.category.removePermission(this.group_name);
|
||||
},
|
||||
@action
|
||||
removeRow(event) {
|
||||
event?.preventDefault();
|
||||
this.category.removePermission(this.group_name);
|
||||
},
|
||||
|
||||
actions: {
|
||||
setPermissionReply() {
|
||||
if (this.type <= PermissionType.CREATE_POST) {
|
||||
this.updatePermission(PermissionType.READONLY);
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import Component from "@ember/component";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { get } from "@ember/object";
|
||||
import { action, get } from "@ember/object";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { next } from "@ember/runloop";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import { debounce, observes } from "discourse-common/utils/decorators";
|
||||
import { searchForTerm } from "discourse/lib/search";
|
||||
|
||||
export default Component.extend({
|
||||
@ -30,45 +29,36 @@ export default Component.extend({
|
||||
this.set("loading", false);
|
||||
},
|
||||
|
||||
@debounce(300)
|
||||
search(title) {
|
||||
discourseDebounce(
|
||||
this,
|
||||
function () {
|
||||
const currentTopicId = this.currentTopicId;
|
||||
if (isEmpty(title)) {
|
||||
this.setProperties({ messages: null, loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEmpty(title)) {
|
||||
this.setProperties({ messages: null, loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
searchForTerm(title, {
|
||||
typeFilter: "private_messages",
|
||||
searchForId: true,
|
||||
restrictToArchetype: "private_message",
|
||||
}).then((results) => {
|
||||
if (results && results.posts && results.posts.length > 0) {
|
||||
this.set(
|
||||
"messages",
|
||||
results.posts
|
||||
.mapBy("topic")
|
||||
.filter((t) => t.get("id") !== currentTopicId)
|
||||
);
|
||||
} else {
|
||||
this.setProperties({ messages: null, loading: false });
|
||||
}
|
||||
});
|
||||
},
|
||||
title,
|
||||
300
|
||||
);
|
||||
searchForTerm(title, {
|
||||
typeFilter: "private_messages",
|
||||
searchForId: true,
|
||||
restrictToArchetype: "private_message",
|
||||
}).then((results) => {
|
||||
if (results?.posts?.length) {
|
||||
this.set(
|
||||
"messages",
|
||||
results.posts
|
||||
.mapBy("topic")
|
||||
.filter((t) => t.get("id") !== this.currentTopicId)
|
||||
);
|
||||
} else {
|
||||
this.setProperties({ messages: null, loading: false });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
chooseMessage(message) {
|
||||
const messageId = get(message, "id");
|
||||
this.set("selectedTopicId", messageId);
|
||||
next(() => $(`#choose-message-${messageId}`).prop("checked", "true"));
|
||||
return false;
|
||||
},
|
||||
@action
|
||||
chooseMessage(message, event) {
|
||||
event?.preventDefault();
|
||||
const messageId = get(message, "id");
|
||||
this.set("selectedTopicId", messageId);
|
||||
next(() => $(`#choose-message-${messageId}`).prop("checked", "true"));
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
import templateOnly from "@ember/component/template-only";
|
||||
|
||||
export default templateOnly();
|
||||
@ -1,5 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import EmberObject from "@ember/object";
|
||||
import EmberObject, { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import LinkLookup from "discourse/lib/link-lookup";
|
||||
import { not } from "@ember/object/computed";
|
||||
@ -54,11 +54,13 @@ export default Component.extend({
|
||||
this.set("messageCount", messages.get("length"));
|
||||
},
|
||||
|
||||
actions: {
|
||||
closeMessage(message) {
|
||||
this._removeMessage(message);
|
||||
},
|
||||
@action
|
||||
closeMessage(message, event) {
|
||||
event?.preventDefault();
|
||||
this._removeMessage(message);
|
||||
},
|
||||
|
||||
actions: {
|
||||
hideMessage(message) {
|
||||
this._removeMessage(message);
|
||||
// kind of hacky but the visibility depends on this
|
||||
|
||||
@ -1,19 +1,32 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
copyIcon: "copy",
|
||||
copyClass: "btn-primary",
|
||||
|
||||
@bind
|
||||
_restoreButton() {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("copyIcon", "copy");
|
||||
this.set("copyClass", "btn-primary");
|
||||
},
|
||||
|
||||
@action
|
||||
copy() {
|
||||
const target = document.querySelector(this.selector);
|
||||
target.select();
|
||||
target.setSelectionRange(0, target.value.length);
|
||||
|
||||
try {
|
||||
document.execCommand("copy");
|
||||
|
||||
if (this.copied) {
|
||||
this.copied();
|
||||
}
|
||||
@ -21,13 +34,7 @@ export default Component.extend({
|
||||
this.set("copyIcon", "check");
|
||||
this.set("copyClass", "btn-primary ok");
|
||||
|
||||
discourseDebounce(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
this.set("copyIcon", "copy");
|
||||
this.set("copyClass", "btn-primary");
|
||||
}, 3000);
|
||||
discourseDebounce(this._restoreButton, 3000);
|
||||
} catch (err) {}
|
||||
},
|
||||
});
|
||||
|
||||
@ -4,7 +4,6 @@ import { htmlSafe } from "@ember/template";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "span",
|
||||
rerenderTriggers: ["count", "suffix"],
|
||||
i18nCount: null,
|
||||
|
||||
didReceiveAttrs() {
|
||||
|
||||
@ -118,8 +118,7 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
click(event) {
|
||||
this._triggerAction(event);
|
||||
return false;
|
||||
return this._triggerAction(event);
|
||||
},
|
||||
|
||||
mouseDown(event) {
|
||||
@ -129,37 +128,41 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
_triggerAction(event) {
|
||||
let { action } = this;
|
||||
let { action, route, href } = this;
|
||||
|
||||
if (action) {
|
||||
if (typeof action === "string") {
|
||||
// Note: This is deprecated in new Embers and needs to be removed in the future.
|
||||
// There is already a warning in the console.
|
||||
this.sendAction("action", this.actionParam);
|
||||
} else if (typeof action === "object" && action.value) {
|
||||
if (this.forwardEvent) {
|
||||
action.value(this.actionParam, event);
|
||||
} else {
|
||||
action.value(this.actionParam);
|
||||
}
|
||||
} else if (typeof this.action === "function") {
|
||||
if (this.forwardEvent) {
|
||||
action(this.actionParam, event);
|
||||
} else {
|
||||
action(this.actionParam);
|
||||
if (action || route || href?.length) {
|
||||
if (action) {
|
||||
if (typeof action === "string") {
|
||||
// Note: This is deprecated in new Embers and needs to be removed in the future.
|
||||
// There is already a warning in the console.
|
||||
this.sendAction("action", this.actionParam);
|
||||
} else if (typeof action === "object" && action.value) {
|
||||
if (this.forwardEvent) {
|
||||
action.value(this.actionParam, event);
|
||||
} else {
|
||||
action.value(this.actionParam);
|
||||
}
|
||||
} else if (typeof this.action === "function") {
|
||||
if (this.forwardEvent) {
|
||||
action(this.actionParam, event);
|
||||
} else {
|
||||
action(this.actionParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.route) {
|
||||
this.router.transitionTo(this.route);
|
||||
}
|
||||
if (route) {
|
||||
this.router.transitionTo(route);
|
||||
}
|
||||
|
||||
if (this.href && this.href.length) {
|
||||
DiscourseURL.routeTo(this.href);
|
||||
}
|
||||
if (href?.length) {
|
||||
DiscourseURL.routeTo(href);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -17,7 +17,7 @@ import I18n from "I18n";
|
||||
import ItsATrap from "@discourse/itsatrap";
|
||||
import { Promise } from "rsvp";
|
||||
import { SKIP } from "discourse/lib/autocomplete";
|
||||
import { categoryHashtagTriggerRule } from "discourse/lib/category-hashtags";
|
||||
import { setupHashtagAutocomplete } from "discourse/lib/hashtag-autocomplete";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
||||
@ -28,7 +28,6 @@ import { linkSeenMentions } from "discourse/lib/link-mentions";
|
||||
import { loadOneboxes } from "discourse/lib/load-oneboxes";
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { resolveCachedShortUrls } from "pretty-text/upload-short-url";
|
||||
import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
|
||||
import { inject as service } from "@ember/service";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { siteDir } from "discourse/lib/text-direction";
|
||||
@ -290,10 +289,6 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
"indentSelection"
|
||||
);
|
||||
}
|
||||
|
||||
if (isTesting()) {
|
||||
this.element.addEventListener("paste", this.paste);
|
||||
}
|
||||
},
|
||||
|
||||
@bind
|
||||
@ -463,28 +458,15 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
},
|
||||
|
||||
_applyCategoryHashtagAutocomplete() {
|
||||
const siteSettings = this.siteSettings;
|
||||
|
||||
this._$textarea.autocomplete({
|
||||
template: findRawTemplate("category-tag-autocomplete"),
|
||||
key: "#",
|
||||
afterComplete: (value) => {
|
||||
setupHashtagAutocomplete(
|
||||
"topic-composer",
|
||||
this._$textarea,
|
||||
this.siteSettings,
|
||||
(value) => {
|
||||
this.set("value", value);
|
||||
schedule("afterRender", this, this.focusTextArea);
|
||||
},
|
||||
transformComplete: (obj) => {
|
||||
return obj.text;
|
||||
},
|
||||
dataSource: (term) => {
|
||||
if (term.match(/\s/)) {
|
||||
return null;
|
||||
}
|
||||
return searchCategoryTag(term, siteSettings);
|
||||
},
|
||||
triggerRule: (textarea, opts) => {
|
||||
return categoryHashtagTriggerRule(textarea, opts);
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
_applyEmojiAutocomplete($textarea) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Component from "@ember/component";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import tippy from "tippy.js";
|
||||
import Ember from "ember";
|
||||
|
||||
export default class DiscourseTooltip extends Component {
|
||||
tagName = "";
|
||||
@ -20,7 +21,6 @@ export default class DiscourseTooltip extends Component {
|
||||
// Ember.ViewUtils.getViewBounds is a private API,
|
||||
// but it's not going to be dropped without a public deprecation warning,
|
||||
// see: https://stackoverflow.com/a/50125938/3206146
|
||||
// eslint-disable-next-line no-undef
|
||||
const viewBounds = Ember.ViewUtils.getViewBounds(this);
|
||||
const element = viewBounds.firstNode;
|
||||
const parent = viewBounds.parentElement;
|
||||
|
||||
@ -13,6 +13,11 @@ export default buildCategoryPanel("images").extend({
|
||||
return uploadedLogoUrl || "";
|
||||
},
|
||||
|
||||
@discourseComputed("category.uploaded_logo_dark.url")
|
||||
logoImageDarkUrl(uploadedLogoDarkUrl) {
|
||||
return uploadedLogoDarkUrl || "";
|
||||
},
|
||||
|
||||
actions: {
|
||||
logoUploadDone(upload) {
|
||||
this._setFromUpload("category.uploaded_logo", upload);
|
||||
@ -22,6 +27,14 @@ export default buildCategoryPanel("images").extend({
|
||||
this._deleteUpload("category.uploaded_logo");
|
||||
},
|
||||
|
||||
logoDarkUploadDone(upload) {
|
||||
this._setFromUpload("category.uploaded_logo_dark", upload);
|
||||
},
|
||||
|
||||
logoDarkUploadDeleted() {
|
||||
this._deleteUpload("category.uploaded_logo_dark");
|
||||
},
|
||||
|
||||
backgroundUploadDone(upload) {
|
||||
this._setFromUpload("category.uploaded_background", upload);
|
||||
},
|
||||
|
||||
@ -2,6 +2,7 @@ import Component from "@ember/component";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import I18n from "I18n";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { action } from "@ember/object";
|
||||
import { empty } from "@ember/object/computed";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
@ -49,13 +50,12 @@ export default Component.extend({
|
||||
return getURL(`/c/${slugPart}/edit/${this.tab}`);
|
||||
},
|
||||
|
||||
actions: {
|
||||
select() {
|
||||
this.set("selectedTab", this.tab);
|
||||
|
||||
if (!this.newCategory) {
|
||||
DiscourseURL.routeTo(this.fullSlug);
|
||||
}
|
||||
},
|
||||
@action
|
||||
select(event) {
|
||||
event?.preventDefault();
|
||||
this.set("selectedTab", this.tab);
|
||||
if (!this.newCategory) {
|
||||
DiscourseURL.routeTo(this.fullSlug);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -240,7 +240,8 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@action
|
||||
onCategorySelection(sectionName) {
|
||||
onCategorySelection(sectionName, event) {
|
||||
event?.preventDefault();
|
||||
const section = document.querySelector(
|
||||
`.emoji-picker-emoji-area .section[data-section="${sectionName}"]`
|
||||
);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { action } from "@ember/object";
|
||||
import { alias, gt } from "@ember/object/computed";
|
||||
import CardContentsBase from "discourse/mixins/card-contents-base";
|
||||
import CleansUp from "discourse/mixins/cleans-up";
|
||||
@ -6,6 +7,7 @@ import { Promise } from "rsvp";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { groupPath } from "discourse/lib/url";
|
||||
import { setting } from "discourse/lib/computed";
|
||||
import { modKeysPressed } from "discourse/lib/utilities";
|
||||
|
||||
const maxMembersToDisplay = 10;
|
||||
|
||||
@ -70,11 +72,25 @@ export default Component.extend(CardContentsBase, CleansUp, {
|
||||
this._close();
|
||||
},
|
||||
|
||||
actions: {
|
||||
close() {
|
||||
this._close();
|
||||
},
|
||||
@action
|
||||
close(event) {
|
||||
event?.preventDefault();
|
||||
this._close();
|
||||
},
|
||||
|
||||
@action
|
||||
handleShowGroup(group, event) {
|
||||
if (event && modKeysPressed(event).length > 0) {
|
||||
return false;
|
||||
}
|
||||
event?.preventDefault();
|
||||
// Invokes `showGroup` argument. Convert to `this.args.showGroup` when
|
||||
// refactoring this to a glimmer component.
|
||||
this.showGroup(group);
|
||||
this._close();
|
||||
},
|
||||
|
||||
actions: {
|
||||
cancelFilter() {
|
||||
const postStream = this.postStream;
|
||||
postStream.cancelFilter();
|
||||
@ -90,8 +106,7 @@ export default Component.extend(CardContentsBase, CleansUp, {
|
||||
},
|
||||
|
||||
showGroup(group) {
|
||||
this.showGroup(group);
|
||||
this._close();
|
||||
this.handleShowGroup(group);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -53,7 +53,8 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@action
|
||||
prefillSettings(provider) {
|
||||
prefillSettings(provider, event) {
|
||||
event?.preventDefault();
|
||||
this.form.setProperties(emailProviderDefaultSettings(provider, "imap"));
|
||||
},
|
||||
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ["item"],
|
||||
|
||||
actions: {
|
||||
remove() {
|
||||
this.removeAction(this.member);
|
||||
},
|
||||
@action
|
||||
remove(event) {
|
||||
event?.preventDefault();
|
||||
this.removeAction(this.member);
|
||||
},
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import getURL from "discourse-common/lib/get-url";
|
||||
import { prioritizeNameInUx } from "discourse/lib/settings";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [
|
||||
@ -41,4 +42,9 @@ export default Component.extend({
|
||||
userUrl(username) {
|
||||
return userPath(username.toLowerCase());
|
||||
},
|
||||
|
||||
@discourseComputed("post.topic.title", "post.post_number")
|
||||
titleAriaLabel(title, postNumber) {
|
||||
return I18n.t("groups.aria_post_number", { postNumber, title });
|
||||
},
|
||||
});
|
||||
|
||||
@ -43,7 +43,8 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@action
|
||||
prefillSettings(provider) {
|
||||
prefillSettings(provider, event) {
|
||||
event?.preventDefault();
|
||||
this.form.setProperties(emailProviderDefaultSettings(provider, "smtp"));
|
||||
},
|
||||
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class HorizontalOverflowNav extends Component {
|
||||
@service site;
|
||||
@tracked hasScroll;
|
||||
@tracked hideRightScroll = false;
|
||||
@tracked hideLeftScroll = true;
|
||||
scrollInterval;
|
||||
|
||||
@bind
|
||||
scrollToActive(element) {
|
||||
const activeElement = element.querySelector("a.active");
|
||||
|
||||
activeElement?.scrollIntoView({
|
||||
block: "nearest",
|
||||
inline: "center",
|
||||
});
|
||||
}
|
||||
|
||||
@bind
|
||||
checkScroll(event) {
|
||||
if (this.site.mobileView) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.watchScroll(event);
|
||||
return (this.hasScroll =
|
||||
event.target.scrollWidth > event.target.offsetWidth);
|
||||
}
|
||||
|
||||
@bind
|
||||
stopScroll() {
|
||||
clearInterval(this.scrollInterval);
|
||||
}
|
||||
|
||||
@bind
|
||||
watchScroll(event) {
|
||||
if (this.site.mobileView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
event.target.offsetWidth + event.target.scrollLeft ===
|
||||
event.target.scrollWidth
|
||||
) {
|
||||
this.hideRightScroll = true;
|
||||
clearInterval(this.scrollInterval);
|
||||
} else {
|
||||
this.hideRightScroll = false;
|
||||
}
|
||||
|
||||
if (event.target.scrollLeft === 0) {
|
||||
this.hideLeftScroll = true;
|
||||
clearInterval(this.scrollInterval);
|
||||
} else {
|
||||
this.hideLeftScroll = false;
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
scrollDrag(event) {
|
||||
if (this.site.mobileView) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const navPills = event.target.closest(".nav-pills");
|
||||
|
||||
const position = {
|
||||
left: navPills.scrollLeft, // current scroll
|
||||
x: event.clientX, // mouse position
|
||||
};
|
||||
|
||||
const mouseDragScroll = function (e) {
|
||||
let mouseChange = e.clientX - position.x;
|
||||
navPills.scrollLeft = position.left - mouseChange;
|
||||
|
||||
navPills.querySelectorAll("a").forEach((a) => {
|
||||
a.style.cursor = "grabbing";
|
||||
});
|
||||
};
|
||||
|
||||
const removeDragScroll = function () {
|
||||
document.removeEventListener("mousemove", mouseDragScroll);
|
||||
|
||||
navPills.querySelectorAll("a").forEach((a) => {
|
||||
a.style.cursor = "pointer";
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener("mousemove", mouseDragScroll);
|
||||
document.addEventListener("mouseup", removeDragScroll);
|
||||
}
|
||||
|
||||
@action
|
||||
horizScroll(event) {
|
||||
let scrollSpeed = 175;
|
||||
let siblingTarget = event.target.previousElementSibling;
|
||||
|
||||
if (event.target.dataset.direction === "left") {
|
||||
scrollSpeed = scrollSpeed * -1;
|
||||
siblingTarget = event.target.nextElementSibling;
|
||||
}
|
||||
|
||||
this.scrollInterval = setInterval(function () {
|
||||
siblingTarget.scrollLeft += scrollSpeed;
|
||||
}, 50);
|
||||
|
||||
event.target.addEventListener("mouseup", this.stopScroll);
|
||||
event.target.addEventListener("mouseleave", this.stopScroll);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { on } from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { next } from "@ember/runloop";
|
||||
import { inject as service } from "@ember/service";
|
||||
import deprecated from "discourse-common/lib/deprecated";
|
||||
@ -56,27 +57,27 @@ export default Component.extend({
|
||||
this.router.off("routeDidChange", this, this.currentRouteChanged);
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleExpanded() {
|
||||
this.toggleProperty("expanded");
|
||||
@action
|
||||
toggleExpanded(event) {
|
||||
event?.preventDefault();
|
||||
this.toggleProperty("expanded");
|
||||
|
||||
next(() => {
|
||||
if (this.expanded) {
|
||||
$(window)
|
||||
.off("click.mobile-nav")
|
||||
.on("click.mobile-nav", (e) => {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
next(() => {
|
||||
if (this.expanded) {
|
||||
$(window)
|
||||
.off("click.mobile-nav")
|
||||
.on("click.mobile-nav", (e) => {
|
||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const expander = this.element.querySelector(".expander");
|
||||
if (expander && e.target !== expander) {
|
||||
this.set("expanded", false);
|
||||
$(window).off("click.mobile-nav");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
const expander = this.element.querySelector(".expander");
|
||||
if (expander && e.target !== expander) {
|
||||
this.set("expanded", false);
|
||||
$(window).off("click.mobile-nav");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -71,6 +71,7 @@ export default Component.extend({
|
||||
this._connected.forEach((v) => v.destroy());
|
||||
this._connected.length = 0;
|
||||
|
||||
traverseCustomWidgets(this._tree, (w) => w.destroy());
|
||||
this._rootNode = patch(this._rootNode, diff(this._tree, null));
|
||||
this._tree = null;
|
||||
},
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import FilterModeMixin from "discourse/mixins/filter-mode";
|
||||
import { next } from "@ember/runloop";
|
||||
@ -61,33 +62,33 @@ export default Component.extend(FilterModeMixin, {
|
||||
DiscourseURL.appEvents.off("dom:clean", this, this.ensureDropClosed);
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleDrop() {
|
||||
this.set("expanded", !this.expanded);
|
||||
@action
|
||||
toggleDrop(event) {
|
||||
event?.preventDefault();
|
||||
this.set("expanded", !this.expanded);
|
||||
|
||||
if (this.expanded) {
|
||||
DiscourseURL.appEvents.on("dom:clean", this, this.ensureDropClosed);
|
||||
if (this.expanded) {
|
||||
DiscourseURL.appEvents.on("dom:clean", this, this.ensureDropClosed);
|
||||
|
||||
next(() => {
|
||||
if (!this.expanded) {
|
||||
return;
|
||||
}
|
||||
next(() => {
|
||||
if (!this.expanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this.element.querySelector(".drop a")).on("click", () => {
|
||||
this.element.querySelector(".drop").style.display = "none";
|
||||
$(this.element.querySelector(".drop a")).on("click", () => {
|
||||
this.element.querySelector(".drop").style.display = "none";
|
||||
|
||||
next(() => {
|
||||
this.ensureDropClosed();
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
$(window).on("click.navigation-bar", () => {
|
||||
next(() => {
|
||||
this.ensureDropClosed();
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
$(window).on("click.navigation-bar", () => {
|
||||
this.ensureDropClosed();
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -13,7 +13,6 @@ export default Component.extend(FilterModeMixin, {
|
||||
],
|
||||
attributeBindings: ["content.title:title"],
|
||||
hidden: false,
|
||||
rerenderTriggers: ["content.count"],
|
||||
activeClass: "",
|
||||
hrefLink: null,
|
||||
|
||||
|
||||
@ -5,13 +5,14 @@ import { getOwner } from "discourse-common/lib/get-owner";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "a",
|
||||
classNameBindings: [":popup-tip", "good", "bad", "lastShownAt::hide"],
|
||||
attributeBindings: ["role", "ariaLabel"],
|
||||
rerenderTriggers: ["validation.reason"],
|
||||
attributeBindings: ["role", "ariaLabel", "tabindex"],
|
||||
tipReason: null,
|
||||
lastShownAt: or("shownAt", "validation.lastShownAt"),
|
||||
bad: reads("validation.failed"),
|
||||
good: not("bad"),
|
||||
tabindex: "0",
|
||||
|
||||
@discourseComputed("bad")
|
||||
role(bad) {
|
||||
@ -25,10 +26,21 @@ export default Component.extend({
|
||||
return reason?.replace(/(<([^>]+)>)/gi, "");
|
||||
},
|
||||
|
||||
click() {
|
||||
dismiss() {
|
||||
this.set("shownAt", null);
|
||||
const composer = getOwner(this).lookup("controller:composer");
|
||||
composer.clearLastValidatedAt();
|
||||
this.element.previousElementSibling?.focus();
|
||||
},
|
||||
|
||||
click() {
|
||||
this.dismiss();
|
||||
},
|
||||
|
||||
keyDown(event) {
|
||||
if (event.key === "Enter") {
|
||||
this.dismiss();
|
||||
}
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
import Component from "@ember/component";
|
||||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
onChange(category) {
|
||||
this.set("value", category);
|
||||
this.categoryChanged && this.categoryChanged(category);
|
||||
},
|
||||
},
|
||||
});
|
||||
export default class extends Component {
|
||||
@tracked categoryId = this.args.value;
|
||||
|
||||
@action
|
||||
onChange(category) {
|
||||
this.categoryId = category;
|
||||
this.args.categoryChanged?.(category);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,13 +2,14 @@ import Category from "discourse/models/category";
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import bootbox from "bootbox";
|
||||
import { classify, dasherize } from "@ember/string";
|
||||
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";
|
||||
import { action, set } from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { inject as service } from "@ember/service";
|
||||
import Ember from "ember";
|
||||
|
||||
let _components = {};
|
||||
|
||||
@ -22,6 +23,7 @@ export function addPluginReviewableParam(reviewableType, param) {
|
||||
|
||||
export default Component.extend({
|
||||
adminTools: optionalService(),
|
||||
dialog: service(),
|
||||
tagName: "",
|
||||
updating: null,
|
||||
editing: false,
|
||||
@ -107,9 +109,7 @@ export default Component.extend({
|
||||
let dasherized = dasherize(type);
|
||||
let templatePath = `components/${dasherized}`;
|
||||
let template =
|
||||
// eslint-disable-next-line no-undef
|
||||
Ember.TEMPLATES[`${templatePath}`] ||
|
||||
// eslint-disable-next-line no-undef
|
||||
Ember.TEMPLATES[`javascripts/${templatePath}`];
|
||||
_components[type] = template ? dasherized : null;
|
||||
return _components[type];
|
||||
@ -121,7 +121,7 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
@bind
|
||||
_performConfirmed(action) {
|
||||
_performConfirmed(performableAction) {
|
||||
let reviewable = this.reviewable;
|
||||
|
||||
let performAction = () => {
|
||||
@ -140,7 +140,7 @@ export default Component.extend({
|
||||
});
|
||||
|
||||
return ajax(
|
||||
`/review/${reviewable.id}/perform/${action.id}?version=${version}`,
|
||||
`/review/${reviewable.id}/perform/${performableAction.id}?version=${version}`,
|
||||
{
|
||||
type: "PUT",
|
||||
data,
|
||||
@ -173,13 +173,16 @@ export default Component.extend({
|
||||
.finally(() => this.set("updating", false));
|
||||
};
|
||||
|
||||
if (action.client_action) {
|
||||
let actionMethod = this[`client${classify(action.client_action)}`];
|
||||
if (performableAction.client_action) {
|
||||
let actionMethod =
|
||||
this[`client${classify(performableAction.client_action)}`];
|
||||
if (actionMethod) {
|
||||
return actionMethod.call(this, reviewable, performAction);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`No handler for ${action.client_action} found`);
|
||||
console.error(
|
||||
`No handler for ${performableAction.client_action} found`
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@ -209,14 +212,16 @@ export default Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
explainReviewable(reviewable) {
|
||||
showModal("explain-reviewable", {
|
||||
title: "review.explain.title",
|
||||
model: reviewable,
|
||||
});
|
||||
},
|
||||
@action
|
||||
explainReviewable(reviewable, event) {
|
||||
event?.preventDefault();
|
||||
showModal("explain-reviewable", {
|
||||
title: "review.explain.title",
|
||||
model: reviewable,
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit() {
|
||||
this.set("editing", true);
|
||||
this.set("_updates", { payload: {} });
|
||||
@ -259,19 +264,18 @@ export default Component.extend({
|
||||
set(this._updates, fieldId, event.target.value);
|
||||
},
|
||||
|
||||
perform(action) {
|
||||
perform(performableAction) {
|
||||
if (this.updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = action.get("confirm_message");
|
||||
let requireRejectReason = action.get("require_reject_reason");
|
||||
let customModal = action.get("custom_modal");
|
||||
if (msg) {
|
||||
bootbox.confirm(msg, (answer) => {
|
||||
if (answer) {
|
||||
return this._performConfirmed(action);
|
||||
}
|
||||
const message = performableAction.get("confirm_message");
|
||||
let requireRejectReason = performableAction.get("require_reject_reason");
|
||||
let customModal = performableAction.get("custom_modal");
|
||||
if (message) {
|
||||
this.dialog.confirm({
|
||||
message,
|
||||
didConfirm: () => this._performConfirmed(performableAction),
|
||||
});
|
||||
} else if (requireRejectReason) {
|
||||
showModal("reject-reason-reviewable", {
|
||||
@ -279,7 +283,7 @@ export default Component.extend({
|
||||
model: this.reviewable,
|
||||
}).setProperties({
|
||||
performConfirmed: this._performConfirmed,
|
||||
action,
|
||||
action: performableAction,
|
||||
});
|
||||
} else if (customModal) {
|
||||
showModal(customModal, {
|
||||
@ -287,10 +291,10 @@ export default Component.extend({
|
||||
model: this.reviewable,
|
||||
}).setProperties({
|
||||
performConfirmed: this._performConfirmed,
|
||||
action,
|
||||
action: performableAction,
|
||||
});
|
||||
} else {
|
||||
return this._performConfirmed(action);
|
||||
return this._performConfirmed(performableAction);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import Component from "@ember/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { action } from "@ember/object";
|
||||
import { gt } from "@ember/object/computed";
|
||||
import { historyHeat } from "discourse/widgets/post-edits-indicator";
|
||||
import { longDate } from "discourse/lib/formatter";
|
||||
@ -18,18 +19,18 @@ export default Component.extend({
|
||||
return longDate(updatedAt);
|
||||
},
|
||||
|
||||
actions: {
|
||||
showEditHistory() {
|
||||
let postId = this.get("reviewable.post_id");
|
||||
this.store.find("post", postId).then((post) => {
|
||||
let historyController = showModal("history", {
|
||||
model: post,
|
||||
modalClass: "history-modal",
|
||||
});
|
||||
historyController.refresh(postId, "latest");
|
||||
historyController.set("post", post);
|
||||
historyController.set("topicController", null);
|
||||
@action
|
||||
showEditHistory(event) {
|
||||
event?.preventDefault();
|
||||
let postId = this.get("reviewable.post_id");
|
||||
this.store.find("post", postId).then((post) => {
|
||||
let historyController = showModal("history", {
|
||||
model: post,
|
||||
modalClass: "history-modal",
|
||||
});
|
||||
},
|
||||
historyController.refresh(postId, "latest");
|
||||
historyController.set("post", post);
|
||||
historyController.set("topicController", null);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
showRawEmail() {
|
||||
showModal("raw-email").set("rawEmail", this.reviewable.payload.raw_email);
|
||||
},
|
||||
@action
|
||||
showRawEmail(event) {
|
||||
event?.preventDefault();
|
||||
showModal("raw-email").set("rawEmail", this.reviewable.payload.raw_email);
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { logSearchLinkClick } from "discourse/lib/search";
|
||||
import { modKeysPressed } from "discourse/lib/utilities";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "div",
|
||||
@ -10,7 +11,11 @@ export default Component.extend({
|
||||
role: "listitem",
|
||||
|
||||
@action
|
||||
logClick(topicId) {
|
||||
logClick(topicId, event) {
|
||||
// Avoid click logging when any modifier keys are pressed.
|
||||
if (event && modKeysPressed(event).length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (this.searchLogId && topicId) {
|
||||
logSearchLinkClick({
|
||||
searchLogId: this.searchLogId,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
@ -48,15 +49,15 @@ export default Component.extend({
|
||||
);
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleSecondFactorMethod() {
|
||||
const secondFactorMethod = this.secondFactorMethod;
|
||||
this.set("secondFactorToken", "");
|
||||
if (secondFactorMethod === SECOND_FACTOR_METHODS.TOTP) {
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.BACKUP_CODE);
|
||||
} else {
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP);
|
||||
}
|
||||
},
|
||||
@action
|
||||
toggleSecondFactorMethod(event) {
|
||||
event?.preventDefault();
|
||||
const secondFactorMethod = this.secondFactorMethod;
|
||||
this.set("secondFactorToken", "");
|
||||
if (secondFactorMethod === SECOND_FACTOR_METHODS.TOTP) {
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.BACKUP_CODE);
|
||||
} else {
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
import { SECOND_FACTOR_METHODS } from "discourse/models/user";
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
useAnotherMethod() {
|
||||
this.set("showSecurityKey", false);
|
||||
this.set("showSecondFactor", true);
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP);
|
||||
},
|
||||
@action
|
||||
useAnotherMethod(event) {
|
||||
event?.preventDefault();
|
||||
this.set("showSecurityKey", false);
|
||||
this.set("showSecondFactor", true);
|
||||
this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP);
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import Component from "@ember/component";
|
||||
import I18n from "I18n";
|
||||
import bootbox from "bootbox";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
dialog: service(),
|
||||
publishing: false,
|
||||
|
||||
@discourseComputed("topic.destination_category_id")
|
||||
@ -18,11 +19,12 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
publish() {
|
||||
bootbox.confirm(I18n.t("shared_drafts.confirm_publish"), (result) => {
|
||||
if (result) {
|
||||
this.dialog.yesNoConfirm({
|
||||
message: I18n.t("shared_drafts.confirm_publish"),
|
||||
didConfirm: () => {
|
||||
this.set("publishing", true);
|
||||
const destinationCategoryId = this.topic.destination_category_id;
|
||||
this.topic
|
||||
return this.topic
|
||||
.publish()
|
||||
.then(() => {
|
||||
this.topic.setProperties({
|
||||
@ -34,7 +36,7 @@ export default Component.extend({
|
||||
.finally(() => {
|
||||
this.set("publishing", false);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@ -13,7 +13,8 @@
|
||||
@model={{sectionLink.model}}
|
||||
@prefixType={{sectionLink.prefixType}}
|
||||
@prefixValue={{sectionLink.prefixValue}}
|
||||
@prefixColor={{sectionLink.prefixColor}} >
|
||||
@prefixColor={{sectionLink.prefixColor}}
|
||||
@prefixElementColors={{sectionLink.prefixElementColors}} >
|
||||
</Sidebar::SectionLink>
|
||||
{{/each}}
|
||||
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
<Sidebar::Section
|
||||
@sectionName="tags"
|
||||
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
||||
@collapsable={{@collapsable}} >
|
||||
{{#if this.displaySection}}
|
||||
<Sidebar::Section
|
||||
@sectionName="tags"
|
||||
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
|
||||
@collapsable={{@collapsable}} >
|
||||
|
||||
{{#each this.sectionLinks as |sectionLink|}}
|
||||
<Sidebar::SectionLink
|
||||
@route={{sectionLink.route}}
|
||||
@content={{sectionLink.text}}
|
||||
@currentWhen={{sectionLink.currentWhen}}
|
||||
@prefixType={{sectionLink.prefixType}}
|
||||
@prefixValue={{sectionLink.prefixValue}}
|
||||
@models={{sectionLink.models}} >
|
||||
</Sidebar::SectionLink>
|
||||
{{/each}}
|
||||
{{#each this.sectionLinks as |sectionLink|}}
|
||||
<Sidebar::SectionLink
|
||||
@route={{sectionLink.route}}
|
||||
@content={{sectionLink.text}}
|
||||
@currentWhen={{sectionLink.currentWhen}}
|
||||
@prefixType={{sectionLink.prefixType}}
|
||||
@prefixValue={{sectionLink.prefixValue}}
|
||||
@models={{sectionLink.models}} >
|
||||
</Sidebar::SectionLink>
|
||||
{{/each}}
|
||||
|
||||
<Sidebar::Common::AllTagsSectionLink />
|
||||
</Sidebar::Section>
|
||||
<Sidebar::Common::AllTagsSectionLink />
|
||||
</Sidebar::Section>
|
||||
{{/if}}
|
||||
|
||||
@ -8,6 +8,13 @@ export default class SidebarAnonymousTagsSection extends Component {
|
||||
@service topicTrackingState;
|
||||
@service site;
|
||||
|
||||
get displaySection() {
|
||||
return (
|
||||
this.site.anonymous_default_sidebar_tags?.length > 0 ||
|
||||
this.site.top_tags?.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
@cached
|
||||
get sectionLinks() {
|
||||
let tags;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user