Detail integration

stable
Alejandro Alonso 2015-02-18 15:16:11 +01:00 committed by David Barragán Merino
parent 05a89bdf32
commit c45a51f313
10 changed files with 277 additions and 23 deletions

View File

@ -0,0 +1,190 @@
###
# 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/common/custom-field-values.coffee
###
taiga = @.taiga
bindMethods = @.taiga.bindMethods
bindOnce = @.taiga.bindOnce
debounce = @.taiga.debounce
generateHash = taiga.generateHash
module = angular.module("taigaCommon")
class CustomAttributesValuesController extends taiga.Controller
@.$inject = ["$scope", "$rootScope", "$tgRepo", "$tgResources", "$tgConfirm", "$q"]
constructor: (@scope, @rootscope, @repo, @rs, @confirm, @q) ->
bindMethods(@)
@.type = null
@.objectId = null
@.projectId = null
@.customAttributes = []
@.customAttributesValues = null
initialize: (type, objectId) ->
@.project = @scope.project
@.type = type
@.objectId = objectId
@.projectId = @scope.projectId
loadCustomAttributesValues: ->
return @.customAttributesValues if not @.objectId
return @rs.customAttributesValues.get(@.type, @.objectId).then (customAttributesValues) =>
@.customAttributes = @.project["#{@.type}_custom_attributes"]
@.customAttributesValues = customAttributesValues
return customAttributesValues
getAttributeValue: (attribute) ->
attributeValue = _.clone(attribute, false)
attributeValue.value = @.customAttributesValues.attributes_values[attribute.id]
return attributeValue
updateAttributeValue: (attributeValue) ->
onSuccess = =>
onError = (response) =>
@confirm.notify("error")
return @q.reject()
# We need to update the full array so angular understand the model is modified
attributesValues = _.clone(@.customAttributesValues.attributes_values, true)
attributesValues[attributeValue.id] = attributeValue.value
@.customAttributesValues.attributes_values = attributesValues
@.customAttributesValues.id = @.objectId
return @repo.save(@.customAttributesValues).then(onSuccess, onError)
CustomAttributesValuesDirective = ($templates, $storage) ->
template = $templates.get("custom-attributes/custom-attributes-values.html", true)
collapsedHash = generateHash(["custom-attributes-collapsed"])
link = ($scope, $el, $attrs, $ctrls) ->
$ctrl = $ctrls[0]
$model = $ctrls[1]
bindOnce $scope, $attrs.ngModel, (value) ->
$ctrl.initialize($attrs.type, value.id)
$ctrl.loadCustomAttributesValues()
$el.on "click", ".custom-fields-header a", ->
collapsed = not($storage.get(collapsedHash) or false)
$storage.set(collapsedHash, collapsed)
if collapsed
$el.find(".custom-fields-header a").removeClass("open")
$el.find(".custom-fields-body").removeClass("open")
else
$el.find(".custom-fields-header a").addClass("open")
$el.find(".custom-fields-body").addClass("open")
$scope.$on "$destroy", ->
$el.off()
templateFn = ($el, $attrs) ->
collapsed = $storage.get(collapsedHash) or false
return template({
requiredEditionPerm: $attrs.requiredEditionPerm
collapsed: collapsed
})
return {
require: ["tgCustomAttributesValues", "ngModel"]
controller: CustomAttributesValuesController
controllerAs: "ctrl"
restrict: "AE"
scope: true
link: link
template: templateFn
}
module.directive("tgCustomAttributesValues", ["$tgTemplate", "$tgStorage", CustomAttributesValuesDirective])
CustomAttributeValueDirective = ($template) ->
template = $template.get("custom-attributes/custom-attribute-value.html", true)
templateEdit = $template.get("custom-attributes/custom-attribute-value-edit.html", true)
link = ($scope, $el, $attrs, $ctrl) ->
render = (attributeValue, edit=false) ->
ctx = {
id: attributeValue.id
name: attributeValue.name
description: attributeValue.description
value: attributeValue.value
isEditable: isEditable()
}
if edit
html = templateEdit(ctx)
else
html = template(ctx)
$el.html(html)
isEditable = ->
permissions = $scope.project.my_permissions
requiredEditionPerm = $attrs.requiredEditionPerm
return permissions.indexOf(requiredEditionPerm) > -1
saveAttributeValue = ->
attributeValue.value = $el.find("input").val()
$scope.$apply ->
$ctrl.updateAttributeValue(attributeValue).then ->
render(attributeValue, false)
$el.on "keyup", "input[name=description]", (event) ->
if event.keyCode == 13
submit(event)
else if event.keyCode == 27
render(attributeValue, false)
## Actions (on view mode)
$el.on "click", ".custom-field-value.read-mode", ->
return if not isEditable()
render(attributeValue, true)
$el.find("input[name='description']").focus().select()
$el.on "click", "a.icon-edit", (event) ->
event.preventDefault()
render(attributeValue, true)
$el.find("input[name='description']").focus().select()
## Actions (on edit mode)
submit = debounce 2000, (event) =>
event.preventDefault()
saveAttributeValue()
$el.on "submit", "form", submit
$el.on "click", "a.icon-floppy", submit
$scope.$on "$destroy", ->
$el.off()
# Bootstrap
attributeValue = $scope.$eval($attrs.tgCustomAttributeValue)
render(attributeValue)
return {
link: link
require: "^tgCustomAttributesValues"
restrict: "AE"
}
module.directive("tgCustomAttributeValue", ["$tgTemplate", CustomAttributeValueDirective])

View File

@ -102,6 +102,11 @@ urls = {
"attachments/task": "/tasks/attachments"
"attachments/wiki_page": "/wiki/attachments"
# Custom field values
"custom-attributes-values/userstory": "/userstories/custom-attributes-values"
"custom-attributes-values/issue": "/issues/custom-attributes-values"
"custom-attributes-values/task": "/tasks/custom-attributes-values"
# Feedback
"feedback": "/feedback"
@ -145,6 +150,7 @@ module.run([
"$tgWikiResourcesProvider",
"$tgSearchResourcesProvider",
"$tgAttachmentsResourcesProvider",
"$tgCustomAttributesValuesResourcesProvider",
"$tgMdRenderResourcesProvider",
"$tgHistoryResourcesProvider",
"$tgKanbanResourcesProvider",

View File

@ -0,0 +1,35 @@
###
# 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/custom-field-values.coffee
###
taiga = @.taiga
resourceProvider = ($rootScope, $config, $urls, $model, $repo, $auth, $q) ->
service = {}
service.get = (type, objectId) ->
return $repo.queryOne("custom-attributes-values/#{type}", objectId)
return (instance) ->
instance.customAttributesValues = service
module = angular.module("taigaResources")
module.factory("$tgCustomAttributesValuesResourcesProvider", ["$rootScope", "$tgConfig", "$tgUrls", "$tgModel", "$tgRepo",
"$tgAuth", "$q", resourceProvider])

View File

@ -0,0 +1,14 @@
form.custom-field-single
div.custom-field-data
label.custom-field-name(for="custom-field-description")
<%- name %>
<% if (description){ %>
span.custom-field-description
<%- description %>
<% } %>
div.custom-field-value
input#custom-field-description(name="description", type="text", placeholder!="<%- value %>")
div.custom-field-options
a.icon.icon-floppy(href="", title="Save Custom Field")

View File

@ -0,0 +1,17 @@
div.custom-field-single
div.custom-field-data
span.custom-field-name
<%- name %>
<% if (description){ %>
span.custom-field-description
<%- description %>
<% } %>
div.custom-field-value.read-mode
span
<%- value %>
<% if (isEditable) { %>
div.custom-field-options
a.icon.icon-edit(href="", title="Edit Custom Field")
<% } %>

View File

@ -0,0 +1,7 @@
section.duty-custom-fields(ng-show="ctrl.customAttributesValues")
div.custom-fields-header
span Custom Fields
// Remove .open class on click on this button in both .icon and .custom-fields-body to close
a.icon.icon-arrow-bottom(class!="<% if (!collapsed) { %>open<% } %>")
div.custom-fields-body(class!="<% if (!collapsed) { %>open<% } %>")
div(ng-repeat="att in ctrl.customAttributes", tg-custom-attribute-value="ctrl.getAttributeValue(att)", required-edition-perm!="<%- requiredEditionPerm %>")

View File

@ -1,21 +0,0 @@
section.duty-custom-fields
div.custom-fields-header
span Custom Fields
// Remove .open class on click on this button in both .icon and .custom-fields-body to close
a.icon.icon-arrow-bottom.open
div.custom-fields-body.open
div.custom-field-single
div.custom-field-data
span.custom-field-name Name
span.custom-field-description This is the description
div.custom-field-value
span Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec.
div.custom-field-options
a.icon.icon-edit(href="", title="Edit Custom Field")
form.custom-field-single
div.custom-field-data
label.custom-field-name(for="custom-field-description") Name
div.custom-field-value
input#custom-field-description(type="text", placeholder="This is the description")
div.custom-field-options
a.icon.icon-floppy(href="", title="Save Custom Field")

View File

@ -35,6 +35,9 @@ div.wrapper(ng-controller="IssueDetailController as ctrl",
section.duty-content(tg-editable-description, ng-model="issue", required-perm="modify_issue")
// Custom Fields
tg-custom-attributes-values(ng-model="issue", type="issue", project="project", required-edition-perm="modify_issue")
tg-attachments(ng-model="issue", type="issue")
tg-history(ng-model="issue", type="issue")

View File

@ -40,6 +40,9 @@ div.wrapper(ng-controller="TaskDetailController as ctrl",
section.duty-content(tg-editable-description, ng-model="task", required-perm="modify_task")
// Custom Fields
tg-custom-attributes-values(ng-model="task", type="task", project="project", required-edition-perm="modify_task")
tg-attachments(ng-model="task", type="task")
tg-history(ng-model="task", type="task")

View File

@ -39,8 +39,8 @@ div.wrapper(ng-controller="UserStoryDetailController as ctrl",
section.duty-content(tg-editable-description, ng-model="us", required-perm="modify_us")
// IF Custom Fields
include ../includes/modules/common/custom-fields
// Custom Fields
tg-custom-attributes-values(ng-model="us", type="userstory", project="project", required-edition-perm="modify_us")
include ../includes/modules/related-tasks