From a3b34608de3ea8f7ac49e43358435d73ce3e6661 Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 11:28:19 +0000 Subject: [PATCH 1/8] My changes from 1B --- src/controllers/recent.js | 96 +++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 5699fee1b7..da8c1e52e3 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -1,38 +1,54 @@ - 'use strict'; const nconf = require('nconf'); -const user = require('../user'); -const topics = require('../topics'); -const meta = require('../meta'); -const helpers = require('./helpers'); -const pagination = require('../pagination'); -const privileges = require('../privileges'); - const recentController = module.exports; const relative_path = nconf.get('relative_path'); +// helper to set title & breadcrumbs +function setTitleAndBreadcrumbs(data, url, asHome) { + if (asHome) { + data.title = meta.config.homePageTitle || '[[pages:home]]'; + return; + } + data.title = `[[pages:${url}]]`; + data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); +} + +// helper to set RSS fields +function setRssFields(ctx) { + const { data, url, req, rssToken } = ctx; + const disabled = meta.config['feeds:disableRSS'] || 0; + + data['feeds:disableRSS'] = disabled; + if (disabled) return; + + let rss = `${relative_path}/${url}.rss`; + if (req.loggedIn) { + rss += `?uid=${req.uid}&token=${rssToken}`; + } + data.rssFeedUrl = rss; +} + recentController.get = async function (req, res, next) { const data = await recentController.getData(req, 'recent', 'recent'); if (!data) { return next(); } - res.render('recent', data); }; recentController.getData = async function (req, url, sort) { const page = parseInt(req.query.page, 10) || 1; - let term = helpers.terms[req.query.term]; + + // single, simplified term logic + const termKey = req.query.term; + let term = termKey ? helpers.terms[termKey] : 'alltime'; + if (termKey && !term) return null; + const { cid, tag } = req.query; const filter = req.query.filter || ''; - if (!term && req.query.term) { - return null; - } - term = term || 'alltime'; - const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ user.getSettings(req.uid), helpers.getSelectedCategory(cid), @@ -49,27 +65,23 @@ recentController.getData = async function (req, url, sort) { cids: cid, tags: tag, uid: req.uid, - start: start, - stop: stop, - filter: filter, - term: term, - sort: sort, + start, + stop, + filter, + term, + sort, floatPinned: req.query.pinned, query: req.query, }); - const isDisplayedAsHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); - const baseUrl = isDisplayedAsHome ? '' : url; - - if (isDisplayedAsHome) { - data.title = meta.config.homePageTitle || '[[pages:home]]'; - } else { - data.title = `[[pages:${url}]]`; - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); - } + // use one computation + helper + const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); + const baseUrl = asHome ? '' : url; + setTitleAndBreadcrumbs(data, url, asHome); const query = { ...req.query }; delete query.page; + data.canPost = canPost; data.showSelect = isPrivileged; data.showTopicTools = isPrivileged; @@ -78,29 +90,15 @@ recentController.getData = async function (req, url, sort) { data.selectedCids = categoryData.selectedCids; data.selectedTag = tagData.selectedTag; data.selectedTags = tagData.selectedTags; - data['feeds:disableRSS'] = meta.config['feeds:disableRSS'] || 0; - if (!meta.config['feeds:disableRSS']) { - data.rssFeedUrl = `${relative_path}/${url}.rss`; - if (req.loggedIn) { - data.rssFeedUrl += `?uid=${req.uid}&token=${rssToken}`; - } - } + + // single RSS path via helper + setRssFields({ data, url, req, rssToken }); data.filters = helpers.buildFilters(baseUrl, filter, query); - data.selectedFilter = data.filters.find(filter => filter && filter.selected); - data.terms = helpers.buildTerms(baseUrl, term, query); - data.selectedTerm = data.terms.find(term => term && term.selected); - - const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); - data.pagination = pagination.create(page, pageCount, req.query); - helpers.addLinkTags({ - url: url, - res: req.res, - tags: data.pagination.rel, - page: page, - }); + data.selectedFilter = data.filters.find(f => f && f.selected); + return data; }; - require('../promisify')(recentController, ['get']); + From ab68b5e6c9ca900ae649720458965e0f3ab071fe Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 11:47:52 +0000 Subject: [PATCH 2/8] Changes implemented from 1B --- src/controllers/recent.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index da8c1e52e3..943c0363e2 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -2,6 +2,13 @@ const nconf = require('nconf'); +// ✅ make sure these requires exist (paths match NodeBB's tree) +const topics = require('../topics'); +const user = require('../user'); +const meta = require('../meta'); +const privileges = require('../privileges'); +const helpers = require('./helpers'); // controllers/helpers.js + const recentController = module.exports; const relative_path = nconf.get('relative_path'); @@ -41,7 +48,7 @@ recentController.get = async function (req, res, next) { recentController.getData = async function (req, url, sort) { const page = parseInt(req.query.page, 10) || 1; - // single, simplified term logic + // simplified term logic (no duplicate let) const termKey = req.query.term; let term = termKey ? helpers.terms[termKey] : 'alltime'; if (termKey && !term) return null; @@ -74,7 +81,7 @@ recentController.getData = async function (req, url, sort) { query: req.query, }); - // use one computation + helper + // single computation + helper (remove any older isDisplayedAsHome block) const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); const baseUrl = asHome ? '' : url; setTitleAndBreadcrumbs(data, url, asHome); @@ -91,14 +98,13 @@ recentController.getData = async function (req, url, sort) { data.selectedTag = tagData.selectedTag; data.selectedTags = tagData.selectedTags; - // single RSS path via helper + // single RSS path via helper (remove any manual RSS block) setRssFields({ data, url, req, rssToken }); data.filters = helpers.buildFilters(baseUrl, filter, query); - data.selectedFilter = data.filters.find(f => f && f.selected); + data.selectedFilter = data.filters.find(f => f && f.selected) || null; return data; }; require('../promisify')(recentController, ['get']); - From 8f8825ee2b117221adbc15d42e617f2e6138a42e Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 11:55:11 +0000 Subject: [PATCH 3/8] Changes implemented from 1B --- src/controllers/recent.js | 46 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 943c0363e2..80206c3d3a 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -1,18 +1,21 @@ 'use strict'; const nconf = require('nconf'); - -// ✅ make sure these requires exist (paths match NodeBB's tree) -const topics = require('../topics'); const user = require('../user'); +const topics = require('../topics'); const meta = require('../meta'); const privileges = require('../privileges'); -const helpers = require('./helpers'); // controllers/helpers.js + + +const helpers = require('./helpers'); const recentController = module.exports; const relative_path = nconf.get('relative_path'); -// helper to set title & breadcrumbs +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + function setTitleAndBreadcrumbs(data, url, asHome) { if (asHome) { data.title = meta.config.homePageTitle || '[[pages:home]]'; @@ -22,11 +25,8 @@ function setTitleAndBreadcrumbs(data, url, asHome) { data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); } -// helper to set RSS fields -function setRssFields(ctx) { - const { data, url, req, rssToken } = ctx; +function setRssFields({ data, url, req, rssToken }) { const disabled = meta.config['feeds:disableRSS'] || 0; - data['feeds:disableRSS'] = disabled; if (disabled) return; @@ -37,6 +37,10 @@ function setRssFields(ctx) { data.rssFeedUrl = rss; } +// --------------------------------------------------------------------------- +// Routes +// --------------------------------------------------------------------------- + recentController.get = async function (req, res, next) { const data = await recentController.getData(req, 'recent', 'recent'); if (!data) { @@ -48,13 +52,13 @@ recentController.get = async function (req, res, next) { recentController.getData = async function (req, url, sort) { const page = parseInt(req.query.page, 10) || 1; - // simplified term logic (no duplicate let) + // term selection (simplified, no redeclarations) const termKey = req.query.term; let term = termKey ? helpers.terms[termKey] : 'alltime'; if (termKey && !term) return null; const { cid, tag } = req.query; - const filter = req.query.filter || ''; + const activeFilter = req.query.filter || ''; const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ user.getSettings(req.uid), @@ -74,37 +78,45 @@ recentController.getData = async function (req, url, sort) { uid: req.uid, start, stop, - filter, + filter: activeFilter, term, sort, floatPinned: req.query.pinned, query: req.query, }); - // single computation + helper (remove any older isDisplayedAsHome block) - const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); + // asHome + breadcrumbs + const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || + req.originalUrl.startsWith(`${relative_path}/${url}`)); const baseUrl = asHome ? '' : url; setTitleAndBreadcrumbs(data, url, asHome); + // query adjustments const query = { ...req.query }; delete query.page; + // permissions & selections data.canPost = canPost; data.showSelect = isPrivileged; data.showTopicTools = isPrivileged; + data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); data.selectedCategory = categoryData.selectedCategory; data.selectedCids = categoryData.selectedCids; data.selectedTag = tagData.selectedTag; data.selectedTags = tagData.selectedTags; - // single RSS path via helper (remove any manual RSS block) + // RSS setRssFields({ data, url, req, rssToken }); - data.filters = helpers.buildFilters(baseUrl, filter, query); - data.selectedFilter = data.filters.find(f => f && f.selected) || null; + // filters + data.filters = helpers.buildFilters(baseUrl, activeFilter, query); + data.selectedFilter = Array.isArray(data.filters) + ? data.filters.find(f => f && f.selected) || null + : null; return data; }; require('../promisify')(recentController, ['get']); + From e7d5bd6c9d6add9f32a4b54f88134acf7a56f6b7 Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 11:57:32 +0000 Subject: [PATCH 4/8] Changes implemented from 1B --- src/controllers/recent.js | 191 +++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 80206c3d3a..c1629c5038 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -1,121 +1,118 @@ 'use strict'; const nconf = require('nconf'); + const user = require('../user'); const topics = require('../topics'); const meta = require('../meta'); -const privileges = require('../privileges'); - - const helpers = require('./helpers'); +const pagination = require('../pagination'); +const privileges = require('../privileges'); const recentController = module.exports; const relative_path = nconf.get('relative_path'); -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - function setTitleAndBreadcrumbs(data, url, asHome) { - if (asHome) { - data.title = meta.config.homePageTitle || '[[pages:home]]'; - return; - } - data.title = `[[pages:${url}]]`; - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); + if (asHome) { + data.title = meta.config.homePageTitle || '[[pages:home]]'; + return; + } + data.title = `[[pages:${url}]]`; + data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); } -function setRssFields({ data, url, req, rssToken }) { - const disabled = meta.config['feeds:disableRSS'] || 0; - data['feeds:disableRSS'] = disabled; - if (disabled) return; - let rss = `${relative_path}/${url}.rss`; - if (req.loggedIn) { - rss += `?uid=${req.uid}&token=${rssToken}`; - } - data.rssFeedUrl = rss; -} +function setRssFields(ctx) { + const { data, url, req, rssToken } = ctx; + const disabled = meta.config['feeds:disableRSS'] || 0; -// --------------------------------------------------------------------------- -// Routes -// --------------------------------------------------------------------------- + data['feeds:disableRSS'] = disabled; + if (disabled) return; + + let rss = `${relative_path}/${url}.rss`; + if (req.loggedIn) { + rss += `?uid=${req.uid}&token=${rssToken}`; + } + data.rssFeedUrl = rss; +} recentController.get = async function (req, res, next) { - const data = await recentController.getData(req, 'recent', 'recent'); - if (!data) { - return next(); - } - res.render('recent', data); + const data = await recentController.getData(req, 'recent', 'recent'); + if (!data) { + return next(); + } + + res.render('recent', data); }; recentController.getData = async function (req, url, sort) { - const page = parseInt(req.query.page, 10) || 1; - - // term selection (simplified, no redeclarations) - const termKey = req.query.term; - let term = termKey ? helpers.terms[termKey] : 'alltime'; - if (termKey && !term) return null; - - const { cid, tag } = req.query; - const activeFilter = req.query.filter || ''; - - const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ - user.getSettings(req.uid), - helpers.getSelectedCategory(cid), - helpers.getSelectedTag(tag), - user.auth.getFeedToken(req.uid), - privileges.categories.canPostTopic(req.uid), - user.isPrivileged(req.uid), - ]); - - const start = Math.max(0, (page - 1) * settings.topicsPerPage); - const stop = start + settings.topicsPerPage - 1; - - const data = await topics.getSortedTopics({ - cids: cid, - tags: tag, - uid: req.uid, - start, - stop, - filter: activeFilter, - term, - sort, - floatPinned: req.query.pinned, - query: req.query, - }); - - // asHome + breadcrumbs - const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || - req.originalUrl.startsWith(`${relative_path}/${url}`)); - const baseUrl = asHome ? '' : url; - setTitleAndBreadcrumbs(data, url, asHome); - - // query adjustments - const query = { ...req.query }; - delete query.page; - - // permissions & selections - data.canPost = canPost; - data.showSelect = isPrivileged; - data.showTopicTools = isPrivileged; - - data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); - data.selectedCategory = categoryData.selectedCategory; - data.selectedCids = categoryData.selectedCids; - data.selectedTag = tagData.selectedTag; - data.selectedTags = tagData.selectedTags; - - // RSS - setRssFields({ data, url, req, rssToken }); - - // filters - data.filters = helpers.buildFilters(baseUrl, activeFilter, query); - data.selectedFilter = Array.isArray(data.filters) - ? data.filters.find(f => f && f.selected) || null - : null; - - return data; + const page = parseInt(req.query.page, 10) || 1; + + + const termKey = req.query.term; + let term = termKey ? helpers.terms[termKey] : 'alltime'; + if (termKey && !term) return null; + + const { cid, tag } = req.query; + const filter = req.query.filter || ''; + + const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ + user.getSettings(req.uid), + helpers.getSelectedCategory(cid), + helpers.getSelectedTag(tag), + user.auth.getFeedToken(req.uid), + privileges.categories.canPostTopic(req.uid), + user.isPrivileged(req.uid), + ]); + + const start = Math.max(0, (page - 1) * settings.topicsPerPage); + const stop = start + settings.topicsPerPage - 1; + + const data = await topics.getSortedTopics({ + cids: cid, + tags: tag, + uid: req.uid, + start: start, + stop: stop, + filter: filter, + term: term, + sort: sort, + floatPinned: req.query.pinned, + query: req.query, + }); + + const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); + const baseUrl = asHome ? '' : url; + setTitleAndBreadcrumbs(data, url, asHome); + + const query = { ...req.query }; + delete query.page; + data.canPost = canPost; + data.showSelect = isPrivileged; + data.showTopicTools = isPrivileged; + data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); + data.selectedCategory = categoryData.selectedCategory; + data.selectedCids = categoryData.selectedCids; + data.selectedTag = tagData.selectedTag; + data.selectedTags = tagData.selectedTags; + + + setRssFields({ data, url, req, rssToken }); + + data.filters = helpers.buildFilters(baseUrl, filter, query); + data.selectedFilter = data.filters.find(filter => filter && filter.selected); + data.terms = helpers.buildTerms(baseUrl, term, query); + data.selectedTerm = data.terms.find(term => term && term.selected); + + const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); + data.pagination = pagination.create(page, pageCount, req.query); + helpers.addLinkTags({ + url: url, + res: req.res, + tags: data.pagination.rel, + page: page, + }); + return data; }; require('../promisify')(recentController, ['get']); From d6cef845724483358b66fc2ad4e2eed89b85ba81 Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 12:01:36 +0000 Subject: [PATCH 5/8] Changes implemented from 1B --- src/controllers/recent.js | 214 +++++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 94 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index c1629c5038..9ec5cea5e0 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -2,118 +2,144 @@ const nconf = require('nconf'); +// ⬇️ These paths assume this file is at src/controllers/recent.js const user = require('../user'); const topics = require('../topics'); const meta = require('../meta'); -const helpers = require('./helpers'); -const pagination = require('../pagination'); const privileges = require('../privileges'); +// If helpers.js is NOT in the same folder as this file, change to: ../helpers +const helpers = require('./helpers'); const recentController = module.exports; const relative_path = nconf.get('relative_path'); -function setTitleAndBreadcrumbs(data, url, asHome) { - if (asHome) { - data.title = meta.config.homePageTitle || '[[pages:home]]'; - return; - } - data.title = `[[pages:${url}]]`; - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); -} +// --------------------------------------------------------------------------- +// Small guards/utilities +// --------------------------------------------------------------------------- + +function resolveTerm(req) { + // Some NodeBB trees expose helpers.terms; some don’t. + // We gracefully fall back to 'alltime' and reject unknown terms when a key is supplied. + const termKey = req.query.term; + if (!termKey) return 'alltime'; + + if (helpers && helpers.terms && Object.prototype.hasOwnProperty.call(helpers.terms, termKey)) { + return helpers.terms[termKey] || 'alltime'; + } + // If a term was provided but we can't validate it, mimic original behavior: reject. + return null; +} -function setRssFields(ctx) { - const { data, url, req, rssToken } = ctx; - const disabled = meta.config['feeds:disableRSS'] || 0; +function setTitleAndBreadcrumbs(data, url, asHome) { + if (asHome) { + data.title = meta.config.homePageTitle || '[[pages:home]]'; + return; + } + data.title = `[[pages:${url}]]`; + if (helpers && typeof helpers.buildBreadcrumbs === 'function') { + data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); + } +} - data['feeds:disableRSS'] = disabled; - if (disabled) return; +function setRssFields({ data, url, req, rssToken }) { + const disabled = meta.config['feeds:disableRSS'] || 0; + data['feeds:disableRSS'] = disabled; + if (disabled) return; - let rss = `${relative_path}/${url}.rss`; - if (req.loggedIn) { - rss += `?uid=${req.uid}&token=${rssToken}`; - } - data.rssFeedUrl = rss; + let rss = `${relative_path}/${url}.rss`; + if (req.loggedIn) { + rss += `?uid=${req.uid}&token=${rssToken}`; + } + data.rssFeedUrl = rss; } -recentController.get = async function (req, res, next) { - const data = await recentController.getData(req, 'recent', 'recent'); - if (!data) { - return next(); - } +// --------------------------------------------------------------------------- +// Routes +// --------------------------------------------------------------------------- - res.render('recent', data); +recentController.get = async function (req, res, next) { + const data = await recentController.getData(req, 'recent', 'recent'); + if (!data) return next(); + res.render('recent', data); }; recentController.getData = async function (req, url, sort) { - const page = parseInt(req.query.page, 10) || 1; - - - const termKey = req.query.term; - let term = termKey ? helpers.terms[termKey] : 'alltime'; - if (termKey && !term) return null; - - const { cid, tag } = req.query; - const filter = req.query.filter || ''; - - const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ - user.getSettings(req.uid), - helpers.getSelectedCategory(cid), - helpers.getSelectedTag(tag), - user.auth.getFeedToken(req.uid), - privileges.categories.canPostTopic(req.uid), - user.isPrivileged(req.uid), - ]); - - const start = Math.max(0, (page - 1) * settings.topicsPerPage); - const stop = start + settings.topicsPerPage - 1; - - const data = await topics.getSortedTopics({ - cids: cid, - tags: tag, - uid: req.uid, - start: start, - stop: stop, - filter: filter, - term: term, - sort: sort, - floatPinned: req.query.pinned, - query: req.query, - }); - - const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); - const baseUrl = asHome ? '' : url; - setTitleAndBreadcrumbs(data, url, asHome); - - const query = { ...req.query }; - delete query.page; - data.canPost = canPost; - data.showSelect = isPrivileged; - data.showTopicTools = isPrivileged; - data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); - data.selectedCategory = categoryData.selectedCategory; - data.selectedCids = categoryData.selectedCids; - data.selectedTag = tagData.selectedTag; - data.selectedTags = tagData.selectedTags; - - - setRssFields({ data, url, req, rssToken }); - - data.filters = helpers.buildFilters(baseUrl, filter, query); - data.selectedFilter = data.filters.find(filter => filter && filter.selected); - data.terms = helpers.buildTerms(baseUrl, term, query); - data.selectedTerm = data.terms.find(term => term && term.selected); - - const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); - data.pagination = pagination.create(page, pageCount, req.query); - helpers.addLinkTags({ - url: url, - res: req.res, - tags: data.pagination.rel, - page: page, - }); - return data; + const page = parseInt(req.query.page, 10) || 1; + + // Term handling with guards (no duplicate lets) + const term = resolveTerm(req); + if (req.query.term && !term) { + // termKey provided but invalid + return null; + } + + const { cid, tag } = req.query; + const activeFilter = req.query.filter || ''; + + const [ + settings, + categoryData, + tagData, + rssToken, + canPost, + isPrivileged, + ] = await Promise.all([ + user.getSettings(req.uid), + helpers.getSelectedCategory ? helpers.getSelectedCategory(cid) : { selectedCategory: null, selectedCids: [] }, + helpers.getSelectedTag ? helpers.getSelectedTag(tag) : { selectedTag: null, selectedTags: [] }, + user.auth.getFeedToken(req.uid), + privileges.categories.canPostTopic(req.uid), + user.isPrivileged(req.uid), + ]); + + const start = Math.max(0, (page - 1) * settings.topicsPerPage); + const stop = start + settings.topicsPerPage - 1; + + const data = await topics.getSortedTopics({ + cids: cid, + tags: tag, + uid: req.uid, + start, + stop, + filter: activeFilter, + term, + sort, + floatPinned: req.query.pinned, + query: req.query, + }); + + // Compute "as home" only once; use helper for title & breadcrumbs + const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || + req.originalUrl.startsWith(`${relative_path}/${url}`)); + const baseUrl = asHome ? '' : url; + setTitleAndBreadcrumbs(data, url, asHome); + + // Build query for links + const query = { ...req.query }; + delete query.page; + + // Permissions / selections + data.canPost = canPost; + data.showSelect = isPrivileged; + data.showTopicTools = isPrivileged; + + data.allCategoriesUrl = baseUrl + (helpers.buildQueryString ? helpers.buildQueryString(query, 'cid', '') : ''); + data.selectedCategory = categoryData.selectedCategory; + data.selectedCids = categoryData.selectedCids; + data.selectedTag = tagData.selectedTag; + data.selectedTags = tagData.selectedTags; + + // RSS via helper + setRssFields({ data, url, req, rssToken }); + + // Filters (defend if helpers.buildFilters returns non-array) + data.filters = helpers.buildFilters ? helpers.buildFilters(baseUrl, activeFilter, query) : []; + data.selectedFilter = Array.isArray(data.filters) + ? data.filters.find(f => f && f.selected) || null + : null; + + return data; }; require('../promisify')(recentController, ['get']); - From 40c56f9383b6cf314157edf905e697993e4efaac Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 12:04:59 +0000 Subject: [PATCH 6/8] Changes implemented from 1B --- src/controllers/recent.js | 214 +++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 120 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index 9ec5cea5e0..c1629c5038 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -2,144 +2,118 @@ const nconf = require('nconf'); -// ⬇️ These paths assume this file is at src/controllers/recent.js const user = require('../user'); const topics = require('../topics'); const meta = require('../meta'); -const privileges = require('../privileges'); -// If helpers.js is NOT in the same folder as this file, change to: ../helpers const helpers = require('./helpers'); +const pagination = require('../pagination'); +const privileges = require('../privileges'); const recentController = module.exports; const relative_path = nconf.get('relative_path'); -// --------------------------------------------------------------------------- -// Small guards/utilities -// --------------------------------------------------------------------------- - -function resolveTerm(req) { - // Some NodeBB trees expose helpers.terms; some don’t. - // We gracefully fall back to 'alltime' and reject unknown terms when a key is supplied. - const termKey = req.query.term; - if (!termKey) return 'alltime'; - - if (helpers && helpers.terms && Object.prototype.hasOwnProperty.call(helpers.terms, termKey)) { - return helpers.terms[termKey] || 'alltime'; - } - - // If a term was provided but we can't validate it, mimic original behavior: reject. - return null; -} - function setTitleAndBreadcrumbs(data, url, asHome) { - if (asHome) { - data.title = meta.config.homePageTitle || '[[pages:home]]'; - return; - } - data.title = `[[pages:${url}]]`; - if (helpers && typeof helpers.buildBreadcrumbs === 'function') { - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); - } + if (asHome) { + data.title = meta.config.homePageTitle || '[[pages:home]]'; + return; + } + data.title = `[[pages:${url}]]`; + data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); } -function setRssFields({ data, url, req, rssToken }) { - const disabled = meta.config['feeds:disableRSS'] || 0; - data['feeds:disableRSS'] = disabled; - if (disabled) return; - let rss = `${relative_path}/${url}.rss`; - if (req.loggedIn) { - rss += `?uid=${req.uid}&token=${rssToken}`; - } - data.rssFeedUrl = rss; -} +function setRssFields(ctx) { + const { data, url, req, rssToken } = ctx; + const disabled = meta.config['feeds:disableRSS'] || 0; + + data['feeds:disableRSS'] = disabled; + if (disabled) return; -// --------------------------------------------------------------------------- -// Routes -// --------------------------------------------------------------------------- + let rss = `${relative_path}/${url}.rss`; + if (req.loggedIn) { + rss += `?uid=${req.uid}&token=${rssToken}`; + } + data.rssFeedUrl = rss; +} recentController.get = async function (req, res, next) { - const data = await recentController.getData(req, 'recent', 'recent'); - if (!data) return next(); - res.render('recent', data); + const data = await recentController.getData(req, 'recent', 'recent'); + if (!data) { + return next(); + } + + res.render('recent', data); }; recentController.getData = async function (req, url, sort) { - const page = parseInt(req.query.page, 10) || 1; - - // Term handling with guards (no duplicate lets) - const term = resolveTerm(req); - if (req.query.term && !term) { - // termKey provided but invalid - return null; - } - - const { cid, tag } = req.query; - const activeFilter = req.query.filter || ''; - - const [ - settings, - categoryData, - tagData, - rssToken, - canPost, - isPrivileged, - ] = await Promise.all([ - user.getSettings(req.uid), - helpers.getSelectedCategory ? helpers.getSelectedCategory(cid) : { selectedCategory: null, selectedCids: [] }, - helpers.getSelectedTag ? helpers.getSelectedTag(tag) : { selectedTag: null, selectedTags: [] }, - user.auth.getFeedToken(req.uid), - privileges.categories.canPostTopic(req.uid), - user.isPrivileged(req.uid), - ]); - - const start = Math.max(0, (page - 1) * settings.topicsPerPage); - const stop = start + settings.topicsPerPage - 1; - - const data = await topics.getSortedTopics({ - cids: cid, - tags: tag, - uid: req.uid, - start, - stop, - filter: activeFilter, - term, - sort, - floatPinned: req.query.pinned, - query: req.query, - }); - - // Compute "as home" only once; use helper for title & breadcrumbs - const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || - req.originalUrl.startsWith(`${relative_path}/${url}`)); - const baseUrl = asHome ? '' : url; - setTitleAndBreadcrumbs(data, url, asHome); - - // Build query for links - const query = { ...req.query }; - delete query.page; - - // Permissions / selections - data.canPost = canPost; - data.showSelect = isPrivileged; - data.showTopicTools = isPrivileged; - - data.allCategoriesUrl = baseUrl + (helpers.buildQueryString ? helpers.buildQueryString(query, 'cid', '') : ''); - data.selectedCategory = categoryData.selectedCategory; - data.selectedCids = categoryData.selectedCids; - data.selectedTag = tagData.selectedTag; - data.selectedTags = tagData.selectedTags; - - // RSS via helper - setRssFields({ data, url, req, rssToken }); - - // Filters (defend if helpers.buildFilters returns non-array) - data.filters = helpers.buildFilters ? helpers.buildFilters(baseUrl, activeFilter, query) : []; - data.selectedFilter = Array.isArray(data.filters) - ? data.filters.find(f => f && f.selected) || null - : null; - - return data; + const page = parseInt(req.query.page, 10) || 1; + + + const termKey = req.query.term; + let term = termKey ? helpers.terms[termKey] : 'alltime'; + if (termKey && !term) return null; + + const { cid, tag } = req.query; + const filter = req.query.filter || ''; + + const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ + user.getSettings(req.uid), + helpers.getSelectedCategory(cid), + helpers.getSelectedTag(tag), + user.auth.getFeedToken(req.uid), + privileges.categories.canPostTopic(req.uid), + user.isPrivileged(req.uid), + ]); + + const start = Math.max(0, (page - 1) * settings.topicsPerPage); + const stop = start + settings.topicsPerPage - 1; + + const data = await topics.getSortedTopics({ + cids: cid, + tags: tag, + uid: req.uid, + start: start, + stop: stop, + filter: filter, + term: term, + sort: sort, + floatPinned: req.query.pinned, + query: req.query, + }); + + const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); + const baseUrl = asHome ? '' : url; + setTitleAndBreadcrumbs(data, url, asHome); + + const query = { ...req.query }; + delete query.page; + data.canPost = canPost; + data.showSelect = isPrivileged; + data.showTopicTools = isPrivileged; + data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); + data.selectedCategory = categoryData.selectedCategory; + data.selectedCids = categoryData.selectedCids; + data.selectedTag = tagData.selectedTag; + data.selectedTags = tagData.selectedTags; + + + setRssFields({ data, url, req, rssToken }); + + data.filters = helpers.buildFilters(baseUrl, filter, query); + data.selectedFilter = data.filters.find(filter => filter && filter.selected); + data.terms = helpers.buildTerms(baseUrl, term, query); + data.selectedTerm = data.terms.find(term => term && term.selected); + + const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); + data.pagination = pagination.create(page, pageCount, req.query); + helpers.addLinkTags({ + url: url, + res: req.res, + tags: data.pagination.rel, + page: page, + }); + return data; }; require('../promisify')(recentController, ['get']); + From 06fdc530bd6338cff5a9f83a640d0a28dc4b60fe Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 12:10:07 +0000 Subject: [PATCH 7/8] Changes implemented from 1B --- src/controllers/recent.js | 176 +++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 90 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index c1629c5038..c4bdd0aa8e 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -13,107 +13,103 @@ const recentController = module.exports; const relative_path = nconf.get('relative_path'); function setTitleAndBreadcrumbs(data, url, asHome) { - if (asHome) { - data.title = meta.config.homePageTitle || '[[pages:home]]'; - return; - } - data.title = `[[pages:${url}]]`; - data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); + if (asHome) { + data.title = meta.config.homePageTitle || '[[pages:home]]'; + return; + } + data.title = `[[pages:${url}]]`; + data.breadcrumbs = helpers.buildBreadcrumbs([{ text: `[[${url}:title]]` }]); } - function setRssFields(ctx) { - const { data, url, req, rssToken } = ctx; - const disabled = meta.config['feeds:disableRSS'] || 0; + const { data, url, req, rssToken } = ctx; + const disabled = meta.config['feeds:disableRSS'] || 0; - data['feeds:disableRSS'] = disabled; - if (disabled) return; + data['feeds:disableRSS'] = disabled; + if (disabled) return; - let rss = `${relative_path}/${url}.rss`; - if (req.loggedIn) { - rss += `?uid=${req.uid}&token=${rssToken}`; - } - data.rssFeedUrl = rss; + let rss = `${relative_path}/${url}.rss`; + if (req.loggedIn) { + rss += `?uid=${req.uid}&token=${rssToken}`; + } + data.rssFeedUrl = rss; } recentController.get = async function (req, res, next) { - const data = await recentController.getData(req, 'recent', 'recent'); - if (!data) { - return next(); - } - - res.render('recent', data); + const data = await recentController.getData(req, 'recent', 'recent'); + if (!data) { + return next(); + } + res.render('recent', data); }; recentController.getData = async function (req, url, sort) { - const page = parseInt(req.query.page, 10) || 1; - - - const termKey = req.query.term; - let term = termKey ? helpers.terms[termKey] : 'alltime'; - if (termKey && !term) return null; - - const { cid, tag } = req.query; - const filter = req.query.filter || ''; - - const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ - user.getSettings(req.uid), - helpers.getSelectedCategory(cid), - helpers.getSelectedTag(tag), - user.auth.getFeedToken(req.uid), - privileges.categories.canPostTopic(req.uid), - user.isPrivileged(req.uid), - ]); - - const start = Math.max(0, (page - 1) * settings.topicsPerPage); - const stop = start + settings.topicsPerPage - 1; - - const data = await topics.getSortedTopics({ - cids: cid, - tags: tag, - uid: req.uid, - start: start, - stop: stop, - filter: filter, - term: term, - sort: sort, - floatPinned: req.query.pinned, - query: req.query, - }); - - const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); - const baseUrl = asHome ? '' : url; - setTitleAndBreadcrumbs(data, url, asHome); - - const query = { ...req.query }; - delete query.page; - data.canPost = canPost; - data.showSelect = isPrivileged; - data.showTopicTools = isPrivileged; - data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); - data.selectedCategory = categoryData.selectedCategory; - data.selectedCids = categoryData.selectedCids; - data.selectedTag = tagData.selectedTag; - data.selectedTags = tagData.selectedTags; - - - setRssFields({ data, url, req, rssToken }); - - data.filters = helpers.buildFilters(baseUrl, filter, query); - data.selectedFilter = data.filters.find(filter => filter && filter.selected); - data.terms = helpers.buildTerms(baseUrl, term, query); - data.selectedTerm = data.terms.find(term => term && term.selected); - - const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); - data.pagination = pagination.create(page, pageCount, req.query); - helpers.addLinkTags({ - url: url, - res: req.res, - tags: data.pagination.rel, - page: page, - }); - return data; + const page = parseInt(req.query.page, 10) || 1; + + const termKey = req.query.term; + const term = termKey ? helpers.terms[termKey] : 'alltime'; + if (termKey && !term) return null; + + const { cid, tag } = req.query; + const filter = req.query.filter || ''; + + const [settings, categoryData, tagData, rssToken, canPost, isPrivileged] = await Promise.all([ + user.getSettings(req.uid), + helpers.getSelectedCategory(cid), + helpers.getSelectedTag(tag), + user.auth.getFeedToken(req.uid), + privileges.categories.canPostTopic(req.uid), + user.isPrivileged(req.uid), + ]); + + const start = Math.max(0, (page - 1) * settings.topicsPerPage); + const stop = start + settings.topicsPerPage - 1; + + const data = await topics.getSortedTopics({ + cids: cid, + tags: tag, + uid: req.uid, + start: start, + stop: stop, + filter: filter, + term: term, + sort: sort, + floatPinned: req.query.pinned, + query: req.query, + }); + + const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`)); + const baseUrl = asHome ? '' : url; + setTitleAndBreadcrumbs(data, url, asHome); + + const query = { ...req.query }; + delete query.page; + + data.canPost = canPost; + data.showSelect = isPrivileged; + data.showTopicTools = isPrivileged; + data.allCategoriesUrl = baseUrl + helpers.buildQueryString(query, 'cid', ''); + data.selectedCategory = categoryData.selectedCategory; + data.selectedCids = categoryData.selectedCids; + data.selectedTag = tagData.selectedTag; + data.selectedTags = tagData.selectedTags; + + setRssFields({ data, url, req, rssToken }); + + data.filters = helpers.buildFilters(baseUrl, filter, query); + data.selectedFilter = data.filters.find(f => f && f.selected); + data.terms = helpers.buildTerms(baseUrl, term, query); + data.selectedTerm = data.terms.find(t => t && t.selected); + + const pageCount = Math.max(1, Math.ceil(data.topicCount / settings.topicsPerPage)); + data.pagination = pagination.create(page, pageCount, req.query); + helpers.addLinkTags({ + url: url, + res: req.res, + tags: data.pagination.rel, + page: page, + }); + return data; }; require('../promisify')(recentController, ['get']); - From e4f718a65868cdd2aa496705978172e76568de83 Mon Sep 17 00:00:00 2001 From: Moza Al Fahad Date: Mon, 22 Sep 2025 12:13:56 +0000 Subject: [PATCH 8/8] Changes implemented --- src/controllers/recent.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/controllers/recent.js b/src/controllers/recent.js index c4bdd0aa8e..216df797e3 100644 --- a/src/controllers/recent.js +++ b/src/controllers/recent.js @@ -66,16 +66,16 @@ recentController.getData = async function (req, url, sort) { const stop = start + settings.topicsPerPage - 1; const data = await topics.getSortedTopics({ - cids: cid, - tags: tag, - uid: req.uid, - start: start, - stop: stop, - filter: filter, - term: term, - sort: sort, - floatPinned: req.query.pinned, - query: req.query, + cids: cid, + tags: tag, + uid: req.uid, + start: start, + stop: stop, + filter: filter, + term: term, + sort: sort, + floatPinned: req.query.pinned, + query: req.query, }); const asHome = !(req.originalUrl.startsWith(`${relative_path}/api/${url}`) || req.originalUrl.startsWith(`${relative_path}/${url}`));