From a8d371d2f63202ca13e80b112f7b094a82beeff1 Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 3 Jul 2014 15:05:35 +0100 Subject: [PATCH] Sort out permissions and displaying anonymous users --- .../web/app/views/project/editor/editor.jade | 3 +- .../app/views/project/editor/file-tree.jade | 48 ++++++++++++++----- .../web/app/views/project/editor/header.jade | 1 + .../web/app/views/project/editor/share.jade | 14 +++--- .../views/project/editor/track-changes.jade | 5 +- services/web/public/coffee/app/ide.coffee | 3 ++ .../ide/connection/ConnectionManager.coffee | 1 + .../highlights/HighlightsManager.coffee | 8 ++-- .../ide/file-tree/directives/draggable.coffee | 12 +++-- .../ide/file-tree/directives/droppable.coffee | 13 ++--- .../file-tree/directives/fileEntity.coffee | 10 +++- .../online-users/OnlineUsersManager.coffee | 2 +- .../ide/permissions/PermissionsManager.coffee | 19 ++++++++ .../track-changes/TrackChangesManager.coffee | 11 +++-- .../stylesheets/app/editor/file-tree.less | 4 ++ 15 files changed, 111 insertions(+), 43 deletions(-) create mode 100644 services/web/public/coffee/app/ide/permissions/PermissionsManager.coffee diff --git a/services/web/app/views/project/editor/editor.jade b/services/web/app/views/project/editor/editor.jade index 117854654a..1bff22a46d 100644 --- a/services/web/app/views/project/editor/editor.jade +++ b/services/web/app/views/project/editor/editor.jade @@ -25,7 +25,8 @@ div.full-size( cursor-position="editor.cursorPosition", goto-line="editor.gotoLine", resize-on="layout:main:resize,layout:pdf:resize", - annotations="pdf.logEntryAnnotations[editor.open_doc_id]" + annotations="pdf.logEntryAnnotations[editor.open_doc_id]", + read-only="!permissions.write" ) .ui-layout-east diff --git a/services/web/app/views/project/editor/file-tree.jade b/services/web/app/views/project/editor/file-tree.jade index 67fa6c5574..d54af53327 100644 --- a/services/web/app/views/project/editor/file-tree.jade +++ b/services/web/app/views/project/editor/file-tree.jade @@ -1,5 +1,5 @@ aside#file-tree(ng-controller="FileTreeController") - .toolbar.toolbar-small + .toolbar.toolbar-small(ng-if="permissions.write") a( href, ng-click="openNewDocModal()", @@ -39,14 +39,19 @@ aside#file-tree(ng-controller="FileTreeController") ) i.fa.fa-trash-o - .file-tree-inner(ng-if="rootFolder", ng-controller="FileTreeRootFolderController") + .file-tree-inner( + ng-if="rootFolder", + ng-controller="FileTreeRootFolderController", + ng-class="{ 'no-toolbar': !permissions.write }" + ) ul.list-unstyled.file-tree-list( - droppable + droppable="permissions.write" accept=".entity-name" on-drop-callback="onDrop" ) file-entity( entity="entity", + permissions="permissions", ng-repeat="entity in rootFolder.children | orderBy:[orderByFoldersFirst, 'name']" ) @@ -76,11 +81,12 @@ script(type='text/ng-template', id='entityListItemTemplate') .entity(ng-if="entity.type != 'folder'") .entity-name( ng-click="select()" - ng-dblclick="startRenaming()" - draggable + ng-dblclick="permissions.write && startRenaming()" + draggable="permissions.write" context-menu data-target="context-menu-{{ entity.id }}" context-menu-container="body" + context-menu-disabled="!permissions.write" ) //- Just a spacer to align with folders i.fa.fa-fw.toggle(ng-if="entity.type != 'folder'") @@ -92,6 +98,7 @@ script(type='text/ng-template', id='entityListItemTemplate') ng-hide="entity.renaming" ) {{ entity.name }} input( + ng-if="permissions.write", ng-show="entity.renaming", ng-model="inputs.name", ng-blur="finishRenaming()", @@ -99,7 +106,10 @@ script(type='text/ng-template', id='entityListItemTemplate') on-enter="finishRenaming()" ) - span.dropdown(ng-show="entity.selected") + span.dropdown( + ng-show="entity.selected", + ng-if="permissions.write" + ) a.dropdown-toggle(href) i.fa.fa-chevron-down @@ -117,7 +127,10 @@ script(type='text/ng-template', id='entityListItemTemplate') ng-click="openDeleteModal()" ) Delete - div.dropdown.context-menu(id="context-menu-{{ entity.id }}") + div.dropdown.context-menu( + id="context-menu-{{ entity.id }}", + ng-if="permissions.write" + ) ul.dropdown-menu li a( @@ -138,9 +151,9 @@ script(type='text/ng-template', id='entityListItemTemplate') .entity(ng-if="entity.type == 'folder'", ng-controller="FileTreeFolderController") .entity-name( ng-click="select()" - ng-dblclick="startRenaming()" - draggable - droppable + ng-dblclick="permissions.write && startRenaming()" + draggable="permissions.write" + droppable="permissions.write" accept=".entity-name" on-drop-callback="onDrop" ) @@ -148,6 +161,7 @@ script(type='text/ng-template', id='entityListItemTemplate') context-menu data-target="context-menu-{{ entity.id }}" context-menu-container="body" + context-menu-disabled="!permissions.write" ) i.fa.fa-fw.toggle( ng-if="entity.type == 'folder'" @@ -168,6 +182,7 @@ script(type='text/ng-template', id='entityListItemTemplate') ng-hide="entity.renaming" ) {{ entity.name }} input( + ng-if="permissions.write", ng-show="entity.renaming", ng-model="inputs.name", ng-blur="finishRenaming()", @@ -175,7 +190,10 @@ script(type='text/ng-template', id='entityListItemTemplate') on-enter="finishRenaming()" ) - span.dropdown(ng-show="entity.selected") + span.dropdown( + ng-if="permissions.write" + ng-show="entity.selected" + ) a.dropdown-toggle(href) i.fa.fa-chevron-down @@ -212,7 +230,10 @@ script(type='text/ng-template', id='entityListItemTemplate') ng-click="openUploadFileModal()" ) Upload File - .dropdown.context-menu(id="context-menu-{{ entity.id }}") + .dropdown.context-menu( + ng-if="permissions.write" + id="context-menu-{{ entity.id }}" + ) ul.dropdown-menu li a( @@ -254,12 +275,13 @@ script(type='text/ng-template', id='entityListItemTemplate') ul.list-unstyled( ng-if="entity.type == 'folder'" ng-show="expanded" - droppable + droppable="permissions.write" accept=".entity-name" on-drop-callback="onDrop" ) file-entity( entity="child", + permissions="permissions", ng-repeat="child in entity.children | orderBy:[orderByFoldersFirst, 'name']" ) diff --git a/services/web/app/views/project/editor/header.jade b/services/web/app/views/project/editor/header.jade index b9184fca4f..cb1dfed94d 100644 --- a/services/web/app/views/project/editor/header.jade +++ b/services/web/app/views/project/editor/header.jade @@ -15,6 +15,7 @@ header.toolbar.toolbar-header(ng-cloak, ng-hide="state.loading") .toolbar-right a.btn.btn-full-height( href, + ng-if="permissions.admin", tooltip="Share", tooltip-placement="bottom", ng-click="openShareProjectModal()", diff --git a/services/web/app/views/project/editor/share.jade b/services/web/app/views/project/editor/share.jade index a6a6df8f9a..de1968b35e 100644 --- a/services/web/app/views/project/editor/share.jade +++ b/services/web/app/views/project/editor/share.jade @@ -9,7 +9,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate') .modal-body.modal-body-share .container-fluid .row.public-access-level(ng-show="project.publicAccesLevel == 'private'") - .col-md-12.text-center + .col-xs-12.text-center | This project is private and can only be accessed by the people below. |    a( @@ -17,7 +17,7 @@ script(type='text/ng-template', id='shareProjectModalTemplate') ng-click="openMakePublicModal()" ) Make Public .row.public-access-level(ng-show="project.publicAccesLevel != 'private'") - .col-md-12.text-center + .col-xs-12.text-center strong(ng-if="project.publicAccesLevel == 'readAndWrite'") This project is public and can be edited by anyone with the URL. strong(ng-if="project.publicAccesLevel == 'readOnly'") This project is public and can be viewed by anyone with the URL. |    @@ -26,16 +26,16 @@ script(type='text/ng-template', id='shareProjectModalTemplate') ng-click="openMakePrivateModal()" ) Make Private .row.project-member - .col-md-8 {{ project.owner.email }} + .col-xs-8 {{ project.owner.email }} .text-right( - ng-class="{'col-md-3': project.members.length > 0, 'col-md-4': project.members.length == 0}" + ng-class="{'col-xs-3': project.members.length > 0, 'col-xs-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 + .col-xs-8 {{ member.email }} + .col-xs-3.text-right span(ng-show="member.privileges == 'readAndWrite'") Can Edit span(ng-show="member.privileges == 'readOnly'") Read Only - .col-md-1 + .col-xs-1 a( href tooltip="Remove collaborator" diff --git a/services/web/app/views/project/editor/track-changes.jade b/services/web/app/views/project/editor/track-changes.jade index 38e3cb890e..29fbb747e2 100644 --- a/services/web/app/views/project/editor/track-changes.jade +++ b/services/web/app/views/project/editor/track-changes.jade @@ -70,8 +70,11 @@ div#trackChanges(ng-show="ui.view == 'track-changes'") div.users div.user(ng-repeat="update_user in update.meta.users") .color-square(ng-style="{'background-color': 'hsl({{ update_user.hue }}, 100%, 50%)'}") - span(ng-if="update_user.id != user.id") {{user.first_name}} {{user.last_name}} + span(ng-if="update_user.id != user.id") {{update_user.first_name}} {{update_user.last_name}} span(ng-if="update_user.id == user.id") You + div.user(ng-if="update.meta.users.length == 0") + .color-square(style="background-color: hsl(100, 100%, 50%)") + span Anonymous .loading(ng-show="trackChanges.loading") i.fa.fa-spin.fa-refresh diff --git a/services/web/public/coffee/app/ide.coffee b/services/web/public/coffee/app/ide.coffee index e7c1e33db4..bd9b15258e 100644 --- a/services/web/public/coffee/app/ide.coffee +++ b/services/web/public/coffee/app/ide.coffee @@ -5,6 +5,7 @@ define [ "ide/editor/EditorManager" "ide/online-users/OnlineUsersManager" "ide/track-changes/TrackChangesManager" + "ide/permissions/PermissionsManager" "ide/pdf/PdfManager" "ide/settings/index" "ide/share/index" @@ -22,6 +23,7 @@ define [ EditorManager OnlineUsersManager TrackChangesManager + PermissionsManager PdfManager ) -> App.controller "IdeController", ["$scope", "$timeout", "ide", ($scope, $timeout, ide) -> @@ -59,6 +61,7 @@ define [ ide.onlineUsersManager = new OnlineUsersManager(ide, $scope) ide.trackChangesManager = new TrackChangesManager(ide, $scope) ide.pdfManager = new PdfManager(ide, $scope) + ide.permissionsManager = new PermissionsManager(ide, $scope) ] angular.bootstrap(document.body, ["SharelatexApp"]) \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee b/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee index 0d3d8b5be9..9d5f29983a 100644 --- a/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee +++ b/services/web/public/coffee/app/ide/connection/ConnectionManager.coffee @@ -57,6 +57,7 @@ define [], () -> @$scope.$apply () => @$scope.protocolVersion = protocolVersion @$scope.project = project + @$scope.permissionsLevel = permissionsLevel @$scope.state.load_progress = 100 @$scope.state.loading = false @$scope.$emit "project:joined" diff --git a/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee b/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee index 18d205c448..a8b7699407 100644 --- a/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee +++ b/services/web/public/coffee/app/ide/editor/highlights/HighlightsManager.coffee @@ -204,16 +204,16 @@ define [ _getColorScheme: (hue) -> if @_isDarkTheme() return { - cursor: "hsl(#{hue}, 100%, 50%)" - labelBackgroundColor: "hsl(#{hue}, 100%, 50%)" + cursor: "hsl(#{hue}, 70%, 50%)" + labelBackgroundColor: "hsl(#{hue}, 70%, 50%)" highlightBackgroundColor: "hsl(#{hue}, 100%, 28%);" strikeThroughBackgroundColor: "hsl(#{hue}, 100%, 20%);" strikeThroughForegroundColor: "hsl(#{hue}, 100%, 60%);" } else return { - cursor: "hsl(#{hue}, 100%, 50%)" - labelBackgroundColor: "hsl(#{hue}, 100%, 50%)" + cursor: "hsl(#{hue}, 70%, 50%)" + labelBackgroundColor: "hsl(#{hue}, 70%, 50%)" highlightBackgroundColor: "hsl(#{hue}, 70%, 85%);" strikeThroughBackgroundColor: "hsl(#{hue}, 70%, 95%);" strikeThroughForegroundColor: "hsl(#{hue}, 70%, 40%);" diff --git a/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee b/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee index 1fd3b4a253..4b08a0daf6 100644 --- a/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee +++ b/services/web/public/coffee/app/ide/file-tree/directives/draggable.coffee @@ -4,9 +4,11 @@ define [ App.directive "draggable", () -> return { link: (scope, element, attrs) -> - element.draggable - delay: 250 - opacity: 0.7 - helper: "clone" - scroll: true + scope.$watch attrs.draggable, (draggable) -> + if draggable + element.draggable + delay: 250 + opacity: 0.7 + helper: "clone" + scroll: true } \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee b/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee index 5c3bdefc35..ea68630a21 100644 --- a/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee +++ b/services/web/public/coffee/app/ide/file-tree/directives/droppable.coffee @@ -7,10 +7,11 @@ define [ onDropCallback: "=" } link: (scope, element, attrs) -> - console.log "DROPPABLE", element, scope.onDropCallback - element.droppable - greedy: true - hoverClass: "droppable-hover" - accept: attrs.accept - drop: scope.onDropCallback + scope.$watch attrs.droppable, (droppable) -> + if droppable + element.droppable + greedy: true + hoverClass: "droppable-hover" + accept: attrs.accept + drop: scope.onDropCallback } \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee b/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee index ea58980d81..9f3fcd2407 100644 --- a/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee +++ b/services/web/public/coffee/app/ide/file-tree/directives/fileEntity.coffee @@ -6,10 +6,18 @@ define [ restrict: "E" scope: { entity: "=" + permissions: "=" } templateUrl: "entityListItemTemplate" compile: (element) -> RecursionHelper.compile element, (scope, element, attrs, ctrl) -> - # Link function here if needed + # Don't freak out if we're already in an apply callback + scope.$originalApply = scope.$apply + scope.$apply = (fn = () ->) -> + phase = @$root.$$phase + if (phase == '$apply' || phase == '$digest') + fn() + else + @$originalApply(fn); } ] \ No newline at end of file diff --git a/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee b/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee index ef4a343bda..5e73a6446b 100644 --- a/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee +++ b/services/web/public/coffee/app/ide/online-users/OnlineUsersManager.coffee @@ -56,7 +56,7 @@ define [ OWN_HUE: 200 # We will always appear as this color to ourselves ANONYMOUS_HUE: 100 getHueForUserId: (user_id) -> - if !user_id? + if !user_id? or user_id == "anonymous-user" return @ANONYMOUS_HUE if window.user.id == user_id diff --git a/services/web/public/coffee/app/ide/permissions/PermissionsManager.coffee b/services/web/public/coffee/app/ide/permissions/PermissionsManager.coffee new file mode 100644 index 0000000000..b52ab933e4 --- /dev/null +++ b/services/web/public/coffee/app/ide/permissions/PermissionsManager.coffee @@ -0,0 +1,19 @@ +define [], () -> + class PermissionsManager + constructor: (@ide, @$scope) -> + @$scope.$watch "permissionsLevel", (permissionsLevel) => + @$scope.permissions = + read: false + write: false + admin: false + if permissionsLevel? + if permissionsLevel == "readOnly" + @$scope.permissions.read = true + else if permissionsLevel == "readAndWrite" + @$scope.permissions.read = true + @$scope.permissions.write = true + else if permissionsLevel == "owner" + @$scope.permissions.read = true + @$scope.permissions.write = true + @$scope.permissions.admin = true + diff --git a/services/web/public/coffee/app/ide/track-changes/TrackChangesManager.coffee b/services/web/public/coffee/app/ide/track-changes/TrackChangesManager.coffee index 6339eb0457..13e9c8ad0d 100644 --- a/services/web/public/coffee/app/ide/track-changes/TrackChangesManager.coffee +++ b/services/web/public/coffee/app/ide/track-changes/TrackChangesManager.coffee @@ -157,21 +157,24 @@ define [ } if entry.i? or entry.d? - name = "#{entry.meta.user.first_name} #{entry.meta.user.last_name}" - if entry.meta.user.id == @$scope.user.id + if entry.meta.user? + name = "#{entry.meta.user.first_name} #{entry.meta.user.last_name}" + else + name = "Anonymous" + if entry.meta.user?.id == @$scope.user.id name = "you" date = moment(entry.meta.end_ts).format("Do MMM YYYY, h:mm a") if entry.i? highlights.push { label: "Added by #{name} on #{date}" highlight: range - hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user.id) + hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id) } else if entry.d? highlights.push { label: "Deleted by #{name} on #{date}" strikeThrough: range - hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user.id) + hue: @ide.onlineUsersManager.getHueForUserId(entry.meta.user?.id) } return {text, highlights} diff --git a/services/web/public/stylesheets/app/editor/file-tree.less b/services/web/public/stylesheets/app/editor/file-tree.less index 55361e394f..cafdd995cd 100644 --- a/services/web/public/stylesheets/app/editor/file-tree.less +++ b/services/web/public/stylesheets/app/editor/file-tree.less @@ -9,6 +9,10 @@ aside#file-tree { left: 0; right: 0; overflow-y: auto; + + &.no-toolbar { + top: 0; + } } h3 {