Feature/sort by event start date (#320)

FEATURE:
 - Added custom fields `sort_topics_by_event_start_date` and `disable_topic_resorting`
 - Sort topics of event categories by event start date. Event categories have custom field `sort_topics_by_event_start_date` set to true.
 - Disable re-sorting of categories with custom field `disable_topic_resorting`
 - Event date displayed beside the Topic title can optionally be set to show the local date-time, instead of relative date-time.
 -
This commit is contained in:
Frank 2022-09-23 06:02:48 +08:00 committed by GitHub
parent 139fdd9cc3
commit 6820f7a433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 184 additions and 2 deletions

View File

@ -0,0 +1,21 @@
<section>
<h3>{{i18n "category.settings_sections.event_sorting"}}</h3>
{{#if siteSettings.sort_categories_by_event_start_date_enabled}}
<section class="field show-subcategory-list-field">
<label>
<Input @type="checkbox" @checked={{this.category.custom_fields.sort_topics_by_event_start_date}} />
{{i18n "category.sort_topics_by_event_start_date"}}
</label>
</section>
{{/if}}
{{#if siteSettings.disable_resorting_on_categories_enabled}}
<section class="field show-subcategory-list-field">
<label>
<Input @type="checkbox" @checked={{this.category.custom_fields.disable_topic_resorting}} />
{{i18n "category.disable_topic_resorting"}}
</label>
</section>
{{/if}}
</section>

View File

@ -1,5 +1,6 @@
import { withPluginApi } from "discourse/lib/plugin-api";
import eventRelativeDate from "discourse/plugins/discourse-calendar/lib/event-relative-date";
import eventLocalDate from "discourse/plugins/discourse-calendar/lib/event-local-date";
function initializeDecorateTopicTitle(api) {
api.decorateTopicTitle((topic, node, topicTitleType) => {
@ -25,8 +26,12 @@ function initializeDecorateTopicTitle(api) {
eventdateContainer.appendChild(eventDate);
node.appendChild(eventdateContainer);
// we force a first computation, as waiting for the auto update might take time
eventRelativeDate(eventDate);
if (topic.siteSettings.use_local_event_date) {
eventLocalDate(eventDate);
} else {
// we force a first computation, as waiting for the auto update might take time
eventRelativeDate(eventDate);
}
}
});
}

View File

@ -0,0 +1,20 @@
import discourseComputed from "discourse-common/utils/decorators";
import { withPluginApi } from "discourse/lib/plugin-api";
export default {
name: "disable-sort",
initialize() {
withPluginApi("0.8", (api) => {
api.modifyClass("component:topic-list", {
@discourseComputed("category")
sortable(category) {
let disableSort = true;
if (category && category.custom_fields) {
disableSort = !!category.custom_fields["disable_topic_resorting"];
}
return !!this.changeSort && !disableSort;
},
});
});
},
};

View File

@ -0,0 +1,21 @@
import guessDateFormat from "discourse/plugins/discourse-calendar/lib/guess-best-date-format";
export default function eventLocalDate(container) {
container.classList.remove("past", "current", "future");
container.innerHTML = "";
const startsAt = moment
.utc(container.dataset.starts_at)
.tz(moment.tz.guess());
const endsAt = moment.utc(container.dataset.ends_at).tz(moment.tz.guess());
const format = guessDateFormat(startsAt);
let title = startsAt.format(format);
if (endsAt) {
title += `${endsAt.format(format)}`;
}
container.setAttribute("title", title);
container.classList.add("past");
container.innerText = startsAt.format(format);
}

View File

@ -413,3 +413,8 @@ en:
creator: "Creator"
status: "Status"
starts_at: "Starts at"
category:
sort_topics_by_event_start_date: "Sort topics by event start date."
disable_topic_resorting: "Disable topic resorting."
settings_sections:
event_sorting: "Event Sorting"

View File

@ -33,6 +33,7 @@ en:
discourse_post_event_enabled: "[experimental] Enables to attach an event to a post. Note: also needs `calendar enabled` to be enabled."
displayed_invitees_limit: "Limits the numbers of invitees displayed on an event."
display_post_event_date_on_topic_title: "Displays the date of the event after the topic title."
use_local_event_date: "Use local date after topic title instead of relative time."
discourse_post_event_allowed_on_groups: "Groups that are allowed to create events."
discourse_post_event_allowed_custom_fields: "Allows to let each event to set the value of custom fields."
discourse_post_event_edit_notifications_time_extension: "Extends (in minutes) the period after the end of an event when `going` invitees are still being notified from edit in the original post."
@ -48,6 +49,8 @@ en:
working_day_end_hour: "End time of the working day hours."
close_to_working_day_hours_extension: "Set extension time in working day hours to highlight the timezones."
events_calendar_categories: "Display an events calendar at the top of a category."
sort_categories_by_event_start_date_enabled: "Enable the sorting of category topics by event start date."
disable_resorting_on_categories_enabled: "Allow categories to disable the ability for users to sort on the event category."
discourse_calendar:
invite_user_notification: "%{username} invited you to: %{description}"
calendar_must_be_in_first_post: "Calendar tag can only be used in first post of a topic."

View File

@ -64,6 +64,9 @@ discourse_post_event:
display_post_event_date_on_topic_title:
default: true
client: true
use_local_event_date:
default: false
client: true
discourse_post_event_max_bulk_invitees:
default: 500
hidden: true
@ -79,3 +82,10 @@ discourse_post_event:
type: category_list
client: true
default: ""
sort_categories_by_event_start_date_enabled:
default: false
client: true
disable_resorting_on_categories_enabled:
default: false
client: true

View File

@ -40,6 +40,30 @@ register_svg_icon 'fas fa-star'
register_svg_icon 'fas fa-file-upload'
after_initialize do
Category.register_custom_field_type("sort_topics_by_event_start_date", :boolean)
Category.register_custom_field_type("disable_topic_resorting", :boolean)
Site.preloaded_category_custom_fields << 'sort_topics_by_event_start_date'
Site.preloaded_category_custom_fields << 'disable_topic_resorting'
add_to_serializer :basic_category, :sort_topics_by_event_start_date do
object.custom_fields["sort_topics_by_event_start_date"]
end
add_to_serializer :basic_category, :disable_topic_resorting do
object.custom_fields["disable_topic_resorting"]
end
TopicQuery.add_custom_filter(:order_by_event_date) do |results, topic_query|
if SiteSetting.sort_categories_by_event_start_date_enabled && topic_query.options[:category_id]
category = Category.find_by(id: topic_query.options[:category_id])
if category && category.custom_fields && category.custom_fields["sort_topics_by_event_start_date"]
results = results.joins("LEFT JOIN topic_custom_fields AS custom_fields on custom_fields.topic_id = topics.id
AND custom_fields.name = '#{DiscoursePostEvent::TOPIC_POST_EVENT_STARTS_AT}'
").reorder("topics.pinned_at ASC, custom_fields.value ASC")
end
end
results
end
module ::DiscourseCalendar
PLUGIN_NAME ||= 'discourse-calendar'

View File

@ -0,0 +1,49 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe ListController do
fab!(:admin) { Fabricate(:admin) }
fab!(:user) { Fabricate(:user) }
fab!(:category) { Fabricate(:category) }
fab!(:topic_1) { Fabricate(:topic, title: "This is the first topic", user: user, category: category) }
fab!(:post_1) { Fabricate(:post, topic: topic_1) }
fab!(:post_event_1) { Fabricate(:event, name: "event1", post: post_1, original_starts_at: 1.days.from_now) }
fab!(:topic_2) { Fabricate(:topic, title: "This is the second topic", user: user, category: category) }
fab!(:post_2) { Fabricate(:post, topic: topic_2) }
fab!(:post_event_2) { Fabricate(:event, name: "event2", post: post_2, original_starts_at: 2.days.from_now) }
before do
admin
SiteSetting.calendar_enabled = true
SiteSetting.sort_categories_by_event_start_date_enabled = true
end
describe '#sort_event_topics' do
it 'gets topics in order of event_date if sort_topics_by_event_start_date is true' do
category.custom_fields["sort_topics_by_event_start_date"] = true
category.save
get "/c/#{category.slug}/#{category.id}/l/latest.json?ascending=false"
topics = response.parsed_body["topic_list"]["topics"]
expect(topics[0]["id"]).to eq(topic_1.id)
expect(topics[1]["id"]).to eq(topic_2.id)
end
it 'does not gets topics in order of event_date if sort_topics_by_event_start_date is false' do
category.custom_fields["sort_topics_by_event_start_date"] = false
category.save
get "/c/#{category.slug}/#{category.id}/l/latest.json?ascending=false"
topics = response.parsed_body["topic_list"]["topics"]
expect(topics[0]["id"]).to eq(topic_2.id)
expect(topics[1]["id"]).to eq(topic_1.id)
end
end
end

View File

@ -0,0 +1,24 @@
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
import { test } from "qunit";
import { visit } from "@ember/test-helpers";
import { cloneJSON } from "discourse-common/lib/object";
import CategoryFixtures from "discourse/tests/fixtures/category-fixtures";
acceptance("Calendar - Disable sorting headers", function (needs) {
needs.user();
needs.pretender((server, helper) => {
const categoryResponse = cloneJSON(CategoryFixtures["/c/1/show.json"]);
categoryResponse.category.custom_fields["disable_topic_resorting"] = true;
server.get("/c/1/show.json", () => helper.response(categoryResponse));
});
test("visiting a category page", async function (assert) {
await visit("/c/bug");
assert.ok(exists(".topic-list"), "The list of topics was rendered");
assert.ok(
exists(".topic-list .topic-list-data"),
"The headers were rendered"
);
assert.ok(!exists(".topic-list .sortable"), "The headers are not sortable");
});
});