commit
515df55db2
|
@ -64,16 +64,36 @@ class RolesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fil
|
|||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
|
||||
@scope.$emit('project:loaded', project)
|
||||
@scope.anyComputableRole = _.some(_.map(project.roles, (point) -> point.computable))
|
||||
|
||||
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: ->
|
||||
return @rs.roles.list(@scope.projectId).then (data) =>
|
||||
@scope.roles = data
|
||||
@scope.role = @scope.roles[0]
|
||||
return data
|
||||
return @rs.roles.list(@scope.projectId)
|
||||
.then @loadExternalUserRole
|
||||
.then (roles) =>
|
||||
@scope.roles = roles
|
||||
@scope.role = @scope.roles[0]
|
||||
|
||||
return roles
|
||||
|
||||
loadInitialData: ->
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
|
@ -256,7 +276,7 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
|||
<div class="category-item" data-id="<%- permission.key %>">
|
||||
<span><%- permission.description %></span>
|
||||
<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>
|
||||
<span class="check-text check-yes">Yes</span>
|
||||
<span class="check-text check-no">No</span>
|
||||
|
@ -279,10 +299,23 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
|||
setActivePermissions = (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) ->
|
||||
return _.map(category, (x) ->
|
||||
_.extend({}, x, {
|
||||
activePermissions: _.filter(x["permissions"], "active").length
|
||||
return _.map(category, (cat) ->
|
||||
cat.permissions = cat.permissions.map (permission) ->
|
||||
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
|
||||
|
||||
target = angular.element(event.currentTarget)
|
||||
|
||||
$scope.role.permissions = getActivePermissions()
|
||||
|
||||
onSuccess = (role) ->
|
||||
categories = generateCategoriesFromRole(role)
|
||||
onSuccess = () ->
|
||||
categories = generateCategoriesFromRole($scope.role)
|
||||
categoryId = target.parents(".category-config").data("id")
|
||||
renderResume(target.parents(".category-config"), categories[categoryId])
|
||||
$rootscope.$broadcast("projects:reload")
|
||||
|
@ -381,7 +415,14 @@ RolePermissionsDirective = ($rootscope, $repo, $confirm) ->
|
|||
target.prop "checked", !target.prop("checked")
|
||||
$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", ->
|
||||
$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
|
||||
deleteCommentUser: comment.delete_comment_user.name if comment.delete_comment_user?.name
|
||||
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) ->
|
||||
|
|
|
@ -36,47 +36,52 @@ module = angular.module("taigaBacklog")
|
|||
|
||||
TaskboardSortableDirective = ($repo, $rs, $rootscope) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
oldParentScope = null
|
||||
newParentScope = null
|
||||
itemEl = null
|
||||
tdom = $el
|
||||
bindOnce $scope, "project", (project) ->
|
||||
# If the user has not enough permissions we don't enable the sortable
|
||||
if not (project.my_permissions.indexOf("modify_us") > -1)
|
||||
return
|
||||
|
||||
deleteElement = (itemEl) ->
|
||||
# Completelly remove item and its scope from dom
|
||||
itemEl.scope().$destroy()
|
||||
itemEl.off()
|
||||
itemEl.remove()
|
||||
oldParentScope = null
|
||||
newParentScope = null
|
||||
itemEl = null
|
||||
tdom = $el
|
||||
|
||||
tdom.sortable({
|
||||
handle: ".taskboard-task-inner",
|
||||
dropOnEmpty: true
|
||||
connectWith: ".taskboard-tasks-box"
|
||||
revert: 400
|
||||
})
|
||||
deleteElement = (itemEl) ->
|
||||
# Completelly remove item and its scope from dom
|
||||
itemEl.scope().$destroy()
|
||||
itemEl.off()
|
||||
itemEl.remove()
|
||||
|
||||
tdom.on "sortstop", (event, ui) ->
|
||||
parentEl = ui.item.parent()
|
||||
itemEl = ui.item
|
||||
itemTask = itemEl.scope().task
|
||||
itemIndex = itemEl.index()
|
||||
newParentScope = parentEl.scope()
|
||||
tdom.sortable({
|
||||
handle: ".taskboard-task-inner",
|
||||
dropOnEmpty: true
|
||||
connectWith: ".taskboard-tasks-box"
|
||||
revert: 400
|
||||
})
|
||||
|
||||
oldUsId = if oldParentScope.us then oldParentScope.us.id else null
|
||||
oldStatusId = oldParentScope.st.id
|
||||
newUsId = if newParentScope.us then newParentScope.us.id else null
|
||||
newStatusId = newParentScope.st.id
|
||||
tdom.on "sortstop", (event, ui) ->
|
||||
parentEl = ui.item.parent()
|
||||
itemEl = ui.item
|
||||
itemTask = itemEl.scope().task
|
||||
itemIndex = itemEl.index()
|
||||
newParentScope = parentEl.scope()
|
||||
|
||||
if newStatusId != oldStatusId or newUsId != oldUsId
|
||||
deleteElement(itemEl)
|
||||
oldUsId = if oldParentScope.us then oldParentScope.us.id else null
|
||||
oldStatusId = oldParentScope.st.id
|
||||
newUsId = if newParentScope.us then newParentScope.us.id else null
|
||||
newStatusId = newParentScope.st.id
|
||||
|
||||
$scope.$apply ->
|
||||
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
|
||||
if newStatusId != oldStatusId or newUsId != oldUsId
|
||||
deleteElement(itemEl)
|
||||
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
$scope.$apply ->
|
||||
$rootscope.$broadcast("taskboard:task:move", itemTask, newUsId, newStatusId, itemIndex)
|
||||
|
||||
tdom.on "sortstart", (event, ui) ->
|
||||
oldParentScope = ui.item.parent().scope()
|
||||
ui.item.find('a').addClass('noclick')
|
||||
ui.item.find('a').removeClass('noclick')
|
||||
|
||||
tdom.on "sortstart", (event, ui) ->
|
||||
oldParentScope = ui.item.parent().scope()
|
||||
ui.item.find('a').addClass('noclick')
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
|
|
@ -69,18 +69,18 @@ class TeamController extends mixOf(taiga.Controller, taiga.PageMixin)
|
|||
loadMembers: ->
|
||||
return @rs.memberships.list(@scope.projectId, {}, false).then (data) =>
|
||||
currentUser = @auth.getUser()
|
||||
if not currentUser.photo?
|
||||
if currentUser? and not currentUser.photo?
|
||||
currentUser.photo = "/images/unnamed.png"
|
||||
|
||||
@scope.currentUser = _.find data, (membership) =>
|
||||
return membership.user == currentUser.id
|
||||
return currentUser? and membership.user == currentUser.id
|
||||
|
||||
@scope.totals = {}
|
||||
_.forEach data, (membership) =>
|
||||
@scope.totals[membership.user] = 0
|
||||
|
||||
@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
|
||||
|
||||
for membership in @scope.memberships
|
||||
|
|
|
@ -238,6 +238,7 @@ EditableWikiContentDirective = ($window, $document, $repo, $confirm, $loading, $
|
|||
$el.on "mouseup", ".view-wiki-content", (event) ->
|
||||
target = angular.element(event.target)
|
||||
return if getSelectedText()
|
||||
return if not isEditable()
|
||||
return if target.is('a')
|
||||
return if target.is('pre')
|
||||
|
||||
|
|
|
@ -40,16 +40,14 @@ div.wrapper(tg-project-profile, ng-controller="ProjectProfileController as ctrl"
|
|||
tg-privacy-settings-inputs
|
||||
div.privacy-settings
|
||||
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")
|
||||
span Public Project
|
||||
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")
|
||||
span Private Project
|
||||
|
||||
p All projects are private during Taiga's beta period.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -8,28 +8,33 @@ div.wrapper.roles(ng-controller="RolesController as ctrl",
|
|||
section.main.admin-roles.admin-common
|
||||
.header-with-actions
|
||||
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()")
|
||||
span Delete
|
||||
|
||||
|
||||
div(tg-edit-role)
|
||||
.edit-role
|
||||
input(type="text", value="{{ role.name }}")
|
||||
a.save.icon.icon-floppy(href="", title="Save")
|
||||
div(ng-if="!role.external_user")
|
||||
div(tg-edit-role)
|
||||
.edit-role
|
||||
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
|
||||
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
|
||||
span.role-name {{ role.name }}
|
||||
|
||||
div(tg-role-permissions, ng-model="role")
|
||||
|
|
|
@ -27,7 +27,7 @@ section.table-team.basic-table
|
|||
div.popover.attribute-explanation
|
||||
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")
|
||||
span Team >
|
||||
|
|
|
@ -44,6 +44,7 @@ div(class="menu-container")
|
|||
span(class="icon icon-settings")
|
||||
span(class="item") Admin
|
||||
<% } %>
|
||||
<% if (user) { %>
|
||||
div(class="user")
|
||||
div(class="user-settings")
|
||||
ul(class="popover")
|
||||
|
@ -61,3 +62,4 @@ div(class="menu-container")
|
|||
a(href="" title="Logout" class="logout") Logout
|
||||
a(href="" title="User preferences" class="avatar" id="nav-user-settings")
|
||||
img(src="{{ user.photo }}" alt="{{ user.full_name_display }}")
|
||||
<% } %>
|
||||
|
|
|
@ -24,10 +24,6 @@
|
|||
position: relative;
|
||||
.view-wiki-content {
|
||||
&:hover {
|
||||
.wysiwyg {
|
||||
background: $whitish;
|
||||
cursor: pointer;
|
||||
}
|
||||
.edit {
|
||||
opacity: 1;
|
||||
top: -1.5rem;
|
||||
|
@ -67,4 +63,12 @@
|
|||
top: .4rem;
|
||||
}
|
||||
}
|
||||
&.editable {
|
||||
&:hover {
|
||||
.wysiwyg {
|
||||
background: $whitish;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,15 @@
|
|||
.privacy-settings {
|
||||
display: flex;
|
||||
margin-bottom: 2rem;
|
||||
div {
|
||||
> div {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
&:first-child {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
}
|
||||
// TODO: This should change when public projects available
|
||||
label {
|
||||
@extend %title;
|
||||
border: 1px solid $gray-light;
|
||||
|
@ -39,14 +40,22 @@
|
|||
span {
|
||||
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;
|
||||
border: 1px solid $fresh-taiga;
|
||||
cursor: default;
|
||||
span {
|
||||
color: $white;
|
||||
}
|
||||
|
|
|
@ -101,5 +101,11 @@
|
|||
opacity: 0;
|
||||
}
|
||||
}
|
||||
input:disabled {
|
||||
cursor: auto;
|
||||
+ div {
|
||||
background-color: #ccc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue