Merge pull request #371 from taigaio/public-projects

Public projects
stable
David Barragán Merino 2015-03-10 14:44:45 +01:00
commit 515df55db2
12 changed files with 152 additions and 81 deletions

View File

@ -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()

View File

@ -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) ->

View File

@ -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()

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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")

View File

@ -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 >

View File

@ -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 }}")
<% } %>

View File

@ -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;
}
}
}
}

View File

@ -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;
}

View File

@ -101,5 +101,11 @@
opacity: 0;
}
}
input:disabled {
cursor: auto;
+ div {
background-color: #ccc;
}
}
}
}