I am not receiving any errors, but I am also not receiving any data in the tpl. Maybe I am not seeing the picture. Maybe it's the number of hours I have been trying to figure this out.
Here is my plugin.json:
{
"id": "nodebb-theme-codename",
"url": "https://github.com/girls-whocode/nodebb-theme-codename",
"library": "lib/library.js",
"hooks": [
{ "hook": "filter:recent.build", "method": "filterRecentBuild" }
],
"staticDirs": {
"static": "static",
"inter": "node_modules/@fontsource/inter/files",
"poppins": "node_modules/@fontsource/poppins/files"
},
"scss": [
"scss/codename.scss"
],
"scripts": [
"public/codename.js"
],
"templates": "templates"
}
Here is my /lib/library.js
'use strict';
const nconf = require.main.require('nconf');
const meta = require.main.require('./src/meta');
const _ = require.main.require('lodash');
const user = require.main.require('./src/user');
const library = module.exports;
library.init = async function (params) {
const { router, middleware } = params;
const routeHelpers = require.main.require('./src/routes/helpers');
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/harmony', [], controllers.renderAdminPage);
routeHelpers.setupPageRoute(router, '/user/:userslug/theme', [
middleware.exposeUid,
middleware.ensureLoggedIn,
middleware.canViewUsers,
middleware.checkAccountPermissions,
], controllers.renderThemeSettings);
if (nconf.get('isPrimary') && process.env.NODE_ENV === 'production') {
setTimeout(buildSkins, 0);
}
};
async function buildSkins() {
try {
const plugins = require.main.require('./src/plugins');
await plugins.prepareForBuild(['client side styles']);
for (const skin of meta.css.supportedSkins) {
// eslint-disable-next-line no-await-in-loop
await meta.css.buildBundle(`client-${skin}`, true);
}
require.main.require('./src/meta/minifier').killAll();
} catch (err) {
console.error(err.stack);
}
}
library.addAdminNavigation = async function (header) {
header.plugins.push({
route: '/plugins/harmony',
icon: 'fa-paint-brush',
name: '[[themes/harmony:theme-name]]',
});
return header;
};
library.addProfileItem = async (data) => {
data.links.push({
id: 'theme',
route: 'theme',
icon: 'fa-paint-brush',
name: '[[themes/harmony:settings.title]]',
visibility: {
self: true,
other: false,
moderator: false,
globalMod: false,
admin: false,
},
});
return data;
};
library.defineWidgetAreas = async function (areas) {
const locations = ['header', 'sidebar', 'footer'];
const templates = [
'categories.tpl', 'category.tpl', 'topic.tpl', 'users.tpl',
'unread.tpl', 'recent.tpl', 'popular.tpl', 'top.tpl', 'tags.tpl', 'tag.tpl',
'login.tpl', 'register.tpl',
];
function capitalizeFirst(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
templates.forEach((template) => {
locations.forEach((location) => {
areas.push({
name: `${capitalizeFirst(template.split('.')[0])} ${capitalizeFirst(location)}`,
template: template,
location: location,
});
});
});
areas = areas.concat([
{
name: 'Main post header',
template: 'topic.tpl',
location: 'mainpost-header',
},
{
name: 'Main post footer',
template: 'topic.tpl',
location: 'mainpost-footer',
},
{
name: 'Sidebar Footer',
template: 'global',
location: 'sidebar-footer',
},
{
name: 'Brand Header',
template: 'global',
location: 'brand-header',
},
{
name: 'About me (before)',
template: 'account/profile.tpl',
location: 'profile-aboutme-before',
},
{
name: 'About me (after)',
template: 'account/profile.tpl',
location: 'profile-aboutme-after',
},
]);
return areas;
};
library.loadThemeConfig = async function (uid) {
const [themeConfig, userConfig] = await Promise.all([
meta.settings.get('harmony'),
user.getSettings(uid),
]);
const config = { ...defaults, ...themeConfig, ...(_.pick(userConfig, Object.keys(defaults))) };
config.enableQuickReply = config.enableQuickReply === 'on';
config.enableBreadcrumbs = config.enableBreadcrumbs === 'on';
config.centerHeaderElements = config.centerHeaderElements === 'on';
config.mobileTopicTeasers = config.mobileTopicTeasers === 'on';
config.stickyToolbar = config.stickyToolbar === 'on';
config.autohideBottombar = config.autohideBottombar === 'on';
config.openSidebars = config.openSidebars === 'on';
config.chatModals = config.chatModals === 'on';
return config;
};
library.getThemeConfig = async function (config) {
config.theme = await library.loadThemeConfig(config.uid);
config.openDraftsOnPageLoad = false;
return config;
};
library.getAdminSettings = async function (hookData) {
if (hookData.plugin === 'harmony') {
hookData.values = {
...defaults,
...hookData.values,
};
}
return hookData;
};
library.saveUserSettings = async function (hookData) {
Object.keys(defaults).forEach((key) => {
if (hookData.data.hasOwnProperty(key)) {
hookData.settings[key] = hookData.data[key] || undefined;
}
});
return hookData;
};
library.filterMiddlewareRenderHeader = async function (hookData) {
hookData.templateData.bootswatchSkinOptions = await meta.css.getSkinSwitcherOptions(hookData.req.uid);
return hookData;
};
library.filterTeasersConfigureStripTags = function (hookData) {
// teasers have a stretched-link to go to last post, the anchors in them are not clickable
hookData.tags.push('a');
return hookData;
};
Here is my public/codename.js
'use strict';
$(document).ready(function() {
setupNProgress();
require(['hooks'], function (hooks) {
hooks.on('filter:recent.build', function (hookData) {
// Ensure topicsList is added to the template context
if (hookData.templateData) {
console.log('Recent Build Hook Triggered: Adding topicsList');
hookData.templateData.topicsList = hookData.templateData.topicsList || [];
}
});
hooks.on('action:skin.change', function (hookData) {
$('[component="skinSwitcher"] i.fa-paintbrush').removeClass('fa-fade');
});
hooks.on('action:notification.updateCount', function (payload) {
$('[component="notification/badge"]')
.text(payload.count)
.toggleClass('hidden', !payload.count);
});
hooks.on('action:chat.updateCount', function (payload) {
$('[component="chat/badge"]')
.text(payload.count)
.toggleClass('hidden', !payload.count);
});
hooks.on('action:unread.updateCount', function (payload) {
const href = $('a[href="' + config.relative_path + payload.url + '"].navigation-link');
const el = href.parent().find('[component="nav/content/badge"]');
if (el.length) {
el.text(payload.count)
.toggleClass('hidden', !payload.count);
$('[component="unread/badge"]')
.text(payload.count)
.toggleClass('hidden', !payload.count);
}
});
});
$(window).on('action:ajaxify.start', function () {
require(['bootstrap'], function (boostrap) {
const offcanvas = boostrap.Offcanvas.getInstance('#offcanvas');
if (offcanvas) {
offcanvas.hide();
}
});
});
function setupNProgress() {
require(['nprogress'], function (NProgress) {
if (NProgress) {
$(window).on('action:ajaxify.start', function () {
NProgress.set(0.7);
});
$(window).on('action:ajaxify.end', function () {
NProgress.done();
});
}
});
}
});
Finally here is my templates/partials/latest.tpl
<div class="nodebbWidget nodebbWidget--horizontal" data-blocktitle="Topic Feed" data-blockid="app_forums_topicFeed_9f557b" data-menustyle="menu" data-blockconfig="1" data-widget-customizable="1" data-controller="core.front.widgets.block">
<header class="nodebbWidget__header">
<h3>Latest Topics</h3>
</header>
<div class="nodebbWidget__content">
<ul class="nodebbData nodebbData--mini-grid nodebbData--carousel nodebbData--topic-feed-widget" id="widget-topic-feed_65456218" tabindex="0">
{{{ each topicsList }}}
<li class="nodebbData__item">
<!-- Topic Title -->
<a href="/topic/{slug}" class="nodebbLinkPanel">
<span>{title}</span>
</a>
<!-- User Avatar or Fallback -->
<figure class="nodebbData__icon">
{{{ if user.picture }}}
<a href="/user/{user.username}" class="nodebbUserPhoto nodebbUserPhoto--fluid">
<img src="{user.picture}" alt="{user.username}" loading="lazy" />
</a>
{{{ else }}}
<div class="nodeBBNavNoPhotoTile nodebbUserPhoto--fluid nodebbUserNav__link"
style="background-color: {user.icon:bgColor || '#555'};
width: 28px;
height: 28px;
font-size: 15px;
line-height: 28px;
border-radius: 50%;">
{user.icon:text}
</div>
{{{ end }}}
</figure>
<!-- Metadata -->
<span>{formattedDate}</span>
<span>Views: {viewcount}</span>
<!-- Category -->
<ul class="nodebbTags nodebbTags--condensed">
<li class="nodebbTags__item">
<a href="/category/{category.slug}"
class="nodebbTags__tag"
rel="tag"
title="{category.name}">
<i class="fa {category.icon}"></i>
<span>{category.name}</span>
</a>
</li>
</ul>
</li>
{{{ end }}}
</ul>
</div>
</div>