Skip to content

Developer FAQ

25 Topics 161 Posts

What is the Developer FAQ?

This category is meant to showcase developer questions that have been answered in-depth by other forum users. We collect these topics in this category because they have been asked multiple times, and the answers to them are sometimes more complicated than a single line or two of explanation/code.

Do you have an idea for a topic we should include in the Developer FAQ? Let us know via chat (or reply to the topic you'd like to see migrated here).

  • NodeBB + ReactJS

    Moved
    11
    1 Votes
    11 Posts
    2k Views
    crazycellsC

    @Matvey good job 👍 any plan to make a "kanban board" plugin? 🙂 that would be useful for us... we could leave trello behind...

  • Why does nodebb check dependencies programmatically?

    Moved Solved
    5
    0 Votes
    5 Posts
    101 Views
    o              oO

    Yeah thanks guys, the key was copying the package.json file out of the install folder (copying, not moving, because install/package.json is then checked during build). Now everything can be installed in one command.

  • 4 Votes
    11 Posts
    871 Views
    barisB

    Translations are pulled regularly into the develop branch so whenever the next minor version is release ie 3.6.0 they become available. See https://community.nodebb.org/topic/17189/how-do-i-retrieve-the-latest-translations-from-transifex-without-waiting-for-misty for manual commands

  • How do I create a registration interstitial?

    Moved Solved
    15
    0 Votes
    15 Posts
    594 Views
    S

    @julian said in How do I create a registration interstitial?:

    The hook is fired on POST /register so that the user can be redirected to the interstitial during the registration process. So you do need to have it respond there in order for the flow to work.

    Interesting, the flow still seems to work perfectly (the custom interstitials do show up on redirect to /register/complete) with the following code right at the beginning of the hook handler:

    const url = data.req.originalUrl; if (!url.startsWith("/register/complete")) { return data; }

    How is that possible?

  • Total vote count on topic list

    Moved
    8
    1 Votes
    8 Posts
    540 Views
    phenomlabP

    @crazycells said in Total vote count on topic list:

    but this calculation makes more sense to us than the first post only.

    And for me.

  • Threaded Discussions

    Moved Solved
    2
    0 Votes
    2 Posts
    358 Views
    barisB

    See https://github.com/NodeBB/NodeBB/issues/7319#issuecomment-487588369

  • How do I add custom user fields to my theme?

    1
    1 Votes
    1 Posts
    176 Views
    julianJ

    If you have a custom theme that introduces custom user fields into the edit page, you might have noticed that the fields don't seem to show up on the frontend even if they're saved in the database.
    Likewise, if you have some custom data you want to maintain on a per-user basis, and you tried using user.updateProfile, you might've noticed that your fields don't seem to save.

    The reason for this behaviour is that the the user fields getter and setter are protected so that values in the user hash are not unintentionally overwritten, or that fields are retrieved that should not be.

    For example:

    nodebb-plugin-foobar saves a private user token in the user's hash. It is not meant to be retrieved. Another plugin calls user.getUsersFields() and that method naively returns everything in the user hash. The private key would then be considered leaked if it is accidentally exposed to the end user, even if unintentionally.

    Getter (retrieving user data)

    If you've saved a custom field into the user hash and you wish to retrieve it via User.getUsersFields(), you will have to explicitly whitelist it by attaching a hook listener to filter:user.whitelistFields. That plugin hook sends in { uids, whitelist }, where uids is an array of uids (requested by the calling script), and whitelist, which is an array containing user field properties.

    You can add a new entry to the whitelist thusly:

    plugin.json

    { ... "hooks": [ { "hook": "filter:user.whitelistFields", "method": "addUserField" }, ] ... }

    library.js

    library.addUserField = async ({ uids, whitelist }) => { whitelist.push('customField'); return { uids, whitelist }; };

    After doing so, a call to user.getUsersFields(uids, ['customField']); will have the customField property show up as it has been explicitly allowed.

    Setter (saving user data)

    Setting user data is comparatively simpler. We recommend using the user.updateProfile() method, as that has some sanity checks and special handling for certain fields. To allow the saving of a certain field, you will need to pass it in to the third argument of user.updateProfile():

    await User.updateProfile(callerUid, { uid, customField: 'value', }, ['customField']);
  • 0 Votes
    7 Posts
    410 Views
    julianJ

    @шЫкель-грубый said in How can I backdate topics and posts (for migration purposes)?:

    I'll also try to comment date changing in sources. I'm not familiar with JS but it looks obviously enough.

    Yes, this is probably the most direct solution. It'll get your script working with the correct timestamps, all you have to do is comment out that line in the file, and restart NodeBB.

  • 1 Votes
    2 Posts
    767 Views
    Irfan BabarI

    thank you for this.

  • 1 Votes
    1 Posts
    134 Views
    julianJ

    Your plugin may want to expose some user-specific options, and to accomplish that, you'll want to create a page accessible from within their user profile.

    In Harmony, plugin-added pages are added to the left-hand sidebar:
    ce07dc7d-e8f3-45a9-ac73-72f1f7309af2-image.png

    In Persona, plugin-added pages are behind the overflow menu:
    af0633f5-175d-45ff-802b-342a0885dd68-image.png

    You'll need to add listeners to two hooks, and modify your page template accordingly.

    static:app.load

    Live example

    You'll need to specify a route in the user profile using this hook. In it, note the accountMiddlewares block, which contains some common middlewares that are sensible defaults. You'll need middleware.buildAccountData in order to retrieve some boilerplate data that all account routes need.

    filter:user.profileMenu

    Live example

    You'll then want to specify the menu option, including label, icon, and visibility options. visibility allows you to specify which users can see the option (e.g. self only, admins only, etc.)

    The template

    In your template for the user page, you'll want to prefix it with <!-- IMPORT partials/account/header.tpl --> and suffix it with <!-- IMPORT partials/account/footer.tpl -->. These two lines will wrap your template content with the theme-specific structure. For example, in Harmony, a sidebar is used in the accounts pages. The header and footer partials will ensure they are also present in your template. The middleware.buildAccountData middleware you added in the first step will ensure the data necessary is present.

  • Place a widget/ad after the first post

    Moved Solved
    4
    0 Votes
    4 Posts
    329 Views
    D

    Awesome! Thank you.

  • How to add main post content of topic to topic lists?

    Moved Solved
    3
    0 Votes
    3 Posts
    646 Views
    nullpointerN

    @baris thank you! I'll try your solution and update here

  • 3 Votes
    1 Posts
    160 Views
    julianJ

    Let's say you have your own site and member database, with its own gated access to content, and you want to mimic this sort of arrangement with your forum.

    e.g. On member site, you have free users and paid users, and you want to only allow access to a couple super special categories on community site to paid users, while free users get the regular set of categories.

    The good news is, the Session Sharing plugin has had support for this since 2018!

    You'll want to enable the options in the session sharing plugin:

    2022-11-15_09-49.png

    You can opt to only add users to groups, only remove users from groups, or both. You can also specify which groups that the automatic group syncing applies to

    Once enabled, you need only update your shared cookie to contain a groups property, which is of type Array. This array contains the group names in NodeBB that the user should be a part of.

    If group leaving is enabled, then the user will be removed from any groups that are not in this array, upon login/revalidation.

    Once you have your group memberships sorted out, you'll want to restrict access to specific categories based on user group membership.

    You can do that from the ACP > Manage > Privileges page. Simply remove access for the registered-users groups from your choice of categories, and grant those viewing/posting privileges to the paid users group.

  • 1 Votes
    2 Posts
    576 Views
    omegaO

    Cool and for context a link to the upgrade doc section - https://docs.nodebb.org/configuring/upgrade/

  • Plugin DB Search - Limitation?

    Moved
    10
    0 Votes
    10 Posts
    834 Views
    N

    I would really like to get this working.
    Does anyone have any thoughts on why this code above might not work?
    The board was converted from phpbb using a plugin. Is it possible that left something behind that is breaking the code shared above?

  • 0 Votes
    2 Posts
    402 Views
    julianJ

    @stevefan1999-personal Considerations when scaling NodeBB are detailed here: https://docs.nodebb.org/configuring/scaling/

    ... but possibly not in the most digestable way. In summary, here's what you need:

    Redis configured in config.json so it will be used as pubsub and for storing user sessions (volatile data) One of the workers needs isPrimary set to true in config.json, conversely, the other instances need jobsDisabled set to false For socket.io, you will need to make sure to use a ip hashing strategy when routing requests from the load balancer. A typical round-robin style means the socket.io handshake will fail since it ends up hitting different servers.
  • 0 Votes
    4 Posts
    660 Views
    Adrian Lukas SteinA

    Okay mate thanks for the info, working fine now thanks a lot 😄

  • 0 Votes
    2 Posts
    522 Views
    julianJ

    I'm assuming you're referring to the chart in the main admin control panel?

    01782223-bf0e-457d-89dc-4b87b7ec44c6-image.png

    The two main analytics tracked are pageviews and unique visitors.

    Page views are fairly self-explanatory, one page load or transition to a new page counts as a new page view. Unique visitors is based on the IP address, and so every time a new IP address shows up in that hour, the count is incremented by one. We also track unique visitors on a daily, weekly, monthly, and all-time basis, for the numbers here: aafa1cf9-e038-4c67-b2ba-6e9f7edaad33-image.png The "registered", "guest", and "bot" variants for pageviews increment alongside each pageview, but depending on whether the user is logged in (or not), or whether that request came from a bot. "registered" and "guest" should add up to the total pageviews, since you're either registered or not. "bots" are usually also guests, but we use some basic logic to detect whether they're a bot or not (probably user-agent string) For a variety of reasons, the "bot" count is not always predictable, since some bots are rather obscure, or fudge their user-agent strings, so take this number with a grain of salt.

    I hope that answers your questions, let me know if there's anything else you'd like to know.

  • 1 Votes
    1 Posts
    345 Views
    julianJ

    This topic was created as an entry in the Developer FAQ. Respond below if you have additional information to add re: SSO or other session-sharing implementations.

    The common causes for a session mismatch error are usually one of the following:

    1. Mis-configured URL parameter in your config.json file

    If you have a misconfigured url value in your config.json file, the cookie may be saved incorrectly (or not at all), causing a session mismatch error. Please ensure that the link you are accessing your site with and the url defined match.

    2. Improper/malformed cookieDomain set in ACP

    Sometimes admins set this value realising that they probably don't need to set it at all. The default is perfectly fine. If this is set, you'll want to revert the setting by editing your database directly:

    Redis: hdel config cookieDomain
    MongoDB: db.objects.update({ _key: "config" }, { $set: { "cookieDomain": "" } });

    3. Missing X-Forwarded-Proto header from nginx/apache

    If you are using a reverse proxy, you will need to have nginx pass a header through to NodeBB so it correctly determines the correct cookie secure property.

    In nginx, you will need to add the directive like so:

    location / { ... proxy_set_header X-Forwarded-Proto $scheme; ... }
  • 1 Votes
    1 Posts
    353 Views
    julianJ

    This topic was created as an entry in the Developer FAQ. Respond below if you have additional information to add re: SSO or other session-sharing implementations.

    The recommended method of sharing sessions between two separate and distinct applications is through OAuth2. We recommend this approach because NodeBB maintains its own user records, so that we can keep track of user-related metrics and other data. Relying on another database would be tricky, prone to breaking, and quite possibly dangerous.

    Luckily, it's quite straightforward to get things working with OAuth2!

    The first step is getting your application to expose an OAuth2 endpoint. If you're running a Node.js based app, you can use a module called OAuth2orize.

    Once that is set up, you'll want to take a look at the SSO plugin skeleton for customised OAuth deployments -- nodebb-plugin-sso-oauth. You'll take this plugin, fork it, and modify it to communicate with your OAuth endpoint.

    Once everything is working properly, you should be able to register and log in/out via your web app.