Skip to content

NodeBB Plugins

Discussion regarding NodeBB Plugin development.

1.8k Topics 15.0k Posts
Most Voted Plugins

Subcategories


  • Have a question about building a plugin? Ask here
    424 Topics
    2k Posts
    barisB

    https://community.nodebb.org/post/88242 I think this answers your question. Let me know if it doesn't work.

  • Need a plugin developed? Ask here!
    222 Topics
    1k Posts
    barisB

    Plugin doesn't show usernames if they set their status to offline AFAIK

  • [nodebb-plugin-adsense] Google Adsense

    82
    6 Votes
    82 Posts
    58k Views
    barisB

    This plugin is updated to work with 3.2.x and up. It also uses widgets now instead of hardcoded ad areas so it is possible to place an ad wherever there is widget area in the theme.

    @omega I've added /ads.txt support into the plugin.
    25311c03-f012-4298-969d-ef0d7b0edc74-image.png

    This only applies if the forum is hosted on the root domain, if you have another CMS on your root domain you need to handle it there. The adsense verification can be done with the custom head tab in the ACP if nodebb is on the root domain.

  • 5 Votes
    26 Posts
    2k Views
    B

    @DownPW change 6M to 150M or 200M

  • nodebb-plugin-topic-noindex

    3
    3 Votes
    3 Posts
    374 Views
    barisB

    @crazycells that's correct

  • 37 Votes
    302 Posts
    246k Views
    barisB

    @Hope33 when you activate shoutbox and rebuild restart it should add a new route at yourforum.com/shoutbox you can also drag and drop it as a widget on a page.

  • Re-render component without refreshing page

    2
    0 Votes
    2 Posts
    156 Views
    barisB

    If you want to re-render something client side you can use

    const html = await app.parseAndTranslate('path/to/template', 'blockName', data);

    Where block name is the name used in the tpl file for a loop like {{{ each topics }}} and then once the render is done you can replace the html with jquery $('#some-target').html(html);.

    If you search core code you will see many uses of app.parseAndTranslate to update the DOM.

  • LDAP Plugin

    30
    0 Votes
    30 Posts
    2k Views
    phenomlabP

    I found this listed in the NPM registry
    https://github.com/Chrischenny/nodebb-plugin-node-ldap-ad

    Can't get it to work though - crashes NodeBB every time.

    EDIT - working now - I had the one amended by @baris still installed, so removed and all good from the stability perspective. Doesn't seem to work with AD though - at least, not for me - investigating.

  • 0 Votes
    22 Posts
    1k Views
    phenomlabP

    @baris Thanks

  • 18 Votes
    356 Posts
    269k Views
    Gliding9426G

    Forgot to mention that if i open that URL from the browser window I get the expected result in the JSON, the response contains

    "isAdmin": true, "isLoggedIn": true,

    It looks like if the browser does the request in the page via the javascript, could not access the cookies and so it gets a "logged out" json (but i tested on clean ms edge with no cookie blocking)

    Ps: i could not edit the other post because of a weird issue, I'm going to open another post to ask if this is intended behavior

  • 7 Votes
    136 Posts
    41k Views
    DownPWD

    @DownPW said in [nodebb-plugin-ns-embed] NS Embed:

    I search dailymotion rules for this plugin.

    I've tested this but doesn't work

    Dailymotion
    Watch

    (?:<a.*?)?(?:https?:\\/\\/)?(?:www\\.)?dailymotion\\.com\\/video\\/([a-zA-Z0-9_-]{4,11})(?:.*?\\/a>)?

    Replace

    <div class='embed-wrapper'><div class='embed-container'><iframe src='//www.dailymotion.com/embed/video/$1' frameborder='0' allowfullscreen></iframe></div></div>

    Anyone ?

  • 0 Votes
    5 Posts
    218 Views
    oplik0O

    If you need to use v1.x, the latest release is v1.19.12
    You can find upgrade instructions here: https://docs.nodebb.org/configuring/upgrade/
    Follow the "via release branches" part and use the v1.x branch.

    However, if you can upgrade to v3 or at least v2 that would probably be preferable, since they'll be supported for longer (v2 will get security fixes until August 2025. source) in addition to offering quite a few new features.

  • This topic is deleted!

    1
    0 Votes
    1 Posts
    30 Views
  • 0 Votes
    6 Posts
    625 Views
    murcsM

    @DownPW said in [nodebb-widget-board-stats] discrepancy to dashboard count:

    any news @murcs ?

    i just digged a little deeper for you: „our“ /var/www/nodebb/node_modules/nodebb-widget-board-stats/library.js looks like this

    'use strict'; const async = require('async'); const nconf = module.parent.require('nconf'); const db = require.main.require('./src/database'); const user = require.main.require('./src/user'); const utils = require.main.require('./src/utils'); const socketPlugins = require.main.require('./src/socket.io/plugins'); const socketRooms = require.main.require('./src/socket.io/admin/rooms'); let app; const Widget = module.exports; Widget.init = function (params, callback) { app = params.app; callback(); }; socketPlugins.boardStats = {}; socketPlugins.boardStats.get = function (socket, tid, callback) { getWidgetData(callback); }; function addDots(text) { return String(text).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1.'); }; function getWidgetData(callback) { async.parallel({ global: function (next) { db.getObjectFields('global', ['topicCount', 'postCount', 'userCount'], next); }, latestUser: getLatestUser, activeUsers: getActiveUsers, onlineUsers: Widget.updateAndGetOnlineUsers, activeWithin24h: function (next) { var now = Date.now(); db.sortedSetCount('users:online', now - 86400000, '+inf', next); }, }, function (err, results) { if (err) { return callback(err); } var data = { within24h: results.activeWithin24h, count: addDots(results.onlineUsers.onlineCount + results.onlineUsers.guestCount), members: addDots(results.onlineUsers.onlineCount), guests: addDots(results.onlineUsers.guestCount), list: joinUsers(results.activeUsers), posts: addDots(results.global.postCount ? results.global.postCount : 0), topics: addDots(results.global.topicCount ? results.global.topicCount : 0), registered: addDots(results.global.userCount ? results.global.userCount : 0), latest: joinUsers(results.latestUser), relative_path: nconf.get('relative_path'), mostUsers: { date: (new Date(parseInt(results.onlineUsers.timestamp, 10))).toDateString(), total: results.onlineUsers.total, }, }; callback(null, data); }); } function getActiveUsers(callback) { async.waterfall([ function (next) { user.getUidsFromSet('users:online', 0, 19, next); }, function (uids, next) { user.getUsersFields(uids, ['username', 'userslug', 'status'], next); }, ], function (err, data) { if (err) { return callback(err); } data = data.filter(function (a) { return a.status === 'online'; }); callback(err, data); }); } function getLatestUser(callback) { async.waterfall([ function (next) { user.getUidsFromSet('users:joindate', 0, 0, next); }, function (uids, next) { user.getUsersWithFields(uids, ['username', 'userslug'], 0, next); }, ], callback); } function joinUsers(usersData) { var str = []; for (var i = 0, ii = usersData.length; i < ii; i++) { str.push('<a href="' + nconf.get('relative_path') + '/user/' + usersData[i].userslug + '">' + usersData[i].username + '</a>'); } return str.join(', '); } Widget.updateAndGetOnlineUsers = function (callback) { callback = typeof callback === 'function' ? callback : function () {}; async.waterfall([ function (next) { next(null, socketRooms.getLocalStats().onlineRegisteredCount); }, function (onlineCount, next) { socketRooms.getTotalGuestCount(function (err, guestCount) { if (err) { return next(err); } next(null, { onlineCount: parseInt(onlineCount, 10), guestCount: parseInt(guestCount, 10), }); }); }, function (users, next) { db.getObjectFields('plugin:widget-board-stats', ['total', 'timestamp'], function (err, data) { if (err) { return next(err); } var totalUsers = users.onlineCount + users.guestCount; data.timestamp = data.timestamp || Date.now(); if (parseInt(data.total || 0, 10) <= totalUsers) { data.timestamp = Date.now(); data.total = totalUsers; db.setObject('plugin:widget-board-stats', data); } data.onlineCount = users.onlineCount; data.guestCount = users.guestCount; return next(null, data); }); }, ], callback); }; Widget.renderWidget = function (widget, callback) { getWidgetData(function (err, data) { if (err) { return callback(err); } app.render('widgets/board-stats', data, function (err, html) { if (err) { return callback(err); } widget.html = html; callback(null, widget); }); }); }; Widget.defineWidgets = function (widgets, callback) { var widget = { widget: 'board-stats', name: 'Board Stats', description: 'Classical board stats widget in real-time.', content: 'admin/board-stats', }; app.render(widget.content, {}, function (err, html) { widget.content = html; widgets.push(widget); callback(err, widgets); }); };

    which corresponds with our localized version of /var/www/nodebb/node_modules/nodebb-widget-board-stats/public/templates/widgets/board-stats.tpl

    <div component="widget/board-stats" class="widget-board-stats" style="border: 1px solid #999"> <h3 style="background: #666; font-size: 15px; font-weight: 500">Folgende Nutzende sind gerade online: <a href="{config.relative_path}/users?section=online">[komplette Liste]</a></h3> <p> Davon sind aktuell <strong component="widget/board-stats/count">{count}</strong> Nutzende aktiv (<strong component="widget/board-stats/members">{members}</strong> Registrierte + <strong component="widget/board-stats/guests">{guests}</strong>G&auml;ste).<br /> <span style="display: block; line-height: 0.5em" >&nbsp;</span> <span component="widget/board-stats/list">{list}</span> <span style="display: block; line-height: 0.5em" >&nbsp;</span> In den letzten 24 Stunden waren <strong component="widget/board-stats/within24h">{within24h}</strong> verschiedene Nutzende eingeloggt. </p> <h3 style="background: #666; font-size: 15px; font-weight: 500">Forum-Statistik</h3> <p> Unsere aktiven registrierten Mitglieder haben <strong component="widget/board-stats/posts">{posts}</strong> Beitr&auml;ge zu <strong component="widget/board-stats/topics">{topics}</strong> Themen verfasst &ndash;<strong component="widget/board-stats/registered">{registered}</strong> Mitglieder sind bei <em>schoenen-dunk.de</em> registriert.<br /> <span style="display: block; line-height: 0.5em" >&nbsp;</span> <span component="widget/board-stats/latest">{latest}</span> ist unser neuestes Mitglied.&nbsp;Herzlich Willkommen!<br /> <span style="display: block; line-height: 0.5em" >&nbsp;</span> Am {mostUsers.date} waren <strong>{mostUsers.total}</strong> Nutzende gleichzeitig online. </p> </div>

    well.  i hope this helps a bit.

    ober!schöne grüße,
    m.

  • 0 Votes
    1 Posts
    88 Views
    R

    As title, But its work on dev mode. how could I solve the problem.

  • 0 Votes
    5 Posts
    794 Views
    呆呆笨笨

    可以参考 https://nomad.justdoless.cn

  • 0 Votes
    5 Posts
    876 Views
    呆呆笨笨

    可以参考 nomad.justdoless.cn

  • 11 Votes
    74 Posts
    15k Views
    phenomlabP

    @baris thanks for this. Really appreciated. We'll wait for 3.2.0 to drop.

  • IMGUR upload issues

    5
    0 Votes
    5 Posts
    326 Views
    phenomlabP

    @DownPW said in IMGUR upload issues:

    IMGUR sucks

    News Flash - it always did

  • 0 Votes
    1 Posts
    103 Views
    Shantur RathoreS

    Hi,

    I am thinking of setting up NodeBB and Ghost and connect them with the plugin for comments.
    At the same time, I want to be able to publish some a tutorial / guide written in one of the NodeBB Topic as an article for Ghost.

    Is this something done by any plugin?

    Thanks

  • chatGPT moderation

    11
    8 Votes
    11 Posts
    1k Views
    omegaO

    AI moderating "conspiracy theories", that's a conspiracy theory confirmed! ☑ 🤣

  • ldap plugin for current version 2.5.2

    20
    0 Votes
    20 Posts
    940 Views
    q16marvinQ

    Hi,

    i have a little Change, that will also leave User when NOT in ldap group:

    groupJoin: (ldapGroup, ldapId, uid, callback) => { winston.verbose("[LDAP] groupJoin " + ldapGroup.cn + " for user " + ldapId + " uid " + uid) nodebb_ldap.createGroup(ldapGroup, (err, groupId) => { if (err) { return callback(err); } let members = ldapGroup.uniqueMember; if (!Array.isArray(members)) { members = [members]; } winston.verbose("[LDAP] groupJoin members " + members && typeof members) let found = false if (members) { members.forEach(member => { if (member && member.indexOf(ldapId) != -1) { found = true } }); } if (found) { const groupsToJoin = [groupId]; if ((master_config.admin_groups || '').split(',').includes(ldapGroup.cn)) { winston.verbose("[LDAP] joins admin group") groupsToJoin.push('administrators'); } if ((master_config.moderator_groups || '').split(',').includes(ldapGroup.cn)) { groupsToJoin.push('Global Moderators'); } return groups.join(groupsToJoin, uid, callback); } else { const groupsToUnJoin = [groupId]; winston.verbose("[LDAP] unjoins group" + ldapGroup.cn + " uid " + uid); return groups.leave(groupsToUnJoin, uid, callback); //callback(); } } ); },

    thats realy cool for use 🙂 maybe someone else will help it 🙂