diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee index d84ae95d3a..f428b9bb14 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsHandler.coffee @@ -26,7 +26,8 @@ module.exports = addUserToProject: (project_id, email, privilegeLevel, callback)-> emails = mimelib.parseAddresses(email) - email = emails[0].address?.toLowerCase() + email = emails[0]?.address?.toLowerCase() + return callback(new Error("no valid email provided")) if !email? self = @ User.findOne {'email':email}, (err, user)-> async.waterfall [ diff --git a/services/web/app/coffee/Features/Editor/EditorController.coffee b/services/web/app/coffee/Features/Editor/EditorController.coffee index 3bb489ea12..32c770e0da 100644 --- a/services/web/app/coffee/Features/Editor/EditorController.coffee +++ b/services/web/app/coffee/Features/Editor/EditorController.coffee @@ -138,7 +138,7 @@ module.exports = EditorController = CollaboratorsHandler.addUserToProject project_id, email, privileges, (err, user)=> ProjectEntityHandler.flushProjectToThirdPartyDataStore project_id, "", -> EditorRealTimeController.emitToRoom(project_id, 'userAddedToProject', user, privileges) - callback null, true + callback null, ProjectEditorHandler.buildUserModelView(user, privileges) removeUserFromProject: (project_id, user_id, callback)-> CollaboratorsHandler.removeUserFromProject project_id, user_id, => diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.jade index 2193b2a927..1fba8d539e 100644 --- a/services/web/app/views/project/editor.jade +++ b/services/web/app/views/project/editor.jade @@ -37,6 +37,8 @@ block content include ./editor/header + include ./editor/share + #ide-body(ng-cloak, layout="main", ng-hide="state.loading") .ui-layout-west include ./editor/file-tree @@ -45,33 +47,6 @@ block content include ./editor/editor include ./editor/track-changes - //- #loadingScreen - //- h3 Loading... - //- p#loadingMessage Loading editor - - //- #errorMessages - //- #connectionLostMessage(style="display: none;") - //- | Lost connection. - //- span#trying-reconnect - //- | Reconnecting in - //- span#reconnection-countdown ? - //- | seconds. - //- a(href='#')#try-reconnect-now Try now. - //- span#reconnecting - //- | Reconnecting... - - //- #savingProblems(style="display: none") - //- | Saving... - - //- div#toolbar.sidebar-navigation - //- ul#tabs - //- #toolbar-footer - - //- #tab-content.tab-content - - //- include ../templates - //- include ../templates/dropbox - script(src='/socket.io/socket.io.js') script(type='text/javascript'). diff --git a/services/web/app/views/project/editor/header.jade b/services/web/app/views/project/editor/header.jade index 6ebd08762a..a6859981ee 100644 --- a/services/web/app/views/project/editor/header.jade +++ b/services/web/app/views/project/editor/header.jade @@ -13,7 +13,13 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading") i.fa.fa-pencil .toolbar-right - a.btn.btn-full-height(href='#', tooltip="Share", tooltip-placement="bottom") + a.btn.btn-full-height( + href, + tooltip="Share", + tooltip-placement="bottom", + ng-click="openShareProjectModal()", + ng-controller="ShareController" + ) i.fa.fa-fw.fa-group a.btn.btn-full-height( href, diff --git a/services/web/app/views/project/editor/share.jade b/services/web/app/views/project/editor/share.jade new file mode 100644 index 0000000000..4efdb72cc4 --- /dev/null +++ b/services/web/app/views/project/editor/share.jade @@ -0,0 +1,61 @@ +script(type='text/ng-template', id='shareProjectModalTemplate') + .modal-header + h3 Share Project + .modal-body.modal-body-share + .container-fluid + .row.public-access-level + .col-md-9 This project is private and can only be accessed by the people below. + .col-md-3.text-right + a(href) Make public + .row.project-member + .col-md-8 {{ project.owner.email }} + .text-right( + ng-class="{'col-md-3': project.members.length > 0, 'col-md-4': project.members.length == 0}" + ) Owner + .row.project-member(ng-repeat="member in project.members") + .col-md-8 {{ member.email }} + .col-md-3.text-right + span(ng-show="member.privileges == 'readAndWrite'") Can Edit + span(ng-show="member.privileges == 'readOnly'") Read Only + .col-md-1 + a( + href + tooltip="Remove collaborator" + tooltip-placement="bottom" + ng-click="removeMember(member)" + ) + i.fa.fa-times + .row.invite-controls + form(ng-show="canAddCollaborators") + .small Share with your collaborators + .form-group + input.form-control( + type="email" + placeholder="Enter email address..." + ng-model="inputs.email" + focus-on="open" + ) + .form-group + .pull-right + select.privileges.form-control( + ng-model="inputs.privileges" + name="privileges" + ) + option(value="readAndWrite") Can Edit + option(value="readOnly") Read Only + |    + button.btn.btn-info( + type="submit" + ng-click="addMember()" + ) Share + div.text-center(ng-hide="canAddCollaborators") + p You need to upgrade your account to add more collaborators. + a.btn.btn-info(href) Start Free Trial + + .modal-footer + .modal-footer-left + i.fa.fa-refresh.fa-spin(ng-show="state.inflight") + span.text-danger.error(ng-show="state.error") {{ state.error }} + button.btn.btn-primary( + ng-click="done()" + ) Done \ No newline at end of file diff --git a/services/web/public/coffee/app/ide.coffee b/services/web/public/coffee/app/ide.coffee index 87bb3a78dc..39e25dd77e 100644 --- a/services/web/public/coffee/app/ide.coffee +++ b/services/web/public/coffee/app/ide.coffee @@ -7,6 +7,7 @@ define [ "ide/online-users/OnlineUsersManager" "ide/track-changes/TrackChangesManager" "ide/pdf/PdfManager" + "ide/share/index" "ide/directives/layout" "ide/services/ide" "directives/focus" diff --git a/services/web/public/coffee/app/ide/share/controllers/ShareController.coffee b/services/web/public/coffee/app/ide/share/controllers/ShareController.coffee new file mode 100644 index 0000000000..d76bd07965 --- /dev/null +++ b/services/web/public/coffee/app/ide/share/controllers/ShareController.coffee @@ -0,0 +1,11 @@ +define [ + "base" +], (App) -> + App.controller "ShareController", ["$scope", "$modal", ($scope, $modal) -> + $scope.openShareProjectModal = () -> + $modal.open( + templateUrl: "shareProjectModalTemplate" + controller: "ShareProjectModalController" + scope: $scope + ) + ] diff --git a/services/web/public/coffee/app/ide/share/controllers/ShareProjectModalController.coffee b/services/web/public/coffee/app/ide/share/controllers/ShareProjectModalController.coffee new file mode 100644 index 0000000000..de849e4d8a --- /dev/null +++ b/services/web/public/coffee/app/ide/share/controllers/ShareProjectModalController.coffee @@ -0,0 +1,57 @@ +define [ + "base" +], (App) -> + App.controller "ShareProjectModalController", ["$scope", "$modalInstance", "$timeout", "projectMembers", ($scope, $modalInstance, $timeout, projectMembers) -> + $scope.inputs = { + privileges: "readAndWrite" + email: "" + } + $scope.state = { + error: null + inflight: false + } + + $modalInstance.opened.then () -> + $timeout () -> + $scope.$broadcast "open" + , 200 + + INFINITE_COLLABORATORS = -1 + $scope.$watch "project.members.length", (noOfMembers) -> + allowedNoOfMembers = $scope.project.features.collaborators + $scope.canAddCollaborators = noOfMembers < allowedNoOfMembers or allowedNoOfMembers == INFINITE_COLLABORATORS + + $scope.addMember = () -> + console.log "EMAIL", $scope.inputs.email + return if !$scope.inputs.email? or $scope.inputs.email == "" + $scope.state.error = null + $scope.state.inflight = true + projectMembers + .addMember($scope.inputs.email, $scope.inputs.privileges) + .then (user) -> + $scope.state.inflight = false + $scope.inputs.email = "" + console.log "GOT USER", user + $scope.project.members.push user + .catch () -> + $scope.state.inflight = false + $scope.state.error = "Sorry, something went wrong :(" + + + $scope.removeMember = (member) -> + $scope.state.error = null + $scope.state.inflight = true + projectMembers + .removeMember(member) + .then () -> + $scope.state.inflight = false + index = $scope.project.members.indexOf(member) + return if index == -1 + $scope.project.members.splice(index, 1) + .catch () -> + $scope.state.inflight = false + $scope.state.error = "Sorry, something went wrong :(" + + $scope.done = () -> + $modalInstance.close() + ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/share/index.coffee b/services/web/public/coffee/app/ide/share/index.coffee new file mode 100644 index 0000000000..545a145be3 --- /dev/null +++ b/services/web/public/coffee/app/ide/share/index.coffee @@ -0,0 +1,5 @@ +define [ + "ide/share/controllers/ShareController" + "ide/share/controllers/ShareProjectModalController" + "ide/share/services/projectMembers" +], () -> \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/share/services/projectMembers.coffee b/services/web/public/coffee/app/ide/share/services/projectMembers.coffee new file mode 100644 index 0000000000..144a6c2684 --- /dev/null +++ b/services/web/public/coffee/app/ide/share/services/projectMembers.coffee @@ -0,0 +1,30 @@ +define [ + "base" +], (App) -> + App.factory "projectMembers", ["ide", "$q", (ide, $q) -> + return { + removeMember: (member) -> + deferred = $q.defer() + + ide.socket.emit "removeUserFromProject", member._id, (error) => + if error? + return deferred.reject(error) + deferred.resolve() + + return deferred.promise + + addMember: (email, privileges) -> + deferred = $q.defer() + + ide.socket.emit "addUserToProject", email, privileges, (error, user) => + if error? + return deferred.reject(error) + + if !user + deferred.reject() + else + deferred.resolve(user) + + return deferred.promise + } + ] \ No newline at end of file diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less index 185fec5e64..c1ff220a71 100644 --- a/services/web/public/stylesheets/app/editor.less +++ b/services/web/public/stylesheets/app/editor.less @@ -3,6 +3,7 @@ @import "./editor/toolbar.less"; @import "./editor/left-menu.less"; @import "./editor/pdf.less"; +@import "./editor/share.less"; .full-size { position: absolute; diff --git a/services/web/public/stylesheets/app/editor/share.less b/services/web/public/stylesheets/app/editor/share.less new file mode 100644 index 0000000000..21230a9145 --- /dev/null +++ b/services/web/public/stylesheets/app/editor/share.less @@ -0,0 +1,49 @@ +.modal-body-share { + h3 { + border-bottom: 1px solid @gray-lighter; + padding-bottom: @line-height-computed / 4; + margin: 0; + font-size: 1rem; + } + + .project-member, .public-access-level { + padding: (@line-height-computed / 2) 0; + border-bottom: 1px solid @gray-lighter; + font-size: 14px; + } + + .public-access-level { + color: @gray; + padding-top: 0; + font-size: 12px; + } + + .project-member { + &:hover { + background-color: @gray-lightest; + } + } + + .invite-controls { + .small { + padding: 2px; + } + padding: @line-height-computed / 2; + background-color: @gray-lightest; + margin-top: @line-height-computed / 2; + form { + .form-group { + margin-bottom: @line-height-computed / 2; + &:last-child { + margin-bottom: 0; + } + } + .privileges { + display: inline-block; + width: auto; + height: 30px; + font-size: 14px; + } + } + } +} \ No newline at end of file diff --git a/services/web/public/stylesheets/components/modals.less b/services/web/public/stylesheets/components/modals.less index 4b5d5c5953..a325d0da32 100755 --- a/services/web/public/stylesheets/components/modals.less +++ b/services/web/public/stylesheets/components/modals.less @@ -126,6 +126,10 @@ .btn-block + .btn-block { margin-left: 0; } + + .modal-footer-left { + float: left; + } } // Scale up the modal