Merge pull request #4402 from HabitRPG/add-flag-to-chat
Add flag to chat
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
"NEW_RELIC_APPLICATION_ID":"NEW_RELIC_APPLICATION_ID",
|
||||
"NEW_RELIC_API_KEY":"NEW_RELIC_API_KEY",
|
||||
"GA_ID": "GA_ID",
|
||||
"FLAG_REPORT_EMAIL": "email@mod.com",
|
||||
"EMAIL_SERVER": {
|
||||
"url": "http://example.com",
|
||||
"authUser": "user",
|
||||
|
||||
@@ -135,6 +135,9 @@ a
|
||||
a span.glyphicon
|
||||
color: #333 // Takes base color from bootstrap
|
||||
|
||||
&.text-danger
|
||||
color: #a94442 // Allows glyphicon to use text-danger class
|
||||
|
||||
a.label
|
||||
color: #fff
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
}
|
||||
])
|
||||
|
||||
.controller("MemberModalCtrl", ['$scope', '$rootScope', 'Members', 'Shared', '$http', 'Notification',
|
||||
function($scope, $rootScope, Members, Shared, $http, Notification) {
|
||||
.controller("MemberModalCtrl", ['$scope', '$rootScope', 'Members', 'Shared', '$http', 'Notification', 'Groups',
|
||||
function($scope, $rootScope, Members, Shared, $http, Notification, Groups) {
|
||||
$scope.timestamp = function(timestamp){
|
||||
return moment(timestamp).format('MM/DD/YYYY');
|
||||
}
|
||||
@@ -139,6 +139,20 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
$scope.$close();
|
||||
})
|
||||
}
|
||||
$scope.reportAbuse = function(reporter, message, groupId) {
|
||||
message.flags[reporter._id] = true;
|
||||
Groups.Group.flagChatMessage({gid: groupId, messageId: message.id}, undefined, function(data){
|
||||
Notification.text(window.env.t('abuseReported'));
|
||||
$scope.$close();
|
||||
});
|
||||
}
|
||||
$scope.clearFlagCount = function(message, groupId) {
|
||||
Groups.Group.clearFlagCount({gid: groupId, messageId: message.id}, undefined, function(data){
|
||||
message.flagCount = 0;
|
||||
Notification.text("Flags cleared");
|
||||
$scope.$close();
|
||||
});
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -197,7 +211,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('ChatCtrl', ['$scope', 'Groups', 'User', '$http', 'ApiUrlService', 'Notification', function($scope, Groups, User, $http, ApiUrlService, Notification){
|
||||
.controller('ChatCtrl', ['$scope', 'Groups', 'User', '$http', 'ApiUrlService', 'Notification', 'Members', '$rootScope', function($scope, Groups, User, $http, ApiUrlService, Notification, Members, $rootScope){
|
||||
$scope.message = {content:''};
|
||||
$scope._sending = false;
|
||||
|
||||
@@ -262,6 +276,22 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
||||
$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/like');
|
||||
}
|
||||
|
||||
$scope.flagChatMessage = function(groupId,message) {
|
||||
if(!message.flags) message.flags = {};
|
||||
if(message.flags[User.user._id])
|
||||
Notification.text(window.env.t('abuseAlreadyReported'));
|
||||
else {
|
||||
$scope.abuseObject = message;
|
||||
$scope.groupId = groupId;
|
||||
Members.selectMember(message.uuid, function(){
|
||||
$rootScope.openModal('abuse-flag',{
|
||||
controller:'MemberModalCtrl',
|
||||
scope: $scope
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$scope.sync = function(group){
|
||||
group.$get();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ angular.module('groupServices', ['ngResource']).
|
||||
//query: {method: "GET", isArray:false},
|
||||
postChat: {method: "POST", url: ApiUrlService.get() + '/api/v2/groups/:gid/chat'},
|
||||
deleteChatMessage: {method: "DELETE", url: ApiUrlService.get() + '/api/v2/groups/:gid/chat/:messageId'},
|
||||
flagChatMessage: {method: "POST", url: ApiUrlService.get() + '/api/v2/groups/:gid/chat/:messageId/flag'},
|
||||
clearFlagCount: {method: "POST", url: ApiUrlService.get() + '/api/v2/groups/:gid/chat/:messageId/clearflags'},
|
||||
join: {method: "POST", url: ApiUrlService.get() + '/api/v2/groups/:gid/join'},
|
||||
leave: {method: "POST", url: ApiUrlService.get() + '/api/v2/groups/:gid/leave'},
|
||||
invite: {method: "POST", url: ApiUrlService.get() + '/api/v2/groups/:gid/invite'},
|
||||
|
||||
@@ -7,10 +7,12 @@ function clone(a) {
|
||||
var _ = require('lodash');
|
||||
var nconf = require('nconf');
|
||||
var async = require('async');
|
||||
var utils = require('./../utils');
|
||||
var shared = require('habitrpg-shared');
|
||||
var User = require('./../models/user').model;
|
||||
var Group = require('./../models/group').model;
|
||||
var Challenge = require('./../models/challenge').model;
|
||||
var isProd = nconf.get('NODE_ENV') === 'production';
|
||||
var api = module.exports;
|
||||
|
||||
/*
|
||||
@@ -256,6 +258,82 @@ api.deleteChatMessage = function(req, res, next){
|
||||
});
|
||||
}
|
||||
|
||||
api.flagChatMessage = function(req, res, next){
|
||||
var user = res.locals.user
|
||||
var group = res.locals.group;
|
||||
var message = _.find(group.chat, {id: req.params.mid});
|
||||
|
||||
if(!message) return res.json(404, {err: "Message not found!"});
|
||||
if(message.uuid == user._id) return res.json(401, {err: "Can't report your own message."});
|
||||
|
||||
User.findOne({_id: message.uuid}, {auth: 1}, function(err, author){
|
||||
if(err) return next(err);
|
||||
|
||||
// Log user ids that have flagged the message
|
||||
if(!message.flags) message.flags = {};
|
||||
if(message.flags[user._id] && !user.contributor.admin) return res.json(401, {err: "You have already reported this message"});
|
||||
message.flags[user._id] = true;
|
||||
|
||||
// Log total number of flags (publicly viewable)
|
||||
if(!message.flagCount) message.flagCount = 0;
|
||||
if(user.contributor.admin){
|
||||
// Arbitraty amount, higher than 2
|
||||
message.flagCount = 5;
|
||||
} else {
|
||||
message.flagCount++
|
||||
}
|
||||
|
||||
group.markModified('chat');
|
||||
group.save(function(err,_saved){
|
||||
if(err) return next(err);
|
||||
if (isProd){
|
||||
utils.txnEmail({email: nconf.get('FLAG_REPORT_EMAIL')}, 'flag-report-to-mods', [
|
||||
{name: "MESSAGE_TIME", content: message.timestamp},
|
||||
{name: "MESSAGE_TEXT", content: message.text},
|
||||
|
||||
{name: "REPORTER_USERNAME", content: user.profile.name},
|
||||
{name: "REPORTER_UUID", content: user._id},
|
||||
{name: "REPORTER_EMAIL", content: user.auth.local ? user.auth.local.email : ((user.auth.facebook && user.auth.facebook.emails && user.auth.facebook.emails[0]) ? user.auth.facebook.emails[0].value : null)},
|
||||
{name: "REPORTER_MODAL_URL", content: "https://habitrpg.com/static/front/#?memberId=" + user._id},
|
||||
|
||||
{name: "AUTHOR_USERNAME", content: message.user},
|
||||
{name: "AUTHOR_UUID", content: message.uuid},
|
||||
{name: "AUTHOR_EMAIL", content: author.auth.local ? author.auth.local.email : ((author.auth.facebook && author.auth.facebook.emails && author.auth.facebook.emails[0]) ? author.auth.facebook.emails[0].value : null)},
|
||||
{name: "AUTHOR_MODAL_URL", content: "https://habitrpg.com/static/front/#?memberId=" + message.uuid},
|
||||
|
||||
{name: "GROUP_NAME", content: group.name},
|
||||
{name: "GROUP_TYPE", content: group.type},
|
||||
{name: "GROUP_ID", content: group._id},
|
||||
{name: "GROUP_URL", content: group._id == 'habitrpg' ? (nconf.get('BASE_URL') + '/#/options/groups/tavern') : (group.type === 'guild' ? (nconf.get('BASE_URL')+ '/#/options/groups/guilds/' + group._id) : 'party')},
|
||||
]);
|
||||
}
|
||||
return res.send(204);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
api.clearFlagCount = function(req, res, next){
|
||||
var user = res.locals.user
|
||||
var group = res.locals.group;
|
||||
var message = _.find(group.chat, {id: req.params.mid});
|
||||
|
||||
if(!message) return res.json(404, {err: "Message not found!"});
|
||||
|
||||
if(user.contributor.admin){
|
||||
message.flagCount = 0;
|
||||
|
||||
group.markModified('chat');
|
||||
group.save(function(err,_saved){
|
||||
if(err) return next(err);
|
||||
return res.send(204);
|
||||
});
|
||||
}else{
|
||||
return res.json(401, {err: "Only an admin can clear the flag count!"})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
api.seenMessage = function(req,res,next){
|
||||
// Skip the auth step, we want this to be fast. If !found with uuid/token, then it just doesn't save
|
||||
// Check for req.params.gid to exist
|
||||
|
||||
+16
-2
@@ -101,7 +101,9 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){
|
||||
id: shared.uuid(),
|
||||
text: msg,
|
||||
timestamp: +new Date,
|
||||
likes: {}
|
||||
likes: {},
|
||||
flags: {},
|
||||
flagCount: 0
|
||||
};
|
||||
if (user) {
|
||||
_.defaults(message, {
|
||||
@@ -319,6 +321,18 @@ GroupSchema.statics.bossQuest = function(user, progress, cb) {
|
||||
})
|
||||
}
|
||||
|
||||
GroupSchema.methods.toJSON = function() {
|
||||
var doc = this.toObject();
|
||||
if(doc.chat){
|
||||
doc.chat.forEach(function(msg){
|
||||
msg.flags = {};
|
||||
});
|
||||
}
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
|
||||
module.exports.schema = GroupSchema;
|
||||
var Group = module.exports.model = mongoose.model("Group", GroupSchema);
|
||||
|
||||
@@ -333,4 +347,4 @@ Group.count({_id:'habitrpg'},function(err,ct){
|
||||
type: 'guild',
|
||||
privacy:'public'
|
||||
}).save();
|
||||
})
|
||||
})
|
||||
|
||||
@@ -577,6 +577,28 @@ module.exports = (swagger, v2) ->
|
||||
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
|
||||
action: groups.likeChatMessage
|
||||
|
||||
"/groups/{gid}/chat/{mid}/flag":
|
||||
spec:
|
||||
method: 'POST'
|
||||
description: "Flag a chat message"
|
||||
parameters: [
|
||||
path 'gid','Group id','string'
|
||||
path 'mid','Message id','string'
|
||||
]
|
||||
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
|
||||
action: groups.flagChatMessage
|
||||
|
||||
"/groups/{gid}/chat/{mid}/clearflags":
|
||||
spec:
|
||||
method: 'POST'
|
||||
description: "Clear flag count from message and unhide it"
|
||||
parameters: [
|
||||
path 'gid','Group id','string'
|
||||
path 'mid','Message id','string'
|
||||
]
|
||||
middleware: [auth.auth, i18n.getUserLanguage, groups.attachGroup]
|
||||
action: groups.clearFlagCount
|
||||
|
||||
# ---------------------------------
|
||||
# Members
|
||||
# ---------------------------------
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
mixin chatMessages(inbox)
|
||||
ul.list-unstyled.tavern-chat
|
||||
- var ngRepeat = inbox ? 'message in user.inbox.messages | toArray:true | orderBy:"sort":true' : 'message in group.chat track by message.id'
|
||||
li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid}', style='{{::message.sent ? "opacity:0.5" : ""}}')
|
||||
.scrollable-message
|
||||
li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin")
|
||||
span.pull-right.text-danger(ng-if="user.contributor.admin && message.flagCount > 0")
|
||||
| {{message.flagCount > 1 ? "Message Hidden" : "1 flag"}}
|
||||
.scrollable-message(ng-class='{"transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount > 1)}')
|
||||
span(ng-if='::message.user')
|
||||
a.label.label-default.chat-message.hidden-label
|
||||
span.glyphicon.glyphicon-arrow-right(ng-if='::message.sent')
|
||||
@@ -23,9 +25,12 @@ mixin chatMessages(inbox)
|
||||
if inbox
|
||||
a(ng-click="quickReply(message.uuid)")
|
||||
span.glyphicon.glyphicon-share-alt(tooltip=env.t('pm-reply'))
|
||||
|
|
||||
|
|
||||
a(ng-click='#{inbox? "user.ops.deletePM({params:{id:message.$key}})" : "deleteChatMessage(group, message)"}', ng-if='#{inbox ? "true" : ":: user.contributor.admin || message.uuid == user.id"}')
|
||||
span.glyphicon.glyphicon-trash(tooltip=env.t('delete'))
|
||||
|
|
||||
a(ng-click="flagChatMessage(group._id, message)", ng-if=':: user.contributor.admin || (!message.sent && user.flags.communityGuidelinesAccepted && message.uuid != user.id && message.uuid != "system")')
|
||||
span.glyphicon.glyphicon-flag(tooltip="{{message.flags[user._id] ? env.t('abuseAlreadyReported') : env.t('abuseFlag')}}" ng-class='message.flags[user._id] ? "text-danger" : ""')
|
||||
span.float-label(ng-class='::contribText(message.contributor, message.backer).length > 30 ? "long-title" : ""')
|
||||
a.label.label-default.chat-message(ng-if=':: message.user', ng-class='::userLevelStyleFromLevel(message.contributor.level, message.backer.npc, style)', ng-click='clickMember(message.uuid, true)')
|
||||
span.glyphicon.glyphicon-arrow-right(ng-if='::message.sent')
|
||||
|
||||
@@ -88,4 +88,17 @@ script(type='text/ng-template', id='modals/send-gift.html')
|
||||
button.btn.btn-primary(ng-show=fromBal, ng-click='sendGift(profile._id, gift)')=env.t("send")
|
||||
a.btn.btn-primary(ng-hide=fromBal, ng-click='Payments.showStripe({gift:gift, uuid:profile._id})')=env.t('card')
|
||||
a.btn.btn-warning(ng-hide=fromBal, href='/paypal/checkout?_id={{::user._id}}&apiToken={{::user.apiToken}}&gift={{Payments.encodeGift(profile._id, gift)}}') PayPal
|
||||
button.btn.btn-default(ng-click='$close()')=env.t('cancel')
|
||||
button.btn.btn-default(ng-click='$close()')=env.t('cancel')
|
||||
|
||||
script(type='text/ng-template', id='modals/abuse-flag.html')
|
||||
.modal-header
|
||||
h4!=env.t('abuseFlagModalHeading', {name: "<span class='text-danger'>{{profile.profile.name}}</span>"})
|
||||
.modal-body
|
||||
blockquote
|
||||
markdown(ng-model="abuseObject.text")
|
||||
p!=env.t('abuseFlagModalBody', {firstLinkStart: "<a href='/static/community-guidelines' target='_blank'>", secondLinkStart: "<a href='/static/terms' target='_blank'>", linkEnd: "</a>"})
|
||||
.modal-footer
|
||||
button.pull-left.btn.btn-danger(ng-click='clearFlagCount(abuseObject, groupId)', ng-if='user.contributor.admin && abuseObject.flagCount >= 2')
|
||||
| Reset Flag Count
|
||||
button.btn.btn-primary(ng-click='$close()')=env.t('cancel')
|
||||
button.btn.btn-default(ng-click='reportAbuse(user, abuseObject, groupId)')=env.t("abuseFlagModalButton")
|
||||
|
||||
Reference in New Issue
Block a user