dev: discourse findings/suggestions

Hi there @SYZYGY , I’m glad to hear you got your community migrated over to Discourse as well. Thanks a lot for sharing any tips or insight you have gained from that experience.

Thanks, the custom sidebar sections are a brand new feature in Discourse, and I also have my JS script with a bunch of CSS to customize the Community section as well, which the custom sidebar sections don’t support yet. But a recent Discourse change required a rework of some of my custom CSS tweaks for the sidebar, so at your suggestion I took advantage of the opportunity to add the custom section and then re-implement my Community section tweaks too.

Yep, to avoid too much unnecessary shock for BLF users coming from the old forum I’m not yet doing very much with tags. I did use tags for the old “Reviews…” sub-sub-categories that used to exist for most battery type categories, as Discourse doesn’t really like to have more than 1 level of sub-categories. For that I ran a bunch of rake commands like you mentioned to tag everything that was in a “Reviews…” sub-sub-category with review and then another rake task to move them to the main parent battery type sub-category. Also @tercet sent me a fantastically comprehensive tag vocabulary for everything flashlight related. I haven’t implemented them yet because I’m not sure how consistently users will apply them, in which case the tags might actually have the opposite effect of causing more fragmentation and disorganization. I’m still thinking that one over. Suggestions are of course welcome!

1 Thank

i’ll think about it and see if i can come up with specific suggestions for cats/tags on blf. could you share that stuff @tercet sent?

for background, i was honestly not a fan of the categories on the old site. personal taste, but i thought it was weird that flashlight topics had to be primarily categorized by battery type.

that aside, logically, i really do think it makes sense to use cats only for very broad organizational hierarchy where mutual exclusivity is required (or permissible). and then use tags for everything else.

you already got the users over to the new software. they are living with the shock (barely), lol!

so think of it this way. both there and here, you already enforced sorting requirements with cats. since that was ok, you could do the same thing with tags if you want and gain a little extra flexibility where you can. like you can set a required tag group and make them pick certain kinds of tags.

so if you wanted, you could, for example, have a Flashlights cat. then upon topic creation in there, force them to choose at least one tag from a tag group containing battery types. you could then re-use those tags in the batteries and chargers cat (enforced or not).

note that this example sounds a little contrived and doesn’t illuminate a lot of value, but it just shows how you’re at least not losing anything functionally by changing most/all subcats to tags.

also, random question. do you have a good understanding of watching vs tracking a topic? i googled this some, but i only saw stuff about how people thought it was unclear, but no explanation of what they really are lol.

nice. yeah, use their features where you can. :slight_smile:
when you do that, there’s 0 maintenance burden, and if it breaks, it’s supported (their problem).

i notice that you got a little creative with your /about sections.

i recently went through that, so i noticed.

not sure if that helps or if you knew it already.

but if you set SiteSetting.faq_url, you get an extra about section to work with (5 instead of 4). this would allow you to preserve a ToS page instead of cannibalizing it.

Big +1 on the customizable sidebar feature.

Here’s a quick example with 2 custom sections with links I find useful:

I put “To Do” at the top to remind me to actually do the things I bookmarked.

The “Notifications” link shows only unread notifications, which is handy when something scrolled off the pull-down notifications list. It’s like a secondary todo list.

The “… Replies” part is handy since it lets me see a preview of what the reply contains without marking it as “read”. Same for the “… Likes” link.

The “Topics :: Read” link is something I didn’t discover until today. It’s nice because it shows me every topic I’ve looked at, even if it’s not being watched or tracked, including topics from before the migration to Discourse.

“Polls” isn’t very useful yet, but if tags start getting used more, it’ll be useful then.

I know “Users” doesn’t really fit under “Topics”, but I didn’t want a separate section for it. This menu is just a first draft. :sweat_smile:

In both menus, items are roughly in the order I would check them in… with the most-used items at the top of each section, and the bottom holding links I hardly ever use but still find worth having available.

2 Thanks

good stuff, @toykeeper. i may add some of those queries/views to my nav. thanks.

never knew that either. potentially very useful, especially if you don’t use browser history for whatever reason. i have it disabled because i use firefox with container tabs, and unfortunately history is not (yet?) written to be container-aware (lacks container context in the db schema).

nvm, i’m an idiot. you have it explained in your faq (which is nice btw… i will use it as inspiration for mine!), and i guess the software also explains it clearly if you know where to look – i kept missing it.

speaking of faq… after reflection, here’s an opinion that no one asked for, lol:

enable the split-/faq feature and point it to your faq topic
SiteSetting.faq_url = '/t/217457' (or admin > settings)
I18n.t('js.faq') = 'FAQ' (or admin > customize > text)
I18n.t('') = 'FAQ'

use /tos for BLF Rules + ToS
i see you have kind of a combined rules and ToS thing going on. in this case, i would use the existing /tos route for that instead of commercial sellers. here’s why. that route is already used for other stuff in the code. for example, on the user registration form, it has a link to /tos for new users to agree to.
I18n.t('js.tos') = 'BLF Rules'

use /guidelines as your commercial sellers info:
I18n.t('js.guidelines') = 'Commercial Sellers'

and then leave privacy policy as is.

this would yield:

one final thought. maybe My Topics instead of My Threads for consistent feature language.

Thanks @SYZYGY . Interesting idea. I know I tried messing around with SiteSetting.faq_url but there was some kind of janky behavior with the FAQ / TOS / Privacy buttons on the header of that section when I tried it. I don’t remember exactly what the problem was, but I think it was like swapping them around in a different order or duplicating the BLF Rules and FAQ buttons.

Browser history is very limited, and ironically not very convenient to browse. It typically only goes back about 3 months, which isn’t very useful when I want to see things from several years ago. It’s limited to only the current browser instance on the current user account on the current computer, so it doesn’t show things I saw while using a different browser or different computer. It also includes every URL, perhaps even every time it was visited, instead of showing only a list of forum topics.

I can work around a lot of that. I have some scripts built to send URLs over the network to an archiver daemon, which logs the activity and saves a copy of the page as it appeared at that time. So I can see full browsing history back to the day I built the archiver, and see how those pages looked at the time. But it’s still not particularly easy to browse, and it doesn’t go back far enough to include my first few years on BLF. And with phone browsers being so artificially limited, I haven’t gotten my phone hooked into this system yet.

The Topics Read function is much more convenient and compact, since it gives a pretty simple list of all threads I’ve ever viewed. Very handy for finding all the threads I read or was subscribed to before the migration.

Yep, and when you have the history sorted by visited date and go back to look for a historical page and you click on something to see if that’s the one you were looking for, it moves it to the top of the recently browsed pages, which confuses things even more if that wasn’t the page you were actually looking for.

I added /read to the homepage next to Latest. I just wish English wasn’t so ambiguous… we now have a Read me!! button on the homepage. Or would it mean To be read? :wink: Or maybe I’ll change the string translation to Readed.

I honestly don’t understand where it pulls that history from in the case of the imported Drupal threads. Drupal didn’t save user activity history for more than 12 hours, and the Discourse importer script didn’t import those stats anyway. I’m pretty sure that Discourse infers that you must have read those historical threads because you posted in them. (And it also now remembers if you visited any threads in Discourse, whether you posted in them or not.)

So now that I think about it, maybe /read would be a better option for the My Threads > Replied To list instead of /search?expanded=false&q=in%3Aposted%20order%3Alatest
What do you all think?

I don’t know either, but I discovered it this morning and it seems to do a better job of showing my old subscribed threads than any other method I’ve found. So I put it in my sidebar, and will probably click a few of those old threads periodically when I have time.

But first I have plenty of other stuff to catch up on, like my own topics. For example, my notifications say there are 2068 new replies in “Flashlight Firmware Repository”, so… I’ll get through those before I go digging through other old threads. I’m not actually 2068 posts behind there… it just doesn’t know where I left off pre-migration. And TBH, I don’t either. So I’ll just go through it really quick to mark all the old stuff as “read”, and then catch up on the newer stuff.

Anyway, the /read or /my/activity/read page seems to show all my old subscribed threads in a convenient topic list widget, one row per topic, almost exactly like the old site did. So it seems like a good link to have available.

1 Thank

FWIW, I’ve noticed two minor inconveniences in the custom sidebar links…

  • No GET args parsed after /my/. So if I link to /my/notifications?filter=unread, it strips the ?filter=unread part while redirecting to the incomplete destination page of /u/ToyKeeper/notifications.

  • If I enable Preferences → User Interface → Open all external links in a new tab, many of the sidebar links also open in a new tab. For example, a link of /my/messages opens in a new tab even though it’s not external.

The workarounds are easy: Don’t use “/my/” when GET args are needed, and turn off the external-link option. But in case these are fixable, I thought I should mention it.

1 Thank

Thanks for mentioning that. From what I can tell, the current sidebar code does a full page reload to resolve the /my/ link to /u/ToyKeeper , which is why it opens it in a new tab if that option is enabled, or if it’s disabled you’ll see it refresh the entire app instead of just an AJAX repaint of a part of the viewport. It would be more elegant to allow some kind of a placeholder variable in the link and then use JS to resolve it to the current /u/username . I know the devs say they still have a lot of work to do yet on the sidebar custom sections, so that might eventually be improved, but probably not soon.

1 Thank

/read seems like a good addition to the top navigation tabs:
latest | new | unread | top | read | categories

/read seems less of a good fit in “my threads,” if “my” implies something i’ve actively engaged with: either by writing (created, replied to) or tagging (watching, tracking, bookmarked). “read” is more passive, less possessive (not “my”).

but if you choose to put it in “my threads,” i suggest it be added rather than replace something.

and if you’re still considering a link change for “replied to,” maybe use this one to be consistent with the links for “created” and “bookmarked”:

btw, i can see the benefit of /read. but i personally won’t be using it much because it includes threads which i’ve muted (off-topic chatter i wasn’t interested in) and deferred (which i thought meant setting as not-viewed, but apparently not).

I just noticed another oddity. If I collapse the “Community” sidebar section, some of its links move into another section.

Specifically, the links for “BLF Rules”, “Commercial Sellers”, and “Contact the Admin” move so they’re at the end of my first custom section. Then if I expand Community and reload, the links move back to their original position.

this has been happening in some form since the beginning, where the blf-specific links (rules, sellers, etc) would disappear and reappear. but i couldn’t replicate the error consistently, and chalked it up to sb making configuration changes. more recently, those same links have sometimes appeared under “my threads”—i think(?) this behavior pre-dated the new “add custom section” widget.

i also noticed the “tags” section is no longer in the sidebar.

here’s a current screenshot:

edit - and another one:

Just playing around a bit, the custom sidebar feature makes it possible to organize the sidebar however you want… but the default sections are still there unless you use a stylesheet override. So, if anyone wants to customize it that far, here’s an example:

/* remove default sidebar sections */
foo {
    display: none !important;

/* smaller, more compact sidebar */
.sidebar-wrapper .sidebar-sections {
    padding: 0.5em 0;

.sidebar-row {
    height: unset;
    padding: .2rem 0.66rem;

.sidebar-section-wrapper .sidebar-section-content {
    padding-bottom: 0.5em;

:root {
    --d-sidebar-width: 9em;

And an example of how it looks:

I’ve found some other overrides helpful too, to remove page elements that I don’t use. Like, editor buttons I never click…

/* remove extra buttons in editor */
/* (these take, like, a couple keypresses ... no point having a mouse button for it) */
foo {
    display: none;



True. The thing is, as TK discovered, /read also has the interesting effect of showing older threads from Drupal that a user posted in. But then it will also show threads that the user only viewed within Discourse. So I guess we can leave it as is with the search query for “Replied To” and the “Read” section on the homepage.

@ToyKeeper and @tercet : Regarding the janky behavior of the sidebar items, yes, the code that customizes the sidebar menus is ridiculously complex for what it accomplishes. But I can’t find a better way, and it’s unfortunately necessary. Here’s how the sidebar would look without it:

Experience has shown that many users will never find items that are even slightly hidden under a single level of UI, so I had to move the “FAQ” entry out of the “More” expander and rename it to “BLF Rules”. We also need the “Commercial Sellers” and “Contact the Admin” links to appear front and center sidebar. So I use this monstrosity:


const links = [
    { title: "BLF Rules", src: "/rules", icon: "gavel" },
    { title: "Commercial Sellers", src: "/commercial", icon: "gift" },
    { title: "Contact the Admin", src: "/new-message?username=sb56637", icon: "envelope" }

$(document).ready(function () {
    if (document.getElementById("toggle-hamburger-menu")) {
        // We're in mobile view
    } else {
        // We're in desktop view

    function addToggleListener(toggleEl) {
        if (toggleEl) {
            toggleEl.addEventListener("click", function () {
                // Wait a bit for the sidebar to load
                setTimeout(function() {
                    let sidebar = document.getElementsByClassName("sidebar-section-header").length
                    if (sidebar) {
                }, 100)
    function addCustomLinks() {
        // Wait a bit until navigation has been loaded
        setTimeout(function () {
            const parentEl = document.getElementsByClassName("sidebar-section-content")[0]
            let linksAlreadyAdded = document.getElementsByClassName("sidebar-section-custom-link").length
            if (parentEl && !linksAlreadyAdded) {
                links.filter(function (link) {
                    let linkDiv = document.createElement("li")
                    let linkTitleTrim = link.title.replace(/\s+/g, '')
                    linkDiv.className = "sidebar-section-link-wrapper sidebar-section-custom-link"
                    linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                            <span class="sidebar-section-link-prefix icon" id="link_${linkTitleTrim}"></span>
                            <span class="sidebar-section-link-content-text"> ${link.title} </span>
                    let linkIcon = document.getElementById("link_" + linkTitleTrim)
                    if (linkIcon && link.icon) {
                        linkIcon.innerHTML = `<svg viewBox="0 0 640 512" class="fa d-icon svg-icon prefix-icon svg-string d-icon-${link.icon}" xmlns="">
                                <use xlink:href="#${link.icon}"></use>
        }, 100)
.sidebar-section-message { /* Don't show the site slogan in the Community menu to anon users */
    display: none;
.sidebar-wrapper li a.sidebar-section-link-about {
    display: none;
.sidebar-more-section-links-details-content-secondary .sidebar-section-link.sidebar-section-link-about {
    display: none;
.sidebar-wrapper li a.sidebar-section-link-faq {
    display: none;
.sidebar-more-section-links-details-content-secondary .sidebar-section-link.sidebar-section-link-faq {
    display: none;
.sidebar-section-content {
  display: flex; /* Setup a flex layout so you can reorder things */
  flex-direction: column;
  .sidebar-more-section-links-details {
    order: +1;
.sidebar-section[data-section-name="tags"] {
  display: none
.sidebar-section[data-section-name="categories"] {
  display: flex; /* Setup a flex layout so you can reorder things */
  flex-direction: column;
  order: +1;
.sidebar-custom-sections {
  display: flex; /* Setup a flex layout so you can reorder things */
  flex-direction: column;
  order: +1;

So you can probably figure out from the JS code above why the custom links that I add by default appear and disappear; it’s basically injecting them into an existing sidebar <div>, which only exists when the sidebar is visible. The code includes a kludge to make the custom items reappear after manually hiding/showing the sidebar with the hamburger menu, but it doesn’t handle the case where the sidebar disappears and then reappears when you horizontally shrink and then increase the browser window width. The same goes for hiding/expanding the “Community” menu. Those are the only cases I’m aware of that consistently loses the custom items until the page is hard-reloaded. @tercet do you remember how you got it to do this? Did you add your own custom sidebar menu section?

I suppose that to reduce the jank and the complexity I could hide almost everything in the “Community” section and re-implement those links in another Discourse custom section. It would look something like this:

But the Discourse devs are planning on making the “Community” section customizable at some point in the future, so it might not be worth the effort to perfect it at this stage.

I hid the tags menu because it gets in the way in the case of my admin sidebar, and since there are only 2 tags I didn’t think that anybody would find that section useful. I can restore them if needed though.

2 Thanks

hey @sb56637 are you hosting your own mailserver or using paid service? figuring out email is next on my list. i guess using sendmail isn’t an option for me anymore lol

clear history/cache/cookies, fresh login. in sidebar, collapse all sections except “my threads.” hide sidebar. unhide sidebar. blf-specific links (rules, sellers, admin) appear in “my threads” section.

1 Thank