Skip to content

Instantly share code, notes, and snippets.

@ftc2
Last active April 16, 2023 23:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ftc2/c2e23fab9cfd3ff14972972f459e9c4e to your computer and use it in GitHub Desktop.
Save ftc2/c2e23fab9cfd3ff14972972f459e9c4e to your computer and use it in GitHub Desktop.
add/remove discourse sidebar links
<script type="text/discourse-plugin" version="0.8">
'use strict';
const sidebarMod = {
/**
* link positioners:
* (bool) prepend, append: prepend/append link to section
* (str) addBeforeKebab, addAfterKebab: add link before/after existing link
* link visibility:
* (str) injectFor: inject link only for 'anon' users, only for 'registered' users, or for 'both' (default: 'both')
*/
injections: [
{ sectionSelector: '.sidebar-section[data-section-name="community"]',
links: [
{ text: 'Prepended For Users', kebab: 'prepended-for-users', title: 'Visible only to registered users', href: 'https://www.google.com', icon: 'far-eye', prepend: true, injectFor: 'registered' },
{ text: 'Prepended For Guests', kebab: 'prepended-for-guests', title: 'Visible only to guests', href: 'https://www.google.com', icon: 'asterisk', prepend: true, injectFor: 'anon' },
{ text: 'Before "My Posts"', kebab: 'before-my-posts', title: 'Injected before "My Posts"', href: 'https://www.google.com', icon: 'arrow-down', addBeforeKebab: 'my-posts' },
{ text: 'After "My Posts"', kebab: 'after-my-posts', title: 'Injected after "My Posts"', href: 'https://www.google.com', icon: 'arrow-up', addAfterKebab: 'my-posts' },
{ text: 'Appended Link', kebab: 'appended-link', title: 'Appended to the bottom of the section', href: 'https://www.google.com', icon: 'angle-double-down', append: true }
]
},
{ sectionSelector: '.sidebar-section[data-section-name="categories"]',
links: [
{ text: 'Categories Prepended', kebab: 'categories-prepended', title: 'A longer description seen when hovering', href: 'https://www.google.com', icon: 'map-marker-alt', prepend: true }
]
}
],
/**
* section options:
* (str) removeFor: remove section only for 'anon' users, only for 'registered' users, or for 'both' (default: 'none')
* (str) removeMore: remove '⋮More' only for 'anon' users, only for 'registered' users, or for 'both' (default: 'none')
* useful if you know it's empty due to link removals
* link removal options:
* (str) removeFor: remove link only for 'anon' users, only for 'registered' users, or for 'both' (default: 'both')
*/
removals: [
{ sectionSelector: '.sidebar-section[data-section-name="community"]',
links: [
{ kebab: 'badges', removeFor: 'registered' }
],
removeMore: 'anon'
},
{ sectionSelector: '.sidebar-custom-sections .sidebar-section[data-section-name="my-topicss"]',
removeFor: 'anon'
}
],
observeSidebar(sidebarEl) {
new MutationObserver((_mutationRecords, _observer) => {
// console.log('sidebarMod: mutated!');
this.injectStuff();
this.removeStuff();
}).observe(sidebarEl, {
childList: true,
subtree: true
});
},
injectStuff() {
// https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/lib/plugin-api.js
const user = api.getCurrentUser();
this.injections.forEach((section) => {
elementReady(section.sectionSelector).then((sectionEl) => {
section.links.forEach((link) => {
if (!(link.injectFor == 'anon' && user) && !(link.injectFor == 'registered' && !user)) {
this.injectLink(link, sectionEl);
}
});
});
});
},
injectLink(link, sectionEl) {
// console.log(`sidebarMod: checking for '${link.kebab}'`);
const linkEl = this.findLink(link.kebab, sectionEl);
if (!linkEl) {
const html = `
<li class="sidebar-section-link-wrapper">
<a class="ember-view sidebar-section-link sidebar-row" title="${link.title}" data-link-name="${link.kebab}" href="${link.href}">
<span class="sidebar-section-link-prefix icon">
<svg class="fa d-icon d-icon-${link.icon} svg-icon prefix-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use href="#${link.icon}"></use></svg>
</span>
<span class="sidebar-section-link-content-text">${link.text}</span>
</a>
</li>`;
if (link.prepend) {
// console.log(`sidebarMod: ***adding '${link.kebab}' (prepending its section)`);
const positionerEl = sectionEl.querySelector('.sidebar-section-link')?.parentElement;
positionerEl?.insertAdjacentHTML('beforebegin', html);
} else if (link.append) {
// console.log(`sidebarMod: ***adding '${link.kebab}' (appending its section)`);
const sectionLinks = sectionEl.querySelectorAll('.sidebar-section-link');
const positionerEl = sectionLinks[sectionLinks.length - 1]?.parentElement;
positionerEl?.insertAdjacentHTML('afterend', html);
} else if (link.addBeforeKebab) {
// console.log(`sidebarMod: ***adding '${link.kebab}' before '${link.addBeforeKebab}'`);
const positionerEl = this.findLink(link.addBeforeKebab, sectionEl);
positionerEl?.insertAdjacentHTML('beforebegin', html);
} else if (link.addAfterKebab) {
// console.log(`sidebarMod: ***adding '${link.kebab}' after '${link.addAfterKebab}'`);
const positionerEl = this.findLink(link.addAfterKebab, sectionEl);
positionerEl?.insertAdjacentHTML('afterend', html);
}
}
},
removeStuff() {
const user = api.getCurrentUser();
this.removals.forEach((section) => {
if (section.removeFor == 'anon' && !user) {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeSection(sectionEl); });
} else if (section.removeFor == 'registered' && user) {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeSection(sectionEl); });
} else if (section.removeFor == 'both') {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeSection(sectionEl); });
}
if (section.removeMore == 'anon' && !user) {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeMore(sectionEl); });
} else if (section.removeMore == 'registered' && user) {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeMore(sectionEl); });
} else if (section.removeMore == 'both') {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeMore(sectionEl); });
}
section.links?.forEach((link) => {
if (link.removeFor == 'anon' && !user) {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeLink(link, sectionEl); });
} else if (link.removeFor == 'registered' && user) {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeLink(link, sectionEl); });
} else if (link.removeFor == 'both') {
elementReady(section.sectionSelector).then((sectionEl) => { this.removeLink(link, sectionEl); });
}
});
});
},
removeSection(sectionEl) {
sectionEl?.remove();
},
removeMore(sectionEl) {
sectionEl?.querySelector('.sidebar-more-section-links-details')?.remove();
},
removeLink(link, sectionEl) {
const linkEl = this.findLink(link.kebab, sectionEl);
linkEl?.remove();
},
findLink(kebab, sectionEl) {
return sectionEl?.querySelector(`.sidebar-section-link[data-link-name="${kebab}"]`)?.parentElement;
}
};
addEventListener('DOMContentLoaded', () => {
// watch for mutations on elements known to exist in ANY state
// desktop
elementReady('.sidebar-wrapper').then(() => { sidebarMod.injectStuff(); });
elementReady('.sidebar-wrapper').then(() => { sidebarMod.removeStuff(); });
elementReady('.sidebar-wrapper').then((sidebarEl) => { sidebarMod.observeSidebar(sidebarEl); });
// mobile
elementReady('.panel[role="navigation"]').then(() => { sidebarMod.injectStuff(); });
elementReady('.panel[role="navigation"]').then(() => { sidebarMod.removeStuff(); });
elementReady('.panel[role="navigation"]').then((sidebarEl) => { sidebarMod.observeSidebar(sidebarEl); });
});
function elementReady(selector) {
// credit: https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
return new Promise((resolve, reject) => {
let el = document.querySelector(selector);
if (el) {
resolve(el);
return
}
new MutationObserver((_mutationRecords, observer) => {
// Query for elements matching the specified selector
Array.from(document.querySelectorAll(selector)).forEach((element) => {
resolve(element);
//Once we have resolved we don't need the observer anymore.
observer.disconnect();
});
})
.observe(document.documentElement, {
childList: true,
subtree: true
});
});
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment