overleaf/services/web/public/coffee/main/project-list/project-list.coffee
Alasdair Smith 8f71b104c5 Fix bug where unowned project would show archive quick action instead of leave
If the user does not own the project, the project can only be left, not
archived. Previously the quick action button was only showing the archive icon
but clicking the button would correctly leave the project. This is confusing,
so this commit corrects to show the leave icon for projects not owned by the
current user
2018-06-06 16:59:13 +01:00

514 lines
15 KiB
CoffeeScript

define [
"base"
], (App) ->
App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout, localStorage) ->
$scope.projects = window.data.projects
$scope.tags = window.data.tags
$scope.notifications = window.data.notifications
$scope.allSelected = false
$scope.selectedProjects = []
$scope.isArchiveableProjectSelected = false
$scope.filter = "all"
$scope.predicate = "lastUpdated"
$scope.nUntagged = 0
$scope.reverse = true
$scope.searchText =
value : ""
$timeout () ->
recalculateProjectListHeight()
, 10
$scope.$watch((
() -> $scope.projects.filter((project) -> (!project.tags? or project.tags.length == 0) and !project.archived).length
), (newVal) -> $scope.nUntagged = newVal)
storedUIOpts = JSON.parse(localStorage("project_list"))
recalculateProjectListHeight = () ->
$projListCard = $(".project-list-card")
topOffset = $projListCard.offset()?.top
cardPadding = $projListCard.outerHeight() - $projListCard.height()
bottomOffset = $("footer").outerHeight()
height = $window.innerHeight - topOffset - bottomOffset - cardPadding
$scope.projectListHeight = height
angular.element($window).bind "resize", () ->
recalculateProjectListHeight()
$scope.$apply()
# Allow tags to be accessed on projects as well
projectsById = {}
for project in $scope.projects
projectsById[project.id] = project
for tag in $scope.tags
for project_id in tag.project_ids or []
project = projectsById[project_id]
if project?
project.tags ||= []
project.tags.push tag
markTagAsSelected = (id) ->
for tag in $scope.tags
if tag._id == id
tag.selected = true
else
tag.selected = false
$scope.changePredicate = (newPredicate)->
if $scope.predicate == newPredicate
$scope.reverse = !$scope.reverse
$scope.predicate = newPredicate
$scope.getSortIconClass = (column)->
if column == $scope.predicate and $scope.reverse
return "fa-caret-down"
else if column == $scope.predicate and !$scope.reverse
return "fa-caret-up"
else
return ""
$scope.searchProjects = ->
event_tracking.send 'project-list-page-interaction', 'project-search', 'keydown'
$scope.updateVisibleProjects()
$scope.clearSearchText = () ->
$scope.searchText.value = ""
$scope.filter = "all"
$scope.$emit "search:clear"
$scope.updateVisibleProjects()
$scope.setFilter = (filter) ->
$scope.filter = filter
$scope.updateVisibleProjects()
$scope.updateSelectedProjects = () ->
$scope.selectedProjects = $scope.projects.filter (project) -> project.selected
$scope.isArchiveableProjectSelected = $scope.selectedProjects.some (project) ->
window.user_id == project.owner._id
$scope.getSelectedProjects = () ->
$scope.selectedProjects
$scope.getSelectedProjectIds = () ->
$scope.selectedProjects.map (project) -> project.id
$scope.getFirstSelectedProject = () ->
$scope.selectedProjects[0]
$scope.updateVisibleProjects = () ->
$scope.visibleProjects = []
selectedTag = $scope.getSelectedTag()
for project in $scope.projects
visible = true
# Only show if it matches any search text
if $scope.searchText.value? and $scope.searchText.value != ""
if project.name.toLowerCase().indexOf($scope.searchText.value.toLowerCase()) == -1
visible = false
# Only show if it matches the selected tag
if $scope.filter == "tag" and selectedTag? and project.id not in selectedTag.project_ids
visible = false
# Hide tagged projects if we only want to see the uncategorized ones
if $scope.filter == "untagged" and project.tags?.length > 0
visible = false
# Hide projects we own if we only want to see shared projects
if $scope.filter == "shared" and project.accessLevel == "owner"
visible = false
# Hide projects from V1 if we only want to see shared projects
if $scope.filter == "shared" and project.isV1Project
visible = false
# Hide projects we don't own if we only want to see owned projects
if $scope.filter == "owned" and project.accessLevel != "owner"
visible = false
if $scope.filter == "archived"
# Only show archived projects
if !project.archived
visible = false
else
# Only show non-archived projects
if project.archived
visible = false
if $scope.filter == "v1" and !project.isV1Project
visible = false
if visible
$scope.visibleProjects.push project
else
# We don't want hidden selections
project.selected = false
localStorage("project_list", JSON.stringify({
filter: $scope.filter,
selectedTagId: selectedTag?._id
}))
$scope.updateSelectedProjects()
$scope.getSelectedTag = () ->
for tag in $scope.tags
return tag if tag.selected
return null
$scope._removeProjectIdsFromTagArray = (tag, remove_project_ids) ->
# Remove project_id from tag.project_ids
remaining_project_ids = []
removed_project_ids = []
for project_id in tag.project_ids
if project_id not in remove_project_ids
remaining_project_ids.push project_id
else
removed_project_ids.push project_id
tag.project_ids = remaining_project_ids
return removed_project_ids
$scope._removeProjectFromList = (project) ->
index = $scope.projects.indexOf(project)
if index > -1
$scope.projects.splice(index, 1)
$scope.removeSelectedProjectsFromTag = (tag) ->
tag.showWhenEmpty = true
selected_project_ids = $scope.getSelectedProjectIds()
selected_projects = $scope.getSelectedProjects()
removed_project_ids = $scope._removeProjectIdsFromTagArray(tag, selected_project_ids)
# Remove tag from project.tags
remaining_tags = []
for project in selected_projects
project.tags ||= []
index = project.tags.indexOf tag
if index > -1
project.tags.splice(index, 1)
for project_id in removed_project_ids
queuedHttp({
method: "DELETE"
url: "/tag/#{tag._id}/project/#{project_id}"
headers:
"X-CSRF-Token": window.csrfToken
})
# If we're filtering by this tag then we need to remove
# the projects from view
$scope.updateVisibleProjects()
$scope.removeProjectFromTag = (project, tag) ->
tag.showWhenEmpty = true
project.tags ||= []
index = project.tags.indexOf tag
if index > -1
$scope._removeProjectIdsFromTagArray(tag, [ project.id ])
project.tags.splice(index, 1)
queuedHttp({
method: "DELETE"
url: "/tag/#{tag._id}/project/#{project.id}"
headers:
"X-CSRF-Token": window.csrfToken
})
$scope.updateVisibleProjects()
$scope.addSelectedProjectsToTag = (tag) ->
selected_projects = $scope.getSelectedProjects()
event_tracking.send 'project-list-page-interaction', 'project action', 'addSelectedProjectsToTag'
# Add project_ids into tag.project_ids
added_project_ids = []
for project_id in $scope.getSelectedProjectIds()
unless project_id in tag.project_ids
tag.project_ids.push project_id
added_project_ids.push project_id
# Add tag into each project.tags
for project in selected_projects
project.tags ||= []
unless tag in project.tags
project.tags.push tag
for project_id in added_project_ids
queuedHttp.post "/tag/#{tag._id}/project/#{project_id}", {
_csrf: window.csrfToken
}
$scope.createTag = (name) ->
return tag
$scope.openNewTagModal = (e) ->
modalInstance = $modal.open(
templateUrl: "newTagModalTemplate"
controller: "NewTagModalController"
)
modalInstance.result.then(
(tag) ->
$scope.tags.push tag
$scope.addSelectedProjectsToTag(tag)
)
$scope.createProject = (name, template = "none") ->
return queuedHttp
.post("/project/new", {
_csrf: window.csrfToken
projectName: name
template: template
})
.then((response) ->
{ data } = response
$scope.projects.push {
name: name
_id: data.project_id
accessLevel: "owner"
# TODO: Check access level if correct after adding it in
# to the rest of the app
}
$scope.updateVisibleProjects()
)
$scope.openCreateProjectModal = (template = "none") ->
event_tracking.send 'project-list-page-interaction', 'new-project', template
modalInstance = $modal.open(
templateUrl: "newProjectModalTemplate"
controller: "NewProjectModalController"
resolve:
template: () -> template
scope: $scope
)
modalInstance.result.then (project_id) ->
window.location = "/project/#{project_id}"
$scope.renameProject = (project, newName) ->
return queuedHttp.post "/project/#{project.id}/rename", {
newProjectName: newName,
_csrf: window.csrfToken
}
.then () ->
project.name = newName
$scope.openRenameProjectModal = () ->
project = $scope.getFirstSelectedProject()
return if !project? or project.accessLevel != "owner"
event_tracking.send 'project-list-page-interaction', 'project action', 'Rename'
modalInstance = $modal.open(
templateUrl: "renameProjectModalTemplate"
controller: "RenameProjectModalController"
resolve:
project: () -> project
scope: $scope
)
$scope.cloneProject = (project, cloneName) ->
event_tracking.send 'project-list-page-interaction', 'project action', 'Clone'
queuedHttp
.post("/project/#{project.id}/clone", {
_csrf: window.csrfToken
projectName: cloneName
})
.then((response) ->
{ data } = response
$scope.projects.push {
name: cloneName
id: data.project_id
accessLevel: "owner"
# TODO: Check access level if correct after adding it in
# to the rest of the app
}
$scope.updateVisibleProjects()
)
$scope.openCloneProjectModal = () ->
project = $scope.getFirstSelectedProject()
return if !project?
modalInstance = $modal.open(
templateUrl: "cloneProjectModalTemplate"
controller: "CloneProjectModalController"
resolve:
project: () -> project
scope: $scope
)
$scope.openArchiveProjectsModal = () ->
modalInstance = $modal.open(
templateUrl: "deleteProjectsModalTemplate"
controller: "DeleteProjectsModalController"
resolve:
projects: () -> $scope.getSelectedProjects()
)
event_tracking.send 'project-list-page-interaction', 'project action', 'Delete'
modalInstance.result.then () ->
$scope.archiveOrLeaveSelectedProjects()
$scope.archiveOrLeaveSelectedProjects = () ->
$scope.archiveOrLeaveProjects($scope.getSelectedProjects())
$scope.archiveOrLeaveProjects = (projects) ->
projectIds = projects.map (p) -> p.id
# Remove project from any tags
for tag in $scope.tags
$scope._removeProjectIdsFromTagArray(tag, projectIds)
for project in projects
project.tags = []
if project.accessLevel == "owner"
project.archived = true
queuedHttp {
method: "DELETE"
url: "/project/#{project.id}"
headers:
"X-CSRF-Token": window.csrfToken
}
else
$scope._removeProjectFromList project
queuedHttp {
method: "POST"
url: "/project/#{project.id}/leave"
headers:
"X-CSRF-Token": window.csrfToken
}
$scope.updateVisibleProjects()
$scope.openDeleteProjectsModal = () ->
modalInstance = $modal.open(
templateUrl: "deleteProjectsModalTemplate"
controller: "DeleteProjectsModalController"
resolve:
projects: () -> $scope.getSelectedProjects()
)
modalInstance.result.then () ->
$scope.deleteSelectedProjects()
$scope.deleteSelectedProjects = () ->
selected_projects = $scope.getSelectedProjects()
selected_project_ids = $scope.getSelectedProjectIds()
# Remove projects from array
for project in selected_projects
$scope._removeProjectFromList project
# Remove project from any tags
for tag in $scope.tags
$scope._removeProjectIdsFromTagArray(tag, selected_project_ids)
for project_id in selected_project_ids
queuedHttp {
method: "DELETE"
url: "/project/#{project_id}?forever=true"
headers:
"X-CSRF-Token": window.csrfToken
}
$scope.updateVisibleProjects()
$scope.restoreSelectedProjects = () ->
$scope.restoreProjects($scope.getSelectedProjects())
$scope.restoreProjects = (projects) ->
projectIds = projects.map (p) -> p.id
for project in projects
project.archived = false
for projectId in projectIds
queuedHttp {
method: "POST"
url: "/project/#{projectId}/restore"
headers:
"X-CSRF-Token": window.csrfToken
}
$scope.updateVisibleProjects()
$scope.openUploadProjectModal = () ->
modalInstance = $modal.open(
templateUrl: "uploadProjectModalTemplate"
controller: "UploadProjectModalController"
)
$scope.downloadSelectedProjects = () ->
$scope.downloadProjectsById($scope.getSelectedProjectIds())
$scope.downloadProjectsById = (projectIds) ->
event_tracking.send 'project-list-page-interaction', 'project action', 'Download Zip'
if projectIds.length > 1
path = "/project/download/zip?project_ids=#{projectIds.join(',')}"
else
path = "/project/#{projectIds[0]}/download/zip"
window.location = path
$scope.openV1ImportModal = (project) ->
$modal.open(
templateUrl: 'v1ImportModalTemplate'
controller: 'V1ImportModalController'
size: 'lg'
windowClass: 'v1-import-modal'
resolve:
project: () -> project
)
if storedUIOpts?.filter?
if storedUIOpts.filter == "tag" and storedUIOpts.selectedTagId?
markTagAsSelected(storedUIOpts.selectedTagId)
$scope.setFilter(storedUIOpts.filter)
else
$scope.updateVisibleProjects()
App.controller "ProjectListItemController", ($scope) ->
$scope.shouldDisableCheckbox = (project) ->
$scope.filter == 'archived' && project.accessLevel != 'owner'
$scope.projectLink = (project) ->
if project.accessLevel == 'readAndWrite' and project.source == 'token'
"/#{project.tokens.readAndWrite}"
else if project.accessLevel == 'readOnly' and project.source == 'token'
"/read/#{project.tokens.readOnly}"
else
"/project/#{project.id}"
$scope.isLinkSharingProject = (project) ->
return project.source == 'token'
$scope.ownerName = () ->
if $scope.project.accessLevel == "owner"
return "You"
else if $scope.project.owner?
return [$scope.project.owner.first_name, $scope.project.owner.last_name].filter((n) -> n?).join(" ")
else
return "None"
$scope.isOwner = () ->
window.user_id == $scope.project.owner._id
$scope.$watch "project.selected", (value) ->
if value?
$scope.updateSelectedProjects()
$scope.clone = (e) ->
e.stopPropagation()
$scope.cloneProject($scope.project, "#{$scope.project.name} (Copy)")
$scope.download = (e) ->
e.stopPropagation()
$scope.downloadProjectsById([$scope.project.id])
$scope.archiveOrLeave = (e) ->
e.stopPropagation()
$scope.archiveOrLeaveProjects([$scope.project])
$scope.restore = (e) ->
e.stopPropagation()
$scope.restoreProjects([$scope.project])