register-form.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import Ember from 'ember';
  2. export default Ember.Component.extend({
  3. tagName: 'form',
  4. classNames: 'form-horizontal',
  5. zxcvbn: Ember.inject.service('zxcvbn'),
  6. isReady: false,
  7. isErrored: false,
  8. isLoading: false,
  9. username: '',
  10. email: '',
  11. password: '',
  12. validation: null,
  13. setValidation: function() {
  14. this.set('validation', Ember.Object.create({}));
  15. }.on('init'),
  16. router: function() {
  17. return this.container.lookup('router:main');
  18. }.property(),
  19. passwordScore: function() {
  20. return this.get('zxcvbn').scorePassword(this.get('password'), [
  21. this.get('username'), this.get('email')
  22. ]);
  23. }.property('password', 'username', 'email'),
  24. passwordHelpText: function() {
  25. if (Ember.$.trim(this.get('password')).length === 0) {
  26. return null;
  27. }
  28. return [
  29. gettext('Password is very weak.'),
  30. gettext('Password is weak.'),
  31. gettext('Password is average.'),
  32. gettext('Password is strong.'),
  33. gettext('Password is very strong.')
  34. ][this.get('passwordScore')];
  35. }.property('password', 'username', 'email', 'passwordScore'),
  36. passwordBarWidth: function() {
  37. this.$('.progress-bar').css('width', ((this.get('passwordScore') + 1) * 20) + '%');
  38. }.observes('password', 'username', 'email', 'passwordScore'),
  39. passwordBarClass: function() {
  40. return 'progress-bar-' + [
  41. 'danger',
  42. 'warning',
  43. 'warning',
  44. 'primary',
  45. 'success'
  46. ][this.get('passwordScore')];
  47. }.property('password', 'username', 'email', 'passwordScore'),
  48. loadServices: function() {
  49. var self = this;
  50. var promises = [
  51. this.get('zxcvbn').load(),
  52. this.get('captcha').load()
  53. ];
  54. Ember.RSVP.allSettled(promises).then(function(array) {
  55. if (self.isDestroyed) { return; }
  56. if (array[0].state === 'rejected') {
  57. self.set('isErrored', true);
  58. console.log('zxcvbn service failed to load.');
  59. }
  60. if (array[1].state === 'rejected') {
  61. self.set('isErrored', true);
  62. console.log('captcha service failed to load.');
  63. }
  64. self.set('isReady', !self.get('isErrored'));
  65. self.get('captcha').reset();
  66. });
  67. }.on('didInsertElement'),
  68. usernameValidation: function() {
  69. var state = true;
  70. var value = Ember.$.trim(this.get('username'));
  71. var valueLength = value.length;
  72. var limit = 0;
  73. var message = null;
  74. if (valueLength < this.get('settings.username_length_min')) {
  75. limit = this.get('settings.username_length_min');
  76. message = ngettext('Username must be at least one character long.',
  77. 'Username must be at least %(limit)s characters long.',
  78. limit);
  79. state = [interpolate(message, {limit: limit}, true)];
  80. } else if (valueLength > this.get('settings.username_length_max')) {
  81. limit = this.get('settings.username_length_max');
  82. message = ngettext('Username cannot be longer than one characters.',
  83. 'Username cannot be longer than %(limit)s characters.',
  84. limit);
  85. state = [interpolate(message, {limit: limit}, true)];
  86. } else if (!new RegExp('^[0-9a-z]+$', 'i').test(value)) {
  87. state = [gettext('Username can only contain latin alphabet letters and digits.')];
  88. }
  89. if (state === true) {
  90. this.set('validation.username', 'ok');
  91. } else {
  92. this.set('validation.username', state);
  93. }
  94. }.observes('username'),
  95. emailValidation: function() {
  96. var state = true;
  97. var value = Ember.$.trim(this.get('email'));
  98. var valueLength = value.length;
  99. var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
  100. if (valueLength < 4) {
  101. state = [gettext('Enter valid e-mail address')];
  102. } else if (!re.test(value)) {
  103. state = [gettext('Invalid e-mail address.')];
  104. }
  105. if (state === true) {
  106. this.set('validation.email', 'ok');
  107. } else {
  108. this.set('validation.email', state);
  109. }
  110. }.observes('email'),
  111. passwordValidation: function() {
  112. var state = true;
  113. var valueLength = Ember.$.trim(this.get('password')).length;
  114. var limit = this.get('settings.password_length_min');
  115. if (valueLength < limit) {
  116. var message = ngettext('Valid password must be at least one character long.',
  117. 'Valid password must be at least %(limit)s characters long.',
  118. limit);
  119. state = [interpolate(message, {limit: limit}, true)];
  120. }
  121. if (state === true) {
  122. this.set('validation.password', 'ok');
  123. } else {
  124. this.set('validation.password', state);
  125. }
  126. }.observes('password'),
  127. captchaValidation: function() {
  128. this.set('validation.captcha', undefined);
  129. }.observes('captcha.value'),
  130. submit: function() {
  131. if (this.get('isLoading')) {
  132. return false;
  133. }
  134. var data = {
  135. username: Ember.$.trim(this.get('username')),
  136. email: Ember.$.trim(this.get('email')),
  137. password: Ember.$.trim(this.get('password')),
  138. captcha: Ember.$.trim(this.get('captcha.value'))
  139. };
  140. var lengths = [data.username.length, data.email.length, data.password.length];
  141. if (lengths.indexOf(0) !== -1) {
  142. this.toast.error(gettext("Fill out all fields."));
  143. return false;
  144. }
  145. if (this.$('.has-error').length) {
  146. this.toast.error(gettext("Form contains errors."));
  147. return false;
  148. }
  149. this.set('isLoading', true);
  150. var self = this;
  151. this.ajax.post('users', data
  152. ).then(function(response) {
  153. if (self.isDestroyed) { return; }
  154. self.success(response);
  155. }, function(jqXHR) {
  156. if (self.isDestroyed) { return; }
  157. self.error(jqXHR);
  158. }).finally(function() {
  159. if (self.isDestroyed) { return; }
  160. self.set('isLoading', false);
  161. });
  162. return false;
  163. },
  164. success: function(responseJSON) {
  165. this.get('captcha').reset();
  166. var model = Ember.Object.create(responseJSON);
  167. model.set('isActive', model.get('activation') === 'active');
  168. model.set('needsAdminActivation', model.get('activation') === 'activation_by_admin');
  169. this.modal.render('register.done', model);
  170. },
  171. error: function(jqXHR) {
  172. var rejection = jqXHR.responseJSON;
  173. if (jqXHR.status === 400) {
  174. this.toast.error(gettext("Form contains errors."));
  175. this.get('validation').setProperties(jqXHR.responseJSON);
  176. if (!this.get('validation.captcha')) {
  177. this.get('captcha').pass();
  178. } else {
  179. this.get('captcha').refresh();
  180. }
  181. } else if (jqXHR.status === 403 && typeof rejection.ban !== 'undefined') {
  182. this.get('router').intermediateTransitionTo('error-banned', rejection.ban);
  183. this.modal.hide();
  184. } else {
  185. this.toast.apiError(jqXHR);
  186. }
  187. },
  188. showTermsFootnote: function() {
  189. return this.get('settings.terms_of_service') || this.get('settings.terms_of_service_link');
  190. }.property('settings')
  191. });