mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 01:47:22 +08:00
FEATURE: opt-in guidance on topics for users without access (#7852)
Co-Authored-By: majakomel <maja.komel@gmail.com> Co-Authored-By: Robin Ward <robin.ward@gmail.com>
This commit is contained in:
parent
5fdf228db6
commit
71bf9ec1b2
|
@ -0,0 +1,17 @@
|
||||||
|
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ["topic-notice"],
|
||||||
|
|
||||||
|
@computed("model.group.{full_name,name,allow_membership_requests}")
|
||||||
|
accessViaGroupText(group) {
|
||||||
|
const name = group.full_name || group.name;
|
||||||
|
const suffix = group.allow_membership_requests ? "request" : "join";
|
||||||
|
return I18n.t(`topic.group_${suffix}`, { name });
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("model.group.allow_membership_requests")
|
||||||
|
accessViaGroupButtonText(allowRequest) {
|
||||||
|
return `groups.${allowRequest ? "request" : "join"}`;
|
||||||
|
}
|
||||||
|
});
|
|
@ -941,6 +941,46 @@ export default Ember.Controller.extend(bufferedProperty("model"), {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
joinGroup() {
|
||||||
|
const groupId = this.get("model.group.id");
|
||||||
|
if (groupId) {
|
||||||
|
if (this.get("model.group.allow_membership_requests")) {
|
||||||
|
const groupName = this.get("model.group.name");
|
||||||
|
return ajax(`/groups/${groupName}/request_membership`, {
|
||||||
|
type: "POST",
|
||||||
|
data: {
|
||||||
|
topic_id: this.get("model.id")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
bootbox.alert(
|
||||||
|
I18n.t("topic.group_request_sent", {
|
||||||
|
group_name: this.get("model.group.full_name")
|
||||||
|
}),
|
||||||
|
() =>
|
||||||
|
this.previousURL
|
||||||
|
? DiscourseURL.routeTo(this.previousURL)
|
||||||
|
: DiscourseURL.routeTo("/")
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
} else {
|
||||||
|
const topic = this.model;
|
||||||
|
return ajax(`/groups/${groupId}/members`, {
|
||||||
|
type: "PUT",
|
||||||
|
data: { user_id: this.get("currentUser.id") }
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
topic.reload().then(() => {
|
||||||
|
topic.set("view_hidden", false);
|
||||||
|
topic.postStream.refresh();
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
replyAsNewTopic(post, quotedText) {
|
replyAsNewTopic(post, quotedText) {
|
||||||
const composerController = this.composer;
|
const composerController = this.composer;
|
||||||
|
|
||||||
|
|
|
@ -481,12 +481,12 @@ const Topic = RestModel.extend({
|
||||||
|
|
||||||
// Update our attributes from a JSON result
|
// Update our attributes from a JSON result
|
||||||
updateFromJson(json) {
|
updateFromJson(json) {
|
||||||
this.details.updateFromJson(json.details);
|
|
||||||
|
|
||||||
const keys = Object.keys(json);
|
const keys = Object.keys(json);
|
||||||
keys.removeObject("details");
|
if (!json.view_hidden) {
|
||||||
keys.removeObject("post_stream");
|
this.details.updateFromJson(json.details);
|
||||||
|
|
||||||
|
keys.removeObjects(["details", "post_stream"]);
|
||||||
|
}
|
||||||
keys.forEach(key => this.set(key, json[key]));
|
keys.forEach(key => this.set(key, json[key]));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,11 @@ export default Discourse.Route.extend({
|
||||||
// TODO we are seeing errors where closest post is null and this is exploding
|
// TODO we are seeing errors where closest post is null and this is exploding
|
||||||
// we need better handling and logging for this condition.
|
// we need better handling and logging for this condition.
|
||||||
|
|
||||||
|
// there are no closestPost for hidden topics
|
||||||
|
if (topic.view_hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// The post we requested might not exist. Let's find the closest post
|
// The post we requested might not exist. Let's find the closest post
|
||||||
const closestPost = postStream.closestPostForPostNumber(
|
const closestPost = postStream.closestPostForPostNumber(
|
||||||
params.nearPost || 1
|
params.nearPost || 1
|
||||||
|
@ -76,5 +81,14 @@ export default Discourse.Route.extend({
|
||||||
console.log("Could not view topic", e);
|
console.log("Could not view topic", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
willTransition() {
|
||||||
|
this.controllerFor("topic").set(
|
||||||
|
"previousURL",
|
||||||
|
document.location.pathname
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{accessViaGroupText}}
|
||||||
|
{{d-button action=action
|
||||||
|
class="btn-primary topic-join-group"
|
||||||
|
icon="user-plus"
|
||||||
|
label=accessViaGroupButtonText}}
|
|
@ -1,144 +1,95 @@
|
||||||
{{#discourse-topic multiSelect=multiSelect enteredAt=enteredAt topic=model hasScrolled=hasScrolled}}
|
{{#discourse-topic multiSelect=multiSelect enteredAt=enteredAt topic=model hasScrolled=hasScrolled}}
|
||||||
{{#if model}}
|
{{#if model.view_hidden}}
|
||||||
{{add-category-tag-classes category=model.category tags=model.tags}}
|
{{topic-join-group-notice model=model action=(action "joinGroup")}}
|
||||||
<div class="container">
|
{{else}}
|
||||||
{{discourse-banner user=currentUser banner=site.banner overlay=hasScrolled hide=model.errorLoading}}
|
{{#if model}}
|
||||||
</div>
|
{{add-category-tag-classes category=model.category tags=model.tags}}
|
||||||
{{/if}}
|
<div class="container">
|
||||||
|
{{discourse-banner user=currentUser banner=site.banner overlay=hasScrolled hide=model.errorLoading}}
|
||||||
{{#if showSharedDraftControls}}
|
</div>
|
||||||
{{shared-draft-controls topic=model}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{plugin-outlet name="topic-above-post-stream" args=(hash model=model)}}
|
|
||||||
|
|
||||||
{{#if model.postStream.loaded}}
|
|
||||||
{{#if model.postStream.firstPostPresent}}
|
|
||||||
{{#topic-title cancelled=(action "cancelEditingTopic") save=(action "finishedEditingTopic") model=model}}
|
|
||||||
{{#if editingTopic}}
|
|
||||||
<div class="edit-topic-title">
|
|
||||||
{{#if model.isPrivateMessage}}
|
|
||||||
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
|
||||||
{{/if}}
|
|
||||||
{{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
|
|
||||||
{{#if showCategoryChooser}}
|
|
||||||
{{category-chooser
|
|
||||||
class="small"
|
|
||||||
value=(unbound buffered.category_id)
|
|
||||||
onSelectAny=(action "topicCategoryChanged")}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if canEditTags}}
|
|
||||||
{{mini-tag-chooser filterable=true tags=buffered.tags categoryId=buffered.category_id}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{plugin-outlet name="edit-topic" args=(hash model=model buffered=buffered)}}
|
|
||||||
<div class="edit-controls">
|
|
||||||
{{d-button action=(action "finishedEditingTopic") class="btn-primary submit-edit" icon="check"}}
|
|
||||||
{{d-button action=(action "cancelEditingTopic") class="btn-default cancel-edit" icon="times"}}
|
|
||||||
|
|
||||||
{{#if canRemoveTopicFeaturedLink}}
|
|
||||||
<a href {{action "removeFeaturedLink"}} class="remove-featured-link" title="{{i18n "composer.remove_featured_link"}}">
|
|
||||||
{{d-icon "times-circle"}}
|
|
||||||
{{featuredLinkDomain}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{else}}
|
|
||||||
<h1 data-topic-id="{{unbound model.id}}">
|
|
||||||
{{#unless model.is_warning}}
|
|
||||||
{{#if siteSettings.enable_personal_messages}}
|
|
||||||
<a href={{pmPath}}>
|
|
||||||
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
|
||||||
</a>
|
|
||||||
{{else}}
|
|
||||||
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
|
||||||
{{/if}}
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if model.details.loaded}}
|
|
||||||
{{topic-status topic=model}}
|
|
||||||
<a href="{{unbound model.url}}" {{action "jumpTop"}} class="fancy-title">
|
|
||||||
{{{model.fancyTitle}}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if model.details.can_edit}}
|
|
||||||
<a href {{action "editTopic"}} class="edit-topic" title="{{i18n "edit"}}">{{d-icon "pencil-alt"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
{{topic-category topic=model class="topic-category"}}
|
|
||||||
{{/if}}
|
|
||||||
{{/topic-title}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showSharedDraftControls}}
|
||||||
|
{{shared-draft-controls topic=model}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="container posts">
|
{{plugin-outlet name="topic-above-post-stream" args=(hash model=model)}}
|
||||||
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
|
|
||||||
{{partial "selected-posts"}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#topic-navigation topic=model jumpToDate=(action "jumpToDate") jumpToIndex=(action "jumpToIndex") as |info|}}
|
{{#if model.postStream.loaded}}
|
||||||
{{#if info.renderTimeline}}
|
{{#if model.postStream.firstPostPresent}}
|
||||||
{{#if info.renderAdminMenuButton}}
|
{{#topic-title cancelled=(action "cancelEditingTopic") save=(action "finishedEditingTopic") model=model}}
|
||||||
{{topic-admin-menu-button
|
{{#if editingTopic}}
|
||||||
topic=model
|
<div class="edit-topic-title">
|
||||||
fixed="true"
|
{{#if model.isPrivateMessage}}
|
||||||
toggleMultiSelect=(action "toggleMultiSelect")
|
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
||||||
hideMultiSelect=(action "hideMultiSelect")
|
{{/if}}
|
||||||
deleteTopic=(action "deleteTopic")
|
{{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
|
||||||
recoverTopic=(action "recoverTopic")
|
{{#if showCategoryChooser}}
|
||||||
toggleClosed=(action "toggleClosed")
|
{{category-chooser
|
||||||
toggleArchived=(action "toggleArchived")
|
class="small"
|
||||||
toggleVisibility=(action "toggleVisibility")
|
value=(unbound buffered.category_id)
|
||||||
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
onSelectAny=(action "topicCategoryChanged")}}
|
||||||
showFeatureTopic=(route-action "showFeatureTopic")
|
{{/if}}
|
||||||
showChangeTimestamp=(route-action "showChangeTimestamp")
|
|
||||||
resetBumpDate=(action "resetBumpDate")
|
{{#if canEditTags}}
|
||||||
convertToPublicTopic=(action "convertToPublicTopic")
|
{{mini-tag-chooser filterable=true tags=buffered.tags categoryId=buffered.category_id}}
|
||||||
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{plugin-outlet name="edit-topic" args=(hash model=model buffered=buffered)}}
|
||||||
|
<div class="edit-controls">
|
||||||
|
{{d-button action=(action "finishedEditingTopic") class="btn-primary submit-edit" icon="check"}}
|
||||||
|
{{d-button action=(action "cancelEditingTopic") class="btn-default cancel-edit" icon="times"}}
|
||||||
|
|
||||||
|
{{#if canRemoveTopicFeaturedLink}}
|
||||||
|
<a href {{action "removeFeaturedLink"}} class="remove-featured-link" title="{{i18n "composer.remove_featured_link"}}">
|
||||||
|
{{d-icon "times-circle"}}
|
||||||
|
{{featuredLinkDomain}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{else}}
|
||||||
|
<h1 data-topic-id="{{unbound model.id}}">
|
||||||
|
{{#unless model.is_warning}}
|
||||||
|
{{#if siteSettings.enable_personal_messages}}
|
||||||
|
<a href={{pmPath}}>
|
||||||
|
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
||||||
|
</a>
|
||||||
|
{{else}}
|
||||||
|
<span class="private-message-glyph">{{d-icon "envelope"}}</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if model.details.loaded}}
|
||||||
|
{{topic-status topic=model}}
|
||||||
|
<a href="{{unbound model.url}}" {{action "jumpTop"}} class="fancy-title">
|
||||||
|
{{{model.fancyTitle}}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if model.details.can_edit}}
|
||||||
|
<a href {{action "editTopic"}} class="edit-topic" title="{{i18n "edit"}}">{{d-icon "pencil-alt"}}</a>
|
||||||
|
{{/if}}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{{topic-category topic=model class="topic-category"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/topic-title}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{topic-timeline
|
|
||||||
topic=model
|
<div class="container posts">
|
||||||
notificationLevel=model.details.notification_level
|
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
|
||||||
prevEvent=info.prevEvent
|
{{partial "selected-posts"}}
|
||||||
fullscreen=info.topicProgressExpanded
|
</div>
|
||||||
enteredIndex=enteredIndex
|
|
||||||
loading=model.postStream.loading
|
{{#topic-navigation topic=model jumpToDate=(action "jumpToDate") jumpToIndex=(action "jumpToIndex") as |info|}}
|
||||||
jumpToPost=(action "jumpToPost")
|
{{#if info.renderTimeline}}
|
||||||
jumpTop=(action "jumpTop")
|
|
||||||
jumpBottom=(action "jumpBottom")
|
|
||||||
jumpToPostPrompt=(action "jumpToPostPrompt")
|
|
||||||
jumpToIndex=(action "jumpToIndex")
|
|
||||||
replyToPost=(action "replyToPost")
|
|
||||||
toggleMultiSelect=(action "toggleMultiSelect")
|
|
||||||
hideMultiSelect=(action "hideMultiSelect")
|
|
||||||
deleteTopic=(action "deleteTopic")
|
|
||||||
recoverTopic=(action "recoverTopic")
|
|
||||||
toggleClosed=(action "toggleClosed")
|
|
||||||
toggleArchived=(action "toggleArchived")
|
|
||||||
toggleVisibility=(action "toggleVisibility")
|
|
||||||
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
|
||||||
showFeatureTopic=(route-action "showFeatureTopic")
|
|
||||||
showChangeTimestamp=(route-action "showChangeTimestamp")
|
|
||||||
resetBumpDate=(action "resetBumpDate")
|
|
||||||
convertToPublicTopic=(action "convertToPublicTopic")
|
|
||||||
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
|
||||||
{{else}}
|
|
||||||
{{#topic-progress
|
|
||||||
prevEvent=info.prevEvent
|
|
||||||
topic=model
|
|
||||||
expanded=info.topicProgressExpanded
|
|
||||||
jumpToPost=(action "jumpToPost")}}
|
|
||||||
{{#if info.renderAdminMenuButton}}
|
{{#if info.renderAdminMenuButton}}
|
||||||
{{topic-admin-menu-button
|
{{topic-admin-menu-button
|
||||||
topic=model
|
topic=model
|
||||||
openUpwards="true"
|
fixed="true"
|
||||||
rightSide="true"
|
|
||||||
toggleMultiSelect=(action "toggleMultiSelect")
|
toggleMultiSelect=(action "toggleMultiSelect")
|
||||||
hideMultiSelect=(action "hideMultiSelect")
|
hideMultiSelect=(action "hideMultiSelect")
|
||||||
deleteTopic=(action "deleteTopic")
|
deleteTopic=(action "deleteTopic")
|
||||||
|
@ -153,212 +104,265 @@
|
||||||
convertToPublicTopic=(action "convertToPublicTopic")
|
convertToPublicTopic=(action "convertToPublicTopic")
|
||||||
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/topic-progress}}
|
|
||||||
{{/if}}
|
|
||||||
{{/topic-navigation}}
|
|
||||||
|
|
||||||
<div class="row">
|
{{topic-timeline
|
||||||
<section class="topic-area" id="topic" data-topic-id="{{unbound model.id}}">
|
topic=model
|
||||||
|
notificationLevel=model.details.notification_level
|
||||||
|
prevEvent=info.prevEvent
|
||||||
|
fullscreen=info.topicProgressExpanded
|
||||||
|
enteredIndex=enteredIndex
|
||||||
|
loading=model.postStream.loading
|
||||||
|
jumpToPost=(action "jumpToPost")
|
||||||
|
jumpTop=(action "jumpTop")
|
||||||
|
jumpBottom=(action "jumpBottom")
|
||||||
|
jumpToPostPrompt=(action "jumpToPostPrompt")
|
||||||
|
jumpToIndex=(action "jumpToIndex")
|
||||||
|
replyToPost=(action "replyToPost")
|
||||||
|
toggleMultiSelect=(action "toggleMultiSelect")
|
||||||
|
hideMultiSelect=(action "hideMultiSelect")
|
||||||
|
deleteTopic=(action "deleteTopic")
|
||||||
|
recoverTopic=(action "recoverTopic")
|
||||||
|
toggleClosed=(action "toggleClosed")
|
||||||
|
toggleArchived=(action "toggleArchived")
|
||||||
|
toggleVisibility=(action "toggleVisibility")
|
||||||
|
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
||||||
|
showFeatureTopic=(route-action "showFeatureTopic")
|
||||||
|
showChangeTimestamp=(route-action "showChangeTimestamp")
|
||||||
|
resetBumpDate=(action "resetBumpDate")
|
||||||
|
convertToPublicTopic=(action "convertToPublicTopic")
|
||||||
|
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
||||||
|
{{else}}
|
||||||
|
{{#topic-progress
|
||||||
|
prevEvent=info.prevEvent
|
||||||
|
topic=model
|
||||||
|
expanded=info.topicProgressExpanded
|
||||||
|
jumpToPost=(action "jumpToPost")}}
|
||||||
|
{{#if info.renderAdminMenuButton}}
|
||||||
|
{{topic-admin-menu-button
|
||||||
|
topic=model
|
||||||
|
openUpwards="true"
|
||||||
|
rightSide="true"
|
||||||
|
toggleMultiSelect=(action "toggleMultiSelect")
|
||||||
|
hideMultiSelect=(action "hideMultiSelect")
|
||||||
|
deleteTopic=(action "deleteTopic")
|
||||||
|
recoverTopic=(action "recoverTopic")
|
||||||
|
toggleClosed=(action "toggleClosed")
|
||||||
|
toggleArchived=(action "toggleArchived")
|
||||||
|
toggleVisibility=(action "toggleVisibility")
|
||||||
|
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
||||||
|
showFeatureTopic=(route-action "showFeatureTopic")
|
||||||
|
showChangeTimestamp=(route-action "showChangeTimestamp")
|
||||||
|
resetBumpDate=(action "resetBumpDate")
|
||||||
|
convertToPublicTopic=(action "convertToPublicTopic")
|
||||||
|
convertToPrivateMessage=(action "convertToPrivateMessage")}}
|
||||||
|
{{/if}}
|
||||||
|
{{/topic-progress}}
|
||||||
|
{{/if}}
|
||||||
|
{{/topic-navigation}}
|
||||||
|
|
||||||
<div class="posts-wrapper">
|
<div class="row">
|
||||||
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
|
<section class="topic-area" id="topic" data-topic-id="{{unbound model.id}}">
|
||||||
|
|
||||||
{{plugin-outlet name="topic-above-posts" args=(hash model=model)}}
|
<div class="posts-wrapper">
|
||||||
|
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
|
||||||
|
|
||||||
{{#unless model.postStream.loadingFilter}}
|
{{plugin-outlet name="topic-above-posts" args=(hash model=model)}}
|
||||||
{{scrolling-post-stream
|
|
||||||
posts=postsToRender
|
|
||||||
canCreatePost=model.details.can_create_post
|
|
||||||
multiSelect=multiSelect
|
|
||||||
selectedPostsCount=selectedPostsCount
|
|
||||||
selectedQuery=selectedQuery
|
|
||||||
gaps=model.postStream.gaps
|
|
||||||
showFlags=(action "showPostFlags")
|
|
||||||
editPost=(action "editPost")
|
|
||||||
showHistory=(route-action "showHistory")
|
|
||||||
showLogin=(route-action "showLogin")
|
|
||||||
showRawEmail=(route-action "showRawEmail")
|
|
||||||
deletePost=(action "deletePost")
|
|
||||||
recoverPost=(action "recoverPost")
|
|
||||||
expandHidden=(action "expandHidden")
|
|
||||||
newTopicAction=(action "replyAsNewTopic")
|
|
||||||
toggleBookmark=(action "toggleBookmark")
|
|
||||||
togglePostType=(action "togglePostType")
|
|
||||||
rebakePost=(action "rebakePost")
|
|
||||||
changePostOwner=(action "changePostOwner")
|
|
||||||
grantBadge=(action "grantBadge")
|
|
||||||
addNotice=(action "addNotice")
|
|
||||||
removeNotice=(action "removeNotice")
|
|
||||||
lockPost=(action "lockPost")
|
|
||||||
unlockPost=(action "unlockPost")
|
|
||||||
unhidePost=(action "unhidePost")
|
|
||||||
replyToPost=(action "replyToPost")
|
|
||||||
toggleWiki=(action "toggleWiki")
|
|
||||||
toggleSummary=(action "toggleSummary")
|
|
||||||
removeAllowedUser=(action "removeAllowedUser")
|
|
||||||
removeAllowedGroup=(action "removeAllowedGroup")
|
|
||||||
topVisibleChanged=(action "topVisibleChanged")
|
|
||||||
currentPostChanged=(action "currentPostChanged")
|
|
||||||
currentPostScrolled=(action "currentPostScrolled")
|
|
||||||
bottomVisibleChanged=(action "bottomVisibleChanged")
|
|
||||||
togglePostSelection=(action "togglePostSelection")
|
|
||||||
selectReplies=(action "selectReplies")
|
|
||||||
selectBelow=(action "selectBelow")
|
|
||||||
fillGapBefore=(action "fillGapBefore")
|
|
||||||
fillGapAfter=(action "fillGapAfter")
|
|
||||||
showInvite=(route-action "showInvite")}}
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{conditional-loading-spinner condition=model.postStream.loadingBelow}}
|
{{#unless model.postStream.loadingFilter}}
|
||||||
</div>
|
{{scrolling-post-stream
|
||||||
<div id="topic-bottom"></div>
|
posts=postsToRender
|
||||||
|
canCreatePost=model.details.can_create_post
|
||||||
|
multiSelect=multiSelect
|
||||||
|
selectedPostsCount=selectedPostsCount
|
||||||
|
selectedQuery=selectedQuery
|
||||||
|
gaps=model.postStream.gaps
|
||||||
|
showFlags=(action "showPostFlags")
|
||||||
|
editPost=(action "editPost")
|
||||||
|
showHistory=(route-action "showHistory")
|
||||||
|
showLogin=(route-action "showLogin")
|
||||||
|
showRawEmail=(route-action "showRawEmail")
|
||||||
|
deletePost=(action "deletePost")
|
||||||
|
recoverPost=(action "recoverPost")
|
||||||
|
expandHidden=(action "expandHidden")
|
||||||
|
newTopicAction=(action "replyAsNewTopic")
|
||||||
|
toggleBookmark=(action "toggleBookmark")
|
||||||
|
togglePostType=(action "togglePostType")
|
||||||
|
rebakePost=(action "rebakePost")
|
||||||
|
changePostOwner=(action "changePostOwner")
|
||||||
|
grantBadge=(action "grantBadge")
|
||||||
|
addNotice=(action "addNotice")
|
||||||
|
removeNotice=(action "removeNotice")
|
||||||
|
lockPost=(action "lockPost")
|
||||||
|
unlockPost=(action "unlockPost")
|
||||||
|
unhidePost=(action "unhidePost")
|
||||||
|
replyToPost=(action "replyToPost")
|
||||||
|
toggleWiki=(action "toggleWiki")
|
||||||
|
toggleSummary=(action "toggleSummary")
|
||||||
|
removeAllowedUser=(action "removeAllowedUser")
|
||||||
|
removeAllowedGroup=(action "removeAllowedGroup")
|
||||||
|
topVisibleChanged=(action "topVisibleChanged")
|
||||||
|
currentPostChanged=(action "currentPostChanged")
|
||||||
|
currentPostScrolled=(action "currentPostScrolled")
|
||||||
|
bottomVisibleChanged=(action "bottomVisibleChanged")
|
||||||
|
togglePostSelection=(action "togglePostSelection")
|
||||||
|
selectReplies=(action "selectReplies")
|
||||||
|
selectBelow=(action "selectBelow")
|
||||||
|
fillGapBefore=(action "fillGapBefore")
|
||||||
|
fillGapAfter=(action "fillGapAfter")
|
||||||
|
showInvite=(route-action "showInvite")}}
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
{{#conditional-loading-spinner condition=model.postStream.loadingFilter}}
|
{{conditional-loading-spinner condition=model.postStream.loadingBelow}}
|
||||||
{{#if loadedAllPosts}}
|
</div>
|
||||||
|
<div id="topic-bottom"></div>
|
||||||
|
|
||||||
{{#if model.pending_posts}}
|
{{#conditional-loading-spinner condition=model.postStream.loadingFilter}}
|
||||||
<div class='pending-posts'>
|
{{#if loadedAllPosts}}
|
||||||
{{#each model.pending_posts as |pending|}}
|
|
||||||
<div class='reviewable-item'>
|
{{#if model.pending_posts}}
|
||||||
<div class='reviewable-meta-data'>
|
<div class='pending-posts'>
|
||||||
<span class='reviewable-type'>
|
{{#each model.pending_posts as |pending|}}
|
||||||
{{i18n "review.awaiting_approval"}}
|
<div class='reviewable-item'>
|
||||||
</span>
|
<div class='reviewable-meta-data'>
|
||||||
<span class='created-at'>
|
<span class='reviewable-type'>
|
||||||
{{age-with-tooltip pending.created_at}}
|
{{i18n "review.awaiting_approval"}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span class='created-at'>
|
||||||
<div class='post-contents-wrapper'>
|
{{age-with-tooltip pending.created_at}}
|
||||||
{{reviewable-created-by user=currentUser tagName=''}}
|
</span>
|
||||||
<div class='post-contents'>
|
</div>
|
||||||
{{reviewable-created-by-name user=currentUser tagName=''}}
|
<div class='post-contents-wrapper'>
|
||||||
<div class='post-body'>{{cook-text pending.raw}}</div>
|
{{reviewable-created-by user=currentUser tagName=''}}
|
||||||
|
<div class='post-contents'>
|
||||||
|
{{reviewable-created-by-name user=currentUser tagName=''}}
|
||||||
|
<div class='post-body'>{{cook-text pending.raw}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='reviewable-actions'>
|
||||||
|
{{d-button
|
||||||
|
class="btn-danger"
|
||||||
|
label="review.delete"
|
||||||
|
icon="trash-alt"
|
||||||
|
action=(action "deletePending" pending) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='reviewable-actions'>
|
{{/each}}
|
||||||
{{d-button
|
|
||||||
class="btn-danger"
|
|
||||||
label="review.delete"
|
|
||||||
icon="trash-alt"
|
|
||||||
action=(action "deletePending" pending) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if model.queued_posts_count}}
|
|
||||||
<div class="has-pending-posts">
|
|
||||||
<div>
|
|
||||||
{{{i18n "review.topic_has_pending" count=model.queued_posts_count}}}
|
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#link-to 'review' (query-params topic_id=model.id type="ReviewableQueuedPost" status="pending")}}
|
{{#if model.queued_posts_count}}
|
||||||
{{i18n "review.view_pending"}}
|
<div class="has-pending-posts">
|
||||||
{{/link-to}}
|
<div>
|
||||||
</div>
|
{{{i18n "review.topic_has_pending" count=model.queued_posts_count}}}
|
||||||
{{/if}}
|
</div>
|
||||||
|
|
||||||
|
{{#link-to 'review' (query-params topic_id=model.id type="ReviewableQueuedPost" status="pending")}}
|
||||||
|
{{i18n "review.view_pending"}}
|
||||||
|
{{/link-to}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if model.private_topic_timer.execute_at}}
|
||||||
|
{{topic-timer-info
|
||||||
|
topicClosed=model.closed
|
||||||
|
statusType=model.private_topic_timer.status_type
|
||||||
|
executeAt=model.private_topic_timer.execute_at
|
||||||
|
duration=model.private_topic_timer.duration
|
||||||
|
removeTopicTimer=(action "removeTopicTimer" model.private_topic_timer.status_type "private_topic_timer")}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if model.private_topic_timer.execute_at}}
|
|
||||||
{{topic-timer-info
|
{{topic-timer-info
|
||||||
topicClosed=model.closed
|
topicClosed=model.closed
|
||||||
statusType=model.private_topic_timer.status_type
|
statusType=model.topic_timer.status_type
|
||||||
executeAt=model.private_topic_timer.execute_at
|
executeAt=model.topic_timer.execute_at
|
||||||
duration=model.private_topic_timer.duration
|
basedOnLastPost=model.topic_timer.based_on_last_post
|
||||||
removeTopicTimer=(action "removeTopicTimer" model.private_topic_timer.status_type "private_topic_timer")}}
|
duration=model.topic_timer.duration
|
||||||
{{/if}}
|
categoryId=model.topic_timer.category_id
|
||||||
|
removeTopicTimer=(action "removeTopicTimer" model.topic_timer.status_type "topic_timer")}}
|
||||||
|
|
||||||
{{topic-timer-info
|
{{#if session.showSignupCta}}
|
||||||
topicClosed=model.closed
|
{{! replace "Log In to Reply" with the infobox }}
|
||||||
statusType=model.topic_timer.status_type
|
{{signup-cta}}
|
||||||
executeAt=model.topic_timer.execute_at
|
|
||||||
basedOnLastPost=model.topic_timer.based_on_last_post
|
|
||||||
duration=model.topic_timer.duration
|
|
||||||
categoryId=model.topic_timer.category_id
|
|
||||||
removeTopicTimer=(action "removeTopicTimer" model.topic_timer.status_type "topic_timer")}}
|
|
||||||
|
|
||||||
{{#if session.showSignupCta}}
|
|
||||||
{{! replace "Log In to Reply" with the infobox }}
|
|
||||||
{{signup-cta}}
|
|
||||||
{{else}}
|
|
||||||
{{#if currentUser}}
|
|
||||||
{{plugin-outlet name="topic-above-footer-buttons" args=(hash model=model)}}
|
|
||||||
|
|
||||||
{{topic-footer-buttons
|
|
||||||
topic=model
|
|
||||||
toggleMultiSelect=(action "toggleMultiSelect")
|
|
||||||
hideMultiSelect=(action "hideMultiSelect")
|
|
||||||
deleteTopic=(action "deleteTopic")
|
|
||||||
recoverTopic=(action "recoverTopic")
|
|
||||||
toggleClosed=(action "toggleClosed")
|
|
||||||
toggleArchived=(action "toggleArchived")
|
|
||||||
toggleVisibility=(action "toggleVisibility")
|
|
||||||
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
|
||||||
showFeatureTopic=(route-action "showFeatureTopic")
|
|
||||||
showChangeTimestamp=(route-action "showChangeTimestamp")
|
|
||||||
resetBumpDate=(action "resetBumpDate")
|
|
||||||
convertToPublicTopic=(action "convertToPublicTopic")
|
|
||||||
convertToPrivateMessage=(action "convertToPrivateMessage")
|
|
||||||
toggleBookmark=(action "toggleBookmark")
|
|
||||||
showFlagTopic=(route-action "showFlagTopic")
|
|
||||||
toggleArchiveMessage=(action "toggleArchiveMessage")
|
|
||||||
editFirstPost=(action "editFirstPost")
|
|
||||||
deferTopic=(action "deferTopic")
|
|
||||||
replyToPost=(action "replyToPost")}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div id="topic-footer-buttons">
|
{{#if currentUser}}
|
||||||
{{d-button icon="reply" class="btn-primary pull-right" action=(route-action "showLogin") label="topic.reply.title"}}
|
{{plugin-outlet name="topic-above-footer-buttons" args=(hash model=model)}}
|
||||||
|
|
||||||
|
{{topic-footer-buttons
|
||||||
|
topic=model
|
||||||
|
toggleMultiSelect=(action "toggleMultiSelect")
|
||||||
|
hideMultiSelect=(action "hideMultiSelect")
|
||||||
|
deleteTopic=(action "deleteTopic")
|
||||||
|
recoverTopic=(action "recoverTopic")
|
||||||
|
toggleClosed=(action "toggleClosed")
|
||||||
|
toggleArchived=(action "toggleArchived")
|
||||||
|
toggleVisibility=(action "toggleVisibility")
|
||||||
|
showTopicStatusUpdate=(route-action "showTopicStatusUpdate")
|
||||||
|
showFeatureTopic=(route-action "showFeatureTopic")
|
||||||
|
showChangeTimestamp=(route-action "showChangeTimestamp")
|
||||||
|
resetBumpDate=(action "resetBumpDate")
|
||||||
|
convertToPublicTopic=(action "convertToPublicTopic")
|
||||||
|
convertToPrivateMessage=(action "convertToPrivateMessage")
|
||||||
|
toggleBookmark=(action "toggleBookmark")
|
||||||
|
showFlagTopic=(route-action "showFlagTopic")
|
||||||
|
toggleArchiveMessage=(action "toggleArchiveMessage")
|
||||||
|
editFirstPost=(action "editFirstPost")
|
||||||
|
deferTopic=(action "deferTopic")
|
||||||
|
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"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showSelectedPostsAtBottom}}
|
||||||
|
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
|
||||||
|
{{partial "selected-posts"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if showSelectedPostsAtBottom}}
|
{{plugin-outlet name="topic-above-suggested" args=(hash model=model)}}
|
||||||
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
|
<div class="{{if model.relatedMessages.length 'related-messages-wrapper'}} {{if model.suggestedTopics.length 'suggested-topics-wrapper'}}">
|
||||||
{{partial "selected-posts"}}
|
{{#if model.relatedMessages.length}}
|
||||||
|
{{related-messages topic=model}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if model.suggestedTopics.length}}
|
||||||
|
{{suggested-topics topic=model}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{/conditional-loading-spinner}}
|
||||||
|
|
||||||
{{plugin-outlet name="topic-above-suggested" args=(hash model=model)}}
|
</section>
|
||||||
<div class="{{if model.relatedMessages.length 'related-messages-wrapper'}} {{if model.suggestedTopics.length 'suggested-topics-wrapper'}}">
|
</div>
|
||||||
{{#if model.relatedMessages.length}}
|
|
||||||
{{related-messages topic=model}}
|
|
||||||
{{/if}}
|
|
||||||
{{#if model.suggestedTopics.length}}
|
|
||||||
{{suggested-topics topic=model}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/conditional-loading-spinner}}
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="container">
|
||||||
|
{{#conditional-loading-spinner condition=noErrorYet}}
|
||||||
|
{{#if model.notFoundHtml}}
|
||||||
|
<div class="not-found">{{{model.notFoundHtml}}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="topic-error">
|
||||||
|
<div>{{model.message}}</div>
|
||||||
|
{{#if model.noRetry}}
|
||||||
|
{{#unless currentUser}}
|
||||||
|
{{d-button action=(route-action "showLogin") class="btn-primary topic-retry" icon="user" label="log_in"}}
|
||||||
|
{{/unless}}
|
||||||
|
{{else}}
|
||||||
|
{{d-button action=(action "retryLoading") class="btn-primary topic-retry" icon="sync" label="errors.buttons.again"}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{conditional-loading-spinner condition=retrying}}
|
||||||
|
{{/if}}
|
||||||
|
{{/conditional-loading-spinner}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</div>
|
{{share-popup topic=model replyAsNewTopic=(action "replyAsNewTopic")}}
|
||||||
{{else}}
|
|
||||||
<div class="container">
|
|
||||||
{{#conditional-loading-spinner condition=noErrorYet}}
|
|
||||||
{{#if model.notFoundHtml}}
|
|
||||||
<div class="not-found">{{{model.notFoundHtml}}}</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="topic-error">
|
|
||||||
<div>{{model.message}}</div>
|
|
||||||
{{#if model.noRetry}}
|
|
||||||
{{#unless currentUser}}
|
|
||||||
{{d-button action=(route-action "showLogin") class="btn-primary topic-retry" icon="user" label="log_in"}}
|
|
||||||
{{/unless}}
|
|
||||||
{{else}}
|
|
||||||
{{d-button action=(action "retryLoading") class="btn-primary topic-retry" icon="sync" label="errors.buttons.again"}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{conditional-loading-spinner condition=retrying}}
|
|
||||||
{{/if}}
|
|
||||||
{{/conditional-loading-spinner}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{share-popup topic=model replyAsNewTopic=(action "replyAsNewTopic")}}
|
{{#if embedQuoteButton}}
|
||||||
|
{{quote-button quoteState=quoteState selectText=(action "selectText")}}
|
||||||
{{#if embedQuoteButton}}
|
{{/if}}
|
||||||
{{quote-button quoteState=quoteState selectText=(action "selectText")}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/discourse-topic}}
|
{{/discourse-topic}}
|
||||||
|
|
|
@ -52,7 +52,8 @@
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-error {
|
.topic-error,
|
||||||
|
.topic-notice {
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -61,7 +62,8 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: $line-height-medium;
|
line-height: $line-height-medium;
|
||||||
|
|
||||||
.topic-retry {
|
.topic-retry,
|
||||||
|
.topic-join-group {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
|
@ -147,7 +147,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic-error {
|
.topic-error,
|
||||||
|
.topic-notice {
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
@ -155,7 +156,8 @@
|
||||||
font-size: $font-up-4;
|
font-size: $font-up-4;
|
||||||
line-height: $line-height-medium;
|
line-height: $line-height-medium;
|
||||||
|
|
||||||
.topic-retry {
|
.topic-retry,
|
||||||
|
.topic-join-group {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
|
|
@ -420,12 +420,18 @@ class GroupsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_membership
|
def request_membership
|
||||||
params.require(:reason)
|
params.require(:reason) if params[:topic_id].blank?
|
||||||
|
|
||||||
group = find_group(:id)
|
group = find_group(:id)
|
||||||
|
|
||||||
|
if params[:topic_id] && topic = Topic.find_by_id(params[:topic_id])
|
||||||
|
reason = I18n.t("groups.view_hidden_topic_request_reason", group_name: group.name, topic_url: topic.url)
|
||||||
|
end
|
||||||
|
|
||||||
|
reason ||= params[:reason]
|
||||||
|
|
||||||
begin
|
begin
|
||||||
GroupRequest.create!(group: group, user: current_user, reason: params[:reason])
|
GroupRequest.create!(group: group, user: current_user, reason: reason)
|
||||||
rescue ActiveRecord::RecordNotUnique => e
|
rescue ActiveRecord::RecordNotUnique => e
|
||||||
return render json: failed_json.merge(error: I18n.t("groups.errors.already_requested_membership")), status: 409
|
return render json: failed_json.merge(error: I18n.t("groups.errors.already_requested_membership")), status: 409
|
||||||
end
|
end
|
||||||
|
@ -438,7 +444,7 @@ class GroupsController < ApplicationController
|
||||||
)
|
)
|
||||||
|
|
||||||
raw = <<~EOF
|
raw = <<~EOF
|
||||||
#{params[:reason]}
|
#{reason}
|
||||||
|
|
||||||
---
|
---
|
||||||
<a href="#{Discourse.base_uri}/g/#{group.name}/requests">
|
<a href="#{Discourse.base_uri}/g/#{group.name}/requests">
|
||||||
|
|
|
@ -131,6 +131,9 @@ class TopicsController < ApplicationController
|
||||||
perform_show_response
|
perform_show_response
|
||||||
|
|
||||||
rescue Discourse::InvalidAccess => ex
|
rescue Discourse::InvalidAccess => ex
|
||||||
|
if !guardian.can_see_topic?(ex.obj) && guardian.can_get_access_to_topic?(ex.obj)
|
||||||
|
return perform_hidden_topic_show_response(ex.obj)
|
||||||
|
end
|
||||||
|
|
||||||
if current_user
|
if current_user
|
||||||
# If the user can't see the topic, clean up notifications for it.
|
# If the user can't see the topic, clean up notifications for it.
|
||||||
|
@ -950,6 +953,19 @@ class TopicsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform_hidden_topic_show_response(topic)
|
||||||
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
@topic_view = nil
|
||||||
|
render :show
|
||||||
|
end
|
||||||
|
|
||||||
|
format.json do
|
||||||
|
render_serialized(topic, HiddenTopicViewSerializer, root: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def render_topic_changes(dest_topic)
|
def render_topic_changes(dest_topic)
|
||||||
if dest_topic.present?
|
if dest_topic.present?
|
||||||
render json: { success: true, url: dest_topic.relative_url }
|
render json: { success: true, url: dest_topic.relative_url }
|
||||||
|
|
|
@ -1384,6 +1384,15 @@ class Topic < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def access_topic_via_group
|
||||||
|
Group
|
||||||
|
.joins(:category_groups)
|
||||||
|
.where("category_groups.category_id = ?", self.category_id)
|
||||||
|
.where("groups.public_admission OR groups.allow_membership_requests")
|
||||||
|
.order(:allow_membership_requests)
|
||||||
|
.first
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def invite_to_private_message(invited_by, target_user, guardian)
|
def invite_to_private_message(invited_by, target_user, guardian)
|
||||||
|
|
15
app/serializers/hidden_topic_view_serializer.rb
Normal file
15
app/serializers/hidden_topic_view_serializer.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class HiddenTopicViewSerializer < ApplicationSerializer
|
||||||
|
attributes :view_hidden?
|
||||||
|
|
||||||
|
has_one :group, serializer: BasicGroupSerializer, root: false, embed: :objects
|
||||||
|
|
||||||
|
def view_hidden?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def group
|
||||||
|
object.access_topic_via_group
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,141 +1,143 @@
|
||||||
<h1 class="crawler-topic-title">
|
<% if @topic_view %>
|
||||||
<%= render_topic_title(@topic_view.topic) %>
|
<h1 class="crawler-topic-title">
|
||||||
</h1>
|
<%= render_topic_title(@topic_view.topic) %>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<% @breadcrumbs = categories_breadcrumb(@topic_view.topic)
|
<% @breadcrumbs = categories_breadcrumb(@topic_view.topic)
|
||||||
if @breadcrumbs.present? %>
|
if @breadcrumbs.present? %>
|
||||||
<div id='breadcrumbs'>
|
<div id='breadcrumbs'>
|
||||||
<% @breadcrumbs.each_with_index do |c,i| %>
|
<% @breadcrumbs.each_with_index do |c,i| %>
|
||||||
<div id="breadcrumb-<%=i%>" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
|
<div id="breadcrumb-<%=i%>" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
|
||||||
<%-if (i+1) < @breadcrumbs.length%>
|
<%-if (i+1) < @breadcrumbs.length%>
|
||||||
itemref="breadcrumb-<%=(i+1)%>"
|
itemref="breadcrumb-<%=(i+1)%>"
|
||||||
<%-end%>>
|
<%-end%>>
|
||||||
<a href="<%=c[:url]%>" itemprop="url" class='badge-wrapper bullet'>
|
<a href="<%=c[:url]%>" itemprop="url" class='badge-wrapper bullet'>
|
||||||
<span class="badge-category-bg"></span>
|
<span class="badge-category-bg"></span>
|
||||||
<span itemprop="title" class='category-title'><%=c[:name]%></span>
|
<span itemprop="title" class='category-title'><%=c[:name]%></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% if SiteSetting.tagging_enabled %>
|
<% if SiteSetting.tagging_enabled %>
|
||||||
<% @tags = @topic_view.topic.tags %>
|
<% @tags = @topic_view.topic.tags %>
|
||||||
<% if @tags.present? %>
|
<% if @tags.present? %>
|
||||||
<div class='crawler-tags-list' itemscope itemtype='http://schema.org/DiscussionForumPosting'>
|
<div class='crawler-tags-list' itemscope itemtype='http://schema.org/DiscussionForumPosting'>
|
||||||
<% @tags.each_with_index do |tag, i| %>
|
<% @tags.each_with_index do |tag, i| %>
|
||||||
<div itemprop='keywords'>
|
<div itemprop='keywords'>
|
||||||
<a href='<%= "#{Discourse.base_url}/tags/#{tag.name}" %>' rel="tag">
|
<a href='<%= "#{Discourse.base_url}/tags/#{tag.name}" %>' rel="tag">
|
||||||
<span itemprop='headline'><%= tag.name -%></span>
|
<span itemprop='headline'><%= tag.name -%></span>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= server_plugin_outlet "topic_header" %>
|
||||||
|
|
||||||
|
<%- if include_crawler_content? %>
|
||||||
|
|
||||||
|
<% @topic_view.posts.each do |post| %>
|
||||||
|
<div itemscope itemtype='http://schema.org/DiscussionForumPosting' class='topic-body crawler-post'>
|
||||||
|
<% if (u = post.user) %>
|
||||||
|
<div class='crawler-post-meta'>
|
||||||
|
<span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person">
|
||||||
|
<a itemprop="url" href='<%= Discourse.base_uri %>/u/<%= u.username %>'><span itemprop='name'><%= u.username %></span></a>
|
||||||
|
<%= "(#{u.name})" if (SiteSetting.display_name_on_posts && SiteSetting.enable_names? && !u.name.blank?) %>
|
||||||
|
<%
|
||||||
|
post_custom_fields = @topic_view.post_custom_fields[post.id] || {}
|
||||||
|
who_username = post_custom_fields["action_code_who"] || ""
|
||||||
|
if post.action_code
|
||||||
|
%>
|
||||||
|
<%= t("js.action_codes.#{post.action_code}", when: "", who: who_username).html_safe %>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="crawler-post-infos">
|
||||||
|
<% if post.updated_at > post.created_at %>
|
||||||
|
<meta itemprop='datePublished' content='<%= post.created_at.to_formatted_s(:iso8601) %>'>
|
||||||
|
<time itemprop='dateModified' datetime='<%= post.updated_at.to_formatted_s(:iso8601) %>' class='post-time'>
|
||||||
|
<%= l post.updated_at, format: :long %>
|
||||||
|
</time>
|
||||||
|
<% else %>
|
||||||
|
<time itemprop='datePublished' datetime='<%= post.created_at.to_formatted_s(:iso8601) %>' class='post-time'>
|
||||||
|
<%= l post.created_at, format: :long %>
|
||||||
|
</time>
|
||||||
|
<% end %>
|
||||||
|
<span itemprop='position'>#<%= post.post_number %></span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class='post' itemprop='articleBody'>
|
||||||
|
<%= post.hidden ? t('flagging.user_must_edit').html_safe : post.cooked.html_safe %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<meta itemprop='headline' content='<%= @topic_view.title %>'>
|
||||||
|
|
||||||
|
<div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter">
|
||||||
|
<meta itemprop="interactionType" content="http://schema.org/LikeAction"/>
|
||||||
|
<meta itemprop="userInteractionCount" content="<%= post.like_count %>" />
|
||||||
|
<span class='post-likes'><%= post.like_count > 0 ? t('post.has_likes', count: post.like_count) : '' %></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter">
|
||||||
|
<meta itemprop="interactionType" content="http://schema.org/CommentAction"/>
|
||||||
|
<meta itemprop="userInteractionCount" content="<%= post.reply_count %>" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if @topic_view.link_counts[post.id] && @topic_view.link_counts[post.id].length > 0 %>
|
||||||
|
<div class='crawler-linkback-list' itemscope itemtype='http://schema.org/ItemList'>
|
||||||
|
<% @topic_view.link_counts[post.id].each_with_index do |link, i| %>
|
||||||
|
<% if link[:reflection] && link[:title].present? %>
|
||||||
|
<div itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'>
|
||||||
|
<a href="<%=link[:url]%>" itemprop='item'>
|
||||||
|
<meta itemprop='url' content='<%=link[:url]%>'>
|
||||||
|
<meta itemprop='position' content='<%= i+1 %>'>
|
||||||
|
<span itemprop='name'><%=link[:title]%></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= server_plugin_outlet "topic_header" %>
|
|
||||||
|
|
||||||
<%- if include_crawler_content? %>
|
|
||||||
|
|
||||||
<% @topic_view.posts.each do |post| %>
|
|
||||||
<div itemscope itemtype='http://schema.org/DiscussionForumPosting' class='topic-body crawler-post'>
|
|
||||||
<% if (u = post.user) %>
|
|
||||||
<div class='crawler-post-meta'>
|
|
||||||
<span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person">
|
|
||||||
<a itemprop="url" href='<%= Discourse.base_uri %>/u/<%= u.username %>'><span itemprop='name'><%= u.username %></span></a>
|
|
||||||
<%= "(#{u.name})" if (SiteSetting.display_name_on_posts && SiteSetting.enable_names? && !u.name.blank?) %>
|
|
||||||
<%
|
|
||||||
post_custom_fields = @topic_view.post_custom_fields[post.id] || {}
|
|
||||||
who_username = post_custom_fields["action_code_who"] || ""
|
|
||||||
if post.action_code
|
|
||||||
%>
|
|
||||||
<%= t("js.action_codes.#{post.action_code}", when: "", who: who_username).html_safe %>
|
|
||||||
<% end %>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="crawler-post-infos">
|
|
||||||
<% if post.updated_at > post.created_at %>
|
|
||||||
<meta itemprop='datePublished' content='<%= post.created_at.to_formatted_s(:iso8601) %>'>
|
|
||||||
<time itemprop='dateModified' datetime='<%= post.updated_at.to_formatted_s(:iso8601) %>' class='post-time'>
|
|
||||||
<%= l post.updated_at, format: :long %>
|
|
||||||
</time>
|
|
||||||
<% else %>
|
|
||||||
<time itemprop='datePublished' datetime='<%= post.created_at.to_formatted_s(:iso8601) %>' class='post-time'>
|
|
||||||
<%= l post.created_at, format: :long %>
|
|
||||||
</time>
|
|
||||||
<% end %>
|
|
||||||
<span itemprop='position'>#<%= post.post_number %></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class='post' itemprop='articleBody'>
|
|
||||||
<%= post.hidden ? t('flagging.user_must_edit').html_safe : post.cooked.html_safe %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<meta itemprop='headline' content='<%= @topic_view.title %>'>
|
|
||||||
|
|
||||||
<div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter">
|
|
||||||
<meta itemprop="interactionType" content="http://schema.org/LikeAction"/>
|
|
||||||
<meta itemprop="userInteractionCount" content="<%= post.like_count %>" />
|
|
||||||
<span class='post-likes'><%= post.like_count > 0 ? t('post.has_likes', count: post.like_count) : '' %></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter">
|
|
||||||
<meta itemprop="interactionType" content="http://schema.org/CommentAction"/>
|
|
||||||
<meta itemprop="userInteractionCount" content="<%= post.reply_count %>" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% if @topic_view.link_counts[post.id] && @topic_view.link_counts[post.id].length > 0 %>
|
|
||||||
<div class='crawler-linkback-list' itemscope itemtype='http://schema.org/ItemList'>
|
|
||||||
<% @topic_view.link_counts[post.id].each_with_index do |link, i| %>
|
|
||||||
<% if link[:reflection] && link[:title].present? %>
|
|
||||||
<div itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'>
|
|
||||||
<a href="<%=link[:url]%>" itemprop='item'>
|
|
||||||
<meta itemprop='url' content='<%=link[:url]%>'>
|
|
||||||
<meta itemprop='position' content='<%= i+1 %>'>
|
|
||||||
<span itemprop='name'><%=link[:title]%></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% if @topic_view.prev_page || @topic_view.next_page %>
|
|
||||||
<div role='navigation' itemscope itemtype='http://schema.org/SiteNavigationElement' class="topic-body crawler-post">
|
|
||||||
<% if @topic_view.prev_page %>
|
|
||||||
<span itemprop='name'><%= link_to t(:prev_page), @topic_view.prev_page_path, rel: 'prev', itemprop: 'url' %></span>
|
|
||||||
<% end %>
|
|
||||||
<% if @topic_view.next_page %>
|
|
||||||
<span itemprop='name'><b><%= link_to t(:next_page), @topic_view.next_page_path, rel: 'next', itemprop: 'url' %></b></span>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% content_for :head do %>
|
|
||||||
<%= auto_discovery_link_tag(@topic_view, {action: :feed, slug: @topic_view.topic.slug, topic_id: @topic_view.topic.id}, title: t('rss_posts_in_topic', topic: @topic_view.title), type: 'application/rss+xml') %>
|
|
||||||
<%= raw crawlable_meta_data(title: @topic_view.title, description: @topic_view.summary(strip_images: true), image: @topic_view.image_url, read_time: @topic_view.read_time, like_count: @topic_view.like_count, ignore_canonical: true, published_time: @topic_view.published_time) %>
|
|
||||||
|
|
||||||
<% if @topic_view.prev_page || @topic_view.next_page %>
|
<% if @topic_view.prev_page || @topic_view.next_page %>
|
||||||
<% if @topic_view.prev_page %>
|
<div role='navigation' itemscope itemtype='http://schema.org/SiteNavigationElement' class="topic-body crawler-post">
|
||||||
<link rel="prev" href="<%= @topic_view.prev_page_path -%>">
|
<% if @topic_view.prev_page %>
|
||||||
|
<span itemprop='name'><%= link_to t(:prev_page), @topic_view.prev_page_path, rel: 'prev', itemprop: 'url' %></span>
|
||||||
|
<% end %>
|
||||||
|
<% if @topic_view.next_page %>
|
||||||
|
<span itemprop='name'><b><%= link_to t(:next_page), @topic_view.next_page_path, rel: 'next', itemprop: 'url' %></b></span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% content_for :head do %>
|
||||||
|
<%= auto_discovery_link_tag(@topic_view, {action: :feed, slug: @topic_view.topic.slug, topic_id: @topic_view.topic.id}, title: t('rss_posts_in_topic', topic: @topic_view.title), type: 'application/rss+xml') %>
|
||||||
|
<%= raw crawlable_meta_data(title: @topic_view.title, description: @topic_view.summary(strip_images: true), image: @topic_view.image_url, read_time: @topic_view.read_time, like_count: @topic_view.like_count, ignore_canonical: true, published_time: @topic_view.published_time) %>
|
||||||
|
|
||||||
|
<% if @topic_view.prev_page || @topic_view.next_page %>
|
||||||
|
<% if @topic_view.prev_page %>
|
||||||
|
<link rel="prev" href="<%= @topic_view.prev_page_path -%>">
|
||||||
|
<% end %>
|
||||||
|
<% if @topic_view.next_page %>
|
||||||
|
<link rel="next" href="<%= @topic_view.next_page_path -%>">
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if @topic_view.next_page %>
|
<% end %>
|
||||||
<link rel="next" href="<%= @topic_view.next_page_path -%>">
|
|
||||||
|
<% content_for(:title) { @title || "#{gsub_emoji_to_unicode(@topic_view.page_title)} - #{SiteSetting.title}" } %>
|
||||||
|
|
||||||
|
<% if @topic_view.print %>
|
||||||
|
<% content_for :after_body do %>
|
||||||
|
<%= preload_script('print-page') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% content_for(:title) { @title || "#{gsub_emoji_to_unicode(@topic_view.page_title)} - #{SiteSetting.title}" } %>
|
|
||||||
|
|
||||||
<% if @topic_view.print %>
|
|
||||||
<% content_for :after_body do %>
|
|
||||||
<%= preload_script('print-page') %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|
|
@ -1967,6 +1967,9 @@ en:
|
||||||
toggle_information: "toggle topic details"
|
toggle_information: "toggle topic details"
|
||||||
read_more_in_category: "Want to read more? Browse other topics in {{catLink}} or {{latestLink}}."
|
read_more_in_category: "Want to read more? Browse other topics in {{catLink}} or {{latestLink}}."
|
||||||
read_more: "Want to read more? {{catLink}} or {{latestLink}}."
|
read_more: "Want to read more? {{catLink}} or {{latestLink}}."
|
||||||
|
group_request: "You need to request membership to the `{{name}}` group to see this topic"
|
||||||
|
group_join: "You need join the `{{name}}` group to see this topic"
|
||||||
|
group_request_sent: "Your group membership request has been sent. You will be informed when it's accepted."
|
||||||
|
|
||||||
# keys ending with _MF use message format, see https://meta.discourse.org/t/message-format-support-for-localization/7035 for details
|
# keys ending with _MF use message format, see https://meta.discourse.org/t/message-format-support-for-localization/7035 for details
|
||||||
read_more_MF: "There {
|
read_more_MF: "There {
|
||||||
|
|
|
@ -381,6 +381,7 @@ en:
|
||||||
request_membership_pm:
|
request_membership_pm:
|
||||||
title: "Membership Request for @%{group_name}"
|
title: "Membership Request for @%{group_name}"
|
||||||
handle: "handle membership request"
|
handle: "handle membership request"
|
||||||
|
view_hidden_topic_request_reason: "I would like to join the group '%{group_name}', so I may access [this topic](%{topic_url})"
|
||||||
|
|
||||||
education:
|
education:
|
||||||
until_posts:
|
until_posts:
|
||||||
|
|
|
@ -159,6 +159,10 @@ module TopicGuardian
|
||||||
can_see_topic?(topic, false)
|
can_see_topic?(topic, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_get_access_to_topic?(topic)
|
||||||
|
topic&.access_topic_via_group.present? && authenticated?
|
||||||
|
end
|
||||||
|
|
||||||
def filter_allowed_categories(records)
|
def filter_allowed_categories(records)
|
||||||
unless is_admin?
|
unless is_admin?
|
||||||
allowed_ids = allowed_category_ids
|
allowed_ids = allowed_category_ids
|
||||||
|
|
|
@ -2414,4 +2414,31 @@ describe Topic do
|
||||||
expect { topic.reset_bumped_at }.not_to change { topic.bumped_at }
|
expect { topic.reset_bumped_at }.not_to change { topic.bumped_at }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#access_topic_via_group" do
|
||||||
|
let(:open_group) { Fabricate(:group, public_admission: true) }
|
||||||
|
let(:request_group) do
|
||||||
|
Fabricate(:group).tap do |g|
|
||||||
|
g.add_owner(user)
|
||||||
|
g.allow_membership_requests = true
|
||||||
|
g.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
let(:category) { Fabricate(:category) }
|
||||||
|
let(:topic) { Fabricate(:topic, category: category) }
|
||||||
|
|
||||||
|
it "returns a group that is open or accepts membership requests and has access to the topic" do
|
||||||
|
expect(topic.access_topic_via_group).to eq(nil)
|
||||||
|
|
||||||
|
category.set_permissions(request_group => :full)
|
||||||
|
category.save!
|
||||||
|
|
||||||
|
expect(topic.access_topic_via_group).to eq(request_group)
|
||||||
|
|
||||||
|
category.set_permissions(request_group => :full, open_group => :full)
|
||||||
|
category.save!
|
||||||
|
|
||||||
|
expect(topic.access_topic_via_group).to eq(open_group)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1279,6 +1279,7 @@ RSpec.describe TopicsController do
|
||||||
context 'permission errors' do
|
context 'permission errors' do
|
||||||
fab!(:allowed_user) { Fabricate(:user) }
|
fab!(:allowed_user) { Fabricate(:user) }
|
||||||
let(:allowed_group) { Fabricate(:group) }
|
let(:allowed_group) { Fabricate(:group) }
|
||||||
|
let(:accessible_group) { Fabricate(:group, public_admission: true) }
|
||||||
let(:secure_category) do
|
let(:secure_category) do
|
||||||
c = Fabricate(:category)
|
c = Fabricate(:category)
|
||||||
c.permissions = [[allowed_group, :full]]
|
c.permissions = [[allowed_group, :full]]
|
||||||
|
@ -1287,6 +1288,12 @@ RSpec.describe TopicsController do
|
||||||
allowed_user.save
|
allowed_user.save
|
||||||
c
|
c
|
||||||
end
|
end
|
||||||
|
let(:accessible_category) do
|
||||||
|
Fabricate(:category).tap do |c|
|
||||||
|
c.set_permissions(accessible_group => :full)
|
||||||
|
c.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
let(:normal_topic) { Fabricate(:topic) }
|
let(:normal_topic) { Fabricate(:topic) }
|
||||||
let(:secure_topic) { Fabricate(:topic, category: secure_category) }
|
let(:secure_topic) { Fabricate(:topic, category: secure_category) }
|
||||||
let(:private_topic) { Fabricate(:private_message_topic, user: allowed_user) }
|
let(:private_topic) { Fabricate(:private_message_topic, user: allowed_user) }
|
||||||
|
@ -1294,6 +1301,7 @@ RSpec.describe TopicsController do
|
||||||
let(:deleted_secure_topic) { Fabricate(:topic, category: secure_category, deleted_at: 1.day.ago) }
|
let(:deleted_secure_topic) { Fabricate(:topic, category: secure_category, deleted_at: 1.day.ago) }
|
||||||
let(:deleted_private_topic) { Fabricate(:private_message_topic, user: allowed_user, deleted_at: 1.day.ago) }
|
let(:deleted_private_topic) { Fabricate(:private_message_topic, user: allowed_user, deleted_at: 1.day.ago) }
|
||||||
let(:nonexist_topic_id) { Topic.last.id + 10000 }
|
let(:nonexist_topic_id) { Topic.last.id + 10000 }
|
||||||
|
let(:secure_accessible_topic) { Fabricate(:topic, category: accessible_category) }
|
||||||
|
|
||||||
shared_examples "various scenarios" do |expected|
|
shared_examples "various scenarios" do |expected|
|
||||||
expected.each do |key, value|
|
expected.each do |key, value|
|
||||||
|
@ -1314,7 +1322,8 @@ RSpec.describe TopicsController do
|
||||||
deleted_topic: 410,
|
deleted_topic: 410,
|
||||||
deleted_secure_topic: 403,
|
deleted_secure_topic: 403,
|
||||||
deleted_private_topic: 403,
|
deleted_private_topic: 403,
|
||||||
nonexist: 404
|
nonexist: 404,
|
||||||
|
secure_accessible_topic: 403
|
||||||
}
|
}
|
||||||
include_examples "various scenarios", expected
|
include_examples "various scenarios", expected
|
||||||
end
|
end
|
||||||
|
@ -1330,7 +1339,8 @@ RSpec.describe TopicsController do
|
||||||
deleted_topic: 302,
|
deleted_topic: 302,
|
||||||
deleted_secure_topic: 302,
|
deleted_secure_topic: 302,
|
||||||
deleted_private_topic: 302,
|
deleted_private_topic: 302,
|
||||||
nonexist: 302
|
nonexist: 302,
|
||||||
|
secure_accessible_topic: 302
|
||||||
}
|
}
|
||||||
include_examples "various scenarios", expected
|
include_examples "various scenarios", expected
|
||||||
end
|
end
|
||||||
|
@ -1347,7 +1357,8 @@ RSpec.describe TopicsController do
|
||||||
deleted_topic: 410,
|
deleted_topic: 410,
|
||||||
deleted_secure_topic: 403,
|
deleted_secure_topic: 403,
|
||||||
deleted_private_topic: 403,
|
deleted_private_topic: 403,
|
||||||
nonexist: 404
|
nonexist: 404,
|
||||||
|
secure_accessible_topic: 200
|
||||||
}
|
}
|
||||||
include_examples "various scenarios", expected
|
include_examples "various scenarios", expected
|
||||||
end
|
end
|
||||||
|
@ -1364,7 +1375,8 @@ RSpec.describe TopicsController do
|
||||||
deleted_topic: 410,
|
deleted_topic: 410,
|
||||||
deleted_secure_topic: 410,
|
deleted_secure_topic: 410,
|
||||||
deleted_private_topic: 410,
|
deleted_private_topic: 410,
|
||||||
nonexist: 404
|
nonexist: 404,
|
||||||
|
secure_accessible_topic: 200
|
||||||
}
|
}
|
||||||
include_examples "various scenarios", expected
|
include_examples "various scenarios", expected
|
||||||
end
|
end
|
||||||
|
@ -1381,7 +1393,8 @@ RSpec.describe TopicsController do
|
||||||
deleted_topic: 200,
|
deleted_topic: 200,
|
||||||
deleted_secure_topic: 403,
|
deleted_secure_topic: 403,
|
||||||
deleted_private_topic: 403,
|
deleted_private_topic: 403,
|
||||||
nonexist: 404
|
nonexist: 404,
|
||||||
|
secure_accessible_topic: 200
|
||||||
}
|
}
|
||||||
include_examples "various scenarios", expected
|
include_examples "various scenarios", expected
|
||||||
end
|
end
|
||||||
|
@ -1398,7 +1411,8 @@ RSpec.describe TopicsController do
|
||||||
deleted_topic: 200,
|
deleted_topic: 200,
|
||||||
deleted_secure_topic: 200,
|
deleted_secure_topic: 200,
|
||||||
deleted_private_topic: 200,
|
deleted_private_topic: 200,
|
||||||
nonexist: 404
|
nonexist: 404,
|
||||||
|
secure_accessible_topic: 200
|
||||||
}
|
}
|
||||||
include_examples "various scenarios", expected
|
include_examples "various scenarios", expected
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user