Instantly share code, notes, and snippets.
-
Save ftc2/c2e23fab9cfd3ff14972972f459e9c4e to your computer and use it in GitHub Desktop.
add/remove discourse sidebar links
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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