| 
									
										
										
										
											2015-01-03 20:07:09 -05:00
										 |  |  | 'use strict'; | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | const assert = require('assert'); | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | const fs = require('fs'); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | const path = require('path'); | 
					
						
							|  |  |  | const nconf = require('nconf'); | 
					
						
							| 
									
										
										
										
											2021-09-03 16:46:14 -04:00
										 |  |  | const validator = require('validator'); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | const jwt = require('jsonwebtoken'); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | const { setTimeout } = require('node:timers/promises'); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const db = require('./mocks/databasemock'); | 
					
						
							|  |  |  | const User = require('../src/user'); | 
					
						
							|  |  |  | const Topics = require('../src/topics'); | 
					
						
							|  |  |  | const Categories = require('../src/categories'); | 
					
						
							|  |  |  | const Posts = require('../src/posts'); | 
					
						
							|  |  |  | const groups = require('../src/groups'); | 
					
						
							| 
									
										
										
										
											2021-12-22 10:38:22 -05:00
										 |  |  | const messaging = require('../src/messaging'); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | const helpers = require('./helpers'); | 
					
						
							|  |  |  | const meta = require('../src/meta'); | 
					
						
							| 
									
										
										
										
											2022-02-03 16:49:41 -05:00
										 |  |  | const file = require('../src/file'); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | const socketUser = require('../src/socket.io/user'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | const apiUser = require('../src/api/users'); | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | const utils = require('../src/utils'); | 
					
						
							| 
									
										
										
										
											2022-11-15 17:53:15 -05:00
										 |  |  | const privileges = require('../src/privileges'); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | const request = require('../src/request'); | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | describe('User', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 	let userData; | 
					
						
							|  |  |  | 	let testUid; | 
					
						
							|  |  |  | 	let testCid; | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 	const plugins = require('../src/plugins'); | 
					
						
							| 
									
										
										
										
											2020-12-05 14:25:14 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	async function dummyEmailerHook(data) { | 
					
						
							|  |  |  | 		// pretend to handle sending emails
 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	before((done) => { | 
					
						
							| 
									
										
										
										
											2020-12-05 14:25:14 -07:00
										 |  |  | 		// Attach an emailer hook so related requests do not error
 | 
					
						
							| 
									
										
										
										
											2021-01-27 17:36:58 -05:00
										 |  |  | 		plugins.hooks.register('emailer-test', { | 
					
						
							| 
									
										
										
										
											2023-12-05 10:41:23 -05:00
										 |  |  | 			hook: 'static:email.send', | 
					
						
							| 
									
										
										
										
											2020-12-05 14:25:14 -07:00
										 |  |  | 			method: dummyEmailerHook, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 		Categories.create({ | 
					
						
							|  |  |  | 			name: 'Test Category', | 
					
						
							|  |  |  | 			description: 'A test', | 
					
						
							| 
									
										
										
										
											2017-02-17 19:31:21 -07:00
										 |  |  | 			order: 1, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		}, (err, categoryObj) => { | 
					
						
							| 
									
										
										
										
											2016-08-16 19:46:59 +02:00
										 |  |  | 			if (err) { | 
					
						
							|  |  |  | 				return done(err); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 			testCid = categoryObj.cid; | 
					
						
							|  |  |  | 			done(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	after(() => { | 
					
						
							| 
									
										
										
										
											2023-12-05 10:41:23 -05:00
										 |  |  | 		plugins.hooks.unregister('emailer-test', 'static:email.send'); | 
					
						
							| 
									
										
										
										
											2020-12-05 14:25:14 -07:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	beforeEach(() => { | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 		userData = { | 
					
						
							| 
									
										
										
										
											2014-03-06 14:51:43 -05:00
										 |  |  | 			username: 'John Smith', | 
					
						
							| 
									
										
										
										
											2015-05-07 13:43:06 -04:00
										 |  |  | 			fullname: 'John Smith McNamara', | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 			password: 'swordfish', | 
					
						
							|  |  |  | 			email: 'john@example.com', | 
					
						
							| 
									
										
										
										
											2017-02-17 19:31:21 -07:00
										 |  |  | 			callback: undefined, | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 	const goodImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAYAAAABtRhCAAAACXBIWXMAAC4jAAAuIwF4pT92AAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAACcJJREFUeNqMl9tvnNV6xn/f+s5z8DCeg88Zj+NYdhJH4KShFoJAIkzVphLVJnsDaiV6gUKaC2qQUFVATbnoValAakuQYKMqBKUUJCgI9XBBSmOROMqGoCStHbA9sWM7nrFn/I3n9B17kcwoabfarj9gvet53+d9nmdJAwMDAAgh8DyPtbU1XNfFMAwkScK2bTzPw/M8dF1/SAhxKAiCxxVF2aeqqqTr+q+Af+7o6Ch0d3f/69TU1KwkSRiGwbFjx3jmmWd47rnn+OGHH1BVFYX/5QRBkPQ87xeSJP22YRi/oapqStM0PM/D931kWSYIgnHf98cXFxepVqtomjZt2/Zf2bb990EQ4Pv+PXfeU1CSpGYhfN9/TgjxQTQaJQgCw
 | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.create(), when created', () => { | 
					
						
							|  |  |  | 		it('should be created properly', async () => { | 
					
						
							| 
									
										
										
										
											2021-07-07 09:48:49 -04:00
										 |  |  | 			testUid = await User.create({ username: userData.username, password: userData.password }); | 
					
						
							| 
									
										
										
										
											2019-10-07 23:13:43 -04:00
										 |  |  | 			assert.ok(testUid); | 
					
						
							| 
									
										
										
										
											2021-07-07 09:48:49 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			await User.setUserField(testUid, 'email', userData.email); | 
					
						
							|  |  |  | 			await User.email.confirmByUid(testUid); | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should be created properly', async () => { | 
					
						
							| 
									
										
										
										
											2022-08-04 16:02:16 -04:00
										 |  |  | 			const email = '<h1>test</h1>@gmail.com'; | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'weirdemail', email: email }); | 
					
						
							| 
									
										
										
										
											2019-10-07 23:13:43 -04:00
										 |  |  | 			const data = await User.getUserData(uid); | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-04 16:02:16 -04:00
										 |  |  | 			const validationPending = await User.email.isValidationPending(uid, email); | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 			assert.strictEqual(validationPending, true); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Bootstrap5 (#10894)
* chore: up deps
* chore: up composer
* fix(deps): bump 2factor to v7
* chore: up harmony
* chore: up harmony
* fix: missing await
* feat: allow middlewares to pass in template values via res.locals
* feat: buildAccountData middleware automatically added ot all account routes
* fix: properly allow values in res.locals.templateValues to be added to the template data
* refactor: user/blocks
* refactor(accounts): categories and consent
* feat: automatically 404 if exposeUid or exposeGroupName come up empty
* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now
* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization
* fix: move reputation removal check to accountHelpers method
* test: skip i18n tests if ref branch when present is not develop
* fix(deps): bump theme versions
* fix(deps): bump ntfy and 2factor
* chore: up harmony
* fix: add missing return
* fix: #11191, only focus on search input on md environments and up
* feat: allow file uploads on mobile chat
closes https://github.com/NodeBB/NodeBB/issues/11217
* chore: up themes
* chore: add lang string
* fix(deps): bump ntfy to 1.0.15
* refactor: use new if/each syntax
* chore: up composer
* fix: regression from user helper refactor
* chore: up harmony
* chore: up composer
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: fix composer version
* feat: add increment helper
* chore: up harmony
* fix: #11228 no timestamps in future :hourglass:
* chore: up harmony
* check config.theme as well
fire action:posts.loaded after processing dom
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up themes
* chore: up harmony
* remove extra class
* refactor: move these to core from harmony
* chore: up widgets
* chore: up widgets
* height auto
* fix: closes #11238
* dont focus inputs, annoying on mobile
* fix: dont focus twice, only focus on chat input on desktop
dont wrap widget footer in row
* chore: up harmony
* chore: up harmony
* update chat window
* chore: up themes
* fix cache buster for skins
* chat fixes
* chore: up harmony
* chore: up composer
* refactor: change hook logs to debug
* fix: scroll to post right after adding to dom
* fix: hash scrolling and highlighting correct post
* test: re-enable read API schema tests
* fix: add back schema changes for 179faa2270f2ad955dcc4a7b04755acce59e6ffd and c3920ccb10d8ead2dcd9914bb1784bed3f6adfd4
* fix: schema changes from 488f0978a4aa1ca1e4d2a1f2e8c7ef7a681f2f27
* fix: schema changes for f4cf482a874701ce80c0f306c49d8788cec66f87
* fix: schema update for be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 69c96078ea78ee2c45885a90a6f6a59f9042a33c
* fix: schema changes for d1364c313021e48a879a818b24947e1457c062f7
* fix: schema changes for 84ff1152f7552dd866e25a90972d970b9861107e
* fix: schema changes for b860c2605c209e0650ef98f4c80d842ea23a51ce
* fix: schema changes for 23cb67a1126481848fac39aafd1e253441e76d7f
* fix: schema changes for b916e42f400dac8aa51670b15e439f87f0eb8939
* fix: schema change for a9bbb586fcb3a1c61b5fb69052236e78cdf7d743
* fix: schema changes for 4b738c8cd36c936a1dbe2bb900c694bf6c5520ec
* fix: schema changes for 58b5781cea9acb129e6604a82ab5a5bfc0d8394d
* fix: schema changes for 794bf01b21709c4be06584d576d706b3d6342057
* fix: schema changes for 80ea12c1c1963f5b39fb64841e4f3c8da3c87af2, e368feef51e0766f119c9710fb4db8f64724725c, and 52ead114bec961c62fa2eb0786540e229f6e4873
* fix: composer-default object in config?
* fix: schema changes for 9acdc6808c070555352951c651921df181b10993 and 093093420027999df3c67bf0ea6024f6dbf81d2d
* fix: schema changes for c0a52924f1f7ef8caeaacda67363ac269b56042c
* fix: schema change for aba420a3f3b774e949c2539c73f3dc0e1ae79a38, move loggedInUser to optional props
* fix: schema changes for 8c67031609da30d788561459f8bb76e9a69253de
* fix: schema changes for 27e53b42f3ce48fa61d3754375715cd41ffe808d
* fix: schema changes for 28359665187b0a3b9ec6226dca1234ebdbd725a5
* fix: breaking test for email confirmation API call
* fix: schema changes for refactored search page
* fix: schema changes for user object
* fix: schema changes for 9f531f957e08eabb4bae844ddd67bde14d9b59f0
* fix: schema changes for c4042c70decd628e5b880bd109515b47e4e16164 and 23175110a29640e6fa052db1079bfedb34a61055
* fix: schema changes for 9b3616b10392e247974eb0c1e6225a1582bf6c69
* fix: schema changes for 5afd5de07d42fd33f039a6f85ded3b4992200e5a
* fix: schema change for 1d7baf12171cffbd3af8914bef4e6297d1160d49
* fix: schema changes for 57bfb37c55a839662144e684875003ab52315ecc and be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 6e86b4afa20d662af8b9f1c07518df2d8c258105 and 3efad2e13b7319eb9a1f4fda7af047be43ebc11f and 68f66223e73a72f378f193c83a9b5546bede2cda
* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)
* fix: re-login on email change
* fix: schema changes for c926358d734a2fa410de87f4e4a91744215fc14a
* fix: schema changes for 388a8270c9882892bad5c8141f65da8d59eac0fd
* fix: schema change for 2658bcc821c22e137a6eeb9bb74098856a642eaf
* fix: no need to call account middlewares for chats routes
* fix: schema changes for 71743affc3e58dc85d4ffa15ce043d4d9ddd3d67
* fix: final schema changes
* test: support for anyOf and oneOf
* fix: check thumb
* dont scroll to top on back press
* remove group log
* fix: add top margin to merged and deleted alerts
* chore: up widgets
* fix: improve fix-lists mixin
* chore: up harmony/composer
* feat: allow hiding quicksearch results during search
* dont record searches made by composer
* chore: up 54
* chore: up spam be gone
* feat: add prev/next page and page count into mobile paginator
* chore: up harmony
* chore: up harmony
* use old style for IS
* fix: hide entire toolbar row if no posts or not singlePost
* fix: updated messaging for post-queue template, #11206
* fix: btn-sm on post queue back button
* fix: bump harmony, closes #11206
* fix: remove unused alert module import
* fix: bump harmony
* fix: bump harmony
* chore: up harmony
* refactor: IS scrolltop
* fix: update users:search-user-for-chat source string
* feat: support for mark-read toggle on chats dropdown and recent chats list
* feat: api v3 calls to mark chat read/unread
* feat: send event:chats.mark socket event on mark read or unread
* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling
* docs: openapi schema updates for chat marking
* fix: allow unread state toggling in chats dropdown too
* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread
* fix: debug log
* refactor: move userSearch filter to a module
* feat(routes): allow remounting /categories (#11230)
* feat: send flags count to frontend on flags list page
* refactor: filter form client-side js to extract out some logic
* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden
* fix: use userFilter module for assignee, reporterId, targetUid
* fix(openapi): schema changes for updated flags page
* fix: dont allow adding duplicates to userFilter
* use same var
* remove log
* fix: closes #11282
* feat: lang key for x-topics
* chore: up harmony
* chore: up emoji
* chore: up harmony
* fix: update userFilter to allow new option `selectedBlock`
* fix: wrong block name passed to userFilter
* fix: https://github.com/NodeBB/NodeBB/issues/11283
* fix: chats, allow multiple dropdowns like in harmony
* chore: up harmony
* refactor: flag note adding/editing, closes #11285
* fix: remove old prepareEdit logic
* chore: add caveat about hacky code block in userFilter module
* fix: placeholders for userFilter module
* refactor: navigator so it works with multiple thumbs/navigators
* chore: up harmony
* fix: closes #11287, destroy quick reply autocomplete
on navigation
* fix: filter disabled categories on user categories page count
* chore: up harmony
* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying
* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests
* fix: tweak table order in ACP dash searches
* fix: only invoke navigator click drag on left mouse button
* feat: add back unread indicator to navigator
* clear bookmark on mark unread
* fix: navigator crash on ajaxify
* better thumb top calculation
* fix: reset user bookmark when topic is marked unread
* Revert "fix: reset user bookmark when topic is marked unread"
This reverts commit 9bcd85c2c6848c3d325d32027261809da6e11c9e.
* fix: update unread indicator on scroll, add unread count
* chore: bump harmony
* fix: crash on navigator unread update when backing out of a topic
* fix: closes #11183
* fix: update topics:recent zset when rescheduling a topic
* fix: dupe quote button, increase delay, hide immediately on empty selection
* fix: navigator not showing up on first load
* refactor: remove glance
assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes
* more sanity checks for bookmark
dont allow setting bookmark higher than topic postcount
* closes #11218, :train:
* Revert "fix: update topics:recent zset when rescheduling a topic"
This reverts commit 737973cca9e94b6cb3867492a09e1e0b1af391d5.
* fix: #11306, show proper error if queued post doesn't exist
was showing no-privileges if someone else accepted the post
* https://github.com/NodeBB/NodeBB/issues/11307
dont use li
* chore: up harmony
* chore: bump version string
* fix: copy paste fail
* feat: closes #7382, tag filtering
add client side support for filtering by tags on /category, /recent and /unread
* chore: up harmony
* chore: up harmony
* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]
This reverts commit cf6cc2c454dc35c330393c62ee8ce67b42d8eefb.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x
This is a breaking change.
* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309
* chore: up deps
* fix: tests, use x-csrf-token query param removed
* test: fix csrf_token
* lint: remove unused
* feat: add itemprop="image" to avatar helper
* fix: get chat upload button in chat modal
* breaking: remove deprecated socket.io methods
* test: update messaging tests to not use sockets
* fix: parent post links
* fix: prevent post tooltip if mouse leaves before data/tpl is loaded
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up harmony
* fix: nested replies indices
* fix(deps): bump 2factor
* feat: add loggedIn user to all api routes
* chore: up themes
* refactor: audit admin v3 write api routes as per #11321
* refactor: audit category v3 write api routes as per #11321 [breaking]
docs: fix open api spec for #11321
* refactor: audit chat v3 write api routes as per #11321
* refactor: audit files v3 write api routes as per #11321
* refactor: audit flags v3 write api routes as per #11321
* refactor: audit posts v3 write api routes as per #11321
* refactor: audit topics v3 write api routes as per #11321
* refactor: audit users v3 write api routes as per #11321
* fix: lang string
* remove min height
* fix: empty topic/labels taking up space
* fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
* chore: up harmony
* fix: closes #11354, show no post error if queued post already accepted/rejected
* test: #11354
* test: #11354
* fix(deps): bump 2factor
* fix: #11357 clear cache on thumb remove
* fix: thumb remove on windows, closes #11357
* test: openapi for thumbs
* test: fix openapi
---------
Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
											
										 
											2023-03-17 11:58:31 -04:00
										 |  |  | 			assert.equal(data.email, ''); | 
					
						
							| 
									
										
										
										
											2019-10-07 23:13:43 -04:00
										 |  |  | 			assert.strictEqual(data.profileviews, 0); | 
					
						
							|  |  |  | 			assert.strictEqual(data.reputation, 0); | 
					
						
							|  |  |  | 			assert.strictEqual(data.postcount, 0); | 
					
						
							|  |  |  | 			assert.strictEqual(data.topiccount, 0); | 
					
						
							|  |  |  | 			assert.strictEqual(data.lastposttime, 0); | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 			assert.strictEqual(data.banned, false); | 
					
						
							| 
									
										
										
										
											2019-02-11 14:29:25 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should have a valid email, if using an email', (done) => { | 
					
						
							|  |  |  | 			User.create({ username: userData.username, password: userData.password, email: 'fakeMail' }, (err) => { | 
					
						
							| 
									
										
										
										
											2015-10-19 11:45:07 -04:00
										 |  |  | 				assert(err); | 
					
						
							|  |  |  | 				assert.equal(err.message, '[[error:invalid-email]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2017-05-26 00:02:20 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error with invalid password', (done) => { | 
					
						
							|  |  |  | 			User.create({ username: 'test', password: '1' }, (err) => { | 
					
						
							| 
									
										
										
										
											2023-10-05 12:48:50 -04:00
										 |  |  | 				assert.equal(err.message, '[[reset_password:password-too-short]]'); | 
					
						
							| 
									
										
										
										
											2017-05-26 00:02:20 -04:00
										 |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error with invalid password', (done) => { | 
					
						
							|  |  |  | 			User.create({ username: 'test', password: {} }, (err) => { | 
					
						
							| 
									
										
										
										
											2017-05-26 00:02:20 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-password]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error with a too long password', (done) => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			let toolong = ''; | 
					
						
							|  |  |  | 			for (let i = 0; i < 5000; i++) { | 
					
						
							| 
									
										
										
										
											2017-05-26 00:02:20 -04:00
										 |  |  | 				toolong += 'a'; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			User.create({ username: 'test', password: toolong }, (err) => { | 
					
						
							| 
									
										
										
										
											2017-05-26 00:02:20 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:password-too-long]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-06-18 23:16:48 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error if username is already taken or rename user', async () => { | 
					
						
							| 
									
										
										
										
											2020-06-18 23:16:48 -04:00
										 |  |  | 			let err; | 
					
						
							|  |  |  | 			async function tryCreate(data) { | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					return await User.create(data); | 
					
						
							|  |  |  | 				} catch (_err) { | 
					
						
							|  |  |  | 					err = _err; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 21:42:23 -04:00
										 |  |  | 			const [uid1, uid2] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2020-06-18 23:16:48 -04:00
										 |  |  | 				tryCreate({ username: 'dupe1' }), | 
					
						
							|  |  |  | 				tryCreate({ username: 'dupe1' }), | 
					
						
							|  |  |  | 			]); | 
					
						
							| 
									
										
										
										
											2020-07-23 21:42:23 -04:00
										 |  |  | 			if (err) { | 
					
						
							|  |  |  | 				assert.strictEqual(err.message, '[[error:username-taken]]'); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const userData = await User.getUsersFields([uid1, uid2], ['username']); | 
					
						
							|  |  |  | 				const userNames = userData.map(u => u.username); | 
					
						
							|  |  |  | 				// make sure only 1 dupe1 is created
 | 
					
						
							|  |  |  | 				assert.equal(userNames.filter(username => username === 'dupe1').length, 1); | 
					
						
							|  |  |  | 				assert.equal(userNames.filter(username => username === 'dupe1 0').length, 1); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-18 23:16:48 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error if email is already taken', async () => { | 
					
						
							| 
									
										
										
										
											2020-06-18 23:16:48 -04:00
										 |  |  | 			let err; | 
					
						
							|  |  |  | 			async function tryCreate(data) { | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					return await User.create(data); | 
					
						
							|  |  |  | 				} catch (_err) { | 
					
						
							|  |  |  | 					err = _err; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await Promise.all([ | 
					
						
							|  |  |  | 				tryCreate({ username: 'notdupe1', email: 'dupe@dupe.com' }), | 
					
						
							|  |  |  | 				tryCreate({ username: 'notdupe2', email: 'dupe@dupe.com' }), | 
					
						
							|  |  |  | 			]); | 
					
						
							|  |  |  | 			assert.strictEqual(err.message, '[[error:email-taken]]'); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2013-11-04 01:32:31 +02:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.uniqueUsername()', () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should deal with collisions', async () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const users = []; | 
					
						
							|  |  |  | 			for (let i = 0; i < 10; i += 1) { | 
					
						
							| 
									
										
										
										
											2017-03-23 16:00:22 -06:00
										 |  |  | 				users.push({ | 
					
						
							|  |  |  | 					username: 'Jane Doe', | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 					email: `jane.doe${i}@example.com`, | 
					
						
							| 
									
										
										
										
											2017-03-23 16:00:22 -06:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			for (const user of users) { | 
					
						
							|  |  |  | 				// eslint-disable-next-line no-await-in-loop
 | 
					
						
							|  |  |  | 				await User.create(user); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-03-23 16:00:22 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			const username = await User.uniqueUsername({ | 
					
						
							|  |  |  | 				username: 'Jane Doe', | 
					
						
							|  |  |  | 				userslug: 'jane-doe', | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			assert.strictEqual(username, 'Jane Doe 9'); | 
					
						
							| 
									
										
										
										
											2017-03-23 16:00:22 -06:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.isModerator()', () => { | 
					
						
							|  |  |  | 		it('should return false', (done) => { | 
					
						
							|  |  |  | 			User.isModerator(testUid, testCid, (err, isModerator) => { | 
					
						
							| 
									
										
										
										
											2016-08-16 19:46:59 +02:00
										 |  |  | 				assert.equal(err, null); | 
					
						
							| 
									
										
										
										
											2014-12-02 15:33:23 -05:00
										 |  |  | 				assert.equal(isModerator, false); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return two false results', (done) => { | 
					
						
							|  |  |  | 			User.isModerator([testUid, testUid], testCid, (err, isModerator) => { | 
					
						
							| 
									
										
										
										
											2016-08-16 19:46:59 +02:00
										 |  |  | 				assert.equal(err, null); | 
					
						
							| 
									
										
										
										
											2014-12-02 15:33:23 -05:00
										 |  |  | 				assert.equal(isModerator[0], false); | 
					
						
							|  |  |  | 				assert.equal(isModerator[1], false); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return two false results', (done) => { | 
					
						
							|  |  |  | 			User.isModerator(testUid, [testCid, testCid], (err, isModerator) => { | 
					
						
							| 
									
										
										
										
											2016-08-16 19:46:59 +02:00
										 |  |  | 				assert.equal(err, null); | 
					
						
							| 
									
										
										
										
											2014-12-02 15:33:23 -05:00
										 |  |  | 				assert.equal(isModerator[0], false); | 
					
						
							|  |  |  | 				assert.equal(isModerator[1], false); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.getModeratorUids()', () => { | 
					
						
							|  |  |  | 		before((done) => { | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 			groups.join('cid:1:privileges:moderate', 1, done); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should retrieve all users with moderator bit in category privilege', (done) => { | 
					
						
							|  |  |  | 			User.getModeratorUids((err, uids) => { | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual(1, uids.length); | 
					
						
							| 
									
										
										
										
											2017-07-07 12:10:42 -04:00
										 |  |  | 				assert.strictEqual(1, parseInt(uids[0], 10)); | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		after((done) => { | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 			groups.leave('cid:1:privileges:moderate', 1, done); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.getModeratorUids()', () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			await groups.create({ name: 'testGroup' }); | 
					
						
							|  |  |  | 			await groups.join('cid:1:privileges:groups:moderate', 'testGroup'); | 
					
						
							|  |  |  | 			await groups.join('testGroup', 1); | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should retrieve all users with moderator bit in category privilege', (done) => { | 
					
						
							|  |  |  | 			User.getModeratorUids((err, uids) => { | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual(1, uids.length); | 
					
						
							| 
									
										
										
										
											2017-07-07 12:10:42 -04:00
										 |  |  | 				assert.strictEqual(1, parseInt(uids[0], 10)); | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		after(async () => { | 
					
						
							|  |  |  | 			groups.leave('cid:1:privileges:groups:moderate', 'testGroup'); | 
					
						
							|  |  |  | 			groups.destroy('testGroup'); | 
					
						
							| 
									
										
										
										
											2017-07-07 11:56:25 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.isReadyToPost()', () => { | 
					
						
							|  |  |  | 		it('should allow a post if the last post time is > 10 seconds', (done) => { | 
					
						
							|  |  |  | 			User.setUserField(testUid, 'lastposttime', +new Date() - (11 * 1000), () => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 				Topics.post({ | 
					
						
							|  |  |  | 					uid: testUid, | 
					
						
							|  |  |  | 					title: 'Topic 3', | 
					
						
							|  |  |  | 					content: 'lorem ipsum', | 
					
						
							| 
									
										
										
										
											2017-02-17 19:31:21 -07:00
										 |  |  | 					cid: testCid, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}, (err) => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error when a new user posts if the last post time is 10 < 30 seconds', (done) => { | 
					
						
							| 
									
										
										
										
											2017-11-02 14:55:05 -04:00
										 |  |  | 			meta.config.newbiePostDelay = 30; | 
					
						
							| 
									
										
										
										
											2023-11-07 12:36:40 -05:00
										 |  |  | 			meta.config.newbieReputationThreshold = 3; | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			User.setUserField(testUid, 'lastposttime', +new Date() - (20 * 1000), () => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 				Topics.post({ | 
					
						
							|  |  |  | 					uid: testUid, | 
					
						
							|  |  |  | 					title: 'Topic 4', | 
					
						
							|  |  |  | 					content: 'lorem ipsum', | 
					
						
							| 
									
										
										
										
											2017-02-17 19:31:21 -07:00
										 |  |  | 					cid: testCid, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}, (err) => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 					assert(err); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not error if a non-newbie user posts if the last post time is 10 < 30 seconds', (done) => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 			User.setUserFields(testUid, { | 
					
						
							| 
									
										
										
										
											2017-02-18 01:27:46 -07:00
										 |  |  | 				lastposttime: +new Date() - (20 * 1000), | 
					
						
							| 
									
										
										
										
											2017-02-17 19:31:21 -07:00
										 |  |  | 				reputation: 10, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			}, () => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 				Topics.post({ | 
					
						
							|  |  |  | 					uid: testUid, | 
					
						
							|  |  |  | 					title: 'Topic 5', | 
					
						
							|  |  |  | 					content: 'lorem ipsum', | 
					
						
							| 
									
										
										
										
											2017-02-17 19:31:21 -07:00
										 |  |  | 					cid: testCid, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}, (err) => { | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2022-12-25 15:05:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		it('should only post 1 topic out of 10', async () => { | 
					
						
							|  |  |  | 			await User.create({ username: 'flooder', password: '123456' }); | 
					
						
							|  |  |  | 			const { jar } = await helpers.loginUser('flooder', '123456'); | 
					
						
							|  |  |  | 			const titles = new Array(10).fill('topic title'); | 
					
						
							|  |  |  | 			const res = await Promise.allSettled(titles.map(async (title) => { | 
					
						
							|  |  |  | 				const { body } = await helpers.request('post', '/api/v3/topics', { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 					body: { | 
					
						
							| 
									
										
										
										
											2022-12-25 15:05:15 -05:00
										 |  |  | 						cid: testCid, | 
					
						
							|  |  |  | 						title: title, | 
					
						
							|  |  |  | 						content: 'the content', | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					jar: jar, | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 				return body.status; | 
					
						
							|  |  |  | 			})); | 
					
						
							|  |  |  | 			const failed = res.filter(res => res.value.code === 'bad-request'); | 
					
						
							|  |  |  | 			const success = res.filter(res => res.value.code === 'ok'); | 
					
						
							|  |  |  | 			assert.strictEqual(failed.length, 9); | 
					
						
							|  |  |  | 			assert.strictEqual(success.length, 1); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2014-10-31 13:22:42 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.search()', () => { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 		let adminUid; | 
					
						
							|  |  |  | 		let uid; | 
					
						
							|  |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			adminUid = await User.create({ username: 'noteadmin' }); | 
					
						
							|  |  |  | 			await groups.join('administrators', adminUid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return an object containing an array of matching users', (done) => { | 
					
						
							|  |  |  | 			User.search({ query: 'john' }, (err, searchData) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				uid = searchData.users[0].uid; | 
					
						
							|  |  |  | 				assert.equal(Array.isArray(searchData.users) && searchData.users.length > 0, true); | 
					
						
							|  |  |  | 				assert.equal(searchData.users[0].username, 'John Smith'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should search user', async () => { | 
					
						
							|  |  |  | 			const searchData = await apiUser.search({ uid: testUid }, { query: 'john' }); | 
					
						
							|  |  |  | 			assert.equal(searchData.users[0].username, 'John Smith'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should error for guest', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.search({ uid: 0 }, { query: 'john' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2018-05-28 11:29:37 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:no-privileges]]'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should error with invalid data', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.search({ uid: testUid }, null); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should error for unprivileged user', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.search({ uid: testUid }, { searchBy: 'ip', query: '123' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:no-privileges]]'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should error for unprivileged user', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.search({ uid: testUid }, { filters: ['banned'], query: '123' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:no-privileges]]'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should error for unprivileged user', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.search({ uid: testUid }, { filters: ['flagged'], query: '123' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:no-privileges]]'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should search users by ip', async () => { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 			const uid = await User.create({ username: 'ipsearch' }); | 
					
						
							|  |  |  | 			await db.sortedSetAdd('ip:1.1.1.1:uid', [1, 1], [testUid, uid]); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			const data = await apiUser.search({ uid: adminUid }, { query: '1.1.1.1', searchBy: 'ip' }); | 
					
						
							| 
									
										
										
										
											2020-07-04 10:19:05 -04:00
										 |  |  | 			assert(Array.isArray(data.users)); | 
					
						
							|  |  |  | 			assert.equal(data.users.length, 2); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should search users by uid', async () => { | 
					
						
							|  |  |  | 			const data = await apiUser.search({ uid: testUid }, { query: uid, searchBy: 'uid' }); | 
					
						
							|  |  |  | 			assert(Array.isArray(data.users)); | 
					
						
							|  |  |  | 			assert.equal(data.users[0].uid, uid); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should search users by fullname', async () => { | 
					
						
							| 
									
										
										
										
											2020-09-11 23:20:49 -04:00
										 |  |  | 			const uid = await User.create({ username: 'fullnamesearch1', fullname: 'Mr. Fullname' }); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			const data = await apiUser.search({ uid: adminUid }, { query: 'mr', searchBy: 'fullname' }); | 
					
						
							| 
									
										
										
										
											2020-09-11 23:20:49 -04:00
										 |  |  | 			assert(Array.isArray(data.users)); | 
					
						
							|  |  |  | 			assert.equal(data.users.length, 1); | 
					
						
							|  |  |  | 			assert.equal(uid, data.users[0].uid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should search users by fullname', async () => { | 
					
						
							| 
									
										
										
										
											2020-09-11 23:20:49 -04:00
										 |  |  | 			const uid = await User.create({ username: 'fullnamesearch2', fullname: 'Baris:Usakli' }); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			const data = await apiUser.search({ uid: adminUid }, { query: 'baris:', searchBy: 'fullname' }); | 
					
						
							| 
									
										
										
										
											2020-09-11 23:20:49 -04:00
										 |  |  | 			assert(Array.isArray(data.users)); | 
					
						
							|  |  |  | 			assert.equal(data.users.length, 1); | 
					
						
							|  |  |  | 			assert.equal(uid, data.users[0].uid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should return empty array if query is empty', async () => { | 
					
						
							|  |  |  | 			const data = await apiUser.search({ uid: testUid }, { query: '' }); | 
					
						
							|  |  |  | 			assert.equal(data.users.length, 0); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 		it('should filter users', async () => { | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'ipsearch_filter' }); | 
					
						
							|  |  |  | 			await User.bans.ban(uid, 0, ''); | 
					
						
							|  |  |  | 			await User.setUserFields(uid, { flags: 10 }); | 
					
						
							|  |  |  | 			const data = await apiUser.search({ uid: adminUid }, { | 
					
						
							|  |  |  | 				query: 'ipsearch', | 
					
						
							|  |  |  | 				filters: ['online', 'banned', 'flagged'], | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:38:15 -05:00
										 |  |  | 			assert.equal(data.users[0].username, 'ipsearch_filter'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should sort results by username', async () => { | 
					
						
							|  |  |  | 			await User.create({ username: 'brian' }); | 
					
						
							|  |  |  | 			await User.create({ username: 'baris' }); | 
					
						
							|  |  |  | 			await User.create({ username: 'bzari' }); | 
					
						
							|  |  |  | 			const data = await User.search({ | 
					
						
							|  |  |  | 				uid: testUid, | 
					
						
							|  |  |  | 				query: 'b', | 
					
						
							|  |  |  | 				sortBy: 'username', | 
					
						
							|  |  |  | 				paginate: false, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			assert.equal(data.users[0].username, 'baris'); | 
					
						
							|  |  |  | 			assert.equal(data.users[1].username, 'brian'); | 
					
						
							|  |  |  | 			assert.equal(data.users[2].username, 'bzari'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('.delete()', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		let uid; | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		before((done) => { | 
					
						
							|  |  |  | 			User.create({ username: 'usertodelete', password: '123456', email: 'delete@me.com' }, (err, newUid) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				uid = newUid; | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should delete a user account', (done) => { | 
					
						
							|  |  |  | 			User.delete(1, uid, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				User.existsBySlug('usertodelete', (err, exists) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.equal(exists, false); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-05-02 13:27:16 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 18:40:23 -05:00
										 |  |  | 		it('should not re-add user to users:postcount if post is purged after user account deletion', async () => { | 
					
						
							| 
									
										
										
										
											2020-05-02 13:27:16 -04:00
										 |  |  | 			const uid = await User.create({ username: 'olduserwithposts' }); | 
					
						
							|  |  |  | 			assert(await db.isSortedSetMember('users:postcount', uid)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const result = await Topics.post({ | 
					
						
							|  |  |  | 				uid: uid, | 
					
						
							|  |  |  | 				title: 'old user topic', | 
					
						
							|  |  |  | 				content: 'old user topic post content', | 
					
						
							|  |  |  | 				cid: testCid, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			assert.equal(await db.sortedSetScore('users:postcount', uid), 1); | 
					
						
							|  |  |  | 			await User.deleteAccount(uid); | 
					
						
							|  |  |  | 			assert(!await db.isSortedSetMember('users:postcount', uid)); | 
					
						
							|  |  |  | 			await Posts.purge(result.postData.pid, 1); | 
					
						
							|  |  |  | 			assert(!await db.isSortedSetMember('users:postcount', uid)); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 18:40:23 -05:00
										 |  |  | 		it('should not re-add user to users:reputation if post is upvoted after user account deletion', async () => { | 
					
						
							| 
									
										
										
										
											2020-05-02 13:27:16 -04:00
										 |  |  | 			const uid = await User.create({ username: 'olduserwithpostsupvote' }); | 
					
						
							|  |  |  | 			assert(await db.isSortedSetMember('users:reputation', uid)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const result = await Topics.post({ | 
					
						
							|  |  |  | 				uid: uid, | 
					
						
							|  |  |  | 				title: 'old user topic', | 
					
						
							|  |  |  | 				content: 'old user topic post content', | 
					
						
							|  |  |  | 				cid: testCid, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			assert.equal(await db.sortedSetScore('users:reputation', uid), 0); | 
					
						
							|  |  |  | 			await User.deleteAccount(uid); | 
					
						
							|  |  |  | 			assert(!await db.isSortedSetMember('users:reputation', uid)); | 
					
						
							|  |  |  | 			await Posts.upvote(result.postData.pid, 1); | 
					
						
							|  |  |  | 			assert(!await db.isSortedSetMember('users:reputation', uid)); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2021-04-23 14:46:54 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		it('should delete user even if they started a chat', async () => { | 
					
						
							|  |  |  | 			const socketModules = require('../src/socket.io/modules'); | 
					
						
							|  |  |  | 			const uid1 = await User.create({ username: 'chatuserdelete1' }); | 
					
						
							|  |  |  | 			const uid2 = await User.create({ username: 'chatuserdelete2' }); | 
					
						
							| 
									
										
										
										
											2023-07-12 13:03:54 -04:00
										 |  |  | 			const roomId = await messaging.newRoom(uid1, { uids: [uid2] }); | 
					
						
							| 
									
										
										
										
											2021-12-22 10:38:22 -05:00
										 |  |  | 			await messaging.addMessage({ | 
					
						
							|  |  |  | 				uid: uid1, | 
					
						
							|  |  |  | 				content: 'hello', | 
					
						
							|  |  |  | 				roomId, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			await messaging.leaveRoom([uid2], roomId); | 
					
						
							| 
									
										
										
										
											2021-04-23 14:46:54 -04:00
										 |  |  | 			await User.delete(1, uid1); | 
					
						
							|  |  |  | 			assert.strictEqual(await User.exists(uid1), false); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('hash methods', () => { | 
					
						
							|  |  |  | 		it('should return uid from email', (done) => { | 
					
						
							|  |  |  | 			User.getUidByEmail('john@example.com', (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.equal(parseInt(uid, 10), parseInt(testUid, 10)); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return uid from username', (done) => { | 
					
						
							|  |  |  | 			User.getUidByUsername('John Smith', (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.equal(parseInt(uid, 10), parseInt(testUid, 10)); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return uid from userslug', (done) => { | 
					
						
							|  |  |  | 			User.getUidByUserslug('john-smith', (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.equal(parseInt(uid, 10), parseInt(testUid, 10)); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should get user data even if one uid is NaN', (done) => { | 
					
						
							|  |  |  | 			User.getUsersData([NaN, testUid], (err, data) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert(data[0]); | 
					
						
							|  |  |  | 				assert.equal(data[0].username, '[[global:guest]]'); | 
					
						
							|  |  |  | 				assert(data[1]); | 
					
						
							|  |  |  | 				assert.equal(data[1].username, userData.username); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not return private user data', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			User.setUserFields(testUid, { | 
					
						
							|  |  |  | 				fb_token: '123123123', | 
					
						
							|  |  |  | 				another_secret: 'abcde', | 
					
						
							|  |  |  | 				postcount: '123', | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			}, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				User.getUserData(testUid, (err, userData) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert(!userData.hasOwnProperty('fb_token')); | 
					
						
							|  |  |  | 					assert(!userData.hasOwnProperty('another_secret')); | 
					
						
							|  |  |  | 					assert(!userData.hasOwnProperty('password')); | 
					
						
							|  |  |  | 					assert(!userData.hasOwnProperty('rss_token')); | 
					
						
							| 
									
										
										
										
											2018-10-21 16:47:51 -04:00
										 |  |  | 					assert.strictEqual(userData.postcount, 123); | 
					
						
							|  |  |  | 					assert.strictEqual(userData.uid, testUid); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not return password even if explicitly requested', (done) => { | 
					
						
							|  |  |  | 			User.getUserFields(testUid, ['password'], (err, payload) => { | 
					
						
							| 
									
										
										
										
											2019-04-10 13:55:53 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert(!payload.hasOwnProperty('password')); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 18:38:02 -05:00
										 |  |  | 		it('should not modify the fields array passed in', async () => { | 
					
						
							|  |  |  | 			const fields = ['username', 'email']; | 
					
						
							|  |  |  | 			await User.getUserFields(testUid, fields); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(fields, ['username', 'email']); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-19 11:32:08 -05:00
										 |  |  | 		it('should return an icon text and valid background if username and picture is explicitly requested', async () => { | 
					
						
							|  |  |  | 			const payload = await User.getUserFields(testUid, ['username', 'picture']); | 
					
						
							| 
									
										
										
										
											2024-06-11 17:20:31 -04:00
										 |  |  | 			const validBackgrounds = await User.getIconBackgrounds(); | 
					
						
							| 
									
										
										
										
											2021-02-19 11:32:08 -05:00
										 |  |  | 			assert.strictEqual(payload['icon:text'], userData.username.slice(0, 1).toUpperCase()); | 
					
						
							|  |  |  | 			assert(payload['icon:bgColor']); | 
					
						
							|  |  |  | 			assert(validBackgrounds.includes(payload['icon:bgColor'])); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should return a valid background, even if an invalid background colour is set', async () => { | 
					
						
							|  |  |  | 			await User.setUserField(testUid, 'icon:bgColor', 'teal'); | 
					
						
							|  |  |  | 			const payload = await User.getUserFields(testUid, ['username', 'picture']); | 
					
						
							| 
									
										
										
										
											2024-06-11 17:20:31 -04:00
										 |  |  | 			const validBackgrounds = await User.getIconBackgrounds(); | 
					
						
							| 
									
										
										
										
											2021-02-19 11:32:08 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			assert(payload['icon:bgColor']); | 
					
						
							|  |  |  | 			assert(validBackgrounds.includes(payload['icon:bgColor'])); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return private data if field is whitelisted', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			function filterMethod(data, callback) { | 
					
						
							|  |  |  | 				data.whitelist.push('another_secret'); | 
					
						
							|  |  |  | 				callback(null, data); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 17:36:58 -05:00
										 |  |  | 			plugins.hooks.register('test-plugin', { hook: 'filter:user.whitelistFields', method: filterMethod }); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			User.getUserData(testUid, (err, userData) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert(!userData.hasOwnProperty('fb_token')); | 
					
						
							|  |  |  | 				assert.equal(userData.another_secret, 'abcde'); | 
					
						
							| 
									
										
										
										
											2021-01-27 17:36:58 -05:00
										 |  |  | 				plugins.hooks.unregister('test-plugin', 'filter:user.whitelistFields', filterMethod); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return 0 as uid if username is falsy', (done) => { | 
					
						
							|  |  |  | 			User.getUidByUsername('', (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual(uid, 0); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should get username by userslug', (done) => { | 
					
						
							|  |  |  | 			User.getUsernameByUserslug('john-smith', (err, username) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual('John Smith', username); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should get uids by emails', (done) => { | 
					
						
							|  |  |  | 			User.getUidsByEmails(['john@example.com'], (err, uids) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.equal(uids[0], testUid); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-11-21 23:34:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not get groupTitle for guests', (done) => { | 
					
						
							|  |  |  | 			User.getUserData(0, (err, userData) => { | 
					
						
							| 
									
										
										
										
											2018-11-21 23:34:08 -05:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual(userData.groupTitle, ''); | 
					
						
							|  |  |  | 				assert.deepStrictEqual(userData.groupTitleArray, []); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-11-22 21:57:56 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should load guest data', (done) => { | 
					
						
							|  |  |  | 			User.getUsersData([1, 0], (err, data) => { | 
					
						
							| 
									
										
										
										
											2018-11-22 21:57:56 -05:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual(data[1].username, '[[global:guest]]'); | 
					
						
							|  |  |  | 				assert.strictEqual(data[1].userslug, ''); | 
					
						
							|  |  |  | 				assert.strictEqual(data[1].uid, 0); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2025-02-11 13:28:25 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		it('should return null if field or user doesn not exist', async () => { | 
					
						
							|  |  |  | 			assert.strictEqual(await User.getUserField('1', 'doesnotexist'), null); | 
					
						
							|  |  |  | 			assert.strictEqual(await User.getUserField('doesnotexistkey', 'doesnotexist'), null); | 
					
						
							|  |  |  | 			assert.strictEqual(await User.getUserField('0', 'doesnotexist'), null); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('profile methods', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		let uid; | 
					
						
							|  |  |  | 		let jar; | 
					
						
							| 
									
										
										
										
											2022-11-09 13:20:28 -05:00
										 |  |  | 		let csrf_token; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			const newUid = await User.create({ username: 'updateprofile', email: 'update@me.com', password: '123456' }); | 
					
						
							|  |  |  | 			uid = newUid; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 			await User.setUserField(uid, 'email', 'update@me.com'); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 			await User.email.confirmByUid(uid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-09 13:20:28 -05:00
										 |  |  | 			({ jar, csrf_token } = await helpers.loginUser('updateprofile', '123456')); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 		it('should return error if not logged in', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.update({ uid: 0 }, { uid: 1 }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							|  |  |  | 				assert.equal(err.message, '[[error:invalid-uid]]'); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should return error if data is invalid', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.update({ uid: uid }, null); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 		it('should return error if data is missing uid', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.update({ uid: uid }, { username: 'bip', email: 'bop' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 		describe('.updateProfile()', () => { | 
					
						
							|  |  |  | 			let uid; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			it('should update a user\'s profile', async () => { | 
					
						
							|  |  |  | 				uid = await User.create({ username: 'justforupdate', email: 'just@for.updated', password: '123456' }); | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 				await User.setUserField(uid, 'email', 'just@for.updated'); | 
					
						
							|  |  |  | 				await User.email.confirmByUid(uid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				const data = { | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							|  |  |  | 					username: 'updatedUserName', | 
					
						
							|  |  |  | 					email: 'updatedEmail@me.com', | 
					
						
							|  |  |  | 					fullname: 'updatedFullname', | 
					
						
							|  |  |  | 					groupTitle: 'testGroup', | 
					
						
							|  |  |  | 					birthday: '01/01/1980', | 
					
						
							|  |  |  | 					signature: 'nodebb is good', | 
					
						
							|  |  |  | 					password: '123456', | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				const result = await apiUser.update({ uid: uid }, { ...data, password: '123456', invalid: 'field' }); | 
					
						
							|  |  |  | 				assert.equal(result.username, 'updatedUserName'); | 
					
						
							|  |  |  | 				assert.equal(result.userslug, 'updatedusername'); | 
					
						
							| 
									
										
										
										
											2024-11-25 18:42:59 -05:00
										 |  |  | 				assert.equal(result.fullname, 'updatedFullname'); | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				const userData = await db.getObject(`user:${uid}`); | 
					
						
							|  |  |  | 				Object.keys(data).forEach((key) => { | 
					
						
							|  |  |  | 					if (key === 'email') { | 
					
						
							|  |  |  | 						assert.strictEqual(userData.email, 'just@for.updated'); // email remains the same until confirmed
 | 
					
						
							|  |  |  | 					} else if (key !== 'password') { | 
					
						
							|  |  |  | 						assert.equal(data[key], userData[key]); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						assert(userData[key].startsWith('$2a$')); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				// updateProfile only saves valid fields
 | 
					
						
							|  |  |  | 				assert.strictEqual(userData.invalid, undefined); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			it('should also generate an email confirmation code for the changed email', async () => { | 
					
						
							| 
									
										
										
										
											2021-06-18 15:18:56 -04:00
										 |  |  | 				const confirmSent = await User.email.isValidationPending(uid, 'updatedemail@me.com'); | 
					
						
							|  |  |  | 				assert.strictEqual(confirmSent, true); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 		it('should change a user\'s password', async () => { | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'changepassword', password: '123456' }); | 
					
						
							|  |  |  | 			await apiUser.changePassword({ uid: uid }, { uid: uid, newPassword: '654321', currentPassword: '123456' }); | 
					
						
							|  |  |  | 			const correct = await User.isPasswordCorrect(uid, '654321', '127.0.0.1'); | 
					
						
							|  |  |  | 			assert(correct); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-23 09:52:00 -04:00
										 |  |  | 		it('should not let user change their password to their current password', async () => { | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'changepasswordsame', password: '123456' }); | 
					
						
							|  |  |  | 			await assert.rejects( | 
					
						
							|  |  |  | 				apiUser.changePassword({ uid: uid }, { | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							|  |  |  | 					newPassword: '123456', | 
					
						
							|  |  |  | 					currentPassword: '123456', | 
					
						
							|  |  |  | 				}), | 
					
						
							|  |  |  | 				{ message: '[[user:change-password-error-same-password]]' }, | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not let user change another user\'s password', async () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 13:42:55 -04:00
										 |  |  | 			const regularUserUid = await User.create({ username: 'regularuserpwdchange', password: 'regularuser1234' }); | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'changeadminpwd1', password: '123456' }); | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				await apiUser.changePassword({ uid: uid }, { uid: regularUserUid, newPassword: '654321', currentPassword: '123456' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2023-10-05 12:48:50 -04:00
										 |  |  | 				assert.equal(err.message, '[[user:change-password-error-privileges]]'); | 
					
						
							| 
									
										
										
										
											2020-08-12 13:42:55 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not let user change admin\'s password', async () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 13:42:55 -04:00
										 |  |  | 			const adminUid = await User.create({ username: 'adminpwdchange', password: 'admin1234' }); | 
					
						
							|  |  |  | 			await groups.join('administrators', adminUid); | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'changeadminpwd2', password: '123456' }); | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				await apiUser.changePassword({ uid: uid }, { uid: adminUid, newPassword: '654321', currentPassword: '123456' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2023-10-05 12:48:50 -04:00
										 |  |  | 				assert.equal(err.message, '[[user:change-password-error-privileges]]'); | 
					
						
							| 
									
										
										
										
											2020-08-12 13:42:55 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should let admin change another users password', async () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 13:46:30 -04:00
										 |  |  | 			const adminUid = await User.create({ username: 'adminpwdchange2', password: 'admin1234' }); | 
					
						
							|  |  |  | 			await groups.join('administrators', adminUid); | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'forgotmypassword', password: '123456' }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			await apiUser.changePassword({ uid: adminUid }, { uid: uid, newPassword: '654321' }); | 
					
						
							| 
									
										
										
										
											2020-08-12 13:46:30 -04:00
										 |  |  | 			const correct = await User.isPasswordCorrect(uid, '654321', '127.0.0.1'); | 
					
						
							|  |  |  | 			assert(correct); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not let admin change their password if current password is incorrect', async () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 22:09:22 -04:00
										 |  |  | 			const adminUid = await User.create({ username: 'adminforgotpwd', password: 'admin1234' }); | 
					
						
							|  |  |  | 			await groups.join('administrators', adminUid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				await apiUser.changePassword({ uid: adminUid }, { uid: adminUid, newPassword: '654321', currentPassword: 'wrongpwd' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2023-10-05 12:48:50 -04:00
										 |  |  | 				assert.equal(err.message, '[[user:change-password-error-wrong-current]]'); | 
					
						
							| 
									
										
										
										
											2020-08-12 22:09:22 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 		it('should change username', async () => { | 
					
						
							|  |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, username: 'updatedAgain', password: '123456' }); | 
					
						
							|  |  |  | 			const username = await db.getObjectField(`user:${uid}`, 'username'); | 
					
						
							|  |  |  | 			assert.equal(username, 'updatedAgain'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not let setting an empty username', async () => { | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, username: '', password: '123456' }); | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 			const username = await db.getObjectField(`user:${uid}`, 'username'); | 
					
						
							| 
									
										
										
										
											2020-09-12 21:48:56 -04:00
										 |  |  | 			assert.strictEqual(username, 'updatedAgain'); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should let updating profile if current username is above max length and it is not being changed', async () => { | 
					
						
							| 
									
										
										
										
											2020-09-12 21:48:56 -04:00
										 |  |  | 			const maxLength = meta.config.maximumUsernameLength + 1; | 
					
						
							|  |  |  | 			const longName = new Array(maxLength).fill('a').join(''); | 
					
						
							|  |  |  | 			const uid = await User.create({ username: longName }); | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, username: longName, email: 'verylong@name.com' }); | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 			const userData = await db.getObject(`user:${uid}`); | 
					
						
							| 
									
										
										
										
											2021-07-19 15:42:57 -04:00
										 |  |  | 			const awaitingValidation = await User.email.isValidationPending(uid, 'verylong@name.com'); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 15:42:57 -04:00
										 |  |  | 			assert.strictEqual(userData.username, longName); | 
					
						
							|  |  |  | 			assert.strictEqual(awaitingValidation, true); | 
					
						
							| 
									
										
										
										
											2020-09-12 21:48:56 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 		it('should not update a user\'s username if it did not change', async () => { | 
					
						
							|  |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, username: 'updatedAgain', password: '123456' }); | 
					
						
							|  |  |  | 			const data = await db.getSortedSetRevRange(`user:${uid}:usernames`, 0, -1); | 
					
						
							|  |  |  | 			assert.equal(data.length, 2); | 
					
						
							|  |  |  | 			assert(data[0].startsWith('updatedAgain')); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-15 21:45:47 -04:00
										 |  |  | 		it('should not update a user\'s username if a password is not supplied', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				await apiUser.update({ uid: uid }, { uid: uid, username: 'updatedAgain', password: '' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							| 
									
										
										
										
											2020-10-15 21:45:47 -04:00
										 |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 				assert.strictEqual(err.message, '[[error:invalid-password]]'); | 
					
						
							| 
									
										
										
										
											2020-10-15 21:45:47 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 17:11:49 -04:00
										 |  |  | 		it('should properly change username and clean up old sorted sets', async () => { | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'DennyO', password: '123456' }); | 
					
						
							|  |  |  | 			let usernames = await db.getSortedSetRevRangeWithScores('username:uid', 0, -1); | 
					
						
							|  |  |  | 			usernames = usernames.filter(d => d.score === uid); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(usernames, [{ value: 'DennyO', score: uid }]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, username: 'DennyO\'s', password: '123456' }); | 
					
						
							|  |  |  | 			usernames = await db.getSortedSetRevRangeWithScores('username:uid', 0, -1); | 
					
						
							|  |  |  | 			usernames = usernames.filter(d => d.score === uid); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(usernames, [{ value: 'DennyO\'s', score: uid }]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, username: 'Denny O', password: '123456' }); | 
					
						
							|  |  |  | 			usernames = await db.getSortedSetRevRangeWithScores('username:uid', 0, -1); | 
					
						
							|  |  |  | 			usernames = usernames.filter(d => d.score === uid); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(usernames, [{ value: 'Denny O', score: uid }]); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 		it('should send validation email', async () => { | 
					
						
							|  |  |  | 			const uid = await User.create({ username: 'pooremailupdate', email: 'poor@update.me', password: '123456' }); | 
					
						
							| 
									
										
										
										
											2021-06-18 15:18:56 -04:00
										 |  |  | 			await User.email.expireValidation(uid); | 
					
						
							| 
									
										
										
										
											2021-12-13 20:10:45 -05:00
										 |  |  | 			await apiUser.update({ uid: uid }, { uid: uid, email: 'updatedAgain@me.com', password: '123456' }); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 15:55:52 -05:00
										 |  |  | 			assert.strictEqual(await User.email.isValidationPending(uid, 'updatedAgain@me.com'.toLowerCase()), true); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should update cover image', (done) => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const position = '50.0301% 19.2464%'; | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			socketUser.updateCover({ uid: uid }, { uid: uid, imageData: goodImage, position: position }, (err, result) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert(result.url); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				db.getObjectFields(`user:${uid}`, ['cover:url', 'cover:position'], (err, data) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.equal(data['cover:url'], result.url); | 
					
						
							|  |  |  | 					assert.equal(data['cover:position'], position); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 		it('should remove cover image', async () => { | 
					
						
							|  |  |  | 			const coverPath = await User.getLocalCoverPath(uid); | 
					
						
							|  |  |  | 			await socketUser.removeCover({ uid: uid }, { uid: uid }); | 
					
						
							|  |  |  | 			const coverUrlNow = await db.getObjectField(`user:${uid}`, 'cover:url'); | 
					
						
							|  |  |  | 			assert.strictEqual(coverUrlNow, null); | 
					
						
							|  |  |  | 			assert.strictEqual(fs.existsSync(coverPath), false); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should set user status', (done) => { | 
					
						
							|  |  |  | 			socketUser.setStatus({ uid: uid }, 'away', (err, data) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.equal(data.uid, uid); | 
					
						
							|  |  |  | 				assert.equal(data.status, 'away'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should fail for invalid status', (done) => { | 
					
						
							|  |  |  | 			socketUser.setStatus({ uid: uid }, '12345', (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-user-status]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should get user status', (done) => { | 
					
						
							|  |  |  | 			socketUser.checkStatus({ uid: uid }, uid, (err, status) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.equal(status, 'away'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:53:00 -05:00
										 |  |  | 		it('should change user picture', async () => { | 
					
						
							|  |  |  | 			await apiUser.changePicture({ uid: uid }, { type: 'default', uid: uid }); | 
					
						
							|  |  |  | 			const picture = await User.getUserField(uid, 'picture'); | 
					
						
							|  |  |  | 			assert.equal(picture, ''); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-03 16:46:14 -04:00
										 |  |  | 		it('should let you set an external image', async () => { | 
					
						
							|  |  |  | 			const token = await helpers.getCsrfToken(jar); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			const { body } = await request.put(`${nconf.get('url')}/api/v3/users/${uid}/picture`, { | 
					
						
							| 
									
										
										
										
											2021-09-03 16:46:14 -04:00
										 |  |  | 				jar, | 
					
						
							|  |  |  | 				headers: { | 
					
						
							|  |  |  | 					'x-csrf-token': token, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				body: { | 
					
						
							|  |  |  | 					type: 'external', | 
					
						
							|  |  |  | 					url: 'https://example.org/picture.jpg', | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert(body && body.status && body.response); | 
					
						
							|  |  |  | 			assert.strictEqual(body.status.code, 'ok'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const picture = await User.getUserField(uid, 'picture'); | 
					
						
							|  |  |  | 			assert.strictEqual(picture, validator.escape('https://example.org/picture.jpg')); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:53:00 -05:00
										 |  |  | 		it('should fail to change user picture with invalid data', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.changePicture({ uid: uid }, null); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-12-12 11:53:00 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:53:00 -05:00
										 |  |  | 		it('should fail to change user picture with invalid uid', async () => { | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.changePicture({ uid: 0 }, { uid: 1 }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							|  |  |  | 				assert.equal(err.message, '[[error:no-privileges]]'); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 11:53:00 -05:00
										 |  |  | 		it('should set user picture to uploaded', async () => { | 
					
						
							|  |  |  | 			await User.setUserField(uid, 'uploadedpicture', '/test'); | 
					
						
							|  |  |  | 			await apiUser.changePicture({ uid: uid }, { type: 'uploaded', uid: uid }); | 
					
						
							|  |  |  | 			const picture = await User.getUserField(uid, 'picture'); | 
					
						
							|  |  |  | 			assert.equal(picture, `${nconf.get('relative_path')}/test`); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return error if profile image uploads disabled', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			meta.config.allowProfileImageUploads = 0; | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const picture = { | 
					
						
							| 
									
										
										
										
											2019-07-16 14:17:10 -04:00
										 |  |  | 				path: path.join(nconf.get('base_dir'), 'test/files/test_copy.png'), | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				size: 7189, | 
					
						
							|  |  |  | 				name: 'test.png', | 
					
						
							|  |  |  | 				type: 'image/png', | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			User.uploadCroppedPicture({ | 
					
						
							| 
									
										
										
										
											2021-03-03 17:14:55 -05:00
										 |  |  | 				callerUid: uid, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				uid: uid, | 
					
						
							|  |  |  | 				file: picture, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			}, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:profile-image-uploads-disabled]]'); | 
					
						
							| 
									
										
										
										
											2020-06-22 12:08:35 -04:00
										 |  |  | 				meta.config.allowProfileImageUploads = 1; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return error if profile image has no mime type', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			User.uploadCroppedPicture({ | 
					
						
							| 
									
										
										
										
											2021-03-03 17:14:55 -05:00
										 |  |  | 				callerUid: uid, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				uid: uid, | 
					
						
							| 
									
										
										
										
											2020-06-22 12:08:35 -04:00
										 |  |  | 				imageData: 'data:image/invalid;base64,R0lGODlhPQBEAPeoAJosM/', | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			}, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-image]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('user.uploadCroppedPicture', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const badImage = 'data:audio/mp3;base64,R0lGODlhPQBEAPeoAJosM//AwO/AwHVYZ/z595kzAP/s7P+goOXMv8+fhw/v739/f+8PD98fH/8mJl+fn/9ZWb8/PzWlwv///6wWGbImAPgTEMImIN9gUFCEm/gDALULDN8PAD6atYdCTX9gUNKlj8wZAKUsAOzZz+UMAOsJAP/Z2ccMDA8PD/95eX5NWvsJCOVNQPtfX/8zM8+QePLl38MGBr8JCP+zs9myn/8GBqwpAP/GxgwJCPny78lzYLgjAJ8vAP9fX/+MjMUcAN8zM/9wcM8ZGcATEL+QePdZWf/29uc/P9cmJu9MTDImIN+/r7+/vz8/P8VNQGNugV8AAF9fX8swMNgTAFlDOICAgPNSUnNWSMQ5MBAQEJE3QPIGAM9AQMqGcG9vb6MhJsEdGM8vLx8fH98AANIWAMuQeL8fABkTEPPQ0OM5OSYdGFl5jo+Pj/+pqcsTE78wMFNGQLYmID4dGPvd3UBAQJmTkP+8vH9QUK+vr8ZWSHpzcJMmILdwcLOGcHRQUHxwcK9PT9DQ0O/v70w5MLypoG8wKOuwsP/g4P/Q0IcwKEswKMl8aJ9fX2xjdOtGRs/Pz+Dg4GImIP8gIH0sKEAwKKmTiKZ8aB/f39Wsl+LFt8dgUE9PT5x5aHBwcP+AgP+WltdgYMyZfyywz78AAAAAAAD///8AAP9mZv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAKgALAAAAAA9AEQAAAj/AFEJHEiwoMGDCBMqXMiwocAbBww4nEhxoYkUpzJGrMixogkfGUNqlNixJEIDB0SqHGmyJSojM1bKZOmyop0gM3Oe2liTISKMOoPy7GnwY9CjIYcSRYm0aVKSLmE6nfq05QycVLPuhDrxBlCtYJUqNAq2bNWEBj6ZXRuyxZyDRtqwnXvkhACDV+euTeJm1Ki7A73qNWtFiF+/gA95Gly2CJLDhwEHMOUAAuOpLYDEgBxZ4GRTlC1fDnpkM+fOqD6DDj1aZpITp0dtGCDhr+fVuCu3zlg49ijaokTZTo27uG7Gjn2P+hI8+PDPERoUB318bWbfAJ5sUNFcuGRTYUqV/3ogfXp1rWlMc6awJjiAAd2fm4ogXjz56aypOoIde4OE5u/F9x199dlXnnGiHZWEYbGpsAEA3QXYnHwEFliKAgswgJ8LPeiUXGwedCAKABACCN+EA1pYIIYaFlcDhytd51sGAJbo3onOpajiihlO92KHGaUXGwWjUBChjSPiWJuOO/LYIm4v1tXfE6J4gCSJEZ7YgRYUNrkji9P55sF/ogxw5ZkSqIDaZBV6aSGYq/lGZplndkckZ98xoICbTcIJGQAZcNmdmUc210hs35nCyJ58fgmIKX5RQGOZowxaZwYA+JaoKQwswGijBV4C6SiTUmpphMspJx9unX4KaimjDv9aaXOEBteBqmuuxgEHoLX6Kqx+yXqqBANsgCtit4FWQAEkrNbpq7HSOmtwag5w57GrmlJBASEU18ADjUYb3ADTinIttsgSB1oJFfA63bduimuqKB1keqwUhoCSK374wbujvOSu4QG6UvxBRydcpKsav++Ca6G8A6Pr1x2kVMyHwsVxUALDq/krnrhPSOzXG1lUTIoffqGR7Goi2MAxbv6O2kEG56I7CSlRsEFKFVyovDJoIRTg7sugNRDGqCJzJgcKE0ywc0ELm6KBCCJo8DIPFeCWNGcyqNFE06ToAfV0HBRgxsvLThHn1oddQMrXj5DyAQgjEHSAJMWZwS3HPxT/QMbabI/iBCliMLEJKX2EEkomBAUCxRi42VDADxyTYDVogV+wSChqmKxEKCDAYFDFj4OmwbY7bDGdBhtrnTQYOigeChUmc1K3QTnAUfEgGFgAWt88hKA6aCRIXhxnQ1yg3BCayK44EWdkUQcBByEQChFXfCB776aQsG0BIlQgQgE8qO26X1h8cEUep8ngRBnOy74E9QgRgEAC8SvOfQkh7FDBDmS43PmGoIiKUUEGkMEC/PJHgxw0xH74yx/3XnaYRJgMB8obxQW6kL9QYEJ0FIFgByfIL7/IQAlvQwEpnAC7DtLNJCKUoO/w45c44GwCXiAFB/OXAATQryUxdN4LfFiwgjCNYg+kYMIEFkCKDs6PKAIJouyGWMS1FSKJOMRB/BoIxYJIUXFUxNwoIkEKPAgCBZSQHQ1A2EWDfDEUVLyADj5AChSIQW6gu10bE/JG2VnCZGfo4R4d0sdQoBAHhPjhIB94v/wRoRKQWGRHgrhGSQJxCS+0pCZbEhAAOw=='; | 
					
						
							| 
									
										
										
										
											2021-03-11 10:39:36 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			it('should upload cropped profile picture', async () => { | 
					
						
							|  |  |  | 				const result = await socketUser.uploadCroppedPicture({ uid: uid }, { uid: uid, imageData: goodImage }); | 
					
						
							|  |  |  | 				assert(result.url); | 
					
						
							|  |  |  | 				const data = await db.getObjectFields(`user:${uid}`, ['uploadedpicture', 'picture']); | 
					
						
							|  |  |  | 				assert.strictEqual(result.url, data.uploadedpicture); | 
					
						
							|  |  |  | 				assert.strictEqual(result.url, data.picture); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should upload cropped profile picture in chunks', async () => { | 
					
						
							|  |  |  | 				const socketUploads = require('../src/socket.io/uploads'); | 
					
						
							|  |  |  | 				const socketData = { | 
					
						
							|  |  |  | 					uid, | 
					
						
							|  |  |  | 					method: 'user.uploadCroppedPicture', | 
					
						
							|  |  |  | 					size: goodImage.length, | 
					
						
							|  |  |  | 					progress: 0, | 
					
						
							|  |  |  | 				}; | 
					
						
							|  |  |  | 				const chunkSize = 1000; | 
					
						
							|  |  |  | 				let result; | 
					
						
							|  |  |  | 				do { | 
					
						
							|  |  |  | 					const chunk = goodImage.slice(socketData.progress, socketData.progress + chunkSize); | 
					
						
							|  |  |  | 					socketData.progress += chunk.length; | 
					
						
							|  |  |  | 					// eslint-disable-next-line
 | 
					
						
							|  |  |  | 					result = await socketUploads.upload({ uid: uid }, { | 
					
						
							|  |  |  | 						chunk: chunk, | 
					
						
							|  |  |  | 						params: socketData, | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				} while (socketData.progress < socketData.size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				assert(result.url); | 
					
						
							|  |  |  | 				const data = await db.getObjectFields(`user:${uid}`, ['uploadedpicture', 'picture']); | 
					
						
							|  |  |  | 				assert.strictEqual(result.url, data.uploadedpicture); | 
					
						
							|  |  |  | 				assert.strictEqual(result.url, data.picture); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should error if both file and imageData are missing', (done) => { | 
					
						
							|  |  |  | 				User.uploadCroppedPicture({}, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.equal('[[error:invalid-data]]', err.message); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should error if file size is too big', (done) => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 				const temp = meta.config.maximumProfileImageSize; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				meta.config.maximumProfileImageSize = 1; | 
					
						
							|  |  |  | 				User.uploadCroppedPicture({ | 
					
						
							| 
									
										
										
										
											2021-03-03 17:14:55 -05:00
										 |  |  | 					callerUid: uid, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					uid: 1, | 
					
						
							|  |  |  | 					imageData: goodImage, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.equal('[[error:file-too-big, 1]]', err.message); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// Restore old value
 | 
					
						
							|  |  |  | 					meta.config.maximumProfileImageSize = temp; | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should not allow image data with bad MIME type to be passed in', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				User.uploadCroppedPicture({ | 
					
						
							| 
									
										
										
										
											2021-03-03 17:14:55 -05:00
										 |  |  | 					callerUid: uid, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					uid: 1, | 
					
						
							|  |  |  | 					imageData: badImage, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.equal('[[error:invalid-image]]', err.message); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			it('should get profile pictures', (done) => { | 
					
						
							|  |  |  | 				socketUser.getProfilePictures({ uid: uid }, { uid: uid }, (err, data) => { | 
					
						
							|  |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert(data); | 
					
						
							|  |  |  | 					assert(Array.isArray(data)); | 
					
						
							| 
									
										
											  
											
												Bootstrap5 (#10894)
* chore: up deps
* chore: up composer
* fix(deps): bump 2factor to v7
* chore: up harmony
* chore: up harmony
* fix: missing await
* feat: allow middlewares to pass in template values via res.locals
* feat: buildAccountData middleware automatically added ot all account routes
* fix: properly allow values in res.locals.templateValues to be added to the template data
* refactor: user/blocks
* refactor(accounts): categories and consent
* feat: automatically 404 if exposeUid or exposeGroupName come up empty
* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now
* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization
* fix: move reputation removal check to accountHelpers method
* test: skip i18n tests if ref branch when present is not develop
* fix(deps): bump theme versions
* fix(deps): bump ntfy and 2factor
* chore: up harmony
* fix: add missing return
* fix: #11191, only focus on search input on md environments and up
* feat: allow file uploads on mobile chat
closes https://github.com/NodeBB/NodeBB/issues/11217
* chore: up themes
* chore: add lang string
* fix(deps): bump ntfy to 1.0.15
* refactor: use new if/each syntax
* chore: up composer
* fix: regression from user helper refactor
* chore: up harmony
* chore: up composer
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: fix composer version
* feat: add increment helper
* chore: up harmony
* fix: #11228 no timestamps in future :hourglass:
* chore: up harmony
* check config.theme as well
fire action:posts.loaded after processing dom
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up themes
* chore: up harmony
* remove extra class
* refactor: move these to core from harmony
* chore: up widgets
* chore: up widgets
* height auto
* fix: closes #11238
* dont focus inputs, annoying on mobile
* fix: dont focus twice, only focus on chat input on desktop
dont wrap widget footer in row
* chore: up harmony
* chore: up harmony
* update chat window
* chore: up themes
* fix cache buster for skins
* chat fixes
* chore: up harmony
* chore: up composer
* refactor: change hook logs to debug
* fix: scroll to post right after adding to dom
* fix: hash scrolling and highlighting correct post
* test: re-enable read API schema tests
* fix: add back schema changes for 179faa2270f2ad955dcc4a7b04755acce59e6ffd and c3920ccb10d8ead2dcd9914bb1784bed3f6adfd4
* fix: schema changes from 488f0978a4aa1ca1e4d2a1f2e8c7ef7a681f2f27
* fix: schema changes for f4cf482a874701ce80c0f306c49d8788cec66f87
* fix: schema update for be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 69c96078ea78ee2c45885a90a6f6a59f9042a33c
* fix: schema changes for d1364c313021e48a879a818b24947e1457c062f7
* fix: schema changes for 84ff1152f7552dd866e25a90972d970b9861107e
* fix: schema changes for b860c2605c209e0650ef98f4c80d842ea23a51ce
* fix: schema changes for 23cb67a1126481848fac39aafd1e253441e76d7f
* fix: schema changes for b916e42f400dac8aa51670b15e439f87f0eb8939
* fix: schema change for a9bbb586fcb3a1c61b5fb69052236e78cdf7d743
* fix: schema changes for 4b738c8cd36c936a1dbe2bb900c694bf6c5520ec
* fix: schema changes for 58b5781cea9acb129e6604a82ab5a5bfc0d8394d
* fix: schema changes for 794bf01b21709c4be06584d576d706b3d6342057
* fix: schema changes for 80ea12c1c1963f5b39fb64841e4f3c8da3c87af2, e368feef51e0766f119c9710fb4db8f64724725c, and 52ead114bec961c62fa2eb0786540e229f6e4873
* fix: composer-default object in config?
* fix: schema changes for 9acdc6808c070555352951c651921df181b10993 and 093093420027999df3c67bf0ea6024f6dbf81d2d
* fix: schema changes for c0a52924f1f7ef8caeaacda67363ac269b56042c
* fix: schema change for aba420a3f3b774e949c2539c73f3dc0e1ae79a38, move loggedInUser to optional props
* fix: schema changes for 8c67031609da30d788561459f8bb76e9a69253de
* fix: schema changes for 27e53b42f3ce48fa61d3754375715cd41ffe808d
* fix: schema changes for 28359665187b0a3b9ec6226dca1234ebdbd725a5
* fix: breaking test for email confirmation API call
* fix: schema changes for refactored search page
* fix: schema changes for user object
* fix: schema changes for 9f531f957e08eabb4bae844ddd67bde14d9b59f0
* fix: schema changes for c4042c70decd628e5b880bd109515b47e4e16164 and 23175110a29640e6fa052db1079bfedb34a61055
* fix: schema changes for 9b3616b10392e247974eb0c1e6225a1582bf6c69
* fix: schema changes for 5afd5de07d42fd33f039a6f85ded3b4992200e5a
* fix: schema change for 1d7baf12171cffbd3af8914bef4e6297d1160d49
* fix: schema changes for 57bfb37c55a839662144e684875003ab52315ecc and be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 6e86b4afa20d662af8b9f1c07518df2d8c258105 and 3efad2e13b7319eb9a1f4fda7af047be43ebc11f and 68f66223e73a72f378f193c83a9b5546bede2cda
* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)
* fix: re-login on email change
* fix: schema changes for c926358d734a2fa410de87f4e4a91744215fc14a
* fix: schema changes for 388a8270c9882892bad5c8141f65da8d59eac0fd
* fix: schema change for 2658bcc821c22e137a6eeb9bb74098856a642eaf
* fix: no need to call account middlewares for chats routes
* fix: schema changes for 71743affc3e58dc85d4ffa15ce043d4d9ddd3d67
* fix: final schema changes
* test: support for anyOf and oneOf
* fix: check thumb
* dont scroll to top on back press
* remove group log
* fix: add top margin to merged and deleted alerts
* chore: up widgets
* fix: improve fix-lists mixin
* chore: up harmony/composer
* feat: allow hiding quicksearch results during search
* dont record searches made by composer
* chore: up 54
* chore: up spam be gone
* feat: add prev/next page and page count into mobile paginator
* chore: up harmony
* chore: up harmony
* use old style for IS
* fix: hide entire toolbar row if no posts or not singlePost
* fix: updated messaging for post-queue template, #11206
* fix: btn-sm on post queue back button
* fix: bump harmony, closes #11206
* fix: remove unused alert module import
* fix: bump harmony
* fix: bump harmony
* chore: up harmony
* refactor: IS scrolltop
* fix: update users:search-user-for-chat source string
* feat: support for mark-read toggle on chats dropdown and recent chats list
* feat: api v3 calls to mark chat read/unread
* feat: send event:chats.mark socket event on mark read or unread
* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling
* docs: openapi schema updates for chat marking
* fix: allow unread state toggling in chats dropdown too
* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread
* fix: debug log
* refactor: move userSearch filter to a module
* feat(routes): allow remounting /categories (#11230)
* feat: send flags count to frontend on flags list page
* refactor: filter form client-side js to extract out some logic
* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden
* fix: use userFilter module for assignee, reporterId, targetUid
* fix(openapi): schema changes for updated flags page
* fix: dont allow adding duplicates to userFilter
* use same var
* remove log
* fix: closes #11282
* feat: lang key for x-topics
* chore: up harmony
* chore: up emoji
* chore: up harmony
* fix: update userFilter to allow new option `selectedBlock`
* fix: wrong block name passed to userFilter
* fix: https://github.com/NodeBB/NodeBB/issues/11283
* fix: chats, allow multiple dropdowns like in harmony
* chore: up harmony
* refactor: flag note adding/editing, closes #11285
* fix: remove old prepareEdit logic
* chore: add caveat about hacky code block in userFilter module
* fix: placeholders for userFilter module
* refactor: navigator so it works with multiple thumbs/navigators
* chore: up harmony
* fix: closes #11287, destroy quick reply autocomplete
on navigation
* fix: filter disabled categories on user categories page count
* chore: up harmony
* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying
* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests
* fix: tweak table order in ACP dash searches
* fix: only invoke navigator click drag on left mouse button
* feat: add back unread indicator to navigator
* clear bookmark on mark unread
* fix: navigator crash on ajaxify
* better thumb top calculation
* fix: reset user bookmark when topic is marked unread
* Revert "fix: reset user bookmark when topic is marked unread"
This reverts commit 9bcd85c2c6848c3d325d32027261809da6e11c9e.
* fix: update unread indicator on scroll, add unread count
* chore: bump harmony
* fix: crash on navigator unread update when backing out of a topic
* fix: closes #11183
* fix: update topics:recent zset when rescheduling a topic
* fix: dupe quote button, increase delay, hide immediately on empty selection
* fix: navigator not showing up on first load
* refactor: remove glance
assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes
* more sanity checks for bookmark
dont allow setting bookmark higher than topic postcount
* closes #11218, :train:
* Revert "fix: update topics:recent zset when rescheduling a topic"
This reverts commit 737973cca9e94b6cb3867492a09e1e0b1af391d5.
* fix: #11306, show proper error if queued post doesn't exist
was showing no-privileges if someone else accepted the post
* https://github.com/NodeBB/NodeBB/issues/11307
dont use li
* chore: up harmony
* chore: bump version string
* fix: copy paste fail
* feat: closes #7382, tag filtering
add client side support for filtering by tags on /category, /recent and /unread
* chore: up harmony
* chore: up harmony
* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]
This reverts commit cf6cc2c454dc35c330393c62ee8ce67b42d8eefb.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x
This is a breaking change.
* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309
* chore: up deps
* fix: tests, use x-csrf-token query param removed
* test: fix csrf_token
* lint: remove unused
* feat: add itemprop="image" to avatar helper
* fix: get chat upload button in chat modal
* breaking: remove deprecated socket.io methods
* test: update messaging tests to not use sockets
* fix: parent post links
* fix: prevent post tooltip if mouse leaves before data/tpl is loaded
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up harmony
* fix: nested replies indices
* fix(deps): bump 2factor
* feat: add loggedIn user to all api routes
* chore: up themes
* refactor: audit admin v3 write api routes as per #11321
* refactor: audit category v3 write api routes as per #11321 [breaking]
docs: fix open api spec for #11321
* refactor: audit chat v3 write api routes as per #11321
* refactor: audit files v3 write api routes as per #11321
* refactor: audit flags v3 write api routes as per #11321
* refactor: audit posts v3 write api routes as per #11321
* refactor: audit topics v3 write api routes as per #11321
* refactor: audit users v3 write api routes as per #11321
* fix: lang string
* remove min height
* fix: empty topic/labels taking up space
* fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
* chore: up harmony
* fix: closes #11354, show no post error if queued post already accepted/rejected
* test: #11354
* test: #11354
* fix(deps): bump 2factor
* fix: #11357 clear cache on thumb remove
* fix: thumb remove on windows, closes #11357
* test: openapi for thumbs
* test: fix openapi
---------
Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
											
										 
											2023-03-17 11:58:31 -04:00
										 |  |  | 					assert.equal(data[0].type, 'default'); | 
					
						
							| 
									
										
										
										
											2023-10-05 12:48:50 -04:00
										 |  |  | 					assert.equal(data[0].username, '[[user:default-picture]]'); | 
					
						
							| 
									
										
											  
											
												Bootstrap5 (#10894)
* chore: up deps
* chore: up composer
* fix(deps): bump 2factor to v7
* chore: up harmony
* chore: up harmony
* fix: missing await
* feat: allow middlewares to pass in template values via res.locals
* feat: buildAccountData middleware automatically added ot all account routes
* fix: properly allow values in res.locals.templateValues to be added to the template data
* refactor: user/blocks
* refactor(accounts): categories and consent
* feat: automatically 404 if exposeUid or exposeGroupName come up empty
* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now
* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization
* fix: move reputation removal check to accountHelpers method
* test: skip i18n tests if ref branch when present is not develop
* fix(deps): bump theme versions
* fix(deps): bump ntfy and 2factor
* chore: up harmony
* fix: add missing return
* fix: #11191, only focus on search input on md environments and up
* feat: allow file uploads on mobile chat
closes https://github.com/NodeBB/NodeBB/issues/11217
* chore: up themes
* chore: add lang string
* fix(deps): bump ntfy to 1.0.15
* refactor: use new if/each syntax
* chore: up composer
* fix: regression from user helper refactor
* chore: up harmony
* chore: up composer
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: fix composer version
* feat: add increment helper
* chore: up harmony
* fix: #11228 no timestamps in future :hourglass:
* chore: up harmony
* check config.theme as well
fire action:posts.loaded after processing dom
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up themes
* chore: up harmony
* remove extra class
* refactor: move these to core from harmony
* chore: up widgets
* chore: up widgets
* height auto
* fix: closes #11238
* dont focus inputs, annoying on mobile
* fix: dont focus twice, only focus on chat input on desktop
dont wrap widget footer in row
* chore: up harmony
* chore: up harmony
* update chat window
* chore: up themes
* fix cache buster for skins
* chat fixes
* chore: up harmony
* chore: up composer
* refactor: change hook logs to debug
* fix: scroll to post right after adding to dom
* fix: hash scrolling and highlighting correct post
* test: re-enable read API schema tests
* fix: add back schema changes for 179faa2270f2ad955dcc4a7b04755acce59e6ffd and c3920ccb10d8ead2dcd9914bb1784bed3f6adfd4
* fix: schema changes from 488f0978a4aa1ca1e4d2a1f2e8c7ef7a681f2f27
* fix: schema changes for f4cf482a874701ce80c0f306c49d8788cec66f87
* fix: schema update for be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 69c96078ea78ee2c45885a90a6f6a59f9042a33c
* fix: schema changes for d1364c313021e48a879a818b24947e1457c062f7
* fix: schema changes for 84ff1152f7552dd866e25a90972d970b9861107e
* fix: schema changes for b860c2605c209e0650ef98f4c80d842ea23a51ce
* fix: schema changes for 23cb67a1126481848fac39aafd1e253441e76d7f
* fix: schema changes for b916e42f400dac8aa51670b15e439f87f0eb8939
* fix: schema change for a9bbb586fcb3a1c61b5fb69052236e78cdf7d743
* fix: schema changes for 4b738c8cd36c936a1dbe2bb900c694bf6c5520ec
* fix: schema changes for 58b5781cea9acb129e6604a82ab5a5bfc0d8394d
* fix: schema changes for 794bf01b21709c4be06584d576d706b3d6342057
* fix: schema changes for 80ea12c1c1963f5b39fb64841e4f3c8da3c87af2, e368feef51e0766f119c9710fb4db8f64724725c, and 52ead114bec961c62fa2eb0786540e229f6e4873
* fix: composer-default object in config?
* fix: schema changes for 9acdc6808c070555352951c651921df181b10993 and 093093420027999df3c67bf0ea6024f6dbf81d2d
* fix: schema changes for c0a52924f1f7ef8caeaacda67363ac269b56042c
* fix: schema change for aba420a3f3b774e949c2539c73f3dc0e1ae79a38, move loggedInUser to optional props
* fix: schema changes for 8c67031609da30d788561459f8bb76e9a69253de
* fix: schema changes for 27e53b42f3ce48fa61d3754375715cd41ffe808d
* fix: schema changes for 28359665187b0a3b9ec6226dca1234ebdbd725a5
* fix: breaking test for email confirmation API call
* fix: schema changes for refactored search page
* fix: schema changes for user object
* fix: schema changes for 9f531f957e08eabb4bae844ddd67bde14d9b59f0
* fix: schema changes for c4042c70decd628e5b880bd109515b47e4e16164 and 23175110a29640e6fa052db1079bfedb34a61055
* fix: schema changes for 9b3616b10392e247974eb0c1e6225a1582bf6c69
* fix: schema changes for 5afd5de07d42fd33f039a6f85ded3b4992200e5a
* fix: schema change for 1d7baf12171cffbd3af8914bef4e6297d1160d49
* fix: schema changes for 57bfb37c55a839662144e684875003ab52315ecc and be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 6e86b4afa20d662af8b9f1c07518df2d8c258105 and 3efad2e13b7319eb9a1f4fda7af047be43ebc11f and 68f66223e73a72f378f193c83a9b5546bede2cda
* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)
* fix: re-login on email change
* fix: schema changes for c926358d734a2fa410de87f4e4a91744215fc14a
* fix: schema changes for 388a8270c9882892bad5c8141f65da8d59eac0fd
* fix: schema change for 2658bcc821c22e137a6eeb9bb74098856a642eaf
* fix: no need to call account middlewares for chats routes
* fix: schema changes for 71743affc3e58dc85d4ffa15ce043d4d9ddd3d67
* fix: final schema changes
* test: support for anyOf and oneOf
* fix: check thumb
* dont scroll to top on back press
* remove group log
* fix: add top margin to merged and deleted alerts
* chore: up widgets
* fix: improve fix-lists mixin
* chore: up harmony/composer
* feat: allow hiding quicksearch results during search
* dont record searches made by composer
* chore: up 54
* chore: up spam be gone
* feat: add prev/next page and page count into mobile paginator
* chore: up harmony
* chore: up harmony
* use old style for IS
* fix: hide entire toolbar row if no posts or not singlePost
* fix: updated messaging for post-queue template, #11206
* fix: btn-sm on post queue back button
* fix: bump harmony, closes #11206
* fix: remove unused alert module import
* fix: bump harmony
* fix: bump harmony
* chore: up harmony
* refactor: IS scrolltop
* fix: update users:search-user-for-chat source string
* feat: support for mark-read toggle on chats dropdown and recent chats list
* feat: api v3 calls to mark chat read/unread
* feat: send event:chats.mark socket event on mark read or unread
* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling
* docs: openapi schema updates for chat marking
* fix: allow unread state toggling in chats dropdown too
* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread
* fix: debug log
* refactor: move userSearch filter to a module
* feat(routes): allow remounting /categories (#11230)
* feat: send flags count to frontend on flags list page
* refactor: filter form client-side js to extract out some logic
* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden
* fix: use userFilter module for assignee, reporterId, targetUid
* fix(openapi): schema changes for updated flags page
* fix: dont allow adding duplicates to userFilter
* use same var
* remove log
* fix: closes #11282
* feat: lang key for x-topics
* chore: up harmony
* chore: up emoji
* chore: up harmony
* fix: update userFilter to allow new option `selectedBlock`
* fix: wrong block name passed to userFilter
* fix: https://github.com/NodeBB/NodeBB/issues/11283
* fix: chats, allow multiple dropdowns like in harmony
* chore: up harmony
* refactor: flag note adding/editing, closes #11285
* fix: remove old prepareEdit logic
* chore: add caveat about hacky code block in userFilter module
* fix: placeholders for userFilter module
* refactor: navigator so it works with multiple thumbs/navigators
* chore: up harmony
* fix: closes #11287, destroy quick reply autocomplete
on navigation
* fix: filter disabled categories on user categories page count
* chore: up harmony
* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying
* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests
* fix: tweak table order in ACP dash searches
* fix: only invoke navigator click drag on left mouse button
* feat: add back unread indicator to navigator
* clear bookmark on mark unread
* fix: navigator crash on ajaxify
* better thumb top calculation
* fix: reset user bookmark when topic is marked unread
* Revert "fix: reset user bookmark when topic is marked unread"
This reverts commit 9bcd85c2c6848c3d325d32027261809da6e11c9e.
* fix: update unread indicator on scroll, add unread count
* chore: bump harmony
* fix: crash on navigator unread update when backing out of a topic
* fix: closes #11183
* fix: update topics:recent zset when rescheduling a topic
* fix: dupe quote button, increase delay, hide immediately on empty selection
* fix: navigator not showing up on first load
* refactor: remove glance
assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes
* more sanity checks for bookmark
dont allow setting bookmark higher than topic postcount
* closes #11218, :train:
* Revert "fix: update topics:recent zset when rescheduling a topic"
This reverts commit 737973cca9e94b6cb3867492a09e1e0b1af391d5.
* fix: #11306, show proper error if queued post doesn't exist
was showing no-privileges if someone else accepted the post
* https://github.com/NodeBB/NodeBB/issues/11307
dont use li
* chore: up harmony
* chore: bump version string
* fix: copy paste fail
* feat: closes #7382, tag filtering
add client side support for filtering by tags on /category, /recent and /unread
* chore: up harmony
* chore: up harmony
* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]
This reverts commit cf6cc2c454dc35c330393c62ee8ce67b42d8eefb.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x
This is a breaking change.
* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309
* chore: up deps
* fix: tests, use x-csrf-token query param removed
* test: fix csrf_token
* lint: remove unused
* feat: add itemprop="image" to avatar helper
* fix: get chat upload button in chat modal
* breaking: remove deprecated socket.io methods
* test: update messaging tests to not use sockets
* fix: parent post links
* fix: prevent post tooltip if mouse leaves before data/tpl is loaded
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up harmony
* fix: nested replies indices
* fix(deps): bump 2factor
* feat: add loggedIn user to all api routes
* chore: up themes
* refactor: audit admin v3 write api routes as per #11321
* refactor: audit category v3 write api routes as per #11321 [breaking]
docs: fix open api spec for #11321
* refactor: audit chat v3 write api routes as per #11321
* refactor: audit files v3 write api routes as per #11321
* refactor: audit flags v3 write api routes as per #11321
* refactor: audit posts v3 write api routes as per #11321
* refactor: audit topics v3 write api routes as per #11321
* refactor: audit users v3 write api routes as per #11321
* fix: lang string
* remove min height
* fix: empty topic/labels taking up space
* fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
* chore: up harmony
* fix: closes #11354, show no post error if queued post already accepted/rejected
* test: #11354
* test: #11354
* fix(deps): bump 2factor
* fix: #11357 clear cache on thumb remove
* fix: thumb remove on windows, closes #11357
* test: openapi for thumbs
* test: fix openapi
---------
Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
											
										 
											2023-03-17 11:58:31 -04:00
										 |  |  | 					assert.equal(data[1].type, 'uploaded'); | 
					
						
							| 
									
										
										
										
											2023-10-05 12:48:50 -04:00
										 |  |  | 					assert.equal(data[1].username, '[[user:uploaded-picture]]'); | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			it('should get default profile avatar', (done) => { | 
					
						
							|  |  |  | 				assert.strictEqual(User.getDefaultAvatar(), ''); | 
					
						
							|  |  |  | 				meta.config.defaultAvatar = 'https://path/to/default/avatar'; | 
					
						
							|  |  |  | 				assert.strictEqual(User.getDefaultAvatar(), meta.config.defaultAvatar); | 
					
						
							|  |  |  | 				meta.config.defaultAvatar = '/path/to/default/avatar'; | 
					
						
							|  |  |  | 				assert.strictEqual(User.getDefaultAvatar(), nconf.get('relative_path') + meta.config.defaultAvatar); | 
					
						
							|  |  |  | 				meta.config.defaultAvatar = ''; | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			it('should fail to get profile pictures with invalid data', (done) => { | 
					
						
							|  |  |  | 				socketUser.getProfilePictures({ uid: uid }, null, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 					socketUser.getProfilePictures({ uid: uid }, { uid: null }, (err) => { | 
					
						
							|  |  |  | 						assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			it('should remove uploaded picture', async () => { | 
					
						
							|  |  |  | 				const avatarPath = await User.getLocalAvatarPath(uid); | 
					
						
							|  |  |  | 				assert.notStrictEqual(avatarPath, false); | 
					
						
							|  |  |  | 				await socketUser.removeUploadedPicture({ uid: uid }, { uid: uid }); | 
					
						
							|  |  |  | 				const uploadedPicture = await User.getUserField(uid, 'uploadedpicture'); | 
					
						
							|  |  |  | 				assert.strictEqual(uploadedPicture, ''); | 
					
						
							|  |  |  | 				assert.strictEqual(fs.existsSync(avatarPath), false); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			it('should fail to remove uploaded picture with invalid-data', (done) => { | 
					
						
							|  |  |  | 				socketUser.removeUploadedPicture({ uid: uid }, null, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 					socketUser.removeUploadedPicture({ uid: uid }, { }, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 						assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 						socketUser.removeUploadedPicture({ uid: null }, { }, (err) => { | 
					
						
							|  |  |  | 							assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							|  |  |  | 							done(); | 
					
						
							|  |  |  | 						}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should load profile page', async () => { | 
					
						
							|  |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain`, { jar }); | 
					
						
							|  |  |  | 			assert.equal(response.statusCode, 200); | 
					
						
							|  |  |  | 			assert(body); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should load settings page', async () => { | 
					
						
							|  |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/settings`, { jar }); | 
					
						
							|  |  |  | 			assert.equal(response.statusCode, 200); | 
					
						
							|  |  |  | 			assert(body.settings); | 
					
						
							|  |  |  | 			assert(body.languages); | 
					
						
							|  |  |  | 			assert(body.homePageRoutes); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should load edit page', async () => { | 
					
						
							|  |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/edit`, { jar }); | 
					
						
							|  |  |  | 			assert.equal(response.statusCode, 200); | 
					
						
							|  |  |  | 			assert(body); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 		it('should load edit/email page', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/edit/email`, { jar }); | 
					
						
							|  |  |  | 			assert.strictEqual(response.statusCode, 200); | 
					
						
							|  |  |  | 			assert(body); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Accessing this page will mark the user's account as needing an updated email, below code undo's.
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			await request.post(`${nconf.get('url')}/register/abort`, { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 				jar, | 
					
						
							| 
									
										
											  
											
												Bootstrap5 (#10894)
* chore: up deps
* chore: up composer
* fix(deps): bump 2factor to v7
* chore: up harmony
* chore: up harmony
* fix: missing await
* feat: allow middlewares to pass in template values via res.locals
* feat: buildAccountData middleware automatically added ot all account routes
* fix: properly allow values in res.locals.templateValues to be added to the template data
* refactor: user/blocks
* refactor(accounts): categories and consent
* feat: automatically 404 if exposeUid or exposeGroupName come up empty
* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now
* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization
* fix: move reputation removal check to accountHelpers method
* test: skip i18n tests if ref branch when present is not develop
* fix(deps): bump theme versions
* fix(deps): bump ntfy and 2factor
* chore: up harmony
* fix: add missing return
* fix: #11191, only focus on search input on md environments and up
* feat: allow file uploads on mobile chat
closes https://github.com/NodeBB/NodeBB/issues/11217
* chore: up themes
* chore: add lang string
* fix(deps): bump ntfy to 1.0.15
* refactor: use new if/each syntax
* chore: up composer
* fix: regression from user helper refactor
* chore: up harmony
* chore: up composer
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: fix composer version
* feat: add increment helper
* chore: up harmony
* fix: #11228 no timestamps in future :hourglass:
* chore: up harmony
* check config.theme as well
fire action:posts.loaded after processing dom
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up themes
* chore: up harmony
* remove extra class
* refactor: move these to core from harmony
* chore: up widgets
* chore: up widgets
* height auto
* fix: closes #11238
* dont focus inputs, annoying on mobile
* fix: dont focus twice, only focus on chat input on desktop
dont wrap widget footer in row
* chore: up harmony
* chore: up harmony
* update chat window
* chore: up themes
* fix cache buster for skins
* chat fixes
* chore: up harmony
* chore: up composer
* refactor: change hook logs to debug
* fix: scroll to post right after adding to dom
* fix: hash scrolling and highlighting correct post
* test: re-enable read API schema tests
* fix: add back schema changes for 179faa2270f2ad955dcc4a7b04755acce59e6ffd and c3920ccb10d8ead2dcd9914bb1784bed3f6adfd4
* fix: schema changes from 488f0978a4aa1ca1e4d2a1f2e8c7ef7a681f2f27
* fix: schema changes for f4cf482a874701ce80c0f306c49d8788cec66f87
* fix: schema update for be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 69c96078ea78ee2c45885a90a6f6a59f9042a33c
* fix: schema changes for d1364c313021e48a879a818b24947e1457c062f7
* fix: schema changes for 84ff1152f7552dd866e25a90972d970b9861107e
* fix: schema changes for b860c2605c209e0650ef98f4c80d842ea23a51ce
* fix: schema changes for 23cb67a1126481848fac39aafd1e253441e76d7f
* fix: schema changes for b916e42f400dac8aa51670b15e439f87f0eb8939
* fix: schema change for a9bbb586fcb3a1c61b5fb69052236e78cdf7d743
* fix: schema changes for 4b738c8cd36c936a1dbe2bb900c694bf6c5520ec
* fix: schema changes for 58b5781cea9acb129e6604a82ab5a5bfc0d8394d
* fix: schema changes for 794bf01b21709c4be06584d576d706b3d6342057
* fix: schema changes for 80ea12c1c1963f5b39fb64841e4f3c8da3c87af2, e368feef51e0766f119c9710fb4db8f64724725c, and 52ead114bec961c62fa2eb0786540e229f6e4873
* fix: composer-default object in config?
* fix: schema changes for 9acdc6808c070555352951c651921df181b10993 and 093093420027999df3c67bf0ea6024f6dbf81d2d
* fix: schema changes for c0a52924f1f7ef8caeaacda67363ac269b56042c
* fix: schema change for aba420a3f3b774e949c2539c73f3dc0e1ae79a38, move loggedInUser to optional props
* fix: schema changes for 8c67031609da30d788561459f8bb76e9a69253de
* fix: schema changes for 27e53b42f3ce48fa61d3754375715cd41ffe808d
* fix: schema changes for 28359665187b0a3b9ec6226dca1234ebdbd725a5
* fix: breaking test for email confirmation API call
* fix: schema changes for refactored search page
* fix: schema changes for user object
* fix: schema changes for 9f531f957e08eabb4bae844ddd67bde14d9b59f0
* fix: schema changes for c4042c70decd628e5b880bd109515b47e4e16164 and 23175110a29640e6fa052db1079bfedb34a61055
* fix: schema changes for 9b3616b10392e247974eb0c1e6225a1582bf6c69
* fix: schema changes for 5afd5de07d42fd33f039a6f85ded3b4992200e5a
* fix: schema change for 1d7baf12171cffbd3af8914bef4e6297d1160d49
* fix: schema changes for 57bfb37c55a839662144e684875003ab52315ecc and be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 6e86b4afa20d662af8b9f1c07518df2d8c258105 and 3efad2e13b7319eb9a1f4fda7af047be43ebc11f and 68f66223e73a72f378f193c83a9b5546bede2cda
* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)
* fix: re-login on email change
* fix: schema changes for c926358d734a2fa410de87f4e4a91744215fc14a
* fix: schema changes for 388a8270c9882892bad5c8141f65da8d59eac0fd
* fix: schema change for 2658bcc821c22e137a6eeb9bb74098856a642eaf
* fix: no need to call account middlewares for chats routes
* fix: schema changes for 71743affc3e58dc85d4ffa15ce043d4d9ddd3d67
* fix: final schema changes
* test: support for anyOf and oneOf
* fix: check thumb
* dont scroll to top on back press
* remove group log
* fix: add top margin to merged and deleted alerts
* chore: up widgets
* fix: improve fix-lists mixin
* chore: up harmony/composer
* feat: allow hiding quicksearch results during search
* dont record searches made by composer
* chore: up 54
* chore: up spam be gone
* feat: add prev/next page and page count into mobile paginator
* chore: up harmony
* chore: up harmony
* use old style for IS
* fix: hide entire toolbar row if no posts or not singlePost
* fix: updated messaging for post-queue template, #11206
* fix: btn-sm on post queue back button
* fix: bump harmony, closes #11206
* fix: remove unused alert module import
* fix: bump harmony
* fix: bump harmony
* chore: up harmony
* refactor: IS scrolltop
* fix: update users:search-user-for-chat source string
* feat: support for mark-read toggle on chats dropdown and recent chats list
* feat: api v3 calls to mark chat read/unread
* feat: send event:chats.mark socket event on mark read or unread
* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling
* docs: openapi schema updates for chat marking
* fix: allow unread state toggling in chats dropdown too
* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread
* fix: debug log
* refactor: move userSearch filter to a module
* feat(routes): allow remounting /categories (#11230)
* feat: send flags count to frontend on flags list page
* refactor: filter form client-side js to extract out some logic
* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden
* fix: use userFilter module for assignee, reporterId, targetUid
* fix(openapi): schema changes for updated flags page
* fix: dont allow adding duplicates to userFilter
* use same var
* remove log
* fix: closes #11282
* feat: lang key for x-topics
* chore: up harmony
* chore: up emoji
* chore: up harmony
* fix: update userFilter to allow new option `selectedBlock`
* fix: wrong block name passed to userFilter
* fix: https://github.com/NodeBB/NodeBB/issues/11283
* fix: chats, allow multiple dropdowns like in harmony
* chore: up harmony
* refactor: flag note adding/editing, closes #11285
* fix: remove old prepareEdit logic
* chore: add caveat about hacky code block in userFilter module
* fix: placeholders for userFilter module
* refactor: navigator so it works with multiple thumbs/navigators
* chore: up harmony
* fix: closes #11287, destroy quick reply autocomplete
on navigation
* fix: filter disabled categories on user categories page count
* chore: up harmony
* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying
* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests
* fix: tweak table order in ACP dash searches
* fix: only invoke navigator click drag on left mouse button
* feat: add back unread indicator to navigator
* clear bookmark on mark unread
* fix: navigator crash on ajaxify
* better thumb top calculation
* fix: reset user bookmark when topic is marked unread
* Revert "fix: reset user bookmark when topic is marked unread"
This reverts commit 9bcd85c2c6848c3d325d32027261809da6e11c9e.
* fix: update unread indicator on scroll, add unread count
* chore: bump harmony
* fix: crash on navigator unread update when backing out of a topic
* fix: closes #11183
* fix: update topics:recent zset when rescheduling a topic
* fix: dupe quote button, increase delay, hide immediately on empty selection
* fix: navigator not showing up on first load
* refactor: remove glance
assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes
* more sanity checks for bookmark
dont allow setting bookmark higher than topic postcount
* closes #11218, :train:
* Revert "fix: update topics:recent zset when rescheduling a topic"
This reverts commit 737973cca9e94b6cb3867492a09e1e0b1af391d5.
* fix: #11306, show proper error if queued post doesn't exist
was showing no-privileges if someone else accepted the post
* https://github.com/NodeBB/NodeBB/issues/11307
dont use li
* chore: up harmony
* chore: bump version string
* fix: copy paste fail
* feat: closes #7382, tag filtering
add client side support for filtering by tags on /category, /recent and /unread
* chore: up harmony
* chore: up harmony
* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]
This reverts commit cf6cc2c454dc35c330393c62ee8ce67b42d8eefb.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x
This is a breaking change.
* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309
* chore: up deps
* fix: tests, use x-csrf-token query param removed
* test: fix csrf_token
* lint: remove unused
* feat: add itemprop="image" to avatar helper
* fix: get chat upload button in chat modal
* breaking: remove deprecated socket.io methods
* test: update messaging tests to not use sockets
* fix: parent post links
* fix: prevent post tooltip if mouse leaves before data/tpl is loaded
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up harmony
* fix: nested replies indices
* fix(deps): bump 2factor
* feat: add loggedIn user to all api routes
* chore: up themes
* refactor: audit admin v3 write api routes as per #11321
* refactor: audit category v3 write api routes as per #11321 [breaking]
docs: fix open api spec for #11321
* refactor: audit chat v3 write api routes as per #11321
* refactor: audit files v3 write api routes as per #11321
* refactor: audit flags v3 write api routes as per #11321
* refactor: audit posts v3 write api routes as per #11321
* refactor: audit topics v3 write api routes as per #11321
* refactor: audit users v3 write api routes as per #11321
* fix: lang string
* remove min height
* fix: empty topic/labels taking up space
* fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
* chore: up harmony
* fix: closes #11354, show no post error if queued post already accepted/rejected
* test: #11354
* test: #11354
* fix(deps): bump 2factor
* fix: #11357 clear cache on thumb remove
* fix: thumb remove on windows, closes #11357
* test: openapi for thumbs
* test: fix openapi
---------
Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
											
										 
											2023-03-17 11:58:31 -04:00
										 |  |  | 				headers: { | 
					
						
							|  |  |  | 					'x-csrf-token': csrf_token, | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 		it('should load user\'s groups page', async () => { | 
					
						
							|  |  |  | 			await groups.create({ | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				name: 'Test', | 
					
						
							|  |  |  | 				description: 'Foobar!', | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			await groups.join('Test', uid); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			const { body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/groups`, { jar }); | 
					
						
							| 
									
										
										
										
											2021-06-18 11:49:01 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			assert(Array.isArray(body.groups)); | 
					
						
							|  |  |  | 			assert.equal(body.groups[0].name, 'Test'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('user info', () => { | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 		let testUserUid; | 
					
						
							|  |  |  | 		let verifiedTestUserUid; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		before(async () => { | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 			// Might be the first user thus a verified one if this test part is ran alone
 | 
					
						
							|  |  |  | 			verifiedTestUserUid = await User.create({ username: 'bannedUser', password: '123456', email: 'banneduser@example.com' }); | 
					
						
							|  |  |  | 			await User.setUserField(verifiedTestUserUid, 'email:confirmed', 1); | 
					
						
							|  |  |  | 			testUserUid = await User.create({ username: 'bannedUser2', password: '123456', email: 'banneduser2@example.com' }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return error if there is no ban reason', (done) => { | 
					
						
							|  |  |  | 			User.getLatestBanInfo(123, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, 'no-ban-info'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should get history from set', async () => { | 
					
						
							| 
									
										
										
										
											2020-06-13 13:55:48 -04:00
										 |  |  | 			const now = Date.now(); | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 			await db.sortedSetAdd(`user:${testUserUid}:usernames`, now, `derp:${now}`); | 
					
						
							|  |  |  | 			const data = await User.getHistory(`user:${testUserUid}:usernames`); | 
					
						
							| 
									
										
										
										
											2020-06-13 13:55:48 -04:00
										 |  |  | 			assert.equal(data[0].value, 'derp'); | 
					
						
							|  |  |  | 			assert.equal(data[0].timestamp, now); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should return the correct ban reason', async () => { | 
					
						
							|  |  |  | 			await User.bans.ban(testUserUid, 0, ''); | 
					
						
							|  |  |  | 			const data = await User.getModerationHistory(testUserUid); | 
					
						
							|  |  |  | 			assert.equal(data.bans.length, 1, 'one ban'); | 
					
						
							|  |  |  | 			assert.equal(data.bans[0].reason, '[[user:info.banned-no-reason]]', 'no ban reason'); | 
					
						
							|  |  |  | 			await User.bans.unban(testUserUid); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should ban user permanently', (done) => { | 
					
						
							|  |  |  | 			User.bans.ban(testUserUid, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				User.bans.isBanned(testUserUid, (err, isBanned) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.equal(isBanned, true); | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 					User.bans.unban(testUserUid, done); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should ban user temporarily', async () => { | 
					
						
							|  |  |  | 			await User.bans.ban(testUserUid, Date.now() + 2000); | 
					
						
							|  |  |  | 			let isBanned = await User.bans.isBanned(testUserUid); | 
					
						
							|  |  |  | 			assert.equal(isBanned, true); | 
					
						
							|  |  |  | 			await setTimeout(3000); | 
					
						
							|  |  |  | 			isBanned = await User.bans.isBanned(testUserUid); | 
					
						
							|  |  |  | 			assert.equal(isBanned, false); | 
					
						
							|  |  |  | 			await User.bans.unban(testUserUid); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error if until is NaN', (done) => { | 
					
						
							|  |  |  | 			User.bans.ban(testUserUid, 'asd', (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:ban-expiry-missing]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should be member of "banned-users" system group only after a ban', async () => { | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 			await User.bans.ban(testUserUid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const systemGroups = groups.systemGroups.filter(group => group !== groups.BANNED_USERS); | 
					
						
							|  |  |  | 			const isMember = await groups.isMember(testUserUid, groups.BANNED_USERS); | 
					
						
							|  |  |  | 			const isMemberOfAny = await groups.isMemberOfAny(testUserUid, systemGroups); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual(isMember, true); | 
					
						
							|  |  |  | 			assert.strictEqual(isMemberOfAny, false); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should restore system group memberships after an unban (for an unverified user)', async () => { | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 			await User.bans.unban(testUserUid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const isMemberOfGroups = await groups.isMemberOfGroups(testUserUid, groups.systemGroups); | 
					
						
							|  |  |  | 			const membership = new Map(groups.systemGroups.map((item, index) => [item, isMemberOfGroups[index]])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('registered-users'), true); | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('verified-users'), false); | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('unverified-users'), true); | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get(groups.BANNED_USERS), false); | 
					
						
							|  |  |  | 			// administrators cannot be banned
 | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('administrators'), false); | 
					
						
							|  |  |  | 			// This will not restored
 | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('Global Moderators'), false); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should restore system group memberships after an unban (for a verified user)', async () => { | 
					
						
							| 
									
										
										
										
											2020-12-14 09:20:41 +03:00
										 |  |  | 			await User.bans.ban(verifiedTestUserUid); | 
					
						
							|  |  |  | 			await User.bans.unban(verifiedTestUserUid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const isMemberOfGroups = await groups.isMemberOfGroups(verifiedTestUserUid, groups.systemGroups); | 
					
						
							|  |  |  | 			const membership = new Map(groups.systemGroups.map((item, index) => [item, isMemberOfGroups[index]])); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('verified-users'), true); | 
					
						
							|  |  |  | 			assert.strictEqual(membership.get('unverified-users'), false); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2022-11-15 17:53:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		it('should be able to post in category for banned users', async () => { | 
					
						
							|  |  |  | 			const { cid } = await Categories.create({ | 
					
						
							|  |  |  | 				name: 'Test Category', | 
					
						
							|  |  |  | 				description: 'A test', | 
					
						
							|  |  |  | 				order: 1, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			const testUid = await User.create({ username: userData.username }); | 
					
						
							|  |  |  | 			await User.bans.ban(testUid); | 
					
						
							|  |  |  | 			let _err; | 
					
						
							|  |  |  | 			try { | 
					
						
							|  |  |  | 				await Topics.post({ title: 'banned topic', content: 'tttttttttttt', cid: cid, uid: testUid }); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							|  |  |  | 				_err = err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			assert.strictEqual(_err && _err.message, '[[error:no-privileges]]'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await Promise.all([ | 
					
						
							|  |  |  | 				privileges.categories.give(['groups:topics:create', 'groups:topics:reply'], cid, 'banned-users'), | 
					
						
							|  |  |  | 				privileges.categories.rescind(['groups:topics:create', 'groups:topics:reply'], cid, 'registered-users'), | 
					
						
							|  |  |  | 			]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const result = await Topics.post({ title: 'banned topic', content: 'tttttttttttt', cid: cid, uid: testUid }); | 
					
						
							|  |  |  | 			assert(result); | 
					
						
							|  |  |  | 			assert.strictEqual(result.topicData.title, 'banned topic'); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 21:22:44 -05:00
										 |  |  | 	describe('Digest.getSubscribers', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		const uidIndex = {}; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		before(async () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const testUsers = ['daysub', 'offsub', 'nullsub', 'weeksub']; | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			await Promise.all(testUsers.map(async (username) => { | 
					
						
							|  |  |  | 				const uid = await User.create({ username, email: `${username}@example.com` }); | 
					
						
							|  |  |  | 				if (username === 'nullsub') { | 
					
						
							|  |  |  | 					return; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				uidIndex[username] = uid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				const sub = username.slice(0, -3); | 
					
						
							|  |  |  | 				await User.updateDigestSetting(uid, sub); | 
					
						
							|  |  |  | 				await User.setSetting(uid, 'dailyDigestFreq', sub); | 
					
						
							|  |  |  | 			})); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should accurately build digest list given ACP default "null" (not set)', (done) => { | 
					
						
							|  |  |  | 			User.digest.getSubscribers('day', (err, subs) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				assert.strictEqual(subs.length, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should accurately build digest list given ACP default "day"', async () => { | 
					
						
							|  |  |  | 			await meta.configs.set('dailyDigestFreq', 'day'); | 
					
						
							|  |  |  | 			const subs = await User.digest.getSubscribers('day'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			assert.strictEqual(subs.includes(uidIndex.daysub.toString()), true); // daysub does get emailed
 | 
					
						
							|  |  |  | 			assert.strictEqual(subs.includes(uidIndex.weeksub.toString()), false); // weeksub does not get emailed
 | 
					
						
							|  |  |  | 			assert.strictEqual(subs.includes(uidIndex.offsub.toString()), false); // offsub doesn't get emailed
 | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should accurately build digest list given ACP default "week"', async () => { | 
					
						
							|  |  |  | 			await meta.configs.set('dailyDigestFreq', 'week'); | 
					
						
							|  |  |  | 			const subs = await User.digest.getSubscribers('week'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			assert.strictEqual(subs.includes(uidIndex.weeksub.toString()), true); // weeksub gets emailed
 | 
					
						
							|  |  |  | 			assert.strictEqual(subs.includes(uidIndex.daysub.toString()), false); // daysub gets emailed
 | 
					
						
							|  |  |  | 			assert.strictEqual(subs.includes(uidIndex.offsub.toString()), false); // offsub does not get emailed
 | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should accurately build digest list given ACP default "off"', async () => { | 
					
						
							|  |  |  | 			await meta.configs.set('dailyDigestFreq', 'off'); | 
					
						
							|  |  |  | 			const subs = await User.digest.getSubscribers('day'); | 
					
						
							|  |  |  | 			assert.strictEqual(subs.length, 1); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('digests', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		let uid; | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			uid = await User.create({ username: 'digestuser', email: 'test@example.com' }); | 
					
						
							|  |  |  | 			await User.updateDigestSetting(uid, 'day'); | 
					
						
							|  |  |  | 			await User.setSetting(uid, 'dailyDigestFreq', 'day'); | 
					
						
							|  |  |  | 			await User.setSetting(uid, 'notificationType_test', 'notificationemail'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 18:44:22 -04:00
										 |  |  | 		it('should send digests', async () => { | 
					
						
							| 
									
										
										
										
											2021-11-22 21:22:44 -05:00
										 |  |  | 			const oldValue = meta.config.includeUnverifiedEmails; | 
					
						
							|  |  |  | 			meta.config.includeUnverifiedEmails = true; | 
					
						
							| 
									
										
										
										
											2023-05-11 19:01:28 -04:00
										 |  |  | 			const uid = await User.create({ username: 'digest' }); | 
					
						
							|  |  |  | 			await User.setUserField(uid, 'email', 'email@test.com'); | 
					
						
							|  |  |  | 			await User.email.confirmByUid(uid); | 
					
						
							| 
									
										
										
										
											2023-05-11 18:44:22 -04:00
										 |  |  | 			await User.digest.execute({ | 
					
						
							|  |  |  | 				interval: 'day', | 
					
						
							|  |  |  | 				subscribers: [uid], | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-05-11 18:44:22 -04:00
										 |  |  | 			meta.config.includeUnverifiedEmails = oldValue; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 19:01:28 -04:00
										 |  |  | 		it('should return 0', async () => { | 
					
						
							|  |  |  | 			const sent = await User.digest.send({ subscribers: [] }); | 
					
						
							|  |  |  | 			assert.strictEqual(sent, 0); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 20:21:57 -04:00
										 |  |  | 		it('should get users with single uid', async () => { | 
					
						
							|  |  |  | 			const res = await User.digest.getUsersInterval(1); | 
					
						
							|  |  |  | 			assert.strictEqual(res, false); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should not send digests', async () => { | 
					
						
							|  |  |  | 			const oldValue = meta.config.disableEmailSubsriptions; | 
					
						
							|  |  |  | 			meta.config.disableEmailSubsriptions = 1; | 
					
						
							|  |  |  | 			const res = await User.digest.execute({}); | 
					
						
							|  |  |  | 			assert.strictEqual(res, false); | 
					
						
							|  |  |  | 			meta.config.disableEmailSubsriptions = oldValue; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 18:44:22 -04:00
										 |  |  | 		it('should not send digests', async () => { | 
					
						
							|  |  |  | 			await User.digest.execute({ interval: 'month' }); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 21:22:44 -05:00
										 |  |  | 		it('should get delivery times', async () => { | 
					
						
							|  |  |  | 			const data = await User.digest.getDeliveryTimes(0, -1); | 
					
						
							|  |  |  | 			const users = data.users.filter(u => u.username === 'digestuser'); | 
					
						
							|  |  |  | 			assert.strictEqual(users[0].setting, 'day'); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('unsubscribe via POST', () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			it('should unsubscribe from digest if one-click unsubscribe is POSTed', async () => { | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 				const token = jwt.sign({ | 
					
						
							|  |  |  | 					template: 'digest', | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							|  |  |  | 				}, nconf.get('secret')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							|  |  |  | 				const value = await db.getObjectField(`user:${uid}:settings`, 'dailyDigestFreq'); | 
					
						
							|  |  |  | 				assert.strictEqual(value, 'off'); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			it('should unsubscribe from notifications if one-click unsubscribe is POSTed', async () => { | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 				const token = jwt.sign({ | 
					
						
							|  |  |  | 					template: 'notification', | 
					
						
							|  |  |  | 					type: 'test', | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							|  |  |  | 				}, nconf.get('secret')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const value = await db.getObjectField(`user:${uid}:settings`, 'notificationType_test'); | 
					
						
							|  |  |  | 				assert.strictEqual(value, 'notification'); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			it('should return errors on missing template in token', async () => { | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 				const token = jwt.sign({ | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							|  |  |  | 				}, nconf.get('secret')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 404); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			it('should return errors on wrong template in token', async () => { | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 				const token = jwt.sign({ | 
					
						
							|  |  |  | 					template: 'user', | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							|  |  |  | 				}, nconf.get('secret')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 404); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			it('should return errors on missing token', async () => { | 
					
						
							|  |  |  | 				const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/`); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 404); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			it('should return errors on token signed with wrong secret (verify-failure)', async () => { | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 				const token = jwt.sign({ | 
					
						
							|  |  |  | 					template: 'notification', | 
					
						
							|  |  |  | 					type: 'test', | 
					
						
							|  |  |  | 					uid: uid, | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 				}, `${nconf.get('secret')}aababacaba`); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							| 
									
										
										
										
											2019-01-08 13:56:11 -05:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('socket methods', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		const socketUser = require('../src/socket.io/user'); | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 		let delUid; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should fail with invalid data', (done) => { | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			meta.userOrGroupExists(null, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-10 16:59:55 -04:00
										 |  |  | 		it('should return true/false if user/group exists or not', async () => { | 
					
						
							|  |  |  | 			assert.strictEqual(await meta.userOrGroupExists('registered-users'), true); | 
					
						
							|  |  |  | 			assert.strictEqual(await meta.userOrGroupExists('John Smith'), true); | 
					
						
							|  |  |  | 			assert.strictEqual(await meta.userOrGroupExists('doesnot exist'), false); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(await meta.userOrGroupExists(['doesnot exist', 'nope not here']), [false, false]); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(await meta.userOrGroupExists(['doesnot exist', 'John Smith']), [false, true]); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(await meta.userOrGroupExists(['administrators', 'John Smith']), [true, true]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await assert.rejects( | 
					
						
							|  |  |  | 				meta.userOrGroupExists(['', undefined]), | 
					
						
							|  |  |  | 				{ message: '[[error:invalid-data]]' }, | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should delete user', async () => { | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 			delUid = await User.create({ username: 'willbedeleted' }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Upload some avatars and covers before deleting
 | 
					
						
							|  |  |  | 			meta.config['profile:keepAllUserImages'] = 1; | 
					
						
							|  |  |  | 			let result = await socketUser.uploadCroppedPicture({ uid: delUid }, { uid: delUid, imageData: goodImage }); | 
					
						
							|  |  |  | 			assert(result.url); | 
					
						
							|  |  |  | 			result = await socketUser.uploadCroppedPicture({ uid: delUid }, { uid: delUid, imageData: goodImage }); | 
					
						
							|  |  |  | 			assert(result.url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const position = '50.0301% 19.2464%'; | 
					
						
							|  |  |  | 			result = await socketUser.updateCover({ uid: delUid }, { uid: delUid, imageData: goodImage, position: position }); | 
					
						
							|  |  |  | 			assert(result.url); | 
					
						
							|  |  |  | 			result = await socketUser.updateCover({ uid: delUid }, { uid: delUid, imageData: goodImage, position: position }); | 
					
						
							|  |  |  | 			assert(result.url); | 
					
						
							|  |  |  | 			meta.config['profile:keepAllUserImages'] = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			await apiUser.deleteAccount({ uid: delUid }, { uid: delUid }); | 
					
						
							|  |  |  | 			const exists = await meta.userOrGroupExists('willbedeleted'); | 
					
						
							| 
									
										
										
										
											2020-11-25 14:33:19 -05:00
										 |  |  | 			assert(!exists); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 16:21:23 +03:00
										 |  |  | 		it('should clean profile images after account deletion', () => { | 
					
						
							|  |  |  | 			const allProfileFiles = fs.readdirSync(path.join(nconf.get('upload_path'), 'profile')); | 
					
						
							|  |  |  | 			const deletedUserImages = allProfileFiles.filter( | 
					
						
							|  |  |  | 				f => f.startsWith(`${delUid}-profilecover`) || f.startsWith(`${delUid}-profileavatar`) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			assert.strictEqual(deletedUserImages.length, 0); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should fail to delete user with wrong password', async () => { | 
					
						
							| 
									
										
										
										
											2020-11-25 14:33:19 -05:00
										 |  |  | 			const uid = await User.create({ username: 'willbedeletedpwd', password: '123456' }); | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 				await apiUser.deleteAccount({ uid: uid }, { uid: uid, password: '654321' }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							|  |  |  | 				assert.strictEqual(err.message, '[[error:invalid-password]]'); | 
					
						
							| 
									
										
										
										
											2020-11-25 14:33:19 -05:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should delete user with correct password', async () => { | 
					
						
							| 
									
										
										
										
											2020-11-25 14:33:19 -05:00
										 |  |  | 			const uid = await User.create({ username: 'willbedeletedcorrectpwd', password: '123456' }); | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			await apiUser.deleteAccount({ uid: uid }, { uid: uid, password: '123456' }); | 
					
						
							| 
									
										
										
										
											2020-11-25 14:33:19 -05:00
										 |  |  | 			const exists = await User.exists(uid); | 
					
						
							|  |  |  | 			assert(!exists); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should fail to delete user if account deletion is not allowed', async () => { | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			const oldValue = meta.config.allowAccountDelete; | 
					
						
							|  |  |  | 			meta.config.allowAccountDelete = 0; | 
					
						
							| 
									
										
										
										
											2020-02-13 11:31:20 -05:00
										 |  |  | 			const uid = await User.create({ username: 'tobedeleted' }); | 
					
						
							|  |  |  | 			try { | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 				await apiUser.deleteAccount({ uid: uid }, { uid: uid }); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							| 
									
										
										
										
											2020-02-13 11:31:20 -05:00
										 |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 				assert.strictEqual(err.message, '[[error:account-deletion-disabled]]'); | 
					
						
							| 
									
										
										
										
											2020-02-13 11:31:20 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			meta.config.allowAccountDelete = oldValue; | 
					
						
							| 
									
										
										
										
											2020-02-13 11:31:20 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should send reset email', (done) => { | 
					
						
							|  |  |  | 			socketUser.reset.send({ uid: 0 }, 'john@example.com', (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return invalid-data error', (done) => { | 
					
						
							|  |  |  | 			socketUser.reset.send({ uid: 0 }, null, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not error', (done) => { | 
					
						
							|  |  |  | 			socketUser.reset.send({ uid: 0 }, 'doestnot@exist.com', (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should commit reset', (done) => { | 
					
						
							|  |  |  | 			db.getObject('reset:uid', (err, data) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 				const code = Object.keys(data).find(code => parseInt(data[code], 10) === parseInt(testUid, 10)); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				socketUser.reset.commit({ uid: 0 }, { code: code, password: 'pwdchange' }, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 		it('should save user settings', async () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const data = { | 
					
						
							| 
									
										
										
										
											2019-12-26 20:17:54 -05:00
										 |  |  | 				uid: testUid, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				settings: { | 
					
						
							|  |  |  | 					bootswatchSkin: 'default', | 
					
						
							|  |  |  | 					homePageRoute: 'none', | 
					
						
							|  |  |  | 					homePageCustom: '', | 
					
						
							|  |  |  | 					openOutgoingLinksInNewTab: 0, | 
					
						
							|  |  |  | 					scrollToMyPost: 1, | 
					
						
							|  |  |  | 					userLang: 'en-GB', | 
					
						
							|  |  |  | 					usePagination: 1, | 
					
						
							|  |  |  | 					topicsPerPage: '10', | 
					
						
							|  |  |  | 					postsPerPage: '5', | 
					
						
							|  |  |  | 					showemail: 1, | 
					
						
							|  |  |  | 					showfullname: 1, | 
					
						
							|  |  |  | 					restrictChat: 0, | 
					
						
							|  |  |  | 					followTopicsOnCreate: 1, | 
					
						
							|  |  |  | 					followTopicsOnReply: 1, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			await apiUser.updateSettings({ uid: testUid }, data); | 
					
						
							|  |  |  | 			const userSettings = await User.getSettings(testUid); | 
					
						
							|  |  |  | 			assert.strictEqual(userSettings.usePagination, true); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 		it('should properly escape homePageRoute', async () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const data = { | 
					
						
							| 
									
										
										
										
											2020-10-12 14:18:59 -04:00
										 |  |  | 				uid: testUid, | 
					
						
							|  |  |  | 				settings: { | 
					
						
							|  |  |  | 					bootswatchSkin: 'default', | 
					
						
							|  |  |  | 					homePageRoute: 'category/6/testing-ground', | 
					
						
							|  |  |  | 					homePageCustom: '', | 
					
						
							|  |  |  | 					openOutgoingLinksInNewTab: 0, | 
					
						
							|  |  |  | 					scrollToMyPost: 1, | 
					
						
							|  |  |  | 					userLang: 'en-GB', | 
					
						
							|  |  |  | 					usePagination: 1, | 
					
						
							|  |  |  | 					topicsPerPage: '10', | 
					
						
							|  |  |  | 					postsPerPage: '5', | 
					
						
							|  |  |  | 					showemail: 1, | 
					
						
							|  |  |  | 					showfullname: 1, | 
					
						
							|  |  |  | 					restrictChat: 0, | 
					
						
							|  |  |  | 					followTopicsOnCreate: 1, | 
					
						
							|  |  |  | 					followTopicsOnReply: 1, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			await apiUser.updateSettings({ uid: testUid }, data); | 
					
						
							|  |  |  | 			const userSettings = await User.getSettings(testUid); | 
					
						
							|  |  |  | 			assert.strictEqual(userSettings.homePageRoute, 'category/6/testing-ground'); | 
					
						
							| 
									
										
										
										
											2020-10-12 14:18:59 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 		it('should error if language is invalid', async () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const data = { | 
					
						
							| 
									
										
										
										
											2020-01-13 12:27:50 -05:00
										 |  |  | 				uid: testUid, | 
					
						
							|  |  |  | 				settings: { | 
					
						
							|  |  |  | 					userLang: '<invalid-string>', | 
					
						
							|  |  |  | 					topicsPerPage: '10', | 
					
						
							|  |  |  | 					postsPerPage: '5', | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}; | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			try { | 
					
						
							|  |  |  | 				await apiUser.updateSettings({ uid: testUid }, data); | 
					
						
							|  |  |  | 				assert(false); | 
					
						
							|  |  |  | 			} catch (err) { | 
					
						
							| 
									
										
										
										
											2020-01-13 12:27:50 -05:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-language]]'); | 
					
						
							| 
									
										
										
										
											2021-12-14 12:05:12 -05:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-01-13 12:27:50 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should set moderation note', async () => { | 
					
						
							|  |  |  | 			const adminUid = await User.create({ username: 'noteadmin' }); | 
					
						
							|  |  |  | 			await groups.join('administrators', adminUid); | 
					
						
							|  |  |  | 			await socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: 'this is a test user' }); | 
					
						
							|  |  |  | 			await setTimeout(50); | 
					
						
							|  |  |  | 			await socketUser.setModerationNote({ uid: adminUid }, { uid: testUid, note: '<svg/onload=alert(document.location);//' }); | 
					
						
							|  |  |  | 			const notes = await User.getModerationNotes(testUid, 0, -1); | 
					
						
							| 
									
										
										
										
											2024-04-23 13:31:35 -04:00
										 |  |  | 			assert.equal(notes[0].note, ''); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			assert.equal(notes[0].uid, adminUid); | 
					
						
							|  |  |  | 			assert.equal(notes[1].note, 'this is a test user'); | 
					
						
							|  |  |  | 			assert(notes[0].timestamp); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2021-11-23 18:45:30 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		it('should get unread count 0 for guest', async () => { | 
					
						
							|  |  |  | 			const count = await socketUser.getUnreadCount({ uid: 0 }); | 
					
						
							|  |  |  | 			assert.strictEqual(count, 0); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get unread count for user', async () => { | 
					
						
							|  |  |  | 			const count = await socketUser.getUnreadCount({ uid: testUid }); | 
					
						
							| 
									
										
										
										
											2022-12-25 15:05:15 -05:00
										 |  |  | 			assert.strictEqual(count, 4); | 
					
						
							| 
									
										
										
										
											2021-11-23 18:45:30 -05:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get unread chat count 0 for guest', async () => { | 
					
						
							|  |  |  | 			const count = await socketUser.getUnreadChatCount({ uid: 0 }); | 
					
						
							|  |  |  | 			assert.strictEqual(count, 0); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get unread chat count for user', async () => { | 
					
						
							|  |  |  | 			const count = await socketUser.getUnreadChatCount({ uid: testUid }); | 
					
						
							|  |  |  | 			assert.strictEqual(count, 0); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get unread counts 0 for guest', async () => { | 
					
						
							|  |  |  | 			const counts = await socketUser.getUnreadCounts({ uid: 0 }); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(counts, {}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get unread counts for user', async () => { | 
					
						
							|  |  |  | 			const counts = await socketUser.getUnreadCounts({ uid: testUid }); | 
					
						
							|  |  |  | 			assert.deepStrictEqual(counts, { | 
					
						
							|  |  |  | 				unreadChatCount: 0, | 
					
						
							|  |  |  | 				unreadCounts: { | 
					
						
							| 
									
										
										
										
											2022-12-25 15:05:15 -05:00
										 |  |  | 					'': 4, | 
					
						
							|  |  |  | 					new: 4, | 
					
						
							|  |  |  | 					unreplied: 4, | 
					
						
							| 
									
										
										
										
											2021-11-23 18:45:30 -05:00
										 |  |  | 					watched: 0, | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2022-12-25 15:05:15 -05:00
										 |  |  | 				unreadNewTopicCount: 4, | 
					
						
							| 
									
										
										
										
											2021-11-23 18:45:30 -05:00
										 |  |  | 				unreadNotificationCount: 0, | 
					
						
							| 
									
										
										
										
											2022-12-25 15:05:15 -05:00
										 |  |  | 				unreadTopicCount: 4, | 
					
						
							|  |  |  | 				unreadUnrepliedTopicCount: 4, | 
					
						
							| 
									
										
										
										
											2021-11-23 18:45:30 -05:00
										 |  |  | 				unreadWatchedTopicCount: 0, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get user data by uid', async () => { | 
					
						
							|  |  |  | 			const userData = await socketUser.getUserByUID({ uid: testUid }, testUid); | 
					
						
							|  |  |  | 			assert.strictEqual(userData.uid, testUid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get user data by username', async () => { | 
					
						
							|  |  |  | 			const userData = await socketUser.getUserByUsername({ uid: testUid }, 'John Smith'); | 
					
						
							|  |  |  | 			assert.strictEqual(userData.uid, testUid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should get user data by email', async () => { | 
					
						
							|  |  |  | 			const userData = await socketUser.getUserByEmail({ uid: testUid }, 'john@example.com'); | 
					
						
							|  |  |  | 			assert.strictEqual(userData.uid, testUid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should check/consent gdpr status', async () => { | 
					
						
							|  |  |  | 			const consent = await socketUser.gdpr.check({ uid: testUid }, { uid: testUid }); | 
					
						
							|  |  |  | 			assert(!consent); | 
					
						
							|  |  |  | 			await socketUser.gdpr.consent({ uid: testUid }); | 
					
						
							|  |  |  | 			const consentAfter = await socketUser.gdpr.check({ uid: testUid }, { uid: testUid }); | 
					
						
							|  |  |  | 			assert(consentAfter); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('approval queue', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		let oldRegistrationApprovalType; | 
					
						
							|  |  |  | 		let adminUid; | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		before((done) => { | 
					
						
							| 
									
										
										
										
											2019-06-04 11:10:20 -04:00
										 |  |  | 			oldRegistrationApprovalType = meta.config.registrationApprovalType; | 
					
						
							|  |  |  | 			meta.config.registrationApprovalType = 'admin-approval'; | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			User.create({ username: 'admin', password: '123456' }, (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				adminUid = uid; | 
					
						
							|  |  |  | 				groups.join('administrators', uid, done); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		after((done) => { | 
					
						
							| 
									
										
										
										
											2019-06-04 11:10:20 -04:00
										 |  |  | 			meta.config.registrationApprovalType = oldRegistrationApprovalType; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			done(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 		it('should add user to approval queue', async () => { | 
					
						
							|  |  |  | 			await helpers.registerUser({ | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				username: 'rejectme', | 
					
						
							|  |  |  | 				password: '123456', | 
					
						
							|  |  |  | 				'password-confirm': '123456', | 
					
						
							|  |  |  | 				email: '<script>alert("ok")<script>reject@me.com', | 
					
						
							| 
									
										
										
										
											2018-04-12 13:28:14 -04:00
										 |  |  | 				gdpr_consent: true, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 			const { jar } = await helpers.loginUser('admin', '123456'); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			const { body: { users } } = await request.get(`${nconf.get('url')}/api/admin/manage/registration`, { jar }); | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 			assert.equal(users[0].username, 'rejectme'); | 
					
						
							|  |  |  | 			assert.equal(users[0].email, '<script>alert("ok")<script>reject@me.com'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should fail to add user to queue if username is taken', async () => { | 
					
						
							|  |  |  | 			const { body } = await helpers.registerUser({ | 
					
						
							| 
									
										
										
										
											2018-10-27 06:29:12 -04:00
										 |  |  | 				username: 'rejectme', | 
					
						
							|  |  |  | 				password: '123456', | 
					
						
							|  |  |  | 				'password-confirm': '123456', | 
					
						
							|  |  |  | 				email: '<script>alert("ok")<script>reject@me.com', | 
					
						
							|  |  |  | 				gdpr_consent: true, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			assert.equal(body, '[[error:username-taken]]'); | 
					
						
							| 
									
										
										
										
											2018-10-27 06:29:12 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should fail to add user to queue if email is taken', async () => { | 
					
						
							|  |  |  | 			const { body } = await helpers.registerUser({ | 
					
						
							| 
									
										
										
										
											2018-10-27 06:29:12 -04:00
										 |  |  | 				username: 'rejectmenew', | 
					
						
							|  |  |  | 				password: '123456', | 
					
						
							|  |  |  | 				'password-confirm': '123456', | 
					
						
							|  |  |  | 				email: '<script>alert("ok")<script>reject@me.com', | 
					
						
							|  |  |  | 				gdpr_consent: true, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			assert.equal(body, '[[error:email-taken]]'); | 
					
						
							| 
									
										
										
										
											2018-10-27 06:29:12 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should reject user registration', async () => { | 
					
						
							|  |  |  | 			await socketUser.rejectRegistration({ uid: adminUid }, { username: 'rejectme' }); | 
					
						
							|  |  |  | 			const users = await User.getRegistrationQueue(0, -1); | 
					
						
							|  |  |  | 			assert.equal(users.length, 0); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should accept user registration', async () => { | 
					
						
							|  |  |  | 			await helpers.registerUser({ | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				username: 'acceptme', | 
					
						
							|  |  |  | 				password: '123456', | 
					
						
							|  |  |  | 				'password-confirm': '123456', | 
					
						
							|  |  |  | 				email: 'accept@me.com', | 
					
						
							| 
									
										
										
										
											2018-04-12 13:28:14 -04:00
										 |  |  | 				gdpr_consent: true, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const uid = await socketUser.acceptRegistration({ uid: adminUid }, { username: 'acceptme' }); | 
					
						
							|  |  |  | 			const exists = await User.exists(uid); | 
					
						
							|  |  |  | 			assert(exists); | 
					
						
							|  |  |  | 			const users = await User.getRegistrationQueue(0, -1); | 
					
						
							|  |  |  | 			assert.equal(users.length, 0); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-08-26 22:04:18 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should trim username and add user to registration queue', async () => { | 
					
						
							|  |  |  | 			await helpers.registerUser({ | 
					
						
							| 
									
										
										
										
											2020-08-26 22:04:18 -04:00
										 |  |  | 				username: 'invalidname\r\n', | 
					
						
							|  |  |  | 				password: '123456', | 
					
						
							|  |  |  | 				'password-confirm': '123456', | 
					
						
							|  |  |  | 				email: 'invalidtest@test.com', | 
					
						
							|  |  |  | 				gdpr_consent: true, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const users = await db.getSortedSetRange('registration:queue', 0, -1); | 
					
						
							|  |  |  | 			assert.equal(users[0], 'invalidname'); | 
					
						
							| 
									
										
										
										
											2020-08-26 22:04:18 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('invites', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		let notAnInviterUid; | 
					
						
							|  |  |  | 		let inviterUid; | 
					
						
							|  |  |  | 		let adminUid; | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		const PUBLIC_GROUP = 'publicGroup'; | 
					
						
							|  |  |  | 		const PRIVATE_GROUP = 'privateGroup'; | 
					
						
							|  |  |  | 		const OWN_PRIVATE_GROUP = 'ownPrivateGroup'; | 
					
						
							|  |  |  | 		const HIDDEN_GROUP = 'hiddenGroup'; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 		const COMMON_PW = '123456'; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			const results = await utils.promiseParallel({ | 
					
						
							|  |  |  | 				publicGroup: groups.create({ name: PUBLIC_GROUP, private: 0 }), | 
					
						
							|  |  |  | 				privateGroup: groups.create({ name: PRIVATE_GROUP, private: 1 }), | 
					
						
							|  |  |  | 				hiddenGroup: groups.create({ name: HIDDEN_GROUP, hidden: 1 }), | 
					
						
							|  |  |  | 				notAnInviter: User.create({ username: 'notAnInviter', password: COMMON_PW }), | 
					
						
							|  |  |  | 				inviter: User.create({ username: 'inviter', password: COMMON_PW }), | 
					
						
							|  |  |  | 				admin: User.create({ username: 'adminInvite', password: COMMON_PW }), | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			notAnInviterUid = results.notAnInviter; | 
					
						
							|  |  |  | 			inviterUid = results.inviter; | 
					
						
							|  |  |  | 			adminUid = results.admin; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await User.setUserField(inviterUid, 'email', 'inviter@nodebb.org'); | 
					
						
							|  |  |  | 			await Promise.all([ | 
					
						
							|  |  |  | 				groups.create({ name: OWN_PRIVATE_GROUP, ownerUid: inviterUid, private: 1 }), | 
					
						
							|  |  |  | 				groups.join('administrators', adminUid), | 
					
						
							|  |  |  | 				groups.join('cid:0:privileges:invite', inviterUid), | 
					
						
							|  |  |  | 				User.email.confirmByUid(inviterUid), | 
					
						
							|  |  |  | 			]); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('when inviter is not an admin and does not have invite privilege', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			let csrf_token; | 
					
						
							|  |  |  | 			let jar; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 			before(async () => { | 
					
						
							|  |  |  | 				({ jar, csrf_token } = await helpers.loginUser('notAnInviter', COMMON_PW)); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should error if user does not have invite privilege', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, notAnInviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should error out if user tries to use an inviter\'s uid via the API', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				const numInvites = await User.getInvitesNumber(inviterUid); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.'); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				assert.strictEqual(numInvites, 0); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('when inviter has invite privilege', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			let csrf_token; | 
					
						
							|  |  |  | 			let jar; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 			before(async () => { | 
					
						
							|  |  |  | 				({ jar, csrf_token } = await helpers.loginUser('inviter', COMMON_PW)); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should error with invalid data', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({}, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 400); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'Invalid Data'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should error if user is not admin and type is admin-invite-only', async () => { | 
					
						
							|  |  |  | 				meta.config.registrationType = 'admin-invite-only'; | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should send invitation email (without groups to be joined)', async () => { | 
					
						
							|  |  |  | 				meta.config.registrationType = 'normal'; | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should send multiple invitation emails (with a public group to be joined)', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite2@test.com,invite3@test.com', groupsToJoin: [PUBLIC_GROUP] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should error if the user has not permission to invite to the group', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: [PRIVATE_GROUP] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			it('should error if a non-admin tries to invite to the administrators group', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: ['administrators'] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.'); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should to invite to own private group', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: [OWN_PRIVATE_GROUP] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should to invite to multiple groups', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'invite5@test.com', groupsToJoin: [PUBLIC_GROUP, OWN_PRIVATE_GROUP] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should error if tries to invite to hidden group', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [HIDDEN_GROUP] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Bootstrap5 (#10894)
* chore: up deps
* chore: up composer
* fix(deps): bump 2factor to v7
* chore: up harmony
* chore: up harmony
* fix: missing await
* feat: allow middlewares to pass in template values via res.locals
* feat: buildAccountData middleware automatically added ot all account routes
* fix: properly allow values in res.locals.templateValues to be added to the template data
* refactor: user/blocks
* refactor(accounts): categories and consent
* feat: automatically 404 if exposeUid or exposeGroupName come up empty
* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now
* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization
* fix: move reputation removal check to accountHelpers method
* test: skip i18n tests if ref branch when present is not develop
* fix(deps): bump theme versions
* fix(deps): bump ntfy and 2factor
* chore: up harmony
* fix: add missing return
* fix: #11191, only focus on search input on md environments and up
* feat: allow file uploads on mobile chat
closes https://github.com/NodeBB/NodeBB/issues/11217
* chore: up themes
* chore: add lang string
* fix(deps): bump ntfy to 1.0.15
* refactor: use new if/each syntax
* chore: up composer
* fix: regression from user helper refactor
* chore: up harmony
* chore: up composer
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: fix composer version
* feat: add increment helper
* chore: up harmony
* fix: #11228 no timestamps in future :hourglass:
* chore: up harmony
* check config.theme as well
fire action:posts.loaded after processing dom
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up themes
* chore: up harmony
* remove extra class
* refactor: move these to core from harmony
* chore: up widgets
* chore: up widgets
* height auto
* fix: closes #11238
* dont focus inputs, annoying on mobile
* fix: dont focus twice, only focus on chat input on desktop
dont wrap widget footer in row
* chore: up harmony
* chore: up harmony
* update chat window
* chore: up themes
* fix cache buster for skins
* chat fixes
* chore: up harmony
* chore: up composer
* refactor: change hook logs to debug
* fix: scroll to post right after adding to dom
* fix: hash scrolling and highlighting correct post
* test: re-enable read API schema tests
* fix: add back schema changes for 179faa2270f2ad955dcc4a7b04755acce59e6ffd and c3920ccb10d8ead2dcd9914bb1784bed3f6adfd4
* fix: schema changes from 488f0978a4aa1ca1e4d2a1f2e8c7ef7a681f2f27
* fix: schema changes for f4cf482a874701ce80c0f306c49d8788cec66f87
* fix: schema update for be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 69c96078ea78ee2c45885a90a6f6a59f9042a33c
* fix: schema changes for d1364c313021e48a879a818b24947e1457c062f7
* fix: schema changes for 84ff1152f7552dd866e25a90972d970b9861107e
* fix: schema changes for b860c2605c209e0650ef98f4c80d842ea23a51ce
* fix: schema changes for 23cb67a1126481848fac39aafd1e253441e76d7f
* fix: schema changes for b916e42f400dac8aa51670b15e439f87f0eb8939
* fix: schema change for a9bbb586fcb3a1c61b5fb69052236e78cdf7d743
* fix: schema changes for 4b738c8cd36c936a1dbe2bb900c694bf6c5520ec
* fix: schema changes for 58b5781cea9acb129e6604a82ab5a5bfc0d8394d
* fix: schema changes for 794bf01b21709c4be06584d576d706b3d6342057
* fix: schema changes for 80ea12c1c1963f5b39fb64841e4f3c8da3c87af2, e368feef51e0766f119c9710fb4db8f64724725c, and 52ead114bec961c62fa2eb0786540e229f6e4873
* fix: composer-default object in config?
* fix: schema changes for 9acdc6808c070555352951c651921df181b10993 and 093093420027999df3c67bf0ea6024f6dbf81d2d
* fix: schema changes for c0a52924f1f7ef8caeaacda67363ac269b56042c
* fix: schema change for aba420a3f3b774e949c2539c73f3dc0e1ae79a38, move loggedInUser to optional props
* fix: schema changes for 8c67031609da30d788561459f8bb76e9a69253de
* fix: schema changes for 27e53b42f3ce48fa61d3754375715cd41ffe808d
* fix: schema changes for 28359665187b0a3b9ec6226dca1234ebdbd725a5
* fix: breaking test for email confirmation API call
* fix: schema changes for refactored search page
* fix: schema changes for user object
* fix: schema changes for 9f531f957e08eabb4bae844ddd67bde14d9b59f0
* fix: schema changes for c4042c70decd628e5b880bd109515b47e4e16164 and 23175110a29640e6fa052db1079bfedb34a61055
* fix: schema changes for 9b3616b10392e247974eb0c1e6225a1582bf6c69
* fix: schema changes for 5afd5de07d42fd33f039a6f85ded3b4992200e5a
* fix: schema change for 1d7baf12171cffbd3af8914bef4e6297d1160d49
* fix: schema changes for 57bfb37c55a839662144e684875003ab52315ecc and be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 6e86b4afa20d662af8b9f1c07518df2d8c258105 and 3efad2e13b7319eb9a1f4fda7af047be43ebc11f and 68f66223e73a72f378f193c83a9b5546bede2cda
* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)
* fix: re-login on email change
* fix: schema changes for c926358d734a2fa410de87f4e4a91744215fc14a
* fix: schema changes for 388a8270c9882892bad5c8141f65da8d59eac0fd
* fix: schema change for 2658bcc821c22e137a6eeb9bb74098856a642eaf
* fix: no need to call account middlewares for chats routes
* fix: schema changes for 71743affc3e58dc85d4ffa15ce043d4d9ddd3d67
* fix: final schema changes
* test: support for anyOf and oneOf
* fix: check thumb
* dont scroll to top on back press
* remove group log
* fix: add top margin to merged and deleted alerts
* chore: up widgets
* fix: improve fix-lists mixin
* chore: up harmony/composer
* feat: allow hiding quicksearch results during search
* dont record searches made by composer
* chore: up 54
* chore: up spam be gone
* feat: add prev/next page and page count into mobile paginator
* chore: up harmony
* chore: up harmony
* use old style for IS
* fix: hide entire toolbar row if no posts or not singlePost
* fix: updated messaging for post-queue template, #11206
* fix: btn-sm on post queue back button
* fix: bump harmony, closes #11206
* fix: remove unused alert module import
* fix: bump harmony
* fix: bump harmony
* chore: up harmony
* refactor: IS scrolltop
* fix: update users:search-user-for-chat source string
* feat: support for mark-read toggle on chats dropdown and recent chats list
* feat: api v3 calls to mark chat read/unread
* feat: send event:chats.mark socket event on mark read or unread
* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling
* docs: openapi schema updates for chat marking
* fix: allow unread state toggling in chats dropdown too
* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread
* fix: debug log
* refactor: move userSearch filter to a module
* feat(routes): allow remounting /categories (#11230)
* feat: send flags count to frontend on flags list page
* refactor: filter form client-side js to extract out some logic
* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden
* fix: use userFilter module for assignee, reporterId, targetUid
* fix(openapi): schema changes for updated flags page
* fix: dont allow adding duplicates to userFilter
* use same var
* remove log
* fix: closes #11282
* feat: lang key for x-topics
* chore: up harmony
* chore: up emoji
* chore: up harmony
* fix: update userFilter to allow new option `selectedBlock`
* fix: wrong block name passed to userFilter
* fix: https://github.com/NodeBB/NodeBB/issues/11283
* fix: chats, allow multiple dropdowns like in harmony
* chore: up harmony
* refactor: flag note adding/editing, closes #11285
* fix: remove old prepareEdit logic
* chore: add caveat about hacky code block in userFilter module
* fix: placeholders for userFilter module
* refactor: navigator so it works with multiple thumbs/navigators
* chore: up harmony
* fix: closes #11287, destroy quick reply autocomplete
on navigation
* fix: filter disabled categories on user categories page count
* chore: up harmony
* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying
* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests
* fix: tweak table order in ACP dash searches
* fix: only invoke navigator click drag on left mouse button
* feat: add back unread indicator to navigator
* clear bookmark on mark unread
* fix: navigator crash on ajaxify
* better thumb top calculation
* fix: reset user bookmark when topic is marked unread
* Revert "fix: reset user bookmark when topic is marked unread"
This reverts commit 9bcd85c2c6848c3d325d32027261809da6e11c9e.
* fix: update unread indicator on scroll, add unread count
* chore: bump harmony
* fix: crash on navigator unread update when backing out of a topic
* fix: closes #11183
* fix: update topics:recent zset when rescheduling a topic
* fix: dupe quote button, increase delay, hide immediately on empty selection
* fix: navigator not showing up on first load
* refactor: remove glance
assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes
* more sanity checks for bookmark
dont allow setting bookmark higher than topic postcount
* closes #11218, :train:
* Revert "fix: update topics:recent zset when rescheduling a topic"
This reverts commit 737973cca9e94b6cb3867492a09e1e0b1af391d5.
* fix: #11306, show proper error if queued post doesn't exist
was showing no-privileges if someone else accepted the post
* https://github.com/NodeBB/NodeBB/issues/11307
dont use li
* chore: up harmony
* chore: bump version string
* fix: copy paste fail
* feat: closes #7382, tag filtering
add client side support for filtering by tags on /category, /recent and /unread
* chore: up harmony
* chore: up harmony
* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]
This reverts commit cf6cc2c454dc35c330393c62ee8ce67b42d8eefb.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x
This is a breaking change.
* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309
* chore: up deps
* fix: tests, use x-csrf-token query param removed
* test: fix csrf_token
* lint: remove unused
* feat: add itemprop="image" to avatar helper
* fix: get chat upload button in chat modal
* breaking: remove deprecated socket.io methods
* test: update messaging tests to not use sockets
* fix: parent post links
* fix: prevent post tooltip if mouse leaves before data/tpl is loaded
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up harmony
* fix: nested replies indices
* fix(deps): bump 2factor
* feat: add loggedIn user to all api routes
* chore: up themes
* refactor: audit admin v3 write api routes as per #11321
* refactor: audit category v3 write api routes as per #11321 [breaking]
docs: fix open api spec for #11321
* refactor: audit chat v3 write api routes as per #11321
* refactor: audit files v3 write api routes as per #11321
* refactor: audit flags v3 write api routes as per #11321
* refactor: audit posts v3 write api routes as per #11321
* refactor: audit topics v3 write api routes as per #11321
* refactor: audit users v3 write api routes as per #11321
* fix: lang string
* remove min height
* fix: empty topic/labels taking up space
* fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
* chore: up harmony
* fix: closes #11354, show no post error if queued post already accepted/rejected
* test: #11354
* test: #11354
* fix(deps): bump 2factor
* fix: #11357 clear cache on thumb remove
* fix: thumb remove on windows, closes #11357
* test: openapi for thumbs
* test: fix openapi
---------
Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
											
										 
											2023-03-17 11:58:31 -04:00
										 |  |  | 			it('should error if out of invitations', async () => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				meta.config.maximumInvites = 1; | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, `You have invited the maximum amount of people (${5} out of ${1}).`); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				meta.config.maximumInvites = 10; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should send invitation email after maximumInvites increased', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should error if invite is sent via API with a different UID', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response, body } = await helpers.invite({ emails: 'inviter@nodebb.org', groupsToJoin: [] }, adminUid, jar, csrf_token); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				const numInvites = await User.getInvitesNumber(adminUid); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							|  |  |  | 				assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.'); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				assert.strictEqual(numInvites, 0); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 15:42:57 -04:00
										 |  |  | 			it('should succeed if email exists but not actually send an invite', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'inviter@nodebb.org', groupsToJoin: [] }, inviterUid, jar, csrf_token); | 
					
						
							| 
									
										
										
										
											2021-07-19 15:42:57 -04:00
										 |  |  | 				const numInvites = await User.getInvitesNumber(adminUid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2021-07-19 15:42:57 -04:00
										 |  |  | 				assert.strictEqual(numInvites, 0); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('when inviter is an admin', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			let csrf_token; | 
					
						
							|  |  |  | 			let jar; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 			before(async () => { | 
					
						
							|  |  |  | 				({ jar, csrf_token } = await helpers.loginUser('adminInvite', COMMON_PW)); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should escape email', async () => { | 
					
						
							|  |  |  | 				await helpers.invite({ emails: '<script>alert("ok");</script>', groupsToJoin: [] }, adminUid, jar, csrf_token); | 
					
						
							|  |  |  | 				const data = await User.getInvites(adminUid); | 
					
						
							|  |  |  | 				assert.strictEqual(data[0], '<script>alert("ok");</script>'); | 
					
						
							|  |  |  | 				await User.deleteInvitationKey('<script>alert("ok");</script>'); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should invite to the administrators group if inviter is an admin', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.invite({ emails: 'invite99@test.com', groupsToJoin: ['administrators'] }, adminUid, jar, csrf_token); | 
					
						
							|  |  |  | 				assert.strictEqual(response.statusCode, 200); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('after invites checks', () => { | 
					
						
							|  |  |  | 			it('should get user\'s invites', (done) => { | 
					
						
							|  |  |  | 				User.getInvites(inviterUid, (err, data) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					Array.from(Array(6)).forEach((_, i) => { | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 						assert.notEqual(data.indexOf(`invite${i + 1}@test.com`), -1); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should get all invites', (done) => { | 
					
						
							|  |  |  | 				User.getAllInvites((err, data) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 					const adminData = data.filter(d => parseInt(d.uid, 10) === adminUid)[0]; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					assert.notEqual(adminData.invitations.indexOf('invite99@test.com'), -1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 					const inviterData = data.filter(d => parseInt(d.uid, 10) === inviterUid)[0]; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					Array.from(Array(6)).forEach((_, i) => { | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 						assert.notEqual(inviterData.invitations.indexOf(`invite${i + 1}@test.com`), -1); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should fail to verify invitation with invalid data', (done) => { | 
					
						
							|  |  |  | 				User.verifyInvitation({ token: '', email: '' }, (err) => { | 
					
						
							| 
									
										
										
										
											2021-01-24 14:05:11 -05:00
										 |  |  | 					assert.strictEqual(err.message, '[[register:invite.error-invite-only]]'); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should fail to verify invitation with invalid email', (done) => { | 
					
						
							|  |  |  | 				User.verifyInvitation({ token: 'test', email: 'doesnotexist@test.com' }, (err) => { | 
					
						
							| 
									
										
										
										
											2021-01-24 14:05:11 -05:00
										 |  |  | 					assert.strictEqual(err.message, '[[register:invite.error-invalid-data]]'); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should verify installation with no errors', (done) => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 				const email = 'invite1@test.com'; | 
					
						
							| 
									
										
										
										
											2021-07-18 20:06:26 +03:00
										 |  |  | 				db.get(`invitation:uid:${inviterUid}:invited:${email}`, 'token', (err, token) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					User.verifyInvitation({ token: token, email: 'invite1@test.com' }, (err) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should error with invalid username', (done) => { | 
					
						
							|  |  |  | 				User.deleteInvitation('doesnotexist', 'test@test.com', (err) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					assert.equal(err.message, '[[error:invalid-username]]'); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should delete invitation', (done) => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 				const socketUser = require('../src/socket.io/user'); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				socketUser.deleteInvitation({ uid: adminUid }, { invitedBy: 'inviter', email: 'invite1@test.com' }, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					db.isSetMember(`invitation:uid:${inviterUid}`, 'invite1@test.com', (err, isMember) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						assert.equal(isMember, false); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should delete invitation key', (done) => { | 
					
						
							|  |  |  | 				User.deleteInvitationKey('invite99@test.com', (err) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					db.isSetMember(`invitation:uid:${adminUid}`, 'invite99@test.com', (err, isMember) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						assert.equal(isMember, false); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 						db.isSetMember('invitation:uids', adminUid, (err, isMember) => { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 							assert.ifError(err); | 
					
						
							|  |  |  | 							assert.equal(isMember, false); | 
					
						
							|  |  |  | 							done(); | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should joined the groups from invitation after registration', async () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 				const email = 'invite5@test.com'; | 
					
						
							|  |  |  | 				const groupsToJoin = [PUBLIC_GROUP, OWN_PRIVATE_GROUP]; | 
					
						
							| 
									
										
										
										
											2021-07-18 20:06:26 +03:00
										 |  |  | 				const token = await db.get(`invitation:uid:${inviterUid}:invited:${email}`); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { body } = await helpers.registerUser({ | 
					
						
							|  |  |  | 					username: 'invite5', | 
					
						
							|  |  |  | 					password: '123456', | 
					
						
							|  |  |  | 					'password-confirm': '123456', | 
					
						
							|  |  |  | 					email: email, | 
					
						
							|  |  |  | 					gdpr_consent: true, | 
					
						
							|  |  |  | 					token: token, | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				const memberships = await groups.isMemberOfGroups(body.uid, groupsToJoin); | 
					
						
							|  |  |  | 				const joinedToAll = memberships.filter(Boolean); | 
					
						
							|  |  |  | 				assert.strictEqual(joinedToAll.length, groupsToJoin.length, 'Not joined to the groups'); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 		describe('invite groups', () => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			let csrf_token; | 
					
						
							|  |  |  | 			let jar; | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 			before(async () => { | 
					
						
							|  |  |  | 				({ jar, csrf_token } = await helpers.loginUser('inviter', COMMON_PW)); | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should show a list of groups for adding to an invite', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { body } = await helpers.request('get', `/api/v3/users/${inviterUid}/invites/groups`, { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					jar, | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				assert(Array.isArray(body.response)); | 
					
						
							|  |  |  | 				assert.strictEqual(2, body.response.length); | 
					
						
							|  |  |  | 				assert.deepStrictEqual(body.response, ['ownPrivateGroup', 'publicGroup']); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			it('should error out if you request invite groups for another uid', async () => { | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				const { response } = await helpers.request('get', `/api/v3/users/${adminUid}/invites/groups`, { | 
					
						
							| 
									
										
										
										
											2020-11-16 22:47:23 +03:00
										 |  |  | 					jar, | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 				assert.strictEqual(response.statusCode, 403); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('email confirm', () => { | 
					
						
							|  |  |  | 		it('should error with invalid code', (done) => { | 
					
						
							|  |  |  | 			User.email.confirmByCode('asdasda', (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:invalid-data]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should confirm email of user', async () => { | 
					
						
							| 
									
										
										
										
											2020-10-13 22:42:50 -04:00
										 |  |  | 			const email = 'confirm@me.com'; | 
					
						
							|  |  |  | 			const uid = await User.create({ | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				username: 'confirme', | 
					
						
							|  |  |  | 				email: email, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:42:50 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 			const code = await User.email.sendValidationEmail(uid, { email, force: 1 }); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:42:50 -04:00
										 |  |  | 			const unverified = await groups.isMember(uid, 'unverified-users'); | 
					
						
							|  |  |  | 			assert.strictEqual(unverified, true); | 
					
						
							| 
									
										
										
										
											2020-12-10 20:25:48 -05:00
										 |  |  | 			await User.email.confirmByCode(code); | 
					
						
							|  |  |  | 			const [confirmed, isVerified] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 				db.getObjectField(`user:${uid}`, 'email:confirmed'), | 
					
						
							| 
									
										
										
										
											2020-12-10 20:25:48 -05:00
										 |  |  | 				groups.isMember(uid, 'verified-users', uid), | 
					
						
							|  |  |  | 			]); | 
					
						
							|  |  |  | 			assert.strictEqual(parseInt(confirmed, 10), 1); | 
					
						
							|  |  |  | 			assert.strictEqual(isVerified, true); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should confirm email of user by uid', async () => { | 
					
						
							| 
									
										
										
										
											2020-12-10 20:25:48 -05:00
										 |  |  | 			const email = 'confirm2@me.com'; | 
					
						
							|  |  |  | 			const uid = await User.create({ | 
					
						
							|  |  |  | 				username: 'confirme2', | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 				email, | 
					
						
							| 
									
										
										
										
											2020-12-10 20:25:48 -05:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2022-08-03 13:02:21 -04:00
										 |  |  | 			await User.setUserField(uid, 'email', email); | 
					
						
							| 
									
										
										
										
											2020-12-10 20:25:48 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const unverified = await groups.isMember(uid, 'unverified-users'); | 
					
						
							|  |  |  | 			assert.strictEqual(unverified, true); | 
					
						
							|  |  |  | 			await User.email.confirmByUid(uid); | 
					
						
							| 
									
										
										
										
											2020-10-13 22:42:50 -04:00
										 |  |  | 			const [confirmed, isVerified] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2021-02-03 23:59:08 -07:00
										 |  |  | 				db.getObjectField(`user:${uid}`, 'email:confirmed'), | 
					
						
							| 
									
										
										
										
											2020-10-13 22:42:50 -04:00
										 |  |  | 				groups.isMember(uid, 'verified-users', uid), | 
					
						
							|  |  |  | 			]); | 
					
						
							|  |  |  | 			assert.strictEqual(parseInt(confirmed, 10), 1); | 
					
						
							|  |  |  | 			assert.strictEqual(isVerified, true); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2021-11-08 15:16:39 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		it('should remove the email from a different account if the email is already in use', async () => { | 
					
						
							|  |  |  | 			const email = 'confirm2@me.com'; | 
					
						
							|  |  |  | 			const uid = await User.create({ | 
					
						
							|  |  |  | 				username: 'confirme3', | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const oldUid = await db.sortedSetScore('email:uid', email); | 
					
						
							|  |  |  | 			const code = await User.email.sendValidationEmail(uid, email); | 
					
						
							|  |  |  | 			await User.email.confirmByCode(code); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const oldUserData = await User.getUserData(oldUid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual((await db.sortedSetScore('email:uid', email)), uid); | 
					
						
							|  |  |  | 			assert.strictEqual(oldUserData.email, ''); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('user jobs', () => { | 
					
						
							|  |  |  | 		it('should start user jobs', (done) => { | 
					
						
							| 
									
										
										
										
											2019-07-16 11:42:24 -04:00
										 |  |  | 			User.startJobs(); | 
					
						
							|  |  |  | 			done(); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should stop user jobs', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			User.stopJobs(); | 
					
						
							|  |  |  | 			done(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should send digest', (done) => { | 
					
						
							|  |  |  | 			db.sortedSetAdd('digest:day:uids', [Date.now(), Date.now()], [1, 2], (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				User.digest.execute({ interval: 'day' }, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('hideEmail/hideFullname', () => { | 
					
						
							| 
									
										
										
										
											2021-06-21 19:58:31 +03:00
										 |  |  | 		const COMMON_PW = '123456'; | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 		const hidingUser = { | 
					
						
							|  |  |  | 			username: 'hiddenemail', | 
					
						
							|  |  |  | 			email: 'should@be.hidden', | 
					
						
							|  |  |  | 			fullname: 'baris soner usakli', | 
					
						
							|  |  |  | 			password: COMMON_PW, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		const regularUser = { | 
					
						
							|  |  |  | 			username: 'regularUser', | 
					
						
							|  |  |  | 			email: 'regular@example.com', | 
					
						
							|  |  |  | 			fullname: 'regular user', | 
					
						
							|  |  |  | 			password: COMMON_PW, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		let hidingUserJar; | 
					
						
							|  |  |  | 		let adminUid; | 
					
						
							|  |  |  | 		let adminJar; | 
					
						
							|  |  |  | 		let globalModJar; | 
					
						
							|  |  |  | 		let regularUserJar; | 
					
						
							| 
									
										
										
										
											2021-06-21 19:58:31 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		before(async () => { | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 			adminUid = await User.create({ | 
					
						
							|  |  |  | 				username: 'adminhideemail', | 
					
						
							|  |  |  | 				password: COMMON_PW, | 
					
						
							| 
									
										
										
										
											2021-06-21 19:58:31 +03:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 			await groups.join('administrators', adminUid); | 
					
						
							|  |  |  | 			({ jar: adminJar } = await helpers.loginUser('adminhideemail', COMMON_PW)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Edge case: In a grepped test, this user should not be created as the first user to have its email not confirmed
 | 
					
						
							|  |  |  | 			hidingUser.uid = await User.create(hidingUser); | 
					
						
							|  |  |  | 			({ jar: hidingUserJar } = await helpers.loginUser(hidingUser.username, COMMON_PW)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const globalModUid = await User.create({ | 
					
						
							|  |  |  | 				username: 'globalmodhideemail', | 
					
						
							| 
									
										
										
										
											2021-06-21 19:58:31 +03:00
										 |  |  | 				password: COMMON_PW, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 			await groups.join('Global Moderators', globalModUid); | 
					
						
							|  |  |  | 			({ jar: globalModJar } = await helpers.loginUser('globalmodhideemail', COMMON_PW)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			regularUser.uid = await User.create(regularUser); | 
					
						
							|  |  |  | 			({ jar: regularUserJar } = await helpers.loginUser(regularUser.username, COMMON_PW)); | 
					
						
							| 
									
										
										
										
											2021-06-21 19:58:31 +03:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		after((done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			meta.config.hideEmail = 0; | 
					
						
							|  |  |  | 			meta.config.hideFullname = 0; | 
					
						
							|  |  |  | 			done(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 		async function assertPrivacy({ expectVisible, jar, v3Api, emailOnly }) { | 
					
						
							|  |  |  | 			const path = v3Api ? `v3/users/${hidingUser.uid}` : `user/${hidingUser.username}`; | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 			const { body } = await request.get(`${nconf.get('url')}/api/${path}`, { jar }); | 
					
						
							|  |  |  | 			const userData = v3Api ? body.response : body; | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			assert.strictEqual(userData.email, expectVisible ? hidingUser.email : ''); | 
					
						
							|  |  |  | 			if (!emailOnly) { | 
					
						
							|  |  |  | 				assert.strictEqual(userData.fullname, expectVisible ? hidingUser.fullname : ''); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide unconfirmed emails on profile pages', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, emailOnly: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: hidingUserJar, emailOnly: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: adminJar, emailOnly: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: globalModJar, emailOnly: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: regularUserJar, emailOnly: true }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Let's confirm for afterwards
 | 
					
						
							| 
									
										
											  
											
												Bootstrap5 (#10894)
* chore: up deps
* chore: up composer
* fix(deps): bump 2factor to v7
* chore: up harmony
* chore: up harmony
* fix: missing await
* feat: allow middlewares to pass in template values via res.locals
* feat: buildAccountData middleware automatically added ot all account routes
* fix: properly allow values in res.locals.templateValues to be added to the template data
* refactor: user/blocks
* refactor(accounts): categories and consent
* feat: automatically 404 if exposeUid or exposeGroupName come up empty
* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now
* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization
* fix: move reputation removal check to accountHelpers method
* test: skip i18n tests if ref branch when present is not develop
* fix(deps): bump theme versions
* fix(deps): bump ntfy and 2factor
* chore: up harmony
* fix: add missing return
* fix: #11191, only focus on search input on md environments and up
* feat: allow file uploads on mobile chat
closes https://github.com/NodeBB/NodeBB/issues/11217
* chore: up themes
* chore: add lang string
* fix(deps): bump ntfy to 1.0.15
* refactor: use new if/each syntax
* chore: up composer
* fix: regression from user helper refactor
* chore: up harmony
* chore: up composer
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: fix composer version
* feat: add increment helper
* chore: up harmony
* fix: #11228 no timestamps in future :hourglass:
* chore: up harmony
* check config.theme as well
fire action:posts.loaded after processing dom
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up themes
* chore: up harmony
* remove extra class
* refactor: move these to core from harmony
* chore: up widgets
* chore: up widgets
* height auto
* fix: closes #11238
* dont focus inputs, annoying on mobile
* fix: dont focus twice, only focus on chat input on desktop
dont wrap widget footer in row
* chore: up harmony
* chore: up harmony
* update chat window
* chore: up themes
* fix cache buster for skins
* chat fixes
* chore: up harmony
* chore: up composer
* refactor: change hook logs to debug
* fix: scroll to post right after adding to dom
* fix: hash scrolling and highlighting correct post
* test: re-enable read API schema tests
* fix: add back schema changes for 179faa2270f2ad955dcc4a7b04755acce59e6ffd and c3920ccb10d8ead2dcd9914bb1784bed3f6adfd4
* fix: schema changes from 488f0978a4aa1ca1e4d2a1f2e8c7ef7a681f2f27
* fix: schema changes for f4cf482a874701ce80c0f306c49d8788cec66f87
* fix: schema update for be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 69c96078ea78ee2c45885a90a6f6a59f9042a33c
* fix: schema changes for d1364c313021e48a879a818b24947e1457c062f7
* fix: schema changes for 84ff1152f7552dd866e25a90972d970b9861107e
* fix: schema changes for b860c2605c209e0650ef98f4c80d842ea23a51ce
* fix: schema changes for 23cb67a1126481848fac39aafd1e253441e76d7f
* fix: schema changes for b916e42f400dac8aa51670b15e439f87f0eb8939
* fix: schema change for a9bbb586fcb3a1c61b5fb69052236e78cdf7d743
* fix: schema changes for 4b738c8cd36c936a1dbe2bb900c694bf6c5520ec
* fix: schema changes for 58b5781cea9acb129e6604a82ab5a5bfc0d8394d
* fix: schema changes for 794bf01b21709c4be06584d576d706b3d6342057
* fix: schema changes for 80ea12c1c1963f5b39fb64841e4f3c8da3c87af2, e368feef51e0766f119c9710fb4db8f64724725c, and 52ead114bec961c62fa2eb0786540e229f6e4873
* fix: composer-default object in config?
* fix: schema changes for 9acdc6808c070555352951c651921df181b10993 and 093093420027999df3c67bf0ea6024f6dbf81d2d
* fix: schema changes for c0a52924f1f7ef8caeaacda67363ac269b56042c
* fix: schema change for aba420a3f3b774e949c2539c73f3dc0e1ae79a38, move loggedInUser to optional props
* fix: schema changes for 8c67031609da30d788561459f8bb76e9a69253de
* fix: schema changes for 27e53b42f3ce48fa61d3754375715cd41ffe808d
* fix: schema changes for 28359665187b0a3b9ec6226dca1234ebdbd725a5
* fix: breaking test for email confirmation API call
* fix: schema changes for refactored search page
* fix: schema changes for user object
* fix: schema changes for 9f531f957e08eabb4bae844ddd67bde14d9b59f0
* fix: schema changes for c4042c70decd628e5b880bd109515b47e4e16164 and 23175110a29640e6fa052db1079bfedb34a61055
* fix: schema changes for 9b3616b10392e247974eb0c1e6225a1582bf6c69
* fix: schema changes for 5afd5de07d42fd33f039a6f85ded3b4992200e5a
* fix: schema change for 1d7baf12171cffbd3af8914bef4e6297d1160d49
* fix: schema changes for 57bfb37c55a839662144e684875003ab52315ecc and be6bbabd0e2551fbe9571dcf3ee40ad721764543
* fix: schema changes for 6e86b4afa20d662af8b9f1c07518df2d8c258105 and 3efad2e13b7319eb9a1f4fda7af047be43ebc11f and 68f66223e73a72f378f193c83a9b5546bede2cda
* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)
* fix: re-login on email change
* fix: schema changes for c926358d734a2fa410de87f4e4a91744215fc14a
* fix: schema changes for 388a8270c9882892bad5c8141f65da8d59eac0fd
* fix: schema change for 2658bcc821c22e137a6eeb9bb74098856a642eaf
* fix: no need to call account middlewares for chats routes
* fix: schema changes for 71743affc3e58dc85d4ffa15ce043d4d9ddd3d67
* fix: final schema changes
* test: support for anyOf and oneOf
* fix: check thumb
* dont scroll to top on back press
* remove group log
* fix: add top margin to merged and deleted alerts
* chore: up widgets
* fix: improve fix-lists mixin
* chore: up harmony/composer
* feat: allow hiding quicksearch results during search
* dont record searches made by composer
* chore: up 54
* chore: up spam be gone
* feat: add prev/next page and page count into mobile paginator
* chore: up harmony
* chore: up harmony
* use old style for IS
* fix: hide entire toolbar row if no posts or not singlePost
* fix: updated messaging for post-queue template, #11206
* fix: btn-sm on post queue back button
* fix: bump harmony, closes #11206
* fix: remove unused alert module import
* fix: bump harmony
* fix: bump harmony
* chore: up harmony
* refactor: IS scrolltop
* fix: update users:search-user-for-chat source string
* feat: support for mark-read toggle on chats dropdown and recent chats list
* feat: api v3 calls to mark chat read/unread
* feat: send event:chats.mark socket event on mark read or unread
* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling
* docs: openapi schema updates for chat marking
* fix: allow unread state toggling in chats dropdown too
* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread
* fix: debug log
* refactor: move userSearch filter to a module
* feat(routes): allow remounting /categories (#11230)
* feat: send flags count to frontend on flags list page
* refactor: filter form client-side js to extract out some logic
* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden
* fix: use userFilter module for assignee, reporterId, targetUid
* fix(openapi): schema changes for updated flags page
* fix: dont allow adding duplicates to userFilter
* use same var
* remove log
* fix: closes #11282
* feat: lang key for x-topics
* chore: up harmony
* chore: up emoji
* chore: up harmony
* fix: update userFilter to allow new option `selectedBlock`
* fix: wrong block name passed to userFilter
* fix: https://github.com/NodeBB/NodeBB/issues/11283
* fix: chats, allow multiple dropdowns like in harmony
* chore: up harmony
* refactor: flag note adding/editing, closes #11285
* fix: remove old prepareEdit logic
* chore: add caveat about hacky code block in userFilter module
* fix: placeholders for userFilter module
* refactor: navigator so it works with multiple thumbs/navigators
* chore: up harmony
* fix: closes #11287, destroy quick reply autocomplete
on navigation
* fix: filter disabled categories on user categories page count
* chore: up harmony
* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying
* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests
* fix: tweak table order in ACP dash searches
* fix: only invoke navigator click drag on left mouse button
* feat: add back unread indicator to navigator
* clear bookmark on mark unread
* fix: navigator crash on ajaxify
* better thumb top calculation
* fix: reset user bookmark when topic is marked unread
* Revert "fix: reset user bookmark when topic is marked unread"
This reverts commit 9bcd85c2c6848c3d325d32027261809da6e11c9e.
* fix: update unread indicator on scroll, add unread count
* chore: bump harmony
* fix: crash on navigator unread update when backing out of a topic
* fix: closes #11183
* fix: update topics:recent zset when rescheduling a topic
* fix: dupe quote button, increase delay, hide immediately on empty selection
* fix: navigator not showing up on first load
* refactor: remove glance
assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes
* more sanity checks for bookmark
dont allow setting bookmark higher than topic postcount
* closes #11218, :train:
* Revert "fix: update topics:recent zset when rescheduling a topic"
This reverts commit 737973cca9e94b6cb3867492a09e1e0b1af391d5.
* fix: #11306, show proper error if queued post doesn't exist
was showing no-privileges if someone else accepted the post
* https://github.com/NodeBB/NodeBB/issues/11307
dont use li
* chore: up harmony
* chore: bump version string
* fix: copy paste fail
* feat: closes #7382, tag filtering
add client side support for filtering by tags on /category, /recent and /unread
* chore: up harmony
* chore: up harmony
* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]
This reverts commit cf6cc2c454dc35c330393c62ee8ce67b42d8eefb.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x
This is a breaking change.
* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309
* chore: up deps
* fix: tests, use x-csrf-token query param removed
* test: fix csrf_token
* lint: remove unused
* feat: add itemprop="image" to avatar helper
* fix: get chat upload button in chat modal
* breaking: remove deprecated socket.io methods
* test: update messaging tests to not use sockets
* fix: parent post links
* fix: prevent post tooltip if mouse leaves before data/tpl is loaded
* chore: up harmony
* chore: up harmony
* chore: up harmony
* chore: up harmony
* fix: nested replies indices
* fix(deps): bump 2factor
* feat: add loggedIn user to all api routes
* chore: up themes
* refactor: audit admin v3 write api routes as per #11321
* refactor: audit category v3 write api routes as per #11321 [breaking]
docs: fix open api spec for #11321
* refactor: audit chat v3 write api routes as per #11321
* refactor: audit files v3 write api routes as per #11321
* refactor: audit flags v3 write api routes as per #11321
* refactor: audit posts v3 write api routes as per #11321
* refactor: audit topics v3 write api routes as per #11321
* refactor: audit users v3 write api routes as per #11321
* fix: lang string
* remove min height
* fix: empty topic/labels taking up space
* fix: tag filtering when changing filter to watched topics
or changing popular time limit to month
* chore: up harmony
* fix: closes #11354, show no post error if queued post already accepted/rejected
* test: #11354
* test: #11354
* fix(deps): bump 2factor
* fix: #11357 clear cache on thumb remove
* fix: thumb remove on windows, closes #11357
* test: openapi for thumbs
* test: fix openapi
---------
Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
											
										 
											2023-03-17 11:58:31 -04:00
										 |  |  | 			await User.setUserField(hidingUser.uid, 'email', 'should@be.hidden'); | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 			await User.email.confirmByUid(hidingUser.uid); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide from guests by default', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide from unprivileged users by default', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: regularUserJar }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: regularUserJar }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to self by default', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to privileged users by default', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide from guests (system-wide: hide, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			meta.config.hideEmail = 1; | 
					
						
							|  |  |  | 			meta.config.hideFullname = 1; | 
					
						
							|  |  |  | 			// Explicitly set user's privacy settings to hide its email and fullname
 | 
					
						
							|  |  |  | 			const data = { uid: hidingUser.uid, settings: { showemail: 0, showfullname: 0 } }; | 
					
						
							|  |  |  | 			await apiUser.updateSettings({ uid: hidingUser.uid }, data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide from unprivileged users (system-wide: hide, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: regularUserJar }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: regularUserJar }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to self (system-wide: hide, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to privileged users (system-wide: hide, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide from guests (system-wide: show, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			meta.config.hideEmail = 0; | 
					
						
							|  |  |  | 			meta.config.hideFullname = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should hide from unprivileged users (system-wide: show, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: regularUserJar }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: regularUserJar }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to self (system-wide: show, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to privileged users (system-wide: show, by-user: hide)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to guests (system-wide: show, by-user: show)', async () => { | 
					
						
							|  |  |  | 			meta.config.hideEmail = 0; | 
					
						
							|  |  |  | 			meta.config.hideFullname = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Set user's individual privacy settings to show its email and fullname
 | 
					
						
							|  |  |  | 			const data = { uid: hidingUser.uid, settings: { showemail: 1, showfullname: 1 } }; | 
					
						
							|  |  |  | 			await apiUser.updateSettings({ uid: hidingUser.uid }, data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to unprivileged users (system-wide: show, by-user: show)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: regularUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: regularUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// System-wide "hide" prioritized over individual users' settings
 | 
					
						
							|  |  |  | 		it('should hide from guests (system-wide: hide, by-user: show)', async () => { | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 			meta.config.hideEmail = 1; | 
					
						
							|  |  |  | 			meta.config.hideFullname = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 			await assertPrivacy({ v3Api: false }); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 		it('should hide from unprivileged users (system-wide: hide, by-user: show)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: regularUserJar }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: regularUserJar }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to self (system-wide: hide, by-user: show)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: hidingUserJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should be visible to privileged users (system-wide: hide, by-user: show)', async () => { | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: adminJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: false, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 			await assertPrivacy({ v3Api: true, jar: globalModJar, expectVisible: true }); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('should handle array of user data (system-wide: hide)', async () => { | 
					
						
							|  |  |  | 			const userData = await User.hidePrivateData([hidingUser, regularUser], hidingUser.uid); | 
					
						
							|  |  |  | 			assert.strictEqual(userData[0].fullname, hidingUser.fullname); | 
					
						
							|  |  |  | 			assert.strictEqual(userData[0].email, hidingUser.email); | 
					
						
							|  |  |  | 			assert.strictEqual(userData[1].fullname, ''); | 
					
						
							|  |  |  | 			assert.strictEqual(userData[1].email, ''); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 		it('should hide fullname in topic list and topic', async () => { | 
					
						
							|  |  |  | 			await Topics.post({ | 
					
						
							| 
									
										
										
										
											2022-08-20 08:38:14 +03:00
										 |  |  | 				uid: hidingUser.uid, | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 				title: 'Topic hidden', | 
					
						
							|  |  |  | 				content: 'lorem ipsum', | 
					
						
							|  |  |  | 				cid: testCid, | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2023-12-18 12:08:34 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			const { body: body1 } = await request.get(`${nconf.get('url')}/api/recent`); | 
					
						
							|  |  |  | 			assert(!body1.topics[0].user.hasOwnProperty('fullname')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const { body: body2 } = await request.get(`${nconf.get('url')}/api/topic/${body1.topics[0].slug}`); | 
					
						
							|  |  |  | 			assert(!body2.posts[0].user.hasOwnProperty('fullname')); | 
					
						
							| 
									
										
										
										
											2018-04-27 15:55:36 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2017-02-21 15:08:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('user blocking methods', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 		let blockeeUid; | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		before((done) => { | 
					
						
							| 
									
										
										
										
											2017-02-21 15:08:11 +03:00
										 |  |  | 			User.create({ | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 				username: 'blockee', | 
					
						
							|  |  |  | 				email: 'blockee@example.org', | 
					
						
							|  |  |  | 				fullname: 'Block me', | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			}, (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 				blockeeUid = uid; | 
					
						
							|  |  |  | 				done(err); | 
					
						
							| 
									
										
										
										
											2017-02-21 15:08:11 +03:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2017-12-01 17:38:02 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('.toggle()', () => { | 
					
						
							|  |  |  | 			it('should toggle block', (done) => { | 
					
						
							| 
									
										
										
										
											2024-04-11 16:33:50 -04:00
										 |  |  | 				socketUser.toggleBlock({ uid: 1 }, { blockerUid: 1, blockeeUid: blockeeUid, action: 'block' }, (err) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 15:12:23 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					User.blocks.is(blockeeUid, 1, (err, blocked) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 15:12:23 -04:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						assert(blocked); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should toggle block', (done) => { | 
					
						
							| 
									
										
										
										
											2024-04-11 16:33:50 -04:00
										 |  |  | 				socketUser.toggleBlock({ uid: 1 }, { blockerUid: 1, blockeeUid: blockeeUid, action: 'unblock' }, (err) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 15:12:23 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					User.blocks.is(blockeeUid, 1, (err, blocked) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 15:12:23 -04:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						assert(!blocked); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('.add()', () => { | 
					
						
							|  |  |  | 			it('should block a uid', (done) => { | 
					
						
							|  |  |  | 				User.blocks.add(blockeeUid, 1, (err) => { | 
					
						
							| 
									
										
										
										
											2017-12-01 17:38:02 -05:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					User.blocks.list(1, (err, blocked_uids) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 18:13:47 -04:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						assert.strictEqual(Array.isArray(blocked_uids), true); | 
					
						
							|  |  |  | 						assert.strictEqual(blocked_uids.length, 1); | 
					
						
							|  |  |  | 						assert.strictEqual(blocked_uids.includes(blockeeUid), true); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2017-12-01 17:38:02 -05:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2017-05-16 17:14:50 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should automatically increment corresponding user field', (done) => { | 
					
						
							|  |  |  | 				db.getObjectField('user:1', 'blocksCount', (err, count) => { | 
					
						
							| 
									
										
										
										
											2017-05-16 17:14:50 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2018-04-28 10:25:17 -04:00
										 |  |  | 					assert.strictEqual(parseInt(count, 10), 1); | 
					
						
							| 
									
										
										
										
											2017-05-16 17:14:50 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2017-07-26 14:32:53 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should error if you try to block the same uid again', (done) => { | 
					
						
							|  |  |  | 				User.blocks.add(blockeeUid, 1, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 					assert.equal(err.message, '[[error:already-blocked]]'); | 
					
						
							| 
									
										
										
										
											2017-07-26 14:32:53 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('.remove()', () => { | 
					
						
							|  |  |  | 			it('should unblock a uid', (done) => { | 
					
						
							|  |  |  | 				User.blocks.remove(blockeeUid, 1, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					User.blocks.list(1, (err, blocked_uids) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 18:13:47 -04:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						assert.strictEqual(Array.isArray(blocked_uids), true); | 
					
						
							|  |  |  | 						assert.strictEqual(blocked_uids.length, 0); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should automatically decrement corresponding user field', (done) => { | 
					
						
							|  |  |  | 				db.getObjectField('user:1', 'blocksCount', (err, count) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2018-04-28 10:25:17 -04:00
										 |  |  | 					assert.strictEqual(parseInt(count, 10), 0); | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should error if you try to unblock the same uid again', (done) => { | 
					
						
							|  |  |  | 				User.blocks.remove(blockeeUid, 1, (err) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 					assert.equal(err.message, '[[error:already-unblocked]]'); | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('.is()', () => { | 
					
						
							|  |  |  | 			before((done) => { | 
					
						
							| 
									
										
										
										
											2018-04-27 12:51:04 -04:00
										 |  |  | 				User.blocks.add(blockeeUid, 1, done); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should return a Boolean with blocked status for the queried uid', (done) => { | 
					
						
							|  |  |  | 				User.blocks.is(blockeeUid, 1, (err, blocked) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:15:52 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.strictEqual(blocked, true); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('.list()', () => { | 
					
						
							|  |  |  | 			it('should return a list of blocked uids', (done) => { | 
					
						
							|  |  |  | 				User.blocks.list(1, (err, blocked_uids) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.strictEqual(Array.isArray(blocked_uids), true); | 
					
						
							|  |  |  | 					assert.strictEqual(blocked_uids.length, 1); | 
					
						
							|  |  |  | 					assert.strictEqual(blocked_uids.includes(blockeeUid), true); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		describe('.filter()', () => { | 
					
						
							|  |  |  | 			it('should remove entries by blocked uids and return filtered set', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 				User.blocks.filter(1, [{ | 
					
						
							|  |  |  | 					foo: 'foo', | 
					
						
							|  |  |  | 					uid: blockeeUid, | 
					
						
							|  |  |  | 				}, { | 
					
						
							|  |  |  | 					foo: 'bar', | 
					
						
							|  |  |  | 					uid: 1, | 
					
						
							|  |  |  | 				}, { | 
					
						
							|  |  |  | 					foo: 'baz', | 
					
						
							|  |  |  | 					uid: blockeeUid, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}], (err, filtered) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.strictEqual(Array.isArray(filtered), true); | 
					
						
							|  |  |  | 					assert.strictEqual(filtered.length, 1); | 
					
						
							|  |  |  | 					assert.equal(filtered[0].uid, 1); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should allow property argument to be passed in to customise checked property', (done) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 				User.blocks.filter(1, 'fromuid', [{ | 
					
						
							|  |  |  | 					foo: 'foo', | 
					
						
							|  |  |  | 					fromuid: blockeeUid, | 
					
						
							|  |  |  | 				}, { | 
					
						
							|  |  |  | 					foo: 'bar', | 
					
						
							|  |  |  | 					fromuid: 1, | 
					
						
							|  |  |  | 				}, { | 
					
						
							|  |  |  | 					foo: 'baz', | 
					
						
							|  |  |  | 					fromuid: blockeeUid, | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				}], (err, filtered) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.strictEqual(Array.isArray(filtered), true); | 
					
						
							|  |  |  | 					assert.strictEqual(filtered.length, 1); | 
					
						
							|  |  |  | 					assert.equal(filtered[0].fromuid, 1); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should not process invalid sets', (done) => { | 
					
						
							|  |  |  | 				User.blocks.filter(1, [{ foo: 'foo' }, { foo: 'bar' }, { foo: 'baz' }], (err, filtered) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.strictEqual(Array.isArray(filtered), true); | 
					
						
							|  |  |  | 					assert.strictEqual(filtered.length, 3); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					filtered.forEach((obj) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 						assert.strictEqual(obj.hasOwnProperty('foo'), true); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should process plain sets that just contain uids', (done) => { | 
					
						
							|  |  |  | 				User.blocks.filter(1, [1, blockeeUid], (err, filtered) => { | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.strictEqual(filtered.length, 1); | 
					
						
							|  |  |  | 					assert.strictEqual(filtered[0], 1); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-07-03 18:43:29 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 			it('should filter uids that are blocking targetUid', (done) => { | 
					
						
							|  |  |  | 				User.blocks.filterUids(blockeeUid, [1, 2], (err, filtered) => { | 
					
						
							| 
									
										
										
										
											2018-07-03 18:43:29 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							|  |  |  | 					assert.deepEqual(filtered, [2]); | 
					
						
							|  |  |  | 					done(); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-04-20 14:12:15 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('status/online', () => { | 
					
						
							|  |  |  | 		it('should return offline if user is guest', (done) => { | 
					
						
							| 
									
										
										
										
											2021-02-04 00:06:15 -07:00
										 |  |  | 			const status = User.getStatus({ uid: 0 }); | 
					
						
							| 
									
										
										
										
											2020-01-26 21:51:05 -05:00
										 |  |  | 			assert.strictEqual(status, 'offline'); | 
					
						
							|  |  |  | 			done(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return offline if user is guest', async () => { | 
					
						
							| 
									
										
										
										
											2020-01-26 21:51:05 -05:00
										 |  |  | 			assert.strictEqual(await User.isOnline(0), false); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should return true', async () => { | 
					
						
							| 
									
										
										
										
											2020-01-26 21:51:05 -05:00
										 |  |  | 			assert.strictEqual(await User.isOnline(testUid), true); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	describe('isPrivilegedOrSelf', () => { | 
					
						
							|  |  |  | 		it('should return not error if self', (done) => { | 
					
						
							|  |  |  | 			User.isPrivilegedOrSelf(1, 1, (err) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should not error if privileged', (done) => { | 
					
						
							|  |  |  | 			User.create({ username: 'theadmin' }, (err, uid) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 				assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 				groups.join('administrators', uid, (err) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 					assert.ifError(err); | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 					User.isPrivilegedOrSelf(uid, 2, (err) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 						assert.ifError(err); | 
					
						
							|  |  |  | 						done(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 		it('should error if not privileged', (done) => { | 
					
						
							|  |  |  | 			User.isPrivilegedOrSelf(0, 1, (err) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 				assert.equal(err.message, '[[error:no-privileges]]'); | 
					
						
							|  |  |  | 				done(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 00:01:39 -07:00
										 |  |  | 	it('should get admins and mods', (done) => { | 
					
						
							|  |  |  | 		User.getAdminsandGlobalMods((err, data) => { | 
					
						
							| 
									
										
										
										
											2018-06-27 12:15:38 -04:00
										 |  |  | 			assert.ifError(err); | 
					
						
							|  |  |  | 			assert(Array.isArray(data)); | 
					
						
							|  |  |  | 			done(); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2019-12-26 20:17:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-04 16:07:25 -05:00
										 |  |  | 	it('should allow user to login even if password is weak', async () => { | 
					
						
							|  |  |  | 		await User.create({ username: 'weakpwd', password: '123456' }); | 
					
						
							|  |  |  | 		const oldValue = meta.config.minimumPasswordStrength; | 
					
						
							|  |  |  | 		meta.config.minimumPasswordStrength = 3; | 
					
						
							|  |  |  | 		await helpers.loginUser('weakpwd', '123456'); | 
					
						
							|  |  |  | 		meta.config.minimumPasswordStrength = oldValue; | 
					
						
							| 
									
										
										
										
											2019-12-26 20:17:54 -05:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2022-02-03 16:49:41 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	describe('User\'s', async () => { | 
					
						
							|  |  |  | 		let files; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		before(async () => { | 
					
						
							|  |  |  | 			files = await file.walk(path.resolve(__dirname, './user')); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		it('subfolder tests', () => { | 
					
						
							|  |  |  | 			files.forEach((filePath) => { | 
					
						
							|  |  |  | 				require(filePath); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2016-11-02 11:59:01 -05:00
										 |  |  | }); |