Merge "Add UI for making stories private to a list of people"

This commit is contained in:
Jenkins 2016-05-10 14:22:16 +00:00 committed by Gerrit Code Review
commit 10c54bab47
4 changed files with 317 additions and 49 deletions

View File

@ -20,7 +20,7 @@
angular.module('sb.story').controller('StoryDetailController',
function ($log, $rootScope, $scope, $state, $stateParams, $modal, Session,
Preference, TimelineEvent, Comment, TimelineEventTypes, story,
Story, creator, tasks, Task, DSCacheFactory, User,
Story, creator, tasks, Task, DSCacheFactory, User, $q,
storyboardApiBase, SubscriptionList, CurrentUser,
SessionModalService, moment, $document) {
'use strict';
@ -325,6 +325,51 @@ angular.module('sb.story').controller('StoryDetailController',
SessionModalService.showLoginRequiredModal();
};
/**
* User typeahead search method.
*/
$scope.searchUsers = function (value, array) {
var deferred = $q.defer();
User.browse({full_name: value, limit: 10},
function(searchResults) {
var results = [];
angular.forEach(searchResults, function(result) {
if (array.indexOf(result.id) === -1) {
results.push(result);
}
});
deferred.resolve(results);
}
);
return deferred.promise;
};
/**
* Formats the user name.
*/
$scope.formatUserName = function (model) {
if (!!model) {
return model.name;
}
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.
*/
$scope.removeUser = function (model) {
var idx = $scope.story.users.indexOf(model);
$scope.story.users.splice(idx, 1);
};
// ###################################################################
// Task Management
// ###################################################################

View File

@ -18,11 +18,20 @@
* Controller for the "new story" modal popup.
*/
angular.module('sb.story').controller('StoryModalController',
function ($scope, $modalInstance, params, Project, Story, Task) {
function ($scope, $modalInstance, params, Project, Story, Task, User,
$q, CurrentUser) {
'use strict';
var currentUser = CurrentUser.resolve();
$scope.projects = Project.browse({});
$scope.story = new Story({title: ''});
currentUser.then(function(user) {
$scope.story = new Story({
title: '',
users: [user]
});
});
$scope.tasks = [new Task({
title: '',
@ -153,5 +162,51 @@ angular.module('sb.story').controller('StoryModalController',
$scope.selectNewProject = function (model, task) {
task.project_id = model.id;
};
/**
* User typeahead search method.
*/
$scope.searchUsers = function (value, array) {
var deferred = $q.defer();
User.browse({full_name: value, limit: 10},
function(searchResults) {
var results = [];
angular.forEach(searchResults, function(result) {
if (array.indexOf(result.id) === -1) {
results.push(result);
}
});
deferred.resolve(results);
}
);
return deferred.promise;
};
/**
* Formats the user name.
*/
$scope.formatUserName = function (model) {
if (!!model) {
return model.name;
}
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.
*/
$scope.removeUser = function (model) {
var idx = $scope.story.users.indexOf(model);
$scope.story.users.splice(idx, 1);
};
})
;

View File

@ -1,5 +1,6 @@
<!--
~ Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
~ Copyright (c) 2016 Codethink Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License"); you may
~ not use this file except in compliance with the License. You may obtain
@ -17,6 +18,11 @@
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="alert alert-danger" ng-show="story.private">
<i class="fa fa-eye-slash"></i>
<strong>This story is private</strong>.
Edit this story to change the privacy settings.
</div>
<div ng-include
src="'/inline/story_detail.html'"
ng-hide="showEditForm">
@ -91,59 +97,158 @@
<!-- Template for the header and description -->
<script type="text/ng-template" id="/inline/story_detail_form.html">
<form name="storyForm">
<div class="form-group">
<textarea type="text"
class="form-control context-edit h1"
ng-model="story.title"
required
ng-disabled="isUpdating"
maxlength="255"
placeholder="Story Title">
</textarea>
</div>
<div class="form-group" ng-show="previewStory">
<insert-markdown content="story.description">
</insert-markdown>
</div>
<div class="form-group">
<textarea placeholder="Enter a story description here"
class="form-control context-edit"
msd-elastic
rows="3"
required
ng-disabled="isUpdating"
ng-model="story.description">
</textarea>
</div>
<hr>
<form name="storyForm" role="form" class="form-horizontal">
<div class="form-group has-feedback"
ng-class="{'has-error': storyForm.title.$invalid,
'has-success': !storyForm.title.$invalid}">
<label for="title" class="col-sm-2 control-label">
Title
</label>
<div class="col-sm-10">
<input id="title"
name="title"
type="text"
class="form-control"
ng-model="story.title"
required
focus
maxlength="100"
placeholder="Story title"
ng-disabled="isUpdating">
<span class="form-control-feedback"
ng-show="storyForm.title.$invalid">
<i class="fa fa-times fa-lg"></i>
</span>
<span class="form-control-feedback"
ng-show="!storyForm.title.$invalid">
<i class="fa fa-check fa-lg"></i>
</span>
<div class="clearfix">
<div class="pull-right">
<div class="btn" ng-show="isUpdating">
<i class="fa fa-spinner fa-lg fa-spin"></i>
<div class="help-block text-danger"
ng-show="storyForm.title.$invalid">
<span ng-show="storyForm.title.$error.required">
A story title is required.
</span>
</div>
</div>
</div>
<hr ng-show="preview">
<div class="form-group" ng-show="preview">
<div class="col-sm-offset-1 col-sm-10">
<insert-markdown content="story.description">
</insert-markdown>
</div>
</div>
<hr ng-show="preview">
<div class="form-group">
<label for="description"
class="col-sm-2 control-label">
Description
</label>
<div class="col-sm-10">
<textarea id="description"
class="form-control"
ng-model="story.description"
msd-elastic
placeholder="Enter a story description here."
ng-disabled="isUpdating">
</textarea>
</div>
</div>
<div class="form-group">
<label for="private" class="col-sm-2 control-label">
Private
</label>
<div class="col-sm-10 checkbox">
<input id="private"
type="checkbox"
class="modal-checkbox"
ng-model="story.private"
ng-disabled="isUpdating"
/>
</div>
</div>
<div class="row">
<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
</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)"
ng-show="story.users.length > 1">
&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>
</div>
</div>
<div class="row">
<div class="clearfix">
<div class="pull-right">
<div class="btn" ng-show="isUpdating">
<i class="fa fa-spinner fa-lg fa-spin"></i>
</div>
<button type="button"
class="btn btn-primary"
ng-click="update()"
ng-disabled="!storyForm.$valid">
Save
</button>
<button type="button"
class="btn btn-default"
ng-click="cancel()">
Cancel
</button>
</div>
<button type="button"
class="btn btn-primary"
ng-click="update()"
ng-disabled="!storyForm.$valid">
Save
class="btn btn-link"
ng-click="remove()" permission="is_superuser">
Remove this story
</button>
<button type="button"
class="btn btn-default"
ng-click="cancel()">
Cancel
class="btn btn-primary"
ng-click="previewStory = !previewStory">
Toggle Preview
</button>
</div>
<button type="button"
class="btn btn-link"
ng-click="remove()" permission="is_superuser">
Remove this story
</button>
<button type="button"
class="btn btn-primary"
ng-click="previewStory = !previewStory">
Toggle Preview
</button>
</div>
</form>
</script>

View File

@ -49,6 +49,69 @@
</textarea>
</div>
</div>
<div class="form-group">
<label for="private"
class="col-sm-2 control-label">
Private
</label>
<div class="col-sm-10 checkbox">
<input id="private"
type="checkbox"
class="modal-checkbox"
ng-model="story.private"
ng-disabled="isSaving"
/>
</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
</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>
</div>
</form>
</div>
</div>