commit
515df55db2
|
@ -64,16 +64,36 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
|
||||||
loadProject: ->
|
loadProject: ->
|
||||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||||
@scope.project = project
|
@scope.project = project
|
||||||
|
|
||||||
@scope.$emit('project:loaded', project)
|
@scope.$emit('project:loaded', project)
|
||||||
@scope.anyComputableRole = _.some(_.map(project.roles, (point) -> point.computable))
|
@scope.anyComputableRole = _.some(_.map(project.roles, (point) -> point.computable))
|
||||||
|
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
loadExternalUserRole: (roles) ->
|
||||||
|
roles = roles.map (role) ->
|
||||||
|
role.external_user = false
|
||||||
|
|
||||||
|
return role
|
||||||
|
|
||||||
|
public_permission = {
|
||||||
|
"name": "External User",
|
||||||
|
"permissions": @scope.project.public_permissions,
|
||||||
|
"external_user": true
|
||||||
|
}
|
||||||
|
|
||||||
|
roles.push(public_permission)
|
||||||
|
|
||||||
|
return roles
|
||||||
|
|
||||||
loadRoles: ->
|
loadRoles: ->
|
||||||
return @rs.roles.list(@scope.projectId).then (data) =>
|
return @rs.roles.list(@scope.projectId)
|
||||||
@scope.roles = data
|
.then @loadExternalUserRole
|
||||||
@scope.role = @scope.roles[0]
|
.then (roles) =>
|
||||||
return data
|
@scope.roles = roles
|
||||||
|
@scope.role = @scope.roles[0]
|
||||||
|
|
||||||
|
return roles
|
||||||
|
|
||||||
loadInitialData: ->
|
loadInitialData: ->
|
||||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||||
|
@ -256,7 +276,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
||||||
<div class="category-item" data-id="<%- permission.key %>">
|
<div class="category-item" data-id="<%- permission.key %>">
|
||||||
<span><%- permission.description %></span>
|
<span><%- permission.description %></span>
|
||||||
<div class="check">
|
<div class="check">
|
||||||
<input type="checkbox" <% if(permission.active) { %>checked="checked"<% } %>/>
|
<input type="checkbox" <% if(!permission.editable) { %>disabled="disabled"<% } %> <% if(permission.active) { %>checked="checked"<% } %>/>
|
||||||
<div></div>
|
<div></div>
|
||||||
<span class="check-text check-yes">Yes</span>
|
<span class="check-text check-yes">Yes</span>
|
||||||
<span class="check-text check-no">No</span>
|
<span class="check-text check-no">No</span>
|
||||||
|
@ -279,10 +299,23 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
||||||
setActivePermissions = (permissions) ->
|
setActivePermissions = (permissions) ->
|
||||||
return _.map(permissions, (x) -> _.extend({}, x, {active: x["key"] in role.permissions}))
|
return _.map(permissions, (x) -> _.extend({}, x, {active: x["key"] in role.permissions}))
|
||||||
|
|
||||||
|
isPermissionEditable = (permission, role, project) ->
|
||||||
|
if role.external_user &&
|
||||||
|
!project.is_private &&
|
||||||
|
permission.key.indexOf("view_") == 0
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
|
||||||
setActivePermissionsPerCategory = (category) ->
|
setActivePermissionsPerCategory = (category) ->
|
||||||
return _.map(category, (x) ->
|
return _.map(category, (cat) ->
|
||||||
_.extend({}, x, {
|
cat.permissions = cat.permissions.map (permission) ->
|
||||||
activePermissions: _.filter(x["permissions"], "active").length
|
permission.editable = isPermissionEditable(permission, role, $scope.project)
|
||||||
|
|
||||||
|
return permission
|
||||||
|
|
||||||
|
_.extend({}, cat, {
|
||||||
|
activePermissions: _.filter(cat["permissions"], "active").length
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -366,10 +399,11 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
||||||
return activePermissions
|
return activePermissions
|
||||||
|
|
||||||
target = angular.element(event.currentTarget)
|
target = angular.element(event.currentTarget)
|
||||||
|
|
||||||
$scope.role.permissions = getActivePermissions()
|
$scope.role.permissions = getActivePermissions()
|
||||||
|
|
||||||
onSuccess = (role) ->
|
onSuccess = () ->
|
||||||
categories = generateCategoriesFromRole(role)
|
categories = generateCategoriesFromRole($scope.role)
|
||||||
categoryId = target.parents(".category-config").data("id")
|
categoryId = target.parents(".category-config").data("id")
|
||||||
renderResume(target.parents(".category-config"), categories[categoryId])
|
renderResume(target.parents(".category-config"), categories[categoryId])
|
||||||
$rootscope.$broadcast("projects:reload")
|
$rootscope.$broadcast("projects:reload")
|
||||||
|
@ -381,7 +415,14 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
||||||
target.prop "checked", !target.prop("checked")
|
target.prop "checked", !target.prop("checked")
|
||||||
$scope.role.permissions = getActivePermissions()
|
$scope.role.permissions = getActivePermissions()
|
||||||
|
|
||||||
$repo.save($scope.role).then onSuccess, onError
|
if $scope.role.external_user
|
||||||
|
$scope.project.public_permissions = $scope.role.permissions
|
||||||
|
$scope.project.anon_permissions = $scope.role.permissions.filter (permission) ->
|
||||||
|
return permission.indexOf("view_") == 0
|
||||||
|
|
||||||
|
$repo.save($scope.project).then onSuccess, onError
|
||||||
|
else
|
||||||
|
$repo.save($scope.role).then onSuccess, onError
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
|
|
@ -258,7 +258,7 @@ HistoryDirective = ($log, $loading, $qqueue, $template, $confirm) ->
|
||||||
deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date
|
deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date
|
||||||
deleteCommentUser: comment.delete_comment_user.name if comment.delete_comment_user?.name
|
deleteCommentUser: comment.delete_comment_user.name if comment.delete_comment_user?.name
|
||||||
activityId: comment.id
|
activityId: comment.id
|
||||||
canDeleteComment: comment.user.pk == $scope.user.id or $scope.project.my_permissions.indexOf("modify_project") > -1
|
canDeleteComment: comment.user.pk == $scope.user?.id or $scope.project.my_permissions.indexOf("modify_project") > -1
|
||||||
})
|
})
|
||||||
|
|
||||||
renderChange = (change) ->
|
renderChange = (change) ->
|
||||||
|
|
|
@ -36,47 +36,52 @@ module = angular.module("taigaBacklog")
|
||||||
|
|
||||||
TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
oldParentScope = null
|
bindOnce $scope, "project", (project) ->
|
||||||
newParentScope = null
|
# If the user has not enough permissions we don't enable the sortable
|
||||||
itemEl = null
|
if not (project.my_permissions.indexOf("modify_us") > -1)
|
||||||
tdom = $el
|
return
|
||||||
|
|
||||||
deleteElement = (itemEl) ->
|
oldParentScope = null
|
||||||
# Completelly remove item and its scope from dom
|
newParentScope = null
|
||||||
itemEl.scope().$destroy()
|
itemEl = null
|
||||||
itemEl.off()
|
tdom = $el
|
||||||
itemEl.remove()
|
|
||||||
|
|
||||||
tdom.sortable({
|
deleteElement = (itemEl) ->
|
||||||
handle: ".taskboard-task-inner",
|
# Completelly remove item and its scope from dom
|
||||||
dropOnEmpty: true
|
itemEl.scope().$destroy()
|
||||||
connectWith: ".taskboard-tasks-box"
|
itemEl.off()
|
||||||
revert: 400
|
itemEl.remove()
|
||||||
})
|
|
||||||
|
|
||||||
tdom.on "sortstop", (event, ui) ->
|
tdom.sortable({
|
||||||
parentEl = ui.item.parent()
|
handle: ".taskboard-task-inner",
|
||||||
itemEl = ui.item
|
dropOnEmpty: true
|
||||||
itemTask = itemEl.scope().task
|
connectWith: ".taskboard-tasks-box"
|
||||||
itemIndex = itemEl.index()
|
revert: 400
|
||||||
newParentScope = parentEl.scope()
|
})
|
||||||
|
|
||||||
oldUsId = if oldParentScope.us then oldParentScope.us.id else null
|
tdom.on "sortstop", (event, ui) ->
|
||||||
oldStatusId = oldParentScope.st.id
|
parentEl = ui.item.parent()
|
||||||
newUsId = if newParentScope.us then newParentScope.us.id else null
|
itemEl = ui.item
|
||||||
newStatusId = newParentScope.st.id
|
itemTask = itemEl.scope().task
|
||||||
|
itemIndex = itemEl.index()
|
||||||
|
newParentScope = parentEl.scope()
|
||||||
|
|
||||||
if newStatusId != oldStatusId or newUsId != oldUsId
|
oldUsId = if oldParentScope.us then oldParentScope.us.id else null
|
||||||
deleteElement(itemEl)
|
oldStatusId = oldParentScope.st.id
|
||||||
|
newUsId = if newParentScope.us then newParentScope.us.id else null
|
||||||
|
newStatusId = newParentScope.st.id
|
||||||
|
|
||||||
$scope.$apply ->
|
if newStatusId != oldStatusId or newUsId != oldUsId
|
||||||
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
|
deleteElement(itemEl)
|
||||||
|
|
||||||
ui.item.find('a').removeClass('noclick')
|
$scope.$apply ->
|
||||||
|
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
|
||||||
|
|
||||||
tdom.on "sortstart", (event, ui) ->
|
ui.item.find('a').removeClass('noclick')
|
||||||
oldParentScope = ui.item.parent().scope()
|
|
||||||
ui.item.find('a').addClass('noclick')
|
tdom.on "sortstart", (event, ui) ->
|
||||||
|
oldParentScope = ui.item.parent().scope()
|
||||||
|
ui.item.find('a').addClass('noclick')
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
|
|
@ -69,18 +69,18 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
loadMembers: ->
|
loadMembers: ->
|
||||||
return @rs.memberships.list(@scope.projectId, {}, false).then (data) =>
|
return @rs.memberships.list(@scope.projectId, {}, false).then (data) =>
|
||||||
currentUser = @auth.getUser()
|
currentUser = @auth.getUser()
|
||||||
if not currentUser.photo?
|
if currentUser? and not currentUser.photo?
|
||||||
currentUser.photo = "/images/unnamed.png"
|
currentUser.photo = "/images/unnamed.png"
|
||||||
|
|
||||||
@scope.currentUser = _.find data, (membership) =>
|
@scope.currentUser = _.find data, (membership) =>
|
||||||
return membership.user == currentUser.id
|
return currentUser? and membership.user == currentUser.id
|
||||||
|
|
||||||
@scope.totals = {}
|
@scope.totals = {}
|
||||||
_.forEach data, (membership) =>
|
_.forEach data, (membership) =>
|
||||||
@scope.totals[membership.user] = 0
|
@scope.totals[membership.user] = 0
|
||||||
|
|
||||||
@scope.memberships = _.filter data, (membership) =>
|
@scope.memberships = _.filter data, (membership) =>
|
||||||
if membership.user && membership.user != currentUser.id && membership.is_user_active
|
if membership.user && (not currentUser? or membership.user != currentUser.id) && membership.is_user_active
|
||||||
return membership
|
return membership
|
||||||
|
|
||||||
for membership in @scope.memberships
|
for membership in @scope.memberships
|
||||||
|
|
|
@ -238,6 +238,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
|
||||||
$el.on "mouseup", ".view-wiki-content", (event) ->
|
$el.on "mouseup", ".view-wiki-content", (event) ->
|
||||||
target = angular.element(event.target)
|
target = angular.element(event.target)
|
||||||
return if getSelectedText()
|
return if getSelectedText()
|
||||||
|
return if not isEditable()
|
||||||
return if target.is('a')
|
return if target.is('a')
|
||||||
return if target.is('pre')
|
return if target.is('pre')
|
||||||
|
|
||||||
|
|
|
@ -40,16 +40,14 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl"
|
||||||
tg-privacy-settings-inputs
|
tg-privacy-settings-inputs
|
||||||
div.privacy-settings
|
div.privacy-settings
|
||||||
div
|
div
|
||||||
input.hidden(type="radio", disabled="disabled")
|
input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="false")
|
||||||
label.trans-button(for="public-project")
|
label.trans-button(for="public-project")
|
||||||
span Public Project
|
span Public Project
|
||||||
div
|
div
|
||||||
input.hidden(type="radio", checked="checked", disabled="disabled")
|
input.privacy-project(type="radio", name="private-project", ng-model="project.is_private", ng-value="true")
|
||||||
label.trans-button(for="private-project")
|
label.trans-button(for="private-project")
|
||||||
span Private Project
|
span Private Project
|
||||||
|
|
||||||
p All projects are private during Taiga's beta period.
|
|
||||||
|
|
||||||
button.button-green.submit-button(type="submit", title="Save") Save
|
button.button-green.submit-button(type="submit", title="Save") Save
|
||||||
a.delete-project(href="", title="Delete this project", ng-click="ctrl.openDeleteLightbox()") Delete this project
|
a.delete-project(href="", title="Delete this project", ng-click="ctrl.openDeleteLightbox()") Delete this project
|
||||||
|
|
||||||
|
|
|
@ -8,28 +8,33 @@ div.wrapper.roles(ng-controller="RolesController as ctrl",
|
||||||
section.main.admin-roles.admin-common
|
section.main.admin-roles.admin-common
|
||||||
.header-with-actions
|
.header-with-actions
|
||||||
include ../includes/components/mainTitle
|
include ../includes/components/mainTitle
|
||||||
.action-buttons
|
.action-buttons(ng-if="!role.external_user")
|
||||||
a.button-red.delete-role(href="", title="Delete", ng-click="ctrl.delete()")
|
a.button-red.delete-role(href="", title="Delete", ng-click="ctrl.delete()")
|
||||||
span Delete
|
span Delete
|
||||||
|
|
||||||
|
|
||||||
div(tg-edit-role)
|
div(ng-if="!role.external_user")
|
||||||
.edit-role
|
div(tg-edit-role)
|
||||||
input(type="text", value="{{ role.name }}")
|
.edit-role
|
||||||
a.save.icon.icon-floppy(href="", title="Save")
|
input(type="text", value="{{ role.name }}")
|
||||||
|
a.save.icon.icon-floppy(href="", title="Save")
|
||||||
|
|
||||||
|
p.total
|
||||||
|
span.role-name(title="{{ role.members_count }} members with this role") {{ role.name }}
|
||||||
|
a.edit-value.icon.icon-edit
|
||||||
|
|
||||||
|
div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories
|
||||||
|
|
||||||
|
div.general-category
|
||||||
|
| When enabled, members assigned to this role will be able to estimate the point value for user stories
|
||||||
|
div.check
|
||||||
|
input(type="checkbox", ng-model="role.computable", ng-change="ctrl.setComputable()")
|
||||||
|
div
|
||||||
|
span.check-text.check-yes Yes
|
||||||
|
span.check-text.check-no No
|
||||||
|
|
||||||
|
div(ng-if="role.external_user")
|
||||||
p.total
|
p.total
|
||||||
span.role-name(title="{{ role.members_count }} members with this role") {{ role.name }}
|
span.role-name {{ role.name }}
|
||||||
a.edit-value.icon.icon-edit
|
|
||||||
|
|
||||||
div.any-computable-role(ng-hide="anyComputableRole") Be careful, no role in your project will be able to estimate the point value for user stories
|
|
||||||
|
|
||||||
div.general-category
|
|
||||||
| When enabled, members assigned to this role will be able to estimate the point value for user stories
|
|
||||||
div.check
|
|
||||||
input(type="checkbox", ng-model="role.computable", ng-change="ctrl.setComputable()")
|
|
||||||
div
|
|
||||||
span.check-text.check-yes Yes
|
|
||||||
span.check-text.check-no No
|
|
||||||
|
|
||||||
div(tg-role-permissions, ng-model="role")
|
div(tg-role-permissions, ng-model="role")
|
||||||
|
|
|
@ -27,7 +27,7 @@ section.table-team.basic-table
|
||||||
div.popover.attribute-explanation
|
div.popover.attribute-explanation
|
||||||
span Total Points
|
span Total Points
|
||||||
|
|
||||||
div.hero(tg-team-current-user, stats="stats", currentuser="currentUser", projectid="projectId", issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled")
|
div.hero(tg-team-current-user, stats="stats", currentuser="currentUser", projectid="projectId", issuesEnabled="issuesEnabled", tasksenabled="tasksEnabled", wikienabled="wikiEnabled", ng-if="::currentUser")
|
||||||
|
|
||||||
h2(ng-show="memberships.length")
|
h2(ng-show="memberships.length")
|
||||||
span Team >
|
span Team >
|
||||||
|
|
|
@ -44,6 +44,7 @@ div(class="menu-container")
|
||||||
span(class="icon icon-settings")
|
span(class="icon icon-settings")
|
||||||
span(class="item") Admin
|
span(class="item") Admin
|
||||||
<% } %>
|
<% } %>
|
||||||
|
<% if (user) { %>
|
||||||
div(class="user")
|
div(class="user")
|
||||||
div(class="user-settings")
|
div(class="user-settings")
|
||||||
ul(class="popover")
|
ul(class="popover")
|
||||||
|
@ -61,3 +62,4 @@ div(class="menu-container")
|
||||||
a(href="" title="Logout" class="logout") Logout
|
a(href="" title="Logout" class="logout") Logout
|
||||||
a(href="" title="User preferences" class="avatar" id="nav-user-settings")
|
a(href="" title="User preferences" class="avatar" id="nav-user-settings")
|
||||||
img(src="{{ user.photo }}" alt="{{ user.full_name_display }}")
|
img(src="{{ user.photo }}" alt="{{ user.full_name_display }}")
|
||||||
|
<% } %>
|
||||||
|
|
|
@ -24,10 +24,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
.view-wiki-content {
|
.view-wiki-content {
|
||||||
&:hover {
|
&:hover {
|
||||||
.wysiwyg {
|
|
||||||
background: $whitish;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.edit {
|
.edit {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
top: -1.5rem;
|
top: -1.5rem;
|
||||||
|
@ -67,4 +63,12 @@
|
||||||
top: .4rem;
|
top: .4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.editable {
|
||||||
|
&:hover {
|
||||||
|
.wysiwyg {
|
||||||
|
background: $whitish;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,15 @@
|
||||||
.privacy-settings {
|
.privacy-settings {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
div {
|
> div {
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-right: .5rem;
|
margin-right: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: This should change when public projects available
|
|
||||||
label {
|
label {
|
||||||
@extend %title;
|
@extend %title;
|
||||||
border: 1px solid $gray-light;
|
border: 1px solid $gray-light;
|
||||||
|
@ -39,14 +40,22 @@
|
||||||
span {
|
span {
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
}
|
}
|
||||||
// &:hover {
|
|
||||||
// border: 1px solid $fresh-taiga;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
input:checked+label {
|
}
|
||||||
|
.privacy-project {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 500px;
|
||||||
|
left: -10px;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
width: 500px;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
.privacy-project:checked {
|
||||||
|
+ label {
|
||||||
background: $fresh-taiga;
|
background: $fresh-taiga;
|
||||||
border: 1px solid $fresh-taiga;
|
border: 1px solid $fresh-taiga;
|
||||||
cursor: default;
|
|
||||||
span {
|
span {
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,5 +101,11 @@
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
input:disabled {
|
||||||
|
cursor: auto;
|
||||||
|
+ div {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue