diff --git a/services/web/app/views/project/list.jade b/services/web/app/views/project/list.jade
index cfaacb498d..e75b5091fb 100644
--- a/services/web/app/views/project/list.jade
+++ b/services/web/app/views/project/list.jade
@@ -231,7 +231,7 @@ block content
.row.row-spaced
.col-md-12
.card.card-thin
- ul.list-unstyled.project-list(
+ ul.list-unstyled.project-list.structured-list(
select-all-list,
ng-if="projects.length > 0",
ng-cloak
@@ -243,11 +243,11 @@ block content
select-all,
type="checkbox"
)
- span.title TITLE
+ | TITLE
.col-md-2
- span.owner OWNER
+ | OWNER
.col-md-4
- span.last-modified LAST MODIFIED
+ | LAST MODIFIED
li.project_entry.container-fluid(
ng-repeat="project in visibleProjects | orderBy:'lastUpdated':true",
ng-controller="ProjectListItemController"
diff --git a/services/web/app/views/subscriptions/group_admin.jade b/services/web/app/views/subscriptions/group_admin.jade
index 823b57beeb..fba2978e83 100644
--- a/services/web/app/views/subscriptions/group_admin.jade
+++ b/services/web/app/views/subscriptions/group_admin.jade
@@ -1,52 +1,84 @@
extends ../layout
block content
- .container.box
- .row
- .span12
- .page-header
- h2 Group Admin
+ .content.content-alt
+ .container
+ .row
+ .col-md-10.col-md-offset-1
+ .card(ng-controller="GroupMembersController")
+ .page-header
+ .pull-right(ng-cloak)
+ small(ng-show="selectedUsers.length == 0") You have added {{ users.length }} of {{ groupSize }} available members
+ a.btn.btn-danger(
+ href,
+ ng-show="selectedUsers.length > 0"
+ ng-click="removeMembers()"
+ ) Remove from group
+ h1 Group Account
- div You are allowed up to
- strong #{subscription.membersLimit}
- | members in this group
+ .row-spaced-small
+ ul.list-unstyled.structured-list(
+ select-all-list,
+ ng-cloak
+ )
+ li.container-fluid
+ .row
+ .col-md-5
+ input.select-all(
+ select-all,
+ type="checkbox"
+ )
+ span.email EMAIL
+ .col-md-5
+ span.name NAME
+ .col-md-2
+ span.registered REGISTERED
+ li.container-fluid(
+ ng-repeat="user in users | orderBy:'email':true",
+ ng-controller="GroupMemberListItemController"
+ )
+ .row
+ .col-md-5
+ input.select-item(
+ select-individual,
+ type="checkbox",
+ ng-model="user.selected"
+ )
+ span.email {{ user.email }}
+ .col-md-5
+ span.name {{ user.first_name }} {{ user.last_name }}
+ .col-md-2
+ span.registered
+ i.fa.fa-check.text-success(ng-show="!user.holdingAccount")
+ i.fa.fa-times(ng-show="user.holdingAccount")
+ li(
+ ng-if="users.length == 0",
+ ng-cloak
+ )
+ .row
+ .col-md-12.text-centered
+ small No members
- table.table-striped.table.table-striped
- thead
- tr
- th
- input(type="checkbox").select-all
- th email
- th Name
- th Registered
+ div(ng-if="users.length < groupSize", ng-cloak)
+ hr
+ p
+ .small Add more members
+ form.form
+ .row
+ .col-xs-6
+ input.form-control(
+ name="email",
+ type="text",
+ placeholder="jane@example.com, joe@example.com",
+ ng-model="inputs.emails",
+ on-enter="addMembers()"
+ )
+ .col-xs-6
+ button.btn.btn-primary(ng-click="addMembers()") Add
- tbody#userList
- -each user in users
- tr
- td
- input(type="checkbox").select-one
- td #{user.email}
- td #{user.first_name} #{user.last_name}
- td #{!user.holdingAccount}
- td
- input(type="hidden", name="user_id", value=user._id).user_id {{user._id}}
-
-
- div
- button.btn.btn-danger#deleteUsers Delete Selected
- div
- div
- form.well.form-inline#addUserToGroup
- div
- input(name="_csrf", type="hidden", value=csrfToken)
- input(name="email", type="email", placeholder="someone@email.com")#newEmail.email.input-large
- button.btn.btn-primary.addUser Add
- div
- div Add multiple emails seperated with commas or space.
-
-
- - locals.supressDefaultJs = true
- script(data-main='/js/SubscriptionGroupsManager.js', src='/js/libs/require.js')
+ script(type="text/javascript").
+ window.users = !{JSON.stringify(users)};
+ window.groupSize = #{subscription.membersLimit};
diff --git a/services/web/public/coffee/app/directives/selectAll.coffee b/services/web/public/coffee/app/directives/selectAll.coffee
new file mode 100644
index 0000000000..79b683c20a
--- /dev/null
+++ b/services/web/public/coffee/app/directives/selectAll.coffee
@@ -0,0 +1,67 @@
+define [
+ "base"
+], (App) ->
+ App.directive "selectAllList", () ->
+ return {
+ controller: ["$scope", ($scope) ->
+ # Selecting or deselecting all should apply to all projects
+ selectAll = () ->
+ $scope.$broadcast "select-all:select"
+
+ deselectAll = () ->
+ $scope.$broadcast "select-all:deselect"
+
+ clearSelectAllState = () ->
+ $scope.$broadcast "select-all:clear"
+
+ return {
+ clearSelectAllState: clearSelectAllState
+ selectAll: selectAll
+ deselectAll: deselectAll
+ }
+ ]
+ link: (scope, element, attrs) ->
+
+
+ }
+
+ App.directive "selectAll", () ->
+ return {
+ require: "^selectAllList"
+ link: (scope, element, attrs, selectAllListController) ->
+ scope.$on "select-all:clear", () ->
+ element.prop("checked", false)
+
+ element.change () ->
+ if element.is(":checked")
+ selectAllListController.selectAll()
+ else
+ selectAllListController.deselectAll()
+ return true
+ }
+
+ App.directive "selectIndividual", () ->
+ return {
+ require: "^selectAllList"
+ scope: {
+ ngModel: "="
+ }
+ link: (scope, element, attrs, selectAllListController) ->
+ ignoreChanges = false
+
+ scope.$watch "ngModel", (value) ->
+ if value? and !ignoreChanges
+ selectAllListController.clearSelectAllState()
+
+ scope.$on "select-all:select", () ->
+ ignoreChanges = true
+ scope.$apply () ->
+ scope.ngModel = true
+ ignoreChanges = false
+
+ scope.$on "select-all:deselect", () ->
+ ignoreChanges = true
+ scope.$apply () ->
+ scope.ngModel = false
+ ignoreChanges = false
+ }
\ No newline at end of file
diff --git a/services/web/public/coffee/app/main.coffee b/services/web/public/coffee/app/main.coffee
index 452c661b8f..c2a70fdb78 100644
--- a/services/web/public/coffee/app/main.coffee
+++ b/services/web/public/coffee/app/main.coffee
@@ -3,12 +3,14 @@ define [
"main/user-details"
"main/account-settings"
"main/plans"
+ "main/group-members"
"directives/asyncForm"
"directives/stopPropagation"
"directives/focus"
"directives/equals"
"directives/fineUpload"
"directives/onEnter"
+ "directives/selectAll"
"filters/formatDate"
], () ->
angular.bootstrap(document.body, ["SharelatexApp"])
\ No newline at end of file
diff --git a/services/web/public/coffee/app/main/group-members.coffee b/services/web/public/coffee/app/main/group-members.coffee
new file mode 100644
index 0000000000..f1f61623ee
--- /dev/null
+++ b/services/web/public/coffee/app/main/group-members.coffee
@@ -0,0 +1,54 @@
+define [
+ "base"
+], (App) ->
+ App.controller "GroupMembersController", ($scope, queuedHttp) ->
+ $scope.users = window.users
+ $scope.groupSize = window.groupSize
+ $scope.selectedUsers = []
+
+ $scope.inputs =
+ emails: ""
+
+ parseEmails = (emailsString)->
+ regexBySpaceOrComma = /[\s,]+/
+ emails = emailsString.split(regexBySpaceOrComma)
+ emails = _.map emails, (email)->
+ email = email.trim()
+ emails = _.select emails, (email)->
+ email.indexOf("@") != -1
+ return emails
+
+ $scope.addMembers = () ->
+ emails = parseEmails($scope.inputs.emails)
+ for email in emails
+ queuedHttp
+ .post("/subscription/group/user", {
+ email: email,
+ _csrf: window.csrfToken
+ })
+ .success (data) ->
+ $scope.users.push data.user if data.user?
+ $scope.inputs.emails = ""
+
+ $scope.removeMembers = () ->
+ for user in $scope.selectedUsers
+ do (user) ->
+ queuedHttp({
+ method: "DELETE",
+ url: "/subscription/group/user/#{user._id}"
+ headers:
+ "X-Csrf-Token": window.csrfToken
+ })
+ .success () ->
+ index = $scope.users.indexOf(user)
+ return if index == -1
+ $scope.users.splice(index, 1)
+ $scope.selectedUsers = []
+
+ $scope.updateSelectedUsers = () ->
+ $scope.selectedUsers = $scope.users.filter (user) -> user.selected
+
+ App.controller "GroupMemberListItemController", ($scope) ->
+ $scope.$watch "user.selected", (value) ->
+ if value?
+ $scope.updateSelectedUsers()
\ No newline at end of file
diff --git a/services/web/public/coffee/app/main/project-list.coffee b/services/web/public/coffee/app/main/project-list.coffee
index dc4a76c75c..0ad0c94660 100644
--- a/services/web/public/coffee/app/main/project-list.coffee
+++ b/services/web/public/coffee/app/main/project-list.coffee
@@ -1,71 +1,6 @@
define [
"base"
], (App) ->
- App.directive "selectAllList", () ->
- return {
- controller: ["$scope", ($scope) ->
- # Selecting or deselecting all should apply to all projects
- selectAll = () ->
- $scope.$broadcast "select-all:select"
-
- deselectAll = () ->
- $scope.$broadcast "select-all:deselect"
-
- clearSelectAllState = () ->
- $scope.$broadcast "select-all:clear"
-
- return {
- clearSelectAllState: clearSelectAllState
- selectAll: selectAll
- deselectAll: deselectAll
- }
- ]
- link: (scope, element, attrs) ->
-
-
- }
-
- App.directive "selectAll", () ->
- return {
- require: "^selectAllList"
- link: (scope, element, attrs, selectAllListController) ->
- scope.$on "select-all:clear", () ->
- element.prop("checked", false)
-
- element.change () ->
- if element.is(":checked")
- selectAllListController.selectAll()
- else
- selectAllListController.deselectAll()
- return true
- }
-
- App.directive "selectIndividual", () ->
- return {
- require: "^selectAllList"
- scope: {
- ngModel: "="
- }
- link: (scope, element, attrs, selectAllListController) ->
- ignoreChanges = false
-
- scope.$watch "ngModel", (value) ->
- if value? and !ignoreChanges
- selectAllListController.clearSelectAllState()
-
- scope.$on "select-all:select", () ->
- ignoreChanges = true
- scope.$apply () ->
- scope.ngModel = true
- ignoreChanges = false
-
- scope.$on "select-all:deselect", () ->
- ignoreChanges = true
- scope.$apply () ->
- scope.ngModel = false
- ignoreChanges = false
- }
-
App.factory "queuedHttp", ["$http", "$q", ($http, $q) ->
pendingRequests = []
inflight = false
diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less
index 626b4ac76a..6e1ebe3aed 100644
--- a/services/web/public/stylesheets/app/project-list.less
+++ b/services/web/public/stylesheets/app/project-list.less
@@ -68,16 +68,16 @@ form.project-search {
}
}
-ul.project-list {
+ul.structured-list {
list-style-type: none;
margin: 0;
overflow: hidden;
- overflow-y: scroll;
+ overflow-y: auto;
li {
border-bottom: 1px solid @gray-lightest;
padding: (@line-height-computed / 4) 0;
&:first-child {
- .last-modified, .owner {
+ .header {
font-size: 1rem;
}
}
@@ -101,6 +101,11 @@ ul.project-list {
.select-item, .select-all {
margin-left: @line-height-computed / 4;
}
+ }
+}
+
+ul.project-list {
+ li {
.last-modified, .owner {
font-size: .8rem;
}