overleaf/services/web/frontend/js/directives/asyncForm.js

190 lines
5.8 KiB
JavaScript
Raw Normal View History

import App from '../base'
import '../vendor/libs/passfield'
App.directive('asyncForm', [
'$http',
'validateCaptcha',
'validateCaptchaV3',
($http, validateCaptcha, validateCaptchaV3) => ({
controller: [
'$scope',
'$location',
function ($scope, $location) {
this.getEmail = () => $scope.email
this.getEmailFromQuery = () =>
$location.search().email || $location.search().new_email
return this
},
],
link(scope, element, attrs, ctrl) {
let response
const formName = attrs.asyncForm
scope[attrs.name].response = response = {}
scope[attrs.name].inflight = false
scope.email =
scope.email ||
scope.usersEmail ||
ctrl.getEmailFromQuery() ||
attrs.newEmail
const validateCaptchaIfEnabled = function (callback) {
scope.$applyAsync(() => {
scope[attrs.name].inflight = true
})
if (attrs.captchaActionName) {
validateCaptchaV3(attrs.captchaActionName)
}
if (attrs.captcha != null) {
validateCaptcha(callback)
} else {
callback()
}
}
const _submitRequest = function (grecaptchaResponse) {
const formData = {}
for (const data of Array.from(element.serializeArray())) {
formData[data.name] = data.value
}
if (grecaptchaResponse) {
formData['g-recaptcha-response'] = grecaptchaResponse
}
// clear the response object which may be referenced downstream
Object.keys(response).forEach(field => delete response[field])
// for asyncForm prevent automatic redirect to /login if
// authentication fails, we will handle it ourselves
const httpRequestFn = _httpRequestFn(element.attr('method'))
return httpRequestFn(element.attr('action'), formData, {
disableAutoLoginRedirect: true,
})
.then(function (httpResponse) {
const { data, headers } = httpResponse
scope[attrs.name].inflight = false
response.success = true
response.error = false
const onSuccessHandler = scope[attrs.onSuccess]
if (onSuccessHandler) {
onSuccessHandler(httpResponse)
return
}
if (data.redir) {
ga('send', 'event', formName, 'success')
return (window.location = data.redir)
} else if (data.message) {
response.message = data.message
if (data.message.type === 'error') {
response.success = false
response.error = true
return ga('send', 'event', formName, 'failure', data.message)
} else {
return ga('send', 'event', formName, 'success')
}
} else if (scope.$eval(attrs.asyncFormDownloadResponse)) {
const blob = new Blob([data], {
type: headers('Content-Type'),
})
location.href = URL.createObjectURL(blob) // Trigger file save
}
})
.catch(function (httpResponse) {
const { data, status } = httpResponse
scope[attrs.name].inflight = false
response.success = false
response.error = true
response.status = status
response.data = data
const onErrorHandler = scope[attrs.onError]
if (onErrorHandler) {
onErrorHandler(httpResponse)
return
}
let responseMessage
if (data.message && data.message.text) {
responseMessage = data.message.text
} else {
responseMessage = data.message
}
if (status === 400) {
// Bad Request
response.message = {
text:
responseMessage ||
'Invalid Request. Please correct the data and try again.',
type: 'error',
}
} else if (status === 403) {
// Forbidden
response.message = {
text:
responseMessage ||
'Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies.',
type: 'error',
}
} else if (status === 429) {
response.message = {
text:
responseMessage ||
'Too many attempts. Please wait for a while and try again.',
type: 'error',
}
} else {
response.message = {
text:
responseMessage ||
'Something went wrong talking to the server :(. Please try again.',
type: 'error',
}
}
ga('send', 'event', formName, 'failure', data.message)
})
}
const submit = () =>
validateCaptchaIfEnabled(response => _submitRequest(response))
const _httpRequestFn = (method = 'post') => {
const $HTTP_FNS = {
post: $http.post,
get: $http.get,
}
return $HTTP_FNS[method.toLowerCase()]
}
element.on('submit', function (e) {
e.preventDefault()
submit()
})
if (attrs.autoSubmit) {
submit()
}
},
}),
])
App.directive('formMessages', () => ({
restrict: 'E',
template: `\
<div class="alert" ng-class="{
'alert-danger': form.response.message.type == 'error',
'alert-success': form.response.message.type != 'error'
}" ng-show="!!form.response.message" ng-bind-html="form.response.message.text">
</div>
<div ng-transclude></div>\
`,
transclude: true,
scope: {
form: '=for',
},
}))