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:
Jeff Wong 2020-04-30 07:39:11 -10:00 committed by GitHub
parent 03815f9795
commit 2cb9e85d14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 284 additions and 6 deletions

View File

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

View File

@ -1,5 +1,6 @@
import Component from "@ember/component";
export default Component.extend({
tagName: "",
label: "topic.create"
label: "topic.create",
btnClass: "btn-default"
});

View File

@ -15,6 +15,38 @@ export default Component.extend(FilterModeMixin, {
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()
categories() {
return this.site.get("categoriesList");
@ -65,6 +97,14 @@ export default Component.extend(FilterModeMixin, {
this.reorderCategories();
break;
}
},
clickCreateTopicButton() {
if (this.categoryReadOnlyBanner && !this.hasDraft) {
bootbox.alert(this.categoryReadOnlyBanner);
} else {
this.createTopic();
}
}
}
});

View File

@ -187,7 +187,8 @@ const Category = RestModel.extend({
"navigate_to_first_post_after_read"
),
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"
});

View File

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

View File

@ -1,6 +1,6 @@
{{#if canCreateTopic}}
{{d-button
class="btn-default"
class=btnClass
id="create-topic"
action=action
icon="plus"

View File

@ -25,9 +25,10 @@
{{create-topic-button
canCreateTopic=canCreateTopic
action=createTopic
disabled=createTopicDisabled
action=(action "clickCreateTopicButton")
disabled=createTopicButtonDisabled
label=createTopicLabel
btnClass=createTopicClass
}}
{{#if showCategoryEdit}}

View File

@ -221,6 +221,20 @@
}}
</section>
{{/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>

View File

@ -3,6 +3,7 @@
{{else}}
<div class="container">
{{discourse-banner user=currentUser banner=site.banner}}
{{category-read-only-banner category=category readOnly=navigationCategory.cannotCreateTopicOnCategory}}
</div>
<div class="list-controls">

View File

@ -328,6 +328,7 @@ class CategoriesController < ApplicationController
:allow_global_tags,
:required_tag_group_name,
:min_tags_from_required_group,
:read_only_banner,
custom_fields: [params[:custom_fields].try(:keys)],
permissions: [*p.try(:keys)],
allowed_tags: [],

View File

@ -978,6 +978,7 @@ end
# reviewable_by_group_id :integer
# required_tag_group_id :integer
# min_tags_from_required_group :integer default(1), not null
# read_only_banner :string
#
# Indexes
#

View File

@ -6,7 +6,8 @@ class SiteCategorySerializer < BasicCategorySerializer
:allowed_tag_groups,
:allow_global_tags,
:min_tags_from_required_group,
:required_tag_group_name
:required_tag_group_name,
:read_only_banner
def include_allowed_tags?
SiteSetting.tagging_enabled

View File

@ -2795,6 +2795,7 @@ en:
email_in_disabled_click: 'enable the "email in" setting.'
mailinglist_mirror: "Category mirrors a mailing list"
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:"
subcategory_num_featured_topics: "Number of featured topics on parent category's page:"
all_topics_wiki: "Make new topics wikis by default"

View 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

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

View File

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