Get topic id when the topic show page loads

In my theme, when a topic show page is loaded, I need to get the id of that topic (and name too, if possible, but id would be sufficient).

I then use this information in my theme to make an API call to get information about the topic (with a call to an endpoint like forum-name/t/topic-name/id.json…)

How can I get the id of the topic when the topic show page loads?

The obvious methods are not sufficient

The obvious solution is window.location.href or window.location.pathname to get info from the url. The problem is that the URL for the topic show page can take different forms. Often, it would be:
forum-name/t/topic-name/topic-id

If that was always the form, then using window.location.pathname would work well–giving me “t/topic-name/topic-id”.

BUT, sometimes the topic show page url appends the topic’s category order too, so the url is: forum-name/t/topic-name/topic-id/category-index

So using window.location.pathname is insufficient, because I don’t know programmatically whether the last param will be the topic-id or the category-index.

There may be a way to solve this using regex, but that requires some serious regex skill. There may also be a way to solve this with jquery by looking at elements on the page that might give the id–but I haven’t gotten that to work yet.

When/where does that happen? I couldn’t find it.

Anyhow, when this topic loads it has a link element:

<link rel="canonical" href="https://meta.discourse.org/t/get-topic-id-when-the-topic-show-page-loads/155620" />

Maybe you can grab it from there. :slight_smile:

On my forum it actually happens most of the time when you click on a topic. The last bit of the URL will be the number in the category that the topic is, if you’ve clicked on the topic from a category. Perhaps that’s a setting in discourse? It doesn’t seem to happen on meta-, but I don’t think there’s anything special I’m doing to cause that to happen on my forum.

I can’t always rely on this happening however, because if a topic is the first topic in the category, it is not append the category index. So as far as I can see there’s not a good way right now for me to tell whether it’s going to appear in the category index or not.

Getting the ID from a div is OK, but unfortunately quite a bit slower than just being able to get it directly from the Url or some other direct means. In case there is not another solution though, do you know the Jquery to get the value of that Href?

Try

api.onPageChange((url, title) => {
    var res = url.match(/\/t\/(.*?)\/(\w+)/);
    if (res && res[2] > 0) {
        console.log(res[2]);
    }
});
2 Likes

What are you trying to achieve?

This might work but it’s not very efficient since you’re making an extra AJAX for every topic page view for every user - including anons. So this will create a lot of unnecessary load on your server.

Not really. The URL for a topic looks like this

your.site.com/t/topic_title/topic_id/linked_post_number(optional)

There’s no category order in the URL in a default Discourse installation.

We can help you if you describe the problem you’re trying to solve instead of describing your solutions for it.

3 Likes

This works! Thanks very much. I understand that with the match method here, you are going through the url to get, I assume, the 3rd occurrence of the “/”, bc the id will always happen after the third “/” in the url, which returns the form “/t/name/id/otherstuff”. Can you provide a little info on how you’re regex does this? It would be very helpful in my regex journey.

Thanks for the info. So it’s the “linked_post_number” that seems to show up some times and throws off my API call. You say here it is “optional”–is there a way to be sure it is never shown?

When a user visits the topic show page, I want to:

  1. Programmatically know all the tags associated with that topic. Note that some tags are hidden from the user’s view.
  2. Have a button on the topic page that adds a certain hidden tag to the topic when clicked (if the hidden tag is not there yet), and removes the hidden tag when clicked (if the hidden tag is already there).

This is all straightforward using the Admin API and javascript/jquery (assuming I can get the right topic url to use in the API calls).

I believe the only other way to do this type of thing would be to create a plugin where I get deep into 1. ember, 2. rails and 3. the discourse code base. I’ve reviewed the key discourse posts and docs on how to do this, but I’ve found it slow going bc you really need to understand these 3 pieces. So for now I’ve focused on the API approach.

I’d be interested to hear if there’s another way to do this that would decrease server load.

I could have most certainly helped you with the code if it was a single step solution, but there are multiple steps involved. If you need some help in the form of consultation regarding getting work done or learning to write theme components yourself, please post on marketplace and I’ll reach out.

Thanks, but it’s helpful to get any guidance on any step. For example, other than using the API, how would I know all the tags associated with a topic?

1 Like

A full blown topic object is loaded on the client side when you load the topic page. Now if you want to use it, you need to add your own code to the template to use that data, or reopen classes to utilize the loaded data(i.e. basically creating your own computed props and utilizing them on the template). It has most of the data you need(more than you need probably) loaded on the client side without extra API calls. You also need plugin-outlets to put your own markup on existing templates.

Now it would help if you looked through the codebase/meta for the terms I used above. I’ll be happy to help here if you get stuck in the best of my capacity timewise. Cheers.

Thanks. I’m familiar with some of the basics you mention, but there’s one point that gets me stuck (and I bet this kind of thing gets a lot of people stuck):

On the topic show page, it loads the template /templates/components/topic-category.hbs. That’s what displays the category and also the tag beneath the topic title.

In topic-category, it lists out topic.tags. So that’s the key piece of info I need to get this going.

Here’s where I am stuck: How can I get that topic.tags information to my javascript?

For example, if I just wanted to console.log the contents of topic.tags, how would I do that?


I know how to override templates. For example, in a theme, I could put in a file at discourse/templates/components/topic-category.hbs, and reprint the template there, adding in changes I wanted to make to the view. (I’m using the separate file structure described here).

In my theme, I also know how to put javascript at theme/initializers/initializer-file.js.es6.

And I can make the two interact with some jquery. For example, I could put the topic.tags into a div in the template, and access that in my initializer with jquery by getting the content of that div.

But that’s round about. How can I get that topic.tags info directly so I can parse it and manipulate it?

1 Like

I would console log the whole topic and find which key is required. For jQuery interactions, they are and should be done from within components. didInsertElement is the component hook which is useful here.

By the sound of it, seems to me that you need a computed prop depending on topic.tags returning your manipulation of it on the controller/component in question. Its usable directly in the template. Also, don’t give up on this stuff. It’ll take time but definitely pays off in better ways than you could ask for.

What do you mean by “within components”–what file do I use for the jquery code? (in my theme, I can use jquery in the theme/intializers/intializer-file.js.es6 file, but that does not seem to be “within” the component)

In what file does the didInsertElement code go?

1 Like

At this point you should really look at the Ember guides. A component is a package of functionality you can deploy and attach to other things.

2 Likes

Thanks, guys, this is all helpful info. I am investigating the guides and general docs. It would also be very helpful to know what is the name of the file where I should put the (i) jquery code and (ii) didInsertElement code. Having that kind of concrete info will be a big help in guiding further review.

1 Like

Components = Ember components.

You can reopen all kinds of ember classes and inject your code into them. You’ll have to lookup plugins/theme which do so to get a better idea.

@JQ331 As you probably know, I found regexs on google and tried them here! You can enter variations to get the best results.


1 Like

This looks like plugin territory to me.

If the tags are hidden to the normal user, how would an additional call help? The serialiizer should in any case be hiding those tags from the normal user. If not then you are exposing a content security hole.

If you are going to all this trouble to workaround the limitations of front-end only changes I would suggest you bite the bullet and look at building this into a plugin where you could serialise in one go instead of having a messy setup with additional calls.

1 Like

That makes sense. I’d like to learn more about using the serializer–while the serializer is one of the most important steps in a discourse plugin, I haven’t yet found documentation that lays out some basic examples of using it. Do you know of that documentation?

When you say a serializer should be hiding those tags, could you provide some psuedo-code to give a sense of what you mean? (fine for it not to totally work–I’m just wrapping my head around the concept)

Here:

https://www.rubydoc.info/gems/active_model_serializers/0.8.2/ActiveModel/Serializer

This is just like Jazz Music … the best practices are usually already out there in the existing open-source plugin landscape, just like all the records. Don’t expect a perfect guide on how to do everything, you have to get into the swing (ahem) of learning from prior art much of the time.

Find a plugin from #plugin that does something functionally similar, or an element of what you want to do and inspect the code, see how it’s achieving things. Same goes for the Discourse source code, which you can use as a source of ultimate ‘best practice’, esp. wrt Discourse.

Clone an existing plugin locally and try to change a few things, experiment.

Serializers are just filtering what is sent from the Controllers/Models and they can do some basic manipulation too.

1 Like

Here’s one example from the source: https://github.com/discourse/discourse/blob/888e68a1637ca784a7bf51a6bbb524dcf7413b13/app/serializers/concerns/topic_tags_mixin.rb

  def tags
    # Calling method `pluck` along with `includes` causing N+1 queries
    tags = topic.tags.map(&:name)

    if scope.is_staff?
      tags
    else
      tags - scope.hidden_tag_names
    end
  end

You can see here it is excluding the hidden tags from non-staff.

2 Likes