asyncForm.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import App from '../base'
  2. import '../vendor/libs/passfield'
  3. App.directive('asyncForm', ($http, validateCaptcha, validateCaptchaV3) => ({
  4. controller: [
  5. '$scope',
  6. '$location',
  7. function ($scope, $location) {
  8. this.getEmail = () => $scope.email
  9. this.getEmailFromQuery = () =>
  10. $location.search().email || $location.search().new_email
  11. return this
  12. },
  13. ],
  14. link(scope, element, attrs, ctrl) {
  15. let response
  16. const formName = attrs.asyncForm
  17. scope[attrs.name].response = response = {}
  18. scope[attrs.name].inflight = false
  19. scope.email =
  20. scope.email ||
  21. scope.usersEmail ||
  22. ctrl.getEmailFromQuery() ||
  23. attrs.newEmail
  24. const validateCaptchaIfEnabled = function (callback) {
  25. scope.$applyAsync(() => {
  26. scope[attrs.name].inflight = true
  27. })
  28. if (attrs.captchaActionName) {
  29. validateCaptchaV3(attrs.captchaActionName)
  30. }
  31. if (attrs.captcha != null) {
  32. validateCaptcha(callback)
  33. } else {
  34. callback()
  35. }
  36. }
  37. const _submitRequest = function (grecaptchaResponse) {
  38. const formData = {}
  39. for (const data of Array.from(element.serializeArray())) {
  40. formData[data.name] = data.value
  41. }
  42. if (grecaptchaResponse) {
  43. formData['g-recaptcha-response'] = grecaptchaResponse
  44. }
  45. // clear the response object which may be referenced downstream
  46. Object.keys(response).forEach(field => delete response[field])
  47. // for asyncForm prevent automatic redirect to /login if
  48. // authentication fails, we will handle it ourselves
  49. const httpRequestFn = _httpRequestFn(element.attr('method'))
  50. return httpRequestFn(element.attr('action'), formData, {
  51. disableAutoLoginRedirect: true,
  52. })
  53. .then(function (httpResponse) {
  54. const { data, headers } = httpResponse
  55. scope[attrs.name].inflight = false
  56. response.success = true
  57. response.error = false
  58. const onSuccessHandler = scope[attrs.onSuccess]
  59. if (onSuccessHandler) {
  60. onSuccessHandler(httpResponse)
  61. return
  62. }
  63. if (data.redir) {
  64. ga('send', 'event', formName, 'success')
  65. return (window.location = data.redir)
  66. } else if (data.message) {
  67. response.message = data.message
  68. if (data.message.type === 'error') {
  69. response.success = false
  70. response.error = true
  71. return ga('send', 'event', formName, 'failure', data.message)
  72. } else {
  73. return ga('send', 'event', formName, 'success')
  74. }
  75. } else if (scope.$eval(attrs.asyncFormDownloadResponse)) {
  76. const blob = new Blob([data], {
  77. type: headers('Content-Type'),
  78. })
  79. location.href = URL.createObjectURL(blob) // Trigger file save
  80. }
  81. })
  82. .catch(function (httpResponse) {
  83. const { data, status } = httpResponse
  84. scope[attrs.name].inflight = false
  85. response.success = false
  86. response.error = true
  87. response.status = status
  88. response.data = data
  89. const onErrorHandler = scope[attrs.onError]
  90. if (onErrorHandler) {
  91. onErrorHandler(httpResponse)
  92. return
  93. }
  94. let responseMessage
  95. if (data.message && data.message.text) {
  96. responseMessage = data.message.text
  97. } else {
  98. responseMessage = data.message
  99. }
  100. if (status === 400) {
  101. // Bad Request
  102. response.message = {
  103. text:
  104. responseMessage ||
  105. 'Invalid Request. Please correct the data and try again.',
  106. type: 'error',
  107. }
  108. } else if (status === 403) {
  109. // Forbidden
  110. response.message = {
  111. text:
  112. responseMessage ||
  113. 'Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies.',
  114. type: 'error',
  115. }
  116. } else if (status === 429) {
  117. response.message = {
  118. text:
  119. responseMessage ||
  120. 'Too many attempts. Please wait for a while and try again.',
  121. type: 'error',
  122. }
  123. } else {
  124. response.message = {
  125. text:
  126. responseMessage ||
  127. 'Something went wrong talking to the server :(. Please try again.',
  128. type: 'error',
  129. }
  130. }
  131. ga('send', 'event', formName, 'failure', data.message)
  132. })
  133. }
  134. const submit = () =>
  135. validateCaptchaIfEnabled(response => _submitRequest(response))
  136. const _httpRequestFn = (method = 'post') => {
  137. const $HTTP_FNS = {
  138. post: $http.post,
  139. get: $http.get,
  140. }
  141. return $HTTP_FNS[method.toLowerCase()]
  142. }
  143. element.on('submit', function (e) {
  144. e.preventDefault()
  145. submit()
  146. })
  147. if (attrs.autoSubmit) {
  148. submit()
  149. }
  150. },
  151. }))
  152. App.directive('formMessages', () => ({
  153. restrict: 'E',
  154. template: `\
  155. <div class="alert" ng-class="{
  156. 'alert-danger': form.response.message.type == 'error',
  157. 'alert-success': form.response.message.type != 'error'
  158. }" ng-show="!!form.response.message" ng-bind-html="form.response.message.text">
  159. </div>
  160. <div ng-transclude></div>\
  161. `,
  162. transclude: true,
  163. scope: {
  164. form: '=for',
  165. },
  166. }))