From fe04f371189776b0d28a3ecc7ef06594a3295aef Mon Sep 17 00:00:00 2001 From: Ben Lind Date: Tue, 22 Jul 2014 16:55:50 -0400 Subject: [PATCH 001/202] Fixes #3260: Buttonless habits full width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the user has neither a plus nor a minus button on one of their habits, the habit’s title will now span the entire width of the task div instead of having a gap to the left. --- public/css/tasks.styl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/css/tasks.styl b/public/css/tasks.styl index 5da7b0443a..d201c3deb6 100644 --- a/public/css/tasks.styl +++ b/public/css/tasks.styl @@ -147,6 +147,8 @@ for $stage in $stages .habit-wide .task-text padding-left: 7em +.habit-narrow .task-text + padding-left: 0.75em // when a task is being dragged .task.ui-sortable-helper From 5e1e1b2c13247703df1fdcbd321ba7b0e70a4c3f Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 21:42:41 -0600 Subject: [PATCH 002/202] Flag icon opens blank modal --- public/js/controllers/groupsCtrl.js | 5 +++++ views/options/social/chat-message.jade | 2 ++ views/shared/modals/members.jade | 7 ++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index a9f744839c..b823f0cb84 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,6 +105,11 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } + $scope.flagChat = function(uid) { + Members.selectMember(uid, function(){ + $rootScope.openModal('flag',{controller:'MemberModalCtrl'}); + }); + } } ]) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 120dc2de42..7cbeb87ed0 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,6 +25,8 @@ mixin chatMessages(inbox) |         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="flagChat(message.uuid)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + span.glyphicon.glyphicon-flag(tooltip=env.t('flag')) span.float-label 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') diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 595929c819..f2ab1607e7 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -88,4 +88,9 @@ 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') \ No newline at end of file + button.btn.btn-default(ng-click='$close()')=env.t('cancel') + +script(type='text/ng-template', id='modals/flag.html') + .modal-header + .modal-body + .modal-footer From 00abb168a122cbc594ee4592907eb48ff0109938 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 21:57:32 -0600 Subject: [PATCH 003/202] Added heading and body of flag modal --- views/shared/modals/members.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index f2ab1607e7..afcdd250cb 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -92,5 +92,7 @@ script(type='text/ng-template', id='modals/send-gift.html') script(type='text/ng-template', id='modals/flag.html') .modal-header + h4=env.t('flagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body + h4!=env.t('flagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer From 6fcf1846b3d42c59f5c376af0dab13cbfd1c6520 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 22:00:48 -0600 Subject: [PATCH 004/202] Renamed everything from flag to abuseFlag --- public/js/controllers/groupsCtrl.js | 2 +- views/shared/modals/members.jade | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index b823f0cb84..ec1897451e 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -107,7 +107,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } $scope.flagChat = function(uid) { Members.selectMember(uid, function(){ - $rootScope.openModal('flag',{controller:'MemberModalCtrl'}); + $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl'}); }); } } diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index afcdd250cb..c08064cbf4 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -90,9 +90,9 @@ script(type='text/ng-template', id='modals/send-gift.html') 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') -script(type='text/ng-template', id='modals/flag.html') +script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header - h4=env.t('flagModalHeading', {name: "{{profile.profile.name}}"}) + h4=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body - h4!=env.t('flagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) + h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer From 01c0ce172257d36e19f9dc2108887f3d25fcc1c0 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 22:02:34 -0600 Subject: [PATCH 005/202] Added text-danger span on username --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index c08064cbf4..0b72234771 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -92,7 +92,7 @@ script(type='text/ng-template', id='modals/send-gift.html') script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header - h4=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) + h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer From c90ea166d45548dd695de7bb3732418e03cc2788 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 22:03:35 -0600 Subject: [PATCH 006/202] Corrected tooltip name --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 7cbeb87ed0..077bec02fc 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) 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="flagChat(message.uuid)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') - span.glyphicon.glyphicon-flag(tooltip=env.t('flag')) + span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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') From 62d92db33264abae175d4abdcbf59ffdab647b0e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:41:40 -0600 Subject: [PATCH 007/202] Added buttons to footer, cleaned up functions --- public/js/controllers/groupsCtrl.js | 14 +++++++++++--- views/options/social/chat-message.jade | 2 +- views/shared/modals/members.jade | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index ec1897451e..63933e4fe7 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,9 +105,12 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - $scope.flagChat = function(uid) { - Members.selectMember(uid, function(){ - $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl'}); + $scope.flagChat = function(message) { + $scope.abuseObject = message; + Members.selectMember(message.uuid, function(){ + $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', + scope: $scope + }); }); } } @@ -144,6 +147,11 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$close(); }) } + $scope.reportAbuse = function(reporter, message) { + Notification.text(window.env.t('abuseReported')); + $rootScope.User.sync(); + $scope.$close(); + } } ]) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 077bec02fc..9c5545523f 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,7 +25,7 @@ mixin chatMessages(inbox) |         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="flagChat(message.uuid)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + a(ng-click="flagChat(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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)') diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 0b72234771..6c2958ec63 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -96,3 +96,5 @@ script(type='text/ng-template', id='modals/abuse-flag.html') .modal-body h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer + button.btn.btn-primary(ng-click='$close()')=env.t('cancel') + button.btn.btn-default(ng-click='reportAbuse(user, abuseObject)')=env.t("abuseFlagModalButton") From a2cf14324cd3284504dfe5220eb858e073247032 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:47:04 -0600 Subject: [PATCH 008/202] Added a flags key to message object --- src/models/group.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/models/group.js b/src/models/group.js index 1bc275afe4..649d8d571b 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -98,7 +98,8 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ id: shared.uuid(), text: msg, timestamp: +new Date, - likes: {} + likes: {}, + flags: {} }; if (user) { _.defaults(message, { @@ -330,4 +331,4 @@ Group.count({_id:'habitrpg'},function(err,ct){ type: 'guild', privacy:'public' }).save(); -}) \ No newline at end of file +}) From c44c607ae70124129ee9176b57c98cf01513d3ac Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:50:41 -0600 Subject: [PATCH 009/202] Renamed flagChat to flagChatMessage --- public/js/controllers/groupsCtrl.js | 2 +- views/options/social/chat-message.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 63933e4fe7..523b9abff7 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,7 +105,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - $scope.flagChat = function(message) { + $scope.flagChatMessage = function(message) { $scope.abuseObject = message; Members.selectMember(message.uuid, function(){ $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 9c5545523f..4878e2e6aa 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,7 +25,7 @@ mixin chatMessages(inbox) |         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="flagChat(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + a(ng-click="flagChatMessage(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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)') From 74b94e676427ddc580e4009cb2cfee17f074f896 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:52:49 -0600 Subject: [PATCH 010/202] Removed flag from system notifications --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 4878e2e6aa..07de522d21 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,7 +25,7 @@ mixin chatMessages(inbox) |         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(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + a(ng-click="flagChatMessage(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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)') From 5a6c9fbf8437b752f7fa6099302a1a9b83781f0e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:03:33 -0600 Subject: [PATCH 011/202] Added some logic for adding flags --- public/js/controllers/groupsCtrl.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 523b9abff7..637361bda4 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -148,6 +148,10 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }) } $scope.reportAbuse = function(reporter, message) { + if(!message.flags) message.flags = {}; + message.flags[User.user._id] = true; + // Post to API should look something like this + //$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/flag'); Notification.text(window.env.t('abuseReported')); $rootScope.User.sync(); $scope.$close(); From e9cffe620615f4c68375a8202639c87025a612ec Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:42:52 -0600 Subject: [PATCH 012/202] Error handling for if message already flagged --- public/js/controllers/groupsCtrl.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 637361bda4..7d01db4554 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -106,6 +106,9 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }); } $scope.flagChatMessage = function(message) { + if(!message.flags) message.flags = {}; + if(message.flags[User.user._id]) + Notification.text(window.env.t('abuseAlreadyReported')); $scope.abuseObject = message; Members.selectMember(message.uuid, function(){ $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', @@ -148,8 +151,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }) } $scope.reportAbuse = function(reporter, message) { - if(!message.flags) message.flags = {}; - message.flags[User.user._id] = true; + message.flags[reporter._id] = true; // Post to API should look something like this //$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/flag'); Notification.text(window.env.t('abuseReported')); From 2f506ce0e07610fd09d04cdfc7b4066b4587ccb1 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:43:20 -0600 Subject: [PATCH 013/202] Give flag text danger class and already reported message if already reproted --- public/css/index.styl | 3 +++ views/options/social/chat-message.jade | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/css/index.styl b/public/css/index.styl index 07e2829d0b..381ab03082 100644 --- a/public/css/index.styl +++ b/public/css/index.styl @@ -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 diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 07de522d21..19938dcbe1 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) 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(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') - span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) + 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 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') From 7a7c13ff6d4da96de6f3cd72d547c6d35923421c Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:43:38 -0600 Subject: [PATCH 014/202] Prevent modal from popping up if already reported --- public/js/controllers/groupsCtrl.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 7d01db4554..809b7f1687 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -109,12 +109,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' if(!message.flags) message.flags = {}; if(message.flags[User.user._id]) Notification.text(window.env.t('abuseAlreadyReported')); - $scope.abuseObject = message; - Members.selectMember(message.uuid, function(){ - $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', - scope: $scope + else { + $scope.abuseObject = message; + Members.selectMember(message.uuid, function(){ + $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', + scope: $scope + }); }); - }); + } } } ]) From 8537c96eb6725ead654f6c1a31f7294502924061 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:09:56 -0600 Subject: [PATCH 015/202] Converted heading to paragraph --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 6c2958ec63..a2cc76b95a 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -94,7 +94,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body - h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) + p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') button.btn.btn-default(ng-click='reportAbuse(user, abuseObject)')=env.t("abuseFlagModalButton") From aafda2dad1a1d74547166904660efb476fcda51e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:14:48 -0600 Subject: [PATCH 016/202] Added text of message to make sure user clicked the right flag --- views/shared/modals/members.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index a2cc76b95a..3513b8b433 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -94,6 +94,8 @@ script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body + blockquote + | {{abuseObject.text}} p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') From 5acde1e609002c3554723dec4199eb3ea5aa7fe2 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:15:03 -0600 Subject: [PATCH 017/202] Added todo for paglias --- public/js/controllers/groupsCtrl.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 809b7f1687..a8f21adbbd 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -154,8 +154,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } $scope.reportAbuse = function(reporter, message) { message.flags[reporter._id] = true; - // Post to API should look something like this - //$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/flag'); + // @TODO @paglias API Call goes here Notification.text(window.env.t('abuseReported')); $rootScope.User.sync(); $scope.$close(); From e49e8f57659bbff6cf59e355c0c9eebe2c5f4290 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:20:14 -0600 Subject: [PATCH 018/202] Added note for paglias --- src/models/group.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/group.js b/src/models/group.js index 649d8d571b..af5e22dab2 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -99,7 +99,7 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ text: msg, timestamp: +new Date, likes: {}, - flags: {} + flags: {} // for @paglias, @crookedneighbor added this. }; if (user) { _.defaults(message, { From ae6e0255c69b4ecb60b0c3502840e875a9ff1085 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sun, 7 Dec 2014 14:35:09 +0100 Subject: [PATCH 019/202] feat(flag-message): add server methods --- public/js/controllers/groupsCtrl.js | 45 ++++++++++++++------------ public/js/services/groupServices.js | 1 + src/controllers/groups.js | 16 +++++++++ src/models/group.js | 14 +++++++- src/routes/apiv2.coffee | 11 +++++++ views/options/social/chat-message.jade | 2 +- views/shared/modals/members.jade | 2 +- 7 files changed, 67 insertions(+), 24 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index a8f21adbbd..dfd5718247 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,24 +105,11 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - $scope.flagChatMessage = function(message) { - if(!message.flags) message.flags = {}; - if(message.flags[User.user._id]) - Notification.text(window.env.t('abuseAlreadyReported')); - else { - $scope.abuseObject = message; - Members.selectMember(message.uuid, function(){ - $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', - scope: $scope - }); - }); - } - } } ]) - .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'); } @@ -152,12 +139,12 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$close(); }) } - $scope.reportAbuse = function(reporter, message) { + $scope.reportAbuse = function(reporter, message, groupId) { message.flags[reporter._id] = true; - // @TODO @paglias API Call goes here - Notification.text(window.env.t('abuseReported')); - $rootScope.User.sync(); - $scope.$close(); + Groups.Group.flagChatMessage({gid: groupId, messageId: message.id}, undefined, function(data){ + Notification.text(window.env.t('abuseReported')); + $scope.$close(); + }); } } ]) @@ -217,7 +204,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; @@ -282,6 +269,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(); } diff --git a/public/js/services/groupServices.js b/public/js/services/groupServices.js index 42dd787846..45ecc8b971 100644 --- a/public/js/services/groupServices.js +++ b/public/js/services/groupServices.js @@ -13,6 +13,7 @@ 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'}, 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'}, diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 50272cdab2..0be99d8fae 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -256,6 +256,22 @@ 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."}); + + message.flags[user._id] = true; + group.markModified('chat'); + group.save(function(err,_saved){ + if(err) return next(err); + return res.send(204); + }); +} + 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 diff --git a/src/models/group.js b/src/models/group.js index af5e22dab2..3e00370b67 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -99,7 +99,7 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ text: msg, timestamp: +new Date, likes: {}, - flags: {} // for @paglias, @crookedneighbor added this. + flags: {} }; if (user) { _.defaults(message, { @@ -317,6 +317,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); diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 415577d565..5708bfac51 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -577,6 +577,17 @@ 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 + # --------------------------------- # Members # --------------------------------- diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 19938dcbe1..5a4ba2e1a7 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,7 +25,7 @@ mixin chatMessages(inbox) |         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(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') + a(ng-click="flagChatMessage(group._id, message)", ng-if='#{inbox ? "true" : ":: 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 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)') diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 3513b8b433..3dc12dfe1a 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -99,4 +99,4 @@ script(type='text/ng-template', id='modals/abuse-flag.html') p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') - button.btn.btn-default(ng-click='reportAbuse(user, abuseObject)')=env.t("abuseFlagModalButton") + button.btn.btn-default(ng-click='reportAbuse(user, abuseObject, groupId)')=env.t("abuseFlagModalButton") From 799934c31b3c67de335f11f8e99d09e9c13e0238 Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Wed, 10 Dec 2014 12:31:09 -0800 Subject: [PATCH 020/202] added space in Jade to the arrow and opt out text in the inbox --- views/options/social/chat-message.jade | 2 ++ views/options/social/index.jade | 1 + 2 files changed, 3 insertions(+) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 120dc2de42..dc332452ed 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -6,6 +6,7 @@ mixin chatMessages(inbox) span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label span.glyphicon.glyphicon-arrow-right(ng-if='::message.sent') + |     span {{::message.user}}  span(ng-class='userAdminGlyphiconStyleFromLevel(message.contributor.level)') // this invisible username label is here to push the message text far enough right that the visible label can be floated to this point without covering up any of the text @@ -28,5 +29,6 @@ mixin chatMessages(inbox) span.float-label 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') + |   span(tooltip='{{::contribText(message.contributor, message.backer)}}') {{::message.user}}  span(ng-class='::userAdminGlyphiconStyleFromLevel(message.contributor.level)') diff --git a/views/options/social/index.jade b/views/options/social/index.jade index 0e836cb580..af5c52a899 100644 --- a/views/options/social/index.jade +++ b/views/options/social/index.jade @@ -16,6 +16,7 @@ script(type='text/ng-template', id='partials/options.social.inbox.html') .checkbox label input(type='checkbox', ng-model='user.inbox.optOut', ng-change='set({"inbox.optOut": user.inbox.optOut?true: false})') + |   span.hint(popover-trigger='mouseenter', popover-placement='right', popover=env.t('optOutPopover'))=env.t('optOut') script(type='text/ng-template', id='partials/options.social.tavern.html') From 2ea12d5c713538cf20d60516cf0c9f97668f4386 Mon Sep 17 00:00:00 2001 From: Negue Date: Sat, 22 Nov 2014 20:40:18 +0100 Subject: [PATCH 021/202] Add in-app-purchase Package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index fc3edb043b..061d4a227e 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "grunt-nodemon": "~0.3.0", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop", "icalendar": "git://github.com/lefnire/node-icalendar#master", + "in-app-purchase": "^0.2.0", "jade": "~1.7.0", "js2xmlparser": "~0.1.2", "lodash": "~2.4.1", From cc4812712a2a29e742db4ea5301bd7ce4faa0c84 Mon Sep 17 00:00:00 2001 From: Negue Date: Sun, 23 Nov 2014 01:50:03 +0100 Subject: [PATCH 022/202] dummy iap verify routes --- config.json.example | 3 +- src/controllers/payments/iap.js | 63 +++++++++++++++++++++++++++++++ src/controllers/payments/index.js | 7 +++- src/routes/payments.js | 3 ++ 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 src/controllers/payments/iap.js diff --git a/config.json.example b/config.json.example index d8bb67b62e..caedcf3ae9 100644 --- a/config.json.example +++ b/config.json.example @@ -41,5 +41,6 @@ "mode":"sandbox", "client_id":"client_id", "client_secret":"client_secret" - } + }, + "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/" } diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js new file mode 100644 index 0000000000..f4c91b663f --- /dev/null +++ b/src/controllers/payments/iap.js @@ -0,0 +1,63 @@ +var iap = require('in-app-purchase'); +var async = require('async'); +var payments = require('./index'); + +/* +For google iap, you need to name your public key file as: +iap-sanbox or iap-live +*/ +iap.config({ + googlePublicKeyPath: "/path/to/google/public/key/dir/" +}); + +exports.androidVerify = function(req, res, next) { + console.info(req.body); + + var token = req.body.id; + var user = res.locals.user; + +iap.setup(function (error) { + if (error) { + console.error('something went wrong...'); + console.error(error); + + return; + } + /* + google receipt must be provided as an object + { + "data": "{stringified data object}", + "signature": "signature from google" + } + */ + // iap is ready + /*iap.validate(iap.GOOGLE, googleReceipt, function (err, googleRes) { + if (err) { + return console.error(err); + } + if (iap.isValidated(googleRes)) { + // yay good! + } + });*/ +}); + +/* + async.waterfall([ + function(response, cb) { + payments.buyGems(user, {customerId: response.id, paymentMethod: 'IAP'}); + + user.save(cb); + } + ], function(err, saved){ + if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors + res.send(200); + user = token = null; + });*/ +}; + +exports.iosVerify = function(req, res, next) { + console.info(req.body); + + var token = req.body.id; + var user = res.locals.user; +}; \ No newline at end of file diff --git a/src/controllers/payments/index.js b/src/controllers/payments/index.js index 176098b4c5..b0c59f643c 100644 --- a/src/controllers/payments/index.js +++ b/src/controllers/payments/index.js @@ -7,9 +7,9 @@ var moment = require('moment'); var isProduction = nconf.get("NODE_ENV") === "production"; var stripe = require('./stripe'); var paypal = require('./paypal'); -var User = require('mongoose').model('User'); var members = require('../members') var async = require('async'); +var iap = require('./iap'); function revealMysteryItems(user) { _.each(shared.content.gear.flat, function(item) { @@ -123,4 +123,7 @@ exports.paypalSubscribeSuccess = paypal.executeBillingAgreement; exports.paypalSubscribeCancel = paypal.cancelSubscription; exports.paypalCheckout = paypal.createPayment; exports.paypalCheckoutSuccess = paypal.executePayment; -exports.paypalIPN = paypal.ipn; \ No newline at end of file +exports.paypalIPN = paypal.ipn; + +exports.iapAndroidVerify = iap.androidVerify; +exports.iapIosVerify = iap.iosVerify; diff --git a/src/routes/payments.js b/src/routes/payments.js index c34d77b24b..8cd6b37ef7 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -17,4 +17,7 @@ router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments. //router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel); +router.post("/iap/android/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapAndroidVerify); +router.post("/iap/ios/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapIosVerify); + module.exports = router; \ No newline at end of file From b59a70f1285a8d8ac10cc3229fcd7852c2f01cf9 Mon Sep 17 00:00:00 2001 From: Negue Date: Mon, 24 Nov 2014 20:20:10 +0100 Subject: [PATCH 023/202] more dummy controller, just for iap data --- src/controllers/payments/iap.js | 8 -------- src/routes/payments.js | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index f4c91b663f..2947d44505 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -2,14 +2,6 @@ var iap = require('in-app-purchase'); var async = require('async'); var payments = require('./index'); -/* -For google iap, you need to name your public key file as: -iap-sanbox or iap-live -*/ -iap.config({ - googlePublicKeyPath: "/path/to/google/public/key/dir/" -}); - exports.androidVerify = function(req, res, next) { console.info(req.body); diff --git a/src/routes/payments.js b/src/routes/payments.js index 8cd6b37ef7..5e77417123 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -17,7 +17,7 @@ router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments. //router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel); -router.post("/iap/android/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapAndroidVerify); -router.post("/iap/ios/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapIosVerify); +router.post("/iap/android/verify", /*auth.authWithUrl, i18n.getUserLanguage, */payments.iapAndroidVerify); +router.post("/iap/ios/verify", /*auth.authWithUrl, i18n.getUserLanguage, */ payments.iapIosVerify); module.exports = router; \ No newline at end of file From ecf20f423cf31d5a8f79fe5c7db5b627ba6044f4 Mon Sep 17 00:00:00 2001 From: Negue Date: Thu, 27 Nov 2014 21:25:15 +0100 Subject: [PATCH 024/202] IAP Verification for Android --- src/controllers/payments/iap.js | 70 +++++++++++++++++++++++---------- src/routes/payments.js | 2 +- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index 2947d44505..daf1627212 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -1,20 +1,37 @@ var iap = require('in-app-purchase'); var async = require('async'); var payments = require('./index'); +var nconf = require('nconf'); + +var inAppPurchase = require('in-app-purchase'); +inAppPurchase.config({ + googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") // this is the path to the directory containing iap-sanbox/iap-live files +}); + +// Validation ERROR Codes +var INVALID_PAYLOAD = 6778001; +var CONNECTION_FAILED = 6778002; +var PURCHASE_EXPIRED = 6778003; exports.androidVerify = function(req, res, next) { - console.info(req.body); - - var token = req.body.id; + var iapBody = req.body; var user = res.locals.user; -iap.setup(function (error) { + iap.setup(function (error) { if (error) { - console.error('something went wrong...'); + var resObj = { + ok: false, + data: 'IAP Error' + }; + + console.error('IAP Setup ERROR'); console.error(error); + res.json(resObj); + return; } + /* google receipt must be provided as an object { @@ -22,29 +39,40 @@ iap.setup(function (error) { "signature": "signature from google" } */ + var testObj = { + data: iapBody.transaction.receipt, + signature: iapBody.transaction.signature + }; + // iap is ready - /*iap.validate(iap.GOOGLE, googleReceipt, function (err, googleRes) { + iap.validate(iap.GOOGLE, testObj, function (err, googleRes) { if (err) { - return console.error(err); + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; } if (iap.isValidated(googleRes)) { + var resObj = { + ok: true, + data: googleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP Android'}); + user.save(); + // yay good! + res.json(resObj); } - });*/ + }); }); - -/* - async.waterfall([ - function(response, cb) { - payments.buyGems(user, {customerId: response.id, paymentMethod: 'IAP'}); - - user.save(cb); - } - ], function(err, saved){ - if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors - res.send(200); - user = token = null; - });*/ }; exports.iosVerify = function(req, res, next) { diff --git a/src/routes/payments.js b/src/routes/payments.js index 5e77417123..adc9a65629 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -17,7 +17,7 @@ router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments. //router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel); -router.post("/iap/android/verify", /*auth.authWithUrl, i18n.getUserLanguage, */payments.iapAndroidVerify); +router.post("/iap/android/verify", auth.authWithUrl, /*i18n.getUserLanguage, */payments.iapAndroidVerify); router.post("/iap/ios/verify", /*auth.authWithUrl, i18n.getUserLanguage, */ payments.iapIosVerify); module.exports = router; \ No newline at end of file From d92832bfb2873b7a768272d459dc0d905929e97f Mon Sep 17 00:00:00 2001 From: Negue Date: Fri, 28 Nov 2014 23:44:42 +0100 Subject: [PATCH 025/202] Hopefully a working IOS Verification --- src/controllers/payments/iap.js | 49 +++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index daf1627212..05eb3ff8f7 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -65,7 +65,7 @@ exports.androidVerify = function(req, res, next) { data: googleRes }; - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP Android'}); + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); user.save(); // yay good! @@ -78,6 +78,51 @@ exports.androidVerify = function(req, res, next) { exports.iosVerify = function(req, res, next) { console.info(req.body); - var token = req.body.id; + var iapBody = req.body; var user = res.locals.user; + + iap.setup(function (error) { + if (error) { + var resObj = { + ok: false, + data: 'IAP Error' + }; + + console.error('IAP Setup ERROR'); + console.error(error); + + res.json(resObj); + + return; + } + + // iap is ready + iap.validate(iap.APPLE, iapBody.transaction.receipt, function (err, appleRes) { + if (err) { + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; + } + if (iap.isValidated(appleRes)) { + var resObj = { + ok: true, + data: appleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); + user.save(); + + // yay good! + res.json(resObj); + } + }); +}); }; \ No newline at end of file From bb37c163cf1bca94656d5a962dcc9344f1843cbc Mon Sep 17 00:00:00 2001 From: Negue Date: Mon, 8 Dec 2014 22:21:32 +0100 Subject: [PATCH 026/202] fix spacing --- src/controllers/payments/iap.js | 151 ++++++++++++++++---------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index 05eb3ff8f7..f7d44fcc7b 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -5,7 +5,8 @@ var nconf = require('nconf'); var inAppPurchase = require('in-app-purchase'); inAppPurchase.config({ - googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") // this is the path to the directory containing iap-sanbox/iap-live files + // this is the path to the directory containing iap-sanbox/iap-live files + googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") }); // Validation ERROR Codes @@ -19,24 +20,24 @@ exports.androidVerify = function(req, res, next) { iap.setup(function (error) { if (error) { - var resObj = { - ok: false, - data: 'IAP Error' - }; + var resObj = { + ok: false, + data: 'IAP Error' + }; - console.error('IAP Setup ERROR'); - console.error(error); + console.error('IAP Setup ERROR'); + console.error(error); - res.json(resObj); + res.json(resObj); - return; + return; } /* - google receipt must be provided as an object - { - "data": "{stringified data object}", - "signature": "signature from google" + google receipt must be provided as an object + { + "data": "{stringified data object}", + "signature": "signature from google" } */ var testObj = { @@ -46,33 +47,34 @@ exports.androidVerify = function(req, res, next) { // iap is ready iap.validate(iap.GOOGLE, testObj, function (err, googleRes) { - if (err) { - var resObj = { - ok: false, - data: { - code: INVALID_PAYLOAD, - message: err.toString() - } - }; - - res.json(resObj); - console.error(err); - return; - } - if (iap.isValidated(googleRes)) { - var resObj = { - ok: true, - data: googleRes - }; + if (err) { + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; + } + + if (iap.isValidated(googleRes)) { + var resObj = { + ok: true, + data: googleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); + user.save(); - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); - user.save(); - - // yay good! - res.json(resObj); - } + // yay good! + res.json(resObj); + } }); -}); + }); }; exports.iosVerify = function(req, res, next) { @@ -83,46 +85,47 @@ exports.iosVerify = function(req, res, next) { iap.setup(function (error) { if (error) { - var resObj = { - ok: false, - data: 'IAP Error' - }; - - console.error('IAP Setup ERROR'); - console.error(error); - - res.json(resObj); - - return; + var resObj = { + ok: false, + data: 'IAP Error' + }; + + console.error('IAP Setup ERROR'); + console.error(error); + + res.json(resObj); + + return; } // iap is ready iap.validate(iap.APPLE, iapBody.transaction.receipt, function (err, appleRes) { - if (err) { - var resObj = { - ok: false, - data: { - code: INVALID_PAYLOAD, - message: err.toString() - } - }; - - res.json(resObj); - console.error(err); - return; - } - if (iap.isValidated(appleRes)) { - var resObj = { - ok: true, - data: appleRes - }; + if (err) { + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; + } + + if (iap.isValidated(appleRes)) { + var resObj = { + ok: true, + data: appleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); + user.save(); - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); - user.save(); - - // yay good! - res.json(resObj); - } + // yay good! + res.json(resObj); + } }); -}); + }); }; \ No newline at end of file From f9cc2adda548a15b296cb9de2d8ced137752e4be Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 10 Dec 2014 14:25:00 -0700 Subject: [PATCH 027/202] fix(iap): iap verification fixes --- src/controllers/payments/iap.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index f7d44fcc7b..29b03fedfb 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -67,9 +67,8 @@ exports.androidVerify = function(req, res, next) { data: googleRes }; - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); - user.save(); - + payments.buyGems({user:user, paymentMethod:'IAP GooglePlay'}); + // yay good! res.json(resObj); } @@ -120,8 +119,7 @@ exports.iosVerify = function(req, res, next) { data: appleRes }; - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); - user.save(); + payments.buyGems({user:user, paymentMethod:'IAP AppleStore'}); // yay good! res.json(resObj); From 176175224085d94259fa5cb1650fa703b1a636d5 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 11 Dec 2014 16:51:39 +0100 Subject: [PATCH 028/202] use email server for reset-password --- src/controllers/auth.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 8cf4b9a840..f4e38af542 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -206,16 +206,16 @@ api.resetPassword = function(req, res, next){ if (!user) return res.send(500, {err:"Couldn't find a user registered for email " + email}); user.auth.local.salt = salt; user.auth.local.hashed_password = hashed_password; - utils.sendEmail({ - from: "HabitRPG ", - to: email, - subject: "Password Reset for HabitRPG", - text: "Password for " + user.auth.local.username + " has been reset to " + newPassword + ". Log in at " + nconf.get('BASE_URL'), - html: "Password for " + user.auth.local.username + " has been reset to " + newPassword + ". Log in at " + nconf.get('BASE_URL') + utils.txnEmail(user, 'reset-password', { + NEW_PASSWORD: newPassword, + URL: nconf.get('BASE_URL'), + USERNAME: user.auth.local.username + }); + user.save(function(err){ + if(err) return next(err); + res.send('New password sent to '+ email); + email = salt = newPassword = hashed_password = null; }); - user.save(); - res.send('New password sent to '+ email); - email = salt = newPassword = hashed_password = null; }); }; From 29382d18f7a8bf37b9696ee0d8e5d4ecf55e6e73 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Thu, 11 Dec 2014 17:05:31 +0100 Subject: [PATCH 029/202] fix(reset-password): use array for variables --- src/controllers/auth.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index f4e38af542..4e3877ca3f 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -206,11 +206,11 @@ api.resetPassword = function(req, res, next){ if (!user) return res.send(500, {err:"Couldn't find a user registered for email " + email}); user.auth.local.salt = salt; user.auth.local.hashed_password = hashed_password; - utils.txnEmail(user, 'reset-password', { - NEW_PASSWORD: newPassword, - URL: nconf.get('BASE_URL'), - USERNAME: user.auth.local.username - }); + utils.txnEmail(user, 'reset-password', [ + {name: "NEW_PASSWORD", content: newPassword}, + {name: "URL", content: nconf.get('BASE_URL')}, + {name: "USERNAME", content: user.auth.local.username} + ]); user.save(function(err){ if(err) return next(err); res.send('New password sent to '+ email); From 9bd03042230f8190a7b3c2fc3978815c350d9a02 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 10:23:29 -0600 Subject: [PATCH 030/202] Updated message preview on flag abuse modal to use markdown interpretation --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 3dc12dfe1a..d457213d34 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -95,7 +95,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body blockquote - | {{abuseObject.text}} + markdown(ng-model="abuseObject.text") p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') From 564677340caa75db2020bb40acbfdcbc2af6696d Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 10:29:12 -0600 Subject: [PATCH 031/202] User can only flag message after accepting community guidelines --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index bbc4fed1db..a5b18f2b79 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') + a(ng-click="flagChatMessage(group._id, message)", ng-if='#{inbox ? "true" : ":: 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 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)') From d8515fc8ded7fe0d770e535c273668fe9d63d1d8 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 12:16:57 -0600 Subject: [PATCH 032/202] Added fallback for is message does not contain flags key. Added error for if message was already flagged by user --- src/controllers/groups.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 0be99d8fae..93a39a26f3 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -264,6 +264,9 @@ api.flagChatMessage = function(req, res, next){ 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."}); + if(!message.flags) message.flags = {}; + if(message.flags[user._id]) return res.json(401, {err: "You have already reported this message"}); + message.flags[user._id] = true; group.markModified('chat'); group.save(function(err,_saved){ From d24f6ad1acdfdbef00444322d4a088a01224e535 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 12:45:25 -0600 Subject: [PATCH 033/202] Converted sent transparency to ng-class instead of style --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index a5b18f2b79..c5fbe9e2e6 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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" : ""}}') + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent}') .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From 6fb531234d566048ee3d74f4f1ede2fcdff287c2 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:00:25 -0600 Subject: [PATCH 034/202] Added flagCount variable --- src/controllers/groups.js | 7 ++++++- src/models/group.js | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 93a39a26f3..289e950bdd 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -264,10 +264,15 @@ api.flagChatMessage = function(req, res, next){ 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."}); + // Log user ids that have flagged the message if(!message.flags) message.flags = {}; if(message.flags[user._id]) 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; + message.flagCount++ + group.markModified('chat'); group.save(function(err,_saved){ if(err) return next(err); diff --git a/src/models/group.js b/src/models/group.js index 3e00370b67..be66b59fa7 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -99,7 +99,8 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ text: msg, timestamp: +new Date, likes: {}, - flags: {} + flags: {}, + flagCount: 0 }; if (user) { _.defaults(message, { From 4718641d3eaf9ca3a3ea5861efbc98af8c359f24 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:01:50 -0600 Subject: [PATCH 035/202] Hide message if flag count is 2 or higher --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index c5fbe9e2e6..b559793012 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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, "transparent": message.sent}') + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2") .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From 1fb73b35ab0527a603ec800bedc981dcd99cea17 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:05:48 -0600 Subject: [PATCH 036/202] Always show message if admin --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index b559793012..5490cc6793 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2") + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From c8c64a1b81ffdc1a976e50ec1256bc24488e5671 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:09:00 -0600 Subject: [PATCH 037/202] Allow admin to see messages that have been flagged more than twice, but make them semi-transparent --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 5490cc6793..fa2ab1743d 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From db1f24e7b44ede01dbf3efeed1ef3bcceb856288 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:12:50 -0600 Subject: [PATCH 038/202] Added note about how many flags a message has --- views/options/social/chat-message.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index fa2ab1743d..3aa4aa3b7d 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -2,6 +2,8 @@ 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, "transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}', 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}} flag{{message.flagCount != 1 ? 's' : ''}} .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From 8ac3e312ed29388d153921b15f934723fc3b81db Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:34:43 -0600 Subject: [PATCH 039/202] Admin flagging counts for more, automatically hides message --- src/controllers/groups.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 289e950bdd..1f40fb7684 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -271,7 +271,12 @@ api.flagChatMessage = function(req, res, next){ // Log total number of flags (publicly viewable) if(!message.flagCount) message.flagCount = 0; - message.flagCount++ + if(user.contributor.admin){ + // Arbitraty amount, higher than 2 + message.flagCount = 5; + } else { + message.flagCount++ + } group.markModified('chat'); group.save(function(err,_saved){ From f9c00003f3526ea0bf7f0816243815df618c83f9 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:38:57 -0600 Subject: [PATCH 040/202] Moved ng-class so it would update transparent class in real time after flaggging --- views/options/social/chat-message.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 3aa4aa3b7d..c5ffe81e1b 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,10 +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, "transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") + 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}} flag{{message.flagCount != 1 ? 's' : ''}} - .scrollable-message + .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') From 38cd0e8de1d99dfcc8b7ef9c84e351cc3409bb78 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 17:29:01 -0600 Subject: [PATCH 041/202] Added method for letting an admin unhide a flagged message --- public/js/controllers/groupsCtrl.js | 8 ++++++++ public/js/services/groupServices.js | 1 + src/controllers/groups.js | 17 +++++++++++++++++ src/routes/apiv2.coffee | 11 +++++++++++ views/shared/modals/members.jade | 2 ++ 5 files changed, 39 insertions(+) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index dfd5718247..f99a79ec76 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -146,6 +146,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$close(); }); } + $scope.clearFlagCount = function(message, groupId) { + console.log("Heeey"); + Groups.Group.clearFlagCount({gid: groupId, messageId: message.id}, undefined, function(data){ + message.flagCount = 0; + Notification.text("Flags cleared"); + $scope.$close(); + }); + } } ]) diff --git a/public/js/services/groupServices.js b/public/js/services/groupServices.js index 45ecc8b971..e07f21b15c 100644 --- a/public/js/services/groupServices.js +++ b/public/js/services/groupServices.js @@ -14,6 +14,7 @@ angular.module('groupServices', ['ngResource']). 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'}, diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 1f40fb7684..dbeaefbd0d 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -285,6 +285,23 @@ api.flagChatMessage = function(req, res, next){ }); } +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); + }); +} + 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 diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 5708bfac51..0f8d272c69 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -588,6 +588,17 @@ module.exports = (swagger, v2) -> 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 # --------------------------------- diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index d457213d34..27d42d4623 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -98,5 +98,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') markdown(ng-model="abuseObject.text") p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer + button.pull-left.btn.btn-primary(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") From 6b8280ccbb6d72e575439590b2a1984c41634557 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 17:41:54 -0600 Subject: [PATCH 042/202] Adjusted spacing of chat buttons --- views/options/social/chat-message.jade | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index c5ffe81e1b..b9be7a2349 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,9 +25,10 @@ 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='#{inbox ? "true" : ":: 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 From c67da1804af7843c99d0a000119eb0b94b21c504 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:40:25 -0600 Subject: [PATCH 043/202] Changed transparent class for admin to only appear if it is hidden --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index b9be7a2349..c7a7dc6619 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -4,7 +4,7 @@ mixin chatMessages(inbox) 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}} flag{{message.flagCount != 1 ? 's' : ''}} - .scrollable-message(ng-class='{"transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}') + .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') From 3c2319b63cc3bb544ecd0c531d36fa9b06a3e45f Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:42:39 -0600 Subject: [PATCH 044/202] Made it impossible to flag own message in inbox --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index c7a7dc6619..e763a202ee 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -29,7 +29,7 @@ mixin chatMessages(inbox) 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='#{inbox ? "true" : ":: user.flags.communityGuidelinesAccepted && message.uuid != user.id && message.uuid != \'system\'"}') + a(ng-click="flagChatMessage(group._id, message)", ng-if=':: !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 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)') From a021ab289659bda4067b7256636e6315625abf76 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:43:01 -0600 Subject: [PATCH 045/202] Allow admin to reflag a message they've flagged and unflagged previously --- src/controllers/groups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index dbeaefbd0d..caa36809cc 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -266,7 +266,7 @@ api.flagChatMessage = function(req, res, next){ // Log user ids that have flagged the message if(!message.flags) message.flags = {}; - if(message.flags[user._id]) return res.json(401, {err: "You have already reported this message"}); + 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) From a0a43784651d2ca31ff96ac8822cff3e2b9bbe7c Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:44:58 -0600 Subject: [PATCH 046/202] Admin can see flag on own message --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index e763a202ee..4945b44628 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -29,7 +29,7 @@ mixin chatMessages(inbox) 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=':: !message.sent && user.flags.communityGuidelinesAccepted && message.uuid != user.id && message.uuid != "system"') + 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 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)') From dff7a741355bb0f8a3d6ee650c4236fc8a9ac017 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:47:06 -0600 Subject: [PATCH 047/202] Made flag number more helpful --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 4945b44628..afe4d2e1b3 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -3,7 +3,7 @@ mixin chatMessages(inbox) - 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}', 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}} flag{{message.flagCount != 1 ? 's' : ''}} + | {{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 From a95947b2dcb7f30fb9681944f4372ed012f7e9c8 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:49:42 -0600 Subject: [PATCH 048/202] Made reset flag count button red --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 27d42d4623..0255ab8e6d 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -98,7 +98,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') markdown(ng-model="abuseObject.text") p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer - button.pull-left.btn.btn-primary(ng-click='clearFlagCount(abuseObject, groupId)', ng-if='user.contributor.admin && abuseObject.flagCount >= 2') + 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") From 3027ccff83c13400a18184361be3a857f910d975 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 11 Dec 2014 21:06:38 -0700 Subject: [PATCH 049/202] fix(plans): fix fields which are NaN --- migrations/20141211_NaN_consecutives.js | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 migrations/20141211_NaN_consecutives.js diff --git a/migrations/20141211_NaN_consecutives.js b/migrations/20141211_NaN_consecutives.js new file mode 100644 index 0000000000..2822df0267 --- /dev/null +++ b/migrations/20141211_NaN_consecutives.js @@ -0,0 +1,4 @@ +db.users.update({'purchased.plan.consecutive.count':NaN}, {$set:{'purchased.plan.consecutive.count':0}}, {multi:1}); +db.users.update({'purchased.plan.consecutive.offset':NaN}, {$set:{'purchased.plan.consecutive.offset':0}}, {multi:1}); +db.users.update({'purchased.plan.consecutive.gemCapExtra':NaN}, {$set:{'purchased.plan.consecutive.gemCapExtra':0}}, {multi:1}); +db.users.update({'purchased.plan.consecutive.trinkets':NaN}, {$set:{'purchased.plan.consecutive.trinkets':0}}, {multi:1}); \ No newline at end of file From ae762837e2846ddc6deda0f52c14c8a3b1f9c888 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Thu, 11 Dec 2014 21:20:22 -0700 Subject: [PATCH 050/202] refactor(challenges): cleanup challenge-creation. can use async.auto to keep variables around, no need to set parent closure vars --- src/controllers/challenges.js | 104 ++++++++++++++++------------------ 1 file changed, 49 insertions(+), 55 deletions(-) diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index b5fe4d6982..d09decf572 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -147,69 +147,63 @@ api.getMember = function(req, res, next) { // CREATE api.create = function(req, res, next){ var user = res.locals.user; - var group, chal; - // First, make sure they've selected a legit group, and store it for later - var waterfall = [ - function(cb){ - Group.findById(req.body.group).exec(cb); + async.auto({ + get_group: function(cb){ + var q = {_id:req.body.group}; + if (req.body.group!='habitrpg') q.members = {$in:[user._id]}; // make sure they're a member of the group + Group.findOne(q, cb); }, - function(_group, cb){ - if (!_group) return cb("Group." + req.body.group + " not found"); - group = _group; - cb(null); - } - ]; + save_chal: ['get_group', function(cb, results){ + var group = results.get_group, + prize = +req.body.prize; + if (!group) + return cb({code:404, err:"Group." + req.body.group + " not found"}); + //if (group.leaderOnly.challenges && group.leader !== user._id) + // return cb({code:401, err: "Only the group leader can create challenges"}); + // If they're adding a prize, do some validation + if (prize < 0) + return cb({code:401, err: 'Challenge prize must be >= 0'}); + if (req.body.group=='habitrpg' && prize < 1) + return cb({code:401, err: 'Prize must be at least 1 Gem for public challenges.'}); + if (prize > 0) { + var groupBalance = ((group.balance && group.leader==user._id) ? group.balance : 0); + var prizeCost = prize/4; // I really should have stored user.balance as gems rather than dollars... stupid... + if (prizeCost > user.balance + groupBalance) + return cb("You can't afford this prize. Purchase more gems or lower the prize amount.") - // If they're adding a prize, do some validation - if (+req.body.prize < 0) return res.json(401, {err: 'Challenge prize must be >= 0'}); - if (req.body.group=='habitrpg' && +req.body.prize < 1) return res.json(401, {err: 'Prize must be at least 1 Gem for public challenges.'}); - if (+req.body.prize > 0) { - waterfall.push(function(cb){ - var groupBalance = ((group.balance && group.leader==user._id) ? group.balance : 0); - var prizeCost = req.body.prize/4; // I really should have stored user.balance as gems rather than dollars... stupid... - if (prizeCost > user.balance + groupBalance) - return cb("You can't afford this prize. Purchase more gems or lower the prize amount.") - - if (groupBalance >= prizeCost) { - // Group pays for all of prize - group.balance -= prizeCost; - } else if (groupBalance > 0) { - // User pays remainder of prize cost after group - var remainder = prizeCost - group.balance; - group.balance = 0; - user.balance -= remainder; - } else { - // User pays for all of prize - user.balance -= prizeCost; - } - cb(null); - }); - } - - waterfall = waterfall.concat([ - function(cb) { // if we're dealing with prize above, arguemnts will be `group, numRows, cb` - else `cb` + if (groupBalance >= prizeCost) { + // Group pays for all of prize + group.balance -= prizeCost; + } else if (groupBalance > 0) { + // User pays remainder of prize cost after group + var remainder = prizeCost - group.balance; + group.balance = 0; + user.balance -= remainder; + } else { + // User pays for all of prize + user.balance -= prizeCost; + } + } req.body.leader = user._id; req.body.official = user.contributor.admin && req.body.official; var chal = new Challenge(req.body); // FIXME sanitize chal.members.push(user._id); - chal.save(cb) - }, - function(_chal, num, cb){ - chal = _chal; - group.challenges.push(chal._id); - group.save(cb); - }, - function(_group, num, cb) { + chal.save(cb); + }], + save_group: ['save_chal', function(cb, results){ + results.get_group.challenges.push(results.save_chal[0]._id); + results.get_group.save(cb); + }], + sync_user: ['save_group', function(cb, results){ // Auto-join creator to challenge (see members.push above) - chal.syncToUser(user, cb); - } - ]); - async.waterfall(waterfall, function(err){ - if (err) return next(err); - res.json(chal); - user = group = chal = null; - }); + results.save_chal[0].syncToUser(user, cb); + }] + }, function(err, results){ + if (err) return err.code? res.json(err.code, err) : next(err); + return res.json(results.save_chal[0]); + user = null; + }) } // UPDATE From d70de5972e1a6b3a6674ba105df50189b9783b1c Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Thu, 11 Dec 2014 22:28:07 -0800 Subject: [PATCH 051/202] added the image to the end screen and made adjustments to the line --- views/shared/modals/quests.jade | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/shared/modals/quests.jade b/views/shared/modals/quests.jade index ccd4d39850..21abc8c095 100644 --- a/views/shared/modals/quests.jade +++ b/views/shared/modals/quests.jade @@ -5,7 +5,8 @@ script(type='text/ng-template', id='modals/questCompleted.html') h4 "{{::Content.quests[user.party.quest.completed].text()}}" =env.t('completed') .modal-body - p(ng-bind-html='::Content.quests[user.party.quest.completed].completion()') + .pull-right(class='quest_{{user.party.quest.completed}}') + p(ng-bind-html='::Content.quests[user.party.quest.completed].completion()' style='min-height: 80px') quest-rewards(key='{{user.party.quest.completed}}', header=env.t('youReceived')) .modal-footer button.btn.btn-primary(ng-click='set({"party.quest.completed":""}); $close()')=env.t('ok') From 1e3a07b8b52fb6a20a5c431e18e3d55b45732881 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 21:42:41 -0600 Subject: [PATCH 052/202] Flag icon opens blank modal --- public/js/controllers/groupsCtrl.js | 5 +++++ views/options/social/chat-message.jade | 2 ++ views/shared/modals/members.jade | 7 ++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index a9f744839c..b823f0cb84 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,6 +105,11 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } + $scope.flagChat = function(uid) { + Members.selectMember(uid, function(){ + $rootScope.openModal('flag',{controller:'MemberModalCtrl'}); + }); + } } ]) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index dc332452ed..bcd0016188 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,6 +26,8 @@ mixin chatMessages(inbox) |         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="flagChat(message.uuid)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + span.glyphicon.glyphicon-flag(tooltip=env.t('flag')) span.float-label 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') diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 595929c819..f2ab1607e7 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -88,4 +88,9 @@ 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') \ No newline at end of file + button.btn.btn-default(ng-click='$close()')=env.t('cancel') + +script(type='text/ng-template', id='modals/flag.html') + .modal-header + .modal-body + .modal-footer From b3a56b6ded4ef9f6d88177f3169aa77a957f63a8 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 21:57:32 -0600 Subject: [PATCH 053/202] Added heading and body of flag modal --- views/shared/modals/members.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index f2ab1607e7..afcdd250cb 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -92,5 +92,7 @@ script(type='text/ng-template', id='modals/send-gift.html') script(type='text/ng-template', id='modals/flag.html') .modal-header + h4=env.t('flagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body + h4!=env.t('flagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer From ee9214396b9d6db9dfa4b25021948f7cd525268a Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 22:00:48 -0600 Subject: [PATCH 054/202] Renamed everything from flag to abuseFlag --- public/js/controllers/groupsCtrl.js | 2 +- views/shared/modals/members.jade | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index b823f0cb84..ec1897451e 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -107,7 +107,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } $scope.flagChat = function(uid) { Members.selectMember(uid, function(){ - $rootScope.openModal('flag',{controller:'MemberModalCtrl'}); + $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl'}); }); } } diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index afcdd250cb..c08064cbf4 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -90,9 +90,9 @@ script(type='text/ng-template', id='modals/send-gift.html') 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') -script(type='text/ng-template', id='modals/flag.html') +script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header - h4=env.t('flagModalHeading', {name: "{{profile.profile.name}}"}) + h4=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body - h4!=env.t('flagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) + h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer From f50f40b77dc13527b6db06261b9d33c77cc285a5 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 22:02:34 -0600 Subject: [PATCH 055/202] Added text-danger span on username --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index c08064cbf4..0b72234771 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -92,7 +92,7 @@ script(type='text/ng-template', id='modals/send-gift.html') script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header - h4=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) + h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer From 658046024e9ed00f86d4ad01ac3252f2dbb2d301 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 4 Dec 2014 22:03:35 -0600 Subject: [PATCH 056/202] Corrected tooltip name --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index bcd0016188..4315cb0566 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -27,7 +27,7 @@ mixin chatMessages(inbox) 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="flagChat(message.uuid)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') - span.glyphicon.glyphicon-flag(tooltip=env.t('flag')) + span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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') From 9fd4588b7429ff66307235a6ba53ad0d55238938 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:41:40 -0600 Subject: [PATCH 057/202] Added buttons to footer, cleaned up functions --- public/js/controllers/groupsCtrl.js | 14 +++++++++++--- views/options/social/chat-message.jade | 2 +- views/shared/modals/members.jade | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index ec1897451e..63933e4fe7 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,9 +105,12 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - $scope.flagChat = function(uid) { - Members.selectMember(uid, function(){ - $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl'}); + $scope.flagChat = function(message) { + $scope.abuseObject = message; + Members.selectMember(message.uuid, function(){ + $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', + scope: $scope + }); }); } } @@ -144,6 +147,11 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$close(); }) } + $scope.reportAbuse = function(reporter, message) { + Notification.text(window.env.t('abuseReported')); + $rootScope.User.sync(); + $scope.$close(); + } } ]) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 4315cb0566..161293e87e 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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="flagChat(message.uuid)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + a(ng-click="flagChat(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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)') diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 0b72234771..6c2958ec63 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -96,3 +96,5 @@ script(type='text/ng-template', id='modals/abuse-flag.html') .modal-body h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer + button.btn.btn-primary(ng-click='$close()')=env.t('cancel') + button.btn.btn-default(ng-click='reportAbuse(user, abuseObject)')=env.t("abuseFlagModalButton") From e2c0a41f8845d9f62eb8d047a276db0f3b818604 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:47:04 -0600 Subject: [PATCH 058/202] Added a flags key to message object --- src/models/group.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/models/group.js b/src/models/group.js index 1bc275afe4..649d8d571b 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -98,7 +98,8 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ id: shared.uuid(), text: msg, timestamp: +new Date, - likes: {} + likes: {}, + flags: {} }; if (user) { _.defaults(message, { @@ -330,4 +331,4 @@ Group.count({_id:'habitrpg'},function(err,ct){ type: 'guild', privacy:'public' }).save(); -}) \ No newline at end of file +}) From d5d83d67e93e3fb6528de624a9a47987417a2c32 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:50:41 -0600 Subject: [PATCH 059/202] Renamed flagChat to flagChatMessage --- public/js/controllers/groupsCtrl.js | 2 +- views/options/social/chat-message.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 63933e4fe7..523b9abff7 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,7 +105,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - $scope.flagChat = function(message) { + $scope.flagChatMessage = function(message) { $scope.abuseObject = message; Members.selectMember(message.uuid, function(){ $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 161293e87e..a143f13b44 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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="flagChat(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + a(ng-click="flagChatMessage(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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)') From 50e823600814dcd9553e62bcfba32d3c1ac92eb2 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 06:52:49 -0600 Subject: [PATCH 060/202] Removed flag from system notifications --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index a143f13b44..3dbd70ef6a 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id"}') + a(ng-click="flagChatMessage(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) span.float-label 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)') From 3c62e0b8629f17705b41db87ed4b14067785b4a9 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:03:33 -0600 Subject: [PATCH 061/202] Added some logic for adding flags --- public/js/controllers/groupsCtrl.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 523b9abff7..637361bda4 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -148,6 +148,10 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }) } $scope.reportAbuse = function(reporter, message) { + if(!message.flags) message.flags = {}; + message.flags[User.user._id] = true; + // Post to API should look something like this + //$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/flag'); Notification.text(window.env.t('abuseReported')); $rootScope.User.sync(); $scope.$close(); From a22c3c59683163003a19b175b560712f2e0993a3 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:42:52 -0600 Subject: [PATCH 062/202] Error handling for if message already flagged --- public/js/controllers/groupsCtrl.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 637361bda4..7d01db4554 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -106,6 +106,9 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }); } $scope.flagChatMessage = function(message) { + if(!message.flags) message.flags = {}; + if(message.flags[User.user._id]) + Notification.text(window.env.t('abuseAlreadyReported')); $scope.abuseObject = message; Members.selectMember(message.uuid, function(){ $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', @@ -148,8 +151,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }) } $scope.reportAbuse = function(reporter, message) { - if(!message.flags) message.flags = {}; - message.flags[User.user._id] = true; + message.flags[reporter._id] = true; // Post to API should look something like this //$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/flag'); Notification.text(window.env.t('abuseReported')); From 6d656d3713946b50057b5c33550fea80232b34b7 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:43:20 -0600 Subject: [PATCH 063/202] Give flag text danger class and already reported message if already reproted --- public/css/index.styl | 3 +++ views/options/social/chat-message.jade | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/css/index.styl b/public/css/index.styl index 07e2829d0b..381ab03082 100644 --- a/public/css/index.styl +++ b/public/css/index.styl @@ -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 diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 3dbd70ef6a..ab0dc37e0d 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -27,7 +27,7 @@ mixin chatMessages(inbox) 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(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') - span.glyphicon.glyphicon-flag(tooltip=env.t('abuseFlag')) + 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 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') From 057a8b1a06828c58c0734c2ff208bf4d2b1b3335 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 07:43:38 -0600 Subject: [PATCH 064/202] Prevent modal from popping up if already reported --- public/js/controllers/groupsCtrl.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 7d01db4554..809b7f1687 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -109,12 +109,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' if(!message.flags) message.flags = {}; if(message.flags[User.user._id]) Notification.text(window.env.t('abuseAlreadyReported')); - $scope.abuseObject = message; - Members.selectMember(message.uuid, function(){ - $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', - scope: $scope + else { + $scope.abuseObject = message; + Members.selectMember(message.uuid, function(){ + $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', + scope: $scope + }); }); - }); + } } } ]) From dd68e6a29ddbf9d78b7c8639f182be17df0042dd Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:09:56 -0600 Subject: [PATCH 065/202] Converted heading to paragraph --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 6c2958ec63..a2cc76b95a 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -94,7 +94,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body - h4!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) + p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') button.btn.btn-default(ng-click='reportAbuse(user, abuseObject)')=env.t("abuseFlagModalButton") From bbf94aac647fe972ce68668b3ebcf21e7ed9db83 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:14:48 -0600 Subject: [PATCH 066/202] Added text of message to make sure user clicked the right flag --- views/shared/modals/members.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index a2cc76b95a..3513b8b433 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -94,6 +94,8 @@ script(type='text/ng-template', id='modals/abuse-flag.html') .modal-header h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body + blockquote + | {{abuseObject.text}} p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') From 1a59815cc8ba1b1a2a01c98f2cffaca66c9a5d80 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:15:03 -0600 Subject: [PATCH 067/202] Added todo for paglias --- public/js/controllers/groupsCtrl.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 809b7f1687..a8f21adbbd 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -154,8 +154,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } $scope.reportAbuse = function(reporter, message) { message.flags[reporter._id] = true; - // Post to API should look something like this - //$http.post(ApiUrlService.get() + '/api/v2/groups/' + group._id + '/chat/' + message.id + '/flag'); + // @TODO @paglias API Call goes here Notification.text(window.env.t('abuseReported')); $rootScope.User.sync(); $scope.$close(); From 6749109423717c9a246d0b5334fb359b5d9db8b0 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 5 Dec 2014 22:20:14 -0600 Subject: [PATCH 068/202] Added note for paglias --- src/models/group.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/group.js b/src/models/group.js index 649d8d571b..af5e22dab2 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -99,7 +99,7 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ text: msg, timestamp: +new Date, likes: {}, - flags: {} + flags: {} // for @paglias, @crookedneighbor added this. }; if (user) { _.defaults(message, { From e84880d3e872e85af3e29e0a0381a6a4ad8b4551 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sun, 7 Dec 2014 14:35:09 +0100 Subject: [PATCH 069/202] feat(flag-message): add server methods --- public/js/controllers/groupsCtrl.js | 45 ++++++++++++++------------ public/js/services/groupServices.js | 1 + src/controllers/groups.js | 16 +++++++++ src/models/group.js | 14 +++++++- src/routes/apiv2.coffee | 11 +++++++ views/options/social/chat-message.jade | 2 +- views/shared/modals/members.jade | 2 +- 7 files changed, 67 insertions(+), 24 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index a8f21adbbd..dfd5718247 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -105,24 +105,11 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $rootScope.openModal('private-message',{controller:'MemberModalCtrl'}); }); } - $scope.flagChatMessage = function(message) { - if(!message.flags) message.flags = {}; - if(message.flags[User.user._id]) - Notification.text(window.env.t('abuseAlreadyReported')); - else { - $scope.abuseObject = message; - Members.selectMember(message.uuid, function(){ - $rootScope.openModal('abuse-flag',{controller:'MemberModalCtrl', - scope: $scope - }); - }); - } - } } ]) - .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'); } @@ -152,12 +139,12 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$close(); }) } - $scope.reportAbuse = function(reporter, message) { + $scope.reportAbuse = function(reporter, message, groupId) { message.flags[reporter._id] = true; - // @TODO @paglias API Call goes here - Notification.text(window.env.t('abuseReported')); - $rootScope.User.sync(); - $scope.$close(); + Groups.Group.flagChatMessage({gid: groupId, messageId: message.id}, undefined, function(data){ + Notification.text(window.env.t('abuseReported')); + $scope.$close(); + }); } } ]) @@ -217,7 +204,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; @@ -282,6 +269,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(); } diff --git a/public/js/services/groupServices.js b/public/js/services/groupServices.js index 42dd787846..45ecc8b971 100644 --- a/public/js/services/groupServices.js +++ b/public/js/services/groupServices.js @@ -13,6 +13,7 @@ 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'}, 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'}, diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 50272cdab2..0be99d8fae 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -256,6 +256,22 @@ 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."}); + + message.flags[user._id] = true; + group.markModified('chat'); + group.save(function(err,_saved){ + if(err) return next(err); + return res.send(204); + }); +} + 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 diff --git a/src/models/group.js b/src/models/group.js index af5e22dab2..3e00370b67 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -99,7 +99,7 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ text: msg, timestamp: +new Date, likes: {}, - flags: {} // for @paglias, @crookedneighbor added this. + flags: {} }; if (user) { _.defaults(message, { @@ -317,6 +317,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); diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 415577d565..5708bfac51 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -577,6 +577,17 @@ 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 + # --------------------------------- # Members # --------------------------------- diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index ab0dc37e0d..bbc4fed1db 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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(message)", ng-if='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') + a(ng-click="flagChatMessage(group._id, message)", ng-if='#{inbox ? "true" : ":: 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 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)') diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 3513b8b433..3dc12dfe1a 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -99,4 +99,4 @@ script(type='text/ng-template', id='modals/abuse-flag.html') p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') - button.btn.btn-default(ng-click='reportAbuse(user, abuseObject)')=env.t("abuseFlagModalButton") + button.btn.btn-default(ng-click='reportAbuse(user, abuseObject, groupId)')=env.t("abuseFlagModalButton") From 739106f13e59a0b260be5c94a9f1400b374d2adc Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 10:23:29 -0600 Subject: [PATCH 070/202] Updated message preview on flag abuse modal to use markdown interpretation --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 3dc12dfe1a..d457213d34 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -95,7 +95,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') h4!=env.t('abuseFlagModalHeading', {name: "{{profile.profile.name}}"}) .modal-body blockquote - | {{abuseObject.text}} + markdown(ng-model="abuseObject.text") p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer button.btn.btn-primary(ng-click='$close()')=env.t('cancel') From b6ae2302e8d50f75d102bda0e9a4f76ccd7d3ed2 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 10:29:12 -0600 Subject: [PATCH 071/202] User can only flag message after accepting community guidelines --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index bbc4fed1db..a5b18f2b79 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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='#{inbox ? "true" : ":: message.uuid != user.id && message.uuid != \'system\'"}') + a(ng-click="flagChatMessage(group._id, message)", ng-if='#{inbox ? "true" : ":: 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 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)') From 3771d38d4867e0e1ba0ff8c67fb954172779f432 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 12:16:57 -0600 Subject: [PATCH 072/202] Added fallback for is message does not contain flags key. Added error for if message was already flagged by user --- src/controllers/groups.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 0be99d8fae..93a39a26f3 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -264,6 +264,9 @@ api.flagChatMessage = function(req, res, next){ 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."}); + if(!message.flags) message.flags = {}; + if(message.flags[user._id]) return res.json(401, {err: "You have already reported this message"}); + message.flags[user._id] = true; group.markModified('chat'); group.save(function(err,_saved){ From 27ebe1a0fe249bd5406a7196a25d6313a662a4f1 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 12:45:25 -0600 Subject: [PATCH 073/202] Converted sent transparency to ng-class instead of style --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index a5b18f2b79..c5fbe9e2e6 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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" : ""}}') + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent}') .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From 45b91b1251c8fa2fbd3acde894aea71a91ad7156 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:00:25 -0600 Subject: [PATCH 074/202] Added flagCount variable --- src/controllers/groups.js | 7 ++++++- src/models/group.js | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 93a39a26f3..289e950bdd 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -264,10 +264,15 @@ api.flagChatMessage = function(req, res, next){ 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."}); + // Log user ids that have flagged the message if(!message.flags) message.flags = {}; if(message.flags[user._id]) 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; + message.flagCount++ + group.markModified('chat'); group.save(function(err,_saved){ if(err) return next(err); diff --git a/src/models/group.js b/src/models/group.js index 3e00370b67..be66b59fa7 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -99,7 +99,8 @@ var chatDefaults = module.exports.chatDefaults = function(msg,user){ text: msg, timestamp: +new Date, likes: {}, - flags: {} + flags: {}, + flagCount: 0 }; if (user) { _.defaults(message, { From b056faeb34704ecd36d642211a608eb38db34e68 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:01:50 -0600 Subject: [PATCH 075/202] Hide message if flag count is 2 or higher --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index c5fbe9e2e6..b559793012 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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, "transparent": message.sent}') + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2") .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From d3d5430bf25001a3026c5bdca2767023baab833f Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:05:48 -0600 Subject: [PATCH 076/202] Always show message if admin --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index b559793012..5490cc6793 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2") + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From dfd982cb045548c3c4d092b12c3a1beedad8929a Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:09:00 -0600 Subject: [PATCH 077/202] Allow admin to see messages that have been flagged more than twice, but make them semi-transparent --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 5490cc6793..fa2ab1743d 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,7 +1,7 @@ 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, "transparent": message.sent || message.flags[user._id]}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") + li.chat-message(ng-repeat=ngRepeat, ng-class=':: {highlight: isUserMentioned(user,message) || message.uuid=="system", "own-message": user._id == message.uuid, "transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From d6262efd3f9d4423338efd6b689b5570f4574ed1 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:12:50 -0600 Subject: [PATCH 078/202] Added note about how many flags a message has --- views/options/social/chat-message.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index fa2ab1743d..3aa4aa3b7d 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -2,6 +2,8 @@ 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, "transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}', 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}} flag{{message.flagCount != 1 ? 's' : ''}} .scrollable-message span(ng-if='::message.user') a.label.label-default.chat-message.hidden-label From 6bb51c36e2b228c18fbbebb47f9540d43443a8d7 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:34:43 -0600 Subject: [PATCH 079/202] Admin flagging counts for more, automatically hides message --- src/controllers/groups.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 289e950bdd..1f40fb7684 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -271,7 +271,12 @@ api.flagChatMessage = function(req, res, next){ // Log total number of flags (publicly viewable) if(!message.flagCount) message.flagCount = 0; - message.flagCount++ + if(user.contributor.admin){ + // Arbitraty amount, higher than 2 + message.flagCount = 5; + } else { + message.flagCount++ + } group.markModified('chat'); group.save(function(err,_saved){ From eb36a53f6e2144db50a414afa843981772fe55ad Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 13:38:57 -0600 Subject: [PATCH 080/202] Moved ng-class so it would update transparent class in real time after flaggging --- views/options/social/chat-message.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 3aa4aa3b7d..c5ffe81e1b 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,10 +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, "transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}', ng-if="!message.flagCount || message.flagCount < 2 || user.contributor.admin") + 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}} flag{{message.flagCount != 1 ? 's' : ''}} - .scrollable-message + .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') From b1600198be2ad999b182537f3b01341fafc98251 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 17:29:01 -0600 Subject: [PATCH 081/202] Added method for letting an admin unhide a flagged message --- public/js/controllers/groupsCtrl.js | 8 ++++++++ public/js/services/groupServices.js | 1 + src/controllers/groups.js | 17 +++++++++++++++++ src/routes/apiv2.coffee | 11 +++++++++++ views/shared/modals/members.jade | 2 ++ 5 files changed, 39 insertions(+) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index dfd5718247..f99a79ec76 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -146,6 +146,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$close(); }); } + $scope.clearFlagCount = function(message, groupId) { + console.log("Heeey"); + Groups.Group.clearFlagCount({gid: groupId, messageId: message.id}, undefined, function(data){ + message.flagCount = 0; + Notification.text("Flags cleared"); + $scope.$close(); + }); + } } ]) diff --git a/public/js/services/groupServices.js b/public/js/services/groupServices.js index 45ecc8b971..e07f21b15c 100644 --- a/public/js/services/groupServices.js +++ b/public/js/services/groupServices.js @@ -14,6 +14,7 @@ angular.module('groupServices', ['ngResource']). 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'}, diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 1f40fb7684..dbeaefbd0d 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -285,6 +285,23 @@ api.flagChatMessage = function(req, res, next){ }); } +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); + }); +} + 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 diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 5708bfac51..0f8d272c69 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -588,6 +588,17 @@ module.exports = (swagger, v2) -> 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 # --------------------------------- diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index d457213d34..27d42d4623 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -98,5 +98,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') markdown(ng-model="abuseObject.text") p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer + button.pull-left.btn.btn-primary(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") From 5f49031c5e70cb1a990895049aa623843d26e5e7 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 17:41:54 -0600 Subject: [PATCH 082/202] Adjusted spacing of chat buttons --- views/options/social/chat-message.jade | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index c5ffe81e1b..b9be7a2349 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -25,9 +25,10 @@ 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='#{inbox ? "true" : ":: 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 From c737729470ac55debde583c20d8a6406befba14b Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:40:25 -0600 Subject: [PATCH 083/202] Changed transparent class for admin to only appear if it is hidden --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index b9be7a2349..c7a7dc6619 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -4,7 +4,7 @@ mixin chatMessages(inbox) 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}} flag{{message.flagCount != 1 ? 's' : ''}} - .scrollable-message(ng-class='{"transparent": message.sent || message.flags[user._id] || (user.contributor.admin && message.flagCount >= 1)}') + .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') From 595ac1c03e49256cd0c95d14386ae16b992b6f2f Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:42:39 -0600 Subject: [PATCH 084/202] Made it impossible to flag own message in inbox --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index c7a7dc6619..e763a202ee 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -29,7 +29,7 @@ mixin chatMessages(inbox) 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='#{inbox ? "true" : ":: user.flags.communityGuidelinesAccepted && message.uuid != user.id && message.uuid != \'system\'"}') + a(ng-click="flagChatMessage(group._id, message)", ng-if=':: !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 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)') From 7d38eb3b591e90babff9f094eedd0808ddba2256 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:43:01 -0600 Subject: [PATCH 085/202] Allow admin to reflag a message they've flagged and unflagged previously --- src/controllers/groups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index dbeaefbd0d..caa36809cc 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -266,7 +266,7 @@ api.flagChatMessage = function(req, res, next){ // Log user ids that have flagged the message if(!message.flags) message.flags = {}; - if(message.flags[user._id]) return res.json(401, {err: "You have already reported this message"}); + 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) From 35675e3f28032424b4d4ff2009809c9c9524765d Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:44:58 -0600 Subject: [PATCH 086/202] Admin can see flag on own message --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index e763a202ee..4945b44628 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -29,7 +29,7 @@ mixin chatMessages(inbox) 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=':: !message.sent && user.flags.communityGuidelinesAccepted && message.uuid != user.id && message.uuid != "system"') + 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 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)') From 5b17b8b38222a73c57382586fb7f40127d449cce Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:47:06 -0600 Subject: [PATCH 087/202] Made flag number more helpful --- views/options/social/chat-message.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 4945b44628..afe4d2e1b3 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -3,7 +3,7 @@ mixin chatMessages(inbox) - 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}', 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}} flag{{message.flagCount != 1 ? 's' : ''}} + | {{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 From ac34332582eb2cf113ced78b2f804bbf9ff4af83 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 11 Dec 2014 21:49:42 -0600 Subject: [PATCH 088/202] Made reset flag count button red --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 27d42d4623..0255ab8e6d 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -98,7 +98,7 @@ script(type='text/ng-template', id='modals/abuse-flag.html') markdown(ng-model="abuseObject.text") p!=env.t('abuseFlagModalBody', {firstLinkStart: "", secondLinkStart: "", linkEnd: ""}) .modal-footer - button.pull-left.btn.btn-primary(ng-click='clearFlagCount(abuseObject, groupId)', ng-if='user.contributor.admin && abuseObject.flagCount >= 2') + 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") From 948d5762cd2c73179a77835bd22bb7fae76458d6 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 12 Dec 2014 11:39:22 +0100 Subject: [PATCH 089/202] fix(flag-message): small refactor --- src/controllers/groups.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index caa36809cc..06f87e82a5 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -292,14 +292,18 @@ api.clearFlagCount = function(req, res, next){ if(!message) return res.json(404, {err: "Message not found!"}); - if(user.contributor.admin) + 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!"}) + } - group.markModified('chat'); - group.save(function(err,_saved){ - if(err) return next(err); - return res.send(204); - }); } api.seenMessage = function(req,res,next){ From 7356e0bafeb3843d6c457c654abc7a68f999cf44 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 12 Dec 2014 12:11:26 +0100 Subject: [PATCH 090/202] feat(loggly): add loggly --- package.json | 1 + src/logging.js | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/package.json b/package.json index 061d4a227e..6f14a60e51 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "jade": "~1.7.0", "js2xmlparser": "~0.1.2", "lodash": "~2.4.1", + "loggly": "~1.0.8", "method-override": "~2.2.0", "moment": "~2.8.3", "mongoose": "~3.8.17", diff --git a/src/logging.js b/src/logging.js index 5e1449db3d..07e795e137 100644 --- a/src/logging.js +++ b/src/logging.js @@ -3,6 +3,19 @@ var winston = require('winston'); require('winston-mail').Mail; require('winston-newrelic'); +var loggly = require('loggly').createClient({ + token: nconf.get('LOGGLY:token'), + subdomain: nconf.get('LOGGLY:subdomain'), + auth: { + username: nconf.get('LOGGLY:username'), + password: nconf.get('LOGGLY:password') + }, + // + // Optional: Tag to send with EVERY log message + // + tags: ['heroku'] +}); + var logger; if (logger == null) { @@ -49,3 +62,8 @@ module.exports.error = function(/* variable args */) { if (logger) logger.error.apply(logger, arguments); }; + +module.exports.loggly = function(/* variable args */){ + if(loggly) + loggly.log(loggly, arguments); +}; From af1ff6d74059e12683603ca89a55e5a12ff1a9e4 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 12 Dec 2014 12:17:48 +0100 Subject: [PATCH 091/202] feat(loggly): add test log, use only in production and add sample config --- config.json.example | 8 +++++++- src/logging.js | 26 ++++++++++++++------------ src/server.js | 2 ++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/config.json.example b/config.json.example index caedcf3ae9..f50242f74c 100644 --- a/config.json.example +++ b/config.json.example @@ -42,5 +42,11 @@ "client_id":"client_id", "client_secret":"client_secret" }, - "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/" + "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/", + "LOGGLY": { + "subdomain": "subdomain", + "token": "token", + "username": "username", + "password": "password" + } } diff --git a/src/logging.js b/src/logging.js index 07e795e137..9cfe8cfbd8 100644 --- a/src/logging.js +++ b/src/logging.js @@ -3,18 +3,20 @@ var winston = require('winston'); require('winston-mail').Mail; require('winston-newrelic'); -var loggly = require('loggly').createClient({ - token: nconf.get('LOGGLY:token'), - subdomain: nconf.get('LOGGLY:subdomain'), - auth: { - username: nconf.get('LOGGLY:username'), - password: nconf.get('LOGGLY:password') - }, - // - // Optional: Tag to send with EVERY log message - // - tags: ['heroku'] -}); +if (nconf.get('NODE_ENV') == 'production'){ + var loggly = require('loggly').createClient({ + token: nconf.get('LOGGLY:token'), + subdomain: nconf.get('LOGGLY:subdomain'), + auth: { + username: nconf.get('LOGGLY:username'), + password: nconf.get('LOGGLY:password') + }, + // + // Optional: Tag to send with EVERY log message + // + tags: ['heroku'] + }); +} var logger; diff --git a/src/server.js b/src/server.js index 8e551f7891..bd0e1310b0 100644 --- a/src/server.js +++ b/src/server.js @@ -49,6 +49,8 @@ if (cluster.isMaster && (isDev || isProd)) { }); autoinc.init(db); + logging.loggly('test from beta'); + // load schemas & models require('./models/challenge'); require('./models/group'); From b10d4606f5bf728b02e65c2ba892669d0144bf93 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 12 Dec 2014 13:09:21 +0100 Subject: [PATCH 092/202] fix(loggly): typo and remove testing --- src/logging.js | 2 +- src/server.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/logging.js b/src/logging.js index 9cfe8cfbd8..476a76d41b 100644 --- a/src/logging.js +++ b/src/logging.js @@ -67,5 +67,5 @@ module.exports.error = function(/* variable args */) { module.exports.loggly = function(/* variable args */){ if(loggly) - loggly.log(loggly, arguments); + loggly.log.apply(loggly, arguments); }; diff --git a/src/server.js b/src/server.js index bd0e1310b0..8e551f7891 100644 --- a/src/server.js +++ b/src/server.js @@ -49,8 +49,6 @@ if (cluster.isMaster && (isDev || isProd)) { }); autoinc.init(db); - logging.loggly('test from beta'); - // load schemas & models require('./models/challenge'); require('./models/group'); From 375253fb847355c68c190d6dabddd7ceb4b17ce2 Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Fri, 12 Dec 2014 09:38:13 -0800 Subject: [PATCH 093/202] changing the reward text to clear the image --- views/shared/modals/quest-rewards.jade | 2 +- views/shared/modals/quests.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/views/shared/modals/quest-rewards.jade b/views/shared/modals/quest-rewards.jade index ffa90622ab..acddcbf53f 100644 --- a/views/shared/modals/quest-rewards.jade +++ b/views/shared/modals/quest-rewards.jade @@ -1,5 +1,5 @@ script(id='partials/options.social.party.quest-rewards.html', type='text/ng-template') - hr + hr(style='clear:both') h5 {{header}} table.table.table-striped tr(ng-repeat='drop in quest.drop.items') diff --git a/views/shared/modals/quests.jade b/views/shared/modals/quests.jade index 21abc8c095..6ff8b9cd6a 100644 --- a/views/shared/modals/quests.jade +++ b/views/shared/modals/quests.jade @@ -6,7 +6,7 @@ script(type='text/ng-template', id='modals/questCompleted.html') =env.t('completed') .modal-body .pull-right(class='quest_{{user.party.quest.completed}}') - p(ng-bind-html='::Content.quests[user.party.quest.completed].completion()' style='min-height: 80px') + p(ng-bind-html='::Content.quests[user.party.quest.completed].completion()') quest-rewards(key='{{user.party.quest.completed}}', header=env.t('youReceived')) .modal-footer button.btn.btn-primary(ng-click='set({"party.quest.completed":""}); $close()')=env.t('ok') From 0943a229ec5cbb976e204ec7ec4641b138aea39b Mon Sep 17 00:00:00 2001 From: benmanley Date: Fri, 12 Dec 2014 17:38:17 +0000 Subject: [PATCH 094/202] fix(avatar.styl): improve label layout Fixes #4349 * Tweak position of labels to align better with background border. * Increase z-index of share button to prevent it being obscured by avatar. * Move share button to bottom left corner to prevent it obscuring avatar name. --- public/css/avatar.styl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/public/css/avatar.styl b/public/css/avatar.styl index f5132b070f..ea00c40e02 100644 --- a/public/css/avatar.styl +++ b/public/css/avatar.styl @@ -31,16 +31,17 @@ future re: pets and whatnot, this is just temporary. position: absolute @extend $hrpg-label .avatar-level - bottom: 2px - right: 2px + bottom: 4px + right: 4px // when a buff/rebirth exists, add space between the icon and the level .glyphicon-circle-arrow-up padding-right: 4px .glyphicon-plus-sign padding-left: 4px .avatar-name - top: 2px - left: 2px + max-width: 132px + top: 4px + left: 4px opacity: 0 // Hide username by default transition: opacity 0.2s ease-out @@ -83,8 +84,9 @@ future re: pets and whatnot, this is just temporary. width: 50px !important .addthis_native_toolbox position: absolute - top:2px - right:2px + bottom:4px + left:4px + z-index: 2 opacity:0 &:hover .addthis_native_toolbox opacity:1 From 45d30ffef56cdec74849448195e9cca02af58d7d Mon Sep 17 00:00:00 2001 From: benmanley Date: Fri, 12 Dec 2014 17:41:26 +0000 Subject: [PATCH 095/202] fix(footerCtrl.js): disable addThis hover Activate addThis dropdown on click/tap not on hover. --- public/js/controllers/footerCtrl.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/js/controllers/footerCtrl.js b/public/js/controllers/footerCtrl.js index 9017602aec..4a42b10952 100644 --- a/public/js/controllers/footerCtrl.js +++ b/public/js/controllers/footerCtrl.js @@ -42,6 +42,7 @@ //$.getScript("//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5016f6cc44ad68a4"); //FIXME why isn't this working when here? instead it's now in var addthisServices = 'facebook,twitter,googleplus,tumblr,'+window.env.BASE_URL.replace('https://','').replace('http://',''); window.addthis_config = { + ui_click: true, services_custom:{ name: "Download", url: window.env.BASE_URL+"/export/avatar-"+User.user._id+".png", From 2db80c684156b1550ee6078ccf07cbb7ffce47c4 Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Fri, 12 Dec 2014 10:26:55 -0800 Subject: [PATCH 096/202] changed to style sheet --- public/css/quests.styl | 6 +++++- views/shared/modals/quest-rewards.jade | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/public/css/quests.styl b/public/css/quests.styl index fe7209f4e9..d885e32372 100644 --- a/public/css/quests.styl +++ b/public/css/quests.styl @@ -1,3 +1,7 @@ .quest_collected_true color: #ccc - text-decoration:line-through \ No newline at end of file + text-decoration:line-through + +quest-rewards + hr + clear:both \ No newline at end of file diff --git a/views/shared/modals/quest-rewards.jade b/views/shared/modals/quest-rewards.jade index acddcbf53f..ffa90622ab 100644 --- a/views/shared/modals/quest-rewards.jade +++ b/views/shared/modals/quest-rewards.jade @@ -1,5 +1,5 @@ script(id='partials/options.social.party.quest-rewards.html', type='text/ng-template') - hr(style='clear:both') + hr h5 {{header}} table.table.table-striped tr(ng-repeat='drop in quest.drop.items') From e1ea1a24c80674ab0aea0c60b65383db38c0c02e Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 12 Dec 2014 11:35:51 -0700 Subject: [PATCH 097/202] feat(challenges): optional checkbox on group create/edit: restrict challenge-creation to leader. Commented-out code for similar setup for member invites, shouldn't be hard --- src/controllers/challenges.js | 4 ++-- src/controllers/groups.js | 2 +- src/models/group.js | 5 ++++- views/options/social/create-group.jade | 5 +++++ views/options/social/group.jade | 5 +++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index d09decf572..71e1d82d33 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -159,8 +159,8 @@ api.create = function(req, res, next){ prize = +req.body.prize; if (!group) return cb({code:404, err:"Group." + req.body.group + " not found"}); - //if (group.leaderOnly.challenges && group.leader !== user._id) - // return cb({code:401, err: "Only the group leader can create challenges"}); + if (group.leaderOnly && group.leaderOnly.challenges && group.leader !== user._id) + return cb({code:401, err: "Only the group leader can create challenges"}); // If they're adding a prize, do some validation if (prize < 0) return cb({code:401, err: 'Challenge prize must be >= 0'}); diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 50272cdab2..1d365d7d8b 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -187,7 +187,7 @@ api.update = function(req, res, next) { if(group.leader !== user._id) return res.json(401, {err: "Only the group leader can update the group!"}); - 'name description logo logo leaderMessage leader'.split(' ').forEach(function(attr){ + 'name description logo logo leaderMessage leader leaderOnly'.split(' ').forEach(function(attr){ group[attr] = req.body[attr]; }); diff --git a/src/models/group.js b/src/models/group.js index 1bc275afe4..85653670dc 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -26,7 +26,10 @@ var GroupSchema = new Schema({ # id: String # }] */ - + leaderOnly: { // restrict group actions to leader (members can't do them) + challenges: {type:Boolean, 'default':false}, + //invites: {type:Boolean, 'default':false} + }, memberCount: {type: Number, 'default': 0}, challengeCount: {type: Number, 'default': 0}, balance: Number, diff --git a/views/options/social/create-group.jade b/views/options/social/create-group.jade index b2211862c8..6c0c4c20e8 100644 --- a/views/options/social/create-group.jade +++ b/views/options/social/create-group.jade @@ -19,5 +19,10 @@ form.col-md-12.form-horizontal(ng-submit='create(newGroup)') span.gem-cost= '4 ' + env.t('gems') p small=env.t('gemCost') + .form-group + .checkbox + label + input(type='checkbox', ng-model='newGroup.leaderOnly.challenges') + | Only group leader can create challenges .form-group(ng-show='type=="party"') input.btn.btn-default.form-control(type='submit', value=env.t('create')) diff --git a/views/options/social/group.jade b/views/options/social/group.jade index 1b1d7a6f84..a3c5e257d8 100644 --- a/views/options/social/group.jade +++ b/views/options/social/group.jade @@ -33,6 +33,11 @@ a.pull-right.gem-wallet(ng-if='group.type!="party"', popover-trigger='mouseenter .form-group label=env.t('logoUrl') input.form-control(type='url', placeholder=env.t('logoUrl'), ng-model='group.logo') + .form-group + .checkbox + label + input(type='checkbox', ng-model='group.leaderOnly.challenges') + | Only group leader can create challenges h4=env.t('assignLeader') select#group-leader-selection(ng-model='group._newLeader', ng-options='member.profile.name for member in group.members') From 670542f3ce002d451a57605bf28d38c1b9d089c4 Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Fri, 12 Dec 2014 10:44:06 -0800 Subject: [PATCH 098/202] cleaning up white space --- public/css/quests.styl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/css/quests.styl b/public/css/quests.styl index d885e32372..c74377dc71 100644 --- a/public/css/quests.styl +++ b/public/css/quests.styl @@ -3,5 +3,5 @@ text-decoration:line-through quest-rewards - hr - clear:both \ No newline at end of file + hr + clear:both \ No newline at end of file From 7c6d8d4dc23a552653f8e9f5a84effcfc2c8b4f5 Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Fri, 12 Dec 2014 11:02:55 -0800 Subject: [PATCH 099/202] Update quests.styl further white space cleanup --- public/css/quests.styl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/quests.styl b/public/css/quests.styl index c74377dc71..3f5fa8a79b 100644 --- a/public/css/quests.styl +++ b/public/css/quests.styl @@ -4,4 +4,4 @@ quest-rewards hr - clear:both \ No newline at end of file + clear:both From 8935538b5351d8da9b0aee454b1444b63480932c Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 12 Dec 2014 20:05:37 +0100 Subject: [PATCH 100/202] feat(loggly): enable json loggin and start loggin errors during cron --- src/controllers/user.js | 65 +++++++++++++++++++++++------------------ src/logging.js | 3 +- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index bceb3eb677..68cc7b9ccb 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -257,37 +257,44 @@ api.update = function(req, res, next) { }; api.cron = function(req, res, next) { - var user = res.locals.user, - progress = user.fns.cron(), - ranCron = user.isModified(), - quest = shared.content.quests[user.party.quest.key]; + try{ + var user = res.locals.user, + progress = user.fns.cron(), + ranCron = user.isModified(), + quest = shared.content.quests[user.party.quest.key]; + if (ranCron) res.locals.wasModified = true; + if (!ranCron) return next(null,user); + Group.tavernBoss(user,progress); + if (!quest) return user.save(next); - if (ranCron) res.locals.wasModified = true; - if (!ranCron) return next(null,user); - Group.tavernBoss(user,progress); - if (!quest) return user.save(next); - - // If user is on a quest, roll for boss & player, or handle collections - // FIXME this saves user, runs db updates, loads user. Is there a better way to handle this? - async.waterfall([ - function(cb){ - user.save(cb); // make sure to save the cron effects - }, - function(saved, count, cb){ - var type = quest.boss ? 'boss' : 'collect'; - Group[type+'Quest'](user,progress,cb); - }, - function(){ - var cb = arguments[arguments.length-1]; - // User has been updated in boss-grapple, reload - User.findById(user._id, cb); - } - ], function(err, saved) { - res.locals.user = saved; - next(err,saved); - user = progress = quest = null; - }); + // If user is on a quest, roll for boss & player, or handle collections + // FIXME this saves user, runs db updates, loads user. Is there a better way to handle this? + async.waterfall([ + function(cb){ + user.save(cb); // make sure to save the cron effects + }, + function(saved, count, cb){ + var type = quest.boss ? 'boss' : 'collect'; + Group[type+'Quest'](user,progress,cb); + }, + function(){ + var cb = arguments[arguments.length-1]; + // User has been updated in boss-grapple, reload + User.findById(user._id, cb); + } + ], function(err, saved) { + if(err) logging.loggly({error: e, stack: e.stack}) + res.locals.user = saved; + next(err,saved); + user = progress = quest = null; + }); + }catch(e){ + logging.loggly({ + error: e.stack || e + }); + throw e; + } }; diff --git a/src/logging.js b/src/logging.js index 476a76d41b..63ab51ade9 100644 --- a/src/logging.js +++ b/src/logging.js @@ -14,7 +14,8 @@ if (nconf.get('NODE_ENV') == 'production'){ // // Optional: Tag to send with EVERY log message // - tags: ['heroku'] + tags: ['heroku'], + json: true }); } From e98243e3d80dfd727a52ca373b8ad45f283aad3a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 12 Dec 2014 11:57:55 -0700 Subject: [PATCH 101/202] refactor(env): small cleanup --- src/middleware.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/middleware.js b/src/middleware.js index ba5ed01f07..2efe9b470f 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -173,12 +173,9 @@ module.exports.locals = function(req, res, next) { language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined); var tavern = require('./models/group').tavern; - res.locals.habitrpg = { - NODE_ENV: nconf.get('NODE_ENV'), - BASE_URL: nconf.get('BASE_URL'), - GA_ID: nconf.get("GA_ID"), + var envVars = _.pick(nconf.get(), 'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY'.split(' ')); + res.locals.habitrpg = _.merge(envVars, { IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')), - STRIPE_PUB_KEY: nconf.get('STRIPE_PUB_KEY'), getManifestFiles: getManifestFiles, getBuildUrl: getBuildUrl, avalaibleLanguages: i18n.avalaibleLanguages, @@ -193,11 +190,9 @@ module.exports.locals = function(req, res, next) { siteVersion: siteVersion, Content: shared.content, mods: require('./models/user').mods, - FACEBOOK_KEY: nconf.get('FACEBOOK_KEY'), - tavern: tavern, // for world boss worldDmg: (tavern && tavern.quest && tavern.quest.extra && tavern.quest.extra.worldDmg) || {} - }; + }); // Put query-string party invitations into session to be handled later try{ From 9d6caaa138d31d4c784737150ea313d68c69b745 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Fri, 12 Dec 2014 16:18:33 -0700 Subject: [PATCH 102/202] feat(iap): ignore keys/ directory. upload any keys/iap-sandbox iap-live etc on a private branch --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1717063857..5b5e6f87bd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ test/*.js test/*.map public/docs *.sublime-workspace +keys From dac2fc682dfedbd9373b9d2695a857b1cf7afebb Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 13 Dec 2014 14:48:14 +0100 Subject: [PATCH 103/202] feat(loggly): change tag based on base_url, logs internal server errors --- src/controllers/user.js | 5 +++-- src/logging.js | 6 +++--- src/middleware.js | 7 +++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 68cc7b9ccb..e24800980a 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -284,14 +284,15 @@ api.cron = function(req, res, next) { User.findById(user._id, cb); } ], function(err, saved) { - if(err) logging.loggly({error: e, stack: e.stack}) + if(err) logging.loggly({error: "Cron caught", stack: e.stack || e}) res.locals.user = saved; next(err,saved); user = progress = quest = null; }); }catch(e){ logging.loggly({ - error: e.stack || e + error: "Cron uncaught", + stack: e.stack || e }); throw e; } diff --git a/src/logging.js b/src/logging.js index 63ab51ade9..441af207ee 100644 --- a/src/logging.js +++ b/src/logging.js @@ -3,6 +3,8 @@ var winston = require('winston'); require('winston-mail').Mail; require('winston-newrelic'); +var logger, loggly; + if (nconf.get('NODE_ENV') == 'production'){ var loggly = require('loggly').createClient({ token: nconf.get('LOGGLY:token'), @@ -14,13 +16,11 @@ if (nconf.get('NODE_ENV') == 'production'){ // // Optional: Tag to send with EVERY log message // - tags: ['heroku'], + tags: [('heroku-'+nconf.get('BASE_URL'))], json: true }); } -var logger; - if (logger == null) { logger = new (winston.Logger)({}); if (nconf.get('NODE_ENV') == 'production') { diff --git a/src/middleware.js b/src/middleware.js index 2efe9b470f..56e72bda7e 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -75,6 +75,13 @@ module.exports.errorHandler = function(err, req, res, next) { "\n\nbody: " + JSON.stringify(req.body) + (res.locals.ops ? "\n\ncompleted ops: " + JSON.stringify(res.locals.ops) : ""); logging.error(stack); + logging.loggly({ + error: "Uncaught error", + stack: (err.stack || err.message || err), + body: req.body, headers: req.header, + auth: (req.headers['x-api-user'] + ' | ' + req.headers['x-api-key']), + originalUrl: req.originalUrl + }); var message = err.message ? err.message : err; message = (message.length < 200) ? message : message.substring(0,100) + message.substring(message.length-100,message.length); res.json(500,{err:message}); //res.end(err.message); From 012e579cfe2f5a2003bc0f50ce9421e2b5f51041 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 13 Dec 2014 14:57:05 +0100 Subject: [PATCH 104/202] fix(loggly): typo --- src/controllers/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index e24800980a..7bf164cd73 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -284,7 +284,7 @@ api.cron = function(req, res, next) { User.findById(user._id, cb); } ], function(err, saved) { - if(err) logging.loggly({error: "Cron caught", stack: e.stack || e}) + if(err) logging.loggly({error: "Cron caught", stack: err.stack || err}); res.locals.user = saved; next(err,saved); user = progress = quest = null; From 61ae5c74209e7399a817b48d7f6d4afc4cbed4fc Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 13 Dec 2014 15:01:38 +0100 Subject: [PATCH 105/202] fix(loggly): enable only manually --- config.json.example | 1 + src/logging.js | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/config.json.example b/config.json.example index f50242f74c..74e2f0822c 100644 --- a/config.json.example +++ b/config.json.example @@ -44,6 +44,7 @@ }, "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/", "LOGGLY": { + "enabled": false, "subdomain": "subdomain", "token": "token", "username": "username", diff --git a/src/logging.js b/src/logging.js index 441af207ee..5c0d973812 100644 --- a/src/logging.js +++ b/src/logging.js @@ -5,8 +5,8 @@ require('winston-newrelic'); var logger, loggly; -if (nconf.get('NODE_ENV') == 'production'){ - var loggly = require('loggly').createClient({ +if (nconf.get('LOGGLY:enabled')){ + loggly = require('loggly').createClient({ token: nconf.get('LOGGLY:token'), subdomain: nconf.get('LOGGLY:subdomain'), auth: { @@ -67,6 +67,6 @@ module.exports.error = function(/* variable args */) { }; module.exports.loggly = function(/* variable args */){ - if(loggly) - loggly.log.apply(loggly, arguments); + if (loggly) + loggly.log.apply(loggly, arguments); }; From 4adf95feb644e35eb2c55fe53d0cb2a0248b10bf Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sat, 13 Dec 2014 12:05:17 -0600 Subject: [PATCH 106/202] fix(purchases): Hide "2/locked" as appropriate --- views/options/profile.jade | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index 6e212061dd..350079b9a5 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -15,8 +15,9 @@ mixin customizeProfile(mobile) if ~['limited','seasonal'].indexOf(status) .label.label-info.pull-right.hint(popover=limited, popover-title=env.t(status+'Edition'), popover-placement='right', popover-trigger='mouseenter')=env.t(status+'Edition') menu(label=env.t(title)) - +gemCost(2) - button.btn.btn-xs(ng-hide='#{status=="disabled"} || #{showPath("user.purchased."+path, colors, "||")}', ng-click='#{unlockPath(path, colors)}')!= env.t('unlockSet',{cost:5}) + ' ' + span(ng-hide='#{status=="disabled"} || #{showPath("user.purchased."+path, colors, "&&")}') + +gemCost(2) + button.btn.btn-xs(ng-click='#{unlockPath(path, colors)}')!= env.t('unlockSet',{cost:5}) + ' ' each color in colors button.customize-option(type='button', class='#{path=="skin" ? "skin_"+color : "customize-option hair hair_bangs_1_"+color}', ng-class='{locked: !user.purchased.#{path}["#{color}"]}', ng-if='#{status!="disabled"} || user.purchased.#{path}["#{color}"]', ng-click='unlock("#{path}.#{color}")') @@ -38,8 +39,9 @@ mixin customizeProfile(mobile) menu(label=env.t('specialShirts')) - var specialShirts = ['convict', 'cross', 'fire', 'horizon', 'ocean', 'purple', 'rainbow', 'redblue', 'thunder', 'tropical', 'zombie'] - +gemCost(2) - button.btn.btn-xs(ng-hide='#{showPath("user.purchased.shirt", specialShirts, "&&")}', ng-click='#{unlockPath("shirt",specialShirts)}')!= env.t('unlockSet',{cost:5}) + ' ' + span(ng-hide='#{showPath("user.purchased.shirt", specialShirts, "&&")}') + +gemCost(2) + button.btn.btn-xs(ng-click='#{unlockPath("shirt",specialShirts)}')!= env.t('unlockSet',{cost:5}) + ' ' each shirt in specialShirts button.customize-option(type='button', class='{{user.preferences.size}}_shirt_'+shirt, ng-class='{locked: !user.purchased.shirt.'+shirt+'}', ng-click='unlock("shirt.'+shirt+'")') @@ -80,8 +82,9 @@ mixin customizeProfile(mobile) // Purchasable hairstyles menu(label=env.t('hairSet1')) - var colors = [2,4,5,6,7,8] - +gemCost(2) - button.btn.btn-xs(ng-hide='#{showPath("user.purchased.hair", colors, "&&")}', ng-click='#{unlockPath("hair.base",colors)}')!= env.t('unlockSet',{cost:5}) + ' ' + span(ng-hide='#{showPath("user.purchased.hair", colors, "&&")}') + +gemCost(2) + button.btn.btn-xs(ng-click='#{unlockPath("hair.base",colors)}')!= env.t('unlockSet',{cost:5}) + ' ' each num in colors button(class='hair_base_#{num}_{{user.preferences.hair.color}} customize-option', type='button', ng-class='{locked: !user.purchased.hair.base["#{num}"]}', ng-click='unlock("hair.base.#{num}")') @@ -94,8 +97,9 @@ mixin customizeProfile(mobile) li.customize-menu h5=env.t('bodyFacialHair') - +gemCost(2) - button.btn.btn-xs(ng-hide='user.purchased.hair.mustache["1"] && user.purchased.hair.mustache["2"] && user.purchased.hair.beard["1"] && user.purchased.hair.beard["2"] && user.purchased.hair.beard["3"]', ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockSet',{cost:5}) + ' ' + span(ng-hide='user.purchased.hair.mustache["1"] && user.purchased.hair.mustache["2"] && user.purchased.hair.beard["1"] && user.purchased.hair.beard["2"] && user.purchased.hair.beard["3"]') + +gemCost(2) + button.btn.btn-xs(ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockSet',{cost:5}) + ' ' // Beard menu(label=env.t('beard')) @@ -249,9 +253,9 @@ mixin backgrounds(mobile) - var k = bgsKeys[i], bgs = env.Content.backgrounds[k]; li.customize-menu menu(label=env.t(k)) - +gemCost(7) - //-button.btn.btn-xs(ng-hide="ownsSet('background',Content.backgrounds['#{k}'])",ng-click="unlock(setKeys('background',Content.backgrounds['#{k}']))")!= env.t('unlockSet',{cost:15}) + ' ' - button.btn.btn-xs(ng-hide="ownsSet('background',#{JSON.stringify(bgs)})",ng-click="unlock(setKeys('background',#{JSON.stringify(bgs)}))")!= env.t('unlockSet',{cost:15}) + ' ' + span(ng-hide="ownsSet('background',#{JSON.stringify(bgs)})") + +gemCost(7) + button.btn.btn-xs(ng-click="unlock(setKeys('background',#{JSON.stringify(bgs)}))")!= env.t('unlockSet',{cost:15}) + ' ' each bg,k in bgs button.customize-option(type='button', class='background_#{k}', ng-class="user.purchased.background.#{k} ? 'background-unlocked' : 'background-locked'", ng-click='unlock("background.#{k}")', popover-title=bg.text(), popover=bg.notes(),popover-trigger='mouseenter') i.glyphicon.glyphicon-lock(ng-if="!user.purchased.background.#{k}") From 4ded747b8811d779d64f957bd937fd566add89b3 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sat, 13 Dec 2014 15:53:39 -0600 Subject: [PATCH 107/202] fix(purchases): Correct hair path Fixes https://github.com/HabitRPG/habitrpg/issues/4312. --- views/options/profile.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index 350079b9a5..2427b9cd6a 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -82,7 +82,7 @@ mixin customizeProfile(mobile) // Purchasable hairstyles menu(label=env.t('hairSet1')) - var colors = [2,4,5,6,7,8] - span(ng-hide='#{showPath("user.purchased.hair", colors, "&&")}') + span(ng-hide='#{showPath("user.purchased.hair.base", colors, "&&")}') +gemCost(2) button.btn.btn-xs(ng-click='#{unlockPath("hair.base",colors)}')!= env.t('unlockSet',{cost:5}) + ' ' each num in colors From e4df3435e13690288c920236c039b70988bc24cf Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sat, 13 Dec 2014 16:07:58 -0600 Subject: [PATCH 108/202] fix(avatar): Correct "Facial Hair" text Adjusts the text style of "Facial Hair" header in Avatar shop to follow the same scheme as Hair. Before, the text was less header-like than elements under it. --- views/options/profile.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index 2427b9cd6a..cf8fef0e25 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -96,7 +96,7 @@ mixin customizeProfile(mobile) button(class='hair_flower_#{num} customize-option', type='button', ng-click='set({"preferences.hair.flower":#{num}})') li.customize-menu - h5=env.t('bodyFacialHair') + menu(label=env.t('bodyFacialHair')) span(ng-hide='user.purchased.hair.mustache["1"] && user.purchased.hair.mustache["2"] && user.purchased.hair.beard["1"] && user.purchased.hair.beard["2"] && user.purchased.hair.beard["3"]') +gemCost(2) button.btn.btn-xs(ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockSet',{cost:5}) + ' ' From 41e1f4c75f0a1d69e90426045d9900c80a3e8b97 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 08:13:50 -0600 Subject: [PATCH 109/202] Conditionally allow completed quest image to be centered if no completion text is present --- public/css/helpers.styl | 8 +++++++- views/shared/modals/quests.jade | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/public/css/helpers.styl b/public/css/helpers.styl index 52d245a3fb..5dd3e778f0 100644 --- a/public/css/helpers.styl +++ b/public/css/helpers.styl @@ -32,4 +32,10 @@ visibility: hidden .transparent - opacity: 0.5 \ No newline at end of file + opacity: 0.5 + +// Allow block element to be centered +.col-centered + margin-left: auto + margin-right: auto + diff --git a/views/shared/modals/quests.jade b/views/shared/modals/quests.jade index 6ff8b9cd6a..ad53c0cd1f 100644 --- a/views/shared/modals/quests.jade +++ b/views/shared/modals/quests.jade @@ -5,7 +5,7 @@ script(type='text/ng-template', id='modals/questCompleted.html') h4 "{{::Content.quests[user.party.quest.completed].text()}}" =env.t('completed') .modal-body - .pull-right(class='quest_{{user.party.quest.completed}}') + div(ng-class='::Content.quests[user.party.quest.completed].completion() ? "pull-right" : "col-centered"', class='quest_{{user.party.quest.completed}}') p(ng-bind-html='::Content.quests[user.party.quest.completed].completion()') quest-rewards(key='{{user.party.quest.completed}}', header=env.t('youReceived')) .modal-footer From d0df9e46d64f72efb821d4253db75f4cd66876d8 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 08:31:40 -0600 Subject: [PATCH 110/202] Added helper classes for floats at various screen sizes --- public/css/helpers.styl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/public/css/helpers.styl b/public/css/helpers.styl index 5dd3e778f0..d9b54a3089 100644 --- a/public/css/helpers.styl +++ b/public/css/helpers.styl @@ -39,3 +39,26 @@ margin-left: auto margin-right: auto +// .pull-right & .pull-left for specific screen sizes +// Follows conventions for col-x-SIZE classes + +@media (min-width: 768px) + .pull-left-sm + float: left + + .pull-right-sm + float: right + +@media (min-width: 992px) + .pull-left-md + float: left + + .pull-right-md + float: right + +@media (min-width: 1200px) + .pull-left-lg + float: left + + .pull-right-lg + float: right From 396176f14f153bec5532dd20a323bb9d0c1ddcbf Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 08:32:05 -0600 Subject: [PATCH 111/202] Quest completion screen floats to right on everything but xs --- views/shared/modals/quests.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/quests.jade b/views/shared/modals/quests.jade index ad53c0cd1f..6ccf0dd06f 100644 --- a/views/shared/modals/quests.jade +++ b/views/shared/modals/quests.jade @@ -5,7 +5,7 @@ script(type='text/ng-template', id='modals/questCompleted.html') h4 "{{::Content.quests[user.party.quest.completed].text()}}" =env.t('completed') .modal-body - div(ng-class='::Content.quests[user.party.quest.completed].completion() ? "pull-right" : "col-centered"', class='quest_{{user.party.quest.completed}}') + div(ng-class='::Content.quests[user.party.quest.completed].completion() ? "pull-right-sm" : ""', class='col-centered quest_{{user.party.quest.completed}}') p(ng-bind-html='::Content.quests[user.party.quest.completed].completion()') quest-rewards(key='{{user.party.quest.completed}}', header=env.t('youReceived')) .modal-footer From a961d6b342deed6fea4bd5fc3b862ed7d8a1447f Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Tue, 9 Dec 2014 07:00:24 -0600 Subject: [PATCH 112/202] Added wrapping rule for user tooltip in chat messages closes #4367 --- public/css/game-pane.styl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/css/game-pane.styl b/public/css/game-pane.styl index 1a002370ba..f5416d9cb2 100644 --- a/public/css/game-pane.styl +++ b/public/css/game-pane.styl @@ -37,6 +37,10 @@ markdown, span.time padding: 0 3px line-height: 1.618em + + .tooltip + white-space: normal + span.time white-space: nowrap From dde3f5ffef2a937a4ba8af3b6424be2bcff60592 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 12:18:59 -0600 Subject: [PATCH 113/202] Adjusted tooltip wrapping so it would only affect long titles --- public/css/game-pane.styl | 3 +-- views/options/social/chat-message.jade | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/public/css/game-pane.styl b/public/css/game-pane.styl index f5416d9cb2..8ccc3c211d 100644 --- a/public/css/game-pane.styl +++ b/public/css/game-pane.styl @@ -38,9 +38,8 @@ padding: 0 3px line-height: 1.618em - .tooltip + .long-title .tooltip white-space: normal - span.time white-space: nowrap diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index dc332452ed..89ebc181e6 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -26,7 +26,7 @@ mixin chatMessages(inbox) |         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')) - span.float-label + 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') |   From 35b02cfdd894a21f69175b81b5fefc74a0c35cda Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 12:59:45 -0600 Subject: [PATCH 114/202] fix(I18N): Make Time Travel translatable --- views/options/inventory/index.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/index.jade b/views/options/inventory/index.jade index af191f3de1..6ef2e4302b 100644 --- a/views/options/inventory/index.jade +++ b/views/options/inventory/index.jade @@ -17,7 +17,7 @@ script(type='text/ng-template', id='partials/options.inventory.html') =env.t('equipment') li.equipment-tab(ng-class="{ active: $state.includes('options.inventory.timetravelers') }") a(ui-sref='options.inventory.timetravelers') - | Time Travelers + =env.t('timeTravelers') .tab-content .tab-pane.active From a98a68597127f385c7729f25ff6f9f282ea20d0a Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 13:37:32 -0600 Subject: [PATCH 115/202] feat(seasonal): Seasonal Shop Initial commit of frontend for Seasonal Shop feature. --- views/options/inventory/index.jade | 3 +++ views/options/inventory/inventory.jade | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/views/options/inventory/index.jade b/views/options/inventory/index.jade index 6ef2e4302b..b2f0cfb3ec 100644 --- a/views/options/inventory/index.jade +++ b/views/options/inventory/index.jade @@ -18,6 +18,9 @@ script(type='text/ng-template', id='partials/options.inventory.html') li.equipment-tab(ng-class="{ active: $state.includes('options.inventory.timetravelers') }") a(ui-sref='options.inventory.timetravelers') =env.t('timeTravelers') + li.equipment-tab(ng-class="{ active: $state.includes('options.inventory.seasonalshop') }") + a(ui-sref='options.inventory.seasonalshop') + =env.t('seasonalShop') .tab-content .tab-pane.active diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 3d872231a6..d0fc5ad7d2 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -19,6 +19,18 @@ script(type='text/ng-template', id='partials/options.inventory.equipment.html') div(ng-repeat='item in gear[klass]') button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({params:{type:"costume", key:item.key}})', class='shop_{{::item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') +script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html') +.container-fluid + .stable.row + .col-md-2 + .seasonalshop_closed + .col-md-10 + .popover.static-popover.fade.right.in + .arrow + h3.popover-title!=env.t('seasonalShopClosedTitle') + .popover-content + p=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) + script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid .stable.row(ng-if='user.purchased.plan.consecutive.trinkets <= 0') From 506c396baf0ee708026644ef49a2c52ec676f620 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 13:53:00 -0600 Subject: [PATCH 116/202] fix(I18N): Translate TT in menu --- views/shared/header/menu.jade | 1 + 1 file changed, 1 insertion(+) diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index 486cdd95f3..f0a24e16c5 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -50,6 +50,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') a(ui-sref='options.inventory.equipment')=env.t('equipment') li a(ui-sref='options.inventory.timetravelers') Time Travelers + a(ui-sref='options.inventory.timetravelers')=env.t('timeTravelers') ul.toolbar-submenu li a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}')=env.t('dataTool') From 29f693cd246c20f0a85c644442566657e7a8fd93 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 13:55:40 -0600 Subject: [PATCH 117/202] fix(I18N): Remove hardcoded text --- views/shared/header/menu.jade | 1 - 1 file changed, 1 deletion(-) diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index f0a24e16c5..d2586c99c5 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -49,7 +49,6 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') li a(ui-sref='options.inventory.equipment')=env.t('equipment') li - a(ui-sref='options.inventory.timetravelers') Time Travelers a(ui-sref='options.inventory.timetravelers')=env.t('timeTravelers') ul.toolbar-submenu li From 7988dfcf824079bc9f6c5be32f13aa1cc58234ce Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 14:01:25 -0600 Subject: [PATCH 118/202] fix(seasonal): Corrections for Shop Fix Jade indenting, add state control, add menu option --- public/js/app.js | 4 ++++ views/options/inventory/inventory.jade | 20 ++++++++++---------- views/shared/header/menu.jade | 2 ++ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/public/js/app.js b/public/js/app.js index c12ba3aa08..8bf5c0c47e 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -185,6 +185,10 @@ window.habitrpg = angular.module('habitrpg', url: '/timetravelers', templateUrl: "partials/options.inventory.timetravelers.html" }) + .state('options.inventory.seasonalshop', { + url: '/seasonalshop', + templateUrl: "partials/options.inventory.seasonalshop.html" + }) // Options > Settings .state('options.settings', { diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index d0fc5ad7d2..947500f63a 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -20,16 +20,16 @@ script(type='text/ng-template', id='partials/options.inventory.equipment.html') button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({params:{type:"costume", key:item.key}})', class='shop_{{::item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html') -.container-fluid - .stable.row - .col-md-2 - .seasonalshop_closed - .col-md-10 - .popover.static-popover.fade.right.in - .arrow - h3.popover-title!=env.t('seasonalShopClosedTitle') - .popover-content - p=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) + .container-fluid + .stable.row + .col-md-2 + .seasonalshop_closed + .col-md-10 + .popover.static-popover.fade.right.in + .arrow + h3.popover-title!=env.t('seasonalShopClosedTitle') + .popover-content + p=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index 486cdd95f3..c83fbe8e78 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -50,6 +50,8 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') a(ui-sref='options.inventory.equipment')=env.t('equipment') li a(ui-sref='options.inventory.timetravelers') Time Travelers + li + a(ui-sref='options.inventory.seasonalshop')=env.t('seasonalshop') ul.toolbar-submenu li a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}')=env.t('dataTool') From 6dcbd329a50fc6182a36f8a0c2c0c89cb8ae0261 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 14:05:43 -0600 Subject: [PATCH 119/202] fix(seasonal): Typo in string resource --- views/shared/header/menu.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index c83fbe8e78..0cdb1ea759 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -51,7 +51,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') li a(ui-sref='options.inventory.timetravelers') Time Travelers li - a(ui-sref='options.inventory.seasonalshop')=env.t('seasonalshop') + a(ui-sref='options.inventory.seasonalshop')=env.t('seasonalShop') ul.toolbar-submenu li a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}')=env.t('dataTool') From 8cd1ea48dbc4eea07cd4822bd0743046cad00aba Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 14:10:39 -0600 Subject: [PATCH 120/202] fix(I18N): Remove more hardcoded text --- views/shared/header/menu.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index d2586c99c5..c52c700c27 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -131,7 +131,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') li a(ui-sref='options.inventory.equipment')=env.t('equipment') li - a(ui-sref='options.inventory.timetravelers') Time Travelers + a(ui-sref='options.inventory.timetravelers')=env.t('timeTravelers') li.toolbar-button-dropdown a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}') span=env.t('data') From 867f50b514566e0140576bba75abb706ce156a6d Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 14:12:20 -0600 Subject: [PATCH 121/202] fix(seasonal): Add menu item --- views/shared/header/menu.jade | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index 0cdb1ea759..364db96d7e 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -134,6 +134,8 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') a(ui-sref='options.inventory.equipment')=env.t('equipment') li a(ui-sref='options.inventory.timetravelers') Time Travelers + li + a(ui-sref='options.inventory.seasonalshop')=env.t('seasonalShop') li.toolbar-button-dropdown a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}') span=env.t('data') From 9f46e14cd6ca5ca222d958548a3d794a41662c7a Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 14 Dec 2014 14:18:57 -0600 Subject: [PATCH 122/202] fix(seasonal): missing bang --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 947500f63a..929919d94b 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -29,7 +29,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .arrow h3.popover-title!=env.t('seasonalShopClosedTitle') .popover-content - p=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) + p!=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid From d1b1a866ac6401e05e81fa8428420fac19efd1c8 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 19:01:52 -0600 Subject: [PATCH 123/202] Fixed when routing. Closes #4371 --- public/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/app.js b/public/js/app.js index c12ba3aa08..387692dbaf 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -22,9 +22,9 @@ window.habitrpg = angular.module('habitrpg', // Setup default selected tabs .when('/options', '/options/profile/avatar') .when('/options/profile', '/options/profile/avatar') - .when('/options/groups', '/options/groups/tavern') .when('/options/groups/guilds', '/options/groups/guilds/public') .when('/options/groups/hall', '/options/groups/hall/heroes') + .when('/options/groups', '/options/groups/tavern') .when('/options/inventory', '/options/inventory/drops') .when('/options/settings', '/options/settings/settings') From 9cdbca8c2b5e6b352c463ae875b211004c8d894e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 20:05:01 -0600 Subject: [PATCH 124/202] Reverted routing for groups back --- public/js/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/js/app.js b/public/js/app.js index 387692dbaf..ee390df22d 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -7,7 +7,6 @@ window.habitrpg = angular.module('habitrpg', // @see https://github.com/angular-ui/ui-router/issues/110 and https://github.com/HabitRPG/habitrpg/issues/1705 // temporary hack until they have a better solution - .value('$anchorScroll', angular.noop) .constant("API_URL", "") .constant("STORAGE_USER_ID", 'habitrpg-user') @@ -22,9 +21,9 @@ window.habitrpg = angular.module('habitrpg', // Setup default selected tabs .when('/options', '/options/profile/avatar') .when('/options/profile', '/options/profile/avatar') + .when('/options/groups', '/options/groups/tavern') .when('/options/groups/guilds', '/options/groups/guilds/public') .when('/options/groups/hall', '/options/groups/hall/heroes') - .when('/options/groups', '/options/groups/tavern') .when('/options/inventory', '/options/inventory/drops') .when('/options/settings', '/options/settings/settings') From 63c2248b38b9449e5f15adfce6ac06efdfac6597 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 20:07:46 -0600 Subject: [PATCH 125/202] Corrected guild links --- views/options/social/index.jade | 2 +- views/shared/header/menu.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/views/options/social/index.jade b/views/options/social/index.jade index 89a1e98e7e..32100f2aff 100644 --- a/views/options/social/index.jade +++ b/views/options/social/index.jade @@ -105,7 +105,7 @@ script(type='text/ng-template', id='partials/options.social.html') a(ui-sref='options.social.party') =env.t('party') li(ng-class="{ active: $state.includes('options.social.guilds') }") - a(ui-sref='options.social.guilds') + a(ui-sref='options.social.guilds.public') =env.t('guilds') li(ng-class="{ active: $state.includes('options.social.challenges') }") a(ui-sref='options.social.challenges') diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index c52c700c27..5ba7fe1dfb 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -110,7 +110,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') li a(ui-sref='options.social.party')=env.t('party') li - a(ui-sref='options.social.guilds')=env.t('guilds') + a(ui-sref='options.social.guilds.public')=env.t('guilds') li a(ui-sref='options.social.challenges')=env.t('challenges') li From d56eb1eeeb5600e76b3f839b61d77810626148a2 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Sun, 14 Dec 2014 20:10:42 -0600 Subject: [PATCH 126/202] Added last guild link, corrected hall links --- views/options/social/index.jade | 2 +- views/shared/header/menu.jade | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/views/options/social/index.jade b/views/options/social/index.jade index 32100f2aff..24b86ab7e5 100644 --- a/views/options/social/index.jade +++ b/views/options/social/index.jade @@ -111,7 +111,7 @@ script(type='text/ng-template', id='partials/options.social.html') a(ui-sref='options.social.challenges') =env.t('challenges') li(ng-class="{ active: $state.includes('options.social.hall') }") - a(ui-sref='options.social.hall') + a(ui-sref='options.social.hall.heroes') =env.t('hall') .tab-content diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index 5ba7fe1dfb..024c9463d4 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -34,11 +34,11 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') li a(ui-sref='options.social.party')=env.t('party') li - a(ui-sref='options.social.guilds')=env.t('guilds') + a(ui-sref='options.social.guilds.public')=env.t('guilds') li a(ui-sref='options.social.challenges')=env.t('challenges') li - a(ui-sref='options.social.hall')=env.t('hall') + a(ui-sref='options.social.hall.heroes')=env.t('hall') ul.toolbar-submenu li a(ui-sref='options.inventory.drops')=env.t('market') @@ -114,7 +114,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}') li a(ui-sref='options.social.challenges')=env.t('challenges') li - a(ui-sref='options.social.hall')=env.t('hall') + a(ui-sref='options.social.hall.heroes')=env.t('hall') li.toolbar-button-dropdown a(ui-sref='options.inventory.drops') span=env.t('inventory') From 43961a5ab948def947406673380c2ddb4df6b596 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 16 Dec 2014 11:43:58 -0700 Subject: [PATCH 127/202] refactor(iap): use env var for google IAP, intead of key file. See https://github.com/voltrue2/in-app-purchase/commit/cb31043281dc1221c61efb2dfbd13687761bceca cc @negue --- config.json.example | 2 +- package.json | 2 +- src/controllers/payments/iap.js | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/config.json.example b/config.json.example index 74e2f0822c..196ceb369f 100644 --- a/config.json.example +++ b/config.json.example @@ -42,7 +42,7 @@ "client_id":"client_id", "client_secret":"client_secret" }, - "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/", + "GOOGLE_IAB_PUBLICKEY_LIVE": "GOOGLE_IAB_PUBLICKEY_LIVE", "LOGGLY": { "enabled": false, "subdomain": "subdomain", diff --git a/package.json b/package.json index 6f14a60e51..84fd5bccfc 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "grunt-nodemon": "~0.3.0", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop", "icalendar": "git://github.com/lefnire/node-icalendar#master", - "in-app-purchase": "^0.2.0", + "in-app-purchase": "^0.3.0", "jade": "~1.7.0", "js2xmlparser": "~0.1.2", "lodash": "~2.4.1", diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index 29b03fedfb..9dcef12efc 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -3,12 +3,6 @@ var async = require('async'); var payments = require('./index'); var nconf = require('nconf'); -var inAppPurchase = require('in-app-purchase'); -inAppPurchase.config({ - // this is the path to the directory containing iap-sanbox/iap-live files - googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") -}); - // Validation ERROR Codes var INVALID_PAYLOAD = 6778001; var CONNECTION_FAILED = 6778002; From 709fb2b706cb49a1393a3f61a60a858dede1a983 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 16 Dec 2014 13:05:39 -0700 Subject: [PATCH 128/202] Revert "refactor(iap): use env var for google IAP". orders are being left in "Chargeable - Delivered" for some reason. This reverts commit 43961a5ab948def947406673380c2ddb4df6b596. --- .gitignore | 1 - config.json.example | 2 +- keys/google/iap-live | 1 + package.json | 2 +- src/controllers/payments/iap.js | 6 ++++++ 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 keys/google/iap-live diff --git a/.gitignore b/.gitignore index 5b5e6f87bd..1717063857 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,3 @@ test/*.js test/*.map public/docs *.sublime-workspace -keys diff --git a/config.json.example b/config.json.example index 196ceb369f..74e2f0822c 100644 --- a/config.json.example +++ b/config.json.example @@ -42,7 +42,7 @@ "client_id":"client_id", "client_secret":"client_secret" }, - "GOOGLE_IAB_PUBLICKEY_LIVE": "GOOGLE_IAB_PUBLICKEY_LIVE", + "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/", "LOGGLY": { "enabled": false, "subdomain": "subdomain", diff --git a/keys/google/iap-live b/keys/google/iap-live new file mode 100644 index 0000000000..69dc38136b --- /dev/null +++ b/keys/google/iap-live @@ -0,0 +1 @@ +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk6EVccD7D6y5vLmsUxUXVYTMyByq+NjfTzjMBaKCk93fq8Ahn6Ipha1K4oBIvArUj3CsP5Tb/Yf6c6SrEsSnE3eqgKQgeq+fYyfIm+Fb+tFGTttLonZ2Sqemqeyx2PjOG2CVZBXj1+Zr7jb0ocMNYraYUTvklWnpcK6/5ZaZLLent1PGiU9l5jIfRAsxmJH/UpssgpFPnCs+8PRKUALkJATTfVCCF+PJef6mcKAafAArKUEo0lR2/NeaE62Wpii33DLuKDPfMqL3g4EHZTEVE1ezFl/3WPeLlxuQm38hZLpEfEYKp17qzplU+GmWIfjZt6OdtVdonlYxIxNAZLnWEQIDAQAB \ No newline at end of file diff --git a/package.json b/package.json index 84fd5bccfc..6f14a60e51 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "grunt-nodemon": "~0.3.0", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop", "icalendar": "git://github.com/lefnire/node-icalendar#master", - "in-app-purchase": "^0.3.0", + "in-app-purchase": "^0.2.0", "jade": "~1.7.0", "js2xmlparser": "~0.1.2", "lodash": "~2.4.1", diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index 9dcef12efc..29b03fedfb 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -3,6 +3,12 @@ var async = require('async'); var payments = require('./index'); var nconf = require('nconf'); +var inAppPurchase = require('in-app-purchase'); +inAppPurchase.config({ + // this is the path to the directory containing iap-sanbox/iap-live files + googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") +}); + // Validation ERROR Codes var INVALID_PAYLOAD = 6778001; var CONNECTION_FAILED = 6778002; From 2822ce233fae8fbf422c064d46c1292b21e17220 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 16 Dec 2014 14:17:38 -0700 Subject: [PATCH 129/202] fix(inn): see https://github.com/HabitRPG/habitrpg-shared/pull/231 --- views/options/social/tavern.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/social/tavern.jade b/views/options/social/tavern.jade index 9eea1159f3..0739126588 100644 --- a/views/options/social/tavern.jade +++ b/views/options/social/tavern.jade @@ -21,7 +21,7 @@ span(ng-hide='user.preferences.sleep')=env.t('innCheckIn') =env.t('danielText2') .alert.alert-info(ng-show='user.preferences.sleep') - =env.t('innText') + =env.t('innText',{name:"{{user.profile.name}}"}) // Resources .panel.panel-default From d91d9e1eb3d04b180701d40542e7ae4fc554a919 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 16 Dec 2014 16:30:51 -0700 Subject: [PATCH 130/202] refactor(facebook): use async.auto to collect multiple vars, cleaner than waterfall --- src/controllers/auth.js | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 4e3877ca3f..67ddbc08f5 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -148,21 +148,19 @@ api.loginSocial = function(req, res, next) { network = req.body.network; if (network!=='facebook') return res.json(401, {err:"Only Facebook supported currently."}); - async.waterfall([ - function(cb){ + async.auto({ + profile: function (cb) { passport._strategies[network].userProfile(access_token, cb); }, - function(profile, cb) { - var q = {};q['auth.'+network+'.id'] = profile.id; - User.findOne(q, {_id:1, apiToken:1, auth:1}, function(err, user){ - if (err) return cb(err); - cb(null, {user:user, profile:profile}); - }); - }, - function(data, cb){ - if (data.user) return cb(null, data.user); + user: ['profile', function (cb, results) { + var q = {}; + q['auth.' + network + '.id'] = results.profile.id; + User.findOne(q, {_id: 1, apiToken: 1, auth: 1}, cb); + }], + register: ['profile', 'user', function (cb, results) { + if (results.user) return cb(null, results.user); // Create new user - var prof = data.profile; + var prof = results.profile; var user = { preferences: { language: req.language // User language detected from browser, not saved @@ -175,15 +173,16 @@ api.loginSocial = function(req, res, next) { user = new User(user); user.save(cb); - if(isProd && prof.emails && prof.emails[0] && prof.emails[0].value){ - utils.txnEmail({name:prof.displayName || prof.username, email:prof.emails[0].value}, 'welcome'); + if (isProd && prof.emails && prof.emails[0] && prof.emails[0].value) { + utils.txnEmail({name: prof.displayName || prof.username, email: prof.emails[0].value}, 'welcome'); } ga.event('register', network).send(); - } - ], function(err, user){ + }] + }, function(err, results){ if (err) return res.json(401, {err: err.toString ? err.toString() : err}); - if (user.auth.blocked) return res.json(401, accountSuspended(user._id)); - return res.json(200, {id: user.id, token:user.apiToken}); + var acct = results.register[0] ? results.register[0] : results.register; + if (acct.auth.blocked) return res.json(401, accountSuspended(acct._id)); + return res.json(200, {id:acct._id, token:acct.apiToken}); }) }; From 58957118c0d38d23e139b53d40e1d195ea6d0826 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 16 Dec 2014 20:18:55 -0600 Subject: [PATCH 131/202] chore(news): Bailey for Seasonal Shop --- views/shared/new-stuff.jade | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index b059de69df..632780c771 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -1,19 +1,41 @@ -h5 PENGUIN PET QUEST AND WINTER PLOT-LINE +h5 ANDROID APP UPDATE, SEASONAL SHOP, AND WINTER PLOT-LINE CONTINUES tr td - h5 Penguin Pet Quest - .quest_penguin.pull-right - p Habiticans wanted to go ice-skating, but instead, a giant penguin is freezing everything in sight! All we wanted was to go ice-skating... Can you get this penguin to chill out? If so, you'll be rewarded with some penguins of your own! - p.small.muted by Melynnrose, Breadstrings, Rattify, Painter de Cluster, Daniel the Bard, and Leephon + h5 Android App Update: December Art and Buying Gems! + .background_south_pole.pull-right + p The December backgrounds and penguin pet quest are now visible in the Android mobile app! Also, we’ve made it possible to buy gems directly from the app. Now you don’t have to switch to the website to stock up! + p You can get the Android app here! We will announce when the iOS app is available as well. + p.small.muted by negue tr td - h5 Winter Plot-Line - p Lemoness enters the Tavern with worrying news from the far north of Habitica. "Nobody's heard from the StoĂ¯kalm Steppes for over a week," she says. "It's hard to imagine anything troubling the citizens there, since it's such a placid part of the continent... But just in case, maybe I should pay a visit." Sounds like a good plan to us! + h5 Seasonal Shop Tab + .seasonalshop_closed.pull-right + p Looks like a new tab has appeared under Inventory - the Seasonal Shop! It's still closed, but I've heard a rumor that it will open soon... + p.small.muted by SabreCat and Lemoness + tr + td + h5 Winter Plot-Line Continues + p Lemoness bursts into the Tavern, shaking icicles off her hat. "The StoĂ¯kalm Steppes are completely abandoned!" she says, gulping the cup of tea that Daniel the Barkeep offers her. "No people milling about, no mounts and pets playing in the snow - and when I tried to fly closer, my dragon spooked and refused to land!" + p A cloaked figure in the corner steps into the fire light - SabreCat, a powerful adventurer from the north. "The StoĂ¯kalm Steppes are the last home of many animals that have long since gone extinct elsewhere," he says. "The stoic StoĂ¯kalmers would never flee their lands unless something was threatening their pets and mounts!" + p He turns to Lemoness. "I can speak the language of the northern beasts. I'll try to contact the roaming sabertooth prides to see if they know what happened." As he lopes off into the distance, a cold wind begins to blow. + p.muted Missed the first part of the Winter Plot-Line? Read it here. hr a(href='/static/old-news', target='_blank') Read older news mixin oldNews + h5 12/9/2014 - Penguin Pet Quest and Winter Plot-Line + tr + td + h5 Penguin Pet Quest + .quest_penguin.pull-right + p Habiticans wanted to go ice-skating, but instead, a giant penguin is freezing everything in sight! All we wanted was to go ice-skating... Can you get this penguin to chill out? If so, you'll be rewarded with some penguins of your own! + p.small.muted by Melynnrose, Breadstrings, Rattify, Painter de Cluster, Daniel the Bard, and Leephon + tr + td + h5 Winter Plot-Line + p Lemoness enters the Tavern with worrying news from the far north of Habitica. "Nobody's heard from the StoĂ¯kalm Steppes for over a week," she says. "It's hard to imagine anything troubling the citizens there, since it's such a placid part of the continent... But just in case, maybe I should pay a visit." Sounds like a good plan to us! + h5 12/3/2014 - Gifting Subscriptions And Gems, New Subscription Benefits, Mysterious Time Travelers, Steampunk Item Sets, And Block Subscriptions! table.table.table-striped tr From dbe608bc3aa06606cea20867bcdb63d40c516bc1 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 16 Dec 2014 20:24:04 -0600 Subject: [PATCH 132/202] fix(news): correct link and awkward image --- views/shared/new-stuff.jade | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index 632780c771..8162251f90 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -2,7 +2,6 @@ h5 ANDROID APP UPDATE, SEASONAL SHOP, AND WINTER PLOT-LINE CONTINUES tr td h5 Android App Update: December Art and Buying Gems! - .background_south_pole.pull-right p The December backgrounds and penguin pet quest are now visible in the Android mobile app! Also, we’ve made it possible to buy gems directly from the app. Now you don’t have to switch to the website to stock up! p You can get the Android app here! We will announce when the iOS app is available as well. p.small.muted by negue @@ -10,7 +9,7 @@ h5 ANDROID APP UPDATE, SEASONAL SHOP, AND WINTER PLOT-LINE CONTINUES td h5 Seasonal Shop Tab .seasonalshop_closed.pull-right - p Looks like a new tab has appeared under Inventory - the Seasonal Shop! It's still closed, but I've heard a rumor that it will open soon... + p Looks like a new tab has appeared under Inventory - the Seasonal Shop! It's still closed, but I've heard a rumor that it will open soon... p.small.muted by SabreCat and Lemoness tr td From 8ff0a4a4a435dd9c01af09d7accbe7eff19436a9 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Wed, 17 Dec 2014 18:27:37 -0600 Subject: [PATCH 133/202] fix(seasonal): Leslie link --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 929919d94b..da48ed04fc 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -27,7 +27,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .col-md-10 .popover.static-popover.fade.right.in .arrow - h3.popover-title!=env.t('seasonalShopClosedTitle') + h3.popover-title!=env.t('seasonalShopClosedTitle', {linkStart:"", linkEnd: ""}) .popover-content p!=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) From dc18569e78201dd5ee736483721a78ca3a9eb0e9 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 17 Dec 2014 22:07:49 -0700 Subject: [PATCH 134/202] fix(phantom): downgrade to node 0.10.22 due to CERT error, see https://github.com/Medium/phantomjs/issues/262 (cc @sabrecat) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f14a60e51..b6b97ba199 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "www.habitrpg.com" ], "engines": { - "node": "0.10.x", + "node": "0.10.22", "npm": "1.2.x" }, "scripts": { From 9406b3cf7003a52c8e157dc2a0a52f76cfd0a506 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 18 Dec 2014 22:36:52 -0600 Subject: [PATCH 135/202] Removed unnecessary console.log --- public/js/controllers/groupsCtrl.js | 1 - 1 file changed, 1 deletion(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index f99a79ec76..8e4b8695dd 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -147,7 +147,6 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }); } $scope.clearFlagCount = function(message, groupId) { - console.log("Heeey"); Groups.Group.clearFlagCount({gid: groupId, messageId: message.id}, undefined, function(data){ message.flagCount = 0; Notification.text("Flags cleared"); From 1675bb26b2c9151f7ef8c5c8a2f675d5f42a0e0f Mon Sep 17 00:00:00 2001 From: thehindenburg Date: Fri, 19 Dec 2014 07:47:28 -0800 Subject: [PATCH 136/202] change link in gala page to gala wiki --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index da48ed04fc..d5d254c2a3 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -29,7 +29,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .arrow h3.popover-title!=env.t('seasonalShopClosedTitle', {linkStart:"", linkEnd: ""}) .popover-content - p!=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) + p!=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid From a07863641697f7db072715844247acd35c040546 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 19 Dec 2014 10:43:15 -0600 Subject: [PATCH 137/202] Fixed get members so it can be used on static/api page --- src/routes/apiv2.coffee | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 415577d565..408fd3f73f 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -580,8 +580,12 @@ module.exports = (swagger, v2) -> # --------------------------------- # Members # --------------------------------- - "/members/{uuid}": - spec:{} + "/members/{uuid}:GET": + spec: + path: '/members/{uuid}' + description: "Get a member." + parameters: [path('uuid','Member ID','string')] + middleware: [auth.auth, i18n.getUserLanguage] action: members.getMember "/members/{uuid}/message": spec: From d358236f42fa3fb87d040feee9fe92177ba3f3ca Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 19 Dec 2014 10:44:55 -0600 Subject: [PATCH 138/202] Corrected example for show private message so it would result in a valid call --- src/routes/apiv2.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 408fd3f73f..7b1ae522a0 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -593,7 +593,7 @@ module.exports = (swagger, v2) -> description: 'Send a private message to a member' parameters: [ path 'uuid', 'The UUID of the member to message', 'string' - body '', '{message: "The private message to send"}', 'object' + body '', '{"message": "The private message to send"}', 'object' ] middleware: [auth.auth] action: members.sendPrivateMessage From be37e88dc33f26847efa2290565d4895978ab2e0 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 19 Dec 2014 11:05:24 -0600 Subject: [PATCH 139/202] Corrected example for send gift to be correct --- src/routes/apiv2.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 7b1ae522a0..40b0adf15d 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -612,7 +612,7 @@ module.exports = (swagger, v2) -> description: 'Send a gift to a member' parameters: [ path 'uuid', 'The UUID of the member', 'string' - body '', '{gems:{amount:Number, fromBalance:Boolean}, subscription:{months:Number}}', 'object' + body '', '{"type": "gems or subscription", "gems":{"amount":Number, "fromBalance":Boolean}, "subscription":{"months":Number}}', 'object' ] middleware: [auth.auth] action: members.sendGift From 7c0b25d58a6c5b1b585f42c9da147bb949ac62d8 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 19 Dec 2014 19:46:05 +0100 Subject: [PATCH 140/202] pass base_url to emails as variable --- src/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils.js b/src/utils.js index ba8dd47f95..f37d7cd9d0 100644 --- a/src/utils.js +++ b/src/utils.js @@ -35,6 +35,8 @@ function getMailingInfo(user) { } module.exports.txnEmail = function(mailingInfo, emailType, variables){ + var variables = [{name: 'BASE_URL', content: nconf.get('BASE_URL')}].concat(variables || []); + if (mailingInfo._id) mailingInfo = getMailingInfo(mailingInfo); if (!mailingInfo.email) return; request({ From 739d8885a36057bbf2198e350d507e92f87503d4 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Fri, 19 Dec 2014 19:59:12 +0100 Subject: [PATCH 141/202] fix(emails): remove unused variable --- src/controllers/auth.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 67ddbc08f5..3a00eda7f0 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -207,7 +207,6 @@ api.resetPassword = function(req, res, next){ user.auth.local.hashed_password = hashed_password; utils.txnEmail(user, 'reset-password', [ {name: "NEW_PASSWORD", content: newPassword}, - {name: "URL", content: nconf.get('BASE_URL')}, {name: "USERNAME", content: user.auth.local.username} ]); user.save(function(err){ From 95951a470618c242c18b9f87177d754a11364f15 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 19 Dec 2014 19:41:22 -0600 Subject: [PATCH 142/202] Updated moment calls to use number, period to fix the deprecation error --- test/api.mocha.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/api.mocha.coffee b/test/api.mocha.coffee index 28b2a97c3d..70521f91a1 100644 --- a/test/api.mocha.coffee +++ b/test/api.mocha.coffee @@ -141,7 +141,7 @@ describe "API", -> id: res.body.todos[0].id body: - dateCompleted: moment().subtract("days", 4) + dateCompleted: moment().subtract(4, "days") } { op: "updateTask" @@ -149,7 +149,7 @@ describe "API", -> id: res.body.todos[1].id body: - dateCompleted: moment().subtract("days", 4) + dateCompleted: moment().subtract(4, "days") } ]).end (res) -> expect(_.size(res.body.todos)).to.be 4 @@ -460,7 +460,7 @@ describe "API", -> { op: "update" body: - lastCron: moment().subtract("days", 1) + lastCron: moment().subtract(1, "days") } ]).end (res) -> expect(res.body.party.quest.progress.up).to.be.above up @@ -532,7 +532,7 @@ describe "API", -> { op: "update" body: - lastCron: moment().subtract("days", 1) + lastCron: moment().subtract(1, "days") } ]).end -> cb() @@ -608,7 +608,7 @@ describe "API", -> it "Handles unsubscription", (done) -> cron = -> - user.lastCron = moment().subtract("d", 1) + user.lastCron = moment().subtract(1, "d") user.fns.cron() expect(user.purchased.plan.customerId).to.not.be.ok() @@ -624,7 +624,7 @@ describe "API", -> cron() expect(user.purchased.plan.customerId).to.be.ok() expect(user.purchased.plan.dateTerminated).to.be.ok() - user.purchased.plan.dateTerminated = moment().subtract("d", 2) + user.purchased.plan.dateTerminated = moment().subtract(2, "d") cron() expect(user.purchased.plan.customerId).to.not.be.ok() payments.createSubscription user, From 8b3c6f94bf85f99620765e959885c77d08b5809d Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 20 Dec 2014 16:20:40 +0100 Subject: [PATCH 143/202] fix(loggly): send more infos about cron errors --- src/controllers/user.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 7bf164cd73..a7f80bf5f7 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -284,7 +284,13 @@ api.cron = function(req, res, next) { User.findById(user._id, cb); } ], function(err, saved) { - if(err) logging.loggly({error: "Cron caught", stack: err.stack || err}); + if(err) logging.loggly({ + error: "Cron caught", + stack: (err.stack || err.message || err), + body: req.body, headers: req.header, + auth: (req.headers['x-api-user'] + ' | ' + req.headers['x-api-key']), + originalUrl: req.originalUrl + }); res.locals.user = saved; next(err,saved); user = progress = quest = null; From 492260d74ae4c435a191bf0df1b26aed344387af Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 20 Dec 2014 17:58:00 -0700 Subject: [PATCH 144/202] refactor(coupons): coupons cleanup using async.auto --- src/models/coupon.js | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/models/coupon.js b/src/models/coupon.js index c5122b6ba7..497b45697f 100644 --- a/src/models/coupon.js +++ b/src/models/coupon.js @@ -18,38 +18,34 @@ CouponSchema.statics.generate = function(event, count, callback) { } CouponSchema.statics.apply = function(user, code, next){ - var _coupon,_user; - async.waterfall([ - function(cb) { + async.auto({ + get_coupon: function (cb) { mongoose.model('Coupon').findById(cc.validate(code), cb); }, - function(coupon, cb) { - _coupon = coupon; - if (!coupon) return cb("Invalid coupon code"); - if (coupon.user) return cb("Coupon already used"); - switch (coupon.event) { + apply_coupon: ['get_coupon', function (cb, results) { + if (!results.get_coupon) return cb("Invalid coupon code"); + if (results.get_coupon.user) return cb("Coupon already used"); + switch (results.get_coupon.event) { case 'wondercon': - user.items.gear.owned.eyewear_special_wondercon_red = true; - user.items.gear.owned.eyewear_special_wondercon_black = true; - user.items.gear.owned.back_special_wondercon_black = true; - user.items.gear.owned.back_special_wondercon_red = true; - user.items.gear.owned.body_special_wondercon_red = true; - user.items.gear.owned.body_special_wondercon_black = true; - user.items.gear.owned.body_special_wondercon_gold = true; + user.items.gear.owned.eyewear_special_wondercon_red = true; + user.items.gear.owned.eyewear_special_wondercon_black = true; + user.items.gear.owned.back_special_wondercon_black = true; + user.items.gear.owned.back_special_wondercon_red = true; + user.items.gear.owned.body_special_wondercon_red = true; + user.items.gear.owned.body_special_wondercon_black = true; + user.items.gear.owned.body_special_wondercon_gold = true; user.extra = {signupEvent: 'wondercon'}; user.save(cb); break; } - }, - function(user, count, cb){ - _user = user; - _coupon.user = user._id; - _coupon.save(cb); - } - ], function(err){ + }], + expire_coupon: ['apply_coupon', function (cb, results) { + results.get_coupon.user = user._id; + results.get_coupon.save(cb); + }] + }, function(err, results){ if (err) return next(err); - next(null,_user); - _coupon = _user = null; + next(null,results.apply_coupon[0]); }) } From bb89a103560147de71811a6630f4cb781c40bcdf Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 21 Dec 2014 13:58:47 -0600 Subject: [PATCH 145/202] feat(event): Wintery Hair Colors --- views/options/profile.jade | 1 + 1 file changed, 1 insertion(+) diff --git a/views/options/profile.jade b/views/options/profile.jade index cf8fef0e25..715b32072f 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -63,6 +63,7 @@ mixin customizeProfile(mobile) button(type='button', ng-if='user.purchased.hair.color.#{color}', class='customize-option hair hair_bangs_1_#{color}', ng-click='unlock("hair.color.#{color}")') +buyPref('hair.color', ['rainbow','yellow','green','purple','blue','TRUred'], 'rainbowColors') +buyPref('hair.color', ['candycorn','ghostwhite','halloween','midnight','pumpkin','zombie'], 'hauntedColors', 'disabled') + +buyPref('hair.color', ['aurora','festive','hollygreen','peppermint','snowy','winterstar'], 'winteryColors') li.customize-menu menu(label=env.t('bodyHair')) From 833ff90a59eaf948c0dc0bd30d5fd7ea67bec444 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 21 Dec 2014 15:41:32 -0600 Subject: [PATCH 146/202] chore(news): Wonderland Bailey 1 --- views/shared/new-stuff.jade | 52 +++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index 8162251f90..76188b0e7b 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -1,28 +1,52 @@ -h5 ANDROID APP UPDATE, SEASONAL SHOP, AND WINTER PLOT-LINE CONTINUES +h5 WINTER WONDERLAND BEGINS! Winter Class Outfits, Wintery Hair Colors, and NPC Decorations! tr td - h5 Android App Update: December Art and Buying Gems! - p The December backgrounds and penguin pet quest are now visible in the Android mobile app! Also, we’ve made it possible to buy gems directly from the app. Now you don’t have to switch to the website to stock up! - p You can get the Android app here! We will announce when the iOS app is available as well. - p.small.muted by negue + h5 Winter Wonderland Begins! + p Winter has arrived, and the snow is gently drifting down over Habit City. Come celebrate with us! tr td - h5 Seasonal Shop Tab - .seasonalshop_closed.pull-right - p Looks like a new tab has appeared under Inventory - the Seasonal Shop! It's still closed, but I've heard a rumor that it will open soon... - p.small.muted by SabreCat and Lemoness + h5 Winter Class Outfits + .promo_winterclasses2015.pull-right + p From now until January 31st, limited edition outfits are available in the Rewards column. Depending on your class, you can be a Soothing Skater, Mage of the North, Gingerbread Warrior, or Icicle Drake! You'd better get productive to earn enough gold before they disappear. Good luck! + p.small.muted by Lemoness tr td - h5 Winter Plot-Line Continues - p Lemoness bursts into the Tavern, shaking icicles off her hat. "The StoĂ¯kalm Steppes are completely abandoned!" she says, gulping the cup of tea that Daniel the Barkeep offers her. "No people milling about, no mounts and pets playing in the snow - and when I tried to fly closer, my dragon spooked and refused to land!" - p A cloaked figure in the corner steps into the fire light - SabreCat, a powerful adventurer from the north. "The StoĂ¯kalm Steppes are the last home of many animals that have long since gone extinct elsewhere," he says. "The stoic StoĂ¯kalmers would never flee their lands unless something was threatening their pets and mounts!" - p He turns to Lemoness. "I can speak the language of the northern beasts. I'll try to contact the roaming sabertooth prides to see if they know what happened." As he lopes off into the distance, a cold wind begins to blow. - p.muted Missed the first part of the Winter Plot-Line? Read it here. + h5 Wintery Hair Colors + .promo_winteryhair.pull-right + p The Seasonal Edition Wintery Hair Colors are now available for purchase in the avatar customizations page! Now you can dye your avatar's hair Holly Green, Winter Star, Snowy, Peppermint, Aurora, or Festive. + p Seasonal Edition items recur unchanged every year, but they are only available to purchase during a short period of time. This is different from Limited Edition Items, which only recur if something is changed, such as the art or the price. These hair colors may remind some of you of the Holiday Hair Colors that were available last winter. The Holiday Hair Colors have been Retired in favor of the similar Seasonal Edition Wintery Hair Colors. Read more about the difference between Seasonal and Limited Edition items here! + p.small.muted by Lemoness, crystalphoenix, and mariahm + tr + td + h5 NPC Decorations + .npc_alex.pull-right + p Looks like the NPCs are really getting in to the cheery winter mood around the site. Who wouldn't? After all, there's plenty more to come! + p.small.muted by Lemoness hr a(href='/static/old-news', target='_blank') Read older news mixin oldNews + h5 12/17/2014 - Android App Update, Seasonal Shop, and Winter Plot-Line Continues + tr + td + h5 Android App Update: December Art and Buying Gems! + p The December backgrounds and penguin pet quest are now visible in the Android mobile app! Also, we’ve made it possible to buy gems directly from the app. Now you don’t have to switch to the website to stock up! + p You can get the Android app here! We will announce when the iOS app is available as well. + p.small.muted by negue + tr + td + h5 Seasonal Shop Tab + .seasonalshop_closed.pull-right + p Looks like a new tab has appeared under Inventory - the Seasonal Shop! It's still closed, but I've heard a rumor that it will open soon... + p.small.muted by SabreCat and Lemoness + tr + td + h5 Winter Plot-Line Continues + p Lemoness bursts into the Tavern, shaking icicles off her hat. "The StoĂ¯kalm Steppes are completely abandoned!" she says, gulping the cup of tea that Daniel the Barkeep offers her. "No people milling about, no mounts and pets playing in the snow - and when I tried to fly closer, my dragon spooked and refused to land!" + p A cloaked figure in the corner steps into the fire light - SabreCat, a powerful adventurer from the north. "The StoĂ¯kalm Steppes are the last home of many animals that have long since gone extinct elsewhere," he says. "The stoic StoĂ¯kalmers would never flee their lands unless something was threatening their pets and mounts!" + p He turns to Lemoness. "I can speak the language of the northern beasts. I'll try to contact the roaming sabertooth prides to see if they know what happened." As he lopes off into the distance, a cold wind begins to blow. + p.muted Missed the first part of the Winter Plot-Line? Read it here. h5 12/9/2014 - Penguin Pet Quest and Winter Plot-Line tr td From 07fbcbcb08f55161d33438396ecc307f6bbc98ce Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 21 Dec 2014 20:53:03 -0600 Subject: [PATCH 147/202] Revert "fix(phantom): downgrade to node 0.10.22 due to CERT error, see" This reverts commit dc18569e78201dd5ee736483721a78ca3a9eb0e9. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b6b97ba199..6f14a60e51 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "www.habitrpg.com" ], "engines": { - "node": "0.10.22", + "node": "0.10.x", "npm": "1.2.x" }, "scripts": { From 652ce10e2246a7b7a9e699f9aa005999b40f69b4 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 22 Dec 2014 19:48:03 +0100 Subject: [PATCH 148/202] feat(flag-chat): start implemnting email --- config.json.example | 1 + src/controllers/groups.js | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/config.json.example b/config.json.example index 74e2f0822c..e34d187838 100644 --- a/config.json.example +++ b/config.json.example @@ -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", diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 49ef84771f..9cc4d7119d 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -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; /* @@ -281,6 +283,27 @@ api.flagChatMessage = function(req, res, next){ 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: null}, + {name: "REPORTER_UUID", content: null}, + {name: "REPORTER_EMAIL", content: null}, + {name: "REPORTER_MODAL_URL", content: null}, + + {name: "AUTHOR_USERNAME", content: null}, + {name: "AUTHOR_UUID", content: null}, + {name: "AUTHOR_EMAIL", content: null}, + {name: "AUTHOR_MODAL_URL", content: null}, + + {name: "GROUP_NAME", content: null}, + {name: "GROUP_TYPE", content: null}, + {name: "GROUP_ID", content: null}, + {name: "GROUP_URL", content: null}, + ]); + } return res.send(204); }); } From 7aff280a71b85d81daf47491cb4466e0f7c68879 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 22 Dec 2014 19:56:11 +0100 Subject: [PATCH 149/202] feat(flag-chat): add some variables --- src/controllers/groups.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 9cc4d7119d..2db1b447b0 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -288,19 +288,19 @@ api.flagChatMessage = function(req, res, next){ {name: "MESSAGE_TIME", content: message.timestamp}, {name: "MESSAGE_TEXT", content: message.text}, - {name: "REPORTER_USERNAME", content: null}, - {name: "REPORTER_UUID", content: null}, - {name: "REPORTER_EMAIL", content: null}, + {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: null}, - {name: "AUTHOR_USERNAME", content: null}, - {name: "AUTHOR_UUID", content: null}, + {name: "AUTHOR_USERNAME", content: message.user}, + {name: "AUTHOR_UUID", content: message.uuid}, {name: "AUTHOR_EMAIL", content: null}, {name: "AUTHOR_MODAL_URL", content: null}, - - {name: "GROUP_NAME", content: null}, - {name: "GROUP_TYPE", content: null}, - {name: "GROUP_ID", content: null}, + + {name: "GROUP_NAME", content: group.name}, + {name: "GROUP_TYPE", content: group.type}, + {name: "GROUP_ID", content: group._id}, {name: "GROUP_URL", content: null}, ]); } From 2d4c5e7b98152b215ac875acef919bc3ae3896de Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 22 Dec 2014 20:02:22 +0100 Subject: [PATCH 150/202] feat(flag-chat): fetch author and add some group variables --- src/controllers/groups.js | 75 +++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 2db1b447b0..dd7530623b 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -266,46 +266,51 @@ api.flagChatMessage = function(req, res, next){ 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."}); - // 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){ + User.findOne({_id: message.uuid}, {auth: 1}, function(err, author){ 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: null}, + // 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; - {name: "AUTHOR_USERNAME", content: message.user}, - {name: "AUTHOR_UUID", content: message.uuid}, - {name: "AUTHOR_EMAIL", content: null}, - {name: "AUTHOR_MODAL_URL", content: null}, - - {name: "GROUP_NAME", content: group.name}, - {name: "GROUP_TYPE", content: group.type}, - {name: "GROUP_ID", content: group._id}, - {name: "GROUP_URL", content: null}, - ]); + // 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++ } - return res.send(204); + + 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: null}, + + {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: null}, + + {name: "GROUP_NAME", content: group.name}, + {name: "GROUP_TYPE", content: group.type}, + {name: "GROUP_ID", content: group._id}, + {name: "GROUP_URL", content: group.type === 'guild' ? (nconf.get('BASE_URL')+ '/#/options/groups/guilds/' + group._id) : 'party'}, + ]); + } + return res.send(204); + }); }); + } api.clearFlagCount = function(req, res, next){ From 37b9e5012a663c674d4c3bb58bfca0ebdbd976ba Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 22 Dec 2014 13:22:42 -0600 Subject: [PATCH 151/202] Added links to user modals --- src/controllers/groups.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index dd7530623b..82b9b58b0b 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -294,12 +294,12 @@ api.flagChatMessage = function(req, res, next){ {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: 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: 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}, From 4c785e84590c2fa6d69e697e6240020152599346 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 22 Dec 2014 21:09:27 +0100 Subject: [PATCH 152/202] fix(flag-chat): correct url for tavern flags --- src/controllers/groups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 82b9b58b0b..05cc7f1c7a 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -304,7 +304,7 @@ api.flagChatMessage = function(req, res, next){ {name: "GROUP_NAME", content: group.name}, {name: "GROUP_TYPE", content: group.type}, {name: "GROUP_ID", content: group._id}, - {name: "GROUP_URL", content: group.type === 'guild' ? (nconf.get('BASE_URL')+ '/#/options/groups/guilds/' + group._id) : 'party'}, + {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); From c4143c78057d13b07c75c3e4954a8a235ec52872 Mon Sep 17 00:00:00 2001 From: Verabird Date: Mon, 22 Dec 2014 15:19:53 -0500 Subject: [PATCH 153/202] added variable for date display and checking line ending issue --- views/shared/tasks/task.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 0466ff91e1..e8050f1f90 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -4,7 +4,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // Due Date span(ng-if='task.type=="todo" && task.date') - span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{moment(task.date).format('MM/DD')}} + span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{moment(task.date).format(env.t('shortdate'))}} // Streak |   span(ng-show='task.streak') {{task.streak}}  From 42cb93c67313f90be3de84dc8f0201603d2abb67 Mon Sep 17 00:00:00 2001 From: Verabird Date: Mon, 22 Dec 2014 22:23:11 -0500 Subject: [PATCH 154/202] Add variables for medium-length date display --- views/shared/modals/members.jade | 4 ++-- views/shared/tasks/task.jade | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 595929c819..ba0f1449aa 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -15,12 +15,12 @@ script(type='text/ng-template', id='modals/member.html') |  =env.t('memberSince') |  - | {{::timestamp(profile.auth.timestamps.created)}} - + | {{::profile.auth.timestamps.created | date:env.t('mediumdate')}} - li(ng-show='::profile.auth.timestamps.loggedin') |  =env.t('lastLoggedIn') |  - | {{::timestamp(profile.auth.timestamps.loggedin)}} - + | {{::profile.auth.timestamps.loggedin | date:env.t('mediumdate')}} - h3=env.t('stats') .label.label-info {{:: {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer")}[profile.stats.class] }} include ../profiles/stats diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index e8050f1f90..9a6a7e8561 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -184,7 +184,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // if Todos, the due date fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') legend.option-title=env.t('dueDate') - input.option-content.datepicker(type='text', datepicker-popup='MM/dd/yyyy', ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') + input.option-content.datepicker(type='text', datepicker-popup=env.t('mediumdate'), ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') // Tags fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') From 635ebf626bdbef17531cd945726a9837457d598c Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Tue, 23 Dec 2014 18:54:56 +0100 Subject: [PATCH 155/202] fix(flag-email): correctly format date --- src/controllers/groups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 05cc7f1c7a..3891c70618 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -288,7 +288,7 @@ api.flagChatMessage = function(req, res, next){ 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_TIME", content: (new Date(message.timestamp)).toString()}, {name: "MESSAGE_TEXT", content: message.text}, {name: "REPORTER_USERNAME", content: user.profile.name}, From e1eaefa9430a6a4cbe0fd2334e1d427adefa6c37 Mon Sep 17 00:00:00 2001 From: breiste Date: Wed, 24 Dec 2014 00:14:08 +0100 Subject: [PATCH 156/202] Fix #4296 (Subscription "Update Card" message is cut off) --- public/js/services/paymentServices.js | 6 +++--- views/options/settings.jade | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/js/services/paymentServices.js b/public/js/services/paymentServices.js index 4fc1b24e84..0fd7682512 100644 --- a/public/js/services/paymentServices.js +++ b/public/js/services/paymentServices.js @@ -50,9 +50,9 @@ function($rootScope, User, $http, Content) { StripeCheckout.open({ key: window.env.STRIPE_PUB_KEY, address: false, - name: 'Update', - description: 'Update the card to be charged for your subscription', - panelLabel: 'Update Card', + name: window.env.t('subUpdateTitle'), + description: window.env.t('subUpdateDescription'), + panelLabel: window.env.t('subUpdateCard'), token: function(data) { var url = '/stripe/subscribe/edit'; $http.post(url, data).success(function() { diff --git a/views/options/settings.jade b/views/options/settings.jade index ef2727b4f6..57708bf362 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -261,5 +261,5 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template') a.btn.btn-primary(ng-click='Payments.showStripe({subscription:_subscription.months})', ng-disabled='!_subscription.months') Card a.btn.btn-warning(href='/paypal/subscribe?_id={{user._id}}&apiToken={{user.apiToken}}&sub={{_subscription.months}}', ng-disabled='!_subscription.months') PayPal div(ng-if='user.purchased.plan.customerId') - .btn.btn-primary(ng-if='!user.purchased.plan.dateTerminated && user.purchased.plan.paymentMethod=="Stripe"', ng-click='Payments.showStripeEdit()') Update Card + .btn.btn-primary(ng-if='!user.purchased.plan.dateTerminated && user.purchased.plan.paymentMethod=="Stripe"', ng-click='Payments.showStripeEdit()')=env.t('subUpdateCard') .btn.btn-sm.btn-danger(ng-if='!user.purchased.plan.dateTerminated', ng-click='Payments.cancelSubscription()')=env.t('cancelSub') \ No newline at end of file From 8cf3cd66ccd35cf0171a08a696ff67b5243d4235 Mon Sep 17 00:00:00 2001 From: benmanley Date: Wed, 24 Dec 2014 09:52:50 +0000 Subject: [PATCH 157/202] fix(avatar): prevent avatar from obscuring name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increase z-index of avatar-name so that avatar image doesn’t obscure it when riding a mount --- public/css/avatar.styl | 1 + 1 file changed, 1 insertion(+) diff --git a/public/css/avatar.styl b/public/css/avatar.styl index ea00c40e02..13bd58246d 100644 --- a/public/css/avatar.styl +++ b/public/css/avatar.styl @@ -30,6 +30,7 @@ future re: pets and whatnot, this is just temporary. .avatar-level, .avatar-name position: absolute @extend $hrpg-label + z-index: 2 .avatar-level bottom: 4px right: 4px From 118ba88d3ddfaadb4d72952c8f83263d7a48586c Mon Sep 17 00:00:00 2001 From: benmanley Date: Wed, 24 Dec 2014 09:53:32 +0000 Subject: [PATCH 158/202] fix(tasks): improve UI consistency and layout * Match UI elements with rest of site * Tidy up layout --- public/css/global-modules.styl | 12 +- public/css/tasks.styl | 283 ++++++++++++++++++++++----------- views/shared/tasks/task.jade | 88 +++++----- 3 files changed, 244 insertions(+), 139 deletions(-) diff --git a/public/css/global-modules.styl b/public/css/global-modules.styl index cdac07da4b..0629a881df 100644 --- a/public/css/global-modules.styl +++ b/public/css/global-modules.styl @@ -71,7 +71,7 @@ hrpg-button-color-mixin($hrpg-button-color) color: #fff !important; $hrpg-button-master list-style: none - > a, > button, > input + > a, > button, > input, label::after display: inline-block !important padding: 0.25em 0.5em !important text-decoration:none @@ -86,7 +86,7 @@ $hrpg-button-master background-color: #aaa !important $hrpg-button @extend $hrpg-button-master - > a, > button, > input + > a, > button, > input, label::after border: 1px solid #ccc !important border-radius: 0.382em !important $hrpg-button-call-to-action @@ -111,7 +111,7 @@ $hrpg-button-with-input height: 2em input border: 1px solid #ccc - border-radius: 0.382em 0em 0em 0.382em + border-radius: 0.382em 0em 0em 0.382em !important padding-left: 0.618em background-color: #fff !important -webkit-appearance: none @@ -150,15 +150,17 @@ $hrpg-button-bar border-right: 1px solid darken(#fff,3.82%) float:left word-wrap: break-word + button + border: none li:nth-of-type(2) border-radius: 0.382em 0em 0em 0.382em li:first-of-type border-radius: 0.382em 0em 0em 0.382em - a + a, button border-radius: 0.382em 0em 0em 0.382em li:last-of-type border-right: none - a + a, button border-radius: 0em 0.382em 0.382em 0em $hrpg-button-bar-with-heading diff --git a/public/css/tasks.styl b/public/css/tasks.styl index 2f54c9365f..7e50dfbcf3 100644 --- a/public/css/tasks.styl +++ b/public/css/tasks.styl @@ -8,29 +8,99 @@ $stages = (worst $worst) (worse $worse) (bad $bad) (neutral $neutral) (good $goo // for each color stage, generate a named class w/ the appropriate color for $stage in $stages - .color-{$stage[0]} - background-color: $stage[1] - .action-yesno label, - .task-action-btn - background-color: darken($stage[1], 30%) - &:hover, &:focus - background-color: darken($stage[1], 40%) - .task-options + .task-column:not(.rewards) + .color-{$stage[0]}:not(.completed) background-color: $stage[1] - + border: 1px solid shade($stage[1],10%) + .priority-multiplier, .task-attributes, .repeat-days + li + hrpg-button-color-mixin($stage[1]) + button + &.active + box-shadow: inset 0 0 0 1px darken($stage[1],40%) !important + background-color: darken($stage[1],5%) !important + &:focus + border: 1px solid darken($stage[1],30%) + outline: 0 + .plusminus + .task-checker + label:after + border: 1px solid darken($stage[1], 30%) !important + input[type=checkbox]:checked + label:after + //border: 1px solid darken($stage[1], 50%) !important + box-shadow: inset 0 0 0 1px darken($stage[1],40%) !important + background-color: darken($stage[1],5%) !important + .save-close, .task-checklist-edit li + hrpg-button-color-mixin(darken($stage[1],5%)) + button + &:focus + border: 1px solid darken($stage[1],30%) + outline: 0 + .action-yesno label, + .task-action-btn + background-color: darken($stage[1], 30%) + &:hover, &:focus + background-color: darken($stage[1], 40%) + .task-options + background-color: $stage[1] + .option-group:not(.task-checklist) + border-bottom: 1px solid darken($stage[1], 15%) + .option-content + border-color: darken($stage[1], 16.18%) !important + &:hover + border-color: darken($stage[1], 32.8%) !important + &:focus + border-color: darken($stage[1], 61.8%) !important + outline: none; + // completed has to be outside the loop to override the color class .completed + color: darken($completed,30%) background-color: $completed - color: #999 - .glyphicon - color: #333 - .task-checker label - background-color: darken($completed, 30%) + border: 1px solid shade($completed,10%) + .priority-multiplier, .task-attributes, .repeat-days + li + hrpg-button-color-mixin($completed) + button + &.active + box-shadow: inset 0 0 0 1px darken($completed,40%) !important + background-color: darken($completed,5%) !important + &:focus + border: 1px solid darken($completed,30%) + outline: 0 + .plusminus + .task-checker + label:after + border: 1px solid darken($completed, 30%) !important + input[type=checkbox]:checked + label:after + box-shadow: inset 0 0 0 1px darken($completed,40%) !important + background-color: darken($completed,5%) !important + .save-close, .task-checklist-edit li + hrpg-button-color-mixin(darken($completed,5%)) + button + &:focus + border: 1px solid darken($completed,30%) + outline: 0 + .action-yesno label, .task-action-btn background-color: darken($completed, 30%) + &:hover, &:focus + background-color: darken($completed, 40%) + .task-options + background-color: $completed + .option-group:not(.task-checklist) + border-bottom: 1px solid darken($completed, 15%) + .option-content + border-color: darken($completed, 16.18%) !important + &:hover + border-color: darken($completed, 32.8%) !important + &:focus + border-color: darken($completed, 61.8%) !important + outline: 0; -.reward +.reward, .reward-item background-color: white + border: 1px solid $completed !important // 2. Columns & Tasks // =================== @@ -70,7 +140,7 @@ for $stage in $stages // "Add new task" form // ----------------- .task-add - margin-top: 1.618em + margin-top: 1.5em @extend $hrpg-button-with-input hrpg-button-color-mixin($color-options-submenu) input, button @@ -90,7 +160,7 @@ for $stage in $stages padding: 0 min-height: 3em margin-bottom: 0.75em - box-shadow: inset 0 0 1px rgba(0,0,0,0.5) + // box-shadow: inset 0 0 1px rgba(0,0,0,0.5) position: relative &:hover cursor: move @@ -99,6 +169,16 @@ for $stage in $stages label font-weight: 400 + + input[type="text"], input[type="number"], textarea.option-content + border: 1px solid #aaa + border-radius: 0.382em + padding-left: 0.618em + background-color: #fff !important + -webkit-appearance: none + -moz-appearance: none + appearance: none + box-shadow: none //remove red glow in Firefox // task content .task-text @@ -107,7 +187,7 @@ for $stage in $stages line-height: 1.4 word-wrap: break-word - markdown > ul > li // Otherwise, + gets turned into a bullet and gets maksed by buttons + markdown > ul > li // Otherwise, + gets turned into a bullet and gets masked by buttons margin-left:20px span.emoji @@ -176,28 +256,21 @@ for $stage in $stages cursor: pointer // plus/minus checkbox -.action-plusminus - label - margin-right: 1.5em - label:after - font-size: 1.41em - width: 2.12765em - height: 2.12765em - line-height: 2.12765 - text-align: center - display: inline-block - vertical-align top - opacity: 0.3 - background-color: #ddd - outline: 1px solid rgba(0,0,0,0.5) - color: #222 - label[for$="plus"]:after - content: '+' - label[for$="minus"]:after - content: '−' - - &.select-toggle input[type=checkbox]:checked + label:after - opacity: 1 +.plusminus + text-align: center + .task-checker + font-size: 1.5em // 1.41em + @extend $hrpg-button + margin-bottom: 0.5em + &:first-of-type + margin-right: 0.5em + label:after + width: 2em + line-height: 1.5 + label[for$="plus"]:after + content: '+' + label[for$="minus"]:after + content: '−' // yes/no checkbox .action-yesno @@ -229,9 +302,9 @@ for $stage in $stages label:hover:after, label:focus:after content: "\E013" - font-family 'Glyphicons Halflings' - border none - margin 0 + font-family: "Glyphicons Halflings" + border: none + margin: 0 line-height: 1.714285714em height: 1.714285714em width: 1.714285714em @@ -243,9 +316,9 @@ for $stage in $stages // completed icon input[type=checkbox]:checked + label:after content: "\E013" - font-family 'Glyphicons Halflings' - border none - margin 0 + font-family: "Glyphicons Halflings" + border: none + margin: 0 line-height: 1.714285714em height: 1.714285714em width: 1.714285714em @@ -297,36 +370,36 @@ form //background-color: .option-group - border-bottom: 1px solid rgba(0,0,0,0.1) + //border-bottom: 1px solid rgba(0,0,0,0.1) padding: 0 0 1em margin-bottom: 1em .option-title font-size: 1em margin: 0 0 0.5em - line-height: 1.5 + line-height: 2.5 border: 0 padding: 0 - color: #333 - font-style: italic - &:not(.mega):after - content: ':' + //color: #333 + //font-style: italic + font-weight: bold + text-align: center &.mega cursor: pointer - font-style: normal - font-weight: bold - text-decoration: underline + //font-style: normal + &:after + font-family: "Glyphicons Halflings" + font-size: 0.75em + content: "\E114" + padding-left: 0.75em + //&:not(.mega):after + //content: ':' .option-content height: 2.5em width: 100% margin: 0 0 1em padding: 0 0 0 0.5em - outline: 0 - border: 1px solid rgba(0,0,0,0.2) - box-shadow: none - border-radius: 0 - font-family: 'Lato', sans-serif //bootstrap override textarea.option-content height: 5em @@ -395,47 +468,66 @@ form .task-options .task-action-btn.tile margin: 3px - + + form + padding-bottom: 1em + .priority-multiplier, .task-attributes, .repeat-days + text-align: center + li + @extend $hrpg-button + margin-bottom: 1em + margin-right: 0.75em + display: inline-block + &:last-of-type + margin-right: 0 + .repeat-days + li + button + min-width: 2.5em + .save-close + text-align: center + @extend $hrpg-button // todos ui // -------- // Checklists // -------- -.checklist-form - /* Make an input blend into its parent */ - input.inline-edit - reset-box-model() - reset-font() - line-height: inherit - text-align: inherit - background-color:inherit - box-shadow: none - width: 80% +.task-checklist-edit + > ul + text-align: center + li + @extend $hrpg-button + button + span + margin-right: 0.5em + > form + li + @extend $hrpg-button-with-input + @extend $clearfix + input + width: 70% - /* Add interaction cues on hover and focus */ - input.inline-edit:hover,input.inline-edit:focus - background-color: #FFD - transition: background-color 0.5s + /* Add interaction cues on hover and focus */ + &:hover,&:focus + background-color: #FFD + transition: background-color 0.5s - .checklist-icons - opacity:0 - width: 5% - - li - min-height: 20px - padding-left: 10px - margin-bottom: 5px - - [type="checkbox"] - margin: 0px 10px 3px 0px - width: 4% - li:hover .checklist-icons - opacity:1 + .checklist-icon + opacity:0.25 + text-align: center + line-height: 1.5 + float: left + padding: 0.25em 0.5em + + li + margin-bottom: 1em + &:hover .checklist-icon + opacity:1 // Task filters // -------- .task-filter - margin: 1.618em 0 1em 0 + margin: 1.5em 0 1em 0 hrpg-button-bar-mixin($color-tasks) @extend $hrpg-button-bar width: 100% @@ -451,16 +543,18 @@ form width: 100% height: 100% .rewards + .task + min-height: 3.07em // Temp fix for slight misalignment of btn-buy .task-filter li:nth-child(3n + 1):nth-last-child(2) width: 33.333% li:nth-child(3n + 1):nth-last-child(2) + li width: 66.666% - .btn-buy word-wrap: break-word padding-left: 0.2em text-align: left + border:none span position: relative left: -2px @@ -476,9 +570,4 @@ form margin: auto .shop_gold + .reward-cost line-height: 1 - top: -0.1em - -.repeat-days - a - border-radius: 0.382em - + top: -0.1em diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 0466ff91e1..96f792ebcd 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -82,7 +82,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s //-| {{task.text}} div(ng-if='task.checklist && !$state.includes("options.social.challenges") && !task.collapseChecklist && !task._editing') - fieldset.option-group + fieldset.option-group.task-checklist label.checkbox(ng-repeat='item in task.checklist') input(type='checkbox',ng-model='item.completed',ng-change='saveTask(task,true)') markdown(ng-model='item.text',target='_blank') @@ -122,25 +122,24 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s a(ng-click="unlink(task, 'remove-all')")=env.t('removeThem') // Checklists - div(ng-if='!$state.includes("options.social.challenges")') - button.btn.btn-default.btn-sm(ng-if='!task.checklist[0] && (task.type=="daily" || task.type=="todo")',ng-click='addChecklist(task)') - span.glyphicon.glyphicon-tasks - |     - =env.t('addChecklist') - form.checklist-form(ng-if='task.checklist') - fieldset.option-group.task-checklist(ng-if='!$state.includes("options.social.challenges")') + .task-checklist-edit(ng-if='!$state.includes("options.social.challenges")') + ul + li + button(type='button', ng-if='!task.checklist[0] && (task.type=="daily" || task.type=="todo")',ng-click='addChecklist(task)') + span.glyphicon.glyphicon-tasks + span=env.t('addChecklist') + form(ng-if='task.checklist') + fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') legend.option-title span.hint(popover=env.t('checklistText'),popover-trigger='mouseenter',popover-placement='bottom')=env.t('checklist') - ul(hrpg-sort-checklist).list-unstyled + ul(hrpg-sort-checklist) li(ng-repeat='item in task.checklist') - a.pull-right.checklist-icons(ng-click='removeChecklistItem(task,$event,$index,true)') - span.glyphicon.glyphicon-trash(tooltip=env.t('delete')) - span.checklist-icons.glyphicon.glyphicon-resize-vertical() - |   - input(type='checkbox',ng-model='item.completed',ng-change='saveTask(task,true)') + //input(type='checkbox',ng-model='item.completed',ng-change='saveTask(task,true)') //-,ng-blur='saveTask(task,true)') - input.inline-edit(type='text',ng-model='item.text',ui-keydown="{'8 46':'removeChecklistItem(task,$event,$index)'}",ui-keyup="{'13':'addChecklistItem(task,$event,$index)','38 40':'navigateChecklist(task,$index,$event)'}") - + span.checklist-icon.glyphicon.glyphicon-resize-vertical() + input(type='text',ng-model='item.text',ui-keydown="{'8 46':'removeChecklistItem(task,$event,$index)'}",ui-keyup="{'13':'addChecklistItem(task,$event,$index)','38 40':'navigateChecklist(task,$index,$event)'}") + button(ng-click='removeChecklistItem(task,$event,$index,true)') + span.glyphicon.glyphicon-trash(tooltip=env.t('delete')) form(ng-submit='saveTask(task,false,true)') // text & notes @@ -152,27 +151,34 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s textarea.option-content(rows='3', ng-model='task.notes', ng-model-options="{debounce: 1000}") // if Habit, plus/minus command options - fieldset.option-group(ng-if='task.type=="habit" && !task.challenge.id') + fieldset.option-group.plusminus(ng-if='task.type=="habit" && !task.challenge.id') legend.option-title=env.t('direction/Actions') - span.task-checker.action-plusminus.select-toggle + span.task-checker input.visuallyhidden.focusable(id='{{obj._id}}_{{task.id}}-option-plus', type='checkbox', ng-model='task.up') label(for='{{obj._id}}_{{task.id}}-option-plus') - span.task-checker.action-plusminus.select-toggle + span.task-checker input.visuallyhidden.focusable(id='{{obj._id}}_{{task.id}}-option-minus', type='checkbox', ng-model='task.down') label(for='{{obj._id}}_{{task.id}}-option-minus') // if Daily, calendar fieldset(ng-if='::task.type=="daily"', class="option-group") legend.option-title=env.t('repeat') - .task-controls.tile-group.repeat-days(bindonce) + ul.repeat-days(bindonce) // note, does not use data-toggle="buttons-checkbox" - it would interfere with our own click binding - button.task-action-btn.tile(ng-class='{active: task.repeat.su}', type='button', ng-click='task.challenge.id || (task.repeat.su = !task.repeat.su)') {{::moment.weekdaysMin(0)}} - button.task-action-btn.tile(ng-class='{active: task.repeat.m}', type='button', ng-click='task.challenge.id || (task.repeat.m = !task.repeat.m)') {{::moment.weekdaysMin(1)}} - button.task-action-btn.tile(ng-class='{active: task.repeat.t}', type='button', ng-click='task.challenge.id || (task.repeat.t = !task.repeat.t)') {{::moment.weekdaysMin(2)}} - button.task-action-btn.tile(ng-class='{active: task.repeat.w}', type='button', ng-click='task.challenge.id || (task.repeat.w = !task.repeat.w)') {{::moment.weekdaysMin(3)}} - button.task-action-btn.tile(ng-class='{active: task.repeat.th}', type='button', ng-click='task.challenge.id || (task.repeat.th = !task.repeat.th)') {{::moment.weekdaysMin(4)}} - button.task-action-btn.tile(ng-class='{active: task.repeat.f}', type='button', ng-click='task.challenge.id || (task.repeat.f= !task.repeat.f)') {{::moment.weekdaysMin(5)}} - button.task-action-btn.tile(ng-class='{active: task.repeat.s}', type='button', ng-click='task.challenge.id || (task.repeat.s = !task.repeat.s)') {{::moment.weekdaysMin(6)}} + li + button(ng-class='{active: task.repeat.su}', type='button', ng-click='task.challenge.id || (task.repeat.su = !task.repeat.su)') {{::moment.weekdaysMin(0)}} + li + button(ng-class='{active: task.repeat.m}', type='button', ng-click='task.challenge.id || (task.repeat.m = !task.repeat.m)') {{::moment.weekdaysMin(1)}} + li + button(ng-class='{active: task.repeat.t}', type='button', ng-click='task.challenge.id || (task.repeat.t = !task.repeat.t)') {{::moment.weekdaysMin(2)}} + li + button(ng-class='{active: task.repeat.w}', type='button', ng-click='task.challenge.id || (task.repeat.w = !task.repeat.w)') {{::moment.weekdaysMin(3)}} + li + button(ng-class='{active: task.repeat.th}', type='button', ng-click='task.challenge.id || (task.repeat.th = !task.repeat.th)') {{::moment.weekdaysMin(4)}} + li + button(ng-class='{active: task.repeat.f}', type='button', ng-click='task.challenge.id || (task.repeat.f= !task.repeat.f)') {{::moment.weekdaysMin(5)}} + li + button(ng-class='{active: task.repeat.s}', type='button', ng-click='task.challenge.id || (task.repeat.s = !task.repeat.s)') {{::moment.weekdaysMin(6)}} // if Reward, pricing fieldset.option-group.option-short(ng-if='task.type=="reward" && !task.challenge.id') @@ -199,10 +205,13 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s fieldset.option-group.advanced-option(ng-class="{visuallyhidden: task._advanced}") legend.option-title a.hint.priority-multiplier-help(href='https://trello.com/card/priority-multiplier/50e5d3684fe3a7266b0036d6/17', target='_blank', popover-title=env.t('difficultyHelpTitle'), popover-trigger='mouseenter', popover=env.t('difficultyHelpContent'))=env.t('difficulty') - .task-controls.tile-group.priority-multiplier - button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1 || !task.priority}', ng-click='task.challenge.id || (task.priority=1)')=env.t('easy') - button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1.5}', ng-click='task.challenge.id || (task.priority=1.5)')=env.t('medium') - button.task-action-btn.tile(type='button', ng-class='{active: task.priority==2}', ng-click='task.challenge.id || (task.priority=2)')=env.t('hard') + ul.priority-multiplier + li + button(type='button', ng-class='{active: task.priority==1 || !task.priority}', ng-click='task.challenge.id || (task.priority=1)')=env.t('easy') + li + button(type='button', ng-class='{active: task.priority==1.5}', ng-click='task.challenge.id || (task.priority=1.5)')=env.t('medium') + li + button(type='button', ng-class='{active: task.priority==2}', ng-click='task.challenge.id || (task.priority=2)')=env.t('hard') //span(ng-if='task.type=="daily" && !task.challenge.id') span(ng-if='task.type=="daily"') legend.option-title.pull-left=env.t('restoreStreak') @@ -210,12 +219,17 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s div(ng-if='(user.preferences.allocationMode == "taskbased" && user.preferences.automaticAllocation) || $state.is("options.social.challenges")') legend.option-title.pull-left=env.t('attributes') - .task-controls.tile-group.task-attributes - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="str"}', ng-click='task.attribute="str"')=env.t('physical') - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="int"}', ng-click='task.attribute="int"')=env.t('mental') - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="con"}', ng-click='task.attribute="con"')=env.t('social') - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="per"}', ng-click='task.attribute="per"', popover=env.t('otherExamples'), popover-trigger='mouseenter', popover-placement='top')=env.t('other') + ul.task-attributes + li + button(type='button', ng-class='{active: task.attribute=="str"}', ng-click='task.attribute="str"')=env.t('physical') + li + button(type='button', ng-class='{active: task.attribute=="int"}', ng-click='task.attribute="int"')=env.t('mental') + li + button(type='button', ng-class='{active: task.attribute=="con"}', ng-click='task.attribute="con"')=env.t('social') + li + button(type='button', ng-class='{active: task.attribute=="per"}', ng-click='task.attribute="per"', popover=env.t('otherExamples'), popover-trigger='mouseenter', popover-placement='top')=env.t('other') - button.task-action-btn.tile.spacious(type='submit')=env.t('saveAndClose') + .save-close + button(type='submit')=env.t('saveAndClose') div(class='{{obj._id}}{{task.id}}-chart', ng-show='charts[obj._id+task.id]') From fdcc22bde93f7e8bad0dbd74218d1cd8bdae8ae2 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Wed, 24 Dec 2014 09:02:03 -0600 Subject: [PATCH 159/202] Closes #4361 --- views/shared/tasks/lists.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/shared/tasks/lists.jade b/views/shared/tasks/lists.jade index 0cc9abe767..e73661adfc 100644 --- a/views/shared/tasks/lists.jade +++ b/views/shared/tasks/lists.jade @@ -106,11 +106,11 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template") .task-controls a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.#{k})') if canceler - span.reward-cost {{Content.spells.special.#{k}.value}} span.shop_gold + span.reward-cost {{Content.spells.special.#{k}.value}} else - span.reward-cost {{user.items.special.#{k}}} span(class='shop_#{k}',style='display:inline-block;vertical-align:top;') + span.reward-cost {{user.items.special.#{k}}} // main content p.task-text {{Content.spells.special.#{k}.text()}} From a8459c2f96c5f8c9cb2d7c014309c3f6ad19d981 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Wed, 24 Dec 2014 11:17:05 -0600 Subject: [PATCH 160/202] Changing button to a element fixed the delete on enter problem --- views/shared/tasks/task.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 96f792ebcd..f96b33cb57 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -138,7 +138,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s //-,ng-blur='saveTask(task,true)') span.checklist-icon.glyphicon.glyphicon-resize-vertical() input(type='text',ng-model='item.text',ui-keydown="{'8 46':'removeChecklistItem(task,$event,$index)'}",ui-keyup="{'13':'addChecklistItem(task,$event,$index)','38 40':'navigateChecklist(task,$index,$event)'}") - button(ng-click='removeChecklistItem(task,$event,$index,true)') + a(ng-click='removeChecklistItem(task,$event,$index,true)') span.glyphicon.glyphicon-trash(tooltip=env.t('delete')) form(ng-submit='saveTask(task,false,true)') From 03f12a371855138ae14bfe401e0058d39a4b00e4 Mon Sep 17 00:00:00 2001 From: Verabird Date: Wed, 24 Dec 2014 22:18:50 -0500 Subject: [PATCH 161/202] Make it possible to choose between MM/dd and dd/MM date format --- public/js/controllers/settingsCtrl.js | 7 +++++++ src/models/user.js | 1 + views/options/settings.jade | 6 ++++++ views/options/social/chat-message.jade | 2 +- views/shared/modals/members.jade | 4 ++-- views/shared/tasks/task.jade | 5 +++-- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 0c0123d1f3..fb1efe6a12 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -65,6 +65,13 @@ habitrpg.controller('SettingsCtrl', User.set({'preferences.language': $scope.language.code}); } + $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy','yyyy/MM/dd']; + $scope.changeDateFormat = function(){ + + var dateFormat= User.user.preferences.dateFormat; + User.set({'preferences.dateFormat': dateFormat}); + } + $scope.reroll = function(){ User.user.ops.reroll({}); $rootScope.$state.go('tasks'); diff --git a/src/models/user.js b/src/models/user.js index 3dbb7f0f29..a9527cd512 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -278,6 +278,7 @@ var UserSchema = new Schema({ automaticAllocation: Boolean, allocationMode: {type:String, enum: ['flat','classbased','taskbased'], 'default': 'flat'}, costume: Boolean, + dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], 'default': 'MM/dd/yyyy'}, sleep: {type: Boolean, 'default': false}, stickyHeader: {type: Boolean, 'default': true}, disableClasses: {type: Boolean, 'default': false}, diff --git a/views/options/settings.jade b/views/options/settings.jade index ef2727b4f6..85f731bd4c 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -36,6 +36,12 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') br strong !=env.t('helpWithTranslation') + + .form-horizontal + h5=env.t('dateFormat') + select.form-control(ng-model='user.preferences.dateFormat', ng-options='DF for DF in availableFormats', ng-change='changeDateFormat()') + + .checkbox label input(type='checkbox', ng-click='hideHeader() ', ng-checked='user.preferences.hideHeader!==true') diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 89ebc181e6..edfd3b476e 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -12,7 +12,7 @@ mixin chatMessages(inbox) // this invisible username label is here to push the message text far enough right that the visible label can be floated to this point without covering up any of the text markdown(ng-model='::message.text') | - - span.muted.time(from-now='::message.timestamp' tooltip="{{::message.timestamp | date:'short'}}") + span.muted.time(from-now='::message.timestamp' tooltip="{{::message.timestamp | date:user.preferences.dateFormat.concat(' HH:mm:ss')}}") unless inbox span a.label.label-default(ng-show='countExists(message.likes)', ng-class='{"label-success":message.likes[user._id]}', ng-click='likeChatMessage(group,message)') +{{countExists(message.likes)}} diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index ba0f1449aa..9f127f9f61 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -15,12 +15,12 @@ script(type='text/ng-template', id='modals/member.html') |  =env.t('memberSince') |  - | {{::profile.auth.timestamps.created | date:env.t('mediumdate')}} - + | {{::profile.auth.timestamps.created | date:user.preferences.dateFormat}} - li(ng-show='::profile.auth.timestamps.loggedin') |  =env.t('lastLoggedIn') |  - | {{::profile.auth.timestamps.loggedin | date:env.t('mediumdate')}} - + | {{::profile.auth.timestamps.loggedin | date:user.preferences.dateFormat}} - h3=env.t('stats') .label.label-info {{:: {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer")}[profile.stats.class] }} include ../profiles/stats diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 9a6a7e8561..6649113941 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -4,7 +4,8 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // Due Date span(ng-if='task.type=="todo" && task.date') - span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{moment(task.date).format(env.t('shortdate'))}} + span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{task.date | date:user.preferences.dateFormat.substr(0,5)}} + //{{::profile.auth.timestamps.created | date:user.preferences.dateFormat}} // Streak |   span(ng-show='task.streak') {{task.streak}}  @@ -184,7 +185,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // if Todos, the due date fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') legend.option-title=env.t('dueDate') - input.option-content.datepicker(type='text', datepicker-popup=env.t('mediumdate'), ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') + input.option-content.datepicker(type='text', datepicker-popup='{{user.preferences.dateFormat}}', ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') // Tags fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') From 0e28f04c909776037ab799c2bb3c44b10ac2c8bf Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Thu, 25 Dec 2014 14:33:11 -0600 Subject: [PATCH 162/202] feat(event): Implement Seasonal Shop --- public/js/controllers/inventoryCtrl.js | 18 ++++++++++++------ views/options/inventory/inventory.jade | 13 ++++++++++--- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index e1b47a47f0..32511b60ef 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -87,16 +87,22 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', } $scope.purchase = function(type, item){ - if (item.key=='spookDust') return User.user.ops.buySpookDust({}); var gems = User.user.balance * 4; - if(gems < item.value) return $rootScope.openModal('buyGems'); - var string = (type == 'hatchingPotions') ? window.env.t('hatchingPotion') : (type == 'eggs') ? window.env.t('eggSingular') : (type == 'quests') ? window.env.t('quest') : (item.key == 'Saddle') ? window.env.t('foodSaddleText').toLowerCase() : (type == 'special') ? item.key : type; // this is ugly but temporary, once the purchase modal is done this will be removed - var message = window.env.t('buyThis', {text: string, price: item.value, gems: gems}) + var string = (type == 'weapon') ? window.env.t('weapon') : (type == 'armor') ? window.env.t('armor') : (type == 'head') ? window.env.t('headgear') : (type == 'shield') ? window.env.t('offhand') : (type == 'hatchingPotions') ? window.env.t('hatchingPotion') : (type == 'eggs') ? window.env.t('eggSingular') : (type == 'quests') ? window.env.t('quest') : (item.key == 'Saddle') ? window.env.t('foodSaddleText').toLowerCase() : type; // this is ugly but temporary, once the purchase modal is done this will be removed + if (type == 'weapon' || type == 'armor' || type == 'head' || type == 'shield') { + if (gems < ((item.specialClass == "wizard") && (item.type == "weapon")) + 1) return $rootScope.openModal('buyGems'); + var message = window.env.t('buyThis', {text: string, price: ((item.specialClass == "wizard") && (item.type == "weapon")) + 1, gems: gems}) + if($window.confirm(message)) + User.user.ops.purchase({params:{type:"gear",key:item.key}}); + } else { + if(gems < item.value) return $rootScope.openModal('buyGems'); + var message = window.env.t('buyThis', {text: string, price: item.value, gems: gems}) + if($window.confirm(message)) + User.user.ops.purchase({params:{type:type,key:item.key}}); + } - if($window.confirm(message)) - User.user.ops.purchase({params:{type:type,key:item.key}}); } $scope.choosePet = function(egg, potion){ diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index d5d254c2a3..eb1544cfab 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -23,13 +23,20 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .container-fluid .stable.row .col-md-2 - .seasonalshop_closed + .seasonalshop_winter2015 .col-md-10 .popover.static-popover.fade.right.in .arrow - h3.popover-title!=env.t('seasonalShopClosedTitle', {linkStart:"", linkEnd: ""}) + h3.popover-title!=env.t('seasonalShopTitle', {linkStart:"", linkEnd: ""}) .popover-content - p!=env.t('seasonalShopClosedText', {linkStart: "", linkEnd: ""}) + p!=env.t('seasonalShopText') + li.customize-menu.inventory-gear + menu(label='{{::label}}', ng-repeat='(set,label) in {candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') + div(ng-repeat='item in Content.gear.flat | toArray | where:{index:set}') + button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase(item.type,item)', class='shop_{{::item.key}}') + .center + | {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}  + span.Pet_Currency_Gem1x.inline-gems script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid From 1c9088654881d91e3120dcc9729d6d0b2708db0b Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Thu, 25 Dec 2014 15:52:26 -0600 Subject: [PATCH 163/202] fix(event): Better shop flow --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index eb1544cfab..e6038a4078 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -31,7 +31,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .popover-content p!=env.t('seasonalShopText') li.customize-menu.inventory-gear - menu(label='{{::label}}', ng-repeat='(set,label) in {candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') + menu.pets-menu(label='{{::label}}', ng-repeat='(set,label) in {candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') div(ng-repeat='item in Content.gear.flat | toArray | where:{index:set}') button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase(item.type,item)', class='shop_{{::item.key}}') .center From 6d4d9451a582360a0f27ac9aed1d6426ea406165 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Thu, 25 Dec 2014 17:09:06 -0600 Subject: [PATCH 164/202] feat(event): Mystery migration --- migrations/mysteryitems.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/mysteryitems.js b/migrations/mysteryitems.js index b222047bda..47437570ef 100644 --- a/migrations/mysteryitems.js +++ b/migrations/mysteryitems.js @@ -2,7 +2,7 @@ var _id = ''; var update = { $push: { 'purchased.plan.mysteryItems':{ - $each:['head_mystery_201411','weapon_mystery_201411'] + $each:['head_mystery_201412','armor_mystery_201412'] } } }; From 597793e3cac21d0e1faec7762be33fd73ce273a9 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Thu, 25 Dec 2014 17:36:16 -0600 Subject: [PATCH 165/202] feat(event): Quests and Bailey --- views/options/inventory/inventory.jade | 6 +++ views/shared/new-stuff.jade | 74 ++++++++++++++++++-------- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index e6038a4078..6ced2f4816 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -37,6 +37,12 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .center | {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}  span.Pet_Currency_Gem1x.inline-gems + menu.pets-menu(label=env.t('quests')) + div(ng-repeat='quest in Content.quests', ng-if='quest.key == "evilSanta" || quest.key == "evilSanta2"') + button.customize-option(popover="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes() | htmlDecode}}", popover-title='{{::quest.text()}}', popover-trigger='mouseenter', popover-placement='top', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='::{locked: quest.previous && !user.achievements.quests[quest.previous]}') + p + | {{::quest.value}} + span.Pet_Currency_Gem1x.inline-gems script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index 76188b0e7b..6cc1171cf2 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -1,32 +1,62 @@ -h5 WINTER WONDERLAND BEGINS! Winter Class Outfits, Wintery Hair Colors, and NPC Decorations! +h5 tr td - h5 Winter Wonderland Begins! - p Winter has arrived, and the snow is gently drifting down over Habit City. Come celebrate with us! - tr - td - h5 Winter Class Outfits - .promo_winterclasses2015.pull-right - p From now until January 31st, limited edition outfits are available in the Rewards column. Depending on your class, you can be a Soothing Skater, Mage of the North, Gingerbread Warrior, or Icicle Drake! You'd better get productive to earn enough gold before they disappear. Good luck! - p.small.muted by Lemoness - tr - td - h5 Wintery Hair Colors - .promo_winteryhair.pull-right - p The Seasonal Edition Wintery Hair Colors are now available for purchase in the avatar customizations page! Now you can dye your avatar's hair Holly Green, Winter Star, Snowy, Peppermint, Aurora, or Festive. - p Seasonal Edition items recur unchanged every year, but they are only available to purchase during a short period of time. This is different from Limited Edition Items, which only recur if something is changed, such as the art or the price. These hair colors may remind some of you of the Holiday Hair Colors that were available last winter. The Holiday Hair Colors have been Retired in favor of the similar Seasonal Edition Wintery Hair Colors. Read more about the difference between Seasonal and Limited Edition items here! - p.small.muted by Lemoness, crystalphoenix, and mariahm - tr - td - h5 NPC Decorations - .npc_alex.pull-right - p Looks like the NPCs are really getting in to the cheery winter mood around the site. Who wouldn't? After all, there's plenty more to come! - p.small.muted by Lemoness + h5 December Subscriber Item Set and Seasonal Shop! Plus, Flagging Posts and Winter Plot-Line Continues + tr + td + h5 December Subscriber Item Set + .promo_mystery_201412.pull-right + p The December Subscriber Item has been revealed: the Penguin Item Set! All December subscribers will receive the Penguin Hat and the Penguin Suit. You still have six days to subscribe) and receive the item set! Thank you so much for your support - we really do rely on you to keep HabitRPG free to use and running smoothly. + p.small.muted by Lemoness + tr + td + h5 Seasonal Shop: Seasonal Outfits and Quests + .seasonalshop_winter2015.pull-right + p The Seasonal Shop has opened! The Seasonal Sorceress is stocking the seasonal edition versions of last year's winter outfits, now available for Gems instead of Gold, and the two winter quests, Trapper Santa and Find the Cub. The Seasonal Shop will only be open until January 31st, so don't wait! + p.small.muted by SabreCat and Lemoness + tr + td + h5 Flagging Posts + p You can now report inappropriate posts to moderators simply by clicking the new flag button next to the post. You should only report posts that violate the Community Guidelines and/or Terms of Service. Thanks for helping us to keep Habitica safe and pleasant for everybody! + p.small.muted by Alys, Blade, and Matteo + tr + td + h5 Winter Plot-Line Continues + p SabreCat's news is dire. "Most of my sabertooth friends have been impossible to reach, but one thing is clear: the prides have been disappearing from the Steppes. There are also reports that something drove the mammoths to early migration and disturbed the hibernation of the terrible lizards." + p He wraps his cloak around himself as another blast of frigid wind roars through the streets. An icy winter gale has been blowing from the north, rattling the window panes and setting the pets and mounts to trembling and howling. + p "I've never seen anything like it!" says Matt the Beast Master. "Something is terrifying all my animals - even the cacti, who are normally so mighty and brave! For something to frighten a cactus..." He shakes his head. + p The stress level in Habitica is mounting. + p.small.muted Missed the previous Winter Plot-line? Catch up on the story here! + p.small.muted by Lemoness hr a(href='/static/old-news', target='_blank') Read older news mixin oldNews + h5 12/21/2014 - Winter Wonderland Begins, Winter Class Outfits, Wintery Hair Colors, and NPC Decorations! + tr + td + h5 Winter Wonderland Begins! + p Winter has arrived, and the snow is gently drifting down over Habit City. Come celebrate with us! + tr + td + h5 Winter Class Outfits + .promo_winterclasses2015.pull-right + p From now until January 31st, limited edition outfits are available in the Rewards column. Depending on your class, you can be a Soothing Skater, Mage of the North, Gingerbread Warrior, or Icicle Drake! You'd better get productive to earn enough gold before they disappear. Good luck! + p.small.muted by Lemoness + tr + td + h5 Wintery Hair Colors + .promo_winteryhair.pull-right + p The Seasonal Edition Wintery Hair Colors are now available for purchase in the avatar customizations page! Now you can dye your avatar's hair Holly Green, Winter Star, Snowy, Peppermint, Aurora, or Festive. + p Seasonal Edition items recur unchanged every year, but they are only available to purchase during a short period of time. This is different from Limited Edition Items, which only recur if something is changed, such as the art or the price. These hair colors may remind some of you of the Holiday Hair Colors that were available last winter. The Holiday Hair Colors have been Retired in favor of the similar Seasonal Edition Wintery Hair Colors. Read more about the difference between Seasonal and Limited Edition items here! + p.small.muted by Lemoness, crystalphoenix, and mariahm + tr + td + h5 NPC Decorations + .npc_alex.pull-right + p Looks like the NPCs are really getting in to the cheery winter mood around the site. Who wouldn't? After all, there's plenty more to come! + p.small.muted by Lemoness h5 12/17/2014 - Android App Update, Seasonal Shop, and Winter Plot-Line Continues tr td From f4c582eb2475e6f1bb846aba44b3ad690c4fc0b7 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Thu, 25 Dec 2014 17:40:49 -0600 Subject: [PATCH 166/202] fix(event): quest syntax --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 6ced2f4816..90dbd3398e 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -38,7 +38,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html | {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}  span.Pet_Currency_Gem1x.inline-gems menu.pets-menu(label=env.t('quests')) - div(ng-repeat='quest in Content.quests', ng-if='quest.key == "evilSanta" || quest.key == "evilSanta2"') + div(ng-repeat='quest in Content.quests', ng-if='(quest.key == "evilsanta") || (quest.key == "evilsanta2")') button.customize-option(popover="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes() | htmlDecode}}", popover-title='{{::quest.text()}}', popover-trigger='mouseenter', popover-placement='top', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='::{locked: quest.previous && !user.achievements.quests[quest.previous]}') p | {{::quest.value}} From 1b4843ff586340abc51b3f45ccad0fe195fd6475 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Thu, 25 Dec 2014 17:42:09 -0600 Subject: [PATCH 167/202] fix(event): Better quest text placement --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 90dbd3398e..492341a8b5 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -39,7 +39,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html span.Pet_Currency_Gem1x.inline-gems menu.pets-menu(label=env.t('quests')) div(ng-repeat='quest in Content.quests', ng-if='(quest.key == "evilsanta") || (quest.key == "evilsanta2")') - button.customize-option(popover="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes() | htmlDecode}}", popover-title='{{::quest.text()}}', popover-trigger='mouseenter', popover-placement='top', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='::{locked: quest.previous && !user.achievements.quests[quest.previous]}') + button.customize-option(popover="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes() | htmlDecode}}", popover-title='{{::quest.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='::{locked: quest.previous && !user.achievements.quests[quest.previous]}') p | {{::quest.value}} span.Pet_Currency_Gem1x.inline-gems From f5a0bd690308fa1c75b85645c868aa94b404eb09 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Thu, 25 Dec 2014 21:32:07 -0600 Subject: [PATCH 168/202] Added trasnparent class to seasonal items already owned --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 492341a8b5..45162ea4e6 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -32,7 +32,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html p!=env.t('seasonalShopText') li.customize-menu.inventory-gear menu.pets-menu(label='{{::label}}', ng-repeat='(set,label) in {candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') - div(ng-repeat='item in Content.gear.flat | toArray | where:{index:set}') + div(ng-repeat='item in Content.gear.flat | toArray | where:{index:set}' ng-class="{transparent: user.items.gear.owned[item.key]}") button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase(item.type,item)', class='shop_{{::item.key}}') .center | {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}  From 13be267efbb340b6c8a4809358b5b2f73574bfed Mon Sep 17 00:00:00 2001 From: Verabird Date: Mon, 22 Dec 2014 15:19:53 -0500 Subject: [PATCH 169/202] added variable for date display and checking line ending issue --- views/shared/tasks/task.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index f96b33cb57..5826fb6b3e 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -4,7 +4,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // Due Date span(ng-if='task.type=="todo" && task.date') - span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{moment(task.date).format('MM/DD')}} + span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{moment(task.date).format(env.t('shortdate'))}} // Streak |   span(ng-show='task.streak') {{task.streak}}  From e391fc04f0d0b508e47307d1679e111d636e8b5a Mon Sep 17 00:00:00 2001 From: Verabird Date: Mon, 22 Dec 2014 22:23:11 -0500 Subject: [PATCH 170/202] Add variables for medium-length date display --- views/shared/modals/members.jade | 4 ++-- views/shared/tasks/task.jade | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 0255ab8e6d..b112fb059c 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -15,12 +15,12 @@ script(type='text/ng-template', id='modals/member.html') |  =env.t('memberSince') |  - | {{::timestamp(profile.auth.timestamps.created)}} - + | {{::profile.auth.timestamps.created | date:env.t('mediumdate')}} - li(ng-show='::profile.auth.timestamps.loggedin') |  =env.t('lastLoggedIn') |  - | {{::timestamp(profile.auth.timestamps.loggedin)}} - + | {{::profile.auth.timestamps.loggedin | date:env.t('mediumdate')}} - h3=env.t('stats') .label.label-info {{:: {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer")}[profile.stats.class] }} include ../profiles/stats diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 5826fb6b3e..29e5027cc7 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -190,7 +190,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // if Todos, the due date fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') legend.option-title=env.t('dueDate') - input.option-content.datepicker(type='text', datepicker-popup='MM/dd/yyyy', ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') + input.option-content.datepicker(type='text', datepicker-popup=env.t('mediumdate'), ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') // Tags fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') From 7297429888558745c71b104e8af22b0f5c02e61e Mon Sep 17 00:00:00 2001 From: Verabird Date: Wed, 24 Dec 2014 22:18:50 -0500 Subject: [PATCH 171/202] Make it possible to choose between MM/dd and dd/MM date format --- public/js/controllers/settingsCtrl.js | 7 +++++++ src/models/user.js | 1 + views/options/settings.jade | 6 ++++++ views/options/social/chat-message.jade | 2 +- views/shared/modals/members.jade | 4 ++-- views/shared/tasks/task.jade | 5 +++-- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 0c0123d1f3..fb1efe6a12 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -65,6 +65,13 @@ habitrpg.controller('SettingsCtrl', User.set({'preferences.language': $scope.language.code}); } + $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy','yyyy/MM/dd']; + $scope.changeDateFormat = function(){ + + var dateFormat= User.user.preferences.dateFormat; + User.set({'preferences.dateFormat': dateFormat}); + } + $scope.reroll = function(){ User.user.ops.reroll({}); $rootScope.$state.go('tasks'); diff --git a/src/models/user.js b/src/models/user.js index 3dbb7f0f29..a9527cd512 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -278,6 +278,7 @@ var UserSchema = new Schema({ automaticAllocation: Boolean, allocationMode: {type:String, enum: ['flat','classbased','taskbased'], 'default': 'flat'}, costume: Boolean, + dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], 'default': 'MM/dd/yyyy'}, sleep: {type: Boolean, 'default': false}, stickyHeader: {type: Boolean, 'default': true}, disableClasses: {type: Boolean, 'default': false}, diff --git a/views/options/settings.jade b/views/options/settings.jade index ef2727b4f6..85f731bd4c 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -36,6 +36,12 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') br strong !=env.t('helpWithTranslation') + + .form-horizontal + h5=env.t('dateFormat') + select.form-control(ng-model='user.preferences.dateFormat', ng-options='DF for DF in availableFormats', ng-change='changeDateFormat()') + + .checkbox label input(type='checkbox', ng-click='hideHeader() ', ng-checked='user.preferences.hideHeader!==true') diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index e93f304b7d..34ff439004 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -14,7 +14,7 @@ mixin chatMessages(inbox) // this invisible username label is here to push the message text far enough right that the visible label can be floated to this point without covering up any of the text markdown(ng-model='::message.text') | - - span.muted.time(from-now='::message.timestamp' tooltip="{{::message.timestamp | date:'short'}}") + span.muted.time(from-now='::message.timestamp' tooltip="{{::message.timestamp | date:user.preferences.dateFormat.concat(' HH:mm:ss')}}") unless inbox span a.label.label-default(ng-show='countExists(message.likes)', ng-class='{"label-success":message.likes[user._id]}', ng-click='likeChatMessage(group,message)') +{{countExists(message.likes)}} diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index b112fb059c..4fe8e2821a 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -15,12 +15,12 @@ script(type='text/ng-template', id='modals/member.html') |  =env.t('memberSince') |  - | {{::profile.auth.timestamps.created | date:env.t('mediumdate')}} - + | {{::profile.auth.timestamps.created | date:user.preferences.dateFormat}} - li(ng-show='::profile.auth.timestamps.loggedin') |  =env.t('lastLoggedIn') |  - | {{::profile.auth.timestamps.loggedin | date:env.t('mediumdate')}} - + | {{::profile.auth.timestamps.loggedin | date:user.preferences.dateFormat}} - h3=env.t('stats') .label.label-info {{:: {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer")}[profile.stats.class] }} include ../profiles/stats diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 29e5027cc7..7ce1d4cb14 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -4,7 +4,8 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // Due Date span(ng-if='task.type=="todo" && task.date') - span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{moment(task.date).format(env.t('shortdate'))}} + span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{task.date | date:user.preferences.dateFormat.substr(0,5)}} + //{{::profile.auth.timestamps.created | date:user.preferences.dateFormat}} // Streak |   span(ng-show='task.streak') {{task.streak}}  @@ -190,7 +191,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // if Todos, the due date fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') legend.option-title=env.t('dueDate') - input.option-content.datepicker(type='text', datepicker-popup=env.t('mediumdate'), ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') + input.option-content.datepicker(type='text', datepicker-popup='{{user.preferences.dateFormat}}', ng-model='task.date', is-open='datepickerOpened', ng-click='datepickerOpened = true') // Tags fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') From 8852ad61b2a8c3211480510da6e01d65d7b862d6 Mon Sep 17 00:00:00 2001 From: Verabird Date: Fri, 26 Dec 2014 15:51:34 -0500 Subject: [PATCH 172/202] Add dateFormat preference field to user model and make settingsCtrl change the field when form is changed --- public/js/controllers/settingsCtrl.js | 2 +- src/models/user.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index fb1efe6a12..781a08a2ad 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -65,7 +65,7 @@ habitrpg.controller('SettingsCtrl', User.set({'preferences.language': $scope.language.code}); } - $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy','yyyy/MM/dd']; + $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy']; $scope.changeDateFormat = function(){ var dateFormat= User.user.preferences.dateFormat; diff --git a/src/models/user.js b/src/models/user.js index a9527cd512..1c97b34a4e 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -278,7 +278,7 @@ var UserSchema = new Schema({ automaticAllocation: Boolean, allocationMode: {type:String, enum: ['flat','classbased','taskbased'], 'default': 'flat'}, costume: Boolean, - dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], 'default': 'MM/dd/yyyy'}, + dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy'], 'default': 'MM/dd/yyyy'}, sleep: {type: Boolean, 'default': false}, stickyHeader: {type: Boolean, 'default': true}, disableClasses: {type: Boolean, 'default': false}, From 9b10081117aae900a5c61bb2b856859e2822b279 Mon Sep 17 00:00:00 2001 From: Verabird Date: Fri, 26 Dec 2014 16:07:54 -0500 Subject: [PATCH 173/202] Removed useless comment in task.jade --- views/shared/tasks/task.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 7ce1d4cb14..9feeb87a11 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -5,7 +5,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s // Due Date span(ng-if='task.type=="todo" && task.date') span(ng-class='{"label label-danger":(moment(task.date).isBefore(_today, "days") && !task.completed)}') {{task.date | date:user.preferences.dateFormat.substr(0,5)}} - //{{::profile.auth.timestamps.created | date:user.preferences.dateFormat}} + // Streak |   span(ng-show='task.streak') {{task.streak}}  From db6f52e4b5314cf957956b85936dfdce07c5d6fb Mon Sep 17 00:00:00 2001 From: Andy Zinsser Date: Fri, 26 Dec 2014 18:31:21 -0800 Subject: [PATCH 174/202] Add muted class when no food is in inventory --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 492341a8b5..5a155f5fd5 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -108,7 +108,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html') li.customize-menu menu.pets-menu(label=env.t('food') + ' ({{foodCount}})') - p(ng-show='foodCount < 1')=env.t('noFood') + p.muted(ng-show='foodCount < 1')=env.t('noFood') div(ng-repeat='(food,points) in ownedItems(user.items.food)') button.customize-option(popover='{{::Content.food[food].notes()}}', popover-title='{{::Content.food[food].text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseFood(food)', class='Pet_Food_{{::food}}') .badge.badge-info.stack-count {{points}} From 695c463b405ff9b6ba488f0b68f74217bbcaa9b5 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Fri, 26 Dec 2014 21:04:22 -0600 Subject: [PATCH 175/202] Added confirmation dialog before deleting message --- public/js/controllers/groupsCtrl.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 8e4b8695dd..338338f76b 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -253,12 +253,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.deleteChatMessage = function(group, message){ if(message.uuid === User.user.id || (User.user.backer && User.user.contributor.admin)){ var previousMsg = (group.chat && group.chat[0]) ? group.chat[0].id : false; - Groups.Group.deleteChatMessage({gid:group._id, messageId:message.id, previousMsg:previousMsg}, undefined, function(data){ - if(data.chat) group.chat = data.chat; + if(confirm('Are you sure you want to delete this message?')){ + Groups.Group.deleteChatMessage({gid:group._id, messageId:message.id, previousMsg:previousMsg}, undefined, function(data){ + if(data.chat) group.chat = data.chat; - var i = _.findIndex(group.chat, {id: message.id}); - if(i !== -1) group.chat.splice(i, 1); - }); + var i = _.findIndex(group.chat, {id: message.id}); + if(i !== -1) group.chat.splice(i, 1); + }); + } } } From 85f4a2884a077943ac87d065f2ee26aa521e851c Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sat, 27 Dec 2014 12:56:54 +0100 Subject: [PATCH 176/202] add some more debugging for cron --- src/controllers/user.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index a7f80bf5f7..b4505d95f8 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -268,13 +268,18 @@ api.cron = function(req, res, next) { Group.tavernBoss(user,progress); if (!quest) return user.save(next); + // FOR DEBUGGING, PLEASE IGNORE + var opStatus = null; + // If user is on a quest, roll for boss & player, or handle collections // FIXME this saves user, runs db updates, loads user. Is there a better way to handle this? async.waterfall([ function(cb){ + opStatus = 'saveUser'; user.save(cb); // make sure to save the cron effects }, function(saved, count, cb){ + opStatus = 'runQuest'; var type = quest.boss ? 'boss' : 'collect'; Group[type+'Quest'](user,progress,cb); }, @@ -289,7 +294,8 @@ api.cron = function(req, res, next) { stack: (err.stack || err.message || err), body: req.body, headers: req.header, auth: (req.headers['x-api-user'] + ' | ' + req.headers['x-api-key']), - originalUrl: req.originalUrl + originalUrl: req.originalUrl, + opStatus: opStatus }); res.locals.user = saved; next(err,saved); From a082e88b5d4d7889915f8279f0ed02bbe6ef8f1c Mon Sep 17 00:00:00 2001 From: Negue Date: Sat, 27 Dec 2014 23:26:30 +0100 Subject: [PATCH 177/202] Filter Array only once and one time binding - closes #4429 --- public/js/controllers/inventoryCtrl.js | 18 ++++++++++++++++++ views/options/inventory/inventory.jade | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index 32511b60ef..5e71b5bf2e 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -162,5 +162,23 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', $rootScope.selectedQuest = item; $rootScope.openModal('buyQuest', {controller:'InventoryCtrl'}); } + + $scope.getSeasonalShopArray = function(set){ + var flatGearArray = _.toArray(Content.gear.flat); + + var filteredArray = _.where(flatGearArray, {index: set}); + + return filteredArray; + }; + + $scope.getSeasonalShopQuests = function(set){ + var questArray = _.toArray(Content.quests); + + var filteredArray = _.filter(questArray, function(q){ + return q.key == "evilsanta" || q.key == "evilsanta2"; + }); + + return filteredArray; + }; } ]); diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index e0428e11a2..e3fc8a077c 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -31,14 +31,14 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html .popover-content p!=env.t('seasonalShopText') li.customize-menu.inventory-gear - menu.pets-menu(label='{{::label}}', ng-repeat='(set,label) in {candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') - div(ng-repeat='item in Content.gear.flat | toArray | where:{index:set}' ng-class="{transparent: user.items.gear.owned[item.key]}") + menu.pets-menu(label='{{::label}}', ng-repeat='(set,label) in ::{candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') + div(ng-repeat='item in ::getSeasonalShopArray(set)' ng-class="{transparent: user.items.gear.owned[item.key]}") button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase(item.type,item)', class='shop_{{::item.key}}') .center | {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}  span.Pet_Currency_Gem1x.inline-gems menu.pets-menu(label=env.t('quests')) - div(ng-repeat='quest in Content.quests', ng-if='(quest.key == "evilsanta") || (quest.key == "evilsanta2")') + div(ng-repeat='quest in ::getSeasonalShopQuests()') button.customize-option(popover="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes() | htmlDecode}}", popover-title='{{::quest.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='::{locked: quest.previous && !user.achievements.quests[quest.previous]}') p | {{::quest.value}} From 1a4455550d528d51a7f60b97ebe857be1539333e Mon Sep 17 00:00:00 2001 From: Andy Zinsser Date: Sat, 27 Dec 2014 17:49:58 -0800 Subject: [PATCH 178/202] Remove duplicate equipment-tab classes --- views/options/inventory/index.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/options/inventory/index.jade b/views/options/inventory/index.jade index b2f0cfb3ec..63959b1708 100644 --- a/views/options/inventory/index.jade +++ b/views/options/inventory/index.jade @@ -15,10 +15,10 @@ script(type='text/ng-template', id='partials/options.inventory.html') li.equipment-tab(ng-class="{ active: $state.includes('options.inventory.equipment') }") a(ui-sref='options.inventory.equipment') =env.t('equipment') - li.equipment-tab(ng-class="{ active: $state.includes('options.inventory.timetravelers') }") + li(ng-class="{ active: $state.includes('options.inventory.timetravelers') }") a(ui-sref='options.inventory.timetravelers') =env.t('timeTravelers') - li.equipment-tab(ng-class="{ active: $state.includes('options.inventory.seasonalshop') }") + li(ng-class="{ active: $state.includes('options.inventory.seasonalshop') }") a(ui-sref='options.inventory.seasonalshop') =env.t('seasonalShop') From f8cdcd2e75c7baab4760adae2f0920cbd78a0bb7 Mon Sep 17 00:00:00 2001 From: Melissa Mcewen Date: Sun, 28 Dec 2014 23:35:47 -0600 Subject: [PATCH 179/202] Inconsistent alignment of Gem prices in Seasonal Shop #4434 --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index e3fc8a077c..df93bb1001 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -41,7 +41,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html div(ng-repeat='quest in ::getSeasonalShopQuests()') button.customize-option(popover="{{::quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes() | htmlDecode}}", popover-title='{{::quest.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='::{locked: quest.previous && !user.achievements.quests[quest.previous]}') p - | {{::quest.value}} + | {{::quest.value}}  span.Pet_Currency_Gem1x.inline-gems script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') From c4e62fc79ba16d55344f391a9b6b8f049d0afadd Mon Sep 17 00:00:00 2001 From: Melissa Mcewen Date: Sun, 28 Dec 2014 23:51:37 -0600 Subject: [PATCH 180/202] Private Message (PM / DM) textbox is too small #4425 --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 0255ab8e6d..7132477b4b 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -46,7 +46,7 @@ script(type='text/ng-template', id='modals/private-message.html') .modal-header h4=env.t('pmHeading', {name: "{{profile.profile.name}}"}) .modal-body - textarea.form-control(type='text',ng-model='_message') + textarea.form-control(type='text',rows='5', ng-model='_message') .modal-footer button.btn.btn-primary(ng-click='sendPrivateMessage(profile._id, _message)')=env.t("send") button.btn.btn-default(ng-click='$close()')=env.t('cancel') From 8bdc0d55b9cdce680b9f352e3a234db42f0bf873 Mon Sep 17 00:00:00 2001 From: Melissa Mcewen Date: Sun, 28 Dec 2014 23:53:15 -0600 Subject: [PATCH 181/202] Private Message (PM / DM) textbox is too small #4425 --- views/shared/modals/members.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 7132477b4b..1499c9753e 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -46,7 +46,7 @@ script(type='text/ng-template', id='modals/private-message.html') .modal-header h4=env.t('pmHeading', {name: "{{profile.profile.name}}"}) .modal-body - textarea.form-control(type='text',rows='5', ng-model='_message') + textarea.form-control(type='text',rows='5',ng-model='_message') .modal-footer button.btn.btn-primary(ng-click='sendPrivateMessage(profile._id, _message)')=env.t("send") button.btn.btn-default(ng-click='$close()')=env.t('cancel') From 760381041fd82978c2aa6c6f09a73535689510dd Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 29 Dec 2014 11:18:01 -0600 Subject: [PATCH 182/202] Added transparent class to gem purchasing if zero gems are available for purchase --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index e3fc8a077c..f4f0e1a130 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -222,7 +222,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html') p | 4 span.Pet_Currency_Gem1x.inline-gems - div(ng-if='user.purchased.plan.customerId') + div(ng-if='user.purchased.plan.customerId', ng-class='::{transparent:(Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought) < 1}') button.customize-option(popover=env.t('subGemPop'), popover-title=env.t('subGemName'), popover-trigger='mouseenter', popover-placement='top',ng-click='user.ops.purchase({params:{type:"gems",key:"gem"}})') span.Pet_Currency_Gem.inline-gems .badge.badge-success.stack-count {{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}} From 4e1b298a42847bfa3afff87dc6707bd3f474eb00 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 29 Dec 2014 14:16:25 -0600 Subject: [PATCH 183/202] Corrected spelling error in comment --- public/js/controllers/rootCtrl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index cbce9d31fb..eab6ac66fa 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -236,7 +236,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $scope.spell = null; } - // Becuase our angular-ui-router uses anchors for urls (/#/options/groups/party), window.location.href=... won't + // Because our angular-ui-router uses anchors for urls (/#/options/groups/party), window.location.href=... won't // reload the page. Perform manually. $rootScope.hardRedirect = function(url){ window.location.href = url; From 4a37bec652ba7bbc3460341b51deff9a16fc2429 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 29 Dec 2014 21:21:54 +0100 Subject: [PATCH 184/202] fix(pr): indentation and simplify --- public/js/controllers/settingsCtrl.js | 9 ++------- src/models/user.js | 2 +- views/options/settings.jade | 4 +--- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 781a08a2ad..70d8b148a8 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -65,13 +65,8 @@ habitrpg.controller('SettingsCtrl', User.set({'preferences.language': $scope.language.code}); } - $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy']; - $scope.changeDateFormat = function(){ - - var dateFormat= User.user.preferences.dateFormat; - User.set({'preferences.dateFormat': dateFormat}); - } - + $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy']; + $scope.reroll = function(){ User.user.ops.reroll({}); $rootScope.$state.go('tasks'); diff --git a/src/models/user.js b/src/models/user.js index 233fa9015a..1c97b34a4e 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -279,7 +279,7 @@ var UserSchema = new Schema({ allocationMode: {type:String, enum: ['flat','classbased','taskbased'], 'default': 'flat'}, costume: Boolean, dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy'], 'default': 'MM/dd/yyyy'}, - sleep: {type: Boolean, 'default': false}, + sleep: {type: Boolean, 'default': false}, stickyHeader: {type: Boolean, 'default': true}, disableClasses: {type: Boolean, 'default': false}, newTaskEdit: {type: Boolean, 'default': false}, diff --git a/views/options/settings.jade b/views/options/settings.jade index a6382ab468..e8e50b2405 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -39,9 +39,7 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') .form-horizontal h5=env.t('dateFormat') - select.form-control(ng-model='user.preferences.dateFormat', ng-options='DF for DF in availableFormats', ng-change='changeDateFormat()') - - + select.form-control(ng-model='user.preferences.dateFormat', ng-options='DF for DF in availableFormats', ng-change='set({"preferences.dateFormat": user.preferences.dateFormat})') .checkbox label input(type='checkbox', ng-click='hideHeader() ', ng-checked='user.preferences.hideHeader!==true') From cb13b5ac26c7bd43eb024679ca6083e328a2598c Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 29 Dec 2014 22:16:58 +0100 Subject: [PATCH 185/202] fix(date): add YYYY/MM/DD format --- public/js/controllers/settingsCtrl.js | 2 +- src/models/user.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 70d8b148a8..4974a5045c 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -65,7 +65,7 @@ habitrpg.controller('SettingsCtrl', User.set({'preferences.language': $scope.language.code}); } - $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy']; + $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy', 'YYYY/MM/DD']; $scope.reroll = function(){ User.user.ops.reroll({}); diff --git a/src/models/user.js b/src/models/user.js index 1c97b34a4e..85a3d89cb3 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -278,7 +278,7 @@ var UserSchema = new Schema({ automaticAllocation: Boolean, allocationMode: {type:String, enum: ['flat','classbased','taskbased'], 'default': 'flat'}, costume: Boolean, - dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy'], 'default': 'MM/dd/yyyy'}, + dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy', 'YYYY/MM/DD'], 'default': 'MM/dd/yyyy'}, sleep: {type: Boolean, 'default': false}, stickyHeader: {type: Boolean, 'default': true}, disableClasses: {type: Boolean, 'default': false}, From 756fa83418e470578b0b43975a7c4e3d2e244284 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 29 Dec 2014 22:26:12 +0100 Subject: [PATCH 186/202] fix(date): typo --- public/js/controllers/settingsCtrl.js | 2 +- src/models/user.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 4974a5045c..4481ed2480 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -65,7 +65,7 @@ habitrpg.controller('SettingsCtrl', User.set({'preferences.language': $scope.language.code}); } - $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy', 'YYYY/MM/DD']; + $scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy', 'yyyy/MM/dd']; $scope.reroll = function(){ User.user.ops.reroll({}); diff --git a/src/models/user.js b/src/models/user.js index 85a3d89cb3..a9527cd512 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -278,7 +278,7 @@ var UserSchema = new Schema({ automaticAllocation: Boolean, allocationMode: {type:String, enum: ['flat','classbased','taskbased'], 'default': 'flat'}, costume: Boolean, - dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy', 'YYYY/MM/DD'], 'default': 'MM/dd/yyyy'}, + dateFormat: {type: String, enum:['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'], 'default': 'MM/dd/yyyy'}, sleep: {type: Boolean, 'default': false}, stickyHeader: {type: Boolean, 'default': true}, disableClasses: {type: Boolean, 'default': false}, From b3025c36b152f6cd7d51fd5b94aeb51473cc1a92 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 29 Dec 2014 15:43:20 -0600 Subject: [PATCH 187/202] Fixed checklist not advancing to next input. Closes #4430 --- public/js/controllers/tasksCtrl.js | 4 +++- views/shared/tasks/task.jade | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/js/controllers/tasksCtrl.js b/public/js/controllers/tasksCtrl.js index 2ff173dc63..cfc13fff0f 100644 --- a/public/js/controllers/tasksCtrl.js +++ b/public/js/controllers/tasksCtrl.js @@ -108,7 +108,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N */ function focusChecklist(task,index) { window.setTimeout(function(){ - $('#task-'+task.id+' .checklist-form .inline-edit')[index].focus(); + $('#task-'+task.id+' .checklist-form input[type="text"]')[index].focus(); }); } $scope.addChecklist = function(task) { @@ -116,6 +116,8 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N focusChecklist(task,0); } $scope.addChecklistItem = function(task,$event,$index) { + console.log("ADD CHECKLIST ITEM"); + console.log(task); if (!task.checklist[$index].text) { // Don't allow creation of an empty checklist item // TODO Provide UI feedback that this item is still blank diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index f96b33cb57..d72248528d 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -128,7 +128,7 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s button(type='button', ng-if='!task.checklist[0] && (task.type=="daily" || task.type=="todo")',ng-click='addChecklist(task)') span.glyphicon.glyphicon-tasks span=env.t('addChecklist') - form(ng-if='task.checklist') + form.checklist-form(ng-if='task.checklist') fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') legend.option-title span.hint(popover=env.t('checklistText'),popover-trigger='mouseenter',popover-placement='bottom')=env.t('checklist') From ae8542eaf1a086508fbf7d2b306faa7de2249c4e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 29 Dec 2014 15:50:44 -0600 Subject: [PATCH 188/202] Removed extraneous console logs left while testing --- public/js/controllers/tasksCtrl.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/js/controllers/tasksCtrl.js b/public/js/controllers/tasksCtrl.js index cfc13fff0f..fd8bcfe0a9 100644 --- a/public/js/controllers/tasksCtrl.js +++ b/public/js/controllers/tasksCtrl.js @@ -116,8 +116,6 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N focusChecklist(task,0); } $scope.addChecklistItem = function(task,$event,$index) { - console.log("ADD CHECKLIST ITEM"); - console.log(task); if (!task.checklist[$index].text) { // Don't allow creation of an empty checklist item // TODO Provide UI feedback that this item is still blank From 4eca27f19e337d8b5d128ceda9d93853b60c30b7 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 29 Dec 2014 19:15:51 -0600 Subject: [PATCH 189/202] Corrected class name closes #4434 --- views/options/inventory/inventory.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index df93bb1001..6ffe3ce743 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -34,7 +34,7 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html menu.pets-menu(label='{{::label}}', ng-repeat='(set,label) in ::{candycane:env.t("candycaneSet"), ski:env.t("skiSet"), snowflake:env.t("snowflakeSet"), yeti:env.t("yetiSet")}') div(ng-repeat='item in ::getSeasonalShopArray(set)' ng-class="{transparent: user.items.gear.owned[item.key]}") button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase(item.type,item)', class='shop_{{::item.key}}') - .center + .text-center | {{((item.specialClass == "wizard") && (item.type == "weapon")) + 1}}  span.Pet_Currency_Gem1x.inline-gems menu.pets-menu(label=env.t('quests')) From 53fff0dbc61c2bb11bf21f1575c487ab05016214 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sat, 27 Dec 2014 11:49:51 -0600 Subject: [PATCH 190/202] feat(seasonal): Implement Snowball Adds the snowball from Winter 2014 to the Seasonal Shop. --- public/js/controllers/inventoryCtrl.js | 1 + views/options/inventory/inventory.jade | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index 5e71b5bf2e..325584ca26 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -87,6 +87,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', } $scope.purchase = function(type, item){ + if (type == 'special') return User.user.ops.buySpecialSpell({params:{key:item.key}}); var gems = User.user.balance * 4; diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 6ffe3ce743..7c1d8a1539 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -43,6 +43,15 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html p | {{::quest.value}}  span.Pet_Currency_Gem1x.inline-gems + mixin buySpecialSpell(k,gp) + menu.pets-menu(label='{{::Content.spells.special.#{k}.text()}}') + div + button.customize-option(popover='{{::Content.spells.special.#{k}.notes()}}', popover-title='{{::Content.spells.special.#{k}.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase("special", Content.spells.special.#{k})', class='inventory_special_#{k}') + p + | {{::Content.spells.special.#{k}.value}} + span(class='#{gp ? "shop_gold" : "Pet_Currency_Gem1x inline-gems"}') + +buySpecialSpell('snowball',true) + //-+buySpecialSpell('spookDust',true) script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid @@ -203,15 +212,6 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html') p | 4 span.Pet_Currency_Gem1x.inline-gems - mixin buySpecialSpell(k,gp) - div - button.customize-option(popover='{{::Content.spells.special.#{k}.notes()}}', popover-title='{{::Content.spells.special.#{k}.text()}}', popover-trigger='mouseenter', popover-placement='top', ng-click='purchase("special", Content.spells.special.#{k})', class='inventory_special_#{k}') - p - | {{::Content.spells.special.#{k}.value}} - span(class='#{gp ? "shop_gold" : "Pet_Currency_Gem1x inline-gems"}') - //-+buySpecialSpell('snowball',false) - //-+buySpecialSpell('spookDust',true) - div(ng-show='user.flags.rebirthEnabled') button.customize-option(popover=env.t('rebirthPop'), popover-title=env.t('rebirthName'), popover-trigger='mouseenter', popover-placement='top', ng-click='openModal("rebirth")', class='rebirth_orb') p(ng-show='user.stats.lvl < 100') From d8cfc1dd4b9bd734aa99f9d61d6a54cd858ca99c Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Mon, 29 Dec 2014 20:58:28 -0600 Subject: [PATCH 191/202] WIP(NYE): New Year's cards --- src/models/user.js | 7 +++++-- views/options/inventory/inventory.jade | 21 ++++++++++----------- views/shared/modals/limited.jade | 23 ++++++++++++++++++++++- views/shared/profiles/achievements.jade | 6 ++++++ 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/models/user.js b/src/models/user.js index a9527cd512..132f5f76cf 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -47,7 +47,8 @@ var UserSchema = new Schema({ perfect: Number, habitBirthday: Boolean, valentine: Number, - costumeContest: Boolean + costumeContest: Boolean, + nye: Number }, auth: { blocked: Boolean, @@ -173,7 +174,9 @@ var UserSchema = new Schema({ snowball: {type: Number, 'default': 0}, spookDust: {type: Number, 'default': 0}, valentine: Number, - valentineReceived: Array // array of strings, by sender name + valentineReceived: Array, // array of strings, by sender name + nye: Number, + nyeReceived: Array }, // -------------- Animals ------------------- diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 7c1d8a1539..cd2ddf90ca 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -43,15 +43,12 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html p | {{::quest.value}}  span.Pet_Currency_Gem1x.inline-gems - mixin buySpecialSpell(k,gp) - menu.pets-menu(label='{{::Content.spells.special.#{k}.text()}}') - div - button.customize-option(popover='{{::Content.spells.special.#{k}.notes()}}', popover-title='{{::Content.spells.special.#{k}.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase("special", Content.spells.special.#{k})', class='inventory_special_#{k}') - p - | {{::Content.spells.special.#{k}.value}} - span(class='#{gp ? "shop_gold" : "Pet_Currency_Gem1x inline-gems"}') - +buySpecialSpell('snowball',true) - //-+buySpecialSpell('spookDust',true) + menu.pets-menu(label=env.t('seasonalItems')) + div + button.customize-option(popover='{{::Content.spells.special.snowball.notes()}}', popover-title='{{::Content.spells.special.snowball.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='purchase("special", Content.spells.special.snowball)', class='inventory_special_snowball') + p + | {{::Content.spells.special.snowball.value}} + span(class='shop_gold') script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid @@ -132,12 +129,10 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html') +specialItem('snowball') +specialItem('spookDust') - //-menu.pets-menu(label=env.t('valentineCard')) div(ng-if='user.items.special.valentineReceived[0]') button.customize-option(popover="Valentine's Day Card from {{User.user.items.special.valentineReceived[0]}}", popover-trigger='mouseenter', popover-placement='right', ng-click='openModal("valentine")', class='inventory_special_valentine') .badge.badge-info.stack-count {{user.items.special.valentineReceived.length}} - //-menu.pets-menu(label=env.t('subscriberItem')) div(ng-if='user.purchased.plan.customerId || user.purchased.plan.mysteryItems.length') button.customize-option(popover=env.t('subscriberItemText'), popover-trigger='mouseenter', popover-placement='right', class='inventory_present', ng-click="user.ops.openMysteryItem({})") .badge.badge-info.stack-count {{user.purchased.plan.mysteryItems.length}} @@ -146,6 +141,10 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html') button.customize-option(popover=env.t('mysticHourglassPopover'), popover-trigger='mouseenter', popover-placement='right', class='inventory_special_trinket', ng-click="$state.go('options.inventory.timetravelers')") .badge.badge-info.stack-count {{user.purchased.plan.consecutive.trinkets}} + div(ng-if='user.items.special.nyeReceived[0]') + button.customize-option(popover="New Year's Card from {{User.user.items.special.nyeReceived[0]}}", popover-trigger='mouseenter', popover-placement='right', ng-click='openModal("nye")', class='inventory_special_nye') + .badge.badge-info.stack-count {{user.items.special.nyeReceived.length}} + .col-md-6 h2=env.t('market') .row-fluid diff --git a/views/shared/modals/limited.jade b/views/shared/modals/limited.jade index 8d2d31ce90..d60e3e51e7 100644 --- a/views/shared/modals/limited.jade +++ b/views/shared/modals/limited.jade @@ -27,4 +27,25 @@ script(id='modals/valentine.html', type='text/ng-template') p small For enduring such a saccharine poem, you both receive the "Adoring Friends" badge! .modal-footer - button.btn.btn-default(ng-click='user.ops.readValentine({});$close()')=env.t('ok') \ No newline at end of file + button.btn.btn-default(ng-click='user.ops.readValentine({});$close()')=env.t('ok') + +// New Year's +script(id='modals/nye.html', type='text/ng-template') + .modal-header + h4 + .inventory_special_nye.pull-right + =env.t('nyeCard') + .modal-body + .bg-info(style='padding:10px') + p To: {{user.profile.name}}, From: {{user.items.special.nyeReceived[0]}} + hr + div(ng-switch='Math.floor(Math.random()*5)') + p(ng-switch-when='0') Happy New Year! May you slay many a bad Habit. + p(ng-switch-when='1') Happy New Year! May you reap many Rewards. + p(ng-switch-when='2') Happy New Year! May you earn many a Perfect Day. + p(ng-switch-when='3') Happy New Year! May your To-Do list stay short and sweet. + p(ng-switch-default) Happy New Year! May you not get attacked by a raging Hippogriff. + p + small For celebrating the new year together, you both receive the "Auld Acquaintance" badge! + .modal-footer + button.btn.btn-default(ng-click='user.ops.readNYE({});$close()')=env.t('ok') diff --git a/views/shared/profiles/achievements.jade b/views/shared/profiles/achievements.jade index bc8689d17d..43313a5e8e 100644 --- a/views/shared/profiles/achievements.jade +++ b/views/shared/profiles/achievements.jade @@ -179,3 +179,9 @@ div(ng-if='::profile.achievements.costumeContest') =env.t('costumeContestText') hr +div(ng-if='::profile.achievements.nye') + .achievement.achievement-valentine + h5=env.t('auldAcquaintance') + small + =env.t('auldAcquaintanceText', {cards: "{{::profile.achievements.nye}}"}) + hr \ No newline at end of file From 51acc477fe9ad58aba4c7aeede788d496441c3c6 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 30 Dec 2014 19:57:24 -0600 Subject: [PATCH 192/202] feat(NYE): New Year's cards --- public/js/controllers/rootCtrl.js | 3 +++ views/options/inventory/inventory.jade | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index eab6ac66fa..5065a93eb2 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -195,6 +195,9 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $scope.castStart = function(spell) { if (User.user.stats.mp < spell.mana) return Notification.text(window.env.t('notEnoughMana')); + if (spell.key == 'nye' && User.user.stats.gp < spell.value) + return Notification.text('Not enough gold.'); + $rootScope.applyingAction = true; $scope.spell = spell; if (spell.target == 'self') { diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index cd2ddf90ca..44218966fa 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -49,6 +49,11 @@ script(type='text/ng-template', id='partials/options.inventory.seasonalshop.html p | {{::Content.spells.special.snowball.value}} span(class='shop_gold') + div + button.customize-option(popover='{{::Content.spells.special.nye.notes()}}', popover-title='{{::Content.spells.special.nye.text()}}', popover-trigger='mouseenter', popover-placement='right', ng-click='castStart(Content.spells.special.nye)', class='inventory_special_nye') + p + | {{Content.spells.special.nye.value}} + span(class='shop_gold') script(type='text/ng-template', id='partials/options.inventory.timetravelers.html') .container-fluid From e35722865abad24f22bac9833a53f3bbd7d8e1ac Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 30 Dec 2014 20:52:06 -0600 Subject: [PATCH 193/202] fix(NYE): I18N for random text --- views/shared/modals/limited.jade | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/views/shared/modals/limited.jade b/views/shared/modals/limited.jade index d60e3e51e7..9099754893 100644 --- a/views/shared/modals/limited.jade +++ b/views/shared/modals/limited.jade @@ -39,12 +39,17 @@ script(id='modals/nye.html', type='text/ng-template') .bg-info(style='padding:10px') p To: {{user.profile.name}}, From: {{user.items.special.nyeReceived[0]}} hr - div(ng-switch='Math.floor(Math.random()*5)') - p(ng-switch-when='0') Happy New Year! May you slay many a bad Habit. - p(ng-switch-when='1') Happy New Year! May you reap many Rewards. - p(ng-switch-when='2') Happy New Year! May you earn many a Perfect Day. - p(ng-switch-when='3') Happy New Year! May your To-Do list stay short and sweet. - p(ng-switch-default) Happy New Year! May you not get attacked by a raging Hippogriff. + ul.list-unstyled(ng-switch='Math.floor(Math.random()*5)') + li(ng-switch-when='0') + =env.t('newYear0') + li(ng-switch-when='1') + =env.t('newYear1') + li(ng-switch-when='2') + =env.t('newYear2') + li(ng-switch-when='3') + =env.t('newYear3') + li(ng-switch-default) + =env.t('newYear4') p small For celebrating the new year together, you both receive the "Auld Acquaintance" badge! .modal-footer From edf66cbb59c1246fadf5f452a919cdf0a3e83da1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 20 Dec 2014 20:47:16 -0700 Subject: [PATCH 194/202] feat(discount) google discount & use sub.key instead of sub.months --- config.json.example | 9 ++-- public/css/index.styl | 2 + public/js/controllers/groupsCtrl.js | 2 +- public/js/controllers/settingsCtrl.js | 10 +++++ public/js/services/paymentServices.js | 18 ++------ src/controllers/members.js | 2 +- src/controllers/payments/index.js | 16 +++++-- src/controllers/payments/paypal.js | 44 ++++++++++++------- .../payments/paypalBillingSetup.js | 4 +- src/controllers/payments/stripe.js | 27 +++++++++--- src/models/coupon.js | 2 +- src/routes/payments.js | 2 + src/server.js | 2 +- views/options/settings.jade | 33 ++++++++------ views/shared/modals/members.jade | 9 ++-- 15 files changed, 114 insertions(+), 68 deletions(-) diff --git a/config.json.example b/config.json.example index e34d187838..6933f92426 100644 --- a/config.json.example +++ b/config.json.example @@ -34,10 +34,11 @@ }, "PAYPAL":{ "billing_plans": { - "1":"1", - "3":"3", - "6":"6", - "12":"12" + "basic_earned":"basic_earned", + "basic_3mo":"basic_3mo", + "basic_6mo":"basic_6mo", + "google_6mo":"google_6mo", + "basic_12mo":"basic_12mo" }, "mode":"sandbox", "client_id":"client_id", diff --git a/public/css/index.styl b/public/css/index.styl index 381ab03082..998fcad48e 100644 --- a/public/css/index.styl +++ b/public/css/index.styl @@ -179,3 +179,5 @@ a.label .white, .white a color: #fff !important +.line-through + text-decoration line-through \ No newline at end of file diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 338338f76b..fe30af9239 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -129,7 +129,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.gift = { type: 'gems', gems: {amount:0, fromBalance:true}, - subscription: {months:0}, + subscription: {key:''}, message:'' }; $scope.sendGift = function(uuid, gift){ diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 4481ed2480..a7927d5d0c 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -181,5 +181,15 @@ habitrpg.controller('SettingsCtrl', $scope.deleteWebhook = function(id) { User.user.ops.deleteWebhook({params:{id:id}}); } + + $scope.applyCoupon = function(coupon){ + $http.get(ApiUrlService.get() + '/api/v2/coupons/valid-discount/'+coupon) + .success(function(){ + Notification.text("Coupon applied!"); + var subs = $scope.Content.subscriptionBlocks; + subs["basic_6mo"].discount = true; + subs["google_6mo"].discount = false; + }); + } } ]); diff --git a/public/js/services/paymentServices.js b/public/js/services/paymentServices.js index 0fd7682512..3a0f9b21da 100644 --- a/public/js/services/paymentServices.js +++ b/public/js/services/paymentServices.js @@ -3,23 +3,12 @@ angular.module('paymentServices',[]).factory('Payments', ['$rootScope', 'User', '$http', 'Content', function($rootScope, User, $http, Content) { - var user = User.user; - var plan = User.user.purchased.plan; var Payments = {}; - Payments.currentSub = _.find(Content.subscriptionBlocks, function(b){ - switch (plan.paymentMethod) { - case 'Stripe': - case 'Paypal': // FIXME store paypalKey somewhere? - return b.key == plan.planId; - default: return undefined; - } - }) - Payments.showStripe = function(data) { var sub = data.subscription ? data.subscription - : data.gift && data.gift.type=='subscription' ? data.gift.subscription.months + : data.gift && data.gift.type=='subscription' ? data.gift.subscription.key : false; sub = sub && Content.subscriptionBlocks[sub]; var amount = // 500 = $5 @@ -36,7 +25,8 @@ function($rootScope, User, $http, Content) { token: function(res) { var url = '/stripe/checkout?a=a'; // just so I can concat &x=x below if (data.gift) url += '&gift=' + Payments.encodeGift(data.uuid, data.gift); - if (data.subscription) url += '&sub='+sub.months; + if (data.subscription) url += '&sub='+sub.key; + if (data.coupon) url += '&coupon='+data.coupon; $http.post(url, res).success(function() { window.location.reload(true); }).error(function(res) { @@ -66,7 +56,7 @@ function($rootScope, User, $http, Content) { Payments.cancelSubscription = function(){ if (!confirm(window.env.t('sureCancelSub'))) return; - window.location.href = '/' + plan.paymentMethod.toLowerCase() + '/subscribe/cancel?_id=' + user._id + '&apiToken=' + user.apiToken; + window.location.href = '/' + User.user.purchased.plan.paymentMethod.toLowerCase() + '/subscribe/cancel?_id=' + User.user._id + '&apiToken=' + User.user.apiToken; } Payments.encodeGift = function(uuid, gift){ diff --git a/src/controllers/members.js b/src/controllers/members.js index 26092e872c..25ffdce25d 100644 --- a/src/controllers/members.js +++ b/src/controllers/members.js @@ -35,7 +35,7 @@ api.sendMessage = function(user, member, data){ msg = data.message } else { msg = "`Hello " + member.profile.name + ", " + user.profile.name + " has sent you "; - msg += (data.type=='gems') ? data.gems.amount + " gems!`" : data.subscription.months + " months of subscription!`"; + msg += (data.type=='gems') ? data.gems.amount + " gems!`" : shared.content.subscriptionBlocks[data.subscription.key].months + " months of subscription!`"; msg += data.message; } shared.refPush(member.inbox.messages, groups.chatDefaults(msg, user)); diff --git a/src/controllers/payments/index.js b/src/controllers/payments/index.js index b0c59f643c..fea3c73025 100644 --- a/src/controllers/payments/index.js +++ b/src/controllers/payments/index.js @@ -10,6 +10,8 @@ var paypal = require('./paypal'); var members = require('../members') var async = require('async'); var iap = require('./iap'); +var mongoose= require('mongoose'); +var cc = require('coupon-code'); function revealMysteryItems(user) { _.each(shared.content.gear.flat, function(item) { @@ -29,8 +31,8 @@ exports.createSubscription = function(data, cb) { var recipient = data.gift ? data.gift.member : data.user; //if (!recipient.purchased.plan) recipient.purchased.plan = {}; // FIXME double-check, this should never be the case var p = recipient.purchased.plan; - var months = +(data.gift ? data.gift.subscription.months : data.sub.months); - var block = shared.content.subscriptionBlocks[months]; + var block = shared.content.subscriptionBlocks[data.gift ? data.gift.subscription.key : data.sub.key]; + var months = +block.months; if (data.gift) { if (p.customerId && !p.dateTerminated) { // User has active plan @@ -114,6 +116,14 @@ exports.buyGems = function(data, cb) { ], cb); } +exports.validCoupon = function(req, res, next){ + mongoose.model('Coupon').findOne({_id:cc.validate(req.params.code), event:'google_6mo'}, function(err, coupon){ + if (err) return next(err); + if (!coupon) return res.json(401, {err:"Invalid coupon code"}); + return res.send(200); + }); +} + exports.stripeCheckout = stripe.checkout; exports.stripeSubscribeCancel = stripe.subscribeCancel; exports.stripeSubscribeEdit = stripe.subscribeEdit; @@ -126,4 +136,4 @@ exports.paypalCheckoutSuccess = paypal.executePayment; exports.paypalIPN = paypal.ipn; exports.iapAndroidVerify = iap.androidVerify; -exports.iapIosVerify = iap.iosVerify; +exports.iapIosVerify = iap.iosVerify; \ No newline at end of file diff --git a/src/controllers/payments/paypal.js b/src/controllers/payments/paypal.js index 9c7a1d4f3a..f69ab306da 100644 --- a/src/controllers/payments/paypal.js +++ b/src/controllers/payments/paypal.js @@ -9,12 +9,14 @@ var logger = require('../../logging'); var ipn = require('paypal-ipn'); var paypal = require('paypal-rest-sdk'); var shared = require('habitrpg-shared'); +var mongoose = require('mongoose'); +var cc = require('coupon-code'); // This is the plan.id for paypal subscriptions. You have to set up billing plans via their REST sdk (they don't have // a web interface for billing-plan creation), see ./paypalBillingSetup.js for how. After the billing plan is created // there, get it's plan.id and store it in config.json _.each(shared.content.subscriptionBlocks, function(block){ - block.paypalKey = nconf.get("PAYPAL:billing_plans:"+block.months); + block.paypalKey = nconf.get("PAYPAL:billing_plans:"+block.key); }); paypal.configure({ @@ -30,23 +32,33 @@ var parseErr = function(res, err){ } exports.createBillingAgreement = function(req,res,next){ - req.session.paypalBlock = req.query.sub; - var block = shared.content.subscriptionBlocks[req.query.sub]; - var billingPlanTitle = "HabitRPG Subscription" + ' ($'+block.price+' every '+block.months+' months, recurring)'; - var billingAgreementAttributes = { - "name": billingPlanTitle, - "description": billingPlanTitle, - "start_date": moment().add({minutes:5}).format(), - "plan": { - "id": block.paypalKey + var sub = shared.content.subscriptionBlocks[req.query.sub]; + async.waterfall([ + function(cb){ + if (!sub.discount) return cb(null, null); + if (!req.query.coupon) return cb('Please provide a coupon code for this plan.'); + mongoose.model('Coupon').findOne({_id:cc.validate(req.query.coupon), event:sub.key}, cb); }, - "payer": { - "payment_method": "paypal" + function(coupon, cb){ + if (sub.discount && !coupon) return cb('Invalid coupon code.'); + var billingPlanTitle = "HabitRPG Subscription" + ' ($'+sub.price+' every '+sub.months+' months, recurring)'; + var billingAgreementAttributes = { + "name": billingPlanTitle, + "description": billingPlanTitle, + "start_date": moment().add({minutes:5}).format(), + "plan": { + "id": sub.paypalKey + }, + "payer": { + "payment_method": "paypal" + } + }; + paypal.billingAgreement.create(billingAgreementAttributes, cb); } - }; - paypal.billingAgreement.create(billingAgreementAttributes, function (err, billingAgreement) { + ], function(err, billingAgreement){ if (err) return parseErr(res, err); // For approving subscription via Paypal, first redirect user to: approval_url + req.session.paypalBlock = req.query.sub; var approval_url = _.find(billingAgreement.links, {rel:'approval_url'}).href; res.redirect(approval_url); }); @@ -82,10 +94,10 @@ exports.createPayment = function(req, res) { var gift = req.query.gift ? JSON.parse(req.query.gift) : undefined; var price = !gift ? 5.00 : gift.type=='gems' ? Number(gift.gems.amount/4).toFixed(2) - : Number(shared.content.subscriptionBlocks[gift.subscription.months].price).toFixed(2); + : Number(shared.content.subscriptionBlocks[gift.subscription.key].price).toFixed(2); var description = !gift ? "HabitRPG Gems" : gift.type=='gems' ? "HabitRPG Gems (Gift)" - : gift.subscription.months + "mo. HabitRPG Subscription (Gift)"; + : shared.content.subscriptionBlocks[gift.subscription.key].months + "mo. HabitRPG Subscription (Gift)"; var create_payment = { "intent": "sale", "payer": { diff --git a/src/controllers/payments/paypalBillingSetup.js b/src/controllers/payments/paypalBillingSetup.js index 38b70c0101..14721e22d1 100644 --- a/src/controllers/payments/paypalBillingSetup.js +++ b/src/controllers/payments/paypalBillingSetup.js @@ -11,7 +11,7 @@ var paypal = require('paypal-rest-sdk'); var blocks = require('habitrpg-shared').content.subscriptionBlocks; var live = nconf.get('PAYPAL:mode')=='live'; -var OP = 'get'; // list create update remove +var OP = 'create'; // list create update remove paypal.configure({ 'mode': nconf.get("PAYPAL:mode"), //sandbox or live @@ -72,7 +72,7 @@ switch(OP) { }); break; case "create": - paypal.billingPlan.create(blocks["12"].definition, function(err,plan){ + paypal.billingPlan.create(blocks["google_6mo"].definition, function(err,plan){ if (err) return console.log(err); if (plan.state == "ACTIVE") return console.log({err:err, plan:plan}); diff --git a/src/controllers/payments/stripe.js b/src/controllers/payments/stripe.js index 117fc29175..74172cc8ab 100644 --- a/src/controllers/payments/stripe.js +++ b/src/controllers/payments/stripe.js @@ -4,6 +4,8 @@ var async = require('async'); var payments = require('./index'); var User = require('mongoose').model('User'); var shared = require('habitrpg-shared'); +var mongoose = require('mongoose'); +var cc = require('coupon-code'); /* Setup Stripe response when posting payment @@ -17,16 +19,27 @@ exports.checkout = function(req, res, next) { async.waterfall([ function(cb){ if (sub) { - stripe.customers.create({ - email: req.body.email, - metadata: {uuid: user._id}, - card: token, - plan: sub.key - }, cb); + async.waterfall([ + function(cb2){ + if (!sub.discount) return cb2(null, null); + if (!req.query.coupon) return cb2('Please provide a coupon code for this plan.'); + mongoose.model('Coupon').findOne({_id:cc.validate(req.query.coupon), event:sub.key}, cb2); + }, + function(coupon, cb2){ + if (sub.discount && !coupon) return cb2('Invalid coupon code.'); + var customer = { + email: req.body.email, + metadata: {uuid: user._id}, + card: token, + plan: sub.key + }; + stripe.customers.create(customer, cb2); + } + ], cb); } else { stripe.charges.create({ amount: !gift ? "500" //"500" = $5 - : gift.type=='subscription' ? ""+shared.content.subscriptionBlocks[gift.subscription.months].price*100 + : gift.type=='subscription' ? ""+shared.content.subscriptionBlocks[gift.subscription.key].price*100 : ""+gift.gems.amount/4*100, currency: "usd", card: token diff --git a/src/models/coupon.js b/src/models/coupon.js index 497b45697f..4f2680546c 100644 --- a/src/models/coupon.js +++ b/src/models/coupon.js @@ -7,7 +7,7 @@ var autoinc = require('mongoose-id-autoinc'); var CouponSchema = new mongoose.Schema({ _id: {type: String, 'default': cc.generate}, - event: {type:String, enum:['wondercon']}, + event: {type:String, enum:['wondercon','google_6mo']}, user: {type: 'String', ref: 'User'} }); diff --git a/src/routes/payments.js b/src/routes/payments.js index adc9a65629..118f923689 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -20,4 +20,6 @@ router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, p router.post("/iap/android/verify", auth.authWithUrl, /*i18n.getUserLanguage, */payments.iapAndroidVerify); router.post("/iap/ios/verify", /*auth.authWithUrl, i18n.getUserLanguage, */ payments.iapIosVerify); +router.get("/api/v2/coupons/valid-discount/:code", /*auth.authWithUrl, i18n.getUserLanguage, */ payments.validCoupon); + module.exports = router; \ No newline at end of file diff --git a/src/server.js b/src/server.js index 8e551f7891..7c53317e23 100644 --- a/src/server.js +++ b/src/server.js @@ -8,7 +8,7 @@ var logging = require('./logging'); var isProd = nconf.get('NODE_ENV') === 'production'; var isDev = nconf.get('NODE_ENV') === 'development'; -if (cluster.isMaster && (isDev || isProd)) { +if (false && cluster.isMaster && (isDev || isProd)) { // Fork workers. If config.json has CORES=x, use that - otherwise, use all cpus-1 (production) var cpus = require('os').cpus(), cores = +nconf.get("CORES"); diff --git a/views/options/settings.jade b/views/options/settings.jade index e8e50b2405..3ba5192ec8 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -204,7 +204,7 @@ mixin subPerks() tr td span.hint(popover=env.t('buyGemsGoldText', {gemCost: "{{Shared.planGemLimits.convRate}}", gemLimit: "{{Shared.planGemLimits.convCap}}"}),popover-trigger='mouseenter',popover-placement='right') #{env.t('buyGemsGold')}  - span.badge.badge-success(ng-show='_subscription.months>1') Cap raised to {{ [25 + user.purchased.plan.consecutive.gemCapExtra + Math.floor(_subscription.months/3*5), 50] | min }} + span.badge.badge-success(ng-show='_subscription.key!="basic_earned"') Cap raised to {{ [25 + user.purchased.plan.consecutive.gemCapExtra + Math.floor(Content.subscriptionBlocks[_subscription.key].months/3*5), 50] | min }} tr td span.hint(popover=env.t('retainHistoryText'),popover-trigger='mouseenter',popover-placement='right')=env.t('retainHistory') @@ -214,8 +214,8 @@ mixin subPerks() tr td span.hint(popover=env.t('mysteryItemText'),popover-trigger='mouseenter',popover-placement='right') #{env.t('mysteryItem')}  - div(ng-show='_subscription.months>1') - .badge.badge-success +{{Math.floor(_subscription.months/3)}} Mystic Hourglass + div(ng-show='_subscription.key!="basic_earned"') + .badge.badge-success +{{Math.floor(Content.subscriptionBlocks[_subscription.key].months/3)}} Mystic Hourglass .small.muted Mystic Hourglasses allow purchasing a previous month's Mystery Item set. tr td @@ -228,7 +228,7 @@ script(id='partials/feature-matrix-check.html',type='text/ng-template') script(id='partials/options.settings.subscription.html',type='text/ng-template') //-h2=env.t('individualSub') - .container-fluid(ng-init='_subscription={months:1}') + .container-fluid(ng-init='_subscription={key:"basic_earned"}') .row .col-md-6 h3 Benefits @@ -241,7 +241,7 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template') | #{env.t('subCanceled')} {{moment(user.purchased.plan.dateTerminated).format('MM/DD/YYYY')}} tr(ng-if='!user.purchased.plan.dateTerminated'): td h4=env.t('subscribed') - p(ng-if='Payments.currentSub') Recurring ${{Payments.currentSub.price}} each {{Payments.currentSub.months}} Month(s) ({{user.purchased.plan.paymentMethod}}) + p(ng-if='user.purchased.plan.planId') Recurring ${{Content.subscriptionBlocks[user.purchased.plan.planId].price}} each {{Content.subscriptionBlocks[user.purchased.plan.planId].months}} Month(s) ({{user.purchased.plan.paymentMethod}}) tr(ng-if='user.purchased.plan.extraMonths'): td span.glyphicon.glyphicon-credit-card | You have {{user.purchased.plan.extraMonths | number:2}} months of subscription credit. @@ -254,16 +254,23 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template') li Mystic Hourglasses: {{user.purchased.plan.consecutive.trinkets}} div(ng-if='!user.purchased.plan.customerId || (user.purchased.plan.customerId && user.purchased.plan.dateTerminated)') .form-group - each block in env.Content.subscriptionBlocks - .radio - label - input(type="radio", name="subRadio", value="#{block.months}", ng-model='_subscription.months') - //-| #{block.months} Month(s) Recurring: $#{block.price} #{env.t('monthUSD')} - | Recurring $#{block.price} each #{block.months} Month(s) + .radio(ng-repeat='block in Content.subscriptionBlocks | toArray | omit:"discount==true" | orderBy:"months"') + label + input(type="radio", name="subRadio", ng-value="block.key", ng-model='_subscription.key') + span(ng-show='block.original') + | Recurring ${{block.original}} ${{block.price}} each {{block.months}} Month(s) + span(ng-hide='block.original') + | Recurring ${{block.price}} each {{block.months}} Month(s) + + .form-inline + .form-group + input.form-control(type='text', ng-model='_subscription.coupon', placeholder='Promo Code') + .form-group + button.btn.btn-small(type='button',ng-click='applyCoupon(_subscription.coupon)') Apply h3(ng-if='(user.purchased.plan.customerId && user.purchased.plan.dateTerminated)') Resubscribe - a.btn.btn-primary(ng-click='Payments.showStripe({subscription:_subscription.months})', ng-disabled='!_subscription.months') Card - a.btn.btn-warning(href='/paypal/subscribe?_id={{user._id}}&apiToken={{user.apiToken}}&sub={{_subscription.months}}', ng-disabled='!_subscription.months') PayPal + a.btn.btn-primary(ng-click='Payments.showStripe({subscription:_subscription.key, coupon:_subscription.coupon})', ng-disabled='!_subscription.key') Card + a.btn.btn-warning(href='/paypal/subscribe?_id={{user._id}}&apiToken={{user.apiToken}}&sub={{_subscription.key}}{{_subscription.coupon ? "&coupon="+_subscription.coupon : ""}}', ng-disabled='!_subscription.key') PayPal div(ng-if='user.purchased.plan.customerId') .btn.btn-primary(ng-if='!user.purchased.plan.dateTerminated && user.purchased.plan.paymentMethod=="Stripe"', ng-click='Payments.showStripeEdit()')=env.t('subUpdateCard') .btn.btn-sm.btn-danger(ng-if='!user.purchased.plan.dateTerminated', ng-click='Payments.cancelSubscription()')=env.t('cancelSub') \ No newline at end of file diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 7f44481a17..fbe64c6279 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -75,11 +75,10 @@ script(type='text/ng-template', id='modals/send-gift.html') .panel-heading Subscription .panel-body .form-group - each block in env.Content.subscriptionBlocks - .radio - label - input(type="radio", name="subRadio", value="#{block.months}", ng-model='gift.subscription.months') - | #{block.months} Month(s): $#{block.price} + .radio(ng-repeat='block in Content.subscriptionBlocks | toArray | omit:"discount==true" | orderBy:"months"') + label + input(type="radio", name="subRadio", ng-value="block.key", ng-model='gift.subscription.key') + | {{block.months}} Month(s): ${{block.price}} textarea.form-control(rows='3', ng-model='gift.message', placeholder='Personal message (optional)') From 8eebaea3dd69eeb85116e554648cf7ad4043f7f9 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Tue, 30 Dec 2014 20:35:30 -0700 Subject: [PATCH 195/202] woops.. :/ --- src/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.js b/src/server.js index 7c53317e23..8e551f7891 100644 --- a/src/server.js +++ b/src/server.js @@ -8,7 +8,7 @@ var logging = require('./logging'); var isProd = nconf.get('NODE_ENV') === 'production'; var isDev = nconf.get('NODE_ENV') === 'development'; -if (false && cluster.isMaster && (isDev || isProd)) { +if (cluster.isMaster && (isDev || isProd)) { // Fork workers. If config.json has CORES=x, use that - otherwise, use all cpus-1 (production) var cpus = require('os').cpus(), cores = +nconf.get("CORES"); From 1f1b4371b7b75bf420d1e903f326fa80d8deb5e1 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 30 Dec 2014 21:48:02 -0600 Subject: [PATCH 196/202] feat(NYE): Migrate hats --- migrations/20141230_new_years_hats.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 migrations/20141230_new_years_hats.js diff --git a/migrations/20141230_new_years_hats.js b/migrations/20141230_new_years_hats.js new file mode 100644 index 0000000000..08e6a542fe --- /dev/null +++ b/migrations/20141230_new_years_hats.js @@ -0,0 +1,11 @@ +db.users.update( + {'gear.owned.head_special_nye':{$ne:null}}, + {$set:{'gear.owned.head_special_nye2014':false}}, + {multi:1} +) + +db.users.update( + {'gear.owned.head_special_nye':null}, + {$set:{'gear.owned.head_special_nye':false}}, + {multi:1} +) From 230482c57893bea1a1e3aa97509029f600b991f6 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 30 Dec 2014 21:58:15 -0600 Subject: [PATCH 197/202] chore(news): Bailey --- views/shared/new-stuff.jade | 76 +++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index 6cc1171cf2..996f33382b 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -1,38 +1,58 @@ -h5 +h5 NEW YEAR'S EVE CELEBRATION: Party Hats, New Year's Cards, Snowballs, and Last Chance for Penguin Item Set tr td - h5 December Subscriber Item Set and Seasonal Shop! Plus, Flagging Posts and Winter Plot-Line Continues - tr - td - h5 December Subscriber Item Set - .promo_mystery_201412.pull-right - p The December Subscriber Item has been revealed: the Penguin Item Set! All December subscribers will receive the Penguin Hat and the Penguin Suit. You still have six days to subscribe) and receive the item set! Thank you so much for your support - we really do rely on you to keep HabitRPG free to use and running smoothly. - p.small.muted by Lemoness - tr - td - h5 Seasonal Shop: Seasonal Outfits and Quests - .seasonalshop_winter2015.pull-right - p The Seasonal Shop has opened! The Seasonal Sorceress is stocking the seasonal edition versions of last year's winter outfits, now available for Gems instead of Gold, and the two winter quests, Trapper Santa and Find the Cub. The Seasonal Shop will only be open until January 31st, so don't wait! - p.small.muted by SabreCat and Lemoness - tr - td - h5 Flagging Posts - p You can now report inappropriate posts to moderators simply by clicking the new flag button next to the post. You should only report posts that violate the Community Guidelines and/or Terms of Service. Thanks for helping us to keep Habitica safe and pleasant for everybody! - p.small.muted by Alys, Blade, and Matteo - tr - td - h5 Winter Plot-Line Continues - p SabreCat's news is dire. "Most of my sabertooth friends have been impossible to reach, but one thing is clear: the prides have been disappearing from the Steppes. There are also reports that something drove the mammoths to early migration and disturbed the hibernation of the terrible lizards." - p He wraps his cloak around himself as another blast of frigid wind roars through the streets. An icy winter gale has been blowing from the north, rattling the window panes and setting the pets and mounts to trembling and howling. - p "I've never seen anything like it!" says Matt the Beast Master. "Something is terrifying all my animals - even the cacti, who are normally so mighty and brave! For something to frighten a cactus..." He shakes his head. - p The stress level in Habitica is mounting. - p.small.muted Missed the previous Winter Plot-line? Catch up on the story here! - p.small.muted by Lemoness + h5 Party Hats + .promo_partyhats.pull-right + p In honor of the new year, some free Party Hats are available in the Rewards store! New users get the ever-handsome Absurd Party Hat, and users who already received one last year get the Silly Party Hat. These hats will be available to purchase until January 31st, but once you've bought them, you'll have them forever. Enjoy! + p.small.muted by Lemoness and SabreCat + tr + td + h5 New Year's Cards (Until Jan 1st Only!) + .inventory_special_nye.pull-right + p Until January 1st only, the Seasonal Shop is stocking New Year's Cards! Now you can send cards to your friends (and yourself) to wish them a Happy Habit New Year. All senders and recipients will receive the Auld Acquaintance badge! + p.small.muted by Lemoness and SabreCat + tr + td + h5 Snowballs + .inventory_special_snowball.pull-right + p The Seasonal Shop is also stocking Snowballs for gold! Throw them at your friends to have an exciting effect. Anyone hit with a snowball earns the Annoying Friends badge. The results of being hit with a Snowball will last until the end of your day, but you can also reverse them early by buying Salt from the Rewards column. Snowballs are available until January 31st. + p.small.muted by Shaner, Lemoness, and SabreCat + tr + td + h5 Last Chance for Penguin Item Set + p Reminder: this is the final day to subscribe and receive the Penguin Item Set! If you want the Penguin Hat or the Penguin Suit, now's the time! Thanks so much for your support <3 hr a(href='/static/old-news', target='_blank') Read older news mixin oldNews + h5 12/25/2014 - December Subscriber Item Set and Seasonal Shop! Plus, Flagging Posts and Winter Plot-Line Continues + tr + td + h5 December Subscriber Item Set + .promo_mystery_201412.pull-right + p The December Subscriber Item has been revealed: the Penguin Item Set! All December subscribers will receive the Penguin Hat and the Penguin Suit. You still have six days to subscribe) and receive the item set! Thank you so much for your support - we really do rely on you to keep HabitRPG free to use and running smoothly. + p.small.muted by Lemoness + tr + td + h5 Seasonal Shop: Seasonal Outfits and Quests + .seasonalshop_winter2015.pull-right + p The Seasonal Shop has opened! The Seasonal Sorceress is stocking the seasonal edition versions of last year's winter outfits, now available for Gems instead of Gold, and the two winter quests, Trapper Santa and Find the Cub. The Seasonal Shop will only be open until January 31st, so don't wait! + p.small.muted by SabreCat and Lemoness + tr + td + h5 Flagging Posts + p You can now report inappropriate posts to moderators simply by clicking the new flag button next to the post. You should only report posts that violate the Community Guidelines and/or Terms of Service. Thanks for helping us to keep Habitica safe and pleasant for everybody! + p.small.muted by Alys, Blade, and Matteo + tr + td + h5 Winter Plot-Line Continues + p SabreCat's news is dire. "Most of my sabertooth friends have been impossible to reach, but one thing is clear: the prides have been disappearing from the Steppes. There are also reports that something drove the mammoths to early migration and disturbed the hibernation of the terrible lizards." + p He wraps his cloak around himself as another blast of frigid wind roars through the streets. An icy winter gale has been blowing from the north, rattling the window panes and setting the pets and mounts to trembling and howling. + p "I've never seen anything like it!" says Matt the Beast Master. "Something is terrifying all my animals - even the cacti, who are normally so mighty and brave! For something to frighten a cactus..." He shakes his head. + p The stress level in Habitica is mounting. + p.small.muted Missed the previous Winter Plot-line? Catch up on the story here! + p.small.muted by Lemoness h5 12/21/2014 - Winter Wonderland Begins, Winter Class Outfits, Wintery Hair Colors, and NPC Decorations! tr td From 438516b26f8d799de6401d359fc1d9f1c00614e4 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 30 Dec 2014 22:56:40 -0600 Subject: [PATCH 198/202] fix(NYE): Correct achievement badge --- views/shared/profiles/achievements.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/profiles/achievements.jade b/views/shared/profiles/achievements.jade index 43313a5e8e..25eaf4d9a4 100644 --- a/views/shared/profiles/achievements.jade +++ b/views/shared/profiles/achievements.jade @@ -180,7 +180,7 @@ div(ng-if='::profile.achievements.costumeContest') hr div(ng-if='::profile.achievements.nye') - .achievement.achievement-valentine + .achievement.achievement-nye h5=env.t('auldAcquaintance') small =env.t('auldAcquaintanceText', {cards: "{{::profile.achievements.nye}}"}) From ccfe48099fda85f7af5c354533637c1c16b15b96 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Tue, 30 Dec 2014 23:25:34 -0600 Subject: [PATCH 199/202] fix(NYE): fix cards Improve random messages and have Bailey give instruction on where to find cards. --- views/shared/modals/limited.jade | 2 +- views/shared/new-stuff.jade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/views/shared/modals/limited.jade b/views/shared/modals/limited.jade index 9099754893..bdf7049fa9 100644 --- a/views/shared/modals/limited.jade +++ b/views/shared/modals/limited.jade @@ -39,7 +39,7 @@ script(id='modals/nye.html', type='text/ng-template') .bg-info(style='padding:10px') p To: {{user.profile.name}}, From: {{user.items.special.nyeReceived[0]}} hr - ul.list-unstyled(ng-switch='Math.floor(Math.random()*5)') + ul.list-unstyled(ng-switch='::Math.floor(Math.random()*5)') li(ng-switch-when='0') =env.t('newYear0') li(ng-switch-when='1') diff --git a/views/shared/new-stuff.jade b/views/shared/new-stuff.jade index 996f33382b..ad4c8fca75 100644 --- a/views/shared/new-stuff.jade +++ b/views/shared/new-stuff.jade @@ -9,7 +9,7 @@ h5 NEW YEAR'S EVE CELEBRATION: Party Hats, New Year's Cards, Snowballs, and Last td h5 New Year's Cards (Until Jan 1st Only!) .inventory_special_nye.pull-right - p Until January 1st only, the Seasonal Shop is stocking New Year's Cards! Now you can send cards to your friends (and yourself) to wish them a Happy Habit New Year. All senders and recipients will receive the Auld Acquaintance badge! + p Until January 1st only, the Seasonal Shop is stocking New Year's Cards! Now you can send cards to your friends (and yourself) to wish them a Happy Habit New Year. All senders and recipients will receive the Auld Acquaintance badge! When you receive a card, it will appear in your Inventory. Click it to receive a seasonal message! p.small.muted by Lemoness and SabreCat tr td From 774bb7bc3a4613073a45378e6c47345c86fa6664 Mon Sep 17 00:00:00 2001 From: Alice Harris Date: Wed, 31 Dec 2014 15:42:46 +1000 Subject: [PATCH 200/202] add 'items.' to 'gear.owned' --- migrations/20141230_new_years_hats.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/migrations/20141230_new_years_hats.js b/migrations/20141230_new_years_hats.js index 08e6a542fe..595027be0a 100644 --- a/migrations/20141230_new_years_hats.js +++ b/migrations/20141230_new_years_hats.js @@ -1,11 +1,11 @@ db.users.update( - {'gear.owned.head_special_nye':{$ne:null}}, - {$set:{'gear.owned.head_special_nye2014':false}}, + {'items.gear.owned.head_special_nye':{$ne:null}}, + {$set:{'items.gear.owned.head_special_nye2014':false}}, {multi:1} ) db.users.update( - {'gear.owned.head_special_nye':null}, - {$set:{'gear.owned.head_special_nye':false}}, + {'items.gear.owned.head_special_nye':null}, + {$set:{'items.gear.owned.head_special_nye':false}}, {multi:1} ) From 5d958b14566b223d1d8d0f10f75cfa102a660e96 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Wed, 31 Dec 2014 09:31:56 -0600 Subject: [PATCH 201/202] Adjusted styles for snowball and spookDust so they would align like the coins --- public/css/items.styl | 2 +- views/shared/tasks/lists.jade | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/css/items.styl b/public/css/items.styl index 35dd7e7ef2..213e499835 100644 --- a/public/css/items.styl +++ b/public/css/items.styl @@ -4,7 +4,7 @@ line-height: 1.5em padding-left: 0.75em -.shop_gold, .shop_silver, .shop_copper +.shop_gold, .shop_silver, .shop_copper, .shop_spell vertical-align: top display: inline-block diff --git a/views/shared/tasks/lists.jade b/views/shared/tasks/lists.jade index e73661adfc..372410dda7 100644 --- a/views/shared/tasks/lists.jade +++ b/views/shared/tasks/lists.jade @@ -103,13 +103,13 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template") span.task-notes span.glyphicon.glyphicon-comment //left-hand size commands - .task-controls + .task-controls.task-primary a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.#{k})') if canceler span.shop_gold span.reward-cost {{Content.spells.special.#{k}.value}} else - span(class='shop_#{k}',style='display:inline-block;vertical-align:top;') + span.shop_spell(class='shop_#{k}') span.reward-cost {{user.items.special.#{k}}} // main content p.task-text {{Content.spells.special.#{k}.text()}} From 5f179baf6009ca13488bf91afc7985adc0448c2e Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Wed, 31 Dec 2014 10:41:40 -0600 Subject: [PATCH 202/202] Adjusted look of tasks on mobile devices --- public/css/index.styl | 4 +++- public/css/shared.styl | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/css/index.styl b/public/css/index.styl index 998fcad48e..d7e50fb345 100644 --- a/public/css/index.styl +++ b/public/css/index.styl @@ -60,6 +60,8 @@ html, body @media (max-width: 600px) #wrap margin-top: 0 + padding: 0 4px; + overflow-x: hidden; /* Gems -------------------------------------------------- */ @@ -180,4 +182,4 @@ a.label .white, .white a color: #fff !important .line-through - text-decoration line-through \ No newline at end of file + text-decoration line-through diff --git a/public/css/shared.styl b/public/css/shared.styl index 71e7f9f48a..d05ebcbb95 100644 --- a/public/css/shared.styl +++ b/public/css/shared.styl @@ -48,5 +48,3 @@ a.hint:hover .tasks-lists .row .col-md-3 padding-right: 0 padding-left: 0 - .task-column - padding: 0