asyncForm.js 5.4 KB

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