Revert "Revert "Merge branch 'master' of https://github.com/discourse/discourse""

This reverts commit 20780a1eee.

* SECURITY: re-adds accidentally reverted commit:
  03d26cd6: ensure embed_url contains valid http(s) uri
* when the merge commit e62a85cf was reverted, git chose the 2660c2e2 parent to land on
  instead of the 03d26cd6 parent (which contains security fixes)
This commit is contained in:
Michael Brown 2020-05-23 00:56:13 -04:00
parent 20780a1eee
commit d9a02d1336
No known key found for this signature in database
GPG Key ID: 6C07FB3007CF9360
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
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-protection' # security
@ -252,3 +248,5 @@ end
gem 'webpush', require: false
gem 'colored2', require: false
gem 'maxminddb'
gem 'rails_failover', require: false

View File

@ -195,7 +195,7 @@ GEM
mini_sql (0.2.5)
mini_suffix (0.3.0)
ffi (~> 1.9)
minitest (5.14.0)
minitest (5.14.1)
mocha (1.11.2)
mock_redis (0.23.0)
msgpack (1.3.3)
@ -262,7 +262,7 @@ GEM
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.5)
puma (4.3.3)
puma (4.3.5)
nio4r (~> 2.0)
r2 (0.2.7)
rack (2.2.2)
@ -277,6 +277,8 @@ GEM
nokogiri (>= 1.6)
rails-html-sanitizer (1.3.0)
loofah (~> 2.3)
rails_failover (0.2.0)
redis (~> 4)
rails_multisite (2.1.2)
activerecord (> 5.0, < 7)
railties (> 5.0, < 7)
@ -294,7 +296,7 @@ GEM
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
rbtrace (0.4.12)
rbtrace (0.4.13)
ffi (>= 1.0.6)
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
@ -341,13 +343,16 @@ GEM
json-schema (~> 2.2)
railties (>= 3.1, < 7.0)
rtlit (0.0.5)
rubocop (0.83.0)
rubocop (0.84.0)
parallel (~> 1.10)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
rexml
rubocop-ast (>= 0.0.3)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.0.3)
parser (>= 2.7.0.1)
rubocop-discourse (2.1.2)
rubocop (>= 0.69.0)
rubocop-rspec (>= 1.39.0)
@ -509,6 +514,7 @@ DEPENDENCIES
rack (= 2.2.2)
rack-mini-profiler
rack-protection
rails_failover
rails_multisite
railties (= 6.0.3)
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() {
let deletedPosts = 0;
const user = this;

View File

@ -1,5 +1,4 @@
import { get } from "@ember/object";
import { isEmpty } from "@ember/utils";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
@ -15,7 +14,7 @@ export default DiscourseRoute.extend({
},
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"));
}

View File

@ -1,6 +1,6 @@
//= require_tree ./discourse-common/addon
//= require ./polyfills
//= require_tree ./select-kit/app
//= require_tree ./select-kit/addon
//= require ./discourse/app/app
//= require ./app-boot

View File

@ -36,7 +36,6 @@ var define, requirejs;
default: Ember.Object,
get: Ember.get,
getProperties: Ember.getProperties,
guidFor: Ember.guidFor,
set: Ember.set,
setProperties: Ember.setProperties,
computed: Ember.computed,

View File

@ -2,13 +2,10 @@ import I18n from "I18n";
import { inject } from "@ember/controller";
import Controller from "@ember/controller";
import { setDefaultHomepage } from "discourse/lib/utilities";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import {
listThemes,
previewTheme,
setLocalTheme
} from "discourse/lib/theme-selector";
import discourseComputed from "discourse-common/utils/decorators";
import { listThemes, setLocalTheme } from "discourse/lib/theme-selector";
import { popupAjaxError } from "discourse/lib/ajax-error";
import pageReloader from "discourse/helpers/page-reloader";
import {
safariHacksDisabled,
isiPad,
@ -28,6 +25,9 @@ const TEXT_SIZES = ["smaller", "normal", "larger", "largest"];
const TITLE_COUNT_MODES = ["notifications", "contextual"];
export default Controller.extend({
currentThemeId: -1,
preferencesController: inject("preferences"),
@discourseComputed("makeThemeDefault")
saveAttrNames(makeDefault) {
let attrs = [
@ -51,8 +51,6 @@ export default Controller.extend({
return attrs;
},
preferencesController: inject("preferences"),
@discourseComputed()
isiPad() {
// TODO: remove this preference checkbox when iOS adoption > 90%
@ -105,10 +103,14 @@ export default Controller.extend({
return themes && themes.length > 1;
},
@observes("themeId")
themeIdChanged() {
const id = this.themeId;
previewTheme([id]);
@discourseComputed("themeId")
themeIdChanged(themeId) {
if (this.currentThemeId === -1) {
this.set("currentThemeId", themeId);
return false;
} else {
return this.currentThemeId !== themeId;
}
},
@discourseComputed("model.user_option.theme_ids", "themeId")
@ -189,6 +191,10 @@ export default Controller.extend({
this.disableSafariHacks.toString()
);
}
if (this.themeId !== this.currentThemeId) {
pageReloader.reload();
}
})
.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() {
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),
hasBeenSuspended: gt("model.number_of_suspensions", 0),
hasReceivedWarnings: gt("model.warnings_received_count", 0),
hasRejectedPosts: gt("model.number_of_rejected_posts", 0),
showStaffCounters: or(
"hasGivenFlags",
"hasFlaggedPosts",
"hasDeletedPosts",
"hasBeenSuspended",
"hasReceivedWarnings"
"hasReceivedWarnings",
"hasRejectedPosts"
),
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 { iconHTML } from "discourse-common/lib/icon-library";
import I18n from "I18n";
import { guidFor } from "@ember/object/internals";
// http://github.com/feross/clipboard-copy
function clipboardCopy(text) {
@ -90,7 +91,7 @@ export default {
const state = button.innerHTML;
button.innerHTML = I18n.t("copy_codeblock.copied");
const commandId = Ember.guidFor(button);
const commandId = guidFor(button);
if (_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 { 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 {
name: "topic-footer-buttons",
@ -11,8 +18,12 @@ export default {
registerTopicFooterButton({
id: "share-and-invite",
icon: "link",
priority: 999,
label: "topic.share.title",
priority: SHARE_PRIORITY,
label() {
if (!this.get("topic.isPrivateMessage") || this.site.mobileView) {
return "topic.share.title";
}
},
title: "topic.share.help",
action() {
const panels = [
@ -67,7 +78,7 @@ export default {
registerTopicFooterButton({
id: "flag",
icon: "flag",
priority: 998,
priority: FLAG_PRIORITY,
label: "topic.flag_topic.title",
title: "topic.flag_topic.help",
action: "showFlagTopic",
@ -93,14 +104,16 @@ export default {
}
return "bookmark";
},
priority: 1000,
priority: BOOKMARK_PRIORITY,
classNames() {
const bookmarked = this.get("topic.bookmarked");
return bookmarked ? ["bookmark", "bookmarked"] : ["bookmark"];
},
label() {
const bookmarked = this.get("topic.bookmarked");
return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title";
if (!this.get("topic.isPrivateMessage") || this.site.mobileView) {
const bookmarked = this.get("topic.bookmarked");
return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title";
}
},
translatedTitle() {
const bookmarked = this.get("topic.bookmarked");
@ -126,7 +139,7 @@ export default {
registerTopicFooterButton({
id: "archive",
priority: 996,
priority: ARCHIVE_PRIORITY,
icon() {
return this.archiveIcon;
},
@ -155,13 +168,16 @@ export default {
registerTopicFooterButton({
id: "edit-message",
priority: 750,
priority: EDIT_MESSAGE_PRIORITY,
icon: "pencil-alt",
label: "topic.edit_message.title",
title: "topic.edit_message.help",
action: "editFirstPost",
classNames: ["edit-message"],
dependentKeys: ["editFirstPost", "showEditOnFooter"],
dropdown() {
return this.site.mobileView && this.get("topic.isPrivateMessage");
},
displayed() {
return this.showEditOnFooter;
}
@ -170,7 +186,7 @@ export default {
registerTopicFooterButton({
id: "defer",
icon: "circle",
priority: 300,
priority: DEFER_PRIORITY,
label: "topic.defer.title",
title: "topic.defer.help",
action: "deferTopic",

View File

@ -1,5 +1,4 @@
import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
import deprecated from "discourse-common/lib/deprecated";
const keySelector = "meta[name=discourse_theme_ids]";
@ -79,31 +78,6 @@ export function refreshCSS(node, hash, newHref) {
$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) {
let themes = site.get("user_themes");

View File

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

View File

@ -61,12 +61,15 @@ const TopicDetails = RestModel.extend({
}
},
updateNotifications(v) {
this.set("notification_level", v);
this.set("notifications_reason_id", null);
return ajax("/t/" + this.get("topic.id") + "/notifications", {
updateNotifications(level) {
return ajax(`/t/${this.get("topic.id")}/notifications`, {
type: "POST",
data: { notification_level: v }
data: { notification_level: level }
}).then(() => {
this.setProperties({
notification_level: level,
notifications_reason_id: null
});
});
},

View File

@ -64,50 +64,52 @@
</section>
{{/each}}
{{/if}}
<section class="about stats">
<h3>{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}</h3>
{{#if model.can_see_about_stats}}
<section class="about stats">
<h3>{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}</h3>
<table class="table">
<tbody>
<tr>
<th>&nbsp;</th>
<th>{{i18n "about.stat.last_7_days"}}</th>
<th>{{i18n "about.stat.last_30_days"}}</th>
<th>{{i18n "about.stat.all_time"}}</th>
</tr>
<tr>
<td class="title">{{i18n "about.topic_count"}}</td>
<td>{{number model.stats.topics_7_days}}</td>
<td>{{number model.stats.topics_30_days}}</td>
<td>{{number model.stats.topic_count}}</td>
</tr>
<tr>
<td>{{i18n "about.post_count"}}</td>
<td>{{number model.stats.posts_7_days}}</td>
<td>{{number model.stats.posts_30_days}}</td>
<td>{{number model.stats.post_count}}</td>
</tr>
<tr>
<td>{{i18n "about.user_count"}}</td>
<td>{{number model.stats.users_7_days}}</td>
<td>{{number model.stats.users_30_days}}</td>
<td>{{number model.stats.user_count}}</td>
</tr>
<tr>
<td>{{i18n "about.active_user_count"}}</td>
<td>{{number model.stats.active_users_7_days}}</td>
<td>{{number model.stats.active_users_30_days}}</td>
<td>&mdash;</td>
</tr>
<tr>
<td>{{i18n "about.like_count"}}</td>
<td>{{number model.stats.likes_7_days}}</td>
<td>{{number model.stats.likes_30_days}}</td>
<td>{{number model.stats.like_count}}</td>
</tr>
</tbody>
</table>
</section>
<table class="table">
<tbody>
<tr>
<th>&nbsp;</th>
<th>{{i18n "about.stat.last_7_days"}}</th>
<th>{{i18n "about.stat.last_30_days"}}</th>
<th>{{i18n "about.stat.all_time"}}</th>
</tr>
<tr>
<td class="title">{{i18n "about.topic_count"}}</td>
<td>{{number model.stats.topics_7_days}}</td>
<td>{{number model.stats.topics_30_days}}</td>
<td>{{number model.stats.topic_count}}</td>
</tr>
<tr>
<td>{{i18n "about.post_count"}}</td>
<td>{{number model.stats.posts_7_days}}</td>
<td>{{number model.stats.posts_30_days}}</td>
<td>{{number model.stats.post_count}}</td>
</tr>
<tr>
<td>{{i18n "about.user_count"}}</td>
<td>{{number model.stats.users_7_days}}</td>
<td>{{number model.stats.users_30_days}}</td>
<td>{{number model.stats.user_count}}</td>
</tr>
<tr>
<td>{{i18n "about.active_user_count"}}</td>
<td>{{number model.stats.active_users_7_days}}</td>
<td>{{number model.stats.active_users_30_days}}</td>
<td>&mdash;</td>
</tr>
<tr>
<td>{{i18n "about.like_count"}}</td>
<td>{{number model.stats.likes_7_days}}</td>
<td>{{number model.stats.likes_30_days}}</td>
<td>{{number model.stats.like_count}}</td>
</tr>
</tbody>
</table>
</section>
{{/if}}
{{#if contactInfo}}
<section class="about contact">

View File

@ -3,6 +3,9 @@
{{#if showCategoryAdmin}}
{{categories-admin-dropdown
onChange=(action "selectCategoryAdminDropdownAction")
options=(hash
triggerOnChangeOnTab=false
)
}}
{{/if}}

View File

@ -50,10 +50,10 @@
args=(hash topic=topic)
tagName=""
connectorTagName="span"}}
{{pinned-button pinned=topic.pinned topic=topic}}
</div>
{{pinned-button pinned=topic.pinned topic=topic}}
{{#if showNotificationsButton}}
{{topic-notifications-button
notificationLevel=topic.details.notification_level

View File

@ -5,9 +5,11 @@
{{combo-box
content=userSelectableThemes
value=themeId
onChange=(action (mut themeId))
}}
</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}}
<div class="controls">
{{preference-checkbox labelKey="user.theme_default_on_all_devices" checked=makeThemeDefault}}

View File

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

View File

@ -125,6 +125,7 @@ createWidget(
{
attributes: {
href: attrs.user.get("path"),
title: attrs.user.get("name"),
"data-auto-route": true
}
},

View File

@ -67,6 +67,7 @@ export default ComboBoxComponent.extend({
search(filter) {
if (filter) {
filter = filter.toLowerCase();
return this.content.filter(item => {
const category = Category.findById(this.getValue(item));
const categoryName = this.getName(item);

View File

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

View File

@ -1,5 +1,6 @@
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 deprecated from "discourse-common/lib/deprecated";
import { makeArray } from "discourse-common/lib/helpers";
@ -271,7 +272,8 @@ export default Component.extend(
selectedNameComponent: "selected-name",
castInteger: false,
preventsClickPropagation: false,
focusAfterOnChange: true
focusAfterOnChange: true,
triggerOnChangeOnTab: true
},
autoFilterable: computed("content.[]", "selectKit.filter", function() {

View File

@ -140,7 +140,11 @@ export default Component.extend(UtilsMixin, {
this._focusFilterInput();
} else if (event.keyCode === 9) {
// Tab
if (this.selectKit.highlighted && this.selectKit.isExpanded) {
if (
this.selectKit.highlighted &&
this.selectKit.isExpanded &&
this.selectKit.options.triggerOnChangeOnTab
) {
this.selectKit.select(
this.getValue(this.selectKit.highlighted),
this.selectKit.highlighted

View File

@ -1,20 +1,28 @@
import Component from "@ember/component";
import { action } from "@ember/object";
import { action, computed } from "@ember/object";
export default Component.extend({
layoutName: "select-kit/templates/components/topic-notifications-button",
classNames: ["topic-notifications-button"],
classNameBindings: ["isLoading"],
appendReason: true,
showFullTitle: true,
placement: "bottom-start",
notificationLevel: null,
topic: null,
showCaret: true,
isLoading: false,
icon: computed("isLoading", function() {
return this.isLoading ? "spinner" : null;
}),
@action
changeTopicNotificationLevel(levelId) {
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