Merge "Allow teams to be added to the permission list for private stories"

This commit is contained in:
Jenkins 2017-01-03 17:46:42 +00:00 committed by Gerrit Code Review
commit 28b4809425
6 changed files with 203 additions and 92 deletions

View File

@ -123,6 +123,7 @@
<td colspan="2">
<input id="user"
type="text"
autocomplete="off"
placeholder="Click to add a user"
ng-model="asyncUser"
typeahead-wait-ms="200"

View File

@ -106,6 +106,7 @@
<td colspan="2">
<input id="user"
type="text"
autocomplete="off"
placeholder="Click to add a user"
ng-model="asyncUser"
typeahead-wait-ms="200"

View File

@ -24,7 +24,7 @@ angular.module('sb.story').controller('StoryDetailController',
Story, Project, Branch, creator, tasks, Task, DSCacheFactory,
User, $q, storyboardApiBase, SessionModalService, moment,
$document, $anchorScroll, $timeout, $location, currentUser,
enableEditableComments, Tags, worklists) {
enableEditableComments, Tags, worklists, Team) {
'use strict';
var pageSize = Preference.get('story_detail_page_size');
@ -532,51 +532,82 @@ angular.module('sb.story').controller('StoryDetailController',
};
/**
* User typeahead search method.
* User/team typeahead search method.
*/
$scope.searchUsers = function (value, array) {
var userIds = array.map(function(user){return user.id;});
$scope.searchActors = function (value, users, teams) {
var userIds = users.map(function(user){return user.id;});
var teamIds = teams.map(function(team){return team.id;});
var deferred = $q.defer();
var usersDeferred = $q.defer();
var teamsDeferred = $q.defer();
User.browse({full_name: value, limit: 10},
function(searchResults) {
var results = [];
angular.forEach(searchResults, function(result) {
if (userIds.indexOf(result.id) === -1) {
result.name = result.full_name;
result.type = 'user';
results.push(result);
}
});
deferred.resolve(results);
usersDeferred.resolve(results);
}
);
Team.browse({name: value, limit: 10},
function(searchResults) {
var results = [];
angular.forEach(searchResults, function(result) {
if (teamIds.indexOf(result.id) === -1) {
result.type = 'team';
results.push(result);
}
});
teamsDeferred.resolve(results);
}
);
var searches = [teamsDeferred.promise, usersDeferred.promise];
$q.all(searches).then(function(searchResults) {
var results = [];
angular.forEach(searchResults, function(promise) {
angular.forEach(promise, function(result) {
results.push(result);
});
});
deferred.resolve(results);
});
return deferred.promise;
};
/**
* Formats the user name.
* Add a new user or team to one of the permission levels.
*/
$scope.formatUserName = function (model) {
if (!!model) {
return model.name;
$scope.addActor = function (model) {
if (model.type === 'user') {
$scope.story.users.push(model);
} else if (model.type === 'team') {
$scope.story.teams.push(model);
}
return '';
};
/**
* Add a new user to one of the permission levels.
*/
$scope.addUser = function (model) {
$scope.story.users.push(model);
};
/**
* Remove a user from one of the permission levels.
* Remove a user from the permissions.
*/
$scope.removeUser = function (model) {
var idx = $scope.story.users.indexOf(model);
$scope.story.users.splice(idx, 1);
};
/**
* Remove a team from the permissions.
*/
$scope.removeTeam = function(model) {
var idx = $scope.story.teams.indexOf(model);
$scope.story.teams.splice(idx, 1);
};
// ###################################################################
// Task Management
// ###################################################################

View File

@ -19,7 +19,7 @@
*/
angular.module('sb.story').controller('StoryModalController',
function ($scope, $modalInstance, params, Project, Story, Task, User,
$q, CurrentUser) {
Team, $q, CurrentUser) {
'use strict';
var currentUser = CurrentUser.resolve();
@ -29,7 +29,8 @@ angular.module('sb.story').controller('StoryModalController',
currentUser.then(function(user) {
$scope.story = new Story({
title: '',
users: [user]
users: [user],
teams: []
});
});
@ -164,49 +165,80 @@ angular.module('sb.story').controller('StoryModalController',
};
/**
* User typeahead search method.
* User/team typeahead search method.
*/
$scope.searchUsers = function (value, array) {
$scope.searchActors = function (value, users, teams) {
var userIds = users.map(function(user){return user.id;});
var teamIds = teams.map(function(team){return team.id;});
var deferred = $q.defer();
var usersDeferred = $q.defer();
var teamsDeferred = $q.defer();
User.browse({full_name: value, limit: 10},
function(searchResults) {
var results = [];
angular.forEach(searchResults, function(result) {
if (array.indexOf(result.id) === -1) {
if (userIds.indexOf(result.id) === -1) {
result.name = result.full_name;
result.type = 'user';
results.push(result);
}
});
deferred.resolve(results);
usersDeferred.resolve(results);
}
);
Team.browse({name: value, limit: 10},
function(searchResults) {
var results = [];
angular.forEach(searchResults, function(result) {
if (teamIds.indexOf(result.id) === -1) {
result.type = 'team';
results.push(result);
}
});
teamsDeferred.resolve(results);
}
);
var searches = [teamsDeferred.promise, usersDeferred.promise];
$q.all(searches).then(function(searchResults) {
var results = [];
angular.forEach(searchResults, function(promise) {
angular.forEach(promise, function(result) {
results.push(result);
});
});
deferred.resolve(results);
});
return deferred.promise;
};
/**
* Formats the user name.
* Add a new user or team to one of the permission levels.
*/
$scope.formatUserName = function (model) {
if (!!model) {
return model.name;
$scope.addActor = function (model) {
if (model.type === 'user') {
$scope.story.users.push(model);
} else if (model.type === 'team') {
$scope.story.teams.push(model);
}
return '';
};
/**
* Add a new user to one of the permission levels.
*/
$scope.addUser = function (model) {
$scope.story.users.push(model);
};
/**
* Remove a user from one of the permission levels.
* Remove a user from the permissions.
*/
$scope.removeUser = function (model) {
var idx = $scope.story.users.indexOf(model);
$scope.story.users.splice(idx, 1);
};
/**
* Remove a team from the permissions.
*/
$scope.removeTeam = function(model) {
var idx = $scope.story.teams.indexOf(model);
$scope.story.teams.splice(idx, 1);
};
})
;

View File

@ -187,31 +187,43 @@
</div>
</div>
<div class="row">
<div class="col-sm-6 col-sm-offset-3"
<div class="col-md-6 col-md-offset-3"
ng-show="story.private">
<table class="table table-striped">
<thead>
<tr>
<th>Users that can see this story</th>
<th>Teams and Users that can see this story</th>
<th class="text-right">
<small>
<a href
ng-click="showAddUser = !showAddUser">
<i class="fa fa-plus" ng-if="!showAddUser"></i>
<i class="fa fa-minus" ng-if="showAddUser"></i>
Add User
Add Team or User
</a>
</small>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="team in story.teams">
<td colspan="2">
<i class="fa fa-sb-team"></i>
{{ team.name }}
<a class="close"
ng-click="removeTeam(team)"
ng-show="story.teams.length > 1 || story.users.length > 0">
&times;
</a>
</td>
</tr>
<tr ng-repeat="user in story.users">
<td colspan="2">
<i class="fa fa-sb-user"></i>
{{user.full_name}}
<a class="close"
ng-click="removeUser(user)"
ng-show="story.users.length > 1">
ng-show="story.users.length > 1 || story.teams.length > 0">
&times;
</a>
</td>
@ -220,15 +232,16 @@
<td colspan="2">
<input id="user"
type="text"
placeholder="Click to add a user"
autocomplete="off"
placeholder="Click to add a team or user"
ng-model="asyncUser"
typeahead-wait-ms="200"
typeahead-editable="false"
typeahead="user as user.full_name for user in
searchUsers($viewValue, story.users)"
typeahead="actor as actor.name for actor in
searchActors($viewValue, story.users, story.teams)"
typeahead-loading="loadingUsers"
typeahead-input-formatter="formatUserName($model)"
typeahead-on-select="addUser($model)"
typeahead-on-select="addActor($model)"
typeahead-template-url="/inline/actor-typeahead-item.html"
class="form-control input-sm"
/>
</td>
@ -275,6 +288,14 @@
</script>
<script type="text/ng-template" id="/inline/actor-typeahead-item.html">
<a tabindex="-1">
<i ng-class="'fa fa-sb-' + match.model.type"></i>&nbsp;
<span ng-bind-html="match.model.name | typeaheadHighlight:query"></span>
</a>
</script>
<!-- Template for the list of relevant worklists -->
<script type="text/ng-template" id="/inline/worklists.html">
<table class="table table-striped table-condensed">

View File

@ -64,53 +64,69 @@
/>
</div>
</div>
<div class="col-sm-6 col-sm-offset-3"
ng-show="story.private">
<table class="table table-striped">
<thead>
<tr>
<th>Users that can see this story</th>
<th class="text-right">
<small>
<a href
ng-click="showAddUser = !showAddUser">
<i class="fa fa-plus" ng-if="!showAddUser"></i>
<i class="fa fa-minus" ng-if="showAddUser"></i>
Add User
<div class="row">
<div class="col-md-6 col-md-offset-3"
ng-show="story.private">
<table class="table table-striped">
<thead>
<tr>
<th>Teams and Users that can see this story</th>
<th class="text-right">
<small>
<a href
ng-click="showAddUser = !showAddUser">
<i class="fa fa-plus" ng-if="!showAddUser"></i>
<i class="fa fa-minus" ng-if="showAddUser"></i>
Add Team or User
</a>
</small>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="team in story.teams">
<td colspan="2">
<i class="fa fa-sb-team"></i>
{{ team.name }}
<a class="close"
ng-click="removeTeam(team)"
ng-show="story.teams.length > 1 || story.users.length > 0">
&times;
</a>
</small>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in story.users">
<td colspan="2">
{{user.full_name}}
<a class="close"
ng-click="removeUser(user)">
&times;
</a>
</td>
</tr>
<tr ng-show="showAddUser">
<td colspan="2">
<input id="user"
type="text"
placeholder="Click to add a user"
ng-model="asyncUser"
typeahead-wait-ms="200"
typeahead-editable="false"
typeahead="user as user.full_name for user in
searchUsers($viewValue, story.users)"
typeahead-loading="loadingUsers"
typeahead-input-formatter="formatUserName($model)"
typeahead-on-select="addUser($model)"
class="form-control input-sm"
/>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr ng-repeat="user in story.users">
<td colspan="2">
<i class="fa fa-sb-user"></i>
{{user.full_name}}
<a class="close"
ng-click="removeUser(user)"
ng-show="story.users.length > 1 || story.teams.length > 0">
&times;
</a>
</td>
</tr>
<tr ng-show="showAddUser">
<td colspan="2">
<input id="user"
type="text"
autocomplete="off"
placeholder="Click to add a team or user"
ng-model="asyncUser"
typeahead-wait-ms="200"
typeahead-editable="false"
typeahead="actor as actor.name for actor in
searchActors($viewValue, story.users, story.teams)"
typeahead-loading="loadingUsers"
typeahead-on-select="addActor($model)"
typeahead-template-url="/inline/actor-typeahead-item.html"
class="form-control input-sm"
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
@ -163,6 +179,15 @@
</div>
</div>
<script type="text/ng-template" id="/inline/actor-typeahead-item.html">
<a tabindex="-1">
<i ng-class="'fa fa-sb-' + match.model.type"></i>&nbsp;
<span ng-bind-html="match.model.name | typeaheadHighlight:query"></span>
</a>
</script>
<!-- Template for story metadata -->
<script type="text/ng-template" id="/inline/task_row.html">
<td>