Merge branch 'delete-comments'
commit
aa6ba4fe77
|
@ -32,19 +32,16 @@ module = angular.module("taigaCommon")
|
||||||
|
|
||||||
|
|
||||||
class HistoryController extends taiga.Controller
|
class HistoryController extends taiga.Controller
|
||||||
@.$inject = ["$scope", "$tgRepo"]
|
@.$inject = ["$scope", "$tgRepo", "$tgResources"]
|
||||||
|
|
||||||
constructor: (@scope, @repo) ->
|
constructor: (@scope, @repo, @rs) ->
|
||||||
|
|
||||||
initialize: (type, objectId) ->
|
initialize: (type, objectId) ->
|
||||||
@.type = type
|
@.type = type
|
||||||
@.objectId = objectId
|
@.objectId = objectId
|
||||||
|
|
||||||
getHistory: (type, objectId) ->
|
loadHistory: (type, objectId) ->
|
||||||
return @repo.queryOneRaw("history/#{type}", objectId)
|
return @rs.history.get(type, objectId).then (history) =>
|
||||||
|
|
||||||
loadHistory: ->
|
|
||||||
return @.getHistory(@.type, @.objectId).then (history) =>
|
|
||||||
for historyResult in history
|
for historyResult in history
|
||||||
# If description was modified take only the description_html field
|
# If description was modified take only the description_html field
|
||||||
if historyResult.values_diff.description_diff?
|
if historyResult.values_diff.description_diff?
|
||||||
|
@ -56,6 +53,12 @@ class HistoryController extends taiga.Controller
|
||||||
@scope.history = history
|
@scope.history = history
|
||||||
@scope.comments = _.filter(history, (item) -> item.comment != "")
|
@scope.comments = _.filter(history, (item) -> item.comment != "")
|
||||||
|
|
||||||
|
deleteComment: (type, objectId, activityId) ->
|
||||||
|
return @rs.history.deleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
|
||||||
|
|
||||||
|
undeleteComment: (type, objectId, activityId) ->
|
||||||
|
return @rs.history.undeleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId)
|
||||||
|
|
||||||
|
|
||||||
HistoryDirective = ($log) ->
|
HistoryDirective = ($log) ->
|
||||||
templateChangeDiff = _.template("""
|
templateChangeDiff = _.template("""
|
||||||
|
@ -128,6 +131,21 @@ HistoryDirective = ($log) ->
|
||||||
</div>
|
</div>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
templateDeletedComment = _.template("""
|
||||||
|
<div class="activity-single comment deleted-comment">
|
||||||
|
<div>
|
||||||
|
<span>Comment deleted by <%- deleteCommentUser %> on <%- deleteCommentDate %></span>
|
||||||
|
<a href="" title="Show comment" class="show-deleted-comment">(Show deleted comment)</a>
|
||||||
|
<a href="" title="Show comment" class="hide-deleted-comment hidden">(Hide deleted comment)</a>
|
||||||
|
<div class="comment-body wysiwyg"><%= deleteComment %></div>
|
||||||
|
</div>
|
||||||
|
<a href="" class="comment-restore" data-activity-id="<%- activityId %>">
|
||||||
|
<span class="icon icon-reload"></span>
|
||||||
|
<span>Restore comment</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
""")
|
||||||
|
|
||||||
templateActivity = _.template("""
|
templateActivity = _.template("""
|
||||||
<div class="activity-single <%- mode %>">
|
<div class="activity-single <%- mode %>">
|
||||||
<div class="activity-user">
|
<div class="activity-user">
|
||||||
|
@ -146,9 +164,17 @@ HistoryDirective = ($log) ->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if (comment.length > 0) { %>
|
<% if (comment.length > 0) { %>
|
||||||
<div class="comment wysiwyg">
|
<% if ((deleteCommentDate || deleteCommentUser)) { %>
|
||||||
<%= comment %>
|
<div class="deleted-comment">
|
||||||
</div>
|
<span>Comment deleted by <%- deleteCommentUser %> on <%- deleteCommentDate %></span>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
<div class="comment wysiwyg">
|
||||||
|
<%= comment %>
|
||||||
|
</div>
|
||||||
|
<% if (!deleteCommentDate && mode !== "activity") { %>
|
||||||
|
<a href="" class="icon icon-delete comment-delete" data-activity-id="<%- activityId %>"></a>
|
||||||
|
<% } %>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<% if(changes.length > 0) { %>
|
<% if(changes.length > 0) { %>
|
||||||
|
@ -170,6 +196,7 @@ HistoryDirective = ($log) ->
|
||||||
""")
|
""")
|
||||||
|
|
||||||
templateBaseEntries = _.template("""
|
templateBaseEntries = _.template("""
|
||||||
|
|
||||||
<% if (showMore > 0) { %>
|
<% if (showMore > 0) { %>
|
||||||
<a href="" title="Show more" class="show-more show-more-comments">
|
<a href="" title="Show more" class="show-more show-more-comments">
|
||||||
+ Show previous entries (<%- showMore %> more)
|
+ Show previous entries (<%- showMore %> more)
|
||||||
|
@ -226,7 +253,7 @@ HistoryDirective = ($log) ->
|
||||||
objectId = model.id
|
objectId = model.id
|
||||||
|
|
||||||
$ctrl.initialize(type, objectId)
|
$ctrl.initialize(type, objectId)
|
||||||
$ctrl.loadHistory()
|
$ctrl.loadHistory(type, objectId)
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
getHumanizedFieldName = (field) ->
|
getHumanizedFieldName = (field) ->
|
||||||
|
@ -332,6 +359,14 @@ HistoryDirective = ($log) ->
|
||||||
return "Made #{size} changes" # TODO: i18n
|
return "Made #{size} changes" # TODO: i18n
|
||||||
|
|
||||||
renderComment = (comment) ->
|
renderComment = (comment) ->
|
||||||
|
if (comment.delete_comment_date or comment.delete_comment_user)
|
||||||
|
return templateDeletedComment({
|
||||||
|
deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm")
|
||||||
|
deleteCommentUser: getUserFullName(comment.delete_comment_user)
|
||||||
|
deleteComment: comment.comment_html
|
||||||
|
activityId: comment.id
|
||||||
|
})
|
||||||
|
|
||||||
return templateActivity({
|
return templateActivity({
|
||||||
avatar: getUserAvatar(comment.user.pk)
|
avatar: getUserAvatar(comment.user.pk)
|
||||||
userFullName: getUserFullName(comment.user.pk)
|
userFullName: getUserFullName(comment.user.pk)
|
||||||
|
@ -340,6 +375,9 @@ HistoryDirective = ($log) ->
|
||||||
changesText: renderChangesHelperText(comment)
|
changesText: renderChangesHelperText(comment)
|
||||||
changes: renderChangeEntries(comment, false)
|
changes: renderChangeEntries(comment, false)
|
||||||
mode: "comment"
|
mode: "comment"
|
||||||
|
deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date
|
||||||
|
deleteCommentUser: getUserFullName(comment.delete_comment_user) if comment.delete_comment_user
|
||||||
|
activityId: comment.id
|
||||||
})
|
})
|
||||||
|
|
||||||
renderChange = (change) ->
|
renderChange = (change) ->
|
||||||
|
@ -351,6 +389,9 @@ HistoryDirective = ($log) ->
|
||||||
changes: renderChangeEntries(change, false)
|
changes: renderChangeEntries(change, false)
|
||||||
changesText: ""
|
changesText: ""
|
||||||
mode: "activity"
|
mode: "activity"
|
||||||
|
deleteCommentDate: moment(change.delete_comment_date).format("DD MMM YYYY HH:mm") if change.delete_comment_date
|
||||||
|
deleteCommentUser: getUserFullName(change.delete_comment_user) if change.delete_comment_user
|
||||||
|
activityId: change.id
|
||||||
})
|
})
|
||||||
|
|
||||||
renderHistory = (entries, totalEntries) ->
|
renderHistory = (entries, totalEntries) ->
|
||||||
|
@ -388,7 +429,7 @@ HistoryDirective = ($log) ->
|
||||||
$scope.$watch("comments", renderComments)
|
$scope.$watch("comments", renderComments)
|
||||||
$scope.$watch("history", renderActivity)
|
$scope.$watch("history", renderActivity)
|
||||||
|
|
||||||
$scope.$on("history:reload", -> $ctrl.loadHistory())
|
$scope.$on("history:reload", -> $ctrl.loadHistory(type, objectId))
|
||||||
|
|
||||||
# Events
|
# Events
|
||||||
|
|
||||||
|
@ -397,7 +438,7 @@ HistoryDirective = ($log) ->
|
||||||
|
|
||||||
$el.find(".comment-list").addClass("activeanimation")
|
$el.find(".comment-list").addClass("activeanimation")
|
||||||
onSuccess = ->
|
onSuccess = ->
|
||||||
$ctrl.loadHistory()
|
$ctrl.loadHistory(type, objectId)
|
||||||
|
|
||||||
onError = ->
|
onError = ->
|
||||||
$confirm.notify("error")
|
$confirm.notify("error")
|
||||||
|
@ -416,6 +457,20 @@ HistoryDirective = ($log) ->
|
||||||
showAllComments = not showAllComments
|
showAllComments = not showAllComments
|
||||||
renderComments()
|
renderComments()
|
||||||
|
|
||||||
|
$el.on "click", ".show-deleted-comment", (event) ->
|
||||||
|
event.preventDefault()
|
||||||
|
target = angular.element(event.currentTarget)
|
||||||
|
target.parents('.activity-single').find('.hide-deleted-comment').show()
|
||||||
|
target.parents('.activity-single').find('.show-deleted-comment').hide()
|
||||||
|
target.parents('.activity-single').find('.comment-body').show()
|
||||||
|
|
||||||
|
$el.on "click", ".hide-deleted-comment", (event) ->
|
||||||
|
event.preventDefault()
|
||||||
|
target = angular.element(event.currentTarget)
|
||||||
|
target.parents('.activity-single').find('.hide-deleted-comment').hide()
|
||||||
|
target.parents('.activity-single').find('.show-deleted-comment').show()
|
||||||
|
target.parents('.activity-single').find('.comment-body').hide()
|
||||||
|
|
||||||
$el.on "click", ".changes-title", (event) ->
|
$el.on "click", ".changes-title", (event) ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
target = angular.element(event.currentTarget)
|
target = angular.element(event.currentTarget)
|
||||||
|
@ -428,6 +483,16 @@ HistoryDirective = ($log) ->
|
||||||
$el.find(".history-tabs li a").toggleClass("active")
|
$el.find(".history-tabs li a").toggleClass("active")
|
||||||
$el.find(".history section").toggleClass("hidden")
|
$el.find(".history section").toggleClass("hidden")
|
||||||
|
|
||||||
|
$el.on "click", ".comment-delete", (event) ->
|
||||||
|
target = angular.element(event.currentTarget)
|
||||||
|
activityId = target.data('activity-id')
|
||||||
|
$ctrl.deleteComment(type, objectId, activityId)
|
||||||
|
|
||||||
|
$el.on "click", ".comment-restore", (event) ->
|
||||||
|
target = angular.element(event.currentTarget)
|
||||||
|
activityId = target.data('activity-id')
|
||||||
|
$ctrl.undeleteComment(type, objectId, activityId)
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
|
||||||
|
|
|
@ -130,5 +130,6 @@ module.run([
|
||||||
"$tgSearchResourcesProvider",
|
"$tgSearchResourcesProvider",
|
||||||
"$tgAttachmentsResourcesProvider",
|
"$tgAttachmentsResourcesProvider",
|
||||||
"$tgMdRenderResourcesProvider",
|
"$tgMdRenderResourcesProvider",
|
||||||
|
"$tgHistoryResourcesProvider",
|
||||||
initResources
|
initResources
|
||||||
])
|
])
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
###
|
||||||
|
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||||
|
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# File: modules/resources/history.coffee
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
taiga = @.taiga
|
||||||
|
|
||||||
|
resourceProvider = ($repo, $http, $urls) ->
|
||||||
|
service = {}
|
||||||
|
|
||||||
|
service.get = (type, objectId) ->
|
||||||
|
return $repo.queryOneRaw("history/#{type}", objectId)
|
||||||
|
|
||||||
|
service.deleteComment = (type, objectId, activityId) ->
|
||||||
|
url = $urls.resolve("history/#{type}")
|
||||||
|
url = "#{url}/#{objectId}/delete_comment"
|
||||||
|
params = {id: activityId}
|
||||||
|
return $http.post(url, null, params).then (data) =>
|
||||||
|
return data.data
|
||||||
|
|
||||||
|
service.undeleteComment = (type, objectId, activityId) ->
|
||||||
|
url = $urls.resolve("history/#{type}")
|
||||||
|
url = "#{url}/#{objectId}/undelete_comment"
|
||||||
|
params = {id: activityId}
|
||||||
|
return $http.post(url, null, params).then (data) =>
|
||||||
|
return data.data
|
||||||
|
|
||||||
|
return (instance) ->
|
||||||
|
instance.history = service
|
||||||
|
|
||||||
|
|
||||||
|
module = angular.module("taigaResources")
|
||||||
|
module.factory("$tgHistoryResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", resourceProvider])
|
|
@ -4,7 +4,7 @@ $black: #000;
|
||||||
$blackish: #050505;
|
$blackish: #050505;
|
||||||
$gray: #555;
|
$gray: #555;
|
||||||
$grayer: #444;
|
$grayer: #444;
|
||||||
$gray-light: #cdcdcd;
|
$gray-light: #b8b8b8;
|
||||||
$whitish: #f5f5f5;
|
$whitish: #f5f5f5;
|
||||||
$very-light-gray: #fcfcfc;
|
$very-light-gray: #fcfcfc;
|
||||||
$white: #fff;
|
$white: #fff;
|
||||||
|
|
|
@ -2,26 +2,23 @@
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.changes-title {
|
.changes-title {
|
||||||
display: block;
|
display: block;
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
&:hover {
|
&:hover {
|
||||||
.icon {
|
.icon {
|
||||||
@include transform(rotate(180deg));
|
@include transform(rotate(180deg));
|
||||||
@include transition(all.2s linear);
|
@include transition(all .2s linear);
|
||||||
color: $green-taiga;
|
color: $green-taiga;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.icon {
|
.icon {
|
||||||
@include transform(rotate(0));
|
@include transform(rotate(0));
|
||||||
@include transition(all.2s linear);
|
@include transition(all .2s linear);
|
||||||
color: $grayer;
|
color: $grayer;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.change-entry {
|
.change-entry {
|
||||||
@include table-flex;
|
@include table-flex;
|
||||||
border-bottom: 1px solid $gray-light;
|
border-bottom: 1px solid $gray-light;
|
||||||
|
@ -40,7 +37,6 @@
|
||||||
@extend %small;
|
@extend %small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-tabs {
|
.history-tabs {
|
||||||
@extend %title;
|
@extend %title;
|
||||||
border-bottom: 3px solid $gray-light;
|
border-bottom: 3px solid $gray-light;
|
||||||
|
@ -68,7 +64,6 @@
|
||||||
margin-right: .5rem;
|
margin-right: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-comment {
|
.add-comment {
|
||||||
@include clearfix;
|
@include clearfix;
|
||||||
textarea {
|
textarea {
|
||||||
|
@ -94,7 +89,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.show-more-comments {
|
a.show-more-comments {
|
||||||
@extend %small;
|
@extend %small;
|
||||||
border-bottom: 1px solid $gray-light;
|
border-bottom: 1px solid $gray-light;
|
||||||
|
@ -107,7 +101,6 @@ a.show-more-comments {
|
||||||
background: lighten($green-taiga, 60%);
|
background: lighten($green-taiga, 60%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-comments {
|
.more-comments {
|
||||||
@extend %small;
|
@extend %small;
|
||||||
border-bottom: 1px solid $gray-light;
|
border-bottom: 1px solid $gray-light;
|
||||||
|
@ -119,7 +112,6 @@ a.show-more-comments {
|
||||||
margin-left: .5rem;
|
margin-left: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-list {
|
.comment-list {
|
||||||
&.activeanimation {
|
&.activeanimation {
|
||||||
.comment-single.ng-enter:last-child,
|
.comment-single.ng-enter:last-child,
|
||||||
|
@ -136,15 +128,66 @@ a.show-more-comments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-single {
|
.activity-single {
|
||||||
@include table-flex;
|
@include table-flex;
|
||||||
border-bottom: 2px solid $gray-light;
|
border-bottom: 1px solid $gray-light;
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
.comment-delete {
|
||||||
|
@include transition(opacity .2s linear);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.comment-restore {
|
||||||
|
@include transition(opacity .2s linear);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
&.deleted-comment,
|
||||||
|
.deleted-comment {
|
||||||
|
@extend %small;
|
||||||
|
color: $gray-light;
|
||||||
|
padding: .5rem;
|
||||||
|
a {
|
||||||
|
color: $gray-light;
|
||||||
|
margin-left: .3rem;
|
||||||
|
&:hover {
|
||||||
|
@include transition(color .2s linear);
|
||||||
|
color: $green-taiga;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
@include filter(grayscale(100%));
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
.comment-body {
|
||||||
|
display: none;
|
||||||
|
margin: .2rem 0 .5rem;
|
||||||
|
p {
|
||||||
|
@extend %medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.comment-restore {
|
||||||
|
@extend %small;
|
||||||
|
color: $gray-light;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: .4rem;
|
||||||
|
.icon {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
@include transition(color .2s linear);
|
||||||
|
color: $green-taiga;
|
||||||
|
}
|
||||||
|
}
|
||||||
.username {
|
.username {
|
||||||
color: $green-taiga;
|
color: $green-taiga;
|
||||||
margin-bottom: .5rem;
|
margin-bottom: .5rem;
|
||||||
|
@ -158,18 +201,16 @@ a.show-more-comments {
|
||||||
}
|
}
|
||||||
.activity-username {
|
.activity-username {
|
||||||
color: $green-taiga;
|
color: $green-taiga;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-content {
|
.activity-content {
|
||||||
@include table-flex-child(20, 150px, 0);
|
@include table-flex-child(20, 150px, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.changes {
|
.changes {
|
||||||
background: $whitish;
|
background: $whitish;
|
||||||
.change-entry {
|
.change-entry {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
@ -180,18 +221,27 @@ a.show-more-comments {
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wysiwyg {
|
.wysiwyg {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
.comment-delete {
|
||||||
|
@include transition(all .2s linear);
|
||||||
|
color: $red;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 2rem;
|
||||||
|
&:hover {
|
||||||
|
@include transition(color .2s linear);
|
||||||
|
color: $red-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
&.activity {
|
&.activity {
|
||||||
.change-entry {
|
.change-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.more-activity {
|
.more-activity {
|
||||||
@extend %small;
|
@extend %small;
|
||||||
border-bottom: 1px solid $gray-light;
|
border-bottom: 1px solid $gray-light;
|
||||||
|
|
Loading…
Reference in New Issue