diff --git a/app/coffee/modules/admin/memberships.coffee b/app/coffee/modules/admin/memberships.coffee
index f5a30619..e36d8f92 100644
--- a/app/coffee/modules/admin/memberships.coffee
+++ b/app/coffee/modules/admin/memberships.coffee
@@ -366,7 +366,7 @@ module.directive("tgMembershipsRowRoleSelector", ["$log", "$tgRepo", "$tgConfirm
## Member Actions Directive
#############################################################################
-MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $translate) ->
+MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $translate, currentUserService, lightboxFactory) ->
activedTemplate = """
@@ -421,9 +421,7 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla
$rs.memberships.resendInvitation($scope.member.id).then(onSuccess, onError)
- $el.on "click", ".delete", (event) ->
- event.preventDefault()
-
+ leaveConfirm = () ->
title = $translate.instant("ADMIN.MEMBERSHIP.DELETE_MEMBER")
defaultMsg = $translate.instant("ADMIN.MEMBERSHIP.DEFAULT_DELETE_MESSAGE", {email: member.email})
message = if member.user then member.full_name else defaultMsg
@@ -448,6 +446,22 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla
$repo.remove(member).then(onSuccess, onError)
+ $el.on "click", ".delete", (event) ->
+ event.preventDefault()
+
+ if $scope.project.owner.id == member.user
+ currentUser = currentUserService.getUser()
+ isCurrentUser = currentUser.get('id') == member.user
+
+ lightboxFactory.create("tg-lightbox-leave-project-warning", {
+ class: "lightbox lightbox-leave-project-warning"
+ }, {
+ currentUser: isCurrentUser,
+ project: $scope.project
+ })
+ else
+ leaveConfirm()
+
$scope.$on "$destroy", ->
$el.off()
@@ -455,4 +469,4 @@ MembershipsRowActionsDirective = ($log, $repo, $rs, $confirm, $compile, $transla
module.directive("tgMembershipsRowActions", ["$log", "$tgRepo", "$tgResources", "$tgConfirm", "$compile",
- "$translate", MembershipsRowActionsDirective])
+ "$translate", "tgCurrentUserService", "tgLightboxFactory", MembershipsRowActionsDirective])
diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee
index 5db4e843..00a1c86e 100644
--- a/app/coffee/modules/common/lightboxes.coffee
+++ b/app/coffee/modules/common/lightboxes.coffee
@@ -697,3 +697,15 @@ AttachmentPreviewLightboxDirective = (lightboxService, $template, $compile) ->
}
module.directive("tgLbAttachmentPreview", ["lightboxService", "$tgTemplate", "$compile", AttachmentPreviewLightboxDirective])
+
+LightboxLeaveProjectWarningDirective = (lightboxService, $template, $compile) ->
+ link = ($scope, $el, attrs) ->
+ lightboxService.open($el)
+
+ return {
+ templateUrl: 'common/lightbox/lightbox-leave-project-warning.html',
+ link: link,
+ scope: true
+ }
+
+module.directive("tgLightboxLeaveProjectWarning", ["lightboxService", LightboxLeaveProjectWarningDirective])
diff --git a/app/coffee/modules/team/main.coffee b/app/coffee/modules/team/main.coffee
index b74006f4..40b08514 100644
--- a/app/coffee/modules/team/main.coffee
+++ b/app/coffee/modules/team/main.coffee
@@ -184,7 +184,7 @@ TeamMemberCurrentUserDirective = () ->
return {
templateUrl: "team/team-member-current-user.html"
scope: {
- projectId: "=projectid",
+ project: "=project",
currentUser: "=currentuser",
stats: "=",
issuesEnabled: "=issuesenabled",
@@ -225,14 +225,14 @@ module.directive("tgTeamMembers", TeamMembersDirective)
## Leave project Directive
#############################################################################
-LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls, $translate) ->
+LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls, $translate, lightboxFactory) ->
link = ($scope, $el, $attrs) ->
- $scope.leave = () ->
+ leaveConfirm = () ->
leave_project_text = $translate.instant("TEAM.ACTION_LEAVE_PROJECT")
confirm_leave_project_text = $translate.instant("TEAM.CONFIRM_LEAVE_PROJECT")
$confirm.ask(leave_project_text, confirm_leave_project_text).then (response) =>
- promise = $rs.projects.leave($attrs.projectid)
+ promise = $rs.projects.leave($scope.project.id)
promise.then =>
response.finish()
@@ -243,13 +243,27 @@ LeaveProjectDirective = ($repo, $confirm, $location, $rs, $navurls, $translate)
response.finish()
$confirm.notify('error', response.data._error_message)
+ $scope.leave = () ->
+ if $scope.project.owner.id == $scope.user.id
+ lightboxFactory.create("tg-lightbox-leave-project-warning", {
+ class: "lightbox lightbox-leave-project-warning"
+ }, {
+ currentUser: true,
+ project: $scope.project
+ })
+ else
+ leaveConfirm()
+
return {
- scope: {},
+ scope: {
+ user: "=",
+ project: "="
+ },
templateUrl: "team/leave-project.html",
link: link
}
-module.directive("tgLeaveProject", ["$tgRepo", "$tgConfirm", "$tgLocation", "$tgResources", "$tgNavUrls", "$translate",
+module.directive("tgLeaveProject", ["$tgRepo", "$tgConfirm", "$tgLocation", "$tgResources", "$tgNavUrls", "$translate", "tgLightboxFactory",
LeaveProjectDirective])
diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json
index 06645a6b..8bd9721b 100644
--- a/app/locales/taiga/locale-en.json
+++ b/app/locales/taiga/locale-en.json
@@ -919,6 +919,17 @@
"CREATE_MEMBER": {
"PLACEHOLDER_INVITATION_TEXT": "(Optional) Add a personalized text to the invitation. Tell something lovely to your new members ;-)",
"PLACEHOLDER_TYPE_EMAIL": "Type an Email"
+ },
+ "LEAVE_PROJECT_WARNING": {
+ "TITLE": "You can not leave the project without owner",
+ "CURRENT_USER_OWNER": {
+ "DESC": "You are the project owner before leaving it you must pass the property to someone else.",
+ "BUTTON": "Change the project owner"
+ },
+ "OTHER_USER_OWNER": {
+ "DESC": "You can't delete the project owner, you must request a new owner before deleting the user.",
+ "BUTTON": "Request change project owner"
+ }
}
},
"US": {
diff --git a/app/partials/common/lightbox/lightbox-leave-project-warning.jade b/app/partials/common/lightbox/lightbox-leave-project-warning.jade
new file mode 100644
index 00000000..06ce8158
--- /dev/null
+++ b/app/partials/common/lightbox/lightbox-leave-project-warning.jade
@@ -0,0 +1,19 @@
+svg.close.icon.icon-close(href="", title="{{'COMMON.CLOSE' | translate}}")
+ use(xlink:href="#icon-close")
+div.content
+ svg.icon.icon-exclamation
+ use(xlink:href="#icon-exclamation")
+
+ h2.title {{'LIGHTBOX.LEAVE_PROJECT_WARNING.TITLE' | translate}}
+
+ div(ng-if="currentUser")
+ p {{'LIGHTBOX.LEAVE_PROJECT_WARNING.CURRENT_USER_OWNER.DESC' | translate}}
+
+ a.button-green(tg-nav="project-admin-home:project=project.slug", href="")
+ span(translate="LIGHTBOX.LEAVE_PROJECT_WARNING.CURRENT_USER_OWNER.BUTTON")
+
+ div(ng-if="!currentUser")
+ p {{'LIGHTBOX.LEAVE_PROJECT_WARNING.OTHER_USER_OWNER.DESC' | translate}}
+
+ a.button-green(tg-nav="project-admin-home:project=project.slug", href="")
+ span(translate="LIGHTBOX.LEAVE_PROJECT_WARNING.OTHER_USER_OWNER.BUTTON")
\ No newline at end of file
diff --git a/app/partials/includes/modules/team/team-table.jade b/app/partials/includes/modules/team/team-table.jade
index 0f9ac475..b8500578 100644
--- a/app/partials/includes/modules/team/team-table.jade
+++ b/app/partials/includes/modules/team/team-table.jade
@@ -31,7 +31,7 @@ section.table-team.basic-table
tg-team-current-user
stats="stats"
currentuser="currentUser"
- projectid="projectId"
+ project="project"
issuesEnabled="issuesEnabled"
tasksenabled="tasksEnabled"
wikienabled="wikiEnabled"
diff --git a/app/partials/team/team-member-current-user.jade b/app/partials/team/team-member-current-user.jade
index 623c936c..1c425a44 100644
--- a/app/partials/team/team-member-current-user.jade
+++ b/app/partials/team/team-member-current-user.jade
@@ -12,7 +12,7 @@
.position(tg-bo-bind="currentUser.role_name")
- div(tg-leave-project="", projectid="{{projectId}}")
+ div(tg-leave-project="", project="project", user="currentUser")
.member-stats(
tg-team-member-stats
diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss
index 994ce2a4..a3b0d9ac 100644
--- a/app/styles/modules/common/lightbox.scss
+++ b/app/styles/modules/common/lightbox.scss
@@ -552,3 +552,16 @@
height: 5rem;
}
}
+
+.lightbox-leave-project-warning {
+ text-align: center;
+ .icon {
+ fill: $gray-light;
+ height: 3rem;
+ margin-bottom: 1rem;
+ width: 3rem;
+ }
+ .content {
+ width: 500px;
+ }
+}
diff --git a/e2e/helpers/admin-memberships.js b/e2e/helpers/admin-memberships.js
index 5fbe7c34..cb420c35 100644
--- a/e2e/helpers/admin-memberships.js
+++ b/e2e/helpers/admin-memberships.js
@@ -35,10 +35,30 @@ helper.getNewMemberLightbox = function() {
return obj;
};
+helper.leavingProjectWarningLb = function() {
+ return $('div[tg-lightbox-leave-project-warning]');
+};
+
+helper.isLeaveProjectWarningOpen = function() {
+ return helper.leavingProjectWarningLb().isPresent();
+};
+
helper.getMembers = function() {
return $$('.admin-membership-table .row');
};
+helper.getOwner = function() {
+ return helper.getMembers().filter(async (member) => {
+ return !!await member.$$('.icon-badge').count();
+ }).first();
+};
+
+helper.excludeOwner = function(members) {
+ return members.filter(async (member) => {
+ return !await member.$$('.icon-badge').count();
+ });
+};
+
helper.isActive = function(elm) {
return utils.common.hasClass(elm, 'active');
};
diff --git a/e2e/helpers/create-project-helper.js b/e2e/helpers/create-project-helper.js
index c87f5fac..8323b73d 100644
--- a/e2e/helpers/create-project-helper.js
+++ b/e2e/helpers/create-project-helper.js
@@ -23,13 +23,13 @@ helper.createProjectLightbox = function() {
await browser.sleep(1000);
},
submit: function() {
- return $('.wizard-step.active .button-green').click();
+ return $('div[tg-lb-create-project] .button-green').click();
},
name: function() {
- return $$('.create-step2 input').get(0);
+ return $$('div[tg-lb-create-project] input[type="text"]').get(0);
},
description: function() {
- return $$('.create-step2 textarea');
+ return $('div[tg-lb-create-project] textarea');
},
errors: function() {
return $$('.checksley-error-list li');
diff --git a/e2e/helpers/team-helper.js b/e2e/helpers/team-helper.js
index 3517cf7e..d5aa011c 100644
--- a/e2e/helpers/team-helper.js
+++ b/e2e/helpers/team-helper.js
@@ -53,3 +53,11 @@ helper.filters = function() {
return obj;
};
+
+helper.leavingProjectWarningLb = function() {
+ return $('div[tg-lightbox-leave-project-warning]');
+};
+
+helper.isLeaveProjectWarningOpen = function() {
+ return helper.leavingProjectWarningLb().isPresent();
+};
diff --git a/e2e/suites/admin/members.e2e.js b/e2e/suites/admin/members.e2e.js
index 9a024a12..02de904b 100644
--- a/e2e/suites/admin/members.e2e.js
+++ b/e2e/suites/admin/members.e2e.js
@@ -78,7 +78,7 @@ describe('admin - members', function() {
it('delete member', async function() {
let initMembersCount = await adminMembershipsHelper.getMembers().count();
- let member = adminMembershipsHelper.getMembers().last();
+ let member = adminMembershipsHelper.excludeOwner(adminMembershipsHelper.getMembers()).last();
adminMembershipsHelper.delete(member);
@@ -89,6 +89,25 @@ describe('admin - members', function() {
let membersCount = await adminMembershipsHelper.getMembers().count();
expect(membersCount).to.be.equal(initMembersCount - 1);
+
+ await utils.notifications.success.close();
+ });
+
+ it('trying to delete owner', async function() {
+ let member = await adminMembershipsHelper.getOwner();
+
+ adminMembershipsHelper.delete(member);
+
+ utils.common.takeScreenshot('memberships', 'delete-owner-lb');
+
+ let isLeaveProjectWarningOpen = await adminMembershipsHelper.isLeaveProjectWarningOpen();
+
+ expect(isLeaveProjectWarningOpen).to.be.equal(true);
+
+ let lb = adminMembershipsHelper.leavingProjectWarningLb();
+
+ await utils.lightbox.exit(lb);
+ await utils.lightbox.close(lb);
});
it('change role', async function() {
diff --git a/e2e/suites/team.e2e.js b/e2e/suites/team.e2e.js
index 64e12039..da7f33b3 100644
--- a/e2e/suites/team.e2e.js
+++ b/e2e/suites/team.e2e.js
@@ -20,6 +20,31 @@ describe('leaving project', function(){
});
});
+describe('leaving project owner', function(){
+ before(async function(){
+ await utils.common.createProject();
+ await utils.nav
+ .init()
+ .team()
+ .go();
+ });
+
+ it('leave project', async function(){
+ teamHelper.team().leave();
+
+ let isLeaveProjectWarningOpen = await teamHelper.isLeaveProjectWarningOpen();
+
+ await utils.common.takeScreenshot("team", "leave-project-warning");
+
+ expect(isLeaveProjectWarningOpen).to.be.equal(true);
+
+ let lb = teamHelper.leavingProjectWarningLb();
+
+ await utils.lightbox.exit(lb);
+ await utils.lightbox.close(lb);
+ });
+});
+
describe('team', function() {
before(async function(){
browser.get(browser.params.glob.host + 'project/project-5/team');
diff --git a/e2e/utils/lightbox.js b/e2e/utils/lightbox.js
index 0108b023..b4504568 100644
--- a/e2e/utils/lightbox.js
+++ b/e2e/utils/lightbox.js
@@ -3,6 +3,16 @@ var common = require('./common');
var lightbox = module.exports;
var transition = 300;
+lightbox.exit = function(el) {
+ var deferred = protractor.promise.defer();
+
+ if (typeof el === 'string' || el instanceof String) {
+ el = $(el);
+ }
+
+ el.$('.icon-close').click();
+};
+
lightbox.open = async function(el) {
var deferred = protractor.promise.defer();
diff --git a/e2e/utils/nav.js b/e2e/utils/nav.js
index 107317d3..ba663509 100644
--- a/e2e/utils/nav.js
+++ b/e2e/utils/nav.js
@@ -58,6 +58,11 @@ var actions = {
await common.link(task);
+ return common.waitLoader();
+ },
+ team: async function() {
+ await common.link($('#nav-team a'));
+
return common.waitLoader();
}
};
@@ -95,6 +100,10 @@ var nav = {
this.actions.push(actions.task.bind(null, index));
return this;
},
+ team: function(index) {
+ this.actions.push(actions.team.bind(null, index));
+ return this;
+ },
go: function() {
let promise = this.actions[0]();