From ab10336110c136590815fac6345972125d08f331 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 10 Sep 2018 11:18:15 +0100 Subject: [PATCH 1/6] Record last update time and user from project-history --- .../Features/History/HistoryController.coffee | 9 ++++ .../Features/Project/ProjectController.coffee | 7 +++- .../Project/ProjectEntityUpdateHandler.coffee | 2 - .../Project/ProjectUpdateHandler.coffee | 23 ++++------ services/web/app/coffee/models/Project.coffee | 1 + services/web/app/coffee/router.coffee | 1 + .../coffee/ProjectLastUpdatedTests.coffee | 42 +++++++++++++++++++ .../History/HistoryControllerTests.coffee | 1 + .../ProjectEntityUpdateHandlerTests.coffee | 5 --- .../Project/ProjectUpdateHandlerTests.coffee | 15 ++++--- 10 files changed, 78 insertions(+), 28 deletions(-) create mode 100644 services/web/test/acceptance/coffee/ProjectLastUpdatedTests.coffee diff --git a/services/web/app/coffee/Features/History/HistoryController.coffee b/services/web/app/coffee/Features/History/HistoryController.coffee index 189e07b0f5..8b014751f9 100644 --- a/services/web/app/coffee/Features/History/HistoryController.coffee +++ b/services/web/app/coffee/Features/History/HistoryController.coffee @@ -7,6 +7,7 @@ HistoryManager = require "./HistoryManager" ProjectDetailsHandler = require "../Project/ProjectDetailsHandler" ProjectEntityUpdateHandler = require "../Project/ProjectEntityUpdateHandler" RestoreManager = require "./RestoreManager" +ProjectUpdateHandler = require "../Project/ProjectUpdateHandler" module.exports = HistoryController = selectHistoryApi: (req, res, next = (error) ->) -> @@ -143,3 +144,11 @@ module.exports = HistoryController = error = new Error("history api responded with non-success code: #{response.statusCode}") logger.error err: error, "project-history api responded with non-success code: #{response.statusCode}" callback(error) + + setLastUpdated: (req, res, next) -> + {project_id} = req.params + {user_id, timestamp} = req.body + logger.log {project_id, user_id, timestamp}, 'updating last updated date' + ProjectUpdateHandler.markAsUpdated project_id, user_id, timestamp, (error) -> + return next(error) if error? + res.sendStatus 200 diff --git a/services/web/app/coffee/Features/Project/ProjectController.coffee b/services/web/app/coffee/Features/Project/ProjectController.coffee index 59c0647c19..ed5e219e3f 100644 --- a/services/web/app/coffee/Features/Project/ProjectController.coffee +++ b/services/web/app/coffee/Features/Project/ProjectController.coffee @@ -184,7 +184,7 @@ module.exports = ProjectController = notifications: (cb)-> NotificationsHandler.getUserNotifications user_id, cb projects: (cb)-> - ProjectGetter.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref tokens', cb + ProjectGetter.findAllUsersProjects user_id, 'name lastUpdated lastUpdatedBy publicAccesLevel archived owner_ref tokens', cb v1Projects: (cb) -> Modules.hooks.fire "findAllV1Projects", user_id, (error, projects = []) -> if error? and error instanceof V1ConnectionError @@ -392,6 +392,7 @@ module.exports = ProjectController = id: project._id name: project.name lastUpdated: project.lastUpdated + lastUpdatedBy: project.lastUpdatedBy publicAccessLevel: project.publicAccesLevel accessLevel: accessLevel source: source @@ -430,6 +431,8 @@ module.exports = ProjectController = for project in projects if project.owner_ref? users[project.owner_ref.toString()] = true + if project.lastUpdatedBy? + users[project.lastUpdatedBy] = true jobs = [] for user_id, _ of users @@ -444,6 +447,8 @@ module.exports = ProjectController = for project in projects if project.owner_ref? project.owner = users[project.owner_ref.toString()] + if project.lastUpdatedBy? + project.lastUpdatedBy = users[project.lastUpdatedBy.toString()] callback null, projects _buildWarningsList: (v1ProjectData = {}) -> diff --git a/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee index b7be80cb47..4800de05d4 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee @@ -114,8 +114,6 @@ module.exports = ProjectEntityUpdateHandler = self = logger.log {project_id, doc_id, modified}, "finished updating doc lines" # path will only be present if the doc is not deleted if modified && !isDeletedDoc - # Don't need to block for marking as updated - ProjectUpdateHandler.markAsUpdated project_id TpdsUpdateSender.addDoc {project_id:project_id, path:path.fileSystem, doc_id:doc_id, project_name:project.name, rev:rev}, callback else callback() diff --git a/services/web/app/coffee/Features/Project/ProjectUpdateHandler.coffee b/services/web/app/coffee/Features/Project/ProjectUpdateHandler.coffee index ba3b3b6295..d89d7c7e89 100644 --- a/services/web/app/coffee/Features/Project/ProjectUpdateHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectUpdateHandler.coffee @@ -2,30 +2,25 @@ Project = require('../../models/Project').Project logger = require('logger-sharelatex') module.exports = - markAsUpdated : (project_id, callback)-> + markAsUpdated : (project_id, user_id, timestamp, callback)-> conditions = {_id:project_id} - update = {lastUpdated:Date.now()} - Project.update conditions, update, {}, (err)-> - if callback? - callback() + update = { + lastUpdated: new Date(timestamp), + lastUpdatedBy: user_id + } + Project.update conditions, update, {}, callback markAsOpened : (project_id, callback)-> conditions = {_id:project_id} update = {lastOpened:Date.now()} - Project.update conditions, update, {}, (err)-> - if callback? - callback() + Project.update conditions, update, {}, callback markAsInactive: (project_id, callback)-> conditions = {_id:project_id} update = {active:false} - Project.update conditions, update, {}, (err)-> - if callback? - callback() + Project.update conditions, update, {}, callback markAsActive: (project_id, callback)-> conditions = {_id:project_id} update = {active:true} - Project.update conditions, update, {}, (err)-> - if callback? - callback() \ No newline at end of file + Project.update conditions, update, {}, callback diff --git a/services/web/app/coffee/models/Project.coffee b/services/web/app/coffee/models/Project.coffee index 00c2f1be52..f4b478d64e 100644 --- a/services/web/app/coffee/models/Project.coffee +++ b/services/web/app/coffee/models/Project.coffee @@ -19,6 +19,7 @@ DeletedFileSchema = new Schema ProjectSchema = new Schema name : {type:String, default:'new project'} lastUpdated : {type:Date, default: () -> new Date()} + lastUpdatedBy : {type:ObjectId, ref: 'User'} lastOpened : {type:Date} active : { type: Boolean, default: true } owner_ref : {type:ObjectId, ref:'User'} diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index b196c56870..2413632581 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -238,6 +238,7 @@ module.exports = class Router webRouter.post '/project/:project_id/doc/:doc_id/restore', AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreDocFromDeletedDoc webRouter.post "/project/:project_id/restore_file", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.restoreFileFromV2 privateApiRouter.post "/project/:Project_id/history/resync", AuthenticationController.httpAuth, HistoryController.resyncProjectHistory + privateApiRouter.post "/project/:project_id/last_updated", AuthenticationController.httpAuth, HistoryController.setLastUpdated webRouter.get "/project/:Project_id/labels", AuthorizationMiddlewear.ensureUserCanReadProject, HistoryController.selectHistoryApi, HistoryController.ensureProjectHistoryEnabled, HistoryController.getLabels webRouter.post "/project/:Project_id/labels", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, HistoryController.selectHistoryApi, HistoryController.ensureProjectHistoryEnabled, HistoryController.createLabel diff --git a/services/web/test/acceptance/coffee/ProjectLastUpdatedTests.coffee b/services/web/test/acceptance/coffee/ProjectLastUpdatedTests.coffee new file mode 100644 index 0000000000..5798d0236c --- /dev/null +++ b/services/web/test/acceptance/coffee/ProjectLastUpdatedTests.coffee @@ -0,0 +1,42 @@ +expect = require("chai").expect +async = require("async") +User = require "./helpers/User" +request = require "./helpers/request" +settings = require "settings-sharelatex" +Project = require("../../../app/js/models/Project").Project + +markAsUpdated = (project_id, user_id, timestamp, callback) -> + request.post { + url: "/project/#{project_id}/last_updated" + json: { + user_id, + timestamp + } + auth: + user: settings.apis.web.user + pass: settings.apis.web.pass + sendImmediately: true + jar: false + }, callback + +describe "ProjectLastUpdated", -> + before (done) -> + @timeout(90000) + @owner = new User() + @timestamp = Date.now() + @user_id = "abcdef1234567890abcdef12" + async.series [ + (cb) => @owner.login cb + (cb) => @owner.createProject "private-project", (error, @project_id) => cb(error) + ], done + + describe "with user_id and timestamp", -> + it 'should update the project', (done) -> + markAsUpdated @project_id, @user_id, @timestamp, (error, response, body) => + return done(error) if error? + expect(response.statusCode).to.equal 200 + Project.findOne _id: @project_id, (error, project) => + return done(error) if error? + expect(project.lastUpdated.getTime()).to.equal @timestamp + expect(project.lastUpdatedBy.toString()).to.equal @user_id + done() diff --git a/services/web/test/unit/coffee/History/HistoryControllerTests.coffee b/services/web/test/unit/coffee/History/HistoryControllerTests.coffee index d5777f5490..4e29bbae90 100644 --- a/services/web/test/unit/coffee/History/HistoryControllerTests.coffee +++ b/services/web/test/unit/coffee/History/HistoryControllerTests.coffee @@ -23,6 +23,7 @@ describe "HistoryController", -> "../Project/ProjectDetailsHandler": @ProjectDetailsHandler = {} "../Project/ProjectEntityUpdateHandler": @ProjectEntityUpdateHandler = {} "./RestoreManager": @RestoreManager = {} + "../Project/ProjectUpdateHandler": @ProjectUpdateHandler = {} @settings.apis = trackchanges: enabled: false diff --git a/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee index 3de4546e6d..1b4739000f 100644 --- a/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee @@ -190,11 +190,6 @@ describe 'ProjectEntityUpdateHandler', -> .calledWith(project_id, doc_id, @docLines, @version, @ranges) .should.equal true - it "should mark the project as updated", -> - @ProjectUpdater.markAsUpdated - .calledWith(project_id) - .should.equal true - it "should send the doc the to the TPDS", -> @TpdsUpdateSender.addDoc .calledWith({ diff --git a/services/web/test/unit/coffee/Project/ProjectUpdateHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectUpdateHandlerTests.coffee index a68a9be1f1..da719c91f2 100644 --- a/services/web/test/unit/coffee/Project/ProjectUpdateHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectUpdateHandlerTests.coffee @@ -16,12 +16,15 @@ describe 'ProjectUpdateHandler', -> describe 'marking a project as recently updated', -> it 'should send an update to mongo', (done)-> project_id = "project_id" - @handler.markAsUpdated project_id, (err)=> - args = @ProjectModel.update.args[0] - args[0]._id.should.equal project_id - date = args[1].lastUpdated+"" - now = Date.now()+"" - date.substring(0,5).should.equal now.substring(0,5) + user_id = "mock_user_id" + timestamp = Date.now() + @handler.markAsUpdated project_id, user_id, timestamp, (err)=> + @ProjectModel.update.calledWith({ + _id: project_id, + }, { + lastUpdated: new Date(timestamp), + lastUpdatedBy: user_id + }).should.equal true done() describe "markAsOpened", -> From c0729611832aa86550f370b0f3deeb20d89dd137 Mon Sep 17 00:00:00 2001 From: James Allen Date: Mon, 10 Sep 2018 11:18:44 +0100 Subject: [PATCH 2/6] Update project-list list to a table and show last updated user --- services/web/app/views/project/list/item.pug | 149 ++++++++---------- .../app/views/project/list/project-list.pug | 82 +++++----- .../web/app/views/project/list/v1-item.pug | 2 +- .../main/project-list/project-list.coffee | 8 +- .../public/stylesheets/app/project-list.less | 37 ++++- .../public/stylesheets/components/tables.less | 2 +- .../stylesheets/core/_common-variables.less | 2 +- 7 files changed, 144 insertions(+), 138 deletions(-) diff --git a/services/web/app/views/project/list/item.pug b/services/web/app/views/project/list/item.pug index deb9863d04..2e80810be4 100644 --- a/services/web/app/views/project/list/item.pug +++ b/services/web/app/views/project/list/item.pug @@ -1,7 +1,4 @@ -- var titleClasses = settings.overleaf ? "col-xs-6 col-sm-4 col-md-6" : "col-xs-6" -- var lastUpdatedClasses = settings.overleaf ? " col-xs-4 col-sm-3 col-md-2" : "col-xs-4" - -div(class=titleClasses) +td.selectProject input.select-item( select-individual, type="checkbox", @@ -10,29 +7,29 @@ div(class=titleClasses) stop-propagation="click" aria-label=translate('select_project') + " '{{ project.name }}'" ) - span - a.projectName( - ng-href="{{projectLink(project)}}" +td.projectName + a.projectName( + ng-href="{{projectLink(project)}}" + stop-propagation="click" + ) {{project.name}} + span( + ng-controller="TagListController" + ) + .tag-label( + ng-repeat='tag in project.tags' stop-propagation="click" - ) {{project.name}} - span( - ng-controller="TagListController" ) - .tag-label( - ng-repeat='tag in project.tags' - stop-propagation="click" - ) - a.label.label-default.tag-label-name( - href, - ng-click="selectTag(tag)" - ) {{tag.name}} - a.label.label-default.tag-label-remove( - href - ng-click="removeProjectFromTag(project, tag)" - ) × + a.label.label-default.tag-label-name( + href, + ng-click="selectTag(tag)" + ) {{tag.name}} + a.label.label-default.tag-label-remove( + href + ng-click="removeProjectFromTag(project, tag)" + ) × -.col-xs-2 - span.owner {{ownerName()}} +td + span.owner {{userDisplayName(project.owner)}} span(ng-if="isLinkSharingProject(project)") |   i.fa.fa-link.small( @@ -41,64 +38,52 @@ div(class=titleClasses) tooltip-append-to-body="true" ) -div(class=lastUpdatedClasses) - if settings.overleaf - span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") {{project.lastUpdated | fromNowDate}} - else - span.last-modified {{project.lastUpdated | formatDate}} +td + span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") + | {{project.lastUpdated | fromNowDate}} + span(ng-show='project.lastUpdatedBy') + | + | by {{userDisplayName(project.lastUpdatedBy)}} -if settings.overleaf - .hidden-xs.col-sm-3.col-md-2.action-btn-row - div( - ng-if="!project.isTableActionInflight" +td.text-right + div( + ng-if="!project.isTableActionInflight" + ) + button.btn.btn-link.action-btn( + tooltip=translate('copy'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="clone($event)" ) - button.btn.btn-link.action-btn( - tooltip=translate('copy'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="clone($event)" - ) - i.icon.fa.fa-files-o - button.btn.btn-link.action-btn( - tooltip=translate('download'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="download($event)" - ) - i.icon.fa.fa-cloud-download - button.btn.btn-link.action-btn( - ng-if="!project.archived && isOwner()" - tooltip=translate('archive'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="archiveOrLeave($event)" - ) - i.icon.fa.fa-inbox - button.btn.btn-link.action-btn( - ng-if="!project.archived && !isOwner()" - tooltip=translate('leave'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="archiveOrLeave($event)" - ) - i.icon.fa.fa-sign-out - button.btn.btn-link.action-btn( - ng-if="project.archived" - tooltip=translate('unarchive'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="restore($event)" - ) - i.icon.fa.fa-reply - button.btn.btn-link.action-btn( - ng-if="project.archived && isOwner()" - tooltip=translate('delete_forever'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="deleteProject($event)" - ) - i.icon.fa.fa-trash - div( - ng-if="project.isTableActionInflight" + i.icon.fa.fa-files-o + button.btn.btn-link.action-btn( + tooltip=translate('download'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="download($event)" ) - i.fa.fa-spinner.fa-spin \ No newline at end of file + i.icon.fa.fa-cloud-download + button.btn.btn-link.action-btn( + ng-if="!project.archived && isOwner()" + tooltip=translate('archive'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="archiveOrLeave($event)" + ) + i.icon.fa.fa-inbox + button.btn.btn-link.action-btn( + ng-if="!project.archived && !isOwner()" + tooltip=translate('leave'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="archiveOrLeave($event)" + ) + i.icon.fa.fa-sign-out + button.btn.btn-link.action-btn( + ng-if="project.archived" + tooltip=translate('unarchive'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="restore($event)" + ) + i.icon.fa.fa-reply diff --git a/services/web/app/views/project/list/project-list.pug b/services/web/app/views/project/list/project-list.pug index cfea4aa6c6..af3a569f3b 100644 --- a/services/web/app/views/project/list/project-list.pug +++ b/services/web/app/views/project/list/project-list.pug @@ -123,54 +123,44 @@ .col-xs-12 .card.card-thin.project-list-card - ul.list-unstyled.project-list.structured-list( - select-all-list, - ng-if="projects.length > 0", - max-height="projectListHeight - 25", - ng-cloak - ) - li.container-fluid - .row - - var titleClasses = settings.overleaf ? " col-xs-6 col-sm-4 col-md-6" : "col-xs-6" - - var lastUpdatedClasses = settings.overleaf ? " col-xs-4 col-sm-3 col-md-2" : "col-xs-4" - - div(class=titleClasses) - input.select-all( - select-all, - type="checkbox" - aria-label=translate('select_all_projects') - ) - span.header.clickable(ng-click="changePredicate('name')") #{translate("title")} - i.tablesort.fa(ng-class="getSortIconClass('name')") - .col-xs-2 - span.header.clickable(ng-click="changePredicate('accessLevel')") #{translate("owner")} - i.tablesort.fa(ng-class="getSortIconClass('accessLevel')") - div(class=lastUpdatedClasses) - span.header.clickable(ng-click="changePredicate('lastUpdated')") #{translate("last_modified")} - i.tablesort.fa(ng-class="getSortIconClass('lastUpdated')") - if settings.overleaf - .hidden-xs.col-sm-3.col-md-2.action-btn-row-header - span.header #{translate("actions")} - li.project_entry.container-fluid( - ng-repeat="project in visibleProjects | orderBy:predicate:reverse", - ng-controller="ProjectListItemController" - ) - .row( - ng-if="!project.isV1Project" - select-row - ) - include ./item - .row( - ng-if="project.isV1Project" - ) - include ./v1-item - li( - ng-if="visibleProjects.length == 0", + .table-wrapper(max-height="projectListHeight - 25",) + table.table.table-hover.project-list( + select-all-list, + ng-if="projects.length > 0", ng-cloak ) - .row - .col-xs-12.text-centered - small #{translate("no_projects")} + thead + tr + th.selectProject + input.select-all( + select-all, + type="checkbox" + aria-label=translate('select_all_projects') + ) + th.projectName + span.header.clickable(ng-click="changePredicate('name')") #{translate("title")} + i.tablesort.fa(ng-class="getSortIconClass('name')") + th + span.header.clickable(ng-click="changePredicate('accessLevel')") #{translate("owner")} + i.tablesort.fa(ng-class="getSortIconClass('accessLevel')") + th + span.header.clickable(ng-click="changePredicate('lastUpdated')") #{translate("last_modified")} + i.tablesort.fa(ng-class="getSortIconClass('lastUpdated')") + if settings.overleaf + th.text-right + span.header #{translate("actions")} + tbody + tr.project_entry( + ng-repeat="project in visibleProjects | orderBy:predicate:reverse", + ng-controller="ProjectListItemController" + ) + include ./item + tr( + ng-if="visibleProjects.length == 0", + ng-cloak + ) + td(colspan=5).text-center + small #{translate("no_projects")} div.welcome.text-centered(ng-if="projects.length == 0", ng-cloak) h2 #{translate("welcome_to_sl")} diff --git a/services/web/app/views/project/list/v1-item.pug b/services/web/app/views/project/list/v1-item.pug index 5a8e37bca0..bf8d7db9e9 100644 --- a/services/web/app/views/project/list/v1-item.pug +++ b/services/web/app/views/project/list/v1-item.pug @@ -19,7 +19,7 @@ ) {{project.name}} .col-xs-2 - span.owner {{ownerName()}} + span.owner {{userDisplayName(project.owner)}} .col-xs-4.col-sm-3.col-md-2 span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") {{project.lastUpdated | fromNowDate}} \ No newline at end of file diff --git a/services/web/public/coffee/main/project-list/project-list.coffee b/services/web/public/coffee/main/project-list/project-list.coffee index 9a8d7f7a73..a6a4d48ef1 100644 --- a/services/web/public/coffee/main/project-list/project-list.coffee +++ b/services/web/public/coffee/main/project-list/project-list.coffee @@ -485,11 +485,11 @@ define [ $scope.isLinkSharingProject = (project) -> return project.source == 'token' - $scope.ownerName = () -> - if $scope.project.accessLevel == "owner" + $scope.userDisplayName = (user) -> + if user._id == window.user_id return "You" - else if $scope.project.owner? - return [$scope.project.owner.first_name, $scope.project.owner.last_name].filter((n) -> n?).join(" ") + else if user? + return [user.first_name, user.last_name].filter((n) -> n?).join(" ") else return "None" diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index e5bc8da14c..34d3e25c75 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -314,8 +314,38 @@ ul.structured-list { padding: 0 (@line-height-computed / 4); } -ul.project-list { - li { +.table-wrapper { + overflow: scroll; +} + +table.project-list { + margin: 0; + width: 100%; + + th when (@is-overleaf = true) { + font-weight: 600; + } + th when (@is-overleaf = false) { + text-transform: uppercase; + font-weight: normal; + } + + // thead > tr > th { + // padding-top: @line-height-computed / 8; + // } + + td { + @media (min-width: @screen-md-min) { + white-space: nowrap; + } + &.projectName { + white-space: normal; + width: 50%; + } + &.selectProject { + width: 1%; + } + .last-modified when (@is-overleaf = false) { font-size: .8rem; } @@ -325,12 +355,13 @@ ul.project-list { .owner when (@is-overleaf = false) { margin-right: 0; } - .projectName { + a.projectName { margin-right: @line-height-computed / 4; padding: 0; vertical-align: inherit; white-space: normal; text-align: left; + color: @structured-list-link-color; } .tag-label { diff --git a/services/web/public/stylesheets/components/tables.less b/services/web/public/stylesheets/components/tables.less index c41989c04d..30ede6697c 100755 --- a/services/web/public/stylesheets/components/tables.less +++ b/services/web/public/stylesheets/components/tables.less @@ -34,7 +34,7 @@ th { // Bottom align for column headings > thead > tr > th { vertical-align: bottom; - border-bottom: 2px solid @table-border-color; + border-bottom: 1px solid @table-border-color; } // Remove top border from thead by default > caption + thead, diff --git a/services/web/public/stylesheets/core/_common-variables.less b/services/web/public/stylesheets/core/_common-variables.less index 9d7eab3493..41bdbd2d9b 100644 --- a/services/web/public/stylesheets/core/_common-variables.less +++ b/services/web/public/stylesheets/core/_common-variables.less @@ -110,7 +110,7 @@ //## Customizes the `.table` component with basic values, each used across all table variations. //** Padding for ``s and ``s. -@table-cell-padding: 8px; +@table-cell-padding: @line-height-computed / 4; //** Padding for cells in `.table-condensed`. @table-condensed-cell-padding: 5px; From 225d30ffd202b31a2ae092670526191f4c47c78f Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Sep 2018 10:26:59 +0100 Subject: [PATCH 3/6] Add missed lines from rebase --- services/web/app/views/project/list/item.pug | 174 ++++----- .../app/views/project/list/project-list.pug | 330 +++++++++--------- .../web/app/views/project/list/v1-item.pug | 48 +-- 3 files changed, 288 insertions(+), 264 deletions(-) diff --git a/services/web/app/views/project/list/item.pug b/services/web/app/views/project/list/item.pug index 2e80810be4..379f939915 100644 --- a/services/web/app/views/project/list/item.pug +++ b/services/web/app/views/project/list/item.pug @@ -1,89 +1,101 @@ td.selectProject - input.select-item( - select-individual, - type="checkbox", - ng-disabled="shouldDisableCheckbox(project)", - ng-model="project.selected" - stop-propagation="click" - aria-label=translate('select_project') + " '{{ project.name }}'" - ) + input.select-item( + select-individual, + type="checkbox", + ng-disabled="shouldDisableCheckbox(project)", + ng-model="project.selected" + stop-propagation="click" + aria-label=translate('select_project') + " '{{ project.name }}'" + ) td.projectName - a.projectName( - ng-href="{{projectLink(project)}}" - stop-propagation="click" - ) {{project.name}} - span( - ng-controller="TagListController" - ) - .tag-label( - ng-repeat='tag in project.tags' - stop-propagation="click" - ) - a.label.label-default.tag-label-name( - href, - ng-click="selectTag(tag)" - ) {{tag.name}} - a.label.label-default.tag-label-remove( - href - ng-click="removeProjectFromTag(project, tag)" - ) × + a.projectName( + ng-href="{{projectLink(project)}}" + stop-propagation="click" + ) {{project.name}} + span( + ng-controller="TagListController" + ) + .tag-label( + ng-repeat='tag in project.tags' + stop-propagation="click" + ) + a.label.label-default.tag-label-name( + href, + ng-click="selectTag(tag)" + ) {{tag.name}} + a.label.label-default.tag-label-remove( + href + ng-click="removeProjectFromTag(project, tag)" + ) × td - span.owner {{userDisplayName(project.owner)}} - span(ng-if="isLinkSharingProject(project)") - |   - i.fa.fa-link.small( - tooltip=translate("link_sharing") - tooltip-placement="right" - tooltip-append-to-body="true" - ) + span.owner {{userDisplayName(project.owner)}} + span(ng-if="isLinkSharingProject(project)") + |   + i.fa.fa-link.small( + tooltip=translate("link_sharing") + tooltip-placement="right" + tooltip-append-to-body="true" + ) td - span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") - | {{project.lastUpdated | fromNowDate}} - span(ng-show='project.lastUpdatedBy') - | - | by {{userDisplayName(project.lastUpdatedBy)}} + span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") + | {{project.lastUpdated | fromNowDate}} + span(ng-show='project.lastUpdatedBy') + | + | by {{userDisplayName(project.lastUpdatedBy)}} td.text-right - div( - ng-if="!project.isTableActionInflight" - ) - button.btn.btn-link.action-btn( - tooltip=translate('copy'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="clone($event)" - ) - i.icon.fa.fa-files-o - button.btn.btn-link.action-btn( - tooltip=translate('download'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="download($event)" - ) - i.icon.fa.fa-cloud-download - button.btn.btn-link.action-btn( - ng-if="!project.archived && isOwner()" - tooltip=translate('archive'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="archiveOrLeave($event)" - ) - i.icon.fa.fa-inbox - button.btn.btn-link.action-btn( - ng-if="!project.archived && !isOwner()" - tooltip=translate('leave'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="archiveOrLeave($event)" - ) - i.icon.fa.fa-sign-out - button.btn.btn-link.action-btn( - ng-if="project.archived" - tooltip=translate('unarchive'), - tooltip-placement="top", - tooltip-append-to-body="true", - ng-click="restore($event)" - ) - i.icon.fa.fa-reply + div( + ng-if="!project.isTableActionInflight" + ) + button.btn.btn-link.action-btn( + tooltip=translate('copy'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="clone($event)" + ) + i.icon.fa.fa-files-o + button.btn.btn-link.action-btn( + tooltip=translate('download'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="download($event)" + ) + i.icon.fa.fa-cloud-download + button.btn.btn-link.action-btn( + ng-if="!project.archived && isOwner()" + tooltip=translate('archive'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="archiveOrLeave($event)" + ) + i.icon.fa.fa-inbox + button.btn.btn-link.action-btn( + ng-if="!project.archived && !isOwner()" + tooltip=translate('leave'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="archiveOrLeave($event)" + ) + i.icon.fa.fa-sign-out + button.btn.btn-link.action-btn( + ng-if="project.archived" + tooltip=translate('unarchive'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="restore($event)" + ) + i.icon.fa.fa-reply + button.btn.btn-link.action-btn( + ng-if="project.archived && isOwner()" + tooltip=translate('delete_forever'), + tooltip-placement="top", + tooltip-append-to-body="true", + ng-click="deleteProject($event)" + ) + i.icon.fa.fa-trash + div( + ng-if="project.isTableActionInflight" + ) + i.fa.fa-spinner.fa-spin diff --git a/services/web/app/views/project/list/project-list.pug b/services/web/app/views/project/list/project-list.pug index af3a569f3b..e261795fea 100644 --- a/services/web/app/views/project/list/project-list.pug +++ b/services/web/app/views/project/list/project-list.pug @@ -1,175 +1,183 @@ .row - .col-xs-12(ng-cloak) + .col-xs-12(ng-cloak) - form.project-search.form-horizontal(role="form") - .form-group.has-feedback.has-feedback-left.col-md-7.col-xs-12 - input.form-control.col-md-7.col-xs-12( - placeholder=translate('search_projects')+"…", - aria-label=translate('search_projects')+"…", - autofocus='autofocus', - ng-model="searchText.value", - focus-on='search:clear', - ng-keyup="searchProjects()" - ) - i.fa.fa-search.form-control-feedback-left - i.fa.fa-times.form-control-feedback( - ng-click="clearSearchText()", - style="cursor: pointer;", - ng-show="searchText.value.length > 0" - ) - //- i.fa.fa-remove + form.project-search.form-horizontal(role="form") + .form-group.has-feedback.has-feedback-left.col-md-7.col-xs-12 + input.form-control.col-md-7.col-xs-12( + placeholder=translate('search_projects')+"…", + aria-label=translate('search_projects')+"…", + autofocus='autofocus', + ng-model="searchText.value", + focus-on='search:clear', + ng-keyup="searchProjects()" + ) + i.fa.fa-search.form-control-feedback-left + i.fa.fa-times.form-control-feedback( + ng-click="clearSearchText()", + style="cursor: pointer;", + ng-show="searchText.value.length > 0" + ) + //- i.fa.fa-remove - .project-tools(ng-cloak) - .btn-toolbar(ng-show="filter != 'archived'") - .btn-group(ng-hide="selectedProjects.length < 1") - a.btn.btn-default( - href, - tooltip=translate('download'), - tooltip-placement="bottom", - tooltip-append-to-body="true", - ng-click="downloadSelectedProjects()" - ) - i.fa.fa-cloud-download - - var archiveButtonString = settings.overleaf ? translate("archive") : translate("delete") - - var archiveButtonIcon = settings.overleaf ? "fa-inbox" : "fa-trash-o" - a.btn.btn-default( - href, - tooltip=`{{ isArchiveableProjectSelected ? '${archiveButtonString}' : '${translate("leave")}' }}`, - tooltip-placement="bottom", - tooltip-append-to-body="true", - ng-click="openArchiveProjectsModal()" - ) - i.fa(ng-class=`isArchiveableProjectSelected ? '${archiveButtonIcon}' : 'fa-sign-out'`) + .project-tools(ng-cloak) + .btn-toolbar(ng-show="filter != 'archived'") + .btn-group(ng-hide="selectedProjects.length < 1") + a.btn.btn-default( + href, + tooltip=translate('download'), + tooltip-placement="bottom", + tooltip-append-to-body="true", + ng-click="downloadSelectedProjects()" + ) + i.fa.fa-cloud-download + - var archiveButtonString = settings.overleaf ? translate("archive") : translate("delete") + - var archiveButtonIcon = settings.overleaf ? "fa-inbox" : "fa-trash-o" + a.btn.btn-default( + href, + tooltip=`{{ isArchiveableProjectSelected ? '${archiveButtonString}' : '${translate("leave")}' }}`, + tooltip-placement="bottom", + tooltip-append-to-body="true", + ng-click="openArchiveProjectsModal()" + ) + i.fa(ng-class=`isArchiveableProjectSelected ? '${archiveButtonIcon}' : 'fa-sign-out'`) - .btn-group.dropdown(ng-hide="selectedProjects.length < 1", dropdown) - a.btn.btn-default.dropdown-toggle( - href, - data-toggle="dropdown", - dropdown-toggle, - tooltip=translate('add_to_folders'), - tooltip-append-to-body="true", - tooltip-placement="bottom" - ) - i.fa.fa-folder-open-o - | - span.caret - ul.dropdown-menu.dropdown-menu-right.js-tags-dropdown-menu.tags-dropdown-menu( - role="menu" - ng-controller="TagListController" - ) - li.dropdown-header #{translate("add_to_folder")} - li( - ng-repeat="tag in tags | orderBy:'name'", - ng-controller="TagDropdownItemController" - ng-if="!tag.isV1" - ) - a(href="#", ng-click="addOrRemoveProjectsFromTag()", stop-propagation="click") - i.fa( - ng-class="{\ - 'fa-check-square-o': areSelectedProjectsInTag == true,\ - 'fa-square-o': areSelectedProjectsInTag == false,\ - 'fa-minus-square-o': areSelectedProjectsInTag == 'partial'\ - }" - ) - | {{tag.name}} - li.divider - li - a(href, ng-click="openNewTagModal()", stop-propagation="click") #{translate("create_new_folder")} + .btn-group.dropdown(ng-hide="selectedProjects.length < 1", dropdown) + a.btn.btn-default.dropdown-toggle( + href, + data-toggle="dropdown", + dropdown-toggle, + tooltip=translate('add_to_folders'), + tooltip-append-to-body="true", + tooltip-placement="bottom" + ) + i.fa.fa-folder-open-o + | + span.caret + ul.dropdown-menu.dropdown-menu-right.js-tags-dropdown-menu.tags-dropdown-menu( + role="menu" + ng-controller="TagListController" + ) + li.dropdown-header #{translate("add_to_folder")} + li( + ng-repeat="tag in tags | orderBy:'name'", + ng-controller="TagDropdownItemController" + ng-if="!tag.isV1" + ) + a(href="#", ng-click="addOrRemoveProjectsFromTag()", stop-propagation="click") + i.fa( + ng-class="{\ + 'fa-check-square-o': areSelectedProjectsInTag == true,\ + 'fa-square-o': areSelectedProjectsInTag == false,\ + 'fa-minus-square-o': areSelectedProjectsInTag == 'partial'\ + }" + ) + | {{tag.name}} + li.divider + li + a(href, ng-click="openNewTagModal()", stop-propagation="click") #{translate("create_new_folder")} - .btn-group(ng-hide="selectedProjects.length != 1", dropdown).dropdown - a.btn.btn-default.dropdown-toggle( - href, - data-toggle="dropdown", - dropdown-toggle - ) #{translate("more")} - span.caret - ul.dropdown-menu.dropdown-menu-right(role="menu") - li(ng-show="getFirstSelectedProject().accessLevel == 'owner'") - a( - href, - ng-click="openRenameProjectModal()" - ) #{translate("rename")} - li - a( - href, - ng-click="openCloneProjectModal()" - ) #{translate("make_copy")} + .btn-group(ng-hide="selectedProjects.length != 1", dropdown).dropdown + a.btn.btn-default.dropdown-toggle( + href, + data-toggle="dropdown", + dropdown-toggle + ) #{translate("more")} + span.caret + ul.dropdown-menu.dropdown-menu-right(role="menu") + li(ng-show="getFirstSelectedProject().accessLevel == 'owner'") + a( + href, + ng-click="openRenameProjectModal()" + ) #{translate("rename")} + li + a( + href, + ng-click="openCloneProjectModal()" + ) #{translate("make_copy")} - .btn-toolbar(ng-show="filter == 'archived'") - .btn-group(ng-hide="selectedProjects.length < 1") - a.btn.btn-default( - href, - data-original-title="Restore", - data-toggle="tooltip", - data-placement="bottom", - ng-click="restoreSelectedProjects()" - ) #{translate("restore")} + .btn-toolbar(ng-show="filter == 'archived'") + .btn-group(ng-hide="selectedProjects.length < 1") + a.btn.btn-default( + href, + data-original-title="Restore", + data-toggle="tooltip", + data-placement="bottom", + ng-click="restoreSelectedProjects()" + ) #{translate("restore")} - .btn-group(ng-hide="selectedProjects.length < 1") - a.btn.btn-danger( - href, - data-original-title="Delete Forever", - data-toggle="tooltip", - data-placement="bottom", - ng-click="openDeleteProjectsModal()" - ) #{translate("delete_forever")} + .btn-group(ng-hide="selectedProjects.length < 1") + a.btn.btn-danger( + href, + data-original-title="Delete Forever", + data-toggle="tooltip", + data-placement="bottom", + ng-click="openDeleteProjectsModal()" + ) #{translate("delete_forever")} .row.row-spaced - each warning in warnings - .col-xs-12 - .alert.alert-warning(role="alert")= warning + each warning in warnings + .col-xs-12 + .alert.alert-warning(role="alert")= warning - .col-xs-12 - .card.card-thin.project-list-card - .table-wrapper(max-height="projectListHeight - 25",) - table.table.table-hover.project-list( - select-all-list, - ng-if="projects.length > 0", - ng-cloak - ) - thead - tr - th.selectProject - input.select-all( - select-all, - type="checkbox" - aria-label=translate('select_all_projects') - ) - th.projectName - span.header.clickable(ng-click="changePredicate('name')") #{translate("title")} - i.tablesort.fa(ng-class="getSortIconClass('name')") - th - span.header.clickable(ng-click="changePredicate('accessLevel')") #{translate("owner")} - i.tablesort.fa(ng-class="getSortIconClass('accessLevel')") - th - span.header.clickable(ng-click="changePredicate('lastUpdated')") #{translate("last_modified")} - i.tablesort.fa(ng-class="getSortIconClass('lastUpdated')") - if settings.overleaf - th.text-right - span.header #{translate("actions")} - tbody - tr.project_entry( - ng-repeat="project in visibleProjects | orderBy:predicate:reverse", - ng-controller="ProjectListItemController" - ) - include ./item - tr( - ng-if="visibleProjects.length == 0", - ng-cloak - ) - td(colspan=5).text-center - small #{translate("no_projects")} + .col-xs-12 + .card.card-thin.project-list-card + .table-wrapper(max-height="projectListHeight - 25",) + table.table.table-hover.project-list( + select-all-list, + ng-if="projects.length > 0", + ng-cloak + ) + thead + tr + th.selectProject + input.select-all( + select-all, + type="checkbox" + aria-label=translate('select_all_projects') + ) + th.projectName + span.header.clickable(ng-click="changePredicate('name')") #{translate("title")} + i.tablesort.fa(ng-class="getSortIconClass('name')") + th + span.header.clickable(ng-click="changePredicate('accessLevel')") #{translate("owner")} + i.tablesort.fa(ng-class="getSortIconClass('accessLevel')") + th + span.header.clickable(ng-click="changePredicate('lastUpdated')") #{translate("last_modified")} + i.tablesort.fa(ng-class="getSortIconClass('lastUpdated')") + if settings.overleaf + th.text-right + span.header #{translate("actions")} + tbody + tr.project_entry( + ng-repeat="project in visibleProjects | orderBy:predicate:reverse", + ng-controller="ProjectListItemController" + ) + .row( + ng-if="!project.isV1Project" + select-row + ) + include ./item + .row( + ng-if="project.isV1Project" + ) + include ./v1-item + tr( + ng-if="visibleProjects.length == 0", + ng-cloak + ) + td(colspan=5).text-center + small #{translate("no_projects")} + + div.welcome.text-centered(ng-if="projects.length == 0", ng-cloak) + h2 #{translate("welcome_to_sl")} + p #{translate("new_to_latex_look_at")} + a(href="/templates") #{translate("templates").toLowerCase()} + | #{translate("or")} + a(href="/learn") #{translate("latex_help_guide")} + | , + br + | #{translate("or_create_project_left")} - div.welcome.text-centered(ng-if="projects.length == 0", ng-cloak) - h2 #{translate("welcome_to_sl")} - p #{translate("new_to_latex_look_at")} - a(href="/templates") #{translate("templates").toLowerCase()} - | #{translate("or")} - a(href="/learn") #{translate("latex_help_guide")} - | , - br - | #{translate("or_create_project_left")} - diff --git a/services/web/app/views/project/list/v1-item.pug b/services/web/app/views/project/list/v1-item.pug index bf8d7db9e9..a74c48980f 100644 --- a/services/web/app/views/project/list/v1-item.pug +++ b/services/web/app/views/project/list/v1-item.pug @@ -1,25 +1,29 @@ -.col-xs-6.col-sm-4.col-md-6 - .select-item - span.v1-badge( - aria-label=translate("v1_badge") - tooltip-template="'v1ProjectTooltipTemplate'" - tooltip-append-to-body="true" - ) - span - if settings.overleaf && settings.overleaf.host - button.btn.btn-link.projectName( - ng-click="openV1ImportModal(project)" - stop-propagation="click" - ng-show="project.accessLevel == 'owner'" - ) {{project.name}} - a.projectName( - href=settings.overleaf.host + "/{{project.id}}" - target="_blank" - ng-hide="project.accessLevel == 'owner'" - ) {{project.name}} +td -.col-xs-2 +td.selectProject + span.v1-badge( + aria-label=translate("v1_badge") + tooltip-template="'v1ProjectTooltipTemplate'" + tooltip-append-to-body="true" + ) + +td.projectName + if settings.overleaf && settings.overleaf.host + button.btn.btn-link.projectName( + ng-click="openV1ImportModal(project)" + stop-propagation="click" + ng-show="project.accessLevel == 'owner'" + ) {{project.name}} + a.projectName( + href=settings.overleaf.host + "/{{project.id}}" + target="_blank" + ng-hide="project.accessLevel == 'owner'" + ) {{project.name}} + +td span.owner {{userDisplayName(project.owner)}} -.col-xs-4.col-sm-3.col-md-2 - span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") {{project.lastUpdated | fromNowDate}} \ No newline at end of file +td + span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") {{project.lastUpdated | fromNowDate}} + +td From dd4b85b809051c20e67ed03f7d78c221dc127861 Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Sep 2018 11:12:46 +0100 Subject: [PATCH 4/6] Combine v1 and v2 projects into item.pug --- services/web/app/views/project/list/item.pug | 58 +++++++++++++------ .../app/views/project/list/project-list.pug | 10 +--- .../web/app/views/project/list/v1-item.pug | 29 ---------- .../main/project-list/project-list.coffee | 16 ++--- .../public/stylesheets/app/project-list.less | 11 ++-- 5 files changed, 54 insertions(+), 70 deletions(-) delete mode 100644 services/web/app/views/project/list/v1-item.pug diff --git a/services/web/app/views/project/list/item.pug b/services/web/app/views/project/list/item.pug index 379f939915..0ab99893da 100644 --- a/services/web/app/views/project/list/item.pug +++ b/services/web/app/views/project/list/item.pug @@ -1,5 +1,6 @@ td.selectProject input.select-item( + ng-if="!project.isV1Project", select-individual, type="checkbox", ng-disabled="shouldDisableCheckbox(project)", @@ -7,26 +8,45 @@ td.selectProject stop-propagation="click" aria-label=translate('select_project') + " '{{ project.name }}'" ) -td.projectName - a.projectName( - ng-href="{{projectLink(project)}}" - stop-propagation="click" - ) {{project.name}} - span( - ng-controller="TagListController" + span.v1-badge( + ng-if="project.isV1Project", + aria-label=translate("v1_badge") + tooltip-template="'v1ProjectTooltipTemplate'" + tooltip-append-to-body="true" ) - .tag-label( - ng-repeat='tag in project.tags' +td.projectName + span(ng-if="project.isV1Project") + if settings.overleaf && settings.overleaf.host + button.btn.btn-link.projectName( + ng-click="openV1ImportModal(project)" + stop-propagation="click" + ng-show="project.accessLevel == 'owner'" + ) {{project.name}} + a.projectName( + href=settings.overleaf.host + "/{{project.id}}" + target="_blank" + ng-hide="project.accessLevel == 'owner'" + ) {{project.name}} + span(ng-if="!project.isV1Project") + a.projectName( + ng-href="{{projectLink(project)}}" stop-propagation="click" + ) {{project.name}} + span( + ng-controller="TagListController" ) - a.label.label-default.tag-label-name( - href, - ng-click="selectTag(tag)" - ) {{tag.name}} - a.label.label-default.tag-label-remove( - href - ng-click="removeProjectFromTag(project, tag)" - ) × + .tag-label( + ng-repeat='tag in project.tags' + stop-propagation="click" + ) + a.label.label-default.tag-label-name( + href, + ng-click="selectTag(tag)" + ) {{tag.name}} + a.label.label-default.tag-label-remove( + href + ng-click="removeProjectFromTag(project, tag)" + ) × td span.owner {{userDisplayName(project.owner)}} @@ -41,13 +61,13 @@ td td span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") | {{project.lastUpdated | fromNowDate}} - span(ng-show='project.lastUpdatedBy') + span(ng-if='project.lastUpdatedBy') | | by {{userDisplayName(project.lastUpdatedBy)}} td.text-right div( - ng-if="!project.isTableActionInflight" + ng-if="!project.isTableActionInflight && !project.isV1Project" ) button.btn.btn-link.action-btn( tooltip=translate('copy'), diff --git a/services/web/app/views/project/list/project-list.pug b/services/web/app/views/project/list/project-list.pug index e261795fea..a6971aa49a 100644 --- a/services/web/app/views/project/list/project-list.pug +++ b/services/web/app/views/project/list/project-list.pug @@ -154,15 +154,7 @@ ng-repeat="project in visibleProjects | orderBy:predicate:reverse", ng-controller="ProjectListItemController" ) - .row( - ng-if="!project.isV1Project" - select-row - ) - include ./item - .row( - ng-if="project.isV1Project" - ) - include ./v1-item + include ./item tr( ng-if="visibleProjects.length == 0", ng-cloak diff --git a/services/web/app/views/project/list/v1-item.pug b/services/web/app/views/project/list/v1-item.pug deleted file mode 100644 index a74c48980f..0000000000 --- a/services/web/app/views/project/list/v1-item.pug +++ /dev/null @@ -1,29 +0,0 @@ -td - -td.selectProject - span.v1-badge( - aria-label=translate("v1_badge") - tooltip-template="'v1ProjectTooltipTemplate'" - tooltip-append-to-body="true" - ) - -td.projectName - if settings.overleaf && settings.overleaf.host - button.btn.btn-link.projectName( - ng-click="openV1ImportModal(project)" - stop-propagation="click" - ng-show="project.accessLevel == 'owner'" - ) {{project.name}} - a.projectName( - href=settings.overleaf.host + "/{{project.id}}" - target="_blank" - ng-hide="project.accessLevel == 'owner'" - ) {{project.name}} - -td - span.owner {{userDisplayName(project.owner)}} - -td - span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") {{project.lastUpdated | fromNowDate}} - -td diff --git a/services/web/public/coffee/main/project-list/project-list.coffee b/services/web/public/coffee/main/project-list/project-list.coffee index a6a4d48ef1..0146ff3348 100644 --- a/services/web/public/coffee/main/project-list/project-list.coffee +++ b/services/web/public/coffee/main/project-list/project-list.coffee @@ -13,7 +13,7 @@ define [ $scope.predicate = "lastUpdated" $scope.nUntagged = 0 $scope.reverse = true - $scope.searchText = + $scope.searchText = value : "" $timeout () -> @@ -37,7 +37,7 @@ define [ angular.element($window).bind "resize", () -> recalculateProjectListHeight() $scope.$apply() - + # Allow tags to be accessed on projects as well projectsById = {} for project in $scope.projects @@ -56,7 +56,7 @@ define [ tag.selected = true else tag.selected = false - + $scope.changePredicate = (newPredicate)-> if $scope.predicate == newPredicate $scope.reverse = !$scope.reverse @@ -145,7 +145,7 @@ define [ # We don't want hidden selections project.selected = false - localStorage("project_list", JSON.stringify({ + localStorage("project_list", JSON.stringify({ filter: $scope.filter, selectedTagId: selectedTag?._id })) @@ -461,7 +461,7 @@ define [ resolve: project: () -> project ) - + if storedUIOpts?.filter? if storedUIOpts.filter == "tag" and storedUIOpts.selectedTagId? markTagAsSelected(storedUIOpts.selectedTagId) @@ -486,7 +486,7 @@ define [ return project.source == 'token' $scope.userDisplayName = (user) -> - if user._id == window.user_id + if user? and user._id == window.user_id return "You" else if user? return [user.first_name, user.last_name].filter((n) -> n?).join(" ") @@ -535,11 +535,11 @@ define [ url: "/project/#{$scope.project.id}?forever=true" headers: "X-CSRF-Token": window.csrfToken - }).then () -> + }).then () -> $scope.project.isTableActionInflight = false $scope._removeProjectFromList $scope.project for tag in $scope.tags $scope._removeProjectIdsFromTagArray(tag, [ $scope.project.id ]) $scope.updateVisibleProjects() - .catch () -> + .catch () -> $scope.project.isTableActionInflight = false diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index 34d3e25c75..f6b2e4cfce 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -62,7 +62,7 @@ .small { color: @sidebar-color; } - } + } .project-list-sidebar when (@is-overleaf) { overflow-x: hidden; @@ -271,7 +271,7 @@ ul.structured-list { li { border-bottom: 1px solid @structured-list-border-color; padding: (@line-height-computed / 4) 0; - + &:last-child { border-bottom: 0 none; } @@ -294,7 +294,7 @@ ul.structured-list { .header when (@is-overleaf = false) { text-transform: uppercase; } - + .select-item, .select-all { position: absolute; left: @line-height-computed; @@ -355,7 +355,7 @@ table.project-list { .owner when (@is-overleaf = false) { margin-right: 0; } - a.projectName { + a.projectName, button.projectName { margin-right: @line-height-computed / 4; padding: 0; vertical-align: inherit; @@ -400,6 +400,7 @@ table.project-list { .v1-badge { margin-left: -4px; + margin-right: -4px; } .action-btn-row-header, .action-btn-row { @@ -595,7 +596,7 @@ table.project-list { &:last-child { margin-bottom: 0; } - } + } .announcement-header { .page-header; margin: 0; From ab871b9ba73f88cd5985ed8f67a9050e6e9aa3d5 Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 12 Sep 2018 11:07:35 +0100 Subject: [PATCH 5/6] Fix tabs vs spaces --- services/web/public/stylesheets/app/project-list.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/web/public/stylesheets/app/project-list.less b/services/web/public/stylesheets/app/project-list.less index f6b2e4cfce..86a72436c4 100644 --- a/services/web/public/stylesheets/app/project-list.less +++ b/services/web/public/stylesheets/app/project-list.less @@ -331,7 +331,7 @@ table.project-list { } // thead > tr > th { - // padding-top: @line-height-computed / 8; + // padding-top: @line-height-computed / 8; // } td { @@ -400,7 +400,7 @@ table.project-list { .v1-badge { margin-left: -4px; - margin-right: -4px; + margin-right: -4px; } .action-btn-row-header, .action-btn-row { From 5dbebc0693d7c8c22959b8425607d2b4e044caee Mon Sep 17 00:00:00 2001 From: James Allen Date: Thu, 13 Sep 2018 10:36:34 +0100 Subject: [PATCH 6/6] Translate 'by' --- services/web/app/views/project/list/item.pug | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/web/app/views/project/list/item.pug b/services/web/app/views/project/list/item.pug index 0ab99893da..5266571da7 100644 --- a/services/web/app/views/project/list/item.pug +++ b/services/web/app/views/project/list/item.pug @@ -63,7 +63,9 @@ td | {{project.lastUpdated | fromNowDate}} span(ng-if='project.lastUpdatedBy') | - | by {{userDisplayName(project.lastUpdatedBy)}} + | #{translate('by')} + | + | {{userDisplayName(project.lastUpdatedBy)}} td.text-right div(