DEV: move sidebar community section to database (#21166)

* DEV: move sidebar community section to database

Before, community section was hard-coded. In the future, we are planning to allow admins to edit it. Therefore, it has to be moved to database to `custom_sections` table.

Few steps and simplifications has to be made:
- custom section was hidden behind `enable_custom_sidebar_sections` feature flag. It has to be deleted so all forums, see community section;
- migration to add `section_type` column to sidebar section to show it is a special type;
- migration to add `segment` column to sidebar links to determine if link should be displayed in primary section or in more section;
- simplify more section to have one level only (secondary section links are merged);
- ensure that links like `everything` are correctly tracking state;
- make user an anonymous links position consistence. For example, from now on `faq` link for user and anonymous is visible in more tab;
- delete old community-section template.
This commit is contained in:
Krzysztof Kotlarek 2023-05-04 12:14:09 +10:00 committed by GitHub
parent afc1611be7
commit 709fa24558
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 705 additions and 502 deletions

View File

@ -1,36 +0,0 @@
import SidebarCommonCommunitySection from "discourse/components/sidebar/common/community-section";
import EverythingSectionLink from "discourse/lib/sidebar/common/community-section/everything-section-link";
import AboutSectionLink from "discourse/lib/sidebar/common/community-section/about-section-link";
import FAQSectionLink from "discourse/lib/sidebar/common/community-section/faq-section-link";
import GroupsSectionLink from "discourse/lib/sidebar/common/community-section/groups-section-link";
import UsersSectionLink from "discourse/lib/sidebar/common/community-section/users-section-link";
import BadgesSectionLink from "discourse/lib/sidebar/common/community-section/badges-section-link";
export default class SidebarAnonymousCommunitySection extends SidebarCommonCommunitySection {
get defaultMainSectionLinks() {
const defaultLinks = [
EverythingSectionLink,
UsersSectionLink,
FAQSectionLink,
];
defaultLinks.splice(
this.displayShortSiteDescription ? 0 : 2,
0,
AboutSectionLink
);
return defaultLinks;
}
get displayShortSiteDescription() {
return (
!this.currentUser &&
(this.siteSettings.short_site_description || "").length > 0
);
}
get defaultMoreSectionLinks() {
return [GroupsSectionLink, BadgesSectionLink];
}
}

View File

@ -5,27 +5,49 @@
@headerLinkText={{section.decoratedTitle}}
@collapsable={{true}}
>
{{#if section.displayShortSiteDescription}}
<Sidebar::SectionMessage>
{{section.siteSettings.short_site_description}}
</Sidebar::SectionMessage>
{{/if}}
{{#each section.links as |link|}}
{{#if link.external}}
<Sidebar::SectionLink
@linkName={{link.name}}
@content={{replace-emoji link.name}}
@prefixType="icon"
@prefixValue={{link.icon}}
@href={{link.value}}
/>
{{else}}
<Sidebar::SectionLink
@linkName={{link.name}}
@route={{link.route}}
@models={{link.models}}
@query={{link.query}}
@content={{replace-emoji link.name}}
@prefixType="icon"
@prefixValue={{link.icon}}
/>
{{#if link.shouldDisplay}}
{{#if link.external}}
<Sidebar::SectionLink
@linkName={{link.name}}
@content={{replace-emoji link.text}}
@prefixType="icon"
@prefixValue={{link.prefixValue}}
@href={{link.value}}
/>
{{else}}
<Sidebar::SectionLink
@shouldDisplay={{link.shouldDisplay}}
@href={{link.href}}
@title={{link.title}}
@currentWhen={{link.currentWhen}}
@badgeText={{link.badgeText}}
@suffixCSSClass={{link.suffixCSSClass}}
@suffixValue={{link.suffixValue}}
@suffixType={{link.suffixType}}
@linkName={{link.name}}
@route={{link.route}}
@model={{link.model}}
@models={{link.models}}
@query={{link.query}}
@content={{replace-emoji link.text}}
@prefixType="icon"
@prefixValue={{link.prefixValue}}
/>
{{/if}}
{{/if}}
{{/each}}
{{#if section.moreLinks}}
<Sidebar::MoreSectionLinks @sectionLinks={{section.moreLinks}} />
{{/if}}
</Sidebar::Section>
{{/each}}
</div>

View File

@ -1,16 +1,29 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import Section from "discourse/components/sidebar/user/section";
import Section from "discourse/lib/sidebar/section";
import CommunitySection from "discourse/lib/sidebar/community-section";
export default class SidebarAnonymousCustomSections extends Component {
@service router;
@service site;
@service siteSettings;
get sections() {
return this.site.anonymous_sidebar_sections?.map((section) => {
return new Section({
let klass;
switch (section.section_type) {
case "community":
klass = CommunitySection;
break;
default:
klass = Section;
}
return new klass({
section,
currentUser: this.currentUser,
router: this.router,
siteSettings: this.siteSettings,
});
});
}

View File

@ -1,5 +1,4 @@
<div class="sidebar-sections sidebar-sections-anonymous">
<Sidebar::Anonymous::CommunitySection @collapsable={{@collapsableSections}} />
<Sidebar::Anonymous::CustomSections />
<Sidebar::Anonymous::CategoriesSection
@collapsable={{@collapsableSections}}

View File

@ -1,49 +0,0 @@
<Sidebar::Section
@sectionName="community"
@headerLinkText={{i18n "sidebar.sections.community.header_link_text"}}
@headerActionsIcon={{this.headerActionsIcon}}
@headerActions={{this.headerActions}}
@collapsable={{@collapsable}}
>
{{#if this.displayShortSiteDescription}}
<Sidebar::SectionMessage>
{{this.siteSettings.short_site_description}}
</Sidebar::SectionMessage>
{{/if}}
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::SectionLink
@shouldDisplay={{sectionLink.shouldDisplay}}
@linkName={{sectionLink.name}}
@href={{sectionLink.href}}
@route={{sectionLink.route}}
@query={{sectionLink.query}}
@title={{sectionLink.title}}
@content={{sectionLink.text}}
@currentWhen={{sectionLink.currentWhen}}
@badgeText={{sectionLink.badgeText}}
@model={{sectionLink.model}}
@models={{sectionLink.models}}
@prefixType={{sectionLink.prefixType}}
@prefixValue={{sectionLink.prefixValue}}
@suffixCSSClass={{sectionLink.suffixCSSClass}}
@suffixValue={{sectionLink.suffixValue}}
@suffixType={{sectionLink.suffixType}}
/>
{{/each}}
{{#if this.isDesktopDropdownMode}}
{{#each this.moreSectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
{{#each this.moreSecondarySectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
{{else}}
<Sidebar::MoreSectionLinks
@sectionLinks={{this.moreSectionLinks}}
@secondarySectionLinks={{this.moreSecondarySectionLinks}}
/>
{{/if}}
</Sidebar::Section>

View File

@ -1,104 +0,0 @@
import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import { tracked } from "@glimmer/tracking";
import {
customSectionLinks,
secondaryCustomSectionLinks,
} from "discourse/lib/sidebar/custom-community-section-links";
export default class SidebarCommunitySection extends Component {
@service router;
@service topicTrackingState;
@service currentUser;
@service appEvents;
@service site;
@service siteSettings;
@tracked sectionLinks;
@tracked moreSectionLinks;
@tracked moreSecondarySectionLinks;
callbackId;
headerActionsIcon;
headerActions;
constructor() {
super(...arguments);
this.moreSectionLinks = this.#initializeSectionLinks(
[...this.defaultMoreSectionLinks, ...customSectionLinks],
{ inMoreDrawer: true }
);
this.moreSecondarySectionLinks = this.#initializeSectionLinks(
[
...this.defaultMoreSecondarySectionLinks,
...secondaryCustomSectionLinks,
],
{ inMoreDrawer: true }
);
this.sectionLinks = this.#initializeSectionLinks(
this.defaultMainSectionLinks,
{ inMoreDrawer: false }
);
this.callbackId = this.topicTrackingState.onStateChange(() => {
this.sectionLinks.forEach((sectionLink) => {
sectionLink.onTopicTrackingStateChange();
});
});
}
willDestroy() {
[
...this.sectionLinks,
...this.moreSectionLinks,
...this.moreSecondarySectionLinks,
].forEach((sectionLink) => {
sectionLink.teardown?.();
});
this.topicTrackingState.offStateChange(this.callbackId);
}
// Override in child
get defaultMainSectionLinks() {
return [];
}
// Override in child
get defaultMoreSectionLinks() {
return [];
}
// Override in child
get defaultMoreSecondarySectionLinks() {
return [];
}
get isDesktopDropdownMode() {
const headerDropdownMode =
this.siteSettings.navigation_menu === "header dropdown";
return !this.site.mobileView && headerDropdownMode;
}
#initializeSectionLinks(sectionLinkClasses, { inMoreDrawer } = {}) {
return sectionLinkClasses.map((sectionLinkClass) => {
return this.#initializeSectionLink(sectionLinkClass, inMoreDrawer);
});
}
#initializeSectionLink(sectionLinkClass, inMoreDrawer) {
return new sectionLinkClass({
topicTrackingState: this.topicTrackingState,
currentUser: this.currentUser,
appEvents: this.appEvents,
router: this.router,
siteSettings: this.siteSettings,
inMoreDrawer,
});
}
}

View File

@ -3,14 +3,12 @@
<div class="sidebar-footer-actions">
<PluginOutlet @name="sidebar-footer-actions" />
{{#if this.currentUser.custom_sidebar_sections_enabled}}
<DButton
@icon="plus"
@action={{action this.addSection}}
@class="btn-flat add-section"
@title="sidebar.sections.custom.add"
/>
{{/if}}
<DButton
@icon="plus"
@action={{action this.addSection}}
@class="btn-flat add-section"
@title="sidebar.sections.custom.add"
/>
{{#if
(or

View File

@ -27,14 +27,6 @@
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</div>
{{#if (gt this.secondarySectionLinks.length 0)}}
<div class="sidebar-more-section-links-details-content-secondary">
{{#each this.secondarySectionLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
</div>
{{/if}}
</div>
</div>
{{/if}}

View File

@ -12,8 +12,6 @@ export default class SidebarMoreSectionLinks extends Component {
@tracked shouldDisplaySectionLinks = false;
@tracked activeSectionLink;
#allLinks = [...this.args.sectionLinks, ...this.args.secondarySectionLinks];
constructor() {
super(...arguments);
this.#setActiveSectionLink();
@ -92,7 +90,7 @@ export default class SidebarMoreSectionLinks extends Component {
}
#setActiveSectionLink() {
const activeSectionLink = this.#allLinks.find((sectionLink) => {
const activeSectionLink = this.args.sectionLinks.find((sectionLink) => {
const args = [sectionLink.route];
if (sectionLink.model) {

View File

@ -1,77 +0,0 @@
import I18n from "I18n";
import Composer from "discourse/models/composer";
import { getOwner } from "discourse-common/lib/get-owner";
import PermissionType from "discourse/models/permission-type";
import EverythingSectionLink from "discourse/lib/sidebar/common/community-section/everything-section-link";
import MyPostsSectionLink from "discourse/lib/sidebar/user/community-section/my-posts-section-link";
import GroupsSectionLink from "discourse/lib/sidebar/common/community-section/groups-section-link";
import UsersSectionLink from "discourse/lib/sidebar/common/community-section/users-section-link";
import AboutSectionLink from "discourse/lib/sidebar/common/community-section/about-section-link";
import FAQSectionLink from "discourse/lib/sidebar/common/community-section/faq-section-link";
import AdminSectionLink from "discourse/lib/sidebar/user/community-section/admin-section-link";
import BadgesSectionLink from "discourse/lib/sidebar/common/community-section/badges-section-link";
import ReviewSectionLink from "discourse/lib/sidebar/user/community-section/review-section-link";
import SidebarCommonCommunitySection from "discourse/components/sidebar/common/community-section";
import { action } from "@ember/object";
import { next } from "@ember/runloop";
import { inject as service } from "@ember/service";
export default class SidebarUserCommunitySection extends SidebarCommonCommunitySection {
@service composer;
constructor() {
super(...arguments);
this.headerActionsIcon = "plus";
this.headerActions = [
{
action: this.composeTopic,
title: I18n.t("sidebar.sections.community.header_action_title"),
},
];
}
get defaultMainSectionLinks() {
return [
EverythingSectionLink,
MyPostsSectionLink,
AdminSectionLink,
ReviewSectionLink,
];
}
get defaultMoreSectionLinks() {
return [
GroupsSectionLink,
UsersSectionLink,
BadgesSectionLink,
ReviewSectionLink,
];
}
get defaultMoreSecondarySectionLinks() {
return [AboutSectionLink, FAQSectionLink];
}
@action
composeTopic() {
const composerArgs = {
action: Composer.CREATE_TOPIC,
draftKey: Composer.NEW_TOPIC_KEY,
};
const controller = getOwner(this).lookup("controller:navigation/category");
const category = controller.category;
if (category && category.permission === PermissionType.FULL) {
composerArgs.categoryId = category.id;
}
next(() => {
this.composer.open(composerArgs);
});
}
}

View File

@ -5,42 +5,67 @@
@headerLinkText={{section.decoratedTitle}}
@collapsable={{true}}
@headerActions={{section.headerActions}}
@headerActionsIcon="pencil-alt"
@headerActionsIcon={{section.headerActionIcon}}
@class={{section.dragCss}}
>
{{#each section.links as |link|}}
{{#if link.external}}
<Sidebar::SectionLink
@linkName={{link.name}}
@content={{replace-emoji link.name}}
@prefixType="icon"
@prefixValue={{link.icon}}
@href={{link.value}}
@class={{link.linkDragCss}}
{{draggable
didStartDrag=link.didStartDrag
didEndDrag=link.didEndDrag
dragMove=link.dragMove
}}
/>
{{else}}
<Sidebar::SectionLink
@linkName={{link.name}}
@route={{link.route}}
@models={{link.models}}
@query={{link.query}}
@content={{replace-emoji link.name}}
@prefixType="icon"
@prefixValue={{link.icon}}
@class={{link.linkDragCss}}
{{draggable
didStartDrag=link.didStartDrag
didEndDrag=link.didEndDrag
dragMove=link.dragMove
}}
/>
{{#if link.shouldDisplay}}
{{#if link.external}}
<Sidebar::SectionLink
@linkName={{link.name}}
@content={{replace-emoji link.text}}
@prefixType="icon"
@prefixValue={{link.prefixValue}}
@href={{link.value}}
@class={{link.linkDragCss}}
{{draggable
didStartDrag=link.didStartDrag
didEndDrag=link.didEndDrag
dragMove=link.dragMove
}}
/>
{{else}}
<Sidebar::SectionLink
@shouldDisplay={{link.shouldDisplay}}
@href={{link.href}}
@title={{link.title}}
@linkName={{link.name}}
@route={{link.route}}
@model={{link.model}}
@models={{link.models}}
@query={{link.query}}
@content={{replace-emoji link.text}}
@badgeText={{link.badgeText}}
@prefixType="icon"
@prefixValue={{link.prefixValue}}
@suffixCSSClass={{link.suffixCSSClass}}
@suffixValue={{link.suffixValue}}
@suffixType={{link.suffixType}}
@currentWhen={{link.currentWhen}}
@class={{link.linkDragCss}}
{{(if
link.didStartDrag
(modifier
"draggable"
didStartDrag=link.didStartDrag
didEndDrag=link.didEndDrag
dragMove=link.dragMove
)
)}}
/>
{{/if}}
{{/if}}
{{/each}}
{{#if this.isDesktopDropdownMode}}
{{#each section.moreLinks as |sectionLink|}}
<Sidebar::MoreSectionLink @sectionLink={{sectionLink}} />
{{/each}}
{{else}}
{{#if section.moreLinks}}
<Sidebar::MoreSectionLinks @sectionLinks={{section.moreLinks}} />
{{/if}}
{{/if}}
</Sidebar::Section>
{{/each}}
</div>

View File

@ -2,12 +2,18 @@ import Component from "@glimmer/component";
import { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { bind } from "discourse-common/utils/decorators";
import Section from "discourse/components/sidebar/user/section";
import { cached } from "@glimmer/tracking";
import Section from "discourse/lib/sidebar/section";
import CommunitySection from "discourse/lib/sidebar/community-section";
export default class SidebarUserCustomSections extends Component {
@service currentUser;
@service router;
@service messageBus;
@service appEvents;
@service topicTrackingState;
@service site;
@service siteSettings;
constructor() {
super(...arguments);
@ -16,18 +22,43 @@ export default class SidebarUserCustomSections extends Component {
willDestroy() {
this.messageBus.unsubscribe("/refresh-sidebar-sections");
return this.sections.forEach((section) => {
section.teardown?.();
});
}
@cached
get sections() {
return this.currentUser.sidebarSections.map((section) => {
return new Section({
section,
currentUser: this.currentUser,
router: this.router,
});
switch (section.section_type) {
case "community":
const systemSection = new CommunitySection({
section,
currentUser: this.currentUser,
router: this.router,
appEvents: this.appEvents,
topicTrackingState: this.topicTrackingState,
siteSettings: this.siteSettings,
});
return systemSection;
break;
default:
return new Section({
section,
currentUser: this.currentUser,
router: this.router,
});
}
});
}
get isDesktopDropdownMode() {
const headerDropdownMode =
this.siteSettings.navigation_menu === "header dropdown";
return !this.site.mobileView && headerDropdownMode;
}
@bind
_refresh() {
return ajax("/sidebar_sections.json", {}).then((json) => {

View File

@ -1,8 +1,5 @@
<div class="sidebar-sections">
<Sidebar::User::CommunitySection @collapsable={{@collapsableSections}} />
{{#if this.currentUser.custom_sidebar_sections_enabled}}
<Sidebar::User::CustomSections />
{{/if}}
<Sidebar::User::CustomSections />
<Sidebar::User::CategoriesSection @collapsable={{@collapsableSections}} />
{{#if this.currentUser.display_sidebar_tags}}

View File

@ -0,0 +1,169 @@
import I18n from "I18n";
import SectionLink from "discourse/lib/sidebar/section-link";
import Composer from "discourse/models/composer";
import { getOwner } from "discourse-common/lib/get-owner";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { next } from "@ember/runloop";
import PermissionType from "discourse/models/permission-type";
import EverythingSectionLink from "discourse/lib/sidebar/common/community-section/everything-section-link";
import MyPostsSectionLink from "discourse/lib/sidebar/user/community-section/my-posts-section-link";
import AdminSectionLink from "discourse/lib/sidebar/user/community-section/admin-section-link";
import AboutSectionLink from "discourse/lib/sidebar/common/community-section/about-section-link";
import FAQSectionLink from "discourse/lib/sidebar/common/community-section/faq-section-link";
import UsersSectionLink from "discourse/lib/sidebar/common/community-section/users-section-link";
import GroupsSectionLink from "discourse/lib/sidebar/common/community-section/groups-section-link";
import BadgesSectionLink from "discourse/lib/sidebar/common/community-section/badges-section-link";
import ReviewSectionLink from "discourse/lib/sidebar/user/community-section/review-section-link";
import {
customSectionLinks,
secondaryCustomSectionLinks,
} from "discourse/lib/sidebar/custom-community-section-links";
const LINKS_IN_BOTH_SEGMENTS = ["/review"];
const SPECIAL_LINKS_MAP = {
"/latest": EverythingSectionLink,
"/new": EverythingSectionLink,
"/about": AboutSectionLink,
"/u": UsersSectionLink,
"/faq": FAQSectionLink,
"/my/activity": MyPostsSectionLink,
"/review": ReviewSectionLink,
"/badges": BadgesSectionLink,
"/admin": AdminSectionLink,
"/g": GroupsSectionLink,
};
export default class CommunitySection {
@tracked links;
@tracked moreLinks;
constructor({
section,
currentUser,
router,
topicTrackingState,
appEvents,
siteSettings,
}) {
this.section = section;
this.router = router;
this.currentUser = currentUser;
this.slug = section.slug;
this.topicTrackingState = topicTrackingState;
this.appEvents = appEvents;
this.siteSettings = siteSettings;
this.section_type = section.section_type;
this.callbackId = this.topicTrackingState?.onStateChange(() => {
this.links.forEach((link) => {
if (link.onTopicTrackingStateChange) {
link.onTopicTrackingStateChange();
}
});
});
this.apiLinks = customSectionLinks
.concat(secondaryCustomSectionLinks)
.map((link) => this.#initializeSectionLink(link, { inMoreDrawer: true }));
this.links = this.section.links
.filter(
(link) =>
link.segment === "primary" ||
LINKS_IN_BOTH_SEGMENTS.includes(link.value)
)
.map((link) => {
return this.#generateLink(link);
})
.filter((link) => link);
this.moreLinks = this.section.links
.filter(
(link) =>
link.segment === "secondary" ||
LINKS_IN_BOTH_SEGMENTS.includes(link.value)
)
.map((link) => {
return this.#generateLink(link, true);
})
.concat(this.apiLinks)
.filter((link) => link);
}
teardown() {
if (this.callbackId) {
this.topicTrackingState.offStateChange(this.callbackId);
}
[...this.links, ...this.moreLinks].forEach((sectionLink) => {
sectionLink.teardown?.();
});
}
#generateLink(link, inMoreDrawer = false) {
const sectionLinkClass = SPECIAL_LINKS_MAP[link.value];
if (sectionLinkClass) {
return this.#initializeSectionLink(sectionLinkClass, inMoreDrawer);
} else {
return new SectionLink(link, this, this.router);
}
}
#initializeSectionLink(sectionLinkClass, inMoreDrawer) {
if (this.router.isDestroying) {
return;
}
return new sectionLinkClass({
topicTrackingState: this.topicTrackingState,
currentUser: this.currentUser,
appEvents: this.appEvents,
router: this.router,
siteSettings: this.siteSettings,
inMoreDrawer,
});
}
get displayShortSiteDescription() {
return !this.currentUser && !!this.siteSettings.short_site_description;
}
get decoratedTitle() {
return I18n.t(
`sidebar.sections.${this.section.title.toLowerCase()}.header_link_text`
);
}
get headerActions() {
if (this.currentUser) {
return [
{
action: this.composeTopic,
title: I18n.t("sidebar.sections.community.header_action_title"),
},
];
}
}
get headerActionIcon() {
return "plus";
}
@action
composeTopic() {
const composerArgs = {
action: Composer.CREATE_TOPIC,
draftKey: Composer.NEW_TOPIC_KEY,
};
const controller = getOwner(this).lookup("controller:navigation/category");
const category = controller.category;
if (category && category.permission === PermissionType.FULL) {
composerArgs.categoryId = category.id;
}
next(() => {
getOwner(this).lookup("controller:composer").open(composerArgs);
});
}
}

View File

@ -11,9 +11,10 @@ export default class SectionLink {
constructor({ external, icon, id, name, value }, section, router) {
this.external = external;
this.icon = icon;
this.prefixValue = icon;
this.id = id;
this.name = name;
this.text = name;
this.value = value;
this.section = section;
@ -25,6 +26,10 @@ export default class SectionLink {
}
}
get shouldDisplay() {
return true;
}
@bind
didStartDrag(event) {
// 0 represents left button of the mouse

View File

@ -2,7 +2,7 @@ import I18n from "I18n";
import showModal from "discourse/lib/show-modal";
import { iconHTML } from "discourse-common/lib/icon-library";
import { htmlSafe } from "@ember/template";
import SectionLink from "discourse/components/sidebar/user/section-link";
import SectionLink from "discourse/lib/sidebar/section-link";
import { tracked } from "@glimmer/tracking";
import { bind } from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax";
@ -41,6 +41,10 @@ export default class Section {
}
}
get headerActionIcon() {
return "pencil-alt";
}
@bind
disable() {
this.dragCss = "disabled";

View File

@ -7,13 +7,19 @@ import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sideb
const USER_DRAFTS_CHANGED_EVENT = "user-drafts:changed";
export default class MyPostsSectionLink extends BaseSectionLink {
@tracked draftCount = this.currentUser.draft_count;
@tracked draftCount = this.currentUser?.draft_count;
@tracked hideCount =
this.currentUser?.sidebarListDestination !== UNREAD_LIST_DESTINATION;
constructor() {
super(...arguments);
this.appEvents.on(USER_DRAFTS_CHANGED_EVENT, this, this._updateDraftCount);
if (this.shouldDisplay) {
this.appEvents.on(
USER_DRAFTS_CHANGED_EVENT,
this,
this._updateDraftCount
);
}
}
teardown() {
@ -97,4 +103,8 @@ export default class MyPostsSectionLink extends BaseSectionLink {
return "circle";
}
}
get shouldDisplay() {
return this.currentUser;
}
}

View File

@ -12,19 +12,26 @@ export default class ReviewSectionLink extends BaseSectionLink {
super(...arguments);
this._refreshCanDisplay();
this.appEvents.on("user-reviewable-count:changed", this._refreshCanDisplay);
if (this.shouldDisplay) {
this.appEvents.on(
"user-reviewable-count:changed",
this._refreshCanDisplay
);
}
}
teardown() {
this.appEvents.off(
"user-reviewable-count:changed",
this._refreshCanDisplay
);
if (this.shouldDisplay) {
this.appEvents.off(
"user-reviewable-count:changed",
this._refreshCanDisplay
);
}
}
@bind
_refreshCanDisplay() {
if (!this.currentUser.can_review) {
if (!this.currentUser?.can_review) {
this.canDisplay = false;
return;
}

View File

@ -8,9 +8,13 @@ acceptance("Opengraph Tag Updater", function (needs) {
return helper.response({});
});
});
needs.site({});
test("updates OG title and URL", async function (assert) {
await visit("/");
await click(
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
);
await click("a[href='/about']");
assert.strictEqual(

View File

@ -15,6 +15,7 @@ acceptance("Sidebar - Anonymous user - Community Section", function (needs) {
navigation_menu: "sidebar",
faq_url: "https://discourse.org",
});
needs.site({});
test("display short site description site setting when it is set", async function (assert) {
this.siteSettings.short_site_description =
@ -29,19 +30,9 @@ acceptance("Sidebar - Anonymous user - Community Section", function (needs) {
this.siteSettings.short_site_description,
"displays the short site description under the community section"
);
const sectionLinks = queryAll(
".sidebar-section[data-section-name='community'] .sidebar-section-link"
);
assert.strictEqual(
sectionLinks[0].textContent.trim(),
I18n.t("sidebar.sections.community.links.about.content"),
"displays the about section link first"
);
});
test("everything, users, about and FAQ section links are shown by default ", async function (assert) {
test("everything section link is shown by default ", async function (assert) {
await visit("/");
const sectionLinks = queryAll(
@ -53,24 +44,6 @@ acceptance("Sidebar - Anonymous user - Community Section", function (needs) {
I18n.t("sidebar.sections.community.links.everything.content"),
"displays the everything section link first"
);
assert.strictEqual(
sectionLinks[1].textContent.trim(),
I18n.t("sidebar.sections.community.links.users.content"),
"displays the users section link second"
);
assert.strictEqual(
sectionLinks[2].textContent.trim(),
I18n.t("sidebar.sections.community.links.about.content"),
"displays the about section link third"
);
assert.strictEqual(
sectionLinks[3].textContent.trim(),
I18n.t("sidebar.sections.community.links.faq.content"),
"displays the FAQ section link last"
);
});
test("users section link is not shown when hide_user_profiles_from_public site setting is enabled", async function (assert) {
@ -86,7 +59,7 @@ acceptance("Sidebar - Anonymous user - Community Section", function (needs) {
);
});
test("groups and badges section links are shown in more...", async function (assert) {
test("users, about, faq, groups and badges section links are shown in more...", async function (assert) {
await visit("/");
await click(
@ -99,12 +72,30 @@ acceptance("Sidebar - Anonymous user - Community Section", function (needs) {
assert.strictEqual(
sectionLinks[0].textContent.trim(),
I18n.t("sidebar.sections.community.links.users.content"),
"displays the users section link second"
);
assert.strictEqual(
sectionLinks[1].textContent.trim(),
I18n.t("sidebar.sections.community.links.about.content"),
"displays the about section link third"
);
assert.strictEqual(
sectionLinks[2].textContent.trim(),
I18n.t("sidebar.sections.community.links.faq.content"),
"displays the FAQ section link last"
);
assert.strictEqual(
sectionLinks[3].textContent.trim(),
I18n.t("sidebar.sections.community.links.groups.content"),
"displays the groups section link first"
);
assert.strictEqual(
sectionLinks[1].textContent.trim(),
sectionLinks[4].textContent.trim(),
I18n.t("sidebar.sections.community.links.badges.content"),
"displays the badges section link second"
);

View File

@ -15,7 +15,7 @@ import { UNREAD_LIST_DESTINATION } from "discourse/controllers/preferences/sideb
import { bind } from "discourse-common/utils/decorators";
acceptance("Sidebar - Plugin API", function (needs) {
needs.user();
needs.user({});
needs.settings({
navigation_menu: "sidebar",
@ -439,7 +439,7 @@ acceptance("Sidebar - Plugin API", function (needs) {
);
const myCustomTopSectionLink = query(
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-content-secondary .sidebar-section-link[data-link-name='my-custom-top']"
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-content-main .sidebar-section-link[data-link-name='my-custom-top']"
);
assert.ok(

View File

@ -72,7 +72,7 @@ acceptance(
acceptance(
"Sidebar - Experimental sidebar and hamburger setting enabled - Sidebar enabled",
function (needs) {
needs.user();
needs.user({});
needs.settings({
navigation_menu: "sidebar",

View File

@ -50,6 +50,88 @@ export default {
skip_new_user_tips: false,
should_be_redirected_to_top: false,
},
sidebar_sections: [
{
id: 111,
title: "Community",
section_type: "community",
slug: "community",
links: [
{
id: 329,
name: "Everything",
value: "/latest",
icon: "layer-group",
external: false,
segment: "primary",
},
{
id: 330,
name: "Users",
value: "/u",
icon: "users",
external: false,
segment: "secondary",
},
{
id: 331,
name: "Info",
value: "/about",
icon: "info-circle",
external: false,
segment: "secondary",
},
{
id: 332,
name: "Faq",
value: "/faq",
icon: "question-circle",
external: false,
segment: "secondary",
},
{
id: 333,
name: "My Posts",
value: "/my/activity",
icon: "user",
external: false,
segment: "primary",
},
{
id: 334,
name: "Review",
value: "/review",
icon: "flag",
external: false,
segment: "secondary",
},
{
id: 335,
name: "Admin",
value: "/admin",
icon: "wrench",
external: false,
segment: "primary",
},
{
id: 336,
name: "Groups",
value: "/g",
icon: "user-friends",
external: false,
segment: "secondary",
},
{
id: 337,
name: "Badges",
value: "/badges",
icon: "certificate",
external: false,
segment: "secondary",
},
],
},
]
},
},
};

View File

@ -699,7 +699,90 @@ export default {
],
displayed_about_plugin_stat_groups: ["chat_messages"],
hashtag_configurations: { "topic-composer": ["category", "tag"] },
hashtag_icons: ["folder", "tag"]
hashtag_icons: ["folder", "tag"],
anonymous_sidebar_sections: [
{
id: 111,
title: "Community",
links: [
{
id: 329,
name: "Everything",
value: "/latest",
icon: "layer-group",
external: false,
segment: "primary",
},
{
id: 330,
name: "Users",
value: "/u",
icon: "users",
external: false,
segment: "secondary",
},
{
id: 331,
name: "Info",
value: "/about",
icon: "info-circle",
external: false,
segment: "secondary",
},
{
id: 332,
name: "Faq",
value: "/faq",
icon: "question-circle",
external: false,
segment: "secondary",
},
{
id: 333,
name: "My Posts",
value: "/my/activity",
icon: "user",
external: false,
segment: "primary",
},
{
id: 334,
name: "Review",
value: "/review",
icon: "flag",
external: false,
segment: "secondary",
},
{
id: 335,
name: "Admin",
value: "/admin",
icon: "wrench",
external: false,
segment: "primary",
},
{
id: 336,
name: "Groups",
value: "/g",
icon: "user-friends",
external: false,
segment: "secondary",
},
{
id: 337,
name: "Badges",
value: "/badges",
icon: "certificate",
external: false,
segment: "secondary",
},
],
slug: "community",
public: true,
section_type: "community",
},
],
},
},
};

View File

@ -47,8 +47,4 @@
padding: 0.33rem calc(var(--d-sidebar-row-horizontal-padding) / 3);
}
}
.sidebar-more-section-links-details-content-secondary {
border-top: 1.5px solid var(--primary-low);
}
}

View File

@ -2,7 +2,6 @@
class SidebarSectionsController < ApplicationController
requires_login
before_action :check_if_member_of_group
before_action :check_access_if_public
def index
@ -20,11 +19,7 @@ class SidebarSectionsController < ApplicationController
if sidebar_section.public?
StaffActionLogger.new(current_user).log_create_public_sidebar_section(sidebar_section)
MessageBus.publish(
"/refresh-sidebar-sections",
nil,
group_ids: SiteSetting.enable_custom_sidebar_sections_map,
)
MessageBus.publish("/refresh-sidebar-sections", nil)
Site.clear_anon_cache!
end
@ -44,11 +39,7 @@ class SidebarSectionsController < ApplicationController
if sidebar_section.public?
StaffActionLogger.new(current_user).log_update_public_sidebar_section(sidebar_section)
MessageBus.publish(
"/refresh-sidebar-sections",
nil,
group_ids: SiteSetting.enable_custom_sidebar_sections_map,
)
MessageBus.publish("/refresh-sidebar-sections", nil)
Site.clear_anon_cache!
end
@ -86,11 +77,7 @@ class SidebarSectionsController < ApplicationController
if sidebar_section.public?
StaffActionLogger.new(current_user).log_destroy_public_sidebar_section(sidebar_section)
MessageBus.publish(
"/refresh-sidebar-sections",
nil,
group_ids: SiteSetting.enable_custom_sidebar_sections_map,
)
MessageBus.publish("/refresh-sidebar-sections", nil)
end
render json: SidebarSectionSerializer.new(sidebar_section)
rescue Discourse::InvalidAccess
@ -111,14 +98,6 @@ class SidebarSectionsController < ApplicationController
params.permit(:sidebar_section_id, links_order: [])
end
def check_if_member_of_group
### TODO remove when enable_custom_sidebar_sections SiteSetting is removed
if !SiteSetting.enable_custom_sidebar_sections.present? ||
!current_user.in_any_groups?(SiteSetting.enable_custom_sidebar_sections_map)
raise Discourse::InvalidAccess
end
end
private
def check_access_if_public

View File

@ -24,6 +24,7 @@ class SidebarSection < ActiveRecord::Base
}
scope :public_sections, -> { where("public") }
enum :section_type, { community: 0 }, scopes: false, suffix: true
private
@ -36,14 +37,16 @@ end
#
# Table name: sidebar_sections
#
# id :bigint not null, primary key
# user_id :integer not null
# title :string(30) not null
# created_at :datetime not null
# updated_at :datetime not null
# public :boolean default(FALSE), not null
# id :bigint not null, primary key
# user_id :integer not null
# title :string(30) not null
# created_at :datetime not null
# updated_at :datetime not null
# public :boolean default(FALSE), not null
# section_type :integer
#
# Indexes
#
# index_sidebar_sections_on_section_type (section_type) UNIQUE
# index_sidebar_sections_on_user_id_and_title (user_id,title) UNIQUE
#

View File

@ -5,6 +5,17 @@ class SidebarUrl < ActiveRecord::Base
MAX_ICON_LENGTH = 40
MAX_NAME_LENGTH = 80
MAX_VALUE_LENGTH = 200
COMMUNITY_SECTION_LINKS = [
{ name: "Everything", path: "/latest", icon: "layer-group", segment: "primary" },
{ name: "My Posts", path: "/my/activity", icon: "user", segment: "primary" },
{ name: "Review", path: "/review", icon: "flag", segment: "primary" },
{ name: "Admin", path: "/admin", icon: "wrench", segment: "primary" },
{ name: "Users", path: "/u", icon: "users", segment: "secondary" },
{ name: "About", path: "/about", icon: "info-circle", segment: "secondary" },
{ name: "FAQ", path: "/faq", icon: "question-circle", segment: "secondary" },
{ name: "Groups", path: "/g", icon: "user-friends", segment: "secondary" },
{ name: "Badges", path: "/badges", icon: "certificate", segment: "secondary" },
]
validates :icon, presence: true, length: { maximum: MAX_ICON_LENGTH }
validates :name, presence: true, length: { maximum: MAX_NAME_LENGTH }
@ -14,6 +25,8 @@ class SidebarUrl < ActiveRecord::Base
before_validation :remove_internal_hostname, :set_external
enum :segment, { primary: 0, secondary: 1 }, scopes: false, suffix: true
def path_validator
return true if !external?
raise ActionController::RoutingError.new("Not Found") if value !~ Discourse::Utils::URI_REGEXP
@ -48,4 +61,5 @@ end
# updated_at :datetime not null
# icon :string(40) not null
# external :boolean default(FALSE), not null
# segment :integer default("primary"), not null
#

View File

@ -69,7 +69,6 @@ class CurrentUserSerializer < BasicUserSerializer
:sidebar_category_ids,
:sidebar_list_destination,
:sidebar_sections,
:custom_sidebar_sections_enabled,
:new_new_view_enabled?
delegate :user_stat, to: :object, private: true
@ -82,7 +81,7 @@ class CurrentUserSerializer < BasicUserSerializer
.public_sections
.or(SidebarSection.where(user_id: object.id))
.includes(sidebar_section_links: :linkable)
.order("(public IS TRUE) DESC")
.order("(section_type IS NOT NULL) DESC, (public IS TRUE) DESC")
.map { |section| SidebarSectionSerializer.new(section, root: false) }
end
@ -301,12 +300,4 @@ class CurrentUserSerializer < BasicUserSerializer
def include_new_personal_messages_notifications_count?
redesigned_user_menu_enabled
end
def custom_sidebar_sections_enabled
if SiteSetting.enable_custom_sidebar_sections.present?
object.in_any_groups?(SiteSetting.enable_custom_sidebar_sections_map)
else
false
end
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class SidebarSectionSerializer < ApplicationSerializer
attributes :id, :title, :links, :slug, :public
attributes :id, :title, :links, :slug, :public, :section_type
def links
object.sidebar_section_links.map { |link| SidebarUrlSerializer.new(link.linkable, root: false) }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class SidebarUrlSerializer < ApplicationSerializer
attributes :id, :name, :value, :icon, :external
attributes :id, :name, :value, :icon, :external, :segment
def external
object.external? || object.full_reload?

View File

@ -266,6 +266,7 @@ class SiteSerializer < ApplicationSerializer
SidebarSection
.public_sections
.includes(sidebar_section_links: :linkable)
.order("(section_type IS NOT NULL) DESC, (public IS TRUE) DESC")
.map { |section| SidebarSectionSerializer.new(section, root: false) }
end

View File

@ -907,7 +907,6 @@ bg:
default_categories_muted: "Списък с категории, които са заглушени, по подразбиране."
create_post_for_category_and_tag_changes: "Създайте малка публикация за действие, когато категорията или етикетите на тема се променят"
experimental_new_new_view_groups: "ЕКСПЕРИМЕНТАЛНО: Активирайте нов списък с теми, който комбинира непрочетени и нови теми и направете връзката „Всичко“ в страничната лента връзка към него."
enable_custom_sidebar_sections: "ЕКСПЕРИМЕНТАЛНО: Активирайте персонализирани раздели в страничната лента"
errors:
invalid_email: "Невалиден имейл адрес."
invalid_username: "Няма потребител с такова потребителско име."

View File

@ -2162,7 +2162,6 @@ de:
enable_new_notifications_menu: "Aktiviert das neue Benachrichtigungsmenü. Das Deaktivieren dieser Einstellung ist veraltet. Das neue Benachrichtigungsmenü wird immer für Optionen im Navigationsmenü verwendet, die nicht aus den älteren Versionen stammen. <a href='https://meta.discourse.org/t/260358'>Erfahren Sie mehr</a>"
enable_experimental_hashtag_autocomplete: "EXPERIMENTELL: Das neue #hashtag-Autovervollständigungssystem für Kategorien und Schlagwörter, das das ausgewählte Element anders darstellt und eine bessere Suche aufweist, verwenden"
experimental_new_new_view_groups: "EXPERIMENTELL: Aktiviere eine neue Themenliste, die ungelesene und neue Themen kombiniert, und verlinke den Link „Alles“ in der Seitenleiste darauf."
enable_custom_sidebar_sections: "EXPERIMENTELL: Aktiviere benutzerdefinierte Seitenleistenabschnitte"
errors:
invalid_css_color: "Ungültige Farbe. Gib einen Farbnamen oder einen Hexadezimalwert ein."
invalid_email: "Ungültige E-Mail-Adresse."

View File

@ -2269,7 +2269,6 @@ he:
enable_new_notifications_menu: "מפעיל את תפריט ההתראות החדש. השבתת ההגדרה הזאת יצאה מכלל שימוש. תפריט ההתראות החדש תמיד ישמש לבחירות ‚תפריט ניווט’ שאינן מיושנות. <a href='https://meta.discourse.org/t/260358'>מידע נוסף</a>"
enable_experimental_hashtag_autocomplete: "ניסיוני: ניתן להשתמש במערכת אוטומטית להשלמת #תגיות עבור קטגוריות ותגיות שמעבדת את הפריט הנבחר באופן שונה והחיפוש בה משופר"
experimental_new_new_view_groups: "ניסיוני: לאפשר רשימת נושאים חדשה המשלבת נושאים שלא נקראו וחדשים ולקשר אליה את „הכול” בסרגל הצד."
enable_custom_sidebar_sections: "ניסיוני: הפעלת אגפים משלך בסרגל הצד"
errors:
invalid_css_color: "צבע שגוי. נא למלא את שם הצבע או ערך הקסדצימלי."
invalid_email: "כתובת דוא״ל שגויה."

View File

@ -1034,7 +1034,6 @@ hu:
navigation_menu: "Határozza meg, melyik navigációs menüt használja. Az oldalsáv és a fejléc navigációja a felhasználók által testreszabható. A visszamenőleges kompatibilitás érdekében a régebbi opció is elérhető."
enable_experimental_hashtag_autocomplete: "KÍSÉRLETI: Használja az új #hashtag automatikus kiegészítési rendszert a kategóriákhoz és címkékhez, amelyek másképp jelenítik meg a kiválasztott elemet, és javítják a keresést"
experimental_new_new_view_groups: "Kísérleti: Engedélyezzen egy új témalistát, amely egyesíti az olvasatlan és az új témákat, és az oldalsávban lévő \"Minden\" linket kapcsolja rá."
enable_custom_sidebar_sections: "Kísérleti: Egyéni oldalsáv szekciók engedélyezése"
errors:
invalid_email: "Érvénytelen e-mail cím."
invalid_username: "Nincs ilyen nevű felhasználó."

View File

@ -2162,7 +2162,6 @@ sv:
enable_new_notifications_menu: "Aktiverar den nya aviseringsmenyn. Inaktivering av den här inställningen är föråldrat. Den nya aviseringsmenyn används alltid för icke-äldre \"navigeringsmeny\"-val. <a href='https://meta.discourse.org/t/260358'>Lär dig mer</a>"
enable_experimental_hashtag_autocomplete: "EXPERIMENTELLT: Använd det nya #hashtag-autokompletteringssystemet för kategorier och taggar som renderar det valda objektet annorlunda och har förbättrad sökning"
experimental_new_new_view_groups: "EXPERIMENTELLT: Aktivera en ny ämneslista som kombinerar olästa och nya ämnen och gör att länken \"Allt\" i sidofältet länkar till den."
enable_custom_sidebar_sections: "EXPERIMENTELLT: Aktivera anpassade sidofältssektioner"
errors:
invalid_css_color: "Ogiltig färg. Ange ett färgnamn eller ett hexvärde."
invalid_email: "Felaktig e-postadress."

View File

@ -2135,7 +2135,6 @@ tr_TR:
default_sidebar_tags: "Seçilen etiketler varsayılan olarak Kenar Çubuğunun Etiketler bölümünde gösterilir."
enable_experimental_hashtag_autocomplete: "DENEYSEL: Seçilen ögeyi farklı şekilde işleyen ve aramayı iyileştiren kategoriler ve etiketler için yeni #hashtag otomatik tamamlama sistemini kullanın"
experimental_new_new_view_groups: "DENEYSEL: Okunmamış ve yeni konuları birleştiren bir yeni konular listesi etkinleştirin ve kenar çubuğundaki \"Her şey\" bağlantısını buna bağlayın."
enable_custom_sidebar_sections: "DENEYSEL: Özel kenar çubuğu bölümlerini etkinleştir"
errors:
invalid_css_color: "Geçersiz renk. Bir renk adı veya hex değeri girin."
invalid_email: "Geçersiz e-posta adresi."

View File

@ -2106,7 +2106,6 @@ zh_CN:
enable_new_notifications_menu: "启用新的通知菜单。已不能禁用此设置。新的通知菜单始终用于非传统“导航菜单”选项。 <a href='https://meta.discourse.org/t/260358'>了解更多</a>"
enable_experimental_hashtag_autocomplete: "实验性:对类别和标签使用新的 #hashtag 自动补全系统,以不同方式呈现所选条目,并改进了搜索"
experimental_new_new_view_groups: "试验性的:启用一个新的主题列表,将未读和新的主题合并,并使侧边栏中的 \"一切 \"链接指向它。"
enable_custom_sidebar_sections: "实验:启用自定义侧边栏部分"
errors:
invalid_css_color: "颜色无效。输入颜色名称或十六进制值。"
invalid_email: "无效的电子邮件地址。"

View File

@ -2128,13 +2128,6 @@ navigation:
- "unread_new"
enable_new_notifications_menu:
default: true
enable_custom_sidebar_sections:
client: true
type: group_list
list_type: compact
default: ""
allow_any: false
refresh: true
embedding:
embed_by_username:

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddSegmentToSidebarUrls < ActiveRecord::Migration[7.0]
def change
add_column :sidebar_urls, :segment, :integer, default: 0, null: false
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
class AddSectionTypeToSidebarSections < ActiveRecord::Migration[7.0]
def change
add_column :sidebar_sections, :section_type, :integer
add_index :sidebar_sections, :section_type, unique: true
end
end

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
class InsertCommunityToSidebarSections < ActiveRecord::Migration[7.0]
COMMUNITY_SECTION_LINKS = [
{ name: "Everything", path: "/latest", icon: "layer-group", segment: 0 },
{ name: "My Posts", path: "/my/activity", icon: "user", segment: 0 },
{ name: "Review", path: "/review", icon: "flag", segment: 0 },
{ name: "Admin", path: "/admin", icon: "wrench", segment: 0 },
{ name: "Users", path: "/u", icon: "users", segment: 1 },
{ name: "About", path: "/about", icon: "info-circle", segment: 1 },
{ name: "FAQ", path: "/faq", icon: "question-circle", segment: 1 },
{ name: "Groups", path: "/g", icon: "user-friends", segment: 1 },
{ name: "Badges", path: "/badges", icon: "certificate", segment: 1 },
]
def up
result = DB.query <<~SQL
INSERT INTO sidebar_sections(user_id, title, public, section_type, created_at, updated_at)
VALUES (-1, 'Community', true, 0, now(), now())
RETURNING sidebar_sections.id
SQL
community_section_id = result.last&.id
sidebar_urls =
COMMUNITY_SECTION_LINKS.map do |url_data|
"('#{url_data[:name]}', '#{url_data[:path]}', '#{url_data[:icon]}', '#{url_data[:segment]}', false, now(), now())"
end
result = DB.query <<~SQL
INSERT INTO sidebar_urls(name, value, icon, segment, external, created_at, updated_at)
VALUES #{sidebar_urls.join(",")}
RETURNING sidebar_urls.id
SQL
sidebar_section_links =
result.map.with_index do |url, index|
"(-1, #{url.id}, 'SidebarUrl', #{community_section_id}, #{index}, now(), now())"
end
result = DB.query <<~SQL
INSERT INTO sidebar_section_links(user_id, linkable_id, linkable_type, sidebar_section_id, position, created_at, updated_at)
VALUES #{sidebar_section_links.join(",")}
SQL
end
def down
result = DB.query <<~SQL
DELETE FROM sidebar_sections
WHERE section_type = 0
RETURNING sidebar_sections.id
SQL
community_section_id = result.last&.id
return true if !community_section_id
result = DB.query <<~SQL
DELETE FROM sidebar_section_links
WHERE sidebar_section_id = #{community_section_id}
RETURNING sidebar_section_links.linkable_id
SQL
sidebar_url_ids = result.map(&:linkable_id)
DB.query <<~SQL
DELETE FROM sidebar_urls
WHERE id IN (#{sidebar_url_ids.join(",")})
SQL
end
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class RemoveEnableCustomSidebarSectionsSetting < ActiveRecord::Migration[7.0]
def up
execute "DELETE FROM site_settings WHERE name = 'enable_custom_sidebar_sections'"
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@ -51,8 +51,8 @@ RSpec.describe Category do
expect { category_sidebar_section_link.linkable.destroy! }.to change {
SidebarSectionLink.count
}.from(3).to(1)
expect(SidebarSectionLink.first).to eq(tag_sidebar_section_link)
}.from(12).to(10)
expect(SidebarSectionLink.last).to eq(tag_sidebar_section_link)
end
end

View File

@ -29,8 +29,8 @@ RSpec.describe Tag do
expect { tag_sidebar_section_link.linkable.destroy! }.to change {
SidebarSectionLink.count
}.from(3).to(1)
expect(SidebarSectionLink.first).to eq(category_sidebar_section_link)
}.from(12).to(10)
expect(SidebarSectionLink.last).to eq(category_sidebar_section_link)
end
end

View File

@ -131,7 +131,7 @@ RSpec.describe User do
it "should not create any sidebar section link records for staged users" do
user = Fabricate(:user, staged: true)
expect(SidebarSectionLink.exists?).to eq(false)
expect(SidebarSectionLink.exists?(user: user)).to eq(false)
end
it "should create sidebar section link records when user has been unstaged" do
@ -144,7 +144,7 @@ RSpec.describe User do
it "should not create any sidebar section link records for non human users" do
user = Fabricate(:user, id: -Time.now.to_i)
expect(SidebarSectionLink.exists?).to eq(false)
expect(SidebarSectionLink.exists?(user: user)).to eq(false)
end
it "should not create any tag sidebar section link records when tagging is disabled" do

View File

@ -4,14 +4,6 @@ RSpec.describe SidebarSectionsController do
fab!(:user) { Fabricate(:user) }
fab!(:admin) { Fabricate(:admin) }
before do
### TODO remove when enable_custom_sidebar_sections SiteSetting is removed
group = Fabricate(:group)
Fabricate(:group_user, group: group, user: user)
Fabricate(:group_user, group: group, user: admin)
SiteSetting.enable_custom_sidebar_sections = group.id.to_s
end
describe "#index" do
fab!(:sidebar_section) { Fabricate(:sidebar_section, title: "private section", user: user) }
fab!(:sidebar_url_1) { Fabricate(:sidebar_url, name: "tags", value: "/tags") }
@ -29,7 +21,7 @@ RSpec.describe SidebarSectionsController do
get "/sidebar_sections.json"
expect(response.status).to eq(200)
expect(response.parsed_body["sidebar_sections"].map { |section| section["title"] }).to eq(
["public section", "private section"],
["Community", "public section", "private section"],
)
end
end
@ -49,6 +41,8 @@ RSpec.describe SidebarSectionsController do
it "creates custom section for user" do
sign_in(user)
expect(SidebarSection.count).to eq(1)
post "/sidebar_sections.json",
params: {
title: "custom section",
@ -66,7 +60,7 @@ RSpec.describe SidebarSectionsController do
expect(response.status).to eq(200)
expect(SidebarSection.count).to eq(1)
expect(SidebarSection.count).to eq(2)
sidebar_section = SidebarSection.last
expect(sidebar_section.title).to eq("custom section")

View File

@ -303,11 +303,6 @@ RSpec.describe CurrentUserSerializer do
fab!(:group) { Fabricate(:group) }
fab!(:sidebar_section) { Fabricate(:sidebar_section, user: user) }
before do
group.add(user)
SiteSetting.enable_custom_sidebar_sections = group.id
end
it "eager loads sidebar_urls" do
custom_sidebar_section_link_1 =
Fabricate(:custom_sidebar_section_link, user: user, sidebar_section: sidebar_section)
@ -319,11 +314,9 @@ RSpec.describe CurrentUserSerializer do
track_sql_queries do
serialized = described_class.new(user, scope: Guardian.new(user), root: false).as_json
expect(serialized[:sidebar_sections].map { |sidebar_section| sidebar_section.id }).to eq(
[sidebar_section.id],
)
expect(serialized[:sidebar_sections].count).to eq(2)
expect(serialized[:sidebar_sections].first.links.map { |link| link.id }).to eq(
expect(serialized[:sidebar_sections].last.links.map { |link| link.id }).to eq(
[custom_sidebar_section_link_1.linkable.id],
)
end.count
@ -335,11 +328,9 @@ RSpec.describe CurrentUserSerializer do
track_sql_queries do
serialized = described_class.new(user, scope: Guardian.new(user), root: false).as_json
expect(serialized[:sidebar_sections].map { |sidebar_section| sidebar_section.id }).to eq(
[sidebar_section.id],
)
expect(serialized[:sidebar_sections].count).to eq(2)
expect(serialized[:sidebar_sections].first.links.map { |link| link.id }).to eq(
expect(serialized[:sidebar_sections].last.links.map { |link| link.id }).to eq(
[custom_sidebar_section_link_1.linkable.id, custom_sidebar_section_link_2.linkable.id],
)
end.count

View File

@ -209,7 +209,9 @@ RSpec.describe SiteSerializer do
it "includes only public sidebar sections serialised object when user is anonymous" do
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
expect(serialized[:anonymous_sidebar_sections].map(&:title)).to eq(["Public section"])
expect(serialized[:anonymous_sidebar_sections].map(&:title)).to eq(
["Community", "Public section"],
)
end
it "eager loads sidebar_urls" do
@ -222,11 +224,9 @@ RSpec.describe SiteSerializer do
track_sql_queries do
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
expect(
serialized[:anonymous_sidebar_sections].map { |sidebar_section| sidebar_section.id },
).to eq([public_sidebar_section.id])
expect(serialized[:anonymous_sidebar_sections].count).to eq(2)
expect(serialized[:anonymous_sidebar_sections].first.links.map { |link| link.id }).to eq(
expect(serialized[:anonymous_sidebar_sections].last.links.map { |link| link.id }).to eq(
[public_section_link.linkable.id],
)
end.count
@ -240,11 +240,9 @@ RSpec.describe SiteSerializer do
track_sql_queries do
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
expect(
serialized[:anonymous_sidebar_sections].map { |sidebar_section| sidebar_section.id },
).to eq([public_sidebar_section.id])
expect(serialized[:anonymous_sidebar_sections].count).to eq(2)
expect(serialized[:anonymous_sidebar_sections].first.links.map { |link| link.id }).to eq(
expect(serialized[:anonymous_sidebar_sections].last.links.map { |link| link.id }).to eq(
[
public_section_link.linkable.id,
public_section_link_2.linkable.id,

View File

@ -6,14 +6,6 @@ describe "Custom sidebar sections", type: :system, js: true do
let(:section_modal) { PageObjects::Modals::SidebarSectionForm.new }
let(:sidebar) { PageObjects::Components::Sidebar.new }
before do
### TODO remove when enable_custom_sidebar_sections SiteSetting is removed
group = Fabricate(:group)
Fabricate(:group_user, group: group, user: user)
Fabricate(:group_user, group: group, user: admin)
SiteSetting.enable_custom_sidebar_sections = group.id.to_s
end
it "allows the user to create custom section" do
sign_in user
visit("/latest")
@ -112,11 +104,11 @@ describe "Custom sidebar sections", type: :system, js: true do
sign_in user
visit("/latest")
within(".sidebar-custom-sections .sidebar-section-link-wrapper:nth-child(1)") do
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(1)") do
expect(sidebar).to have_section_link("Sidebar Tags")
end
within(".sidebar-custom-sections .sidebar-section-link-wrapper:nth-child(2)") do
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(2)") do
expect(sidebar).to have_section_link("Sidebar Categories")
end
@ -124,11 +116,11 @@ describe "Custom sidebar sections", type: :system, js: true do
categories_link = find(".sidebar-section-link[data-link-name='Sidebar Categories']")
tags_link.drag_to(categories_link, html5: true, delay: 0.4)
within(".sidebar-custom-sections .sidebar-section-link-wrapper:nth-child(1)") do
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(1)") do
expect(sidebar).to have_section_link("Sidebar Categories")
end
within(".sidebar-custom-sections .sidebar-section-link-wrapper:nth-child(2)") do
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(2)") do
expect(sidebar).to have_section_link("Sidebar Tags")
end
end