mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 06:33:55 +01:00
Merge pull request #4140 from overleaf/ae-react-share-modal
Remove Angular share modal code GitOrigin-RevId: 136b10c7b9768f2d8de13e48b16fd71947506624
This commit is contained in:
@@ -35,7 +35,6 @@ const App = angular
|
||||
'ErrorCatcher',
|
||||
'localStorage',
|
||||
'sessionStorage',
|
||||
'ngTagsInput',
|
||||
'ui.select',
|
||||
])
|
||||
.config(function ($qProvider, $httpProvider, uiSelectConfig) {
|
||||
|
||||
@@ -35,7 +35,6 @@ import SafariScrollPatcher from './ide/SafariScrollPatcher'
|
||||
import { loadServiceWorker } from './ide/pdfng/directives/serviceWorkerManager'
|
||||
import './ide/cobranding/CobrandingDataService'
|
||||
import './ide/settings/index'
|
||||
import './ide/share/index'
|
||||
import './ide/binary-files/index'
|
||||
import './ide/chat/index'
|
||||
import './ide/clone/index'
|
||||
@@ -64,6 +63,7 @@ import './main/system-messages'
|
||||
import '../../modules/modules-ide.js'
|
||||
import './shared/context/controllers/root-context-controller'
|
||||
import './features/editor-navigation-toolbar/controllers/editor-navigation-toolbar-controller'
|
||||
import './features/share-project-modal/controllers/react-share-project-modal-controller'
|
||||
import getMeta from './utils/meta'
|
||||
|
||||
App.controller(
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
import App from '../../../base'
|
||||
App.controller(
|
||||
'OwnershipTransferConfirmModalController',
|
||||
function ($scope, $window, $modalInstance, projectMembers) {
|
||||
$scope.state = {
|
||||
inflight: false,
|
||||
error: false,
|
||||
}
|
||||
|
||||
$scope.confirm = function () {
|
||||
const userId = $scope.member._id
|
||||
transferOwnership(userId)
|
||||
}
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss()
|
||||
}
|
||||
|
||||
function transferOwnership(userId) {
|
||||
$scope.state.inflight = true
|
||||
$scope.state.error = false
|
||||
projectMembers
|
||||
.transferOwnership(userId)
|
||||
.then(() => {
|
||||
$scope.state.inflight = false
|
||||
$scope.state.error = false
|
||||
$window.location.reload()
|
||||
})
|
||||
.catch(() => {
|
||||
$scope.state.inflight = false
|
||||
$scope.state.error = true
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,55 +0,0 @@
|
||||
import App from '../../../base'
|
||||
App.controller('ShareController', function (
|
||||
$scope,
|
||||
$modal,
|
||||
ide,
|
||||
projectInvites,
|
||||
projectMembers,
|
||||
// eslint-disable-next-line camelcase
|
||||
eventTracking
|
||||
) {
|
||||
$scope.openShareProjectModal = function (isAdmin) {
|
||||
$scope.isAdmin = isAdmin
|
||||
eventTracking.sendMBOnce('ide-open-share-modal-once')
|
||||
|
||||
$modal.open({
|
||||
templateUrl: 'shareProjectModalTemplate',
|
||||
controller: 'ShareProjectModalController',
|
||||
scope: $scope,
|
||||
})
|
||||
}
|
||||
|
||||
ide.socket.on('project:tokens:changed', data => {
|
||||
if (data.tokens != null) {
|
||||
ide.$scope.project.tokens = data.tokens
|
||||
$scope.$digest()
|
||||
}
|
||||
})
|
||||
|
||||
ide.socket.on('project:membership:changed', data => {
|
||||
if (data.members) {
|
||||
projectMembers
|
||||
.getMembers()
|
||||
.then(response => {
|
||||
if (response.data.members) {
|
||||
$scope.project.members = response.data.members
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.error('Error fetching members for project')
|
||||
})
|
||||
}
|
||||
if (data.invites) {
|
||||
projectInvites
|
||||
.getInvites()
|
||||
.then(response => {
|
||||
if (response.data.invites) {
|
||||
$scope.project.invites = response.data.invites
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.error('Error fetching invites for project')
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,309 +0,0 @@
|
||||
import _ from 'lodash'
|
||||
import App from '../../../base'
|
||||
App.controller('ShareProjectModalController', function (
|
||||
$scope,
|
||||
$modalInstance,
|
||||
$timeout,
|
||||
projectMembers,
|
||||
projectInvites,
|
||||
$modal,
|
||||
$http,
|
||||
ide,
|
||||
validateCaptcha,
|
||||
validateCaptchaV3,
|
||||
settings,
|
||||
// eslint-disable-next-line camelcase
|
||||
eventTracking
|
||||
) {
|
||||
$scope.inputs = {
|
||||
privileges: 'readAndWrite',
|
||||
contacts: [],
|
||||
}
|
||||
$scope.state = {
|
||||
error: null,
|
||||
errorReason: null,
|
||||
inflight: false,
|
||||
startedFreeTrial: false,
|
||||
invites: [],
|
||||
}
|
||||
|
||||
$modalInstance.opened.then(() =>
|
||||
$timeout(() => $scope.$broadcast('open'), 200)
|
||||
)
|
||||
|
||||
const INFINITE_COLLABORATORS = -1
|
||||
|
||||
$scope.refreshCanAddCollaborators = function () {
|
||||
const allowedNoOfMembers = $scope.project.features.collaborators
|
||||
$scope.canAddCollaborators =
|
||||
$scope.project.members.length + $scope.project.invites.length <
|
||||
allowedNoOfMembers || allowedNoOfMembers === INFINITE_COLLABORATORS
|
||||
}
|
||||
$scope.refreshCanAddCollaborators()
|
||||
|
||||
$scope.$watch('canAddCollaborators', function () {
|
||||
if (!$scope.canAddCollaborators) {
|
||||
eventTracking.send(
|
||||
'subscription-funnel',
|
||||
'editor-click-feature',
|
||||
'projectMembers'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
$scope.$watch(
|
||||
'(project.members.length + project.invites.length)',
|
||||
_noOfMembers => $scope.refreshCanAddCollaborators()
|
||||
)
|
||||
|
||||
$scope.autocompleteContacts = []
|
||||
if ($scope.isRestrictedTokenMember) {
|
||||
// Restricted token members are users who join via a read-only link.
|
||||
// They will not be able to invite any users, so skip the lookup of
|
||||
// their contacts. This request would result in a 403 for anonymous
|
||||
// users, which in turn would redirect them to the /login.
|
||||
} else {
|
||||
$http.get('/user/contacts').then(processContactsResponse)
|
||||
}
|
||||
|
||||
function processContactsResponse(response) {
|
||||
const { data } = response
|
||||
$scope.autocompleteContacts = data.contacts || []
|
||||
for (const contact of $scope.autocompleteContacts) {
|
||||
if (contact.type === 'user') {
|
||||
if (
|
||||
contact.first_name === contact.email.split('@')[0] &&
|
||||
!contact.last_name
|
||||
) {
|
||||
// User has not set their proper name so use email as canonical display property
|
||||
contact.display = contact.email
|
||||
} else {
|
||||
contact.name = `${contact.first_name} ${contact.last_name}`
|
||||
contact.display = `${contact.name} <${contact.email}>`
|
||||
}
|
||||
} else {
|
||||
// Must be a group
|
||||
contact.display = contact.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getCurrentMemberEmails = () =>
|
||||
($scope.project.members || []).map(u => u.email)
|
||||
|
||||
const getCurrentInviteEmails = () =>
|
||||
($scope.project.invites || []).map(u => u.email)
|
||||
|
||||
$scope.filterAutocompleteUsers = function ($query) {
|
||||
const currentMemberEmails = getCurrentMemberEmails()
|
||||
return $scope.autocompleteContacts.filter(function (contact) {
|
||||
if (
|
||||
contact.email != null &&
|
||||
currentMemberEmails.includes(contact.email)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
for (const text of [contact.name, contact.email]) {
|
||||
if (
|
||||
text != null &&
|
||||
text.toLowerCase().indexOf($query.toLowerCase()) > -1
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
$scope.addMembers = function () {
|
||||
const addMembers = function () {
|
||||
if ($scope.inputs.contacts.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const members = $scope.inputs.contacts
|
||||
$scope.inputs.contacts = []
|
||||
$scope.clearError()
|
||||
$scope.state.inflight = true
|
||||
|
||||
if ($scope.project.invites == null) {
|
||||
$scope.project.invites = []
|
||||
}
|
||||
|
||||
const currentMemberEmails = getCurrentMemberEmails()
|
||||
const currentInviteEmails = getCurrentInviteEmails()
|
||||
addNextMember()
|
||||
|
||||
function addNextMember() {
|
||||
let email
|
||||
if (members.length === 0 || !$scope.canAddCollaborators) {
|
||||
$scope.state.inflight = false
|
||||
$scope.$apply()
|
||||
return
|
||||
}
|
||||
|
||||
const member = members.shift()
|
||||
if (member.type === 'user') {
|
||||
email = member.email
|
||||
} else {
|
||||
// Not an auto-complete object, so email == display
|
||||
email = member.display
|
||||
}
|
||||
email = email.toLowerCase()
|
||||
|
||||
if (currentMemberEmails.includes(email)) {
|
||||
// Skip this existing member
|
||||
return addNextMember()
|
||||
}
|
||||
// do v3 captcha to collect data only
|
||||
validateCaptchaV3('invite')
|
||||
// do v2 captcha
|
||||
const ExposedSettings = window.ExposedSettings
|
||||
validateCaptcha(function (response) {
|
||||
$scope.grecaptchaResponse = response
|
||||
const invites = $scope.project.invites || []
|
||||
const invite = _.find(invites, invite => invite.email === email)
|
||||
let request
|
||||
if (currentInviteEmails.includes(email) && invite) {
|
||||
request = projectInvites.resendInvite(invite._id)
|
||||
} else {
|
||||
request = projectInvites.sendInvite(
|
||||
email,
|
||||
$scope.inputs.privileges,
|
||||
$scope.grecaptchaResponse
|
||||
)
|
||||
}
|
||||
|
||||
request
|
||||
.then(function (response) {
|
||||
const { data } = response
|
||||
if (data.error) {
|
||||
$scope.setError(data.error)
|
||||
$scope.state.inflight = false
|
||||
} else {
|
||||
if (data.invite) {
|
||||
const { invite } = data
|
||||
$scope.project.invites.push(invite)
|
||||
} else {
|
||||
const users =
|
||||
data.users != null
|
||||
? data.users
|
||||
: data.user != null
|
||||
? [data.user]
|
||||
: []
|
||||
$scope.project.members.push(...users)
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
() =>
|
||||
// Give $scope a chance to update $scope.canAddCollaborators
|
||||
// with new collaborator information.
|
||||
addNextMember(),
|
||||
|
||||
0
|
||||
)
|
||||
})
|
||||
.catch(function (httpResponse) {
|
||||
const { data } = httpResponse
|
||||
$scope.state.inflight = false
|
||||
$scope.setError(data.errorReason)
|
||||
})
|
||||
}, ExposedSettings.recaptchaDisabled.invite)
|
||||
}
|
||||
}
|
||||
|
||||
$timeout(addMembers, 50) // Give email list a chance to update
|
||||
}
|
||||
|
||||
$scope.removeMember = function (member) {
|
||||
$scope.monitorRequest(
|
||||
projectMembers.removeMember(member).then(function () {
|
||||
const index = $scope.project.members.indexOf(member)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
$scope.project.members.splice(index, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
$scope.revokeInvite = function (invite) {
|
||||
$scope.monitorRequest(
|
||||
projectInvites.revokeInvite(invite._id).then(function () {
|
||||
const index = $scope.project.invites.indexOf(invite)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
$scope.project.invites.splice(index, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
$scope.resendInvite = function (invite, event) {
|
||||
$scope.monitorRequest(
|
||||
projectInvites
|
||||
.resendInvite(invite._id)
|
||||
.then(function () {
|
||||
event.target.blur()
|
||||
})
|
||||
.catch(function () {
|
||||
event.target.blur()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
$scope.makeTokenBased = function () {
|
||||
$scope.project.publicAccesLevel = 'tokenBased'
|
||||
settings.saveProjectAdminSettings({ publicAccessLevel: 'tokenBased' })
|
||||
eventTracking.sendMB('project-make-token-based')
|
||||
}
|
||||
|
||||
$scope.makePrivate = function () {
|
||||
$scope.project.publicAccesLevel = 'private'
|
||||
settings.saveProjectAdminSettings({ publicAccessLevel: 'private' })
|
||||
}
|
||||
|
||||
$scope.$watch('project.tokens.readAndWrite', function (token) {
|
||||
if (token != null) {
|
||||
$scope.readAndWriteTokenLink = `${location.origin}/${token}`
|
||||
} else {
|
||||
$scope.readAndWriteTokenLink = null
|
||||
}
|
||||
})
|
||||
|
||||
$scope.$watch('project.tokens.readOnly', function (token) {
|
||||
if (token != null) {
|
||||
$scope.readOnlyTokenLink = `${location.origin}/read/${token}`
|
||||
} else {
|
||||
$scope.readOnlyTokenLink = null
|
||||
}
|
||||
})
|
||||
|
||||
$scope.done = () => $modalInstance.close()
|
||||
|
||||
$scope.cancel = () => $modalInstance.dismiss()
|
||||
|
||||
$scope.monitorRequest = function monitorRequest(request) {
|
||||
$scope.clearError()
|
||||
$scope.state.inflight = true
|
||||
return request
|
||||
.then(() => {
|
||||
$scope.state.inflight = false
|
||||
$scope.clearError()
|
||||
})
|
||||
.catch(err => {
|
||||
$scope.state.inflight = false
|
||||
$scope.setError(err.data && err.data.error)
|
||||
})
|
||||
}
|
||||
|
||||
$scope.clearError = function clearError() {
|
||||
$scope.state.error = false
|
||||
}
|
||||
|
||||
$scope.setError = function setError(reason) {
|
||||
$scope.state.error = true
|
||||
$scope.state.errorReason = reason
|
||||
}
|
||||
})
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
import App from '../../../base'
|
||||
App.controller(
|
||||
'ShareProjectModalMemberRowController',
|
||||
function ($scope, $modal, projectMembers) {
|
||||
$scope.form = {
|
||||
privileges: $scope.member.privileges,
|
||||
|
||||
isModified() {
|
||||
return this.privileges !== $scope.member.privileges
|
||||
},
|
||||
|
||||
submit() {
|
||||
const userId = $scope.member._id
|
||||
const privilegeLevel = $scope.form.privileges
|
||||
if (privilegeLevel === 'owner') {
|
||||
openOwnershipTransferConfirmModal(userId)
|
||||
} else {
|
||||
setPrivilegeLevel(userId, privilegeLevel)
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
this.privileges = $scope.member.privileges
|
||||
$scope.clearError()
|
||||
},
|
||||
}
|
||||
|
||||
function setPrivilegeLevel(userId, privilegeLevel) {
|
||||
$scope.monitorRequest(
|
||||
projectMembers
|
||||
.setMemberPrivilegeLevel(userId, privilegeLevel)
|
||||
.then(() => {
|
||||
$scope.member.privileges = privilegeLevel
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function openOwnershipTransferConfirmModal(userId) {
|
||||
$modal.open({
|
||||
templateUrl: 'ownershipTransferConfirmTemplate',
|
||||
controller: 'OwnershipTransferConfirmModalController',
|
||||
scope: $scope,
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
import './controllers/ShareController'
|
||||
import './controllers/ShareProjectModalController'
|
||||
import './controllers/ShareProjectModalMemberRowController'
|
||||
import './controllers/OwnershipTransferConfirmModalController'
|
||||
import './services/projectMembers'
|
||||
import './services/projectInvites'
|
||||
import '../../features/share-project-modal/controllers/react-share-project-modal-controller'
|
||||
@@ -1,37 +0,0 @@
|
||||
import App from '../../../base'
|
||||
|
||||
export default App.factory('projectInvites', (ide, $http) => ({
|
||||
sendInvite(email, privileges, grecaptchaResponse) {
|
||||
return $http.post(`/project/${ide.project_id}/invite`, {
|
||||
email,
|
||||
privileges,
|
||||
_csrf: window.csrfToken,
|
||||
'g-recaptcha-response': grecaptchaResponse,
|
||||
})
|
||||
},
|
||||
|
||||
revokeInvite(inviteId) {
|
||||
return $http({
|
||||
url: `/project/${ide.project_id}/invite/${inviteId}`,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-Csrf-Token': window.csrfToken,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
resendInvite(inviteId, privileges) {
|
||||
return $http.post(`/project/${ide.project_id}/invite/${inviteId}/resend`, {
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
},
|
||||
|
||||
getInvites() {
|
||||
return $http.get(`/project/${ide.project_id}/invites`, {
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Csrf-Token': window.csrfToken,
|
||||
},
|
||||
})
|
||||
},
|
||||
}))
|
||||
@@ -1,48 +0,0 @@
|
||||
import App from '../../../base'
|
||||
App.factory('projectMembers', (ide, $http) => ({
|
||||
removeMember(member) {
|
||||
return $http({
|
||||
url: `/project/${ide.project_id}/users/${member._id}`,
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-Csrf-Token': window.csrfToken,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
addGroup(groupId, privileges) {
|
||||
return $http.post(`/project/${ide.project_id}/group`, {
|
||||
group_id: groupId,
|
||||
privileges,
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
},
|
||||
|
||||
getMembers() {
|
||||
return $http.get(`/project/${ide.project_id}/members`, {
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Csrf-Token': window.csrfToken,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
setMemberPrivilegeLevel(userId, privilegeLevel) {
|
||||
return $http.put(
|
||||
`/project/${ide.project_id}/users/${userId}`,
|
||||
{ privilegeLevel },
|
||||
{
|
||||
headers: {
|
||||
'X-Csrf-Token': window.csrfToken,
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
transferOwnership(userId) {
|
||||
return $http.post(`/project/${ide.project_id}/transfer-ownership`, {
|
||||
user_id: userId,
|
||||
_csrf: window.csrfToken,
|
||||
})
|
||||
},
|
||||
}))
|
||||
@@ -8,7 +8,6 @@ import 'libs/ng-context-menu-0.1.4'
|
||||
import 'libs/jquery.storage'
|
||||
import 'libs/angular-cookie'
|
||||
import 'libs/passfield'
|
||||
import 'libs/ng-tags-input-3.0.0'
|
||||
import 'libs/select/select'
|
||||
|
||||
// CSS
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user