mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 09:42:07 +08:00
Revert "Revert "Merge branch 'master' of https://github.com/discourse/discourse""
This reverts commit20780a1eee
. * SECURITY: re-adds accidentally reverted commit: 03d26cd6: ensure embed_url contains valid http(s) uri * when the merge commite62a85cf
was reverted, git chose the2660c2e2
parent to land on instead of the03d26cd6
parent (which contains security fixes)
This commit is contained in:
parent
20780a1eee
commit
d9a02d1336
6
Gemfile
6
Gemfile
|
@ -127,10 +127,6 @@ gem 'mini_racer'
|
||||||
# TODO: determine why highline is being held back and upgrade to latest
|
# TODO: determine why highline is being held back and upgrade to latest
|
||||||
gem 'highline', '~> 1.7.0', require: false
|
gem 'highline', '~> 1.7.0', require: false
|
||||||
|
|
||||||
# TODO: Upgrading breaks Sidekiq Web
|
|
||||||
# This is a bit of a hornets nest cause in an ideal world we much prefer
|
|
||||||
# if Sidekiq reused session and CSRF mitigation with Discourse on the
|
|
||||||
# _forum_session cookie instead of a rack.session cookie
|
|
||||||
gem 'rack', '2.2.2'
|
gem 'rack', '2.2.2'
|
||||||
|
|
||||||
gem 'rack-protection' # security
|
gem 'rack-protection' # security
|
||||||
|
@ -252,3 +248,5 @@ end
|
||||||
gem 'webpush', require: false
|
gem 'webpush', require: false
|
||||||
gem 'colored2', require: false
|
gem 'colored2', require: false
|
||||||
gem 'maxminddb'
|
gem 'maxminddb'
|
||||||
|
|
||||||
|
gem 'rails_failover', require: false
|
||||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -195,7 +195,7 @@ GEM
|
||||||
mini_sql (0.2.5)
|
mini_sql (0.2.5)
|
||||||
mini_suffix (0.3.0)
|
mini_suffix (0.3.0)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
minitest (5.14.0)
|
minitest (5.14.1)
|
||||||
mocha (1.11.2)
|
mocha (1.11.2)
|
||||||
mock_redis (0.23.0)
|
mock_redis (0.23.0)
|
||||||
msgpack (1.3.3)
|
msgpack (1.3.3)
|
||||||
|
@ -262,7 +262,7 @@ GEM
|
||||||
pry-rails (0.3.9)
|
pry-rails (0.3.9)
|
||||||
pry (>= 0.10.4)
|
pry (>= 0.10.4)
|
||||||
public_suffix (4.0.5)
|
public_suffix (4.0.5)
|
||||||
puma (4.3.3)
|
puma (4.3.5)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
r2 (0.2.7)
|
r2 (0.2.7)
|
||||||
rack (2.2.2)
|
rack (2.2.2)
|
||||||
|
@ -277,6 +277,8 @@ GEM
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.3.0)
|
rails-html-sanitizer (1.3.0)
|
||||||
loofah (~> 2.3)
|
loofah (~> 2.3)
|
||||||
|
rails_failover (0.2.0)
|
||||||
|
redis (~> 4)
|
||||||
rails_multisite (2.1.2)
|
rails_multisite (2.1.2)
|
||||||
activerecord (> 5.0, < 7)
|
activerecord (> 5.0, < 7)
|
||||||
railties (> 5.0, < 7)
|
railties (> 5.0, < 7)
|
||||||
|
@ -294,7 +296,7 @@ GEM
|
||||||
rb-fsevent (0.10.4)
|
rb-fsevent (0.10.4)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rbtrace (0.4.12)
|
rbtrace (0.4.13)
|
||||||
ffi (>= 1.0.6)
|
ffi (>= 1.0.6)
|
||||||
msgpack (>= 0.4.3)
|
msgpack (>= 0.4.3)
|
||||||
optimist (>= 3.0.0)
|
optimist (>= 3.0.0)
|
||||||
|
@ -341,13 +343,16 @@ GEM
|
||||||
json-schema (~> 2.2)
|
json-schema (~> 2.2)
|
||||||
railties (>= 3.1, < 7.0)
|
railties (>= 3.1, < 7.0)
|
||||||
rtlit (0.0.5)
|
rtlit (0.0.5)
|
||||||
rubocop (0.83.0)
|
rubocop (0.84.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 2.7.0.1)
|
parser (>= 2.7.0.1)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
rexml
|
rexml
|
||||||
|
rubocop-ast (>= 0.0.3)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 2.0)
|
unicode-display_width (>= 1.4.0, < 2.0)
|
||||||
|
rubocop-ast (0.0.3)
|
||||||
|
parser (>= 2.7.0.1)
|
||||||
rubocop-discourse (2.1.2)
|
rubocop-discourse (2.1.2)
|
||||||
rubocop (>= 0.69.0)
|
rubocop (>= 0.69.0)
|
||||||
rubocop-rspec (>= 1.39.0)
|
rubocop-rspec (>= 1.39.0)
|
||||||
|
@ -509,6 +514,7 @@ DEPENDENCIES
|
||||||
rack (= 2.2.2)
|
rack (= 2.2.2)
|
||||||
rack-mini-profiler
|
rack-mini-profiler
|
||||||
rack-protection
|
rack-protection
|
||||||
|
rails_failover
|
||||||
rails_multisite
|
rails_multisite
|
||||||
railties (= 6.0.3)
|
railties (= 6.0.3)
|
||||||
rake
|
rake
|
||||||
|
|
|
@ -77,12 +77,6 @@ const AdminUser = User.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
revokeApiKey() {
|
|
||||||
return ajax(`/admin/users/${this.id}/revoke_api_key`, {
|
|
||||||
type: "DELETE"
|
|
||||||
}).then(() => this.set("api_key", null));
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteAllPosts() {
|
deleteAllPosts() {
|
||||||
let deletedPosts = 0;
|
let deletedPosts = 0;
|
||||||
const user = this;
|
const user = this;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { get } from "@ember/object";
|
import { get } from "@ember/object";
|
||||||
import { isEmpty } from "@ember/utils";
|
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
|
@ -15,7 +14,7 @@ export default DiscourseRoute.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
if (model.get("isNew") || isEmpty(model.get("web_hook_event_types"))) {
|
if (model.get("isNew")) {
|
||||||
model.set("web_hook_event_types", controller.get("defaultEventTypes"));
|
model.set("web_hook_event_types", controller.get("defaultEventTypes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//= require_tree ./discourse-common/addon
|
//= require_tree ./discourse-common/addon
|
||||||
//= require ./polyfills
|
//= require ./polyfills
|
||||||
//= require_tree ./select-kit/app
|
//= require_tree ./select-kit/addon
|
||||||
//= require ./discourse/app/app
|
//= require ./discourse/app/app
|
||||||
//= require ./app-boot
|
//= require ./app-boot
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ var define, requirejs;
|
||||||
default: Ember.Object,
|
default: Ember.Object,
|
||||||
get: Ember.get,
|
get: Ember.get,
|
||||||
getProperties: Ember.getProperties,
|
getProperties: Ember.getProperties,
|
||||||
guidFor: Ember.guidFor,
|
|
||||||
set: Ember.set,
|
set: Ember.set,
|
||||||
setProperties: Ember.setProperties,
|
setProperties: Ember.setProperties,
|
||||||
computed: Ember.computed,
|
computed: Ember.computed,
|
||||||
|
|
|
@ -2,13 +2,10 @@ import I18n from "I18n";
|
||||||
import { inject } from "@ember/controller";
|
import { inject } from "@ember/controller";
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import { setDefaultHomepage } from "discourse/lib/utilities";
|
import { setDefaultHomepage } from "discourse/lib/utilities";
|
||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import {
|
import { listThemes, setLocalTheme } from "discourse/lib/theme-selector";
|
||||||
listThemes,
|
|
||||||
previewTheme,
|
|
||||||
setLocalTheme
|
|
||||||
} from "discourse/lib/theme-selector";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import pageReloader from "discourse/helpers/page-reloader";
|
||||||
import {
|
import {
|
||||||
safariHacksDisabled,
|
safariHacksDisabled,
|
||||||
isiPad,
|
isiPad,
|
||||||
|
@ -28,6 +25,9 @@ const TEXT_SIZES = ["smaller", "normal", "larger", "largest"];
|
||||||
const TITLE_COUNT_MODES = ["notifications", "contextual"];
|
const TITLE_COUNT_MODES = ["notifications", "contextual"];
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
|
currentThemeId: -1,
|
||||||
|
preferencesController: inject("preferences"),
|
||||||
|
|
||||||
@discourseComputed("makeThemeDefault")
|
@discourseComputed("makeThemeDefault")
|
||||||
saveAttrNames(makeDefault) {
|
saveAttrNames(makeDefault) {
|
||||||
let attrs = [
|
let attrs = [
|
||||||
|
@ -51,8 +51,6 @@ export default Controller.extend({
|
||||||
return attrs;
|
return attrs;
|
||||||
},
|
},
|
||||||
|
|
||||||
preferencesController: inject("preferences"),
|
|
||||||
|
|
||||||
@discourseComputed()
|
@discourseComputed()
|
||||||
isiPad() {
|
isiPad() {
|
||||||
// TODO: remove this preference checkbox when iOS adoption > 90%
|
// TODO: remove this preference checkbox when iOS adoption > 90%
|
||||||
|
@ -105,10 +103,14 @@ export default Controller.extend({
|
||||||
return themes && themes.length > 1;
|
return themes && themes.length > 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
@observes("themeId")
|
@discourseComputed("themeId")
|
||||||
themeIdChanged() {
|
themeIdChanged(themeId) {
|
||||||
const id = this.themeId;
|
if (this.currentThemeId === -1) {
|
||||||
previewTheme([id]);
|
this.set("currentThemeId", themeId);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return this.currentThemeId !== themeId;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("model.user_option.theme_ids", "themeId")
|
@discourseComputed("model.user_option.theme_ids", "themeId")
|
||||||
|
@ -189,6 +191,10 @@ export default Controller.extend({
|
||||||
this.disableSafariHacks.toString()
|
this.disableSafariHacks.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.themeId !== this.currentThemeId) {
|
||||||
|
pageReloader.reload();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
},
|
||||||
|
|
|
@ -185,6 +185,13 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("model.category")
|
||||||
|
minimumRequiredTags(category) {
|
||||||
|
return category && category.minimum_required_tags > 0
|
||||||
|
? category.minimum_required_tags
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
|
||||||
_forceRefreshPostStream() {
|
_forceRefreshPostStream() {
|
||||||
this.appEvents.trigger("post-stream:refresh", { force: true });
|
this.appEvents.trigger("post-stream:refresh", { force: true });
|
||||||
},
|
},
|
||||||
|
|
|
@ -51,13 +51,15 @@ export default Controller.extend(CanCheckEmails, {
|
||||||
hasDeletedPosts: gt("model.number_of_deleted_posts", 0),
|
hasDeletedPosts: gt("model.number_of_deleted_posts", 0),
|
||||||
hasBeenSuspended: gt("model.number_of_suspensions", 0),
|
hasBeenSuspended: gt("model.number_of_suspensions", 0),
|
||||||
hasReceivedWarnings: gt("model.warnings_received_count", 0),
|
hasReceivedWarnings: gt("model.warnings_received_count", 0),
|
||||||
|
hasRejectedPosts: gt("model.number_of_rejected_posts", 0),
|
||||||
|
|
||||||
showStaffCounters: or(
|
showStaffCounters: or(
|
||||||
"hasGivenFlags",
|
"hasGivenFlags",
|
||||||
"hasFlaggedPosts",
|
"hasFlaggedPosts",
|
||||||
"hasDeletedPosts",
|
"hasDeletedPosts",
|
||||||
"hasBeenSuspended",
|
"hasBeenSuspended",
|
||||||
"hasReceivedWarnings"
|
"hasReceivedWarnings",
|
||||||
|
"hasRejectedPosts"
|
||||||
),
|
),
|
||||||
|
|
||||||
showFeaturedTopic: and(
|
showFeaturedTopic: and(
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
import Ember from "ember";
|
||||||
|
|
||||||
|
export default EmberObject.create({
|
||||||
|
reload: function() {
|
||||||
|
if (!Ember.testing) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -3,6 +3,7 @@ import { cancel, later } from "@ember/runloop";
|
||||||
import { Promise } from "rsvp";
|
import { Promise } from "rsvp";
|
||||||
import { iconHTML } from "discourse-common/lib/icon-library";
|
import { iconHTML } from "discourse-common/lib/icon-library";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { guidFor } from "@ember/object/internals";
|
||||||
|
|
||||||
// http://github.com/feross/clipboard-copy
|
// http://github.com/feross/clipboard-copy
|
||||||
function clipboardCopy(text) {
|
function clipboardCopy(text) {
|
||||||
|
@ -90,7 +91,7 @@ export default {
|
||||||
const state = button.innerHTML;
|
const state = button.innerHTML;
|
||||||
button.innerHTML = I18n.t("copy_codeblock.copied");
|
button.innerHTML = I18n.t("copy_codeblock.copied");
|
||||||
|
|
||||||
const commandId = Ember.guidFor(button);
|
const commandId = guidFor(button);
|
||||||
|
|
||||||
if (_fadeCopyCodeblocksRunners[commandId]) {
|
if (_fadeCopyCodeblocksRunners[commandId]) {
|
||||||
cancel(_fadeCopyCodeblocksRunners[commandId]);
|
cancel(_fadeCopyCodeblocksRunners[commandId]);
|
||||||
|
|
|
@ -3,6 +3,13 @@ import showModal from "discourse/lib/show-modal";
|
||||||
import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
|
import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button";
|
||||||
import { formattedReminderTime } from "discourse/lib/bookmark";
|
import { formattedReminderTime } from "discourse/lib/bookmark";
|
||||||
|
|
||||||
|
const SHARE_PRIORITY = 1000;
|
||||||
|
const BOOKMARK_PRIORITY = 900;
|
||||||
|
const ARCHIVE_PRIORITY = 800;
|
||||||
|
const FLAG_PRIORITY = 700;
|
||||||
|
const EDIT_MESSAGE_PRIORITY = 600;
|
||||||
|
const DEFER_PRIORITY = 500;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "topic-footer-buttons",
|
name: "topic-footer-buttons",
|
||||||
|
|
||||||
|
@ -11,8 +18,12 @@ export default {
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
id: "share-and-invite",
|
id: "share-and-invite",
|
||||||
icon: "link",
|
icon: "link",
|
||||||
priority: 999,
|
priority: SHARE_PRIORITY,
|
||||||
label: "topic.share.title",
|
label() {
|
||||||
|
if (!this.get("topic.isPrivateMessage") || this.site.mobileView) {
|
||||||
|
return "topic.share.title";
|
||||||
|
}
|
||||||
|
},
|
||||||
title: "topic.share.help",
|
title: "topic.share.help",
|
||||||
action() {
|
action() {
|
||||||
const panels = [
|
const panels = [
|
||||||
|
@ -67,7 +78,7 @@ export default {
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
id: "flag",
|
id: "flag",
|
||||||
icon: "flag",
|
icon: "flag",
|
||||||
priority: 998,
|
priority: FLAG_PRIORITY,
|
||||||
label: "topic.flag_topic.title",
|
label: "topic.flag_topic.title",
|
||||||
title: "topic.flag_topic.help",
|
title: "topic.flag_topic.help",
|
||||||
action: "showFlagTopic",
|
action: "showFlagTopic",
|
||||||
|
@ -93,14 +104,16 @@ export default {
|
||||||
}
|
}
|
||||||
return "bookmark";
|
return "bookmark";
|
||||||
},
|
},
|
||||||
priority: 1000,
|
priority: BOOKMARK_PRIORITY,
|
||||||
classNames() {
|
classNames() {
|
||||||
const bookmarked = this.get("topic.bookmarked");
|
const bookmarked = this.get("topic.bookmarked");
|
||||||
return bookmarked ? ["bookmark", "bookmarked"] : ["bookmark"];
|
return bookmarked ? ["bookmark", "bookmarked"] : ["bookmark"];
|
||||||
},
|
},
|
||||||
label() {
|
label() {
|
||||||
const bookmarked = this.get("topic.bookmarked");
|
if (!this.get("topic.isPrivateMessage") || this.site.mobileView) {
|
||||||
return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title";
|
const bookmarked = this.get("topic.bookmarked");
|
||||||
|
return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
translatedTitle() {
|
translatedTitle() {
|
||||||
const bookmarked = this.get("topic.bookmarked");
|
const bookmarked = this.get("topic.bookmarked");
|
||||||
|
@ -126,7 +139,7 @@ export default {
|
||||||
|
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
id: "archive",
|
id: "archive",
|
||||||
priority: 996,
|
priority: ARCHIVE_PRIORITY,
|
||||||
icon() {
|
icon() {
|
||||||
return this.archiveIcon;
|
return this.archiveIcon;
|
||||||
},
|
},
|
||||||
|
@ -155,13 +168,16 @@ export default {
|
||||||
|
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
id: "edit-message",
|
id: "edit-message",
|
||||||
priority: 750,
|
priority: EDIT_MESSAGE_PRIORITY,
|
||||||
icon: "pencil-alt",
|
icon: "pencil-alt",
|
||||||
label: "topic.edit_message.title",
|
label: "topic.edit_message.title",
|
||||||
title: "topic.edit_message.help",
|
title: "topic.edit_message.help",
|
||||||
action: "editFirstPost",
|
action: "editFirstPost",
|
||||||
classNames: ["edit-message"],
|
classNames: ["edit-message"],
|
||||||
dependentKeys: ["editFirstPost", "showEditOnFooter"],
|
dependentKeys: ["editFirstPost", "showEditOnFooter"],
|
||||||
|
dropdown() {
|
||||||
|
return this.site.mobileView && this.get("topic.isPrivateMessage");
|
||||||
|
},
|
||||||
displayed() {
|
displayed() {
|
||||||
return this.showEditOnFooter;
|
return this.showEditOnFooter;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +186,7 @@ export default {
|
||||||
registerTopicFooterButton({
|
registerTopicFooterButton({
|
||||||
id: "defer",
|
id: "defer",
|
||||||
icon: "circle",
|
icon: "circle",
|
||||||
priority: 300,
|
priority: DEFER_PRIORITY,
|
||||||
label: "topic.defer.title",
|
label: "topic.defer.title",
|
||||||
title: "topic.defer.help",
|
title: "topic.defer.help",
|
||||||
action: "deferTopic",
|
action: "deferTopic",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
|
|
||||||
const keySelector = "meta[name=discourse_theme_ids]";
|
const keySelector = "meta[name=discourse_theme_ids]";
|
||||||
|
@ -79,31 +78,6 @@ export function refreshCSS(node, hash, newHref) {
|
||||||
$orig.data("copy", reloaded);
|
$orig.data("copy", reloaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function previewTheme(ids = []) {
|
|
||||||
ids = ids.reject(id => !id);
|
|
||||||
if (!ids.includes(currentThemeId())) {
|
|
||||||
Discourse.set("assetVersion", "forceRefresh");
|
|
||||||
|
|
||||||
ajax(`/themes/assets/${ids.length > 0 ? ids.join("-") : "default"}`).then(
|
|
||||||
results => {
|
|
||||||
const elem = _.first($(keySelector));
|
|
||||||
if (elem) {
|
|
||||||
elem.content = ids.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
results.themes.forEach(theme => {
|
|
||||||
const node = $(
|
|
||||||
`link[rel=stylesheet][data-target=${theme.target}]`
|
|
||||||
)[0];
|
|
||||||
if (node) {
|
|
||||||
refreshCSS(node, null, theme.new_href);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listThemes(site) {
|
export function listThemes(site) {
|
||||||
let themes = site.get("user_themes");
|
let themes = site.get("user_themes");
|
||||||
|
|
||||||
|
|
|
@ -273,12 +273,17 @@ export function addNavItem(item) {
|
||||||
NavItem.extraNavItemDescriptors.push(item);
|
NavItem.extraNavItemDescriptors.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(Discourse, "NavItem", {
|
if (typeof Discourse !== "undefined") {
|
||||||
get() {
|
Object.defineProperty(Discourse, "NavItem", {
|
||||||
deprecated("Import the NavItem class instead of using Discourse.NavItem", {
|
get() {
|
||||||
since: "2.4.0",
|
deprecated(
|
||||||
dropFrom: "2.5.0"
|
"Import the NavItem class instead of using Discourse.NavItem",
|
||||||
});
|
{
|
||||||
return NavItem;
|
since: "2.4.0",
|
||||||
}
|
dropFrom: "2.5.0"
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
return NavItem;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -61,12 +61,15 @@ const TopicDetails = RestModel.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateNotifications(v) {
|
updateNotifications(level) {
|
||||||
this.set("notification_level", v);
|
return ajax(`/t/${this.get("topic.id")}/notifications`, {
|
||||||
this.set("notifications_reason_id", null);
|
|
||||||
return ajax("/t/" + this.get("topic.id") + "/notifications", {
|
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: { notification_level: v }
|
data: { notification_level: level }
|
||||||
|
}).then(() => {
|
||||||
|
this.setProperties({
|
||||||
|
notification_level: level,
|
||||||
|
notifications_reason_id: null
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -64,50 +64,52 @@
|
||||||
</section>
|
</section>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<section class="about stats">
|
{{#if model.can_see_about_stats}}
|
||||||
<h3>{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}</h3>
|
<section class="about stats">
|
||||||
|
<h3>{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}</h3>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>{{i18n "about.stat.last_7_days"}}</th>
|
<th>{{i18n "about.stat.last_7_days"}}</th>
|
||||||
<th>{{i18n "about.stat.last_30_days"}}</th>
|
<th>{{i18n "about.stat.last_30_days"}}</th>
|
||||||
<th>{{i18n "about.stat.all_time"}}</th>
|
<th>{{i18n "about.stat.all_time"}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title">{{i18n "about.topic_count"}}</td>
|
<td class="title">{{i18n "about.topic_count"}}</td>
|
||||||
<td>{{number model.stats.topics_7_days}}</td>
|
<td>{{number model.stats.topics_7_days}}</td>
|
||||||
<td>{{number model.stats.topics_30_days}}</td>
|
<td>{{number model.stats.topics_30_days}}</td>
|
||||||
<td>{{number model.stats.topic_count}}</td>
|
<td>{{number model.stats.topic_count}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "about.post_count"}}</td>
|
<td>{{i18n "about.post_count"}}</td>
|
||||||
<td>{{number model.stats.posts_7_days}}</td>
|
<td>{{number model.stats.posts_7_days}}</td>
|
||||||
<td>{{number model.stats.posts_30_days}}</td>
|
<td>{{number model.stats.posts_30_days}}</td>
|
||||||
<td>{{number model.stats.post_count}}</td>
|
<td>{{number model.stats.post_count}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "about.user_count"}}</td>
|
<td>{{i18n "about.user_count"}}</td>
|
||||||
<td>{{number model.stats.users_7_days}}</td>
|
<td>{{number model.stats.users_7_days}}</td>
|
||||||
<td>{{number model.stats.users_30_days}}</td>
|
<td>{{number model.stats.users_30_days}}</td>
|
||||||
<td>{{number model.stats.user_count}}</td>
|
<td>{{number model.stats.user_count}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "about.active_user_count"}}</td>
|
<td>{{i18n "about.active_user_count"}}</td>
|
||||||
<td>{{number model.stats.active_users_7_days}}</td>
|
<td>{{number model.stats.active_users_7_days}}</td>
|
||||||
<td>{{number model.stats.active_users_30_days}}</td>
|
<td>{{number model.stats.active_users_30_days}}</td>
|
||||||
<td>—</td>
|
<td>—</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{i18n "about.like_count"}}</td>
|
<td>{{i18n "about.like_count"}}</td>
|
||||||
<td>{{number model.stats.likes_7_days}}</td>
|
<td>{{number model.stats.likes_7_days}}</td>
|
||||||
<td>{{number model.stats.likes_30_days}}</td>
|
<td>{{number model.stats.likes_30_days}}</td>
|
||||||
<td>{{number model.stats.like_count}}</td>
|
<td>{{number model.stats.like_count}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if contactInfo}}
|
{{#if contactInfo}}
|
||||||
<section class="about contact">
|
<section class="about contact">
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
{{#if showCategoryAdmin}}
|
{{#if showCategoryAdmin}}
|
||||||
{{categories-admin-dropdown
|
{{categories-admin-dropdown
|
||||||
onChange=(action "selectCategoryAdminDropdownAction")
|
onChange=(action "selectCategoryAdminDropdownAction")
|
||||||
|
options=(hash
|
||||||
|
triggerOnChangeOnTab=false
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
|
@ -50,10 +50,10 @@
|
||||||
args=(hash topic=topic)
|
args=(hash topic=topic)
|
||||||
tagName=""
|
tagName=""
|
||||||
connectorTagName="span"}}
|
connectorTagName="span"}}
|
||||||
|
|
||||||
{{pinned-button pinned=topic.pinned topic=topic}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{pinned-button pinned=topic.pinned topic=topic}}
|
||||||
|
|
||||||
{{#if showNotificationsButton}}
|
{{#if showNotificationsButton}}
|
||||||
{{topic-notifications-button
|
{{topic-notifications-button
|
||||||
notificationLevel=topic.details.notification_level
|
notificationLevel=topic.details.notification_level
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
{{combo-box
|
{{combo-box
|
||||||
content=userSelectableThemes
|
content=userSelectableThemes
|
||||||
value=themeId
|
value=themeId
|
||||||
onChange=(action (mut themeId))
|
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
{{#if themeIdChanged}}
|
||||||
|
<p class="alert alert-success save-theme-alert">{{i18n "user.save_to_change_theme" save_text=(i18n "save") }}</p>
|
||||||
|
{{/if}}
|
||||||
{{#if showThemeSetDefault}}
|
{{#if showThemeSetDefault}}
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
{{preference-checkbox labelKey="user.theme_default_on_all_devices" checked=makeThemeDefault}}
|
{{preference-checkbox labelKey="user.theme_default_on_all_devices" checked=makeThemeDefault}}
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
options=(hash
|
options=(hash
|
||||||
filterable=true
|
filterable=true
|
||||||
categoryId=buffered.category_id
|
categoryId=buffered.category_id
|
||||||
|
minimum=minimumRequiredTags
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -125,6 +125,7 @@ createWidget(
|
||||||
{
|
{
|
||||||
attributes: {
|
attributes: {
|
||||||
href: attrs.user.get("path"),
|
href: attrs.user.get("path"),
|
||||||
|
title: attrs.user.get("name"),
|
||||||
"data-auto-route": true
|
"data-auto-route": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
0
app/assets/javascripts/select-kit/addon/.gitkeep
Normal file
0
app/assets/javascripts/select-kit/addon/.gitkeep
Normal file
|
@ -67,6 +67,7 @@ export default ComboBoxComponent.extend({
|
||||||
|
|
||||||
search(filter) {
|
search(filter) {
|
||||||
if (filter) {
|
if (filter) {
|
||||||
|
filter = filter.toLowerCase();
|
||||||
return this.content.filter(item => {
|
return this.content.filter(item => {
|
||||||
const category = Category.findById(this.getValue(item));
|
const category = Category.findById(this.getValue(item));
|
||||||
const categoryName = this.getName(item);
|
const categoryName = this.getName(item);
|
|
@ -13,8 +13,7 @@ export default DropdownSelectBoxComponent.extend({
|
||||||
autoFilterable: false,
|
autoFilterable: false,
|
||||||
filterable: false,
|
filterable: false,
|
||||||
i18nPrefix: "",
|
i18nPrefix: "",
|
||||||
i18nPostfix: "",
|
i18nPostfix: ""
|
||||||
showCaret: true
|
|
||||||
},
|
},
|
||||||
|
|
||||||
modifyComponentForRow() {
|
modifyComponentForRow() {
|
|
@ -1,5 +1,6 @@
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import EmberObject, { computed, get, guidFor } from "@ember/object";
|
import EmberObject, { computed, get } from "@ember/object";
|
||||||
|
import { guidFor } from "@ember/object/internals";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
import { makeArray } from "discourse-common/lib/helpers";
|
import { makeArray } from "discourse-common/lib/helpers";
|
||||||
|
@ -271,7 +272,8 @@ export default Component.extend(
|
||||||
selectedNameComponent: "selected-name",
|
selectedNameComponent: "selected-name",
|
||||||
castInteger: false,
|
castInteger: false,
|
||||||
preventsClickPropagation: false,
|
preventsClickPropagation: false,
|
||||||
focusAfterOnChange: true
|
focusAfterOnChange: true,
|
||||||
|
triggerOnChangeOnTab: true
|
||||||
},
|
},
|
||||||
|
|
||||||
autoFilterable: computed("content.[]", "selectKit.filter", function() {
|
autoFilterable: computed("content.[]", "selectKit.filter", function() {
|
|
@ -140,7 +140,11 @@ export default Component.extend(UtilsMixin, {
|
||||||
this._focusFilterInput();
|
this._focusFilterInput();
|
||||||
} else if (event.keyCode === 9) {
|
} else if (event.keyCode === 9) {
|
||||||
// Tab
|
// Tab
|
||||||
if (this.selectKit.highlighted && this.selectKit.isExpanded) {
|
if (
|
||||||
|
this.selectKit.highlighted &&
|
||||||
|
this.selectKit.isExpanded &&
|
||||||
|
this.selectKit.options.triggerOnChangeOnTab
|
||||||
|
) {
|
||||||
this.selectKit.select(
|
this.selectKit.select(
|
||||||
this.getValue(this.selectKit.highlighted),
|
this.getValue(this.selectKit.highlighted),
|
||||||
this.selectKit.highlighted
|
this.selectKit.highlighted
|
|
@ -1,20 +1,28 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action, computed } from "@ember/object";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
layoutName: "select-kit/templates/components/topic-notifications-button",
|
layoutName: "select-kit/templates/components/topic-notifications-button",
|
||||||
classNames: ["topic-notifications-button"],
|
classNames: ["topic-notifications-button"],
|
||||||
|
classNameBindings: ["isLoading"],
|
||||||
appendReason: true,
|
appendReason: true,
|
||||||
showFullTitle: true,
|
showFullTitle: true,
|
||||||
placement: "bottom-start",
|
placement: "bottom-start",
|
||||||
notificationLevel: null,
|
notificationLevel: null,
|
||||||
topic: null,
|
topic: null,
|
||||||
showCaret: true,
|
showCaret: true,
|
||||||
|
isLoading: false,
|
||||||
|
icon: computed("isLoading", function() {
|
||||||
|
return this.isLoading ? "spinner" : null;
|
||||||
|
}),
|
||||||
|
|
||||||
@action
|
@action
|
||||||
changeTopicNotificationLevel(levelId) {
|
changeTopicNotificationLevel(levelId) {
|
||||||
if (levelId !== this.notificationLevel) {
|
if (levelId !== this.notificationLevel) {
|
||||||
this.topic.details.updateNotifications(levelId);
|
this.set("isLoading", true);
|
||||||
|
this.topic.details
|
||||||
|
.updateNotifications(levelId)
|
||||||
|
.finally(() => this.set("isLoading", false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user