FEATURE: Use the same structure as core topic-list-item and add topic excerpts to docs index (#140)

* FEATURE: Optionally show topic excerpts in docs index

This commit adds a new docs_show_topic_excerpts site setting
which, when enabled and used in conjunction either with the
always_include_topic_excerpts site setting or the
serialize_topic_excerpts theme modifier.

For the theme modifier, this commit also fixes an issue
with the Docs::Query class. Since we were re-initializing
Guardian within the class (rather than using the guardian
passed down from the controller), we did not have the request
information needed to determine theme_id, which meant that
the theme modifier had no effect. We now pass down guardian
from the controller instead. In general guardian is almost
always better than a user object, since we can always just
call guardian.user.

Adds a simple system spec as well.

---

We now check both the serialize_topic_excerpts theme modifier
and the always_include_topic_excerpts site setting server-side
within Docs::Query, and use that on the client to determine
whether or not to show the excerpts for docs. This is because if
someone sets one of those values it's logical to think it will
apply everywhere there is a topic list.

Also include a system spec to test whether the theme modifier
works to show the excerpts.

---------

Co-authored-by: Martin Brennan <martin@discourse.org>
This commit is contained in:
Jordan Vidrine 2023-06-15 12:44:44 -05:00 committed by GitHub
parent ed77e768e6
commit 06f6d00ba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 132 additions and 25 deletions

View File

@ -18,7 +18,7 @@ module Docs
page: params[:page], page: params[:page],
} }
query = Docs::Query.new(current_user, filters).list query = Docs::Query.new(guardian, filters).list
if filters[:topic].present? if filters[:topic].present?
begin begin

View File

@ -0,0 +1,24 @@
import Component from "@ember/component";
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
import { findRawTemplate } from "discourse-common/lib/raw-templates";
import { htmlSafe } from "@ember/template";
export default Component.extend({
tagName: "tr",
classNameBindings: [":topic-list-item"],
didInsertElement() {
this._super(...arguments);
this.renderTopicListItem();
},
renderTopicListItem() {
const template = findRawTemplate("docs-topic-list-item");
if (template) {
this.set(
"topicListItemContents",
htmlSafe(template(this, RUNTIME_OPTIONS))
);
}
},
});

View File

@ -48,6 +48,7 @@ export default Controller.extend({
categories: readOnly("model.categories"), categories: readOnly("model.categories"),
topics: alias("model.topics.topic_list.topics"), topics: alias("model.topics.topic_list.topics"),
tags: readOnly("model.tags"), tags: readOnly("model.tags"),
showExcerpts: readOnly("model.meta.show_topic_excerpts"),
tagGroups: readOnly("model.tag_groups"), tagGroups: readOnly("model.tag_groups"),
topicCount: alias("model.topic_count"), topicCount: alias("model.topic_count"),
emptyResults: equal("topicCount", 0), emptyResults: equal("topicCount", 0),
@ -68,6 +69,7 @@ export default Controller.extend({
}, },
}); });
}, },
@discourseComputed("categories", "categorySort", "categoryFilter") @discourseComputed("categories", "categorySort", "categoryFilter")
sortedCategories(categories, categorySort, filter) { sortedCategories(categories, categorySort, filter) {
let { type, direction } = categorySort; let { type, direction } = categorySort;

View File

@ -0,0 +1 @@
{{this.topicListItemContents}}

View File

@ -28,7 +28,11 @@
<tbody class="topic-list-body"> <tbody class="topic-list-body">
{{#each topics as |topic|}} {{#each topics as |topic|}}
{{raw "docs-topic-list-item" topic=topic urlPath=urlPath}} {{docs-topic-list-item
topic=topic
urlPath=urlPath
showExcerpt=showExcerpts
}}
{{/each}} {{/each}}
</tbody> </tbody>
</table> </table>

View File

@ -36,7 +36,9 @@
{{#if categories}} {{#if categories}}
<div class="docs-items docs-categories"> <div class="docs-items docs-categories">
<section class="item-controls"> <section class="item-controls">
<h3>{{i18n "docs.categories"}}</h3> <h3>
{{i18n "docs.categories"}}
</h3>
<div class="item-controls-buttons"> <div class="item-controls-buttons">
<DButton <DButton
class={{if class={{if
@ -87,7 +89,9 @@
{{#if (and tags shouldShowTags)}} {{#if (and tags shouldShowTags)}}
<div class="docs-items docs-tags"> <div class="docs-items docs-tags">
<section class="item-controls"> <section class="item-controls">
<h3>{{i18n "docs.tags"}}</h3> <h3>
{{i18n "docs.tags"}}
</h3>
<div class="item-controls-buttons"> <div class="item-controls-buttons">
<DButton <DButton
class={{if class={{if
@ -142,7 +146,9 @@
{{#if (and tagGroups shouldShowTagsByGroup)}} {{#if (and tagGroups shouldShowTagsByGroup)}}
<div class="docs-items docs-tags"> <div class="docs-items docs-tags">
<section class="item-controls"> <section class="item-controls">
<h3>{{i18n "docs.tags"}}</h3> <h3>
{{i18n "docs.tags"}}
</h3>
<div class="item-controls-buttons"> <div class="item-controls-buttons">
<DButton <DButton
class={{if class={{if
@ -232,6 +238,7 @@
{{#unless emptyResults}} {{#unless emptyResults}}
<DocsTopicList <DocsTopicList
@topics={{topics}} @topics={{topics}}
@showExcerpts={{showExcerpts}}
@ascending={{ascending}} @ascending={{ascending}}
@order={{orderColumn}} @order={{orderColumn}}
@sortBy={{action "sortBy"}} @sortBy={{action "sortBy"}}

View File

@ -1,15 +1,19 @@
<tr class="topic-list-item"> {{~raw-plugin-outlet name="topic-list-before-columns"}}
<td class="main-link topic-list-data">
<span class="link-top-line"> <td class="main-link topic-list-data">
{{~raw "topic-status" topic=topic}} {{~raw-plugin-outlet name="topic-list-before-link"}}
{{~raw "docs-topic-link" topic=topic urlPath=urlPath}} <span class="link-top-line">
</span> {{~raw "topic-status" topic=topic}}
<span class="link-bottom-line"> {{~raw "docs-topic-link" topic=topic urlPath=urlPath}}
{{category-link topic.category}} </span>
{{discourse-tags topic mode="list"}} <span class="link-bottom-line">
</span> {{category-link topic.category}}
</td> {{discourse-tags topic mode="list"}}
<td class="topic-list-data"> </span>
{{format-date topic.bumped_at format="tiny" noTitle="true"}} {{!-- {{#if showExcerpt}} --}}
</td> {{~raw "list/topic-excerpt" topic=topic}}
</tr> {{!-- {{/if}} --}}
</td>
<td class="topic-list-data">
{{format-date topic.bumped_at format="tiny" noTitle="true"}}
</td>

View File

@ -2,8 +2,8 @@
module Docs module Docs
class Query class Query
def initialize(user = nil, filters = {}) def initialize(guardian, filters = {})
@user = user @guardian = guardian
@filters = filters @filters = filters
@limit = 30 @limit = 30
end end
@ -19,7 +19,7 @@ module Docs
def list def list
# query for topics matching selected categories & tags # query for topics matching selected categories & tags
opts = { no_definitions: true, limit: false } opts = { no_definitions: true, limit: false }
tq = TopicQuery.new(@user, opts) tq = TopicQuery.new(@guardian.user, opts)
results = tq.list_docs_topics results = tq.list_docs_topics
results = results =
results.left_outer_joins(SiteSetting.show_tags_by_group ? { tags: :tag_groups } : :tags) results.left_outer_joins(SiteSetting.show_tags_by_group ? { tags: :tag_groups } : :tags)
@ -155,7 +155,7 @@ module Docs
# assemble the object # assemble the object
topic_query = tq.create_list(:docs, { unordered: true }, results) topic_query = tq.create_list(:docs, { unordered: true }, results)
topic_list = TopicListSerializer.new(topic_query, scope: Guardian.new(@user)).as_json topic_list = TopicListSerializer.new(topic_query, scope: @guardian).as_json
if end_of_list.nil? if end_of_list.nil?
topic_list["load_more_url"] = load_more_url topic_list["load_more_url"] = load_more_url
@ -169,6 +169,9 @@ module Docs
:categories => categories, :categories => categories,
:topics => topic_list, :topics => topic_list,
:topic_count => results_length, :topic_count => results_length,
:meta => {
show_topic_excerpts: show_topic_excerpts,
},
} }
end end
@ -200,7 +203,7 @@ module Docs
tags_object << { id: tag[0], count: tag[1], active: active || false } tags_object << { id: tag[0], count: tag[1], active: active || false }
end end
allowed_tags = DiscourseTagging.filter_allowed_tags(Guardian.new(@user)).map(&:name) allowed_tags = DiscourseTagging.filter_allowed_tags(@guardian).map(&:name)
tags_object = tags_object.select { |tag| allowed_tags.include?(tag[:id]) } tags_object = tags_object.select { |tag| allowed_tags.include?(tag[:id]) }
@ -235,5 +238,10 @@ module Docs
"/#{GlobalSetting.docs_path}.json?#{filters.join("&")}" "/#{GlobalSetting.docs_path}.json?#{filters.join("&")}"
end end
def show_topic_excerpts
SiteSetting.always_include_topic_excerpts ||
ThemeModifierHelper.new(request: @guardian.request).serialize_topic_excerpts
end
end end
end end

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
describe "Discourse Docs | Index", type: :system do
fab!(:current_user) { Fabricate(:user) }
fab!(:category) { Fabricate(:category) }
fab!(:topic_1) { Fabricate(:topic, category: category) }
fab!(:topic_2) { Fabricate(:topic, category: category) }
fab!(:post_1) { Fabricate(:post, topic: topic_1) }
fab!(:post_2) { Fabricate(:post, topic: topic_2) }
before do
SiteSetting.docs_enabled = true
SiteSetting.docs_categories = category.id.to_s
sign_in(current_user)
end
it "does not error when showing the index" do
visit("/docs")
expect(page).to have_css(".docs-topic-link", text: topic_1.title)
expect(page).to have_css(".docs-topic-link", text: topic_2.title)
end
describe "topic excerpts" do
before do
topic_1.update_excerpt(post_1.excerpt_for_topic)
topic_2.update_excerpt(post_2.excerpt_for_topic)
end
it "does not show the topic excerpts by default" do
visit("/docs")
expect(page).to have_no_css(".topic-excerpt")
end
context "when docs_show_topic_excerpts is true" do
before { SiteSetting.always_include_topic_excerpts = true }
it "shows the excerpts" do
visit("/docs")
expect(page).to have_css(".topic-excerpt", text: topic_1.excerpt)
expect(page).to have_css(".topic-excerpt", text: topic_2.excerpt)
end
end
context "when the theme modifier serialize_topic_excerpts is true" do
before do
ThemeModifierSet.find_by(theme_id: Theme.first.id).update!(serialize_topic_excerpts: true)
Theme.clear_cache!
end
it "shows the excerpts" do
visit("/docs")
expect(page).to have_css(".topic-excerpt", text: topic_1.excerpt)
expect(page).to have_css(".topic-excerpt", text: topic_2.excerpt)
end
end
end
end