diff --git a/app/controllers/admin/user.js b/app/controllers/admin/user.js index 01aac69..30f3e7b 100644 --- a/app/controllers/admin/user.js +++ b/app/controllers/admin/user.js @@ -61,7 +61,7 @@ class UserController extends SiteController { const { user: userService } = this.dtp.services; try { res.locals.pagination = this.getPaginationParameters(req, 10); - res.locals.userAccounts = await userService.getUserAccounts(res.locals.pagination); + res.locals.userAccounts = await userService.getUserAccounts(res.locals.pagination, req.query.u); res.render('admin/user/index'); } catch (error) { return next(error); diff --git a/app/controllers/user.js b/app/controllers/user.js index 99b08b5..552ea0f 100644 --- a/app/controllers/user.js +++ b/app/controllers/user.js @@ -107,7 +107,23 @@ class UserController extends SiteController { async postCreateUser (req, res, next) { const { user: userService } = this.dtp.services; try { + // verify that the request has submitted a captcha + if ((typeof req.body.captcha !== 'string') || req.body.captcha.length === 0) { + throw new SiteError(403, 'Invalid signup attempt'); + } + // verify that the session has a signup captcha + if (!req.session.captcha || !req.session.captcha.signup) { + throw new SiteError(403, 'Invalid signup attempt'); + } + // verify that the captcha from the form matches the captcha in the signup session flow + if (req.body.captcha !== req.session.captcha.signup) { + throw new SiteError(403, 'The captcha value is not correct'); + } + + // create the user account res.locals.user = await userService.create(req.body); + + // log the user in req.login(res.locals.user, (error) => { if (error) { return next(error); diff --git a/app/models/user.js b/app/models/user.js index d16b2a5..28d9f42 100644 --- a/app/models/user.js +++ b/app/models/user.js @@ -16,6 +16,7 @@ const UserFlagsSchema = new Schema({ const UserPermissionsSchema = new Schema({ canLogin: { type: Boolean, default: true, required: true }, canChat: { type: Boolean, default: true, required: true }, + canCreateLinks: { type: Boolean, default: true, required: true }, }); const UserSchema = new Schema({ @@ -33,6 +34,10 @@ const UserSchema = new Schema({ }, flags: { type: UserFlagsSchema, select: false }, permissions: { type: UserPermissionsSchema, select: false }, + stats: { + profileViewCountTotal: { type: Number, default: 0, required: true }, + profileViewCountUnique: { type: Number, default: 0, required: true }, + } }); module.exports = mongoose.model('User', UserSchema); \ No newline at end of file diff --git a/app/services/link.js b/app/services/link.js index b85d9fd..45d45e6 100644 --- a/app/services/link.js +++ b/app/services/link.js @@ -44,6 +44,9 @@ class LinkService extends SiteService { } async create (user, linkDefinition) { + if (!user.permissions.canCreateLinks) { + throw new SiteError(403, 'You are prohibited from creating links'); + } this.validateUrl(linkDefinition.href); const NOW = new Date(); diff --git a/app/services/user.js b/app/services/user.js index 3612095..cdbff5e 100644 --- a/app/services/user.js +++ b/app/services/user.js @@ -88,6 +88,7 @@ class UserService { user.permissions = { canLogin: true, canChat: true, + canCreateLinks: true, }; this.log.info('creating new user account', { email: userDefinition.email }); @@ -121,6 +122,7 @@ class UserService { 'flags.isModerator': userDefinition.isModerator === 'on', 'permissions.canLogin': userDefinition.canLogin === 'on', 'permissions.canChat': userDefinition.canChat === 'on', + 'permissions.canCreateLinks': userDefinition.canCreateLinks === 'on', }, }, ); @@ -268,9 +270,13 @@ class UserService { return user; } - async getUserAccounts (pagination) { + async getUserAccounts (pagination, username) { + let search = { }; + if (username) { + search.username_lc = { $regex: `^${username.toLowerCase().trim()}` }; + } const users = await User - .find() + .find(search) .sort({ username_lc: 1 }) .select('+email +flags +permissions') .skip(pagination.skip) @@ -426,6 +432,11 @@ class UserService { throw new SiteError(403, 'That username is reserved for system use'); } } + + async recordProfileView (user, req) { + const { resource: resourceService } = this.dtp.services; + await resourceService.recordView(req, 'User', user._id); + } } module.exports = { diff --git a/app/views/admin/user/form.pug b/app/views/admin/user/form.pug index d45a495..c78f5eb 100644 --- a/app/views/admin/user/form.pug +++ b/app/views/admin/user/form.pug @@ -8,6 +8,10 @@ block content form(method="POST", action=`/admin/user/${userAccount._id}`).uk-form input(type="hidden", name="username", value= userAccount.username) input(type="hidden", name="displayName", value= userAccount.displayName) + .uk-margin + label(for="bio").uk-form-label Bio + textarea(id="bio", name="bio", rows="4", placeholder= "Enter profile bio").uk-textarea.uk-resize-vertical= userAccount.bio + .uk-margin div(uk-grid) div(class="uk-width-1-1 uk-width-1-2@m") @@ -33,5 +37,8 @@ block content label input(id="can-chat", name="canChat", type="checkbox", checked= userAccount.permissions.canChat) | Can Chat + label + input(id="can-create-links", name="canCreateLinks", type="checkbox", checked= userAccount.permissions.canCreateLinks) + | Can Create Links button(type="submit").uk-button.uk-button-primary Update User \ No newline at end of file diff --git a/app/views/admin/user/index.pug b/app/views/admin/user/index.pug index ff74877..e29f60e 100644 --- a/app/views/admin/user/index.pug +++ b/app/views/admin/user/index.pug @@ -1,6 +1,14 @@ extends ../layouts/main block content + .uk-margin + form(method="GET", action="/admin/user").uk-form + div(uk-grid).uk-grid-collapse + .uk-width-expand + input(id="username", name="u", placeholder="Enter username", value= query && query.u ? query.u : undefined).uk-input + .uk-width-auto + button(type="submit").uk-button.uk-button-secondary.uk-light Search + .uk-overflow-auto table.uk-table.uk-table-divider.uk-table-hover.uk-table-small.uk-table-justify thead diff --git a/data/patches/user-permissions.js b/data/patches/user-permissions.js new file mode 100644 index 0000000..2764a4d --- /dev/null +++ b/data/patches/user-permissions.js @@ -0,0 +1,15 @@ +'use strict'; + +/* globals db */ + +const cursor = db.users.find({ }); +while (cursor.hasNext()) { + const user = cursor.next(); + print(`updating user: ${user.username}`); + db.users.updateOne( + { _id: user._id }, + { + $set: { 'permissions.canCreateLinks': true }, + }, + ); +} \ No newline at end of file