feat(user): Report profiles
This adds a simple email-based mechanism to report profiles for abuse. An email with text from the report is sent to Free Code Camp's support account with the reporter's account in copy. This also adds the reporter's details to the report for follow ups.
This commit is contained in:
@ -1145,7 +1145,7 @@ and (max-width : 400px) {
|
|||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset/Delete Account Modal Styles
|
// Account Modal Styles
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
margin: 80px;
|
margin: 80px;
|
||||||
|
|
||||||
@ -1184,6 +1184,14 @@ and (max-width : 400px) {
|
|||||||
background-color: #208e36;
|
background-color: #208e36;
|
||||||
border-color: darkgreen;
|
border-color: darkgreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-textarea {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 590px;
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
import certTypes from '../utils/certTypes.json';
|
import certTypes from '../utils/certTypes.json';
|
||||||
import {
|
import {
|
||||||
ifNoUser401,
|
ifNoUser401,
|
||||||
ifNoUserRedirectTo
|
ifNoUserRedirectTo,
|
||||||
|
ifNotVerifiedRedirectToSettings
|
||||||
} from '../utils/middleware';
|
} from '../utils/middleware';
|
||||||
import { observeQuery } from '../utils/rx';
|
import { observeQuery } from '../utils/rx';
|
||||||
import {
|
import {
|
||||||
@ -140,6 +141,7 @@ module.exports = function(app) {
|
|||||||
const api = app.loopback.Router();
|
const api = app.loopback.Router();
|
||||||
const User = app.models.User;
|
const User = app.models.User;
|
||||||
const Block = app.models.Block;
|
const Block = app.models.Block;
|
||||||
|
const { Email } = app.models;
|
||||||
const map$ = cachedMap(Block);
|
const map$ = cachedMap(Block);
|
||||||
function findUserByUsername$(username, fields) {
|
function findUserByUsername$(username, fields) {
|
||||||
return observeQuery(
|
return observeQuery(
|
||||||
@ -223,6 +225,18 @@ module.exports = function(app) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
router.get('/:username', showUserProfile);
|
router.get('/:username', showUserProfile);
|
||||||
|
router.get(
|
||||||
|
'/:username/report-user/',
|
||||||
|
sendNonUserToMap,
|
||||||
|
ifNotVerifiedRedirectToSettings,
|
||||||
|
getReportUserProfile
|
||||||
|
);
|
||||||
|
|
||||||
|
api.post(
|
||||||
|
'/:username/report-user/',
|
||||||
|
ifNoUser401,
|
||||||
|
postReportUserProfile
|
||||||
|
);
|
||||||
|
|
||||||
app.use('/:lang', router);
|
app.use('/:lang', router);
|
||||||
app.use(api);
|
app.use(api);
|
||||||
@ -631,4 +645,55 @@ module.exports = function(app) {
|
|||||||
return res.render('account/forgot');
|
return res.render('account/forgot');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getReportUserProfile(req, res) {
|
||||||
|
const username = req.params.username.toLowerCase();
|
||||||
|
return res.render('account/report-profile', {
|
||||||
|
title: 'Report User',
|
||||||
|
username
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function postReportUserProfile(req, res, next) {
|
||||||
|
const { user } = req;
|
||||||
|
const { username } = req.params;
|
||||||
|
const report = req.sanitize('reportDescription').trimTags();
|
||||||
|
|
||||||
|
if (!username || !report || report === '') {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'Oops, something is not right please re-check your submission.'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Email.send$({
|
||||||
|
type: 'email',
|
||||||
|
to: 'Team@FreeCodeCamp.com',
|
||||||
|
cc: user.email,
|
||||||
|
from: 'Team@FreeCodeCamp.com',
|
||||||
|
subject: 'Abuse Report : Reporting ' + username + '\'s profile.',
|
||||||
|
text: dedent(`
|
||||||
|
Hello Team,\n
|
||||||
|
This is to report the profile of ${username}.\n
|
||||||
|
Report Details:\n
|
||||||
|
${report}\n\n
|
||||||
|
Reported by:
|
||||||
|
Username: ${user.username}
|
||||||
|
Name: ${user.name}
|
||||||
|
Email: ${user.email}\n
|
||||||
|
Thanks and regards,
|
||||||
|
${user.name}
|
||||||
|
`)
|
||||||
|
}, err => {
|
||||||
|
if (err) {
|
||||||
|
err.redirectTo = '/' + username;
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.flash('info', {
|
||||||
|
msg: 'A report was sent to the team with ' + user.email + ' in copy.'
|
||||||
|
});
|
||||||
|
return res.redirect('/');
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,30 @@ export default function() {
|
|||||||
// every file has contents
|
// every file has contents
|
||||||
keys.map(key => value[key]).every(file => isPoly(file));
|
keys.map(key => value[key]).every(file => isPoly(file));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
customSanitizers: {
|
||||||
|
// Refer : http://stackoverflow.com/a/430240/1932901
|
||||||
|
trimTags(value) {
|
||||||
|
const tagBody = '(?:[^"\'>]|"[^"]*"|\'[^\']*\')*';
|
||||||
|
const tagOrComment = new RegExp(
|
||||||
|
'<(?:'
|
||||||
|
// Comment body.
|
||||||
|
+ '!--(?:(?:-*[^->])*--+|-?)'
|
||||||
|
// Special "raw text" elements whose content should be elided.
|
||||||
|
+ '|script\\b' + tagBody + '>[\\s\\S]*?</script\\s*'
|
||||||
|
+ '|style\\b' + tagBody + '>[\\s\\S]*?</style\\s*'
|
||||||
|
// Regular name
|
||||||
|
+ '|/?[a-z]'
|
||||||
|
+ tagBody
|
||||||
|
+ ')>',
|
||||||
|
'gi');
|
||||||
|
let rawValue;
|
||||||
|
do {
|
||||||
|
rawValue = value;
|
||||||
|
value = value.replace(tagOrComment, '');
|
||||||
|
} while (value !== rawValue);
|
||||||
|
return value.replace(/</g, '<');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -29,21 +29,17 @@ export function ifNoUser401(req, res, next) {
|
|||||||
return res.status(401).end();
|
return res.status(401).end();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flashIfNotVerified(req, res, next) {
|
export function ifNotVerifiedRedirectToSettings(req, res, next) {
|
||||||
return next();
|
const { user } = req;
|
||||||
/*
|
|
||||||
// disabled until authorized required bug is fixed
|
|
||||||
const user = req.user;
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
const email = req.user.email;
|
if (!user.emailVerified) {
|
||||||
const emailVerified = req.user.emailVerified;
|
req.flash('error', {
|
||||||
if (!email || !emailVerified) {
|
msg: 'We do not have your verified email address on record, '
|
||||||
req.flash('info', {
|
+ 'please add it in the settings to continue with your request.'
|
||||||
msg: 'Please verify your email address ' +
|
|
||||||
'<a href="/update-email">here</a>.'
|
|
||||||
});
|
});
|
||||||
|
return res.redirect('/settings');
|
||||||
}
|
}
|
||||||
*/
|
return next();
|
||||||
}
|
}
|
||||||
|
29
server/views/account/report-profile.jade
Normal file
29
server/views/account/report-profile.jade
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
extends ../layout
|
||||||
|
block content
|
||||||
|
#modal-dialog.modal
|
||||||
|
.modal-dialog
|
||||||
|
.modal-content
|
||||||
|
.modal-header
|
||||||
|
a.close(href='/settings', data-dismiss='modal', aria-hidden='true') ×
|
||||||
|
h3 Do you want to report #{username}'s profile for abuse?
|
||||||
|
.modal-body
|
||||||
|
p We will notify the community moderators' team,
|
||||||
|
| and a send copy of this report to your email:
|
||||||
|
strong #{user.email}
|
||||||
|
| . We may get back to you for more information, if required.
|
||||||
|
.modal-footer
|
||||||
|
form(action='/' + username +'/report-user/', method='POST')
|
||||||
|
input(type='hidden', name='_csrf', value=_csrf)
|
||||||
|
div
|
||||||
|
textarea.modal-textarea(name='reportDescription', cols='40', rows='5')
|
||||||
|
.spacer
|
||||||
|
button.btn.btn-danger.btn-block(type='submit')
|
||||||
|
| Yes, submit my report about this user's profile.
|
||||||
|
.spacer
|
||||||
|
a.btn.btn-success.btn-block(href='/settings', data-dismiss='modal', aria-hidden='true')
|
||||||
|
| Nevermind, I don't want to report this user.
|
||||||
|
script.
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const modal$ = document.getElementById('modal-dialog');
|
||||||
|
modal$.classList.add('show');
|
||||||
|
});
|
@ -56,6 +56,9 @@ block content
|
|||||||
if isBackEndCert
|
if isBackEndCert
|
||||||
.button-spacer
|
.button-spacer
|
||||||
a.btn.btn-primary.btn-block(href='/' + username + '/back-end-certification') View My Back End Development Certification
|
a.btn.btn-primary.btn-block(href='/' + username + '/back-end-certification') View My Back End Development Certification
|
||||||
|
if (user && user.username != username)
|
||||||
|
.button-spacer
|
||||||
|
a.btn.btn-primary.btn-block(href='/' + username + '/report-user/') Report this user's profile for abuse
|
||||||
.row
|
.row
|
||||||
.col-xs-12.text-center
|
.col-xs-12.text-center
|
||||||
if (badges.coreTeam && badges.coreTeam.length)
|
if (badges.coreTeam && badges.coreTeam.length)
|
||||||
|
Reference in New Issue
Block a user