mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-31 21:01:33 +02:00
Merge pull request #2150 from overleaf/cmg-archiving-frontend
Frontend for new archiving and trashing GitOrigin-RevId: be8676ab6c2fea7f8fb23655772a008d067b2a78
This commit is contained in:
committed by
sharelatex
parent
1a456da017
commit
0abe99d98f
@@ -34,7 +34,6 @@ const Features = require('../../infrastructure/Features')
|
||||
const BrandVariationsHandler = require('../BrandVariations/BrandVariationsHandler')
|
||||
const { getUserAffiliations } = require('../Institutions/InstitutionsAPI')
|
||||
const V1Handler = require('../V1/V1Handler')
|
||||
const { Project } = require('../../models/Project')
|
||||
|
||||
const ProjectController = {
|
||||
_isInPercentageRollout(rolloutName, objectId, percentage) {
|
||||
@@ -145,11 +144,10 @@ const ProjectController = {
|
||||
|
||||
archiveProject(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log({ projectId }, 'received request to archive project')
|
||||
|
||||
const user = AuthenticationController.getSessionUser(req)
|
||||
|
||||
ProjectDeleter.archiveProject(projectId, user._id, function(err) {
|
||||
ProjectDeleter.archiveProject(projectId, userId, function(err) {
|
||||
if (err != null) {
|
||||
return next(err)
|
||||
} else {
|
||||
@@ -160,11 +158,38 @@ const ProjectController = {
|
||||
|
||||
unarchiveProject(req, res, next) {
|
||||
const projectId = req.params.Project_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log({ projectId }, 'received request to unarchive project')
|
||||
|
||||
const user = AuthenticationController.getSessionUser(req)
|
||||
ProjectDeleter.unarchiveProject(projectId, userId, function(err) {
|
||||
if (err != null) {
|
||||
return next(err)
|
||||
} else {
|
||||
return res.sendStatus(200)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
ProjectDeleter.unarchiveProject(projectId, user._id, function(err) {
|
||||
trashProject(req, res, next) {
|
||||
const projectId = req.params.project_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log({ projectId }, 'received request to trash project')
|
||||
|
||||
ProjectDeleter.trashProject(projectId, userId, function(err) {
|
||||
if (err != null) {
|
||||
return next(err)
|
||||
} else {
|
||||
return res.sendStatus(200)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
untrashProject(req, res, next) {
|
||||
const projectId = req.params.project_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log({ projectId }, 'received request to untrash project')
|
||||
|
||||
ProjectDeleter.untrashProject(projectId, userId, function(err) {
|
||||
if (err != null) {
|
||||
return next(err)
|
||||
} else {
|
||||
@@ -210,38 +235,6 @@ const ProjectController = {
|
||||
})
|
||||
},
|
||||
|
||||
trashProject(req, res, next) {
|
||||
const projectId = req.params.project_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
|
||||
Project.update(
|
||||
{ _id: projectId },
|
||||
{ $addToSet: { trashed: userId } },
|
||||
error => {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
res.sendStatus(200)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
untrashProject(req, res, next) {
|
||||
const projectId = req.params.project_id
|
||||
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||
|
||||
Project.update(
|
||||
{ _id: projectId },
|
||||
{ $pull: { trashed: userId } },
|
||||
error => {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
res.sendStatus(200)
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
cloneProject(req, res, next) {
|
||||
res.setTimeout(5 * 60 * 1000) // allow extra time for the copy to complete
|
||||
metrics.inc('cloned-project')
|
||||
@@ -376,7 +369,7 @@ const ProjectController = {
|
||||
projects(cb) {
|
||||
ProjectGetter.findAllUsersProjects(
|
||||
userId,
|
||||
'name lastUpdated lastUpdatedBy publicAccesLevel archived owner_ref tokens',
|
||||
'name lastUpdated lastUpdatedBy publicAccesLevel archived trashed owner_ref tokens',
|
||||
cb
|
||||
)
|
||||
},
|
||||
@@ -925,6 +918,10 @@ const ProjectController = {
|
||||
},
|
||||
|
||||
_buildProjectViewModel(project, accessLevel, source, userId) {
|
||||
const archived = ProjectHelper.isArchived(project, userId)
|
||||
// If a project is simultaneously trashed and archived, we will consider it archived but not trashed.
|
||||
const trashed = ProjectHelper.isTrashed(project, userId) && !archived
|
||||
|
||||
TokenAccessHandler.protectTokens(project, accessLevel)
|
||||
const model = {
|
||||
id: project._id,
|
||||
@@ -934,7 +931,8 @@ const ProjectController = {
|
||||
publicAccessLevel: project.publicAccesLevel,
|
||||
accessLevel,
|
||||
source,
|
||||
archived: ProjectHelper.isArchived(project, userId),
|
||||
archived,
|
||||
trashed,
|
||||
owner_ref: project.owner_ref,
|
||||
tokens: project.tokens,
|
||||
isV1Project: false
|
||||
@@ -947,11 +945,16 @@ const ProjectController = {
|
||||
},
|
||||
|
||||
_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: project.removed || project.archived,
|
||||
archived: archived,
|
||||
trashed: trashed,
|
||||
isV1Project: true
|
||||
}
|
||||
if (
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const { db } = require('../../infrastructure/mongojs')
|
||||
const { db, ObjectId } = require('../../infrastructure/mongojs')
|
||||
const { promisify, callbackify } = require('util')
|
||||
const { Project } = require('../../models/Project')
|
||||
const { DeletedProject } = require('../../models/DeletedProject')
|
||||
@@ -160,11 +160,11 @@ const ProjectDeleter = {
|
||||
|
||||
// Async methods
|
||||
|
||||
async function archiveProject(project_id, userId) {
|
||||
logger.log({ project_id }, 'archiving project from user request')
|
||||
async function archiveProject(projectId, userId) {
|
||||
logger.log({ projectId }, 'archiving project from user request')
|
||||
|
||||
try {
|
||||
let project = await Project.findOne({ _id: project_id }).exec()
|
||||
let project = await Project.findOne({ _id: projectId }).exec()
|
||||
if (!project) {
|
||||
throw new Errors.NotFoundError('project not found')
|
||||
}
|
||||
@@ -174,18 +174,21 @@ async function archiveProject(project_id, userId) {
|
||||
'ARCHIVE'
|
||||
)
|
||||
|
||||
await Project.update({ _id: project_id }, { $set: { archived: archived } })
|
||||
await Project.update(
|
||||
{ _id: projectId },
|
||||
{ $set: { archived: archived }, $pull: { trashed: ObjectId(userId) } }
|
||||
)
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'problem archiving project')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function unarchiveProject(project_id, userId) {
|
||||
logger.log({ project_id }, 'unarchiving project from user request')
|
||||
async function unarchiveProject(projectId, userId) {
|
||||
logger.log({ projectId }, 'unarchiving project from user request')
|
||||
|
||||
try {
|
||||
let project = await Project.findOne({ _id: project_id }).exec()
|
||||
let project = await Project.findOne({ _id: projectId }).exec()
|
||||
if (!project) {
|
||||
throw new Errors.NotFoundError('project not found')
|
||||
}
|
||||
@@ -196,13 +199,54 @@ async function unarchiveProject(project_id, userId) {
|
||||
'UNARCHIVE'
|
||||
)
|
||||
|
||||
await Project.update({ _id: project_id }, { $set: { archived: archived } })
|
||||
await Project.update({ _id: projectId }, { $set: { archived: archived } })
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'problem unarchiving project')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function trashProject(projectId, userId) {
|
||||
logger.log({ projectId }, 'trashing project from user request')
|
||||
|
||||
try {
|
||||
let project = await Project.findOne({ _id: projectId }).exec()
|
||||
if (!project) {
|
||||
throw new Errors.NotFoundError('project not found')
|
||||
}
|
||||
|
||||
await Project.update(
|
||||
{ _id: projectId },
|
||||
{
|
||||
$addToSet: { trashed: ObjectId(userId) },
|
||||
$pull: { archived: ObjectId(userId) }
|
||||
}
|
||||
)
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'problem trashing project')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function untrashProject(projectId, userId) {
|
||||
logger.log({ projectId }, 'untrashing project from user request')
|
||||
|
||||
try {
|
||||
let project = await Project.findOne({ _id: projectId }).exec()
|
||||
if (!project) {
|
||||
throw new Errors.NotFoundError('project not found')
|
||||
}
|
||||
|
||||
await Project.update(
|
||||
{ _id: projectId },
|
||||
{ $pull: { trashed: ObjectId(userId) } }
|
||||
)
|
||||
} catch (err) {
|
||||
logger.warn({ err }, 'problem untrashing project')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteProject(project_id, options = {}) {
|
||||
logger.log({ project_id }, 'deleting project')
|
||||
|
||||
@@ -350,6 +394,8 @@ async function expireDeletedProject(projectId) {
|
||||
const promises = {
|
||||
archiveProject: archiveProject,
|
||||
unarchiveProject: unarchiveProject,
|
||||
trashProject: trashProject,
|
||||
untrashProject: untrashProject,
|
||||
deleteProject: deleteProject,
|
||||
undeleteProject: undeleteProject,
|
||||
expireDeletedProject: expireDeletedProject,
|
||||
@@ -359,6 +405,8 @@ const promises = {
|
||||
ProjectDeleter.promises = promises
|
||||
ProjectDeleter.archiveProject = callbackify(archiveProject)
|
||||
ProjectDeleter.unarchiveProject = callbackify(unarchiveProject)
|
||||
ProjectDeleter.trashProject = callbackify(trashProject)
|
||||
ProjectDeleter.untrashProject = callbackify(untrashProject)
|
||||
ProjectDeleter.deleteProject = callbackify(deleteProject)
|
||||
ProjectDeleter.undeleteProject = callbackify(undeleteProject)
|
||||
ProjectDeleter.expireDeletedProject = callbackify(expireDeletedProject)
|
||||
|
||||
@@ -3,7 +3,6 @@ td.project-list-table-name-cell(ng-if-start="!project.isV1Project")
|
||||
input.project-list-table-select-item(
|
||||
select-individual,
|
||||
type="checkbox",
|
||||
ng-disabled="shouldDisableCheckbox(project)",
|
||||
ng-model="project.selected"
|
||||
stop-propagation="click"
|
||||
aria-label=translate('select_project') + " '{{ project.name }}'"
|
||||
@@ -66,6 +65,7 @@ td.project-list-table-actions-cell(ng-if-end)
|
||||
ng-if="!project.isTableActionInflight"
|
||||
)
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="!(project.archived || project.trashed)"
|
||||
aria-label=translate('copy'),
|
||||
tooltip=translate('copy'),
|
||||
tooltip-placement="top",
|
||||
@@ -82,41 +82,59 @@ td.project-list-table-actions-cell(ng-if-end)
|
||||
)
|
||||
i.icon.fa.fa-cloud-download(aria-hidden="true")
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="!project.archived && isOwner()"
|
||||
ng-if="!project.archived"
|
||||
aria-label=translate('archive'),
|
||||
tooltip=translate('archive'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="archiveOrLeave($event)"
|
||||
ng-click="archive($event)"
|
||||
)
|
||||
i.icon.fa.fa-inbox(aria-hidden="true")
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="!isOwner()"
|
||||
aria-label=translate('leave'),
|
||||
tooltip=translate('leave'),
|
||||
ng-if="!project.trashed"
|
||||
aria-label=translate('trash'),
|
||||
tooltip=translate('trash'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="archiveOrLeave($event)"
|
||||
ng-click="trash($event)"
|
||||
)
|
||||
i.icon.fa.fa-sign-out(aria-hidden="true")
|
||||
i.icon.fa.fa-trash(aria-hidden="true")
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="project.archived && isOwner()"
|
||||
ng-if="project.archived && !project.trashed"
|
||||
aria-label=translate('unarchive'),
|
||||
tooltip=translate('unarchive'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="restore($event)"
|
||||
ng-click="unarchive($event)"
|
||||
)
|
||||
i.icon.fa.fa-reply(aria-hidden="true")
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="project.archived && isOwner()"
|
||||
aria-label=translate('delete_forever'),
|
||||
tooltip=translate('delete_forever'),
|
||||
ng-if="project.trashed && !project.archived"
|
||||
aria-label=translate('untrash'),
|
||||
tooltip=translate('untrash'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="deleteProject($event)"
|
||||
ng-click="untrash($event)"
|
||||
)
|
||||
i.icon.fa.fa-trash(aria-hidden="true")
|
||||
i.icon.fa.fa-reply(aria-hidden="true")
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="project.trashed && !project.archived && !isOwner()"
|
||||
aria-label=translate('leave'),
|
||||
tooltip=translate('leave'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="leave($event)"
|
||||
)
|
||||
i.icon.fa.fa-sign-out(aria-hidden="true")
|
||||
button.btn.btn-link.action-btn(
|
||||
ng-if="project.trashed && !project.archived && isOwner()"
|
||||
aria-label=translate('delete'),
|
||||
tooltip=translate('delete'),
|
||||
tooltip-placement="top",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="delete($event)"
|
||||
)
|
||||
i.icon.fa.fa-ban(aria-hidden="true")
|
||||
div(
|
||||
ng-if="project.isTableActionInflight"
|
||||
aria-label=translate('processing')
|
||||
|
||||
@@ -184,36 +184,54 @@ script(type='text/ng-template', id='newProjectModalTemplate')
|
||||
span(ng-hide="state.inflight") #{translate("create")}
|
||||
span(ng-show="state.inflight") #{translate("creating")} ...
|
||||
|
||||
script(type='text/ng-template', id='deleteProjectsModalTemplate')
|
||||
script(type='text/ng-template', id='archiveTrashLeaveOrDeleteProjectsModalTemplate')
|
||||
.modal-header
|
||||
button.close(
|
||||
type="button"
|
||||
data-dismiss="modal"
|
||||
ng-click="cancel()"
|
||||
) ×
|
||||
h3(ng-if="action == 'delete'") #{translate("delete_projects")}
|
||||
h3(ng-if="action == 'archive'") #{translate("archive_projects")}
|
||||
h3(ng-if="action == 'leave'") #{translate("leave_projects")}
|
||||
h3(ng-if="action == 'delete-and-leave'") #{translate("delete_and_leave_projects")}
|
||||
h3(ng-if="action == 'archive-and-leave'") #{translate("archive_and_leave_projects")}
|
||||
h3(ng-if="action === 'archive'") #{translate("archive_projects")}
|
||||
h3(ng-if="action === 'trash'") #{translate("trash_projects")}
|
||||
h3(ng-if="action === 'leave'") #{translate("leave_projects")}
|
||||
h3(ng-if="action === 'delete'") #{translate("delete_projects")}
|
||||
h3(ng-if="action === 'leaveOrDelete'") #{translate("delete_and_leave_projects")}
|
||||
.modal-body
|
||||
div(ng-show="projectsToDelete.length > 0")
|
||||
p(ng-if="action == 'delete' || action == 'delete-and-leave'") #{translate("about_to_delete_projects")}
|
||||
p(ng-if="action == 'archive' || action == 'archive-and-leave'") #{translate("about_to_archive_projects")}
|
||||
div(ng-if="action !== 'leaveOrDelete'")
|
||||
p(ng-if="action === 'archive'") #{translate("about_to_archive_projects")}
|
||||
p(ng-if="action === 'trash'") #{translate("about_to_trash_projects")}
|
||||
p(ng-if="action === 'leave'") #{translate("about_to_leave_projects")}
|
||||
p(ng-if="action === 'delete'") #{translate("about_to_delete_projects")}
|
||||
ul
|
||||
li(ng-repeat="project in projectsToDelete | orderBy:'name'")
|
||||
li(ng-repeat="project in projects | orderBy:'name'")
|
||||
strong {{project.name}}
|
||||
.project-action-alert.alert.alert-info(ng-if="action === 'archive'")
|
||||
.project-action-alert-msg #{translate("archived_projects_info_note")}
|
||||
a(href="https://www.overleaf.com/blog/new-feature-using-archive-and-trash-to-keep-your-projects-organized").project-action-alert-btn.btn.btn-info.pull-right #{translate("find_out_more")}
|
||||
.project-action-alert.alert.alert-info(ng-if="action === 'trash'")
|
||||
.project-action-alert-msg #{translate("trashed_projects_info_note")}
|
||||
a(href="https://www.overleaf.com/blog/new-feature-using-archive-and-trash-to-keep-your-projects-organized").project-action-alert-btn.btn.btn-info.pull-right #{translate("find_out_more")}
|
||||
.project-action-alert.alert.alert-warning(ng-if="action === 'leave' || action === 'delete'")
|
||||
i.fa.fa-fw.fa-exclamation-triangle
|
||||
.project-action-alert-msg #{translate("this_action_cannot_be_undone")}
|
||||
div(ng-if="action === 'leaveOrDelete'")
|
||||
p #{translate("about_to_delete_projects")}
|
||||
ul
|
||||
li(ng-repeat="project in projects | filter:{accessLevel: 'owner'} | orderBy:'name'")
|
||||
strong {{project.name}}
|
||||
div(ng-show="projectsToLeave.length > 0")
|
||||
p #{translate("about_to_leave_projects")}
|
||||
ul
|
||||
li(ng-repeat="project in projectsToLeave | orderBy:'name'")
|
||||
li(ng-repeat="project in projects | filter:{accessLevel: '!owner'} | orderBy:'name'")
|
||||
strong {{project.name}}
|
||||
.project-action-alert.alert.alert-warning
|
||||
i.fa.fa-fw.fa-exclamation-triangle
|
||||
.project-action-alert-msg #{translate("this_action_cannot_be_undone")}
|
||||
.modal-footer
|
||||
button.btn.btn-default(
|
||||
ng-click="cancel()"
|
||||
) #{translate("cancel")}
|
||||
button.btn.btn-danger(
|
||||
ng-click="delete()"
|
||||
ng-click="confirm()"
|
||||
) #{translate("confirm")}
|
||||
|
||||
script(type="text/template", id="qq-project-uploader-template")
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
) #{translate('clear_search')}
|
||||
|
||||
.project-tools(ng-cloak)
|
||||
.btn-toolbar(ng-show="filter != 'archived'")
|
||||
.btn-toolbar
|
||||
.btn-group(ng-hide="selectedProjects.length < 1")
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
@@ -37,15 +37,28 @@
|
||||
i.fa.fa-cloud-download(aria-hidden="true")
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
aria-label=`{{ isArchiveableProjectSelected ? '${translate("archive")}' : '${translate("leave")}' }}`,
|
||||
tooltip=`{{ isArchiveableProjectSelected ? '${translate("archive")}' : '${translate("leave")}' }}`,
|
||||
ng-if="filter !== 'archived'"
|
||||
aria-label=translate("archive"),
|
||||
tooltip=translate("archive"),
|
||||
tooltip-placement="bottom",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="openArchiveProjectsModal()"
|
||||
)
|
||||
i.fa(ng-class=`isArchiveableProjectSelected ? 'fa-inbox' : 'fa-sign-out'` aria-hidden="true")
|
||||
|
||||
.btn-group.dropdown(ng-hide="selectedProjects.length < 1", dropdown)
|
||||
i.fa.fa-inbox(aria-hidden="true")
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
ng-if="filter !== 'trashed'"
|
||||
aria-label=translate("trash"),
|
||||
tooltip=translate("trash"),
|
||||
tooltip-placement="bottom",
|
||||
tooltip-append-to-body="true",
|
||||
ng-click="openTrashProjectsModal()"
|
||||
)
|
||||
i.fa.fa-trash(aria-hidden="true")
|
||||
.btn-group.dropdown(
|
||||
ng-hide="selectedProjects.length < 1 || filter === 'archived' || filter === 'trashed'",
|
||||
dropdown
|
||||
)
|
||||
a.btn.btn-default.dropdown-toggle(
|
||||
href,
|
||||
data-toggle="dropdown",
|
||||
@@ -54,7 +67,7 @@
|
||||
tooltip-append-to-body="true",
|
||||
tooltip-placement="bottom"
|
||||
)
|
||||
i.fa.fa-folder-open-o
|
||||
i.fa.fa-folder-open
|
||||
|
|
||||
span.caret
|
||||
span.sr-only #{translate('add_to_folders')}
|
||||
@@ -82,7 +95,10 @@
|
||||
li
|
||||
a(href, ng-click="openNewTagModal()", stop-propagation="click") #{translate("create_new_folder")}
|
||||
|
||||
.btn-group(ng-hide="selectedProjects.length != 1", dropdown).dropdown
|
||||
.btn-group.dropdown(
|
||||
ng-hide="selectedProjects.length != 1 || filter === 'archived' || filter === 'trashed'",
|
||||
dropdown
|
||||
)
|
||||
a.btn.btn-default.dropdown-toggle(
|
||||
href,
|
||||
data-toggle="dropdown",
|
||||
@@ -101,24 +117,51 @@
|
||||
ng-click="openCloneProjectModal()"
|
||||
) #{translate("make_copy")}
|
||||
|
||||
.btn-toolbar(ng-show="filter == 'archived'")
|
||||
.btn-group(ng-hide="selectedProjects.length < 1")
|
||||
.btn-group(ng-show="filter === 'archived' && selectedProjects.length > 0")
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
data-original-title="Restore",
|
||||
data-original-title=translate("unarchive"),
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom",
|
||||
ng-click="restoreSelectedProjects()"
|
||||
) #{translate("restore")}
|
||||
ng-click="unarchiveProjects(selectedProjects)"
|
||||
) #{translate("unarchive")}
|
||||
|
||||
.btn-group(ng-hide="selectedProjects.length < 1")
|
||||
.btn-group(ng-show="filter === 'trashed' && selectedProjects.length > 0")
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
data-original-title=translate("untrash"),
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom",
|
||||
ng-click="untrashProjects(selectedProjects)"
|
||||
) #{translate("untrash")}
|
||||
|
||||
.btn-group(ng-show="filter === 'trashed' && selectedProjects.length > 0")
|
||||
a.btn.btn-danger(
|
||||
href,
|
||||
data-original-title="Delete Forever",
|
||||
ng-if="hasLeavableProjectsSelected() && !hasDeletableProjectsSelected()",
|
||||
data-original-title=translate('leave'),
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom",
|
||||
ng-click="openLeaveProjectsModal()"
|
||||
) #{translate("leave")}
|
||||
|
||||
a.btn.btn-danger(
|
||||
href,
|
||||
ng-if="hasDeletableProjectsSelected() && !hasLeavableProjectsSelected()",
|
||||
data-original-title=translate('delete'),
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom",
|
||||
ng-click="openDeleteProjectsModal()"
|
||||
) #{translate("delete_forever")}
|
||||
) #{translate("delete")}
|
||||
|
||||
a.btn.btn-danger(
|
||||
href,
|
||||
ng-if="hasDeletableProjectsSelected() && hasLeavableProjectsSelected()",
|
||||
data-original-title=translate('delete_and_leave'),
|
||||
data-toggle="tooltip",
|
||||
data-placement="bottom",
|
||||
ng-click="openLeaveOrDeleteProjectsModal()"
|
||||
) #{translate("delete_and_leave")}
|
||||
|
||||
.row.row-spaced
|
||||
each warning in warnings
|
||||
|
||||
@@ -58,8 +58,24 @@
|
||||
a(href) #{translate("your_projects")}
|
||||
li(ng-class="{active: (filter == 'shared')}", ng-click="filterProjects('shared')")
|
||||
a(href) #{translate("shared_with_you")}
|
||||
li(ng-class="{active: (filter == 'archived')}", ng-click="filterProjects('archived')")
|
||||
li.folders-menu-item-with-tooltip(ng-class="{active: (filter == 'archived')}", ng-click="filterProjects('archived')")
|
||||
a(href) #{settings.overleaf ? translate("archived_projects") : translate("deleted_projects")}
|
||||
.folders-menu-tooltip-trigger(
|
||||
href
|
||||
tooltip=translate('archived_projects_info_note')
|
||||
tooltip-placement="right"
|
||||
tooltip-append-to-body="true"
|
||||
stop-propagation="click"
|
||||
) i
|
||||
li.folders-menu-item-with-tooltip(ng-class="{active: (filter == 'trashed')}", ng-click="filterProjects('trashed')")
|
||||
a(href) #{translate("trashed_projects")}
|
||||
.folders-menu-tooltip-trigger(
|
||||
href
|
||||
tooltip=translate('trashed_projects_info_note')
|
||||
tooltip-placement="right"
|
||||
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")}
|
||||
|
||||
@@ -135,53 +135,20 @@ define(['base'], function(App) {
|
||||
return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
|
||||
})
|
||||
|
||||
App.controller('DeleteProjectsModalController', function(
|
||||
App.controller('ArchiveTrashLeaveOrDeleteProjectsModalController', function(
|
||||
$scope,
|
||||
$modalInstance,
|
||||
$timeout,
|
||||
projects
|
||||
projects,
|
||||
action
|
||||
) {
|
||||
$scope.projectsToDelete = projects.filter(
|
||||
project => project.accessLevel === 'owner'
|
||||
)
|
||||
$scope.projectsToLeave = projects.filter(
|
||||
project => project.accessLevel !== 'owner'
|
||||
)
|
||||
$scope.projectsToArchive = projects.filter(
|
||||
project => project.accessLevel === 'owner' && !project.archived
|
||||
)
|
||||
$scope.projects = projects
|
||||
|
||||
if (
|
||||
$scope.projectsToLeave.length > 0 &&
|
||||
$scope.projectsToDelete.length > 0
|
||||
) {
|
||||
if (
|
||||
$scope.projectsToArchive.length > 0 &&
|
||||
window.ExposedSettings.isOverleaf
|
||||
) {
|
||||
$scope.action = 'archive-and-leave'
|
||||
} else {
|
||||
$scope.action = 'delete-and-leave'
|
||||
}
|
||||
} else if (
|
||||
$scope.projectsToLeave.length === 0 &&
|
||||
$scope.projectsToDelete.length > 0
|
||||
) {
|
||||
if (
|
||||
$scope.projectsToArchive.length > 0 &&
|
||||
window.ExposedSettings.isOverleaf
|
||||
) {
|
||||
$scope.action = 'archive'
|
||||
} else {
|
||||
$scope.action = 'delete'
|
||||
}
|
||||
} else {
|
||||
$scope.action = 'leave'
|
||||
}
|
||||
$scope.action = action
|
||||
|
||||
$scope.delete = () => $modalInstance.close()
|
||||
$scope.confirm = () => $modalInstance.close({ projects, action })
|
||||
|
||||
return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
|
||||
$scope.cancel = () => $modalInstance.dismiss('cancel')
|
||||
})
|
||||
|
||||
App.controller('UploadProjectModalController', function(
|
||||
|
||||
@@ -15,7 +15,6 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
$scope.notificationsInstitution = window.data.notificationsInstitution
|
||||
$scope.allSelected = false
|
||||
$scope.selectedProjects = []
|
||||
$scope.isArchiveableProjectSelected = false
|
||||
$scope.filter = 'all'
|
||||
$scope.predicate = 'lastUpdated'
|
||||
$scope.nUntagged = 0
|
||||
@@ -182,9 +181,6 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
$scope.selectedProjects = $scope.projects.filter(
|
||||
project => project.selected
|
||||
)
|
||||
$scope.isArchiveableProjectSelected = $scope.selectedProjects.some(
|
||||
project => window.user_id === project.owner._id
|
||||
)
|
||||
}
|
||||
|
||||
$scope.getSelectedProjects = () => $scope.selectedProjects
|
||||
@@ -194,6 +190,18 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
|
||||
$scope.getFirstSelectedProject = () => $scope.selectedProjects[0]
|
||||
|
||||
$scope.hasLeavableProjectsSelected = () =>
|
||||
_.some(
|
||||
$scope.getSelectedProjects(),
|
||||
project => project.accessLevel !== 'owner' && project.trashed
|
||||
)
|
||||
|
||||
$scope.hasDeletableProjectsSelected = () =>
|
||||
_.some(
|
||||
$scope.getSelectedProjects(),
|
||||
project => project.accessLevel === 'owner' && project.trashed
|
||||
)
|
||||
|
||||
$scope.updateVisibleProjects = function() {
|
||||
$scope.visibleProjects = []
|
||||
const selectedTag = $scope.getSelectedTag()
|
||||
@@ -253,6 +261,18 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope.filter === 'trashed') {
|
||||
// Only show trashed projects
|
||||
if (!project.trashed) {
|
||||
visible = false
|
||||
}
|
||||
} else {
|
||||
// Only show non-trashed projects
|
||||
if (project.trashed) {
|
||||
visible = false
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope.filter === 'v1' && !project.isV1Project) {
|
||||
visible = false
|
||||
}
|
||||
@@ -543,64 +563,243 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
})
|
||||
}
|
||||
|
||||
$scope.createArchiveProjectsModal = function(projects) {
|
||||
// Methods to create modals for archiving, trashing, leaving and deleting projects
|
||||
const _createArchiveTrashLeaveOrDeleteProjectsModal = function(
|
||||
action,
|
||||
projects
|
||||
) {
|
||||
eventTracking.send(
|
||||
'project-list-page-interaction',
|
||||
'project action',
|
||||
action
|
||||
)
|
||||
return $modal.open({
|
||||
templateUrl: 'deleteProjectsModalTemplate',
|
||||
controller: 'DeleteProjectsModalController',
|
||||
templateUrl: 'archiveTrashLeaveOrDeleteProjectsModalTemplate',
|
||||
controller: 'ArchiveTrashLeaveOrDeleteProjectsModalController',
|
||||
resolve: {
|
||||
projects() {
|
||||
return projects
|
||||
},
|
||||
action() {
|
||||
return action
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$scope.createArchiveProjectsModal = function(projects) {
|
||||
return _createArchiveTrashLeaveOrDeleteProjectsModal('archive', projects)
|
||||
}
|
||||
|
||||
$scope.createTrashProjectsModal = function(projects) {
|
||||
return _createArchiveTrashLeaveOrDeleteProjectsModal('trash', projects)
|
||||
}
|
||||
|
||||
$scope.createLeaveProjectsModal = function(projects) {
|
||||
return _createArchiveTrashLeaveOrDeleteProjectsModal('leave', projects)
|
||||
}
|
||||
|
||||
$scope.createDeleteProjectsModal = function(projects) {
|
||||
return _createArchiveTrashLeaveOrDeleteProjectsModal('delete', projects)
|
||||
}
|
||||
|
||||
$scope.createLeaveOrDeleteProjectsModal = function(projects) {
|
||||
return _createArchiveTrashLeaveOrDeleteProjectsModal(
|
||||
'leaveOrDelete',
|
||||
projects
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
$scope.openArchiveProjectsModal = function() {
|
||||
const modalInstance = $scope.createArchiveProjectsModal(
|
||||
$scope.getSelectedProjects()
|
||||
)
|
||||
eventTracking.send(
|
||||
'project-list-page-interaction',
|
||||
'project action',
|
||||
'Delete'
|
||||
)
|
||||
modalInstance.result.then(() => $scope.archiveOrLeaveSelectedProjects())
|
||||
modalInstance.result.then(() => $scope.archiveSelectedProjects())
|
||||
}
|
||||
|
||||
$scope.archiveOrLeaveSelectedProjects = () =>
|
||||
$scope.archiveOrLeaveProjects($scope.getSelectedProjects())
|
||||
$scope.openTrashProjectsModal = function() {
|
||||
const modalInstance = $scope.createTrashProjectsModal(
|
||||
$scope.getSelectedProjects()
|
||||
)
|
||||
|
||||
$scope.archiveOrLeaveProjects = function(projects) {
|
||||
modalInstance.result.then(() => $scope.trashSelectedProjects())
|
||||
}
|
||||
|
||||
$scope.openLeaveProjectsModal = function() {
|
||||
const modalInstance = $scope.createLeaveProjectsModal(
|
||||
$scope.getSelectedProjects()
|
||||
)
|
||||
modalInstance.result.then(() => $scope.leaveSelectedProjects())
|
||||
}
|
||||
|
||||
$scope.openDeleteProjectsModal = function() {
|
||||
const modalInstance = $scope.createDeleteProjectsModal(
|
||||
$scope.getSelectedProjects()
|
||||
)
|
||||
modalInstance.result.then(() => $scope.deleteSelectedProjects())
|
||||
}
|
||||
|
||||
$scope.openLeaveOrDeleteProjectsModal = function() {
|
||||
const modalInstance = $scope.createLeaveOrDeleteProjectsModal(
|
||||
$scope.getSelectedProjects()
|
||||
)
|
||||
modalInstance.result.then(() => $scope.leaveOrDeleteSelectedProjects())
|
||||
}
|
||||
|
||||
//
|
||||
$scope.archiveSelectedProjects = () =>
|
||||
$scope.archiveProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.unarchiveSelectedProjects = () =>
|
||||
$scope.unarchiveProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.trashSelectedProjects = () =>
|
||||
$scope.trashProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.untrashSelectedProjects = () =>
|
||||
$scope.untrashProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.leaveSelectedProjects = () =>
|
||||
$scope.leaveProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.deleteSelectedProjects = () =>
|
||||
$scope.deleteProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.leaveOrDeleteSelectedProjects = () =>
|
||||
$scope.leaveOrDeleteProjects($scope.getSelectedProjects())
|
||||
|
||||
//
|
||||
$scope.archiveProjects = function(projects) {
|
||||
for (let project of projects) {
|
||||
$scope.archiveOrLeaveProject(project)
|
||||
project.archived = true
|
||||
project.trashed = false
|
||||
_archiveProject(project)
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.archiveOrLeaveProject = function(project) {
|
||||
if (project.accessLevel === 'owner') {
|
||||
project.archived = true
|
||||
queuedHttp({
|
||||
method: 'DELETE',
|
||||
url: `/project/${project.id}`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
} else {
|
||||
$scope._removeProjectFromList(project)
|
||||
$scope.unarchiveProjects = function(projects) {
|
||||
for (let project of projects) {
|
||||
project.archived = false
|
||||
_unarchiveProject(project)
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
for (let tag of project.tags || []) {
|
||||
$scope._removeProjectIdsFromTagArray(tag, [project._id])
|
||||
$scope.trashProjects = function(projects) {
|
||||
for (let project of projects) {
|
||||
project.trashed = true
|
||||
project.archived = false
|
||||
_trashProject(project)
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.untrashProjects = function(projects) {
|
||||
for (let project of projects) {
|
||||
project.trashed = false
|
||||
_untrashProject(project)
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.leaveProjects = function(projects) {
|
||||
_deleteOrLeaveProjectsLocally(projects)
|
||||
for (let project of projects) {
|
||||
_leaveProject(project)
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.deleteProjects = function(projects) {
|
||||
_deleteOrLeaveProjectsLocally(projects)
|
||||
for (let project of projects) {
|
||||
_deleteProject(project)
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.leaveOrDeleteProjects = function(projects) {
|
||||
_deleteOrLeaveProjectsLocally(projects)
|
||||
for (let project of projects) {
|
||||
if (project.accessLevel === 'owner') {
|
||||
_deleteProject(project)
|
||||
} else {
|
||||
_leaveProject(project)
|
||||
}
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
queuedHttp({
|
||||
method: 'POST',
|
||||
url: `/project/${project.id}/leave`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
// Actual interaction with the backend---we could move this into a service
|
||||
const _archiveProject = function(project) {
|
||||
return queuedHttp({
|
||||
method: 'POST',
|
||||
url: `/project/${project.id}/archive`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const _unarchiveProject = function(project) {
|
||||
return queuedHttp({
|
||||
method: 'DELETE',
|
||||
url: `/project/${project.id}/archive`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const _trashProject = function(project) {
|
||||
return queuedHttp({
|
||||
method: 'POST',
|
||||
url: `/project/${project.id}/trash`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const _untrashProject = function(project) {
|
||||
return queuedHttp({
|
||||
method: 'DELETE',
|
||||
url: `/project/${project.id}/trash`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const _leaveProject = function(project) {
|
||||
return queuedHttp({
|
||||
method: 'POST',
|
||||
url: `/project/${project.id}/leave`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const _deleteProject = function(project) {
|
||||
return queuedHttp({
|
||||
method: 'DELETE',
|
||||
url: `/project/${project.id}?forever=true`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const _deleteOrLeaveProjectsLocally = function(projects) {
|
||||
const projectIds = projects.map(p => p.id)
|
||||
for (let tag of $scope.tags || []) {
|
||||
$scope._removeProjectIdsFromTagArray(tag, projectIds)
|
||||
}
|
||||
for (let project of projects || []) {
|
||||
$scope._removeProjectFromList(project)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,69 +811,6 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
}
|
||||
}
|
||||
|
||||
$scope.openDeleteProjectsModal = function() {
|
||||
const modalInstance = $modal.open({
|
||||
templateUrl: 'deleteProjectsModalTemplate',
|
||||
controller: 'DeleteProjectsModalController',
|
||||
resolve: {
|
||||
projects() {
|
||||
return $scope.getSelectedProjects()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
modalInstance.result.then(() => $scope.deleteSelectedProjects())
|
||||
}
|
||||
|
||||
$scope.deleteSelectedProjects = function() {
|
||||
const selectedProjects = $scope.getSelectedProjects()
|
||||
const selectedProjectIds = $scope.getSelectedProjectIds()
|
||||
|
||||
// Remove projects from array
|
||||
for (let project of selectedProjects) {
|
||||
$scope._removeProjectFromList(project)
|
||||
}
|
||||
|
||||
// Remove project from any tags
|
||||
for (let tag of $scope.tags) {
|
||||
$scope._removeProjectIdsFromTagArray(tag, selectedProjectIds)
|
||||
}
|
||||
|
||||
for (let projectId of selectedProjectIds) {
|
||||
queuedHttp({
|
||||
method: 'DELETE',
|
||||
url: `/project/${projectId}?forever=true`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.restoreSelectedProjects = () =>
|
||||
$scope.restoreProjects($scope.getSelectedProjects())
|
||||
|
||||
$scope.restoreProjects = function(projects) {
|
||||
const projectIds = projects.map(p => p.id)
|
||||
for (let project of projects) {
|
||||
project.archived = false
|
||||
}
|
||||
|
||||
for (let projectId of projectIds) {
|
||||
queuedHttp({
|
||||
method: 'POST',
|
||||
url: `/project/${projectId}/restore`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$scope.updateVisibleProjects()
|
||||
}
|
||||
|
||||
$scope.openUploadProjectModal = function() {
|
||||
$modal.open({
|
||||
templateUrl: 'uploadProjectModalTemplate',
|
||||
@@ -728,9 +864,6 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
queuedHttp,
|
||||
ProjectListService
|
||||
) {
|
||||
$scope.shouldDisableCheckbox = project =>
|
||||
$scope.filter === 'archived' && project.accessLevel !== 'owner'
|
||||
|
||||
$scope.projectLink = function(project) {
|
||||
if (
|
||||
project.accessLevel === 'readAndWrite' &&
|
||||
@@ -796,49 +929,41 @@ define(['base', 'main/project-list/services/project-list'], function(App) {
|
||||
$scope.downloadProjectsById([$scope.project.id])
|
||||
}
|
||||
|
||||
$scope.archiveOrLeave = function(e) {
|
||||
$scope.archive = function(e) {
|
||||
e.stopPropagation()
|
||||
$scope.createArchiveProjectsModal([$scope.project]).result.then(() => {
|
||||
$scope.archiveOrLeaveProject($scope.project)
|
||||
$scope.updateVisibleProjects()
|
||||
$scope.archiveProjects([$scope.project])
|
||||
})
|
||||
}
|
||||
|
||||
$scope.restore = function(e) {
|
||||
$scope.unarchive = function(e) {
|
||||
e.stopPropagation()
|
||||
$scope.restoreProjects([$scope.project])
|
||||
$scope.unarchiveProjects([$scope.project])
|
||||
}
|
||||
|
||||
$scope.deleteProject = function(e) {
|
||||
$scope.trash = function(e) {
|
||||
e.stopPropagation()
|
||||
const modalInstance = $modal.open({
|
||||
templateUrl: 'deleteProjectsModalTemplate',
|
||||
controller: 'DeleteProjectsModalController',
|
||||
resolve: {
|
||||
projects() {
|
||||
return [$scope.project]
|
||||
}
|
||||
}
|
||||
$scope.createTrashProjectsModal([$scope.project]).result.then(() => {
|
||||
$scope.trashProjects([$scope.project])
|
||||
})
|
||||
}
|
||||
|
||||
modalInstance.result.then(function() {
|
||||
$scope.project.isTableActionInflight = true
|
||||
return queuedHttp({
|
||||
method: 'DELETE',
|
||||
url: `/project/${$scope.project.id}?forever=true`,
|
||||
headers: {
|
||||
'X-CSRF-Token': window.csrfToken
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
$scope.project.isTableActionInflight = false
|
||||
$scope._removeProjectFromList($scope.project)
|
||||
for (let tag of $scope.tags) {
|
||||
$scope._removeProjectIdsFromTagArray(tag, [$scope.project.id])
|
||||
}
|
||||
$scope.updateVisibleProjects()
|
||||
})
|
||||
.catch(() => ($scope.project.isTableActionInflight = false))
|
||||
$scope.untrash = function(e) {
|
||||
e.stopPropagation()
|
||||
$scope.untrashProjects([$scope.project])
|
||||
}
|
||||
|
||||
$scope.leave = function(e) {
|
||||
e.stopPropagation()
|
||||
$scope.createLeaveProjectsModal([$scope.project]).result.then(() => {
|
||||
$scope.leaveProjects([$scope.project])
|
||||
})
|
||||
}
|
||||
|
||||
$scope.delete = function(e) {
|
||||
e.stopPropagation()
|
||||
$scope.createDeleteProjectsModal([$scope.project]).result.then(() => {
|
||||
$scope.deleteProjects([$scope.project])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -350,6 +350,12 @@ ul.folders-menu {
|
||||
}
|
||||
}
|
||||
}
|
||||
> li.folders-menu-item-with-tooltip {
|
||||
> a {
|
||||
padding-right: 1.5 * @folders-menu-item-h-padding +
|
||||
@folders-menu-tooltip-size;
|
||||
}
|
||||
}
|
||||
> li > a.small {
|
||||
color: @gray;
|
||||
}
|
||||
@@ -430,6 +436,37 @@ ul.folders-menu {
|
||||
}
|
||||
}
|
||||
|
||||
.folders-menu-tooltip-trigger {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: @folders-menu-item-h-padding;
|
||||
width: @folders-menu-tooltip-size;
|
||||
height: @folders-menu-tooltip-size;
|
||||
margin-top: -(floor(@folders-menu-tooltip-size / 2));
|
||||
line-height: @folders-menu-tooltip-size;
|
||||
background-color: @folders-menu-tooltip-bg;
|
||||
font-family: @font-family-serif;
|
||||
color: #fff;
|
||||
font-style: italic;
|
||||
border-radius: ceil(@folders-menu-tooltip-size / 2);
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
@media (min-width: @screen-sm-min) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.project-action-alert {
|
||||
display: flex;
|
||||
margin-bottom: 0;
|
||||
align-items: center;
|
||||
}
|
||||
.project-action-alert-msg {
|
||||
flex-grow: 1;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
form.project-search {
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -909,7 +909,8 @@
|
||||
@folders-tag-border-color : @text-color;
|
||||
@folders-tag-menu-active-hover : darken(@brand-primary, 10%);
|
||||
@folders-tag-menu-hover : @gray-light;
|
||||
|
||||
@folders-menu-tooltip-size : 21px;
|
||||
@folders-menu-tooltip-bg : @blue;
|
||||
// Progress bars
|
||||
@progress-border-radius : @border-radius-base;
|
||||
@progress-border-width : 1px;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import "./_common-variables.less";
|
||||
@import './_common-variables.less';
|
||||
|
||||
@font-family-sans-serif: "Lato", sans-serif;
|
||||
@font-family-sans-serif: 'Lato', sans-serif;
|
||||
|
||||
@header-height: 68px;
|
||||
@footer-height: 50px;
|
||||
@@ -14,388 +14,388 @@
|
||||
@ol-blue-gray-5 : #2C3645;
|
||||
@ol-blue-gray-6 : #1E2530;
|
||||
|
||||
@ol-green : #138A07;
|
||||
@ol-dark-green : #004A0E;
|
||||
@ol-blue : #3E70BB;
|
||||
@ol-dark-blue : #2857A1;
|
||||
@ol-red : #C9453E;
|
||||
@ol-dark-red : #A6312B;
|
||||
@ol-green : #138A07;
|
||||
@ol-dark-green : #004A0E;
|
||||
@ol-blue : #3E70BB;
|
||||
@ol-dark-blue : #2857A1;
|
||||
@ol-red : #C9453E;
|
||||
@ol-dark-red : #A6312B;
|
||||
|
||||
@ol-type-color : @ol-blue-gray-3;
|
||||
@accent-color-primary: @ol-green;
|
||||
@foo-color: @ol-blue;
|
||||
|
||||
@ol-type-color : @ol-blue-gray-3;
|
||||
@accent-color-primary: @ol-green;
|
||||
@accent-color-secondary: @ol-dark-green;
|
||||
|
||||
// Navbar customization
|
||||
@navbar-title-color : @ol-blue-gray-1;
|
||||
@navbar-title-color : @ol-blue-gray-1;
|
||||
@navbar-title-color-hover : @ol-blue-gray-2;
|
||||
@navbar-brand-width : 130px;
|
||||
@navbar-default-color : #FFF;
|
||||
@navbar-default-bg : @ol-blue-gray-6;
|
||||
@navbar-default-border : transparent;
|
||||
@navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg);
|
||||
@navbar-default-link-bg : transparent;
|
||||
@navbar-brand-width : 130px;
|
||||
@navbar-default-color : #FFF;
|
||||
@navbar-default-bg : @ol-blue-gray-6;
|
||||
@navbar-default-border : transparent;
|
||||
@navbar-brand-image-url : url(/img/ol-brand/overleaf-white.svg);
|
||||
@navbar-default-link-bg : transparent;
|
||||
@nav-pills-active-link-hover-bg: @ol-dark-green;
|
||||
@nav-pills-link-color : @btn-default-bg;
|
||||
@nav-pills-link-hover-bg : darken(@ol-blue-gray-4, 8%); // match button-variant mixin
|
||||
@nav-pills-link-color : @btn-default-bg;
|
||||
@nav-pills-link-hover-bg : darken(@ol-blue-gray-4, 8%); // match button-variant mixin
|
||||
|
||||
// Backgrounds
|
||||
@body-bg : #FFF;
|
||||
@content-alt-bg-color : @ol-blue-gray-0;
|
||||
@body-bg : #FFF;
|
||||
@content-alt-bg-color : @ol-blue-gray-0;
|
||||
|
||||
// Border
|
||||
@border-color-base: @ol-blue-gray-2;
|
||||
@border-color-base: @ol-blue-gray-2;
|
||||
|
||||
// Typography
|
||||
@text-small-color : @ol-type-color;
|
||||
@text-color : @ol-type-color;
|
||||
@link-color : @ol-blue;
|
||||
@link-color-alt : @ol-green;
|
||||
@link-active-color : @ol-dark-green;
|
||||
@link-hover-color : @ol-dark-blue;
|
||||
@link-hover-color-alt : @ol-dark-green;
|
||||
@hr-border : @ol-blue-gray-1;
|
||||
@hr-border-alt : @gray-lighter;
|
||||
@text-small-color : @ol-type-color;
|
||||
@text-color : @ol-type-color;
|
||||
@link-color : @ol-blue;
|
||||
@link-color-alt : @ol-green;
|
||||
@link-active-color : @ol-dark-green;
|
||||
@link-hover-color : @ol-dark-blue;
|
||||
@link-hover-color-alt : @ol-dark-green;
|
||||
@hr-border : @ol-blue-gray-1;
|
||||
@hr-border-alt : @gray-lighter;
|
||||
@blockquote-small-color: @ol-blue-gray-3;
|
||||
|
||||
// Button colors and sizing
|
||||
@btn-border-width : 0;
|
||||
@btn-border-width : 0;
|
||||
@btn-border-bottom-width : 0;
|
||||
|
||||
@btn-border-radius-large : 9999px;
|
||||
@btn-border-radius-base : 9999px;
|
||||
@btn-border-radius-base : 9999px;
|
||||
@btn-border-radius-small : 9999px;
|
||||
|
||||
@btn-default-color : #FFF;
|
||||
@btn-default-bg : @ol-blue-gray-4;
|
||||
@btn-default-border : transparent;
|
||||
@btn-default-color : #FFF;
|
||||
@btn-default-bg : @ol-blue-gray-4;
|
||||
@btn-default-border : transparent;
|
||||
|
||||
@btn-primary-color : #FFF;
|
||||
@btn-primary-bg : @ol-green;
|
||||
@btn-primary-border : transparent;
|
||||
@btn-primary-color : #FFF;
|
||||
@btn-primary-bg : @ol-green;
|
||||
@btn-primary-border : transparent;
|
||||
|
||||
@btn-success-color : #FFF;
|
||||
@btn-success-bg : @ol-green;
|
||||
@btn-success-border : transparent;
|
||||
@btn-success-color : #FFF;
|
||||
@btn-success-bg : @ol-green;
|
||||
@btn-success-border : transparent;
|
||||
|
||||
@btn-info-color : #FFF;
|
||||
@btn-info-bg : @ol-blue;
|
||||
@btn-info-border : transparent;
|
||||
@btn-info-color : #FFF;
|
||||
@btn-info-bg : @ol-blue;
|
||||
@btn-info-border : transparent;
|
||||
|
||||
@btn-switch-color : @ol-blue-gray-4;
|
||||
@btn-switch-hover-color : darken(@ol-blue-gray-4, 8%);
|
||||
@btn-switch-color : @ol-blue-gray-4;
|
||||
@btn-switch-hover-color : darken(@ol-blue-gray-4, 8%);
|
||||
|
||||
// Padding
|
||||
@padding-xs-horizontal : 8px;
|
||||
@padding-xs-horizontal : 8px;
|
||||
|
||||
// Alerts
|
||||
@alert-padding : 15px;
|
||||
@alert-border-radius : @border-radius-base;
|
||||
@alert-link-font-weight : bold;
|
||||
|
||||
@alert-success-bg : @brand-success;
|
||||
@alert-success-text : #FFF;
|
||||
@alert-success-bg : @brand-success;
|
||||
@alert-success-text : #FFF;
|
||||
@alert-success-border: transparent;
|
||||
|
||||
@alert-info-bg : @brand-info;
|
||||
@alert-info-text : #FFF;
|
||||
@alert-info-border : transparent;
|
||||
@alert-info-bg : @brand-info;
|
||||
@alert-info-text : #FFF;
|
||||
@alert-info-border : transparent;
|
||||
|
||||
@alert-warning-bg : @brand-warning;
|
||||
@alert-warning-text : #FFF;
|
||||
@alert-warning-bg : @brand-warning;
|
||||
@alert-warning-text : #FFF;
|
||||
@alert-warning-border: transparent;
|
||||
|
||||
@alert-danger-bg : @brand-danger;
|
||||
@alert-danger-text : #FFF;
|
||||
@alert-danger-bg : @brand-danger;
|
||||
@alert-danger-text : #FFF;
|
||||
@alert-danger-border : transparent;
|
||||
|
||||
@alert-alt-bg : @ol-blue-gray-1;
|
||||
@alert-alt-text : @ol-type-color;
|
||||
@alert-alt-bg : @ol-blue-gray-1;
|
||||
@alert-alt-text : @ol-type-color;
|
||||
@alert-alt-border: transparent;
|
||||
|
||||
|
||||
// Tags
|
||||
@tag-border-radius : 9999px;
|
||||
@tag-color : @ol-blue-gray-4;
|
||||
@tag-bg-color : @ol-blue-gray-1;
|
||||
@tag-bg-hover-color : darken(@ol-blue-gray-1, 5%);
|
||||
@tag-top-adjustment : 2px;
|
||||
@labels-font-size : 85%;
|
||||
@tag-border-radius : 9999px;
|
||||
@tag-color : @ol-blue-gray-4;
|
||||
@tag-bg-color : @ol-blue-gray-1;
|
||||
@tag-bg-hover-color : darken(@ol-blue-gray-1, 5%);
|
||||
@tag-top-adjustment : 2px;
|
||||
@labels-font-size : 85%;
|
||||
|
||||
// Navbar
|
||||
@grid-float-breakpoint : @screen-md-min;
|
||||
@navbar-default-padding-v : (@grid-gutter-width / 2);
|
||||
@navbar-default-padding-h : 10px;
|
||||
@navbar-default-padding : @navbar-default-padding-v @navbar-default-padding-h;
|
||||
@navbar-default-link-color : #FFF;
|
||||
@grid-float-breakpoint : @screen-md-min;
|
||||
@navbar-default-padding-v : (@grid-gutter-width / 2);
|
||||
@navbar-default-padding-h : 10px;
|
||||
@navbar-default-padding : @navbar-default-padding-v @navbar-default-padding-h;
|
||||
@navbar-default-link-color : #FFF;
|
||||
@navbar-default-link-border-color : @navbar-default-link-color;
|
||||
@navbar-default-link-hover-bg : @ol-green;
|
||||
@navbar-default-link-active-bg : @ol-green;
|
||||
@navbar-default-link-hover-color : @ol-green;
|
||||
@navbar-btn-font-size : @font-size-base;
|
||||
@navbar-btn-border-radius : @btn-border-radius-base;
|
||||
@navbar-btn-font-weight : 400;
|
||||
@navbar-btn-padding : (@padding-base-vertical - 1) @padding-base-horizontal @padding-base-vertical;
|
||||
@navbar-btn-line-height : @line-height-base;
|
||||
@navbar-default-link-hover-bg : @ol-green;
|
||||
@navbar-default-link-active-bg : @ol-green;
|
||||
@navbar-default-link-hover-color : @ol-green;
|
||||
@navbar-btn-font-size : @font-size-base;
|
||||
@navbar-btn-border-radius : @btn-border-radius-base;
|
||||
@navbar-btn-font-weight : 400;
|
||||
@navbar-btn-padding : (@padding-base-vertical - 1) @padding-base-horizontal @padding-base-vertical;
|
||||
@navbar-btn-line-height : @line-height-base;
|
||||
|
||||
@navbar-subdued-color : #FFF;
|
||||
@navbar-subdued-padding : (@padding-base-vertical + 1) (@padding-base-horizontal + 1) (@padding-base-vertical + 2);
|
||||
@navbar-subdued-hover-bg : #FFF;
|
||||
@navbar-subdued-hover-color : @ol-green;
|
||||
@navbar-subdued-color : #FFF;
|
||||
@navbar-subdued-padding : (@padding-base-vertical + 1) (@padding-base-horizontal + 1) (@padding-base-vertical + 2);
|
||||
@navbar-subdued-hover-bg : #FFF;
|
||||
@navbar-subdued-hover-color : @ol-green;
|
||||
|
||||
@dropdown-divider-margin : 6px;
|
||||
@dropdown-item-padding : 4px 20px;
|
||||
@dropdown-divider-margin : 6px;
|
||||
@dropdown-item-padding : 4px 20px;
|
||||
|
||||
// Forms
|
||||
@input-color : @ol-blue-gray-3;
|
||||
@input-border-radius : unit(@line-height-base, em);
|
||||
@input-height-base : @line-height-computed + (@padding-base-vertical * 2) - 1;
|
||||
@input-color : @ol-blue-gray-3;
|
||||
@input-border-radius : unit(@line-height-base, em);
|
||||
@input-height-base : @line-height-computed + (@padding-base-vertical * 2) - 1;
|
||||
// TODO Warning color-orange?
|
||||
@btn-warning-color : #FFF;
|
||||
@btn-warning-bg : @ol-red;
|
||||
@btn-warning-border : transparent;
|
||||
@btn-warning-color : #FFF;
|
||||
@btn-warning-bg : @ol-red;
|
||||
@btn-warning-border : transparent;
|
||||
|
||||
@btn-danger-color : #FFF;
|
||||
@btn-danger-bg : @ol-red;
|
||||
@btn-danger-border : transparent;
|
||||
@btn-danger-color : #FFF;
|
||||
@btn-danger-bg : @ol-red;
|
||||
@btn-danger-border : transparent;
|
||||
|
||||
// Cards
|
||||
@card-box-shadow : none;
|
||||
@card-box-shadow : none;
|
||||
|
||||
// Sidebar
|
||||
@sidebar-bg : @ol-blue-gray-5;
|
||||
@sidebar-color : @ol-blue-gray-2;
|
||||
@sidebar-link-color : #FFF;
|
||||
@sidebar-active-border-radius : 0;
|
||||
@sidebar-active-bg : @ol-blue-gray-6;
|
||||
@sidebar-active-color : #FFF;
|
||||
@sidebar-active-font-weight : 700;
|
||||
@sidebar-hover-bg : @ol-blue-gray-4;
|
||||
@sidebar-hover-text-decoration : none;
|
||||
@v2-dash-pane-bg : @ol-blue-gray-4;
|
||||
@v2-dash-pane-link-color : #FFF;
|
||||
@v2-dash-pane-color : #FFF;
|
||||
@v2-dash-pane-toggle-color : #FFF;
|
||||
@v2-dash-pane-btn-bg : @ol-blue-gray-5;
|
||||
@v2-dash-pane-btn-hover-bg : @ol-blue-gray-6;
|
||||
@sidebar-bg : @ol-blue-gray-5;
|
||||
@sidebar-color : @ol-blue-gray-2;
|
||||
@sidebar-link-color : #FFF;
|
||||
@sidebar-active-border-radius : 0;
|
||||
@sidebar-active-bg : @ol-blue-gray-6;
|
||||
@sidebar-active-color : #FFF;
|
||||
@sidebar-active-font-weight : 700;
|
||||
@sidebar-hover-bg : @ol-blue-gray-4;
|
||||
@sidebar-hover-text-decoration : none;
|
||||
@v2-dash-pane-bg : @ol-blue-gray-4;
|
||||
@v2-dash-pane-link-color : #FFF;
|
||||
@v2-dash-pane-color : #FFF;
|
||||
@v2-dash-pane-toggle-color : #FFF;
|
||||
@v2-dash-pane-btn-bg : @ol-blue-gray-5;
|
||||
@v2-dash-pane-btn-hover-bg : @ol-blue-gray-6;
|
||||
|
||||
@folders-menu-margin : 0 -(@grid-gutter-width / 2);
|
||||
@folders-menu-line-height : @structured-list-line-height;
|
||||
@folders-menu-item-v-padding : (@line-height-computed / 4);
|
||||
@folders-menu-item-h-padding : (@grid-gutter-width / 2);
|
||||
@folders-title-padding : @folders-menu-item-v-padding 0;
|
||||
@folders-title-margin-top : 0;
|
||||
@folders-title-margin-bottom : 0;
|
||||
@folders-title-font-weight : normal;
|
||||
@folders-title-font-size : @font-size-small;
|
||||
@folders-title-color : @ol-blue-gray-2;
|
||||
@folders-title-text-transform : uppercase;
|
||||
@folders-tag-display : block;
|
||||
@folders-tag-line-height : 1.4;
|
||||
@folders-tag-padding : @folders-menu-item-v-padding 20px @folders-menu-item-v-padding @folders-menu-item-h-padding;
|
||||
@folders-tag-menu-color : #FFF;
|
||||
@folders-tag-hover : @sidebar-hover-bg;
|
||||
@folders-tag-border-color : @folders-tag-menu-color;
|
||||
@folders-tag-menu-hover : rgba(0, 0, 0, .1);
|
||||
@folders-tag-menu-active-hover : rgba(0, 0, 0, .1);
|
||||
@folders-menu-margin : 0 -(@grid-gutter-width / 2);
|
||||
@folders-menu-line-height : @structured-list-line-height;
|
||||
@folders-menu-item-v-padding : (@line-height-computed / 4);
|
||||
@folders-menu-item-h-padding : (@grid-gutter-width / 2);
|
||||
@folders-menu-tooltip-bg : @ol-blue;
|
||||
@folders-title-padding : @folders-menu-item-v-padding 0;
|
||||
@folders-title-margin-top : 0;
|
||||
@folders-title-margin-bottom : 0;
|
||||
@folders-title-font-weight : normal;
|
||||
@folders-title-font-size : @font-size-small;
|
||||
@folders-title-color : @ol-blue-gray-2;
|
||||
@folders-title-text-transform : uppercase;
|
||||
@folders-tag-display : block;
|
||||
@folders-tag-line-height : 1.4;
|
||||
@folders-tag-padding : @folders-menu-item-v-padding 20px @folders-menu-item-v-padding @folders-menu-item-h-padding;
|
||||
@folders-tag-menu-color : #FFF;
|
||||
@folders-tag-hover : @sidebar-hover-bg;
|
||||
@folders-tag-border-color : @folders-tag-menu-color;
|
||||
@folders-tag-menu-hover : rgba(0, 0, 0, .1);
|
||||
@folders-tag-menu-active-hover : rgba(0, 0, 0, .1);
|
||||
|
||||
// Portal
|
||||
@btn-portal-width : 200px;
|
||||
@btn-portal-width : 200px;
|
||||
|
||||
// Project table
|
||||
@structured-list-line-height : 2.5;
|
||||
@structured-list-link-color : @ol-blue;
|
||||
@structured-list-line-height : 2.5;
|
||||
@structured-list-link-color : @ol-blue;
|
||||
@structured-header-border-color : shade(@ol-blue-gray-1, 5%);
|
||||
@structured-list-border-color : @ol-blue-gray-1;
|
||||
@structured-list-hover-color : lighten(@ol-blue-gray-1, 5%);
|
||||
|
||||
@structured-list-border-color : @ol-blue-gray-1;
|
||||
@structured-list-hover-color : lighten(@ol-blue-gray-1, 5%);
|
||||
|
||||
// Progress bars
|
||||
@progress-border-radius : @line-height-computed;
|
||||
@progress-border-width : 0;
|
||||
@progress-bar-bg : @ol-blue-gray-4;
|
||||
@progress-bar-success-bg : @ol-green;
|
||||
@progress-bar-warning-bg : @brand-warning;
|
||||
@progress-bar-danger-bg : @ol-red;
|
||||
@progress-bar-info-bg : @ol-blue;
|
||||
@progress-bar-shadow : none;
|
||||
@progress-border-radius : @line-height-computed;
|
||||
@progress-border-width : 0;
|
||||
@progress-bar-bg : @ol-blue-gray-4;
|
||||
@progress-bar-success-bg : @ol-green;
|
||||
@progress-bar-warning-bg : @brand-warning;
|
||||
@progress-bar-danger-bg : @ol-red;
|
||||
@progress-bar-info-bg : @ol-blue;
|
||||
@progress-bar-shadow : none;
|
||||
|
||||
// Footer
|
||||
@footer-bg-color : #FFF;
|
||||
@footer-link-color : @ol-green;
|
||||
@footer-link-hover-color : @ol-dark-green;
|
||||
@footer-padding : 2em 0;
|
||||
@footer-bg-color : #FFF;
|
||||
@footer-link-color : @ol-green;
|
||||
@footer-link-hover-color : @ol-dark-green;
|
||||
@footer-padding : 2em 0;
|
||||
|
||||
// Editor header
|
||||
@toolbar-header-bg-color : @ol-blue-gray-6;
|
||||
@toolbar-header-shadow : none;
|
||||
@toolbar-header-bg-color : @ol-blue-gray-6;
|
||||
@toolbar-header-shadow : none;
|
||||
@toolbar-header-branded-btn-bg-color : transparent;
|
||||
@toolbar-btn-color : #FFF;
|
||||
@toolbar-btn-hover-color : #FFF;
|
||||
@toolbar-btn-hover-bg-color : @ol-blue-gray-5;
|
||||
@toolbar-btn-hover-text-shadow : none;
|
||||
@toolbar-btn-active-color : #FFF;
|
||||
@toolbar-btn-active-bg-color : @ol-green;
|
||||
@toolbar-btn-active-shadow : none;
|
||||
@toolbar-border-color : @ol-blue-gray-5;
|
||||
@toolbar-header-btn-border-color : @toolbar-border-color;
|
||||
@toolbar-alt-bg-color : @ol-blue-gray-5;
|
||||
@toolbar-icon-btn-color : #FFF;
|
||||
@toolbar-icon-btn-hover-color : #FFF;
|
||||
@toolbar-icon-btn-hover-shadow : none;
|
||||
@toolbar-border-bottom : 1px solid @toolbar-border-color;
|
||||
@toolbar-icon-btn-hover-boxshadow : none;
|
||||
@toolbar-font-size : 13px;
|
||||
@project-name-color : @ol-blue-gray-2;
|
||||
@project-rename-link-color : @ol-blue-gray-2;
|
||||
@project-rename-link-color-hover : @ol-blue-gray-1;
|
||||
@global-alerts-padding : 7px;
|
||||
@toolbar-btn-color : #FFF;
|
||||
@toolbar-btn-hover-color : #FFF;
|
||||
@toolbar-btn-hover-bg-color : @ol-blue-gray-5;
|
||||
@toolbar-btn-hover-text-shadow : none;
|
||||
@toolbar-btn-active-color : #FFF;
|
||||
@toolbar-btn-active-bg-color : @ol-green;
|
||||
@toolbar-btn-active-shadow : none;
|
||||
@toolbar-border-color : @ol-blue-gray-5;
|
||||
@toolbar-header-btn-border-color : @toolbar-border-color;
|
||||
@toolbar-alt-bg-color : @ol-blue-gray-5;
|
||||
@toolbar-icon-btn-color : #FFF;
|
||||
@toolbar-icon-btn-hover-color : #FFF;
|
||||
@toolbar-icon-btn-hover-shadow : none;
|
||||
@toolbar-border-bottom : 1px solid @toolbar-border-color;
|
||||
@toolbar-icon-btn-hover-boxshadow : none;
|
||||
@toolbar-font-size : 13px;
|
||||
@project-name-color : @ol-blue-gray-2;
|
||||
@project-rename-link-color : @ol-blue-gray-2;
|
||||
@project-rename-link-color-hover : @ol-blue-gray-1;
|
||||
@global-alerts-padding : 7px;
|
||||
|
||||
// Editor file-tree
|
||||
@file-tree-bg : @ol-blue-gray-4;
|
||||
@file-tree-line-height : 2.05;
|
||||
@file-tree-item-color : #FFF;
|
||||
@file-tree-item-focus-color : @file-tree-item-color;
|
||||
@file-tree-bg : @ol-blue-gray-4;
|
||||
@file-tree-line-height : 2.05;
|
||||
@file-tree-item-color : #FFF;
|
||||
@file-tree-item-focus-color : @file-tree-item-color;
|
||||
@file-tree-item-focus-selected-color : @file-tree-item-color;
|
||||
@file-tree-item-selected-color : @file-tree-item-color;
|
||||
@file-tree-item-input-color : @ol-blue-gray-5;
|
||||
@file-tree-item-toggle-color : @ol-blue-gray-2;
|
||||
@file-tree-item-icon-color : @ol-blue-gray-2;
|
||||
@file-tree-item-folder-color : @ol-blue-gray-2;
|
||||
@file-tree-item-hover-bg : @ol-blue-gray-5;
|
||||
@file-tree-item-selected-bg : @ol-green;
|
||||
@file-tree-multiselect-bg : @ol-blue;
|
||||
@file-tree-multiselect-hover-bg : @ol-dark-blue;
|
||||
@file-tree-droppable-bg-color : @ol-blue-gray-2;
|
||||
@file-tree-item-selected-color : @file-tree-item-color;
|
||||
@file-tree-item-input-color : @ol-blue-gray-5;
|
||||
@file-tree-item-toggle-color : @ol-blue-gray-2;
|
||||
@file-tree-item-icon-color : @ol-blue-gray-2;
|
||||
@file-tree-item-folder-color : @ol-blue-gray-2;
|
||||
@file-tree-item-hover-bg : @ol-blue-gray-5;
|
||||
@file-tree-item-selected-bg : @ol-green;
|
||||
@file-tree-multiselect-bg : @ol-blue;
|
||||
@file-tree-multiselect-hover-bg : @ol-dark-blue;
|
||||
@file-tree-droppable-bg-color : @ol-blue-gray-2;
|
||||
|
||||
// Editor resizers
|
||||
@editor-resizer-bg-color : @ol-blue-gray-5;
|
||||
@editor-resizer-bg-color : @ol-blue-gray-5;
|
||||
@editor-resizer-bg-color-dragging : @ol-blue-gray-5;
|
||||
@editor-toggler-bg-color : darken(@ol-blue-gray-2, 15%);
|
||||
@editor-toggler-hover-bg-color : @ol-green;
|
||||
@synctex-controls-z-index : 6;
|
||||
@synctex-controls-padding : 0;
|
||||
@editor-border-color : @ol-blue-gray-5;
|
||||
@editor-toggler-bg-color : darken(@ol-blue-gray-2, 15%);
|
||||
@editor-toggler-hover-bg-color : @ol-green;
|
||||
@synctex-controls-z-index : 6;
|
||||
@synctex-controls-padding : 0;
|
||||
@editor-border-color : @ol-blue-gray-5;
|
||||
|
||||
// Editor toolbar
|
||||
@editor-toolbar-bg : @ol-blue-gray-5;
|
||||
@editor-toolbar-bg : @ol-blue-gray-5;
|
||||
|
||||
// Toggle switch
|
||||
@toggle-switch-bg : @ol-blue-gray-1;
|
||||
@toggle-switch-highlight-color : @ol-green;
|
||||
@toggle-switch-radius-left : @btn-border-radius-base 0 0 @btn-border-radius-base;
|
||||
@toggle-switch-radius-right : 0 @btn-border-radius-base @btn-border-radius-base 0;
|
||||
@toggle-switch-bg : @ol-blue-gray-1;
|
||||
@toggle-switch-highlight-color : @ol-green;
|
||||
@toggle-switch-radius-left : @btn-border-radius-base 0 0 @btn-border-radius-base;
|
||||
@toggle-switch-radius-right : 0 @btn-border-radius-base @btn-border-radius-base 0;
|
||||
|
||||
// Formatting buttons
|
||||
@formatting-btn-color : #FFF;
|
||||
@formatting-btn-bg : @ol-blue-gray-5;
|
||||
@formatting-btn-border : @ol-blue-gray-4;
|
||||
@formatting-menu-bg : @ol-blue-gray-5;
|
||||
@formatting-btn-color : #FFF;
|
||||
@formatting-btn-bg : @ol-blue-gray-5;
|
||||
@formatting-btn-border : @ol-blue-gray-4;
|
||||
@formatting-menu-bg : @ol-blue-gray-5;
|
||||
|
||||
// Chat
|
||||
@chat-bg : @ol-blue-gray-5;
|
||||
@chat-message-color : #FFF;
|
||||
@chat-message-name-color : #FFF;
|
||||
@chat-message-date-color : @ol-blue-gray-2;
|
||||
@chat-message-box-shadow : none;
|
||||
@chat-message-padding : 5px 10px;
|
||||
@chat-message-border-radius : @border-radius-large;
|
||||
@chat-message-weight : bold;
|
||||
@chat-new-message-bg : @ol-blue-gray-4;
|
||||
@chat-new-message-textarea-bg : @ol-blue-gray-1;
|
||||
@chat-new-message-textarea-color : @ol-blue-gray-6;
|
||||
@chat-new-message-border-color : @editor-border-color;
|
||||
@chat-bg : @ol-blue-gray-5;
|
||||
@chat-message-color : #FFF;
|
||||
@chat-message-name-color : #FFF;
|
||||
@chat-message-date-color : @ol-blue-gray-2;
|
||||
@chat-message-box-shadow : none;
|
||||
@chat-message-padding : 5px 10px;
|
||||
@chat-message-border-radius : @border-radius-large;
|
||||
@chat-message-weight : bold;
|
||||
@chat-new-message-bg : @ol-blue-gray-4;
|
||||
@chat-new-message-textarea-bg : @ol-blue-gray-1;
|
||||
@chat-new-message-textarea-color : @ol-blue-gray-6;
|
||||
@chat-new-message-border-color : @editor-border-color;
|
||||
|
||||
// Pagination
|
||||
@pagination-active-bg : @ol-dark-green;
|
||||
@pagination-active-border : @gray-lighter;
|
||||
@pagination-active-color : #FFF;
|
||||
@pagination-bg : #FFF;
|
||||
@pagination-border : @gray-lighter;
|
||||
@pagination-color : @ol-dark-green;
|
||||
@pagination-disabled-color : @gray-dark;
|
||||
@pagination-disabled-bg : @gray-lightest;
|
||||
@pagination-disabled-border : @gray-lighter;
|
||||
@pagination-hover-color : @ol-dark-green;
|
||||
@pagination-hover-bg : @gray-lightest;
|
||||
@pagination-hover-border : @gray-lighter;
|
||||
|
||||
@pagination-active-bg : @ol-dark-green;
|
||||
@pagination-active-border : @gray-lighter;
|
||||
@pagination-active-color : #FFF;
|
||||
@pagination-bg : #FFF;
|
||||
@pagination-border : @gray-lighter;
|
||||
@pagination-color : @ol-dark-green;
|
||||
@pagination-disabled-color : @gray-dark;
|
||||
@pagination-disabled-bg : @gray-lightest;
|
||||
@pagination-disabled-border : @gray-lighter;
|
||||
@pagination-hover-color : @ol-dark-green;
|
||||
@pagination-hover-bg : @gray-lightest;
|
||||
@pagination-hover-border : @gray-lighter;
|
||||
|
||||
// PDF
|
||||
@pdf-top-offset : @toolbar-small-height;
|
||||
@pdf-bg : @ol-blue-gray-1;
|
||||
@pdfjs-bg : transparent;
|
||||
@pdf-page-shadow-color : rgba(0, 0, 0, 0.5);
|
||||
@log-line-no-color : #FFF;
|
||||
@log-hints-color : @ol-blue-gray-4;
|
||||
@pdf-top-offset : @toolbar-small-height;
|
||||
@pdf-bg : @ol-blue-gray-1;
|
||||
@pdfjs-bg : transparent;
|
||||
@pdf-page-shadow-color : rgba(0, 0, 0, 0.5);
|
||||
@log-line-no-color : #FFF;
|
||||
@log-hints-color : @ol-blue-gray-4;
|
||||
|
||||
// Plans
|
||||
@table-hover-bg : @ol-blue-gray-0;
|
||||
@plans-non-highlighted : white;
|
||||
@table-hover-bg : @ol-blue-gray-0;
|
||||
@plans-non-highlighted : white;
|
||||
|
||||
// Portals
|
||||
@black-alpha-strong : rgba(0,0,0,0.8);
|
||||
|
||||
@black-alpha-strong : rgba(0,0,0,0.8);
|
||||
|
||||
// v2 History
|
||||
@history-base-font-size : @font-size-small;
|
||||
@history-base-bg : @ol-blue-gray-1;
|
||||
@history-entry-label-bg-color : @ol-blue;
|
||||
@history-entry-pseudo-label-bg-color : @ol-green;
|
||||
@history-entry-label-color : #FFF;
|
||||
@history-entry-selected-label-bg-color : #FFF;
|
||||
@history-entry-selected-label-color : @ol-blue;
|
||||
@history-base-font-size : @font-size-small;
|
||||
@history-base-bg : @ol-blue-gray-1;
|
||||
@history-entry-label-bg-color : @ol-blue;
|
||||
@history-entry-pseudo-label-bg-color : @ol-green;
|
||||
@history-entry-label-color : #FFF;
|
||||
@history-entry-selected-label-bg-color : #FFF;
|
||||
@history-entry-selected-label-color : @ol-blue;
|
||||
@history-entry-selected-pseudo-label-color: @ol-green;
|
||||
@history-entry-day-bg : @ol-blue-gray-2;
|
||||
@history-entry-selected-bg : @ol-green;
|
||||
@history-entry-handle-bg : darken(@ol-green, 10%);
|
||||
@history-entry-handle-height : 8px;
|
||||
@history-base-color : @ol-blue-gray-2;
|
||||
@history-highlight-color : @ol-type-color;
|
||||
@history-toolbar-bg-color : @editor-toolbar-bg;
|
||||
@history-toolbar-color : #FFF;
|
||||
@history-file-badge-bg : rgba(255, 255, 255, .25);
|
||||
@history-file-badge-color : @file-tree-item-color;
|
||||
@history-entry-day-bg : @ol-blue-gray-2;
|
||||
@history-entry-selected-bg : @ol-green;
|
||||
@history-entry-handle-bg : darken(@ol-green, 10%);
|
||||
@history-entry-handle-height : 8px;
|
||||
@history-base-color : @ol-blue-gray-2;
|
||||
@history-highlight-color : @ol-type-color;
|
||||
@history-toolbar-bg-color : @editor-toolbar-bg;
|
||||
@history-toolbar-color : #FFF;
|
||||
@history-file-badge-bg : rgba(255, 255, 255, .25);
|
||||
@history-file-badge-color : @file-tree-item-color;
|
||||
// Screens
|
||||
// added -size to not conflict with common_variables
|
||||
@screen-size-sm-max : 767px;
|
||||
@screen-size-md-min : 768px;
|
||||
@screen-size-md-max : 991px;
|
||||
@screen-size-sm-max : 767px;
|
||||
@screen-size-md-min : 768px;
|
||||
@screen-size-md-max : 991px;
|
||||
|
||||
// System messages
|
||||
@sys-msg-background : @ol-blue;
|
||||
@sys-msg-color : #FFF;
|
||||
@sys-msg-border : solid 1px lighten(@ol-blue, 10%);
|
||||
@sys-msg-background : @ol-blue;
|
||||
@sys-msg-color : #FFF;
|
||||
@sys-msg-border : solid 1px lighten(@ol-blue, 10%);
|
||||
|
||||
@input-suggestion-v-offset : 4px;
|
||||
@input-suggestion-v-offset : 4px;
|
||||
|
||||
//== Colors
|
||||
//
|
||||
//## Gray and brand colors for use across Bootstrap.
|
||||
@gray-darker: #252525;
|
||||
@gray-dark: #505050;
|
||||
@gray: #7a7a7a;
|
||||
@gray-light: #a4a4a4;
|
||||
@gray-lighter: #cfcfcf;
|
||||
@gray-lightest: #f0f0f0;
|
||||
@white: #ffffff;
|
||||
@gray-darker: #252525;
|
||||
@gray-dark: #505050;
|
||||
@gray: #7a7a7a;
|
||||
@gray-light: #a4a4a4;
|
||||
@gray-lighter: #cfcfcf;
|
||||
@gray-lightest: #f0f0f0;
|
||||
@white: #ffffff;
|
||||
|
||||
@blue: #405ebf;
|
||||
@blueDark: #040D2D;
|
||||
@green: #46a546;
|
||||
@red: #a93529;
|
||||
@yellow: #A1A729;
|
||||
@orange: #f89406;
|
||||
@pink: #c3325f;
|
||||
@purple: #7a43b6;
|
||||
@blue: #405ebf;
|
||||
@blueDark: #040d2d;
|
||||
@green: #46a546;
|
||||
@red: #a93529;
|
||||
@yellow: #a1a729;
|
||||
@orange: #f89406;
|
||||
@pink: #c3325f;
|
||||
@purple: #7a43b6;
|
||||
|
||||
@brand-primary: @ol-green;
|
||||
@brand-secondary: @ol-dark-green;
|
||||
@brand-success: @green;
|
||||
@brand-info: @ol-blue;
|
||||
@brand-warning: @orange;
|
||||
@brand-danger: @ol-red;
|
||||
@brand-primary: @ol-green;
|
||||
@brand-secondary: @ol-dark-green;
|
||||
@brand-success: @green;
|
||||
@brand-info: @ol-blue;
|
||||
@brand-warning: @orange;
|
||||
@brand-danger: @ol-red;
|
||||
|
||||
@editor-header-logo-background: url(/img/ol-brand/overleaf-o-white.svg) center / contain no-repeat;
|
||||
@editor-header-logo-background: url(/img/ol-brand/overleaf-o-white.svg) center /
|
||||
contain no-repeat;
|
||||
@editor-loading-logo-padding-top: 115.44%;
|
||||
@editor-loading-logo-background-url: url(/img/ol-brand/overleaf-o-grey.svg);
|
||||
@editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg);
|
||||
@editor-loading-logo-foreground-url: url(/img/ol-brand/overleaf-o.svg);
|
||||
|
||||
@@ -1223,6 +1223,8 @@ describe('ProjectController', function() {
|
||||
describe('_buildProjectViewModel', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectHelper.isArchived.returns(false)
|
||||
this.ProjectHelper.isTrashed.returns(false)
|
||||
|
||||
this.project = {
|
||||
_id: 'abcd',
|
||||
name: 'netsenits',
|
||||
@@ -1239,31 +1241,70 @@ describe('ProjectController', function() {
|
||||
}
|
||||
})
|
||||
|
||||
it('should produce a model of the project', function() {
|
||||
const result = this.ProjectController._buildProjectViewModel(
|
||||
this.project,
|
||||
'readAndWrite',
|
||||
'owner',
|
||||
this.user._id
|
||||
)
|
||||
expect(result).to.exist
|
||||
expect(result).to.be.an('object')
|
||||
expect(result).to.deep.equal({
|
||||
id: 'abcd',
|
||||
name: 'netsenits',
|
||||
lastUpdated: 1,
|
||||
lastUpdatedBy: 2,
|
||||
publicAccessLevel: 'private',
|
||||
accessLevel: 'readAndWrite',
|
||||
source: 'owner',
|
||||
archived: false,
|
||||
owner_ref: 'defg',
|
||||
tokens: {
|
||||
readAndWrite: '1abcd',
|
||||
readAndWritePrefix: '1',
|
||||
readOnly: 'neiotsranteoia'
|
||||
},
|
||||
isV1Project: false
|
||||
describe('project not being archived or trashed', function() {
|
||||
it('should produce a model of the project', function() {
|
||||
const result = this.ProjectController._buildProjectViewModel(
|
||||
this.project,
|
||||
'readAndWrite',
|
||||
'owner',
|
||||
this.user._id
|
||||
)
|
||||
expect(result).to.exist
|
||||
expect(result).to.be.an('object')
|
||||
expect(result).to.deep.equal({
|
||||
id: 'abcd',
|
||||
name: 'netsenits',
|
||||
lastUpdated: 1,
|
||||
lastUpdatedBy: 2,
|
||||
publicAccessLevel: 'private',
|
||||
accessLevel: 'readAndWrite',
|
||||
source: 'owner',
|
||||
archived: false,
|
||||
trashed: false,
|
||||
owner_ref: 'defg',
|
||||
tokens: {
|
||||
readAndWrite: '1abcd',
|
||||
readAndWritePrefix: '1',
|
||||
readOnly: 'neiotsranteoia'
|
||||
},
|
||||
isV1Project: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('project being simultaneously archived and trashed', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectHelper.isArchived.returns(true)
|
||||
this.ProjectHelper.isTrashed.returns(true)
|
||||
})
|
||||
|
||||
it('should produce a model of the project', function() {
|
||||
const result = this.ProjectController._buildProjectViewModel(
|
||||
this.project,
|
||||
'readAndWrite',
|
||||
'owner',
|
||||
this.user._id
|
||||
)
|
||||
expect(result).to.exist
|
||||
expect(result).to.be.an('object')
|
||||
expect(result).to.deep.equal({
|
||||
id: 'abcd',
|
||||
name: 'netsenits',
|
||||
lastUpdated: 1,
|
||||
lastUpdatedBy: 2,
|
||||
publicAccessLevel: 'private',
|
||||
accessLevel: 'readAndWrite',
|
||||
source: 'owner',
|
||||
archived: true,
|
||||
trashed: false,
|
||||
owner_ref: 'defg',
|
||||
tokens: {
|
||||
readAndWrite: '1abcd',
|
||||
readAndWritePrefix: '1',
|
||||
readOnly: 'neiotsranteoia'
|
||||
},
|
||||
isV1Project: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1286,6 +1327,7 @@ describe('ProjectController', function() {
|
||||
accessLevel: 'readOnly',
|
||||
source: 'token',
|
||||
archived: false,
|
||||
trashed: false,
|
||||
owner_ref: null,
|
||||
tokens: {
|
||||
readAndWrite: '1abcd',
|
||||
|
||||
@@ -158,7 +158,7 @@ describe('ProjectDeleter', function() {
|
||||
'../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
|
||||
'../Docstore/DocstoreManager': this.DocstoreManager,
|
||||
'./ProjectDetailsHandler': this.ProjectDetailsHandler,
|
||||
'../../infrastructure/mongojs': { db: this.db },
|
||||
'../../infrastructure/mongojs': { db: this.db, ObjectId },
|
||||
'logger-sharelatex': this.logger
|
||||
},
|
||||
globals: {
|
||||
@@ -464,7 +464,13 @@ describe('ProjectDeleter', function() {
|
||||
.resolves(this.project)
|
||||
|
||||
this.ProjectMock.expects('update')
|
||||
.withArgs({ _id: this.project_id }, { $set: { archived: archived } })
|
||||
.withArgs(
|
||||
{ _id: this.project_id },
|
||||
{
|
||||
$set: { archived: archived },
|
||||
$pull: { trashed: ObjectId(this.user._id) }
|
||||
}
|
||||
)
|
||||
.resolves()
|
||||
})
|
||||
|
||||
@@ -501,6 +507,57 @@ describe('ProjectDeleter', function() {
|
||||
})
|
||||
})
|
||||
|
||||
describe('trashProject', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectMock.expects('findOne')
|
||||
.withArgs({ _id: this.project_id })
|
||||
.chain('exec')
|
||||
.resolves(this.project)
|
||||
|
||||
this.ProjectMock.expects('update')
|
||||
.withArgs(
|
||||
{ _id: this.project_id },
|
||||
{
|
||||
$addToSet: { trashed: ObjectId(this.user._id) },
|
||||
$pull: { archived: ObjectId(this.user._id) }
|
||||
}
|
||||
)
|
||||
.resolves()
|
||||
})
|
||||
|
||||
it('should update the project', async function() {
|
||||
await this.ProjectDeleter.promises.trashProject(
|
||||
this.project_id,
|
||||
this.user._id
|
||||
)
|
||||
this.ProjectMock.verify()
|
||||
})
|
||||
})
|
||||
|
||||
describe('untrashProject', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectMock.expects('findOne')
|
||||
.withArgs({ _id: this.project_id })
|
||||
.chain('exec')
|
||||
.resolves(this.project)
|
||||
|
||||
this.ProjectMock.expects('update')
|
||||
.withArgs(
|
||||
{ _id: this.project_id },
|
||||
{ $pull: { trashed: ObjectId(this.user._id) } }
|
||||
)
|
||||
.resolves()
|
||||
})
|
||||
|
||||
it('should update the project', async function() {
|
||||
await this.ProjectDeleter.promises.untrashProject(
|
||||
this.project_id,
|
||||
this.user._id
|
||||
)
|
||||
this.ProjectMock.verify()
|
||||
})
|
||||
})
|
||||
|
||||
describe('restoreProject', function() {
|
||||
beforeEach(function() {
|
||||
this.ProjectMock.expects('update')
|
||||
|
||||
Reference in New Issue
Block a user