Merge pull request #902 from taigaio/us/54/max-users-per-project

Warning Max Users per Project
stable
Juanfran 2016-03-02 14:52:08 +01:00
commit 24c20c5eae
14 changed files with 293 additions and 71 deletions

View File

@ -8,6 +8,7 @@
- Moved from iconfont to SVG sprite icon system and redesign.
- Redesign 'Admin > Project > Modules' panel.
- Add badge to project owners
- Limit of user per project.
### Misc
- Lots of small and not so small bugfixes.

View File

@ -47,11 +47,13 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
"$tgNavUrls",
"$tgAnalytics",
"tgAppMetaService",
"$translate"
"$translate",
"tgCurrentUserService",
"$tgAuth"
]
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @navUrls, @analytics,
@appMetaService, @translate) ->
@appMetaService, @translate, @currentUserService, @tgAuth) ->
bindMethods(@)
@scope.project = {}
@ -63,6 +65,7 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
title = @translate.instant("ADMIN.MEMBERSHIPS.PAGE_TITLE", {projectName: @scope.project.name})
description = @scope.project.description
@appMetaService.setAll(title, description)
@._checkUsersLimit()
promise.then null, @.onInitialDataError.bind(@)
@ -93,8 +96,11 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
loadInitialData: ->
promise = @.loadProject()
promise.then =>
@.loadMembers()
@q.all([
@.loadMembers(),
@tgAuth.refresh()
])
return promise
@ -106,6 +112,22 @@ class MembershipsController extends mixOf(taiga.Controller, taiga.PageMixin, tai
addNewMembers: ->
@rootscope.$broadcast("membersform:new")
_checkUsersLimit: ->
@scope.canAddUsers = true
userData = @currentUserService.getUser().toJS()
if @currentUserService.canAddMoreMembersInPrivateProjects(@scope.projectId).valid == false
@.maxMembers = userData.max_members_private_projects
@scope.canAddUsers = false
else if @currentUserService.canAddMoreMembersInPublicProjects(@scope.projectId).valid == false
@.maxMembers = userData.max_members_private_projects
@scope.canAddUsers = false
limitUsersWarning: ->
title = @translate.instant("ADMIN.MEMBERSHIPS.LIMIT_USERS_WARNING")
message = @translate.instant("ADMIN.MEMBERSHIPS.LIMIT_USERS_WARNING_MESSAGE", {members: @.maxMembers})
icon = "/" + window._version + "/svg/icons/team-question.svg"
@confirm.success(title, message,icon)
module.controller("MembershipsController", MembershipsController)

View File

@ -161,11 +161,16 @@ class ConfirmService extends taiga.Service
return defered.promise
success: (title, message) ->
success: (title, message, icon) ->
defered = @q.defer()
el = angular.element(".lightbox-generic-success")
el.find("img").remove()
detailImage = $('<img>').addClass('lb-icon').attr('src', icon)
if detailImage
el.find('section').prepend(detailImage)
# Render content
el.find(".title").html(title) if title
el.find(".message").html(message) if message

View File

@ -408,7 +408,9 @@
"TITLE": "Manage members",
"PAGE_TITLE": "Memberships - {{projectName}}",
"ADD_BUTTON": "+ New member",
"ADD_BUTTON_TITLE": "Add new member"
"ADD_BUTTON_TITLE": "Add new member",
"LIMIT_USERS_WARNING": "Why can't I add more members",
"LIMIT_USERS_WARNING_MESSAGE": "Currently you can only have <strong>{{members}}</strong> members per project. If you want to add more members get in touch with the administrators"
},
"PROJECT_EXPORT": {
"TITLE": "Export",

View File

@ -4,8 +4,8 @@ a(href="", title="Projects", tg-nav="projects")
div.navbar-dropdown.dropdown-project-list
ul
li(tg-repeat="project in vm.projects track by project.get('id')")
a(href="#", tg-nav="project:project=project.get('slug')") {{::project.get("name")}}
li(tg-repeat="project in vm.projects track by project.get('id')")
a(href="#", tg-nav="project:project=project.get('slug')") {{::project.get("name")}}
a.see-more-projects-btn.button-gray(
href="#",

View File

@ -118,6 +118,25 @@ class CurrentUserService
return @.projects
canAddMoreMembersInPrivateProjects: (projectId) ->
project = @.projects.get('all').find (project) -> project.get('id') == projectId
user = @.getUser()
if user.get('max_members_private_projects') != null && project.get('members').size >= user.get('max_members_private_projects')
return {valid: false, reason: 'max_members_private_projects', type: 'private_project'}
return {valid: true}
canAddMoreMembersInPublicProjects: (projectId) ->
project = @.projects.get('all').find (project) -> project.get('id') == projectId
user = @.getUser()
if user.get('max_members_public_projects') != null && project.get('members').size >= user.get('max_members_public_projects')
return {valid: false, reason: 'max_members_public_projects', type: 'public_project'}
return {valid: true}
canBePrivateProject: (projectId) ->
project = @.projects.get('all').find (project) -> project.get('id') == projectId

View File

@ -204,6 +204,112 @@ describe "tgCurrentUserService", ->
done()
it "the user can't add more members in private projects", () ->
user = Immutable.fromJS({
id: 1,
name: "fake1",
max_members_private_projects: 2
})
projects = Immutable.fromJS({
all: [
{id: 1, name: "fake1"},
{id: 2, name: "fake2", members: [1, 2, 3, 4, 5], is_private: true},
{id: 3, name: "fake3"},
{id: 4, name: "fake4"}
]
})
currentUserService._user = user
currentUserService._projects = projects
result = currentUserService.canAddMoreMembersInPrivateProjects(2)
expect(result).to.be.eql({
valid: false,
reason: 'max_members_private_projects',
type: 'private_project'
})
it "the user can add more members in private projects", () ->
user = Immutable.fromJS({
id: 1,
name: "fake1",
max_members_private_projects: 7
})
currentUserService._user = user
projects = Immutable.fromJS({
all: [
{id: 1, name: "fake1"},
{id: 2, name: "fake2", members: [1, 2, 3, 4, 5], is_private: true},
{id: 3, name: "fake3"},
{id: 4, name: "fake4"}
]
})
currentUserService._projects = projects
result = currentUserService.canAddMoreMembersInPrivateProjects(2)
expect(result).to.be.eql({
valid: true
})
it "the user can't add more members in public projects", () ->
user = Immutable.fromJS({
id: 1,
name: "fake1",
max_members_public_projects: 2
})
projects = Immutable.fromJS({
all: [
{id: 1, name: "fake1"},
{id: 2, name: "fake2", members: [1, 2, 3, 4, 5], is_private: false},
{id: 3, name: "fake3"},
{id: 4, name: "fake4"}
]
})
currentUserService._user = user
currentUserService._projects = projects
result = currentUserService.canAddMoreMembersInPublicProjects(2)
expect(result).to.be.eql({
valid: false,
reason: 'max_members_public_projects',
type: 'public_project'
})
it "the user can add more members in public projects", () ->
user = Immutable.fromJS({
id: 1,
name: "fake1",
max_members_public_projects: 7
})
projects = Immutable.fromJS({
all: [
{id: 1, name: "fake1"},
{id: 2, name: "fake2", members: [1, 2, 3, 4, 5], is_private: false},
{id: 3, name: "fake3"},
{id: 4, name: "fake4"}
]
})
currentUserService._user = user
currentUserService._projects = projects
result = currentUserService.canAddMoreMembersInPublicProjects(2)
expect(result).to.be.eql({
valid: true
})
it "the user can't create private projects if they reach the maximum number of private projects", () ->
user = Immutable.fromJS({
id: 1,
@ -214,7 +320,7 @@ describe "tgCurrentUserService", ->
currentUserService._user = user
result = currentUserService.canCreatePrivateProjects(0)
result = currentUserService.canCreatePrivateProjects()
expect(result).to.be.eql({
valid: false,

View File

@ -13,9 +13,19 @@ div.wrapper.memberships(ng-controller="MembershipsController as ctrl",
include ../includes/components/mainTitle
.action-buttons
a.button-green(href="", title="{{ ADMIN.MEMBERSHIPS.ADD_BUTTON_TITLE | translate }}",
ng-click="ctrl.addNewMembers()")
span.text(translate="ADMIN.MEMBERSHIPS.ADD_BUTTON")
a.limit-users-warning(
ng-if="!canAddUsers"
translate="ADMIN.MEMBERSHIPS.LIMIT_USERS_WARNING"
translate-values="{members: ctrl.maxMembers}"
href=""
ng-click="ctrl.limitUsersWarning()"
)
button.button-green(
title="{{ ADMIN.MEMBERSHIPS.ADD_BUTTON_TITLE | translate }}",
ng-click="ctrl.addNewMembers()"
translate="ADMIN.MEMBERSHIPS.ADD_BUTTON"
ng-disabled="!canAddUsers"
)
include ../includes/modules/admin/admin-membership-table

View File

@ -44,7 +44,6 @@
}
}
}
.trans-button {
@extend %medium;
@extend %title;
@ -61,12 +60,9 @@
color: $blackish;
}
}
.submit-button {
width: 100%;
}
.button-green,
a.button-green {
@extend %button;
@ -77,7 +73,6 @@ a.button-green {
color: $white;
}
}
.button-gray,
a.button-gray {
@extend %button;
@ -88,7 +83,6 @@ a.button-gray {
color: $white;
}
}
.button-blackish {
@extend %button;
background: $blackish;
@ -98,7 +92,6 @@ a.button-gray {
color: $white;
}
}
.button-red {
@extend %button;
background: $red-light;
@ -110,7 +103,6 @@ a.button-gray {
color: $white;
}
}
.button-block {
background: $white;
color: $red;
@ -119,7 +111,6 @@ a.button-gray {
color: $white;
}
}
.button-bulk {
@extend %button;
background: $primary;
@ -134,7 +125,6 @@ a.button-gray {
background: $primary-light;
}
}
.button-auth {
@extend %button;
background: $grayer;

View File

@ -50,6 +50,11 @@
opacity: 0;
transition: opacity .3s ease;
}
.lb-icon {
@include svg-size(6rem);
display: block;
margin: 1rem auto;
}
.title {
text-align: center;
}

View File

@ -5,6 +5,11 @@
float: right;
}
}
.limit-users-warning {
@extend %small;
color: $primary;
margin-right: 1rem;
}
.check {
input { // IE needs smaller size
height: 40px;

View File

@ -1,8 +1,8 @@
.lightbox {
@extend %lightbox;
h2 {
@extend %larger;
@extend %text;
@extend %xlarge;
@extend %light;
}
}

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400">
<path
fill="#b8b8b8" d="M165 91.5c-33 0-60 28.7-60 64 0 35.2 27 64 60 64s60-28.8 60-64c0-35.3-27-64-60-64zm0 16.2c24.8 0 44.8 21.3 44.8 47.8 0 26.5-20 47.8-44.8 47.8-25 0-45-21.3-45-47.8 0-26.5 20-47.8 45-47.8zm-77.8 13.5c-29.2 0-53 25.5-53 56.7 0 31 23.8 56.5 53 56.5 16 0 27.5-4.5 37.3-16.6-21.8-9.7-34-36-34-62.5 0-12 3-23.3 8.5-33-3.8-.8-7.7-1.3-11.8-1.3zM165 229.7c-53.3 0-95.8 50.8-96 112.5v8h192v-8c-.2-61.7-42.8-112.5-96-112.5zm-78.7 13.6c-3.4 0-5.8.3-5.8.3-47.2 0-78 45-78.3 99.5v7.3h52.2v-8c0-41.3 15.2-78.6 43.7-98.2-3.8-.6-8.2-.8-11.7-.7zM165 246c42 0 76.8 38.2 80.5 88h-161c3.6-49.8 38.6-88 80.5-88zM339.3 49.7C307 49.7 281 76 281 108.2c0 32.2 26 58.4 58.3 58.4s58.5-26.2 58.5-58.4c0-32.3-26.2-58.5-58.5-58.5zm0 7.3c28.3 0 51.2 23 51.2 51.2 0 28.3-23 51-51.2 51-28.3 0-51-22.7-51-51S311 57 339.2 57zm-.2 11.8c-7 0-14 1.7-20.5 5l3 7c3.5-1.6 6.5-2.7 9-3.3 2.7-.7 5.4-1 8-1 4.4 0 7.6 1 10 3 2.2 1.8 3.3 4.5 3.3 8 0 3-.6 5.4-1.8 7.4-1.2 2-4.2 5-9 9-3.3 2.8-5.7 5.6-7 8.2-1.2 2.6-1.8 6-1.8 10v2.8h6.7v-1.7c0-3.3.5-6 1.6-8 1-2 3.5-4.4 7-7.3 4.4-4 7.2-6.6 8.5-8.3 1.4-1.8 2.4-3.6 3-5.5.8-2 1-4.2 1-6.7 0-6-1.7-10.6-5.4-13.8-3.7-3.3-8.8-5-15.5-5zm-6.8 62.5V148h7.3v-16.7h-7.3z" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 67 KiB