This commit is contained in:
Jeff Atwood 2020-05-22 20:25:42 -07:00
commit e62a85cf6f
236 changed files with 1031 additions and 715 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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"));
} }

View File

@ -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

View File

@ -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,

View File

@ -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);
}, },

View File

@ -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 });
}, },

View File

@ -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(

View File

@ -0,0 +1,10 @@
import EmberObject from "@ember/object";
import Ember from "ember";
export default EmberObject.create({
reload: function() {
if (!Ember.testing) {
location.reload();
}
}
});

View File

@ -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]);

View File

@ -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() {
if (!this.get("topic.isPrivateMessage") || this.site.mobileView) {
const bookmarked = this.get("topic.bookmarked"); const bookmarked = this.get("topic.bookmarked");
return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title"; 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",

View File

@ -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");

View File

@ -273,12 +273,17 @@ export function addNavItem(item) {
NavItem.extraNavItemDescriptors.push(item); NavItem.extraNavItemDescriptors.push(item);
} }
Object.defineProperty(Discourse, "NavItem", { if (typeof Discourse !== "undefined") {
Object.defineProperty(Discourse, "NavItem", {
get() { get() {
deprecated("Import the NavItem class instead of using Discourse.NavItem", { deprecated(
"Import the NavItem class instead of using Discourse.NavItem",
{
since: "2.4.0", since: "2.4.0",
dropFrom: "2.5.0" dropFrom: "2.5.0"
}); }
);
return NavItem; return NavItem;
} }
}); });
}

View File

@ -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
});
}); });
}, },

View File

@ -64,6 +64,7 @@
</section> </section>
{{/each}} {{/each}}
{{/if}} {{/if}}
{{#if model.can_see_about_stats}}
<section class="about stats"> <section class="about stats">
<h3>{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}</h3> <h3>{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}</h3>
@ -108,6 +109,7 @@
</tbody> </tbody>
</table> </table>
</section> </section>
{{/if}}
{{#if contactInfo}} {{#if contactInfo}}
<section class="about contact"> <section class="about contact">

View File

@ -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}}

View File

@ -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

View File

@ -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}}

View File

@ -36,6 +36,7 @@
options=(hash options=(hash
filterable=true filterable=true
categoryId=buffered.category_id categoryId=buffered.category_id
minimum=minimumRequiredTags
) )
}} }}
{{/if}} {{/if}}

View File

@ -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
} }
}, },

View 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);

View File

@ -13,8 +13,7 @@ export default DropdownSelectBoxComponent.extend({
autoFilterable: false, autoFilterable: false,
filterable: false, filterable: false,
i18nPrefix: "", i18nPrefix: "",
i18nPostfix: "", i18nPostfix: ""
showCaret: true
}, },
modifyComponentForRow() { modifyComponentForRow() {

View File

@ -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() {

View File

@ -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

View File

@ -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