mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 19:03:45 +08:00
FEATURE: add category banner for why a user cannot post (#9576)
* FEATURE: add category banner for why a user cannot post Adds a category banner for why a user is unable to post in a category. Also adds an extra alert for the user when a user is unable to create a topic in a category and they still try and click on the disabled-looking new topic button.
This commit is contained in:
parent
03815f9795
commit
2cb9e85d14
|
@ -0,0 +1,11 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { and } from "@ember/object/computed";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
@discourseComputed
|
||||||
|
user() {
|
||||||
|
return this.currentUser;
|
||||||
|
},
|
||||||
|
shouldShow: and("category.read_only_banner", "readOnly", "user")
|
||||||
|
});
|
|
@ -1,5 +1,6 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "",
|
tagName: "",
|
||||||
label: "topic.create"
|
label: "topic.create",
|
||||||
|
btnClass: "btn-default"
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,38 @@ export default Component.extend(FilterModeMixin, {
|
||||||
return category && this.currentUser;
|
return category && this.currentUser;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("category", "createTopicDisabled")
|
||||||
|
categoryReadOnlyBanner(category, createTopicDisabled) {
|
||||||
|
if (category && this.currentUser && createTopicDisabled) {
|
||||||
|
return category.read_only_banner;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"createTopicDisabled",
|
||||||
|
"hasDraft",
|
||||||
|
"categoryReadOnlyBanner"
|
||||||
|
)
|
||||||
|
createTopicButtonDisabled(
|
||||||
|
createTopicDisabled,
|
||||||
|
hasDraft,
|
||||||
|
categoryReadOnlyBanner
|
||||||
|
) {
|
||||||
|
if (categoryReadOnlyBanner && !hasDraft) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return createTopicDisabled;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("categoryReadOnlyBanner", "hasDraft")
|
||||||
|
createTopicClass(categoryReadOnlyBanner, hasDraft) {
|
||||||
|
if (categoryReadOnlyBanner && !hasDraft) {
|
||||||
|
return "btn-default disabled";
|
||||||
|
} else {
|
||||||
|
return "btn-default";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed()
|
@discourseComputed()
|
||||||
categories() {
|
categories() {
|
||||||
return this.site.get("categoriesList");
|
return this.site.get("categoriesList");
|
||||||
|
@ -65,6 +97,14 @@ export default Component.extend(FilterModeMixin, {
|
||||||
this.reorderCategories();
|
this.reorderCategories();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clickCreateTopicButton() {
|
||||||
|
if (this.categoryReadOnlyBanner && !this.hasDraft) {
|
||||||
|
bootbox.alert(this.categoryReadOnlyBanner);
|
||||||
|
} else {
|
||||||
|
this.createTopic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -187,7 +187,8 @@ const Category = RestModel.extend({
|
||||||
"navigate_to_first_post_after_read"
|
"navigate_to_first_post_after_read"
|
||||||
),
|
),
|
||||||
search_priority: this.search_priority,
|
search_priority: this.search_priority,
|
||||||
reviewable_by_group_name: this.reviewable_by_group_name
|
reviewable_by_group_name: this.reviewable_by_group_name,
|
||||||
|
read_only_banner: this.read_only_banner
|
||||||
},
|
},
|
||||||
type: id ? "PUT" : "POST"
|
type: id ? "PUT" : "POST"
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{#if shouldShow}}
|
||||||
|
<div class="row">
|
||||||
|
<div class="alert alert-info category-read-only-banner">
|
||||||
|
{{category.read_only_banner}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -1,6 +1,6 @@
|
||||||
{{#if canCreateTopic}}
|
{{#if canCreateTopic}}
|
||||||
{{d-button
|
{{d-button
|
||||||
class="btn-default"
|
class=btnClass
|
||||||
id="create-topic"
|
id="create-topic"
|
||||||
action=action
|
action=action
|
||||||
icon="plus"
|
icon="plus"
|
||||||
|
|
|
@ -25,9 +25,10 @@
|
||||||
|
|
||||||
{{create-topic-button
|
{{create-topic-button
|
||||||
canCreateTopic=canCreateTopic
|
canCreateTopic=canCreateTopic
|
||||||
action=createTopic
|
action=(action "clickCreateTopicButton")
|
||||||
disabled=createTopicDisabled
|
disabled=createTopicButtonDisabled
|
||||||
label=createTopicLabel
|
label=createTopicLabel
|
||||||
|
btnClass=createTopicClass
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{#if showCategoryEdit}}
|
{{#if showCategoryEdit}}
|
||||||
|
|
|
@ -221,6 +221,20 @@
|
||||||
}}
|
}}
|
||||||
</section>
|
</section>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<section class="field category-read-only-banner">
|
||||||
|
<label for="category-read-only-banner">
|
||||||
|
{{i18n "category.read_only_banner"}}
|
||||||
|
</label>
|
||||||
|
{{text-field
|
||||||
|
valueProperty="value"
|
||||||
|
id="read-only-message"
|
||||||
|
value=category.read_only_banner
|
||||||
|
options=(hash
|
||||||
|
placementStrategy="absolute"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{discourse-banner user=currentUser banner=site.banner}}
|
{{discourse-banner user=currentUser banner=site.banner}}
|
||||||
|
{{category-read-only-banner category=category readOnly=navigationCategory.cannotCreateTopicOnCategory}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-controls">
|
<div class="list-controls">
|
||||||
|
|
|
@ -328,6 +328,7 @@ class CategoriesController < ApplicationController
|
||||||
:allow_global_tags,
|
:allow_global_tags,
|
||||||
:required_tag_group_name,
|
:required_tag_group_name,
|
||||||
:min_tags_from_required_group,
|
:min_tags_from_required_group,
|
||||||
|
:read_only_banner,
|
||||||
custom_fields: [params[:custom_fields].try(:keys)],
|
custom_fields: [params[:custom_fields].try(:keys)],
|
||||||
permissions: [*p.try(:keys)],
|
permissions: [*p.try(:keys)],
|
||||||
allowed_tags: [],
|
allowed_tags: [],
|
||||||
|
|
|
@ -978,6 +978,7 @@ end
|
||||||
# reviewable_by_group_id :integer
|
# reviewable_by_group_id :integer
|
||||||
# required_tag_group_id :integer
|
# required_tag_group_id :integer
|
||||||
# min_tags_from_required_group :integer default(1), not null
|
# min_tags_from_required_group :integer default(1), not null
|
||||||
|
# read_only_banner :string
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
|
|
@ -6,7 +6,8 @@ class SiteCategorySerializer < BasicCategorySerializer
|
||||||
:allowed_tag_groups,
|
:allowed_tag_groups,
|
||||||
:allow_global_tags,
|
:allow_global_tags,
|
||||||
:min_tags_from_required_group,
|
:min_tags_from_required_group,
|
||||||
:required_tag_group_name
|
:required_tag_group_name,
|
||||||
|
:read_only_banner
|
||||||
|
|
||||||
def include_allowed_tags?
|
def include_allowed_tags?
|
||||||
SiteSetting.tagging_enabled
|
SiteSetting.tagging_enabled
|
||||||
|
|
|
@ -2795,6 +2795,7 @@ en:
|
||||||
email_in_disabled_click: 'enable the "email in" setting.'
|
email_in_disabled_click: 'enable the "email in" setting.'
|
||||||
mailinglist_mirror: "Category mirrors a mailing list"
|
mailinglist_mirror: "Category mirrors a mailing list"
|
||||||
show_subcategory_list: "Show subcategory list above topics in this category."
|
show_subcategory_list: "Show subcategory list above topics in this category."
|
||||||
|
read_only_banner: "Banner text when a user cannot create a topic in this category:"
|
||||||
num_featured_topics: "Number of topics shown on the categories page:"
|
num_featured_topics: "Number of topics shown on the categories page:"
|
||||||
subcategory_num_featured_topics: "Number of featured topics on parent category's page:"
|
subcategory_num_featured_topics: "Number of featured topics on parent category's page:"
|
||||||
all_topics_wiki: "Make new topics wikis by default"
|
all_topics_wiki: "Make new topics wikis by default"
|
||||||
|
|
7
db/migrate/20200427222624_add_read_only_to_categories.rb
Normal file
7
db/migrate/20200427222624_add_read_only_to_categories.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddReadOnlyToCategories < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :categories, :read_only_banner, :string
|
||||||
|
end
|
||||||
|
end
|
89
test/javascripts/acceptance/category-banner-test.js
Normal file
89
test/javascripts/acceptance/category-banner-test.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
import DiscoveryFixtures from "fixtures/discovery_fixtures";
|
||||||
|
|
||||||
|
acceptance("Category Banners", {
|
||||||
|
pretend(server, helper) {
|
||||||
|
server.get("/c/test-read-only-without-banner/5/l/latest.json", () => {
|
||||||
|
return helper.response(
|
||||||
|
DiscoveryFixtures["/latest_can_create_topic.json"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
server.get("/c/test-read-only-with-banner/6/l/latest.json", () => {
|
||||||
|
return helper.response(
|
||||||
|
DiscoveryFixtures["/latest_can_create_topic.json"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loggedIn: true,
|
||||||
|
site: {
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "test read only without banner",
|
||||||
|
slug: "test-read-only-without-banner",
|
||||||
|
permission: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: "test read only with banner",
|
||||||
|
slug: "test-read-only-with-banner",
|
||||||
|
permission: null,
|
||||||
|
read_only_banner:
|
||||||
|
"You need to video yourself doing the secret handshake to post here"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Does not display category banners when not set", async assert => {
|
||||||
|
await visit("/c/test-read-only-without-banner");
|
||||||
|
|
||||||
|
await click("#create-topic");
|
||||||
|
assert.ok(!visible(".bootbox.modal"), "it does not pop up a modal");
|
||||||
|
assert.ok(
|
||||||
|
!visible(".category-read-only-banner"),
|
||||||
|
"it does not show a banner"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Displays category banners when set", async assert => {
|
||||||
|
await visit("/c/test-read-only-with-banner");
|
||||||
|
|
||||||
|
await click("#create-topic");
|
||||||
|
assert.ok(visible(".bootbox.modal"), "it pops up a modal");
|
||||||
|
|
||||||
|
await click(".modal-footer>.btn-primary");
|
||||||
|
assert.ok(!visible(".bootbox.modal"), "it closes the modal");
|
||||||
|
assert.ok(visible(".category-read-only-banner"), "it shows a banner");
|
||||||
|
});
|
||||||
|
|
||||||
|
acceptance("Anonymous Category Banners", {
|
||||||
|
pretend(server, helper) {
|
||||||
|
server.get("/c/test-read-only-with-banner/6/l/latest.json", () => {
|
||||||
|
return helper.response(
|
||||||
|
DiscoveryFixtures["/latest_can_create_topic.json"]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loggedIn: false,
|
||||||
|
site: {
|
||||||
|
categories: [
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: "test read only with banner",
|
||||||
|
slug: "test-read-only-with-banner",
|
||||||
|
permission: null,
|
||||||
|
read_only_banner:
|
||||||
|
"You need to video yourself doing the secret handshake to post here"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("Does not display category banners when set", async assert => {
|
||||||
|
await visit("/c/test-read-only-with-banner");
|
||||||
|
assert.ok(
|
||||||
|
!visible(".category-read-only-banner"),
|
||||||
|
"it does not show a banner"
|
||||||
|
);
|
||||||
|
});
|
|
@ -6120,5 +6120,107 @@ export default {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/latest_can_create_topic.json": {
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
username: "tt1",
|
||||||
|
name: null,
|
||||||
|
avatar_template: "/letter_avatar_proxy/v4/letter/t/6de8d8/{size}.png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
primary_groups: [],
|
||||||
|
topic_list: {
|
||||||
|
can_create_topic: true,
|
||||||
|
draft: null,
|
||||||
|
draft_key: "new_topic",
|
||||||
|
draft_sequence: 0,
|
||||||
|
per_page: 30,
|
||||||
|
topics: [
|
||||||
|
{
|
||||||
|
id: 30,
|
||||||
|
title: "I am also creating a new topic here new topic",
|
||||||
|
fancy_title: "I am also creating a new topic here new topic",
|
||||||
|
slug: "i-am-also-creating-a-new-topic-here-new-topic",
|
||||||
|
posts_count: 6,
|
||||||
|
reply_count: 0,
|
||||||
|
highest_post_number: 6,
|
||||||
|
image_url: null,
|
||||||
|
created_at: "2020-04-27T23:47:44.218Z",
|
||||||
|
last_posted_at: "2020-04-28T22:45:47.529Z",
|
||||||
|
bumped: true,
|
||||||
|
bumped_at: "2020-04-28T22:02:20.215Z",
|
||||||
|
archetype: "regular",
|
||||||
|
unseen: false,
|
||||||
|
last_read_post_number: 5,
|
||||||
|
unread: 0,
|
||||||
|
new_posts: 0,
|
||||||
|
pinned: false,
|
||||||
|
unpinned: null,
|
||||||
|
visible: true,
|
||||||
|
closed: false,
|
||||||
|
archived: false,
|
||||||
|
notification_level: 1,
|
||||||
|
bookmarked: false,
|
||||||
|
liked: false,
|
||||||
|
tags: ["test", "test-tag"],
|
||||||
|
views: 6,
|
||||||
|
like_count: 0,
|
||||||
|
has_summary: false,
|
||||||
|
last_poster_username: "tt1",
|
||||||
|
category_id: 5,
|
||||||
|
pinned_globally: false,
|
||||||
|
featured_link: null,
|
||||||
|
posters: [
|
||||||
|
{
|
||||||
|
extras: "latest single",
|
||||||
|
description: "Original Poster, Most Recent Poster",
|
||||||
|
user_id: 1,
|
||||||
|
primary_group_id: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 29,
|
||||||
|
title: "About the test category category",
|
||||||
|
fancy_title: "About the test category category",
|
||||||
|
slug: "about-the-test-category-category",
|
||||||
|
posts_count: 5,
|
||||||
|
reply_count: 0,
|
||||||
|
highest_post_number: 5,
|
||||||
|
image_url: null,
|
||||||
|
created_at: "2020-04-27T22:15:49.424Z",
|
||||||
|
last_posted_at: "2020-04-27T23:51:06.249Z",
|
||||||
|
bumped: true,
|
||||||
|
bumped_at: "2020-04-27T22:15:49.424Z",
|
||||||
|
archetype: "regular",
|
||||||
|
unseen: false,
|
||||||
|
pinned: false,
|
||||||
|
unpinned: null,
|
||||||
|
visible: true,
|
||||||
|
closed: false,
|
||||||
|
archived: false,
|
||||||
|
bookmarked: null,
|
||||||
|
liked: null,
|
||||||
|
tags: [],
|
||||||
|
views: 1,
|
||||||
|
like_count: 0,
|
||||||
|
has_summary: false,
|
||||||
|
last_poster_username: "tt1",
|
||||||
|
category_id: 5,
|
||||||
|
pinned_globally: false,
|
||||||
|
featured_link: null,
|
||||||
|
posters: [
|
||||||
|
{
|
||||||
|
extras: "latest single",
|
||||||
|
description: "Original Poster, Most Recent Poster",
|
||||||
|
user_id: 1,
|
||||||
|
primary_group_id: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user