diff --git a/src/flags.js b/src/flags.js index 8bf57257fd..c6a2a90edd 100644 --- a/src/flags.js +++ b/src/flags.js @@ -36,7 +36,11 @@ Flags.init = async function () { if (!Array.isArray(value)) { sets.push(prefix + value); } else if (value.length) { - value.forEach(x => orSets.push(prefix + x)); + if (value.length === 1) { + sets.push(prefix + value[0]); + } else { + value.forEach(x => orSets.push(prefix + x)); + } } } @@ -67,6 +71,10 @@ Flags.init = async function () { case 'mine': sets.push('flags:byAssignee:' + uid); break; + + case 'unresolved': + prepareSets(sets, orSets, 'flags:byState:', ['open', 'wip']); + break; } }, }, @@ -113,9 +121,13 @@ Flags.get = async function (flagId) { return data.flag; }; -Flags.list = async function (data) { - const filters = data.filters || {}; +Flags.getCount = async function ({ uid, filters }) { + filters = filters || {}; + const flagIds = await Flags.getFlagIdsWithFilters({ filters, uid }); + return flagIds.length; +}; +Flags.getFlagIdsWithFilters = async function ({ filters, uid }) { let sets = []; const orSets = []; @@ -126,7 +138,7 @@ Flags.list = async function (data) { for (var type in filters) { if (filters.hasOwnProperty(type)) { if (Flags._filters.hasOwnProperty(type)) { - Flags._filters[type](sets, orSets, filters[type], data.uid); + Flags._filters[type](sets, orSets, filters[type], uid); } else { winston.warn('[flags/list] No flag filter type found: ' + type); } @@ -152,6 +164,15 @@ Flags.list = async function (data) { } } + return flagIds; +}; + +Flags.list = async function (data) { + const filters = data.filters || {}; + let flagIds = await Flags.getFlagIdsWithFilters({ + filters, + uid: data.uid, + }); flagIds = await Flags.sort(flagIds, data.sort); // Create subset for parsing based on page number (n=20) diff --git a/src/middleware/header.js b/src/middleware/header.js index 2174f7c461..b6c7651c79 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -10,6 +10,7 @@ var db = require('../database'); var user = require('../user'); var topics = require('../topics'); var messaging = require('../messaging'); +var flags = require('../flags'); var meta = require('../meta'); var plugins = require('../plugins'); var navigation = require('../navigation'); @@ -79,9 +80,6 @@ middleware.renderHeader = async function renderHeader(req, res, data) { timeagoCode: languages.userTimeagoCode(res.locals.config.userLang), browserTitle: translator.translate(controllers.helpers.buildTitle(translator.unescape(data.title))), navigation: navigation.get(req.uid), - unreadData: topics.getUnreadData({ uid: req.uid }), - unreadChatCount: messaging.getUnreadCount(req.uid), - unreadNotificationCount: user.notifications.getUnreadCount(req.uid), }); const unreadData = { @@ -105,44 +103,15 @@ middleware.renderHeader = async function renderHeader(req, res, data) { templateValues.bootswatchSkin = (parseInt(meta.config.disableCustomUserSkins, 10) !== 1 ? res.locals.config.bootswatchSkin : '') || meta.config.bootswatchSkin || ''; templateValues.config.bootswatchSkin = templateValues.bootswatchSkin || 'noskin'; // TODO remove in v1.12.0+ - - const unreadCounts = results.unreadData.counts; - const unreadCount = { - topic: unreadCounts[''] || 0, - newTopic: unreadCounts.new || 0, - watchedTopic: unreadCounts.watched || 0, - unrepliedTopic: unreadCounts.unreplied || 0, - chat: results.unreadChatCount || 0, - notification: results.unreadNotificationCount || 0, - }; - - Object.keys(unreadCount).forEach(function (key) { - if (unreadCount[key] > 99) { - unreadCount[key] = '99+'; - } - }); - - const tidsByFilter = results.unreadData.tidsByFilter; - results.navigation = results.navigation.map(function (item) { - function modifyNavItem(item, route, filter, content) { - if (item && validator.unescape(item.originalRoute) === route) { - unreadData[filter] = _.zipObject(tidsByFilter[filter], tidsByFilter[filter].map(() => true)); - item.content = content; - if (unreadCounts[filter] > 0) { - item.iconClass += ' unread-count'; - } - } - } - modifyNavItem(item, '/unread', '', unreadCount.topic); - modifyNavItem(item, '/unread?filter=new', 'new', unreadCount.newTopic); - modifyNavItem(item, '/unread?filter=watched', 'watched', unreadCount.watchedTopic); - modifyNavItem(item, '/unread?filter=unreplied', 'unreplied', unreadCount.unrepliedTopic); - return item; - }); - templateValues.browserTitle = results.browserTitle; - templateValues.navigation = results.navigation; - templateValues.unreadCount = unreadCount; + ({ + navigation: templateValues.navigation, + unreadCount: templateValues.unreadCount, + } = await appendUnreadCounts({ + uid: req.uid, + navigation: results.navigation, + unreadData, + })); templateValues.isAdmin = results.user.isAdmin; templateValues.isGlobalMod = results.user.isGlobalMod; templateValues.showModMenu = results.user.isAdmin || results.user.isGlobalMod || results.user.isMod; @@ -183,6 +152,73 @@ middleware.renderHeader = async function renderHeader(req, res, data) { return await req.app.renderAsync('header', hookReturn.templateValues); }; +async function appendUnreadCounts({ uid, navigation, unreadData }) { + const originalRoutes = navigation.map(nav => nav.originalRoute); + const calls = { + unreadData: topics.getUnreadData({ uid: uid }), + unreadChatCount: messaging.getUnreadCount(uid), + unreadNotificationCount: user.notifications.getUnreadCount(uid), + unreadFlagCount: (async function () { + if (originalRoutes.includes('/flags') && await user.isPrivileged(uid)) { + return flags.getCount({ + uid, + filters: { + quick: 'unresolved', + cid: (await user.isAdminOrGlobalMod(uid)) ? [] : (await user.getModeratedCids(uid)), + }, + }); + } + return 0; + }()), + }; + const results = await utils.promiseParallel(calls); + + const unreadCounts = results.unreadData.counts; + const unreadCount = { + topic: unreadCounts[''] || 0, + newTopic: unreadCounts.new || 0, + watchedTopic: unreadCounts.watched || 0, + unrepliedTopic: unreadCounts.unreplied || 0, + chat: results.unreadChatCount || 0, + notification: results.unreadNotificationCount || 0, + flags: results.unreadFlagCount || 0, + }; + + Object.keys(unreadCount).forEach(function (key) { + if (unreadCount[key] > 99) { + unreadCount[key] = '99+'; + } + }); + + const tidsByFilter = results.unreadData.tidsByFilter; + navigation = navigation.map(function (item) { + function modifyNavItem(item, route, filter, content) { + if (item && item.originalRoute === route) { + unreadData[filter] = _.zipObject(tidsByFilter[filter], tidsByFilter[filter].map(() => true)); + item.content = content; + if (unreadCounts[filter] > 0) { + item.iconClass += ' unread-count'; + } + } + } + modifyNavItem(item, '/unread', '', unreadCount.topic); + modifyNavItem(item, '/unread?filter=new', 'new', unreadCount.newTopic); + modifyNavItem(item, '/unread?filter=watched', 'watched', unreadCount.watchedTopic); + modifyNavItem(item, '/unread?filter=unreplied', 'unreplied', unreadCount.unrepliedTopic); + + ['flags'].forEach((prop) => { + if (item && item.originalRoute === `/${prop}` && unreadCount[prop] > 0) { + item.iconClass += ' unread-count'; + item.content = unreadCount.flags; + } + }); + + return item; + }); + + return { navigation, unreadCount }; +} + middleware.renderFooter = async function renderFooter(req, res, templateValues) { const data = await plugins.hooks.fire('filter:middleware.renderFooter', { req: req, diff --git a/src/navigation/index.js b/src/navigation/index.js index ed639afbfa..a581508367 100644 --- a/src/navigation/index.js +++ b/src/navigation/index.js @@ -1,6 +1,7 @@ 'use strict'; const nconf = require('nconf'); +const validator = require('validator'); const admin = require('./admin'); const groups = require('../groups'); @@ -12,7 +13,7 @@ navigation.get = async function (uid) { let data = await admin.get(); data = data.filter(item => item && item.enabled).map(function (item) { - item.originalRoute = item.route; + item.originalRoute = validator.unescape(item.route); if (!item.route.startsWith('http')) { item.route = relative_path + item.route;