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:
Mrugesh Mohapatra
2016-12-15 02:54:59 +05:30
parent ff07a36378
commit 1f67c72fc6
6 changed files with 139 additions and 14 deletions

View File

@ -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;
}
} }
} }

View File

@ -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('/');
});
}
}; };

View File

@ -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, '&lt;');
}
} }
}); });
} }

View File

@ -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();
} }

View 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');
});

View File

@ -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)