Merge pull request #2807 from overleaf/ns-project-import-deletion-dashboard

Clean up references to v1 projects on project dashboard

GitOrigin-RevId: e574382fb1747da7a701808148434f4d689cbe33
This commit is contained in:
nate stemen
2020-05-19 15:05:36 -04:00
committed by Copybot
parent 8f5270899f
commit 5fca56d4e6
12 changed files with 16 additions and 260 deletions

View File

@@ -29,7 +29,6 @@ const PackageVersions = require('../../infrastructure/PackageVersions')
const Sources = require('../Authorization/Sources')
const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
const Modules = require('../../infrastructure/Modules')
const ProjectEntityHandler = require('./ProjectEntityHandler')
const TpdsProjectFlusher = require('../ThirdPartyDataStore/TpdsProjectFlusher')
const UserGetter = require('../User/UserGetter')
@@ -326,7 +325,6 @@ const ProjectController = {
// _buildProjectList already converts archived/trashed to booleans so isArchivedOrTrashed should not be used here
projects = ProjectController._buildProjectList(projects, userId)
.filter(p => !(p.archived || p.trashed))
.filter(p => !p.isV1Project)
.map(p => ({ _id: p.id, name: p.name, accessLevel: p.accessLevel }))
res.json({ projects })
@@ -380,22 +378,6 @@ const ProjectController = {
cb
)
},
v1Projects(cb) {
if (!Features.hasFeature('overleaf-integration')) {
return cb(null, null)
}
Modules.hooks.fire('findAllV1Projects', userId, (error, projects) => {
if (projects == null) {
projects = []
}
if (error != null && error instanceof V1ConnectionError) {
noV1Connection = true
return cb(null, null)
}
cb(error, projects[0])
})
}, // hooks.fire returns an array of results, only need first
hasSubscription(cb) {
LimitationsManager.hasPaidSubscription(
currentUser,
@@ -433,20 +415,13 @@ const ProjectController = {
logger.warn({ err }, 'error getting data for project list page')
return next(err)
}
if (noV1Connection) {
results.v1Projects = results.v1Projects || { projects: [], tags: [] }
results.v1Projects.noConnection = true
}
const { notifications, user, userAffiliations } = results
// Handle case of deleted user
if (user == null) {
UserController.logout(req, res, next)
return
}
const v1Tags =
(results.v1Projects != null ? results.v1Projects.tags : undefined) ||
[]
const tags = results.tags.concat(v1Tags)
const tags = results.tags
const notificationsInstitution = []
for (const notification of notifications) {
notification.html = req.i18n.translate(
@@ -545,12 +520,9 @@ const ProjectController = {
)
const projects = ProjectController._buildProjectList(
results.projects,
userId,
results.v1Projects != null ? results.v1Projects.projects : undefined
)
const warnings = ProjectController._buildWarningsList(
results.v1Projects
userId
)
const warnings = ProjectController._buildWarningsList(noV1Connection)
// in v2 add notifications for matching university IPs
if (Settings.overleaf != null && req.ip !== user.lastLoginIp) {
@@ -573,9 +545,6 @@ const ProjectController = {
userAffiliations,
hasSubscription: results.hasSubscription,
institutionLinkingError,
isShowingV1Projects:
results.v1Projects != null &&
results.v1Projects.projects.length > 0,
warnings,
zipFileSizeLimit: Settings.maxUploadSize
}
@@ -863,11 +832,8 @@ const ProjectController = {
)
},
_buildProjectList(allProjects, userId, v1Projects) {
_buildProjectList(allProjects, userId) {
let project
if (v1Projects == null) {
v1Projects = []
}
const {
owned,
readAndWrite,
@@ -907,9 +873,6 @@ const ProjectController = {
)
)
}
for (project of v1Projects) {
projects.push(ProjectController._buildV1ProjectViewModel(project))
}
// Token-access
// Only add these projects if they're not already present, this gives us cascading access
// from 'owner' => 'token-read-only'
@@ -973,39 +936,6 @@ const ProjectController = {
return model
},
_buildV1ProjectViewModel(project) {
const archived = project.archived
// If a project is simultaneously trashed and archived, we will consider it archived but not trashed.
const trashed = project.removed && !archived
const projectViewModel = {
id: project.id,
name: project.title,
lastUpdated: new Date(project.updated_at * 1000), // Convert from epoch
archived: archived,
trashed: trashed,
isV1Project: true
}
if (
(project.owner != null && project.owner.user_is_owner) ||
(project.creator != null && project.creator.user_is_creator)
) {
projectViewModel.accessLevel = 'owner'
} else {
projectViewModel.accessLevel = 'readOnly'
}
if (project.owner != null) {
projectViewModel.owner = {
first_name: project.owner.name
}
} else if (project.creator != null) {
projectViewModel.owner = {
first_name: project.creator.name
}
}
return projectViewModel
},
_injectProjectUsers(projects, callback) {
const users = {}
for (const project of projects) {
@@ -1051,22 +981,12 @@ const ProjectController = {
)
},
_buildWarningsList(v1ProjectData) {
if (v1ProjectData == null) {
v1ProjectData = {}
}
const warnings = []
if (v1ProjectData.noConnection) {
warnings.push(
'Error accessing Overleaf V1. Some of your projects or features may be missing.'
)
}
if (v1ProjectData.hasHiddenV1Projects) {
warnings.push(
"Looks like you've got a lot of V1 projects! Some of them may be hidden on V2. To view them all, use the V1 dashboard."
)
}
return warnings
_buildWarningsList(noConnection) {
return noConnection
? [
'Error accessing Overleaf V1. Some of your projects or features may be missing.'
]
: []
},
_buildPortalTemplatesList(affiliations) {

View File

@@ -104,7 +104,6 @@ html(
include layout/footer
!= moduleIncludes("contactModal", locals)
include v1-tooltip
block foot-scripts
script(src=buildJsPath('libraries.js'))

View File

@@ -1,4 +1,4 @@
td.project-list-table-name-cell(ng-if-start="!project.isV1Project")
td.project-list-table-name-cell
.project-list-table-name-container
input.project-list-table-select-item(
select-individual,
@@ -61,7 +61,7 @@ td.project-list-table-lastupdated-cell
| {{getUserName(project.lastUpdatedBy)}}
td.project-list-table-actions-cell(ng-if-end)
td.project-list-table-actions-cell
div(
ng-if="!project.isTableActionInflight"
)

View File

@@ -79,7 +79,6 @@
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(
@@ -204,7 +203,6 @@
select-row
)
include ./item
include ./v1-item
tr(
ng-if="visibleProjects.length == 0",
ng-cloak

View File

@@ -76,9 +76,6 @@
tooltip-append-to-body="true"
stop-propagation="click"
) i
if isShowingV1Projects
li(ng-class="{active: (filter == 'v1')}", ng-click="filterProjects('v1')")
a(href) #{translate("v1_projects")}
li.separator
h2 #{translate("tags_slash_folders")}
li.tag(ng-cloak)
@@ -102,14 +99,8 @@
)
span.name {{tag.name}}
span.subdued ({{countProjectsForTag(tag)}})
span.v1-badge(
ng-if="tag.isV1",
ng-cloak,
aria-label=translate("v1_badge")
tooltip-template="'v1TagTooltipTemplate'"
tooltip-append-to-body="true"
)
span.dropdown.tag-menu(dropdown)(ng-if="!tag.isV1")
span.dropdown.tag-menu(dropdown)
a.dropdown-toggle(
href="#",
data-toggle="dropdown",

View File

@@ -1,19 +0,0 @@
td.project-list-table-name-cell(ng-if-start="project.isV1Project")
.project-list-table-name-container
span.project-list-table-v1-badge-container
span.v1-badge(
aria-label=translate("v1_badge")
tooltip-template="'v1ProjectTooltipTemplate'"
tooltip-append-to-body="true"
)
span.project-list-table-name
a.projectName(href='/{{project.id}}') {{project.name}}
td.project-list-table-owner-cell
span.owner {{ownerName(project)}}
td.project-list-table-lastupdated-cell(
ng-if-end
colspan="2"
)
span.last-modified(tooltip="{{project.lastUpdated | formatDate}}") {{project.lastUpdated | fromNowDate}}

View File

@@ -1,6 +0,0 @@
script(type="text/ng-template", id="v1ProjectTooltipTemplate")
span This project is from Overleaf v1 and has not yet been automatically or manually imported to v2.
script(type="text/ng-template", id="v1TagTooltipTemplate")
span This folder/tag is from Overleaf v1. Once these projects are moved to v2 the tag will also move.

View File

@@ -240,11 +240,6 @@ App.controller('ProjectPageController', function(
visible = false
}
// Hide projects from V1 if we only want to see shared projects
if ($scope.filter === 'shared' && project.isV1Project) {
visible = false
}
// Hide projects we don't own if we only want to see owned projects
if ($scope.filter === 'owned' && project.accessLevel !== 'owner') {
visible = false
@@ -274,10 +269,6 @@ App.controller('ProjectPageController', function(
}
}
if ($scope.filter === 'v1' && !project.isV1Project) {
visible = false
}
if (visible) {
$scope.visibleProjects.push(project)
} else {

View File

@@ -91,7 +91,6 @@
@import 'app/sprites.less';
@import 'app/invite.less';
@import 'app/error-pages.less';
@import 'app/v1-badge.less';
@import 'app/editor/history-v2.less';
@import 'app/metrics.less';
@import 'app/open-in-overleaf.less';

View File

@@ -179,10 +179,6 @@ input.project-list-table-select-item[type='checkbox'] {
margin-top: 5px;
}
.project-list-table-v1-badge-container {
position: absolute;
}
.project-list-table-name {
display: inline-block;
padding-left: @line-height-computed * 1.5;
@@ -532,10 +528,6 @@ ul.project-list {
text-align: left;
}
.v1-badge {
margin-left: -4px;
}
.action-btn {
padding: 0 0.3em;
margin-left: 0.2em;

View File

@@ -1,10 +0,0 @@
.v1-badge {
&:extend(.label);
&:extend(.label-default);
vertical-align: 11%;
padding: 1px 3px;
margin: 0 6px;
&:before {
content: 'V1';
}
}

View File

@@ -102,11 +102,6 @@ describe('ProjectController', function() {
.stub()
.callsArgWith(2, null, { lastLoginIp: '192.170.18.2' })
}
this.Modules = {
hooks: {
fire: sinon.stub()
}
}
this.Features = {
hasFeature: sinon.stub()
}
@@ -171,7 +166,6 @@ describe('ProjectController', function() {
.AuthenticationController,
'../TokenAccess/TokenAccessHandler': this.TokenAccessHandler,
'../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
'../../infrastructure/Modules': this.Modules,
'./ProjectEntityHandler': this.ProjectEntityHandler,
'../Errors/Errors': Errors,
'../../infrastructure/Features': this.Features,
@@ -424,10 +418,7 @@ describe('ProjectController', function() {
null,
this.allProjects
)
this.Modules.hooks.fire
.withArgs('findAllV1Projects', this.user._id)
.yields(undefined)
}) // Without integration module hook, cb returns undefined
})
it('should render the project/list page', function(done) {
this.res.render = (pageName, opts) => {
@@ -521,17 +512,6 @@ describe('ProjectController', function() {
'Error accessing Overleaf V1. Some of your projects or features may be missing.'
})
it('should show a warning when there is an error getting v1 projects', function(done) {
this.Modules.hooks.fire
.withArgs('findAllV1Projects', this.user._id)
.yields(new Errors.V1ConnectionError('error'))
this.res.render = (pageName, opts) => {
expect(opts.warnings).to.contain(this.connectionWarning)
done()
}
this.ProjectController.projectListPage(this.req, this.res)
})
it('should show a warning when there is an error getting subscriptions from v1', function(done) {
this.LimitationsManager.hasPaidSubscription.yields(
new Errors.V1ConnectionError('error')
@@ -593,82 +573,6 @@ describe('ProjectController', function() {
})
})
describe('with overleaf-integration-web-module hook', function() {
beforeEach(function() {
this.Features.hasFeature = sinon
.stub()
.withArgs('overleaf-integration')
.returns(true)
this.V1Response = {
projects: [
{
id: '123mockV1Id',
title: 'mock title',
updated_at: 1509616411,
removed: false,
archived: false
},
{
id: '456mockV1Id',
title: 'mock title 2',
updated_at: 1509616411,
removed: true,
archived: false
}
],
tags: [{ name: 'mock tag', project_ids: ['123mockV1Id'] }]
}
this.Modules.hooks.fire
.withArgs('findAllV1Projects', this.user._id)
.yields(null, [this.V1Response])
}) // Need to wrap response in array, as multiple hooks could fire
it('should include V1 projects', function(done) {
this.res.render = (pageName, opts) => {
opts.projects.length.should.equal(
this.projects.length +
this.collabertions.length +
this.readOnly.length +
this.tokenReadAndWrite.length +
this.tokenReadOnly.length +
this.V1Response.projects.length
)
opts.projects.forEach(p => {
// Check properties correctly mapped from V1
expect(p).to.have.property('id')
expect(p).to.have.property('name')
expect(p).to.have.property('lastUpdated')
expect(p).to.have.property('accessLevel')
expect(p).to.have.property('archived')
})
done()
}
this.ProjectController.projectListPage(this.req, this.res)
})
it('should include V1 tags', function(done) {
this.res.render = (pageName, opts) => {
opts.tags.length.should.equal(
this.tags.length + this.V1Response.tags.length
)
opts.tags.forEach(t => {
expect(t).to.have.property('name')
expect(t).to.have.property('project_ids')
})
done()
}
this.ProjectController.projectListPage(this.req, this.res)
})
it('should have isShowingV1Projects flag', function(done) {
this.res.render = (pageName, opts) => {
opts.isShowingV1Projects.should.equal(true)
done()
}
this.ProjectController.projectListPage(this.req, this.res)
})
})
describe('With Institution SSO feature', function() {
beforeEach(function(done) {
this.institutionEmail = 'test@overleaf.com'
@@ -952,10 +856,7 @@ describe('ProjectController', function() {
null,
this.allProjects
)
this.Modules.hooks.fire
.withArgs('findAllV1Projects', this.user._id)
.yields(undefined)
}) // Without integration module hook, cb returns undefined
})
it('should render the project/list page', function(done) {
this.res.render = (pageName, opts) => {