FEATURE: Modal for profile featured topic & admin wrench refactor (#8545)
This commit is contained in:
parent
7b6bafc651
commit
8c4ffaea1b
|
@ -4,6 +4,7 @@ import Component from "@ember/component";
|
|||
import discourseDebounce from "discourse/lib/debounce";
|
||||
import { searchForTerm } from "discourse/lib/search";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
loading: null,
|
||||
|
@ -11,7 +12,33 @@ export default Component.extend({
|
|||
topics: null,
|
||||
selectedTopicId: null,
|
||||
currentTopicId: null,
|
||||
additionalFilters: null,
|
||||
topicTitle: null,
|
||||
label: null,
|
||||
loadOnInit: false,
|
||||
topicChangedCallback: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.additionalFilters = this.additionalFilters || "";
|
||||
this.topicTitle = this.topicTitle || "";
|
||||
|
||||
if (this.loadOnInit && !isEmpty(this.additionalFilters)) {
|
||||
searchForTerm(this.additionalFilters, {}).then(results => {
|
||||
if (results && results.posts && results.posts.length > 0) {
|
||||
this.set(
|
||||
"topics",
|
||||
results.posts
|
||||
.mapBy("topic")
|
||||
.filter(t => t.id !== this.currentTopicId)
|
||||
);
|
||||
} else {
|
||||
this.setProperties({ topics: null, loading: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@observes("topicTitle")
|
||||
topicTitleChanged() {
|
||||
|
@ -24,6 +51,11 @@ export default Component.extend({
|
|||
this.search(this.topicTitle);
|
||||
},
|
||||
|
||||
@discourseComputed("label")
|
||||
labelText(label) {
|
||||
return label || "choose_topic.title.search";
|
||||
},
|
||||
|
||||
@observes("topics")
|
||||
topicsChanged() {
|
||||
if (this.topics) {
|
||||
|
@ -38,18 +70,21 @@ export default Component.extend({
|
|||
return;
|
||||
}
|
||||
|
||||
const currentTopicId = this.currentTopicId;
|
||||
|
||||
if (isEmpty(title)) {
|
||||
if (isEmpty(title) && isEmpty(this.additionalFilters)) {
|
||||
this.setProperties({ topics: null, loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
searchForTerm(title, {
|
||||
typeFilter: "topic",
|
||||
searchForId: true,
|
||||
restrictToArchetype: "regular"
|
||||
}).then(results => {
|
||||
const currentTopicId = this.currentTopicId;
|
||||
const titleWithFilters = `${title} ${this.additionalFilters}`;
|
||||
let searchParams = {};
|
||||
|
||||
if (!isEmpty(title)) {
|
||||
searchParams.typeFilter = "topic";
|
||||
searchParams.restrictToArchetype = "regular";
|
||||
}
|
||||
|
||||
searchForTerm(titleWithFilters, searchParams).then(results => {
|
||||
if (results && results.posts && results.posts.length > 0) {
|
||||
this.set(
|
||||
"topics",
|
||||
|
@ -67,7 +102,7 @@ export default Component.extend({
|
|||
next(() => {
|
||||
document.getElementById(`choose-topic-${topic.id}`).checked = true;
|
||||
});
|
||||
return false;
|
||||
if (this.topicChangedCallback) this.topicChangedCallback(topic);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ const REGEXP_MIN_POST_COUNT_PREFIX = /^min_post_count:/gi;
|
|||
const REGEXP_POST_TIME_PREFIX = /^(before|after):/gi;
|
||||
const REGEXP_TAGS_REPLACE = /(^(tags?:|#(?=[a-z0-9\-]+::tag))|::tag\s?$)/gi;
|
||||
|
||||
const REGEXP_IN_MATCH = /^(in|with):(posted|watching|tracking|bookmarks|first|pinned|unpinned|wiki|unseen|image)/gi;
|
||||
const REGEXP_IN_MATCH = /^(in|with):(posted|created|watching|tracking|bookmarks|first|pinned|unpinned|wiki|unseen|image)/gi;
|
||||
const REGEXP_SPECIAL_IN_LIKES_MATCH = /^in:likes/gi;
|
||||
const REGEXP_SPECIAL_IN_TITLE_MATCH = /^in:title/gi;
|
||||
const REGEXP_SPECIAL_IN_PERSONAL_MATCH = /^in:personal/gi;
|
||||
|
@ -41,6 +41,7 @@ export default Component.extend({
|
|||
this.inOptionsForUsers = [
|
||||
{ name: I18n.t("search.advanced.filters.unseen"), value: "unseen" },
|
||||
{ name: I18n.t("search.advanced.filters.posted"), value: "posted" },
|
||||
{ name: I18n.t("search.advanced.filters.created"), value: "created" },
|
||||
{ name: I18n.t("search.advanced.filters.watching"), value: "watching" },
|
||||
{ name: I18n.t("search.advanced.filters.tracking"), value: "tracking" },
|
||||
{ name: I18n.t("search.advanced.filters.bookmarks"), value: "bookmarks" }
|
||||
|
@ -57,6 +58,7 @@ export default Component.extend({
|
|||
this.statusOptions = [
|
||||
{ name: I18n.t("search.advanced.statuses.open"), value: "open" },
|
||||
{ name: I18n.t("search.advanced.statuses.closed"), value: "closed" },
|
||||
{ name: I18n.t("search.advanced.statuses.public"), value: "public" },
|
||||
{ name: I18n.t("search.advanced.statuses.archived"), value: "archived" },
|
||||
{
|
||||
name: I18n.t("search.advanced.statuses.noreplies"),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { alias, or, and } from "@ember/object/computed";
|
||||
import { propertyEqual } from "discourse/lib/computed";
|
||||
import Component from "@ember/component";
|
||||
import { getTopicFooterButtons } from "discourse/lib/register-topic-footer-button";
|
||||
|
||||
|
@ -10,11 +9,6 @@ export default Component.extend({
|
|||
// Allow us to extend it
|
||||
layoutName: "components/topic-footer-buttons",
|
||||
|
||||
topicFeaturedOnProfile: propertyEqual(
|
||||
"topic.id",
|
||||
"currentUser.featured_topic.id"
|
||||
),
|
||||
|
||||
@discourseComputed("topic.isPrivateMessage")
|
||||
canArchive(isPM) {
|
||||
return this.siteSettings.enable_personal_messages && isPM;
|
||||
|
@ -44,15 +38,6 @@ export default Component.extend({
|
|||
|
||||
inviteDisabled: or("topic.archived", "topic.closed", "topic.deleted"),
|
||||
|
||||
@discourseComputed
|
||||
showAdminButton() {
|
||||
return (
|
||||
!this.site.mobileView &&
|
||||
this.currentUser &&
|
||||
this.currentUser.get("canManageTopic")
|
||||
);
|
||||
},
|
||||
|
||||
showEditOnFooter: and("topic.isPrivateMessage", "site.can_tag_pms"),
|
||||
|
||||
@discourseComputed("topic.message_archived")
|
||||
|
@ -64,19 +49,5 @@ export default Component.extend({
|
|||
|
||||
@discourseComputed("topic.message_archived")
|
||||
archiveLabel: archived =>
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title",
|
||||
|
||||
@discourseComputed(
|
||||
"topic.user_id",
|
||||
"topic.isPrivateMessage",
|
||||
"topic.category.read_restricted"
|
||||
)
|
||||
showToggleFeatureOnProfileButton(userId, isPm, restricted) {
|
||||
return (
|
||||
this.siteSettings.allow_featured_topic_on_user_profiles &&
|
||||
userId === this.currentUser.get("id") &&
|
||||
!restricted &&
|
||||
!isPm
|
||||
);
|
||||
}
|
||||
archived ? "topic.move_to_inbox.title" : "topic.archive_message.title"
|
||||
});
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import Controller from "@ember/controller";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { none } from "@ember/object/computed";
|
||||
|
||||
export default Controller.extend(ModalFunctionality, {
|
||||
newFeaturedTopic: null,
|
||||
saving: false,
|
||||
noTopicSelected: none("newFeaturedTopic"),
|
||||
|
||||
onClose() {
|
||||
this.set("newFeaturedTopic", null);
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
return ajax(`/u/${this.model.username}/feature-topic`, {
|
||||
type: "PUT",
|
||||
data: { topic_id: this.newFeaturedTopic.id }
|
||||
})
|
||||
.then(() => {
|
||||
this.model.set("featured_topic", this.newFeaturedTopic);
|
||||
this.send("closeModal");
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
newTopicSelected(topic) {
|
||||
this.set("newFeaturedTopic", topic);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -5,6 +5,8 @@ import { default as discourseComputed } from "discourse-common/utils/decorators"
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { cookAsync } from "discourse/lib/text";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Controller.extend(PreferencesTabController, {
|
||||
init() {
|
||||
|
@ -48,6 +50,30 @@ export default Controller.extend(PreferencesTabController, {
|
|||
},
|
||||
|
||||
actions: {
|
||||
showFeaturedTopicModal() {
|
||||
showModal("feature-topic-on-profile", {
|
||||
model: this.model,
|
||||
title: "user.feature_topic_on_profile.title"
|
||||
});
|
||||
},
|
||||
|
||||
clearFeaturedTopicFromProfile() {
|
||||
bootbox.confirm(
|
||||
I18n.t("user.feature_topic_on_profile.clear.warning"),
|
||||
result => {
|
||||
if (result) {
|
||||
ajax(`/u/${this.model.username}/clear-featured-topic`, {
|
||||
type: "PUT"
|
||||
})
|
||||
.then(() => {
|
||||
this.model.set("featured_topic", null);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
save() {
|
||||
this.set("saved", false);
|
||||
|
||||
|
|
|
@ -166,34 +166,5 @@ export default {
|
|||
return this.site.mobileView;
|
||||
}
|
||||
});
|
||||
|
||||
registerTopicFooterButton({
|
||||
dependentKeys: ["currentUser.featured_topic"],
|
||||
id: "toggle-feature-on-profile",
|
||||
icon: "id-card",
|
||||
priority: 300,
|
||||
label() {
|
||||
return this.topicFeaturedOnProfile
|
||||
? "topic.remove_from_profile.title"
|
||||
: "topic.feature_on_profile.title";
|
||||
},
|
||||
title() {
|
||||
return this.topicFeaturedOnProfile
|
||||
? "topic.remove_from_profile.help"
|
||||
: "topic.feature_on_profile.help";
|
||||
},
|
||||
classNames() {
|
||||
return this.topicFeaturedOnProfile
|
||||
? ["feature-on-profile", "featured-on-profile"]
|
||||
: ["feature-on-profile"];
|
||||
},
|
||||
action: "toggleFeaturedOnProfile",
|
||||
displayed() {
|
||||
return this.showToggleFeatureOnProfileButton;
|
||||
},
|
||||
dropdown() {
|
||||
return this.site.mobileView;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<label for='choose-topic-title'>{{i18n 'choose_topic.title.search'}}</label>
|
||||
<label for='choose-topic-title'>{{i18n labelText}}</label>
|
||||
|
||||
{{text-field value=topicTitle placeholderKey="choose_topic.title.placeholder" id="choose-topic-title"}}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<div class='topic-footer-main-buttons'>
|
||||
{{#if showAdminButton}}
|
||||
{{topic-admin-menu-button
|
||||
topic=topic
|
||||
openUpwards="true"
|
||||
toggleMultiSelect=toggleMultiSelect
|
||||
deleteTopic=deleteTopic
|
||||
recoverTopic=recoverTopic
|
||||
toggleClosed=toggleClosed
|
||||
toggleArchived=toggleArchived
|
||||
toggleVisibility=toggleVisibility
|
||||
showTopicStatusUpdate=showTopicStatusUpdate
|
||||
showFeatureTopic=showFeatureTopic
|
||||
showChangeTimestamp=showChangeTimestamp
|
||||
resetBumpDate=resetBumpDate
|
||||
convertToPublicTopic=convertToPublicTopic
|
||||
convertToPrivateMessage=convertToPrivateMessage}}
|
||||
{{/if}}
|
||||
{{topic-admin-menu-button
|
||||
topic=topic
|
||||
openUpwards="true"
|
||||
toggleMultiSelect=toggleMultiSelect
|
||||
deleteTopic=deleteTopic
|
||||
recoverTopic=recoverTopic
|
||||
toggleFeaturedOnProfile=toggleFeaturedOnProfile
|
||||
toggleClosed=toggleClosed
|
||||
toggleArchived=toggleArchived
|
||||
toggleVisibility=toggleVisibility
|
||||
showTopicStatusUpdate=showTopicStatusUpdate
|
||||
showFeatureTopic=showFeatureTopic
|
||||
showChangeTimestamp=showChangeTimestamp
|
||||
resetBumpDate=resetBumpDate
|
||||
convertToPublicTopic=convertToPublicTopic
|
||||
convertToPrivateMessage=convertToPrivateMessage
|
||||
}}
|
||||
|
||||
{{#if site.mobileView}}
|
||||
{{topic-footer-mobile-dropdown topic=topic content=dropdownButtons}}
|
||||
|
|
|
@ -144,7 +144,7 @@
|
|||
<div class="card-row">
|
||||
<div class="featured-topic">
|
||||
<span class="desc">{{i18n 'user.featured_topic'}}</span>
|
||||
{{#link-to "topic" user.featured_topic.slug user.featured_topic.id }}{{user.featured_topic.fancy_title}}{{/link-to}}
|
||||
{{#link-to "topic" user.featured_topic.slug user.featured_topic.id }}{{{user.featured_topic.fancy_title}}}{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{{#d-modal-body class="feature-topic-on-profile"}}
|
||||
{{choose-topic currentTopicId=model.featured_topic.id
|
||||
selectedTopicId=newFeaturedTopicId
|
||||
additionalFilters="in:created status:public"
|
||||
label="user.feature_topic_on_profile.search_label"
|
||||
topicChangedCallback=(action "newTopicSelected")
|
||||
loadOnInit=true
|
||||
}}
|
||||
{{d-button action=(action "save")
|
||||
class="btn-primary save-featured-topic-on-profile"
|
||||
disabled=noTopicSelected
|
||||
label="user.feature_topic_on_profile.save"}}
|
||||
{{/d-modal-body}}
|
|
@ -57,18 +57,32 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if model.featured_topic}}
|
||||
{{#if siteSettings.allow_featured_topic_on_user_profiles}}
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{i18n 'user.featured_topic'}}</label>
|
||||
<label class="control-label">
|
||||
{{#link-to "topic" model.featured_topic.slug model.featured_topic.id}}{{model.featured_topic.fancy_title}}{{/link-to}}
|
||||
</label>
|
||||
{{#if model.featured_topic}}
|
||||
<label class="featured-topic-link">
|
||||
{{#link-to "topic" model.featured_topic.slug model.featured_topic.id}}{{{model.featured_topic.fancy_title}}}{{/link-to}}
|
||||
</label>
|
||||
{{/if}}
|
||||
|
||||
<div>
|
||||
{{d-button action=(action "showFeaturedTopicModal")
|
||||
class="btn-primary feature-topic-on-profile-btn"
|
||||
label="user.feature_topic_on_profile.open_search"}}
|
||||
{{#if model.featured_topic}}
|
||||
{{d-button action=(action "clearFeaturedTopicFromProfile")
|
||||
class="btn-danger clear-feature-topic-on-profile-btn"
|
||||
label="user.feature_topic_on_profile.clear.title"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.change_featured_topic.instructions'}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
{{plugin-outlet name="user-preferences-profile" args=(hash model=model save=(action "save"))}}
|
||||
|
||||
{{plugin-outlet name="user-custom-preferences" args=(hash model=model)}}
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
toggleClosed=(action "toggleClosed")
|
||||
toggleArchived=(action "toggleArchived")
|
||||
toggleVisibility=(action "toggleVisibility")
|
||||
toggleFeaturedOnProfile=(action "toggleFeaturedOnProfile")
|
||||
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
||||
showFeatureTopic=(route-action "showFeatureTopic")
|
||||
showChangeTimestamp=(route-action "showChangeTimestamp")
|
||||
|
@ -303,6 +304,7 @@
|
|||
toggleClosed=(action "toggleClosed")
|
||||
toggleArchived=(action "toggleArchived")
|
||||
toggleVisibility=(action "toggleVisibility")
|
||||
toggleFeaturedOnProfile=(action "toggleFeaturedOnProfile")
|
||||
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
||||
showFeatureTopic=(route-action "showFeatureTopic")
|
||||
showChangeTimestamp=(route-action "showChangeTimestamp")
|
||||
|
@ -314,8 +316,7 @@
|
|||
toggleArchiveMessage=(action "toggleArchiveMessage")
|
||||
editFirstPost=(action "editFirstPost")
|
||||
deferTopic=(action "deferTopic")
|
||||
replyToPost=(action "replyToPost")
|
||||
toggleFeaturedOnProfile=(action "toggleFeaturedOnProfile")}}
|
||||
replyToPost=(action "replyToPost")}}
|
||||
{{else}}
|
||||
<div id="topic-footer-buttons">
|
||||
{{d-button icon="reply" class="btn-primary pull-right" action=(route-action "showLogin") label="topic.reply.title"}}
|
||||
|
|
|
@ -32,14 +32,22 @@ createWidget("topic-admin-menu-button", {
|
|||
},
|
||||
|
||||
html(attrs, state) {
|
||||
if (!this.currentUser || !this.currentUser.get("canManageTopic")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
const menu = this.attach("topic-admin-menu", {
|
||||
position: state.position,
|
||||
fixed: attrs.fixed,
|
||||
topic: attrs.topic,
|
||||
openUpwards: attrs.openUpwards,
|
||||
rightSide: attrs.rightSide,
|
||||
actionButtons: []
|
||||
});
|
||||
|
||||
// We don't show the button when expanded on the right side
|
||||
if (!(attrs.rightSide && state.expanded)) {
|
||||
if (
|
||||
menu.attrs.actionButtons.length &&
|
||||
!(attrs.rightSide && state.expanded)
|
||||
) {
|
||||
result.push(
|
||||
this.attach("button", {
|
||||
className:
|
||||
|
@ -54,15 +62,7 @@ createWidget("topic-admin-menu-button", {
|
|||
}
|
||||
|
||||
if (state.expanded) {
|
||||
result.push(
|
||||
this.attach("topic-admin-menu", {
|
||||
position: state.position,
|
||||
fixed: attrs.fixed,
|
||||
topic: attrs.topic,
|
||||
openUpwards: attrs.openUpwards,
|
||||
rightSide: attrs.rightSide
|
||||
})
|
||||
);
|
||||
result.push(menu);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -102,6 +102,160 @@ export default createWidget("topic-admin-menu", {
|
|||
}
|
||||
},
|
||||
|
||||
init(attrs) {
|
||||
const topic = attrs.topic;
|
||||
const details = topic.get("details");
|
||||
const isPrivateMessage = topic.get("isPrivateMessage");
|
||||
const featured = topic.get("pinned_at") || topic.get("isBanner");
|
||||
const visible = topic.get("visible");
|
||||
|
||||
if (
|
||||
this.siteSettings.allow_featured_topic_on_user_profiles &&
|
||||
topic.user_id === this.currentUser.get("id") &&
|
||||
!topic.isPrivateMessage &&
|
||||
!topic.category.read_restricted
|
||||
) {
|
||||
let topicFeaturedOnProfile =
|
||||
topic.id === this.currentUser.get("featured_topic.id");
|
||||
|
||||
this.addActionButton({
|
||||
className: "topic-action-feature-on-profile",
|
||||
buttonClass: topicFeaturedOnProfile ? "btn-primary" : "btn-default",
|
||||
action: "toggleFeaturedOnProfile",
|
||||
icon: "id-card",
|
||||
fullLabel: topicFeaturedOnProfile
|
||||
? "topic.remove_from_profile.title"
|
||||
: "topic.feature_on_profile.title"
|
||||
});
|
||||
}
|
||||
|
||||
// Admin actions
|
||||
if (this.currentUser && this.currentUser.get("canManageTopic")) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-multi-select",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleMultiSelect",
|
||||
icon: "tasks",
|
||||
label: "actions.multi_select"
|
||||
});
|
||||
|
||||
if (details.get("can_delete")) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-delete",
|
||||
buttonClass: "btn-danger",
|
||||
action: "deleteTopic",
|
||||
icon: "far-trash-alt",
|
||||
label: "actions.delete"
|
||||
});
|
||||
}
|
||||
|
||||
if (topic.get("deleted") && details.get("can_recover")) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-recover",
|
||||
buttonClass: "btn-default",
|
||||
action: "recoverTopic",
|
||||
icon: "undo",
|
||||
label: "actions.recover"
|
||||
});
|
||||
}
|
||||
|
||||
if (topic.get("closed")) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-open",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleClosed",
|
||||
icon: "unlock",
|
||||
label: "actions.open"
|
||||
});
|
||||
} else {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-close",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleClosed",
|
||||
icon: "lock",
|
||||
label: "actions.close"
|
||||
});
|
||||
}
|
||||
|
||||
this.addActionButton({
|
||||
className: "topic-admin-status-update",
|
||||
buttonClass: "btn-default",
|
||||
action: "showTopicStatusUpdate",
|
||||
icon: "far-clock",
|
||||
label: "actions.timed_update"
|
||||
});
|
||||
|
||||
if (!isPrivateMessage && (topic.get("visible") || featured)) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-pin",
|
||||
buttonClass: "btn-default",
|
||||
action: "showFeatureTopic",
|
||||
icon: "thumbtack",
|
||||
label: featured ? "actions.unpin" : "actions.pin"
|
||||
});
|
||||
}
|
||||
|
||||
if (this.currentUser.get("staff")) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-change-timestamp",
|
||||
buttonClass: "btn-default",
|
||||
action: "showChangeTimestamp",
|
||||
icon: "calendar-alt",
|
||||
label: "change_timestamp.title"
|
||||
});
|
||||
}
|
||||
|
||||
this.addActionButton({
|
||||
className: "topic-admin-reset-bump-date",
|
||||
buttonClass: "btn-default",
|
||||
action: "resetBumpDate",
|
||||
icon: "anchor",
|
||||
label: "actions.reset_bump_date"
|
||||
});
|
||||
|
||||
if (!isPrivateMessage) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-archive",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleArchived",
|
||||
icon: "folder",
|
||||
label: topic.get("archived") ? "actions.unarchive" : "actions.archive"
|
||||
});
|
||||
}
|
||||
|
||||
this.addActionButton({
|
||||
className: "topic-admin-visible",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleVisibility",
|
||||
icon: visible ? "far-eye-slash" : "far-eye",
|
||||
label: visible ? "actions.invisible" : "actions.visible"
|
||||
});
|
||||
|
||||
if (details.get("can_convert_topic")) {
|
||||
this.addActionButton({
|
||||
className: "topic-admin-convert",
|
||||
buttonClass: "btn-default",
|
||||
action: isPrivateMessage
|
||||
? "convertToPublicTopic"
|
||||
: "convertToPrivateMessage",
|
||||
icon: isPrivateMessage ? "comment" : "envelope",
|
||||
label: isPrivateMessage
|
||||
? "actions.make_public"
|
||||
: "actions.make_private"
|
||||
});
|
||||
}
|
||||
|
||||
if (this.currentUser.get("staff")) {
|
||||
this.addActionButton({
|
||||
icon: "list",
|
||||
buttonClass: "btn-default",
|
||||
fullLabel: "review.moderation_history",
|
||||
url: `/review?topic_id=${topic.id}&status=all`
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
buildAttributes(attrs) {
|
||||
let { top, left, outerHeight } = attrs.position;
|
||||
const position = attrs.fixed ? "fixed" : "absolute";
|
||||
|
@ -129,148 +283,22 @@ export default createWidget("topic-admin-menu", {
|
|||
}
|
||||
},
|
||||
|
||||
addActionButton(button) {
|
||||
this.attrs.actionButtons.push(button);
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
const buttons = [];
|
||||
buttons.push({
|
||||
className: "topic-admin-multi-select",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleMultiSelect",
|
||||
icon: "tasks",
|
||||
label: "actions.multi_select"
|
||||
});
|
||||
|
||||
const topic = attrs.topic;
|
||||
const details = topic.get("details");
|
||||
|
||||
if (details.get("can_delete")) {
|
||||
buttons.push({
|
||||
className: "topic-admin-delete",
|
||||
buttonClass: "btn-danger",
|
||||
action: "deleteTopic",
|
||||
icon: "far-trash-alt",
|
||||
label: "actions.delete"
|
||||
});
|
||||
}
|
||||
|
||||
if (topic.get("deleted") && details.get("can_recover")) {
|
||||
buttons.push({
|
||||
className: "topic-admin-recover",
|
||||
buttonClass: "btn-default",
|
||||
action: "recoverTopic",
|
||||
icon: "undo",
|
||||
label: "actions.recover"
|
||||
});
|
||||
}
|
||||
|
||||
if (topic.get("closed")) {
|
||||
buttons.push({
|
||||
className: "topic-admin-open",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleClosed",
|
||||
icon: "unlock",
|
||||
label: "actions.open"
|
||||
});
|
||||
} else {
|
||||
buttons.push({
|
||||
className: "topic-admin-close",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleClosed",
|
||||
icon: "lock",
|
||||
label: "actions.close"
|
||||
});
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
className: "topic-admin-status-update",
|
||||
buttonClass: "btn-default",
|
||||
action: "showTopicStatusUpdate",
|
||||
icon: "far-clock",
|
||||
label: "actions.timed_update"
|
||||
});
|
||||
|
||||
const isPrivateMessage = topic.get("isPrivateMessage");
|
||||
|
||||
const featured = topic.get("pinned_at") || topic.get("isBanner");
|
||||
if (!isPrivateMessage && (topic.get("visible") || featured)) {
|
||||
buttons.push({
|
||||
className: "topic-admin-pin",
|
||||
buttonClass: "btn-default",
|
||||
action: "showFeatureTopic",
|
||||
icon: "thumbtack",
|
||||
label: featured ? "actions.unpin" : "actions.pin"
|
||||
});
|
||||
}
|
||||
|
||||
if (this.currentUser.get("staff")) {
|
||||
buttons.push({
|
||||
className: "topic-admin-change-timestamp",
|
||||
buttonClass: "btn-default",
|
||||
action: "showChangeTimestamp",
|
||||
icon: "calendar-alt",
|
||||
label: "change_timestamp.title"
|
||||
});
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
className: "topic-admin-reset-bump-date",
|
||||
buttonClass: "btn-default",
|
||||
action: "resetBumpDate",
|
||||
icon: "anchor",
|
||||
label: "actions.reset_bump_date"
|
||||
});
|
||||
|
||||
if (!isPrivateMessage) {
|
||||
buttons.push({
|
||||
className: "topic-admin-archive",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleArchived",
|
||||
icon: "folder",
|
||||
label: topic.get("archived") ? "actions.unarchive" : "actions.archive"
|
||||
});
|
||||
}
|
||||
|
||||
const visible = topic.get("visible");
|
||||
buttons.push({
|
||||
className: "topic-admin-visible",
|
||||
buttonClass: "btn-default",
|
||||
action: "toggleVisibility",
|
||||
icon: visible ? "far-eye-slash" : "far-eye",
|
||||
label: visible ? "actions.invisible" : "actions.visible"
|
||||
});
|
||||
|
||||
if (details.get("can_convert_topic")) {
|
||||
buttons.push({
|
||||
className: "topic-admin-convert",
|
||||
buttonClass: "btn-default",
|
||||
action: isPrivateMessage
|
||||
? "convertToPublicTopic"
|
||||
: "convertToPrivateMessage",
|
||||
icon: isPrivateMessage ? "comment" : "envelope",
|
||||
label: isPrivateMessage ? "actions.make_public" : "actions.make_private"
|
||||
});
|
||||
}
|
||||
|
||||
if (this.currentUser.get("staff")) {
|
||||
buttons.push({
|
||||
icon: "list",
|
||||
buttonClass: "btn-default",
|
||||
fullLabel: "review.moderation_history",
|
||||
url: `/review?topic_id=${topic.id}&status=all`
|
||||
});
|
||||
}
|
||||
|
||||
const extraButtons = applyDecorators(
|
||||
this,
|
||||
"adminMenuButtons",
|
||||
this.attrs,
|
||||
this.state
|
||||
);
|
||||
|
||||
return [
|
||||
h("h3", I18n.t("admin_title")),
|
||||
h("h3", I18n.t("topic.actions.title")),
|
||||
h(
|
||||
"ul",
|
||||
buttons
|
||||
attrs.actionButtons
|
||||
.concat(extraButtons)
|
||||
.filter(Boolean)
|
||||
.map(b => this.attach("admin-menu-button", b))
|
||||
|
|
|
@ -321,7 +321,7 @@ createWidget("timeline-controls", {
|
|||
const controls = [];
|
||||
const { fullScreen, currentUser, topic } = attrs;
|
||||
|
||||
if (!fullScreen && currentUser && currentUser.get("canManageTopic")) {
|
||||
if (!fullScreen && currentUser) {
|
||||
controls.push(this.attach("topic-admin-menu-button", { topic }));
|
||||
}
|
||||
|
||||
|
|
|
@ -694,6 +694,10 @@
|
|||
padding: 8px;
|
||||
}
|
||||
}
|
||||
.featured-topic-link {
|
||||
padding: 5px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.paginated-topics-list {
|
||||
|
|
|
@ -5,10 +5,5 @@
|
|||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
&.featured-on-profile {
|
||||
.d-icon {
|
||||
color: $tertiary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,7 +187,7 @@ en:
|
|||
enabled: "made this a banner %{when}. It will appear at the top of every page until it is dismissed by the user."
|
||||
disabled: "removed this banner %{when}. It will no longer appear at the top of every page."
|
||||
|
||||
topic_admin_menu: "topic admin actions"
|
||||
topic_admin_menu: "topic actions"
|
||||
|
||||
wizard_required: "Welcome to your new Discourse! Let’s get started with <a href='%{url}' data-auto-route='true'>the setup wizard</a> ✨"
|
||||
emails_are_disabled: "All outgoing email has been globally disabled by an administrator. No email notifications of any kind will be sent."
|
||||
|
@ -831,6 +831,14 @@ en:
|
|||
normal_option_title: "You will be notified if this user replies to you, quotes you, or mentions you."
|
||||
activity_stream: "Activity"
|
||||
preferences: "Preferences"
|
||||
feature_topic_on_profile:
|
||||
open_search: "Select a New Topic"
|
||||
title: "Select a Topic"
|
||||
search_label: "Search for Topic by title"
|
||||
save: "Save"
|
||||
clear:
|
||||
title: "Clear"
|
||||
warning: "Are you sure you want to clear your featured topic?"
|
||||
profile_hidden: "This user's public profile is hidden."
|
||||
expand_profile: "Expand"
|
||||
collapse_profile: "Collapse"
|
||||
|
@ -1063,7 +1071,7 @@ en:
|
|||
|
||||
change_featured_topic:
|
||||
title: "Featured Topic"
|
||||
instructions: "To change this, either navigate to the topic to remove it as the featured topic, or feature a different topic."
|
||||
instructions: "A link to this topic will be on your user card, and profile."
|
||||
|
||||
email:
|
||||
title: "Email"
|
||||
|
@ -1910,6 +1918,7 @@ en:
|
|||
title: Matching in title only
|
||||
likes: I liked
|
||||
posted: I posted in
|
||||
created: I created
|
||||
watching: I'm watching
|
||||
tracking: I'm tracking
|
||||
private: In my messages
|
||||
|
@ -1926,6 +1935,7 @@ en:
|
|||
label: Where topics
|
||||
open: are open
|
||||
closed: are closed
|
||||
public: are public
|
||||
archived: are archived
|
||||
noreplies: have zero replies
|
||||
single_user: contain a single user
|
||||
|
@ -2217,6 +2227,7 @@ en:
|
|||
description: "You will never be notified of anything about this topic, and it will not appear in latest."
|
||||
|
||||
actions:
|
||||
title: "Actions"
|
||||
recover: "Un-Delete Topic"
|
||||
delete: "Delete Topic"
|
||||
open: "Open Topic"
|
||||
|
|
|
@ -303,6 +303,14 @@ class Search
|
|||
posts.where('topics.closed')
|
||||
end
|
||||
|
||||
advanced_filter(/^status:public$/) do |posts|
|
||||
category_ids = Category
|
||||
.where(read_restricted: false)
|
||||
.pluck(:id)
|
||||
|
||||
posts.where("topics.category_id in (?)", category_ids)
|
||||
end
|
||||
|
||||
advanced_filter(/^status:archived$/) do |posts|
|
||||
posts.where('topics.archived')
|
||||
end
|
||||
|
@ -370,6 +378,10 @@ class Search
|
|||
posts.where("posts.user_id = #{@guardian.user.id}") if @guardian.user
|
||||
end
|
||||
|
||||
advanced_filter(/^in:created$/) do |posts|
|
||||
posts.where(user_id: @guardian.user.id, post_number: 1) if @guardian.user
|
||||
end
|
||||
|
||||
advanced_filter(/^in:(watching|tracking)$/) do |posts, match|
|
||||
if @guardian.user
|
||||
level = TopicUser.notification_levels[match.to_sym]
|
||||
|
|
|
@ -1006,24 +1006,34 @@ describe Search do
|
|||
end
|
||||
|
||||
it 'can find by status' do
|
||||
public_category = Fabricate(:category, read_restricted: false)
|
||||
post = Fabricate(:post, raw: 'hi this is a test 123 123')
|
||||
topic = post.topic
|
||||
topic.update(category: public_category)
|
||||
|
||||
private_category = Fabricate(:category, read_restricted: true)
|
||||
post2 = Fabricate(:post, raw: 'hi this is another test 123 123')
|
||||
second_topic = post2.topic
|
||||
second_topic.update(category: private_category)
|
||||
|
||||
post3 = Fabricate(:post, raw: "another test!", user: topic.user, topic: second_topic)
|
||||
|
||||
expect(Search.execute('test status:public').posts.length).to eq(1)
|
||||
expect(Search.execute('test status:closed').posts.length).to eq(0)
|
||||
expect(Search.execute('test status:open').posts.length).to eq(1)
|
||||
expect(Search.execute('test posts_count:1').posts.length).to eq(1)
|
||||
expect(Search.execute('test min_post_count:1').posts.length).to eq(1)
|
||||
|
||||
topic.closed = true
|
||||
topic.save
|
||||
topic.update(closed: true)
|
||||
second_topic.update(category: public_category)
|
||||
|
||||
expect(Search.execute('test status:public').posts.length).to eq(2)
|
||||
expect(Search.execute('test status:closed').posts.length).to eq(1)
|
||||
expect(Search.execute('status:closed').posts.length).to eq(1)
|
||||
expect(Search.execute('test status:open').posts.length).to eq(0)
|
||||
expect(Search.execute('test status:open').posts.length).to eq(1)
|
||||
|
||||
topic.archived = true
|
||||
topic.closed = false
|
||||
topic.save
|
||||
topic.update(archived: true, closed: false)
|
||||
second_topic.update(closed: true)
|
||||
|
||||
expect(Search.execute('test status:archived').posts.length).to eq(1)
|
||||
expect(Search.execute('test status:open').posts.length).to eq(0)
|
||||
|
@ -1032,7 +1042,9 @@ describe Search do
|
|||
|
||||
expect(Search.execute('test in:likes', guardian: Guardian.new(topic.user)).posts.length).to eq(0)
|
||||
|
||||
expect(Search.execute('test in:posted', guardian: Guardian.new(topic.user)).posts.length).to eq(1)
|
||||
expect(Search.execute('test in:posted', guardian: Guardian.new(topic.user)).posts.length).to eq(2)
|
||||
|
||||
expect(Search.execute('test in:created', guardian: Guardian.new(topic.user)).posts.length).to eq(1)
|
||||
|
||||
TopicUser.change(topic.user.id, topic.id, notification_level: TopicUser.notification_levels[:tracking])
|
||||
expect(Search.execute('test in:watching', guardian: Guardian.new(topic.user)).posts.length).to eq(0)
|
||||
|
|
|
@ -363,3 +363,54 @@ QUnit.test("recently connected devices", async assert => {
|
|||
"it should highlight password preferences"
|
||||
);
|
||||
});
|
||||
|
||||
acceptance(
|
||||
"User can select a topic to feature on profile if site setting in enabled",
|
||||
{
|
||||
loggedIn: true,
|
||||
settings: { allow_featured_topic_on_user_profiles: true },
|
||||
|
||||
pretend(server, helper) {
|
||||
server.put("/u/eviltrout/feature-topic", () => {
|
||||
return helper.response({
|
||||
success: true
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("setting featured topic on profile", async assert => {
|
||||
await visit("/u/eviltrout/preferences/profile");
|
||||
|
||||
assert.ok(
|
||||
!exists(".featured-topic-link"),
|
||||
"no featured topic link to present"
|
||||
);
|
||||
assert.ok(
|
||||
!exists(".clear-feature-topic-on-profile-btn"),
|
||||
"clear button not present"
|
||||
);
|
||||
|
||||
const selectTopicBtn = find(".feature-topic-on-profile-btn:first");
|
||||
assert.ok(exists(selectTopicBtn), "feature topic button is present");
|
||||
|
||||
await click(selectTopicBtn);
|
||||
|
||||
assert.ok(exists(".feature-topic-on-profile"), "topic picker modal is open");
|
||||
|
||||
const topicRadioBtn = find('input[name="choose_topic_id"]:first');
|
||||
assert.ok(exists(topicRadioBtn), "Topic options are prefilled");
|
||||
await click(topicRadioBtn);
|
||||
|
||||
await click(".save-featured-topic-on-profile");
|
||||
|
||||
assert.ok(
|
||||
exists(".featured-topic-link"),
|
||||
"link to featured topic is present"
|
||||
);
|
||||
assert.ok(
|
||||
exists(".clear-feature-topic-on-profile-btn"),
|
||||
"clear button is present"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import { moduleForWidget, widgetTest } from "helpers/widget-test";
|
||||
import Topic from "discourse/models/topic";
|
||||
import Category from "discourse/models/category";
|
||||
|
||||
moduleForWidget("topic-admin-menu-button");
|
||||
|
||||
const createArgs = topic => {
|
||||
return {
|
||||
topic: topic,
|
||||
openUpwards: "true",
|
||||
toggleMultiSelect: () => {},
|
||||
deleteTopic: () => {},
|
||||
recoverTopic: () => {},
|
||||
toggleFeaturedOnProfile: () => {},
|
||||
toggleClosed: () => {},
|
||||
toggleArchived: () => {},
|
||||
toggleVisibility: () => {},
|
||||
showTopicStatusUpdate: () => {},
|
||||
showFeatureTopic: () => {},
|
||||
showChangeTimestamp: () => {},
|
||||
resetBumpDate: () => {},
|
||||
convertToPublicTopic: () => {},
|
||||
convertToPrivateMessage: () => {}
|
||||
};
|
||||
};
|
||||
|
||||
widgetTest("topic-admin-menu-button is present for admin/moderators", {
|
||||
template: '{{mount-widget widget="topic-admin-menu-button" args=args}}',
|
||||
|
||||
beforeEach() {
|
||||
this.currentUser.setProperties({
|
||||
admin: true,
|
||||
moderator: true,
|
||||
id: 123
|
||||
});
|
||||
const topic = Topic.create({ user_id: this.currentUser.id });
|
||||
topic.category = Category.create({ read_restricted: true });
|
||||
this.siteSettings.allow_featured_topic_on_user_profiles = true;
|
||||
this.set("args", createArgs(topic));
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(exists(".toggle-admin-menu"), "admin wrench is present");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest(
|
||||
"topic-admin-menu-button shows for non-admin when the use can feature the topic",
|
||||
{
|
||||
template: '{{mount-widget widget="topic-admin-menu-button" args=args}}',
|
||||
|
||||
beforeEach() {
|
||||
this.currentUser.setProperties({
|
||||
admin: false,
|
||||
moderator: false,
|
||||
id: 123
|
||||
});
|
||||
const topic = Topic.create({ user_id: this.currentUser.id });
|
||||
topic.category = Category.create({ read_restricted: false });
|
||||
this.siteSettings.allow_featured_topic_on_user_profiles = true;
|
||||
this.set("args", createArgs(topic));
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(exists(".toggle-admin-menu"), "admin wrench is present");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
widgetTest(
|
||||
"topic-admin-menu-button hides for non-admin when there is no action",
|
||||
{
|
||||
template: '{{mount-widget widget="topic-admin-menu-button" args=args}}',
|
||||
|
||||
beforeEach() {
|
||||
this.currentUser.setProperties({
|
||||
admin: false,
|
||||
moderator: false,
|
||||
id: 123
|
||||
});
|
||||
const topic = Topic.create({ user_id: this.currentUser.id });
|
||||
topic.category = Category.create({ read_restricted: true });
|
||||
this.siteSettings.allow_featured_topic_on_user_profiles = true;
|
||||
this.set("args", createArgs(topic));
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(!exists(".toggle-admin-menu"), "admin wrench is present");
|
||||
}
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue