Browse Source

WIP services update

* page title service
* RPC service
Rafał Pitoń 10 years ago
parent
commit
6dd9031d5b
40 changed files with 315 additions and 202 deletions
  1. 5 5
      docs/developers/settings.rst
  2. 1 1
      misago/conf/defaults.py
  3. 1 1
      misago/conf/middleware.py
  4. 32 0
      misago/emberapp/app/adapters/application.js
  5. 2 3
      misago/emberapp/app/controllers/activation/request-link.js
  6. 5 2
      misago/emberapp/app/controllers/forgotten-password/change-form.js
  7. 1 1
      misago/emberapp/app/controllers/forgotten-password/request-link.js
  8. 0 4
      misago/emberapp/app/controllers/guest-nav.js
  9. 2 2
      misago/emberapp/app/controllers/login-modal.js
  10. 16 0
      misago/emberapp/app/initializers/rpc-service.js
  11. 10 0
      misago/emberapp/app/initializers/zxcvb-service.js
  12. 2 0
      misago/emberapp/app/router.js
  13. 1 2
      misago/emberapp/app/routes/activation/activate.js
  14. 7 0
      misago/emberapp/app/routes/application.js
  15. 1 2
      misago/emberapp/app/routes/forgotten-password/change-form.js
  16. 0 1
      misago/emberapp/app/routes/index.js
  17. 32 0
      misago/emberapp/app/routes/register.js
  18. 4 0
      misago/emberapp/app/services/page-title.js
  19. 52 0
      misago/emberapp/app/services/rpc.js
  20. 5 0
      misago/emberapp/app/services/zxcvb.js
  21. 6 6
      misago/emberapp/app/templates/activation/link-sent.hbs
  22. 1 1
      misago/emberapp/app/templates/error.hbs
  23. 6 6
      misago/emberapp/app/templates/forgotten-password/link-sent.hbs
  24. 1 1
      misago/emberapp/app/templates/guest-nav.hbs
  25. 1 0
      misago/emberapp/app/templates/register.hbs
  26. 15 0
      misago/emberapp/app/templates/register/closed.hbs
  27. 15 0
      misago/emberapp/app/templates/register/done.hbs
  28. 15 0
      misago/emberapp/app/templates/register/form.hbs
  29. 0 63
      misago/emberapp/app/utils/rpc.js
  30. 5 0
      misago/emberapp/config/environment.js
  31. 7 10
      misago/emberapp/tests/acceptance/forgotten-password-test.js
  32. 14 0
      misago/emberapp/tests/unit/routes/register-test.js
  33. 11 0
      misago/emberapp/tests/unit/services/page-title-test.js
  34. 18 0
      misago/emberapp/tests/unit/services/rpc-test.js
  35. 15 0
      misago/emberapp/tests/unit/services/zxcvb-test.js
  36. 1 5
      misago/emberapp/tests/unit/transforms/moment-date-test.js
  37. 0 79
      misago/emberapp/tests/unit/utils/rpc-test.js
  38. 1 1
      misago/emberapp/vendor/testutils/misago-preload-data.js
  39. 3 5
      misago/users/api/changepassword.py
  40. 1 1
      misago/users/migrations/0002_users_settings.py

+ 5 - 5
docs/developers/settings.rst

@@ -185,11 +185,6 @@ MISAGO_ATTACHMENTS_ROOT
 Path to directory that Misago should use to store post attachments. This directory shouldn't be accessible from outside world.
 Path to directory that Misago should use to store post attachments. This directory shouldn't be accessible from outside world.
 
 
 
 
-MISAGO_AUTH_API_URL
--------------------
-Link name to API view used to validate sign-in credentials.
-
-
 MISAGO_AVATAR_SERVER_PATH
 MISAGO_AVATAR_SERVER_PATH
 -------------------------
 -------------------------
 Url path that that all avatar server urls starts with. If you are running Misago subdirectory, make sure to update it (i.e. valid path for  "http://somesite.com/forums/" is ``/forums/user-avatar``).
 Url path that that all avatar server urls starts with. If you are running Misago subdirectory, make sure to update it (i.e. valid path for  "http://somesite.com/forums/" is ``/forums/user-avatar``).
@@ -258,6 +253,11 @@ MISAGO_HOURL_POST_LIMIT
 Hourly limit of posts that may be posted from single account. Fail-safe for situations when forum is flooded by spam bot. Change to 0 to lift this restriction.
 Hourly limit of posts that may be posted from single account. Fail-safe for situations when forum is flooded by spam bot. Change to 0 to lift this restriction.
 
 
 
 
+MISAGO_LOGIN_API_URL
+--------------------
+URL to API endpoint used to authenticate sign-in credentials. Musn't contain api prefix or wrapping slashes. Defaults to 'auth/login'.
+
+
 MISAGO_MAILER_BATCH_SIZE
 MISAGO_MAILER_BATCH_SIZE
 ------------------------
 ------------------------
 
 

+ 1 - 1
misago/conf/defaults.py

@@ -252,7 +252,7 @@ MISAGO_STOP_FORUM_SPAM_MIN_CONFIDENCE = 80
 MISAGO_MAILER_BATCH_SIZE = 20
 MISAGO_MAILER_BATCH_SIZE = 20
 
 
 # Auth paths
 # Auth paths
-MISAGO_AUTH_API_URL = 'misago:api:login'
+MISAGO_LOGIN_API_URL = 'auth/login'
 
 
 LOGIN_REDIRECT_URL = 'misago:index'
 LOGIN_REDIRECT_URL = 'misago:index'
 LOGIN_URL = 'misago:login'
 LOGIN_URL = 'misago:login'

+ 1 - 1
misago/conf/middleware.py

@@ -6,7 +6,7 @@ class PreloadConfigMiddleware(object):
     def process_request(self, request):
     def process_request(self, request):
         preloaded_settings = db_settings.get_public_settings()
         preloaded_settings = db_settings.get_public_settings()
         preloaded_settings.update({
         preloaded_settings.update({
-            'authApiUrl': reverse(settings.MISAGO_AUTH_API_URL),
+            'loginApiUrl': settings.MISAGO_LOGIN_API_URL,
 
 
             'loginRedirectUrl': reverse(settings.LOGIN_REDIRECT_URL),
             'loginRedirectUrl': reverse(settings.LOGIN_REDIRECT_URL),
             'loginUrl': reverse(settings.LOGIN_URL),
             'loginUrl': reverse(settings.LOGIN_URL),

+ 32 - 0
misago/emberapp/app/adapters/application.js

@@ -0,0 +1,32 @@
+import Ember from 'ember';
+import DRFAdapter from './drf';
+import getCsrfToken from 'misago/utils/csrf';
+
+export default DRFAdapter.extend({
+  headers: function() {
+    return {
+      'X-CSRFToken': getCsrfToken()
+    };
+  }.property().volatile(),
+
+  // Simple ajax util for RPC requests
+  // raison d'etre: because default ones are processing server responses
+  // and there isn't response standard to allow that for RPC's
+  rpcAjax: function(url, data) {
+    var adapter = this;
+
+    return new Ember.RSVP.Promise(function(resolve, reject) {
+      var hash = adapter.ajaxOptions(url, 'POST', {data: data || null});
+
+      hash.success = function(json) {
+        Ember.run(null, resolve, json);
+      };
+
+      hash.error = function(jqXHR) {
+        Ember.run(null, reject, jqXHR);
+      };
+
+      Ember.$.ajax(hash);
+    }, 'DS: MisagoAdapter#rpc-ajax POST to ' + url);
+  }
+});

+ 2 - 3
misago/emberapp/app/controllers/activation/request-link.js

@@ -1,8 +1,7 @@
 import Ember from 'ember';
 import Ember from 'ember';
-import rpc from 'misago/utils/rpc';
 
 
 export default Ember.ObjectController.extend({
 export default Ember.ObjectController.extend({
-  rpcUrl: 'activation/send-link/',
+  rpcUrl: 'activation/send-link',
   isLoading: false,
   isLoading: false,
   email: '',
   email: '',
 
 
@@ -22,7 +21,7 @@ export default Ember.ObjectController.extend({
       this.set('isLoading', true);
       this.set('isLoading', true);
 
 
       var self = this;
       var self = this;
-      rpc(this.get('rpcUrl'), {
+      this.get('rpc').ajax(this.get('rpcUrl'), {
         email: email
         email: email
       }).then(function(requestingUser) {
       }).then(function(requestingUser) {
         self.send('success', requestingUser);
         self.send('success', requestingUser);

+ 5 - 2
misago/emberapp/app/controllers/forgotten-password/change-form.js

@@ -1,10 +1,13 @@
 import Ember from 'ember';
 import Ember from 'ember';
-import rpc from 'misago/utils/rpc';
 
 
 export default Ember.ObjectController.extend({
 export default Ember.ObjectController.extend({
   isLoading: false,
   isLoading: false,
   password: '',
   password: '',
 
 
+  change_password_url: function() {
+    return 'change-password/' + this.get('user_id') + '/' + this.get('token');
+  }.property('user_id', 'token'),
+
   actions: {
   actions: {
     submit: function() {
     submit: function() {
       if (this.get('isLoading')) {
       if (this.get('isLoading')) {
@@ -21,7 +24,7 @@ export default Ember.ObjectController.extend({
       this.set('isLoading', true);
       this.set('isLoading', true);
 
 
       var self = this;
       var self = this;
-      rpc(this.get('change_password_url'), {
+      this.get('rpc').ajax(this.get('change_password_url'), {
         password: password
         password: password
       }).then(function() {
       }).then(function() {
         self.send('success');
         self.send('success');

+ 1 - 1
misago/emberapp/app/controllers/forgotten-password/request-link.js

@@ -1,5 +1,5 @@
 import RequestLinkController from 'misago/controllers/activation/request-link';
 import RequestLinkController from 'misago/controllers/activation/request-link';
 
 
 export default RequestLinkController.extend({
 export default RequestLinkController.extend({
-  rpcUrl: 'change-password/send-link/'
+  rpcUrl: 'change-password/send-link'
 });
 });

+ 0 - 4
misago/emberapp/app/controllers/guest-nav.js

@@ -1,4 +0,0 @@
-import Ember from 'ember';
-
-export default Ember.Controller.extend({
-});

+ 2 - 2
misago/emberapp/app/controllers/login-modal.js

@@ -1,5 +1,4 @@
 import Ember from 'ember';
 import Ember from 'ember';
-import rpc from 'misago/utils/rpc';
 import getCsrfToken from 'misago/utils/csrf';
 import getCsrfToken from 'misago/utils/csrf';
 
 
 export default Ember.Controller.extend({
 export default Ember.Controller.extend({
@@ -57,7 +56,7 @@ export default Ember.Controller.extend({
       }
       }
 
 
       var self = this;
       var self = this;
-      rpc(this.get('settings.authApiUrl'), credentials
+      this.get('rpc').ajax(this.get('settings.loginApiUrl'), credentials
       ).then(function() {
       ).then(function() {
         self.send('success', credentials);
         self.send('success', credentials);
       }, function(jqXHR) {
       }, function(jqXHR) {
@@ -99,6 +98,7 @@ export default Ember.Controller.extend({
       } else {
       } else {
         this.get('toast').error(rejection.detail);
         this.get('toast').error(rejection.detail);
       }
       }
+      return false;
     },
     },
 
 
     // Go-to links
     // Go-to links

+ 16 - 0
misago/emberapp/app/initializers/rpc-service.js

@@ -0,0 +1,16 @@
+import RpcService from 'misago/services/rpc';
+
+export function initialize(_container, application) {
+  application.register('service:rpc', RpcService, { singleton: true });
+
+  application.inject('service:rpc', 'store', 'store:main');
+
+  [ 'route', 'controller' ].forEach((factory) => {
+    application.inject(factory, 'rpc', 'service:rpc');
+  });
+}
+
+export default {
+  name: 'rpc-service',
+  initialize: initialize
+};

+ 10 - 0
misago/emberapp/app/initializers/zxcvb-service.js

@@ -0,0 +1,10 @@
+import ZxcvbService from 'misago/services/zxcvb';
+
+export function initialize(container, application) {
+  application.register('service:zxcvb', ZxcvbService, { singleton: true });
+}
+
+export default {
+  name: 'zxcvb-service',
+  initialize: initialize
+};

+ 2 - 0
misago/emberapp/app/router.js

@@ -14,6 +14,7 @@ Router.map(function() {
   this.route('forgotten-password', { path: 'forgotten-password/' }, function() {
   this.route('forgotten-password', { path: 'forgotten-password/' }, function() {
     this.route('change-form', { path: ':user_id/:token/' });
     this.route('change-form', { path: ':user_id/:token/' });
   });
   });
+  this.route('register', { path: 'register/' });
 
 
   // Legal
   // Legal
 
 
@@ -27,6 +28,7 @@ Router.map(function() {
   this.route('error-404', { path: 'error-404/' });
   this.route('error-404', { path: 'error-404/' });
   this.route('error-banned', { path: 'banned/:reason/' });
   this.route('error-banned', { path: 'banned/:reason/' });
   this.route('not-found', { path: '*path' });
   this.route('not-found', { path: '*path' });
+  this.route('register');
 });
 });
 
 
 export default Router;
 export default Router;

+ 1 - 2
misago/emberapp/app/routes/activation/activate.js

@@ -1,10 +1,9 @@
 import Ember from 'ember';
 import Ember from 'ember';
 import ResetScroll from 'misago/mixins/reset-scroll';
 import ResetScroll from 'misago/mixins/reset-scroll';
-import rpc from 'misago/utils/rpc';
 
 
 export default Ember.Route.extend(ResetScroll, {
 export default Ember.Route.extend(ResetScroll, {
   model: function(params) {
   model: function(params) {
-    return rpc('activation/' + params.user_id + '/' + params.token + '/validate-token/');
+    return this.get('rpc').ajax('activation/' + params.user_id + '/' + params.token + '/validate-token');
   },
   },
 
 
   afterModel: function(model) {
   afterModel: function(model) {

+ 7 - 0
misago/emberapp/app/routes/application.js

@@ -2,6 +2,13 @@ import Ember from 'ember';
 
 
 export default Ember.Route.extend({
 export default Ember.Route.extend({
   actions: {
   actions: {
+    // Loading handler
+
+    loading: function() {
+      this.get('page-title').setPlaceholderTitle();
+      return true;
+    },
+
     // Error handlers
     // Error handlers
 
 
     error: function(reason) {
     error: function(reason) {

+ 1 - 2
misago/emberapp/app/routes/forgotten-password/change-form.js

@@ -1,10 +1,9 @@
 import Ember from 'ember';
 import Ember from 'ember';
 import ResetScroll from 'misago/mixins/reset-scroll';
 import ResetScroll from 'misago/mixins/reset-scroll';
-import rpc from 'misago/utils/rpc';
 
 
 export default Ember.Route.extend(ResetScroll, {
 export default Ember.Route.extend(ResetScroll, {
   model: function(params) {
   model: function(params) {
-    return rpc('change-password/' + params.user_id + '/' + params.token + '/validate-token/');
+    return this.get('rpc').ajax('change-password/' + params.user_id + '/' + params.token + '/validate-token');
   },
   },
 
 
   actions: {
   actions: {

+ 0 - 1
misago/emberapp/app/routes/index.js

@@ -4,7 +4,6 @@ import ResetScroll from 'misago/mixins/reset-scroll';
 export default Ember.Route.extend(ResetScroll, {
 export default Ember.Route.extend(ResetScroll, {
   actions: {
   actions: {
     didTransition: function() {
     didTransition: function() {
-      console.log(this.get('page-title'));
       this.get('page-title').setIndexTitle();
       this.get('page-title').setIndexTitle();
     }
     }
   }
   }

+ 32 - 0
misago/emberapp/app/routes/register.js

@@ -0,0 +1,32 @@
+import Ember from 'ember';
+import ResetScroll from 'misago/mixins/reset-scroll';
+
+export default Ember.Route.extend(ResetScroll, {
+  stage: 'form',
+
+  isForm: Ember.computed.equal('stage', 'form'),
+  isDone: Ember.computed.equal('stage', 'done'),
+  isClosed: Ember.computed.equal('stage', 'closed'),
+
+  resolveStage: function() {
+    if (!this.get('isDone') && this.get('settings.account_activation') === 'closed') {
+      // we didn't complete prior registration and registrations aren't open
+      this.set('stage', 'closed');
+    }
+  },
+
+  stageTemplate: function() {
+    return 'register.' + this.get('stage');
+  }.property('stage'),
+
+  renderTemplate: function() {
+    this.resolveStage();
+    this.render(this.get('stageTemplate'));
+  },
+
+  actions: {
+    didTransition: function() {
+      this.get('page-title').setTitle(gettext("Register"));
+    }
+  }
+});

+ 4 - 0
misago/emberapp/app/services/page-title.js

@@ -29,6 +29,10 @@ export default Ember.Service.extend({
     document.title = complete_title;
     document.title = complete_title;
   },
   },
 
 
+  setPlaceholderTitle: function() {
+    document.title = this.get('forumName');
+  },
+
   setIndexTitle: function() {
   setIndexTitle: function() {
     document.title = this.get('indexTitle') || this.get('forumName');
     document.title = this.get('indexTitle') || this.get('forumName');
   }
   }

+ 52 - 0
misago/emberapp/app/services/rpc.js

@@ -0,0 +1,52 @@
+import Ember from 'ember';
+
+export default Ember.Object.extend({
+  ajax: function(recordOrProcedure, dataOrProcedure, data) {
+    // receive args
+    var record = null;
+    var procedure = null;
+
+    if (arguments.length === 3) {
+      record = recordOrProcedure;
+      procedure = dataOrProcedure;
+    } else {
+      procedure = recordOrProcedure;
+      data = dataOrProcedure;
+    }
+
+    // get adapter to be used for RPC
+    // note: in case of Model being null this cheats adapterFor to return
+    // 'adapter:application'. we are doing this, because for some reason
+    // store.defaultAdapter fails to return django adapter
+    var adapter = this.get('store').adapterFor(record || {typeKey: 'application'});
+
+    // build api call URL
+    var url = null;
+    if (record) {
+      url = this.buildRecordProcedureURL(adapter, record, procedure);
+    } else {
+      url = this.buildProcedureURL(adapter, procedure);
+    }
+
+    // return RPC promise
+    return adapter.rpcAjax(url, data || null);
+  },
+
+  buildRecordProcedureURL: function(adapter, record, procedure) {
+    var url = adapter.buildURL(record.typeKey, record.id, record);
+    return url + '/' + Ember.String.camelize(procedure);
+  },
+
+  buildProcedureURL: function(adapter, procedure) {
+    var url = adapter.buildURL(procedure);
+    return this.unpluralizeUrlProcedure(url, procedure);
+  },
+
+  unpluralizeUrlProcedure: function(url, procedure) {
+    // decamelize name and reverse path pluralization for type for procedure
+    var decamelized = Ember.String.decamelize(procedure);
+    var pluralized = Ember.String.pluralize(decamelized);
+
+    return url.replace(pluralized, decamelized);
+  }
+});

+ 5 - 0
misago/emberapp/app/services/zxcvb.js

@@ -0,0 +1,5 @@
+import Ember from 'ember';
+
+export default Ember.Service.extend({
+
+});

+ 6 - 6
misago/emberapp/app/templates/activation/link-sent.hbs

@@ -7,13 +7,13 @@
 
 
   <div class="container">
   <div class="container">
 
 
-  <p class="lead">
-    {{gettext "%(username)s, we have sent your activation link to %(email)s." username=username email=email}}
-  </p>
+    <p class="lead">
+      {{gettext "%(username)s, we have sent your activation link to %(email)s." username=username email=email}}
+    </p>
 
 
-  <button class="btn btn-default" {{action "retry"}}>
-    {{gettext "Try again?"}}
-  </button>
+    <button class="btn btn-default" {{action "retry"}}>
+      {{gettext "Try again?"}}
+    </button>
 
 
   </div>
   </div>
 </div>
 </div>

+ 1 - 1
misago/emberapp/app/templates/error.hbs

@@ -7,7 +7,7 @@
       </div>
       </div>
 
 
       <div class="error-message">
       <div class="error-message">
-        <p class="lead">{{gettext "Server has errored."}}</p>
+        <p class="lead">{{gettext "Application error has errored."}}</p>
       </div>
       </div>
 
 
     </div>
     </div>

+ 6 - 6
misago/emberapp/app/templates/forgotten-password/link-sent.hbs

@@ -7,13 +7,13 @@
 
 
   <div class="container">
   <div class="container">
 
 
-  <p class="lead">
-    {{gettext "%(username)s, we have sent link to your password change form to %(email)s." username=username email=email}}
-  </p>
+    <p class="lead">
+      {{gettext "%(username)s, we have sent link to your password change form to %(email)s." username=username email=email}}
+    </p>
 
 
-  <button class="btn btn-default" {{action "retry"}}>
-    {{gettext "Try again?"}}
-  </button>
+    <button class="btn btn-default" {{action "retry"}}>
+      {{gettext "Try again?"}}
+    </button>
 
 
   </div>
   </div>
 </div>
 </div>

+ 1 - 1
misago/emberapp/app/templates/guest-nav.hbs

@@ -3,7 +3,7 @@
     {{gettext "Sign in"}}
     {{gettext "Sign in"}}
   </button>
   </button>
 
 
-  <button type="button" class="btn btn-info btn-logout navbar-btn btn-sm">
+  <button type="button" class="btn btn-info btn-logout navbar-btn btn-sm" {{action "register"}}>
     {{gettext "Join now"}}
     {{gettext "Join now"}}
   </button>
   </button>
 </div>
 </div>

+ 1 - 0
misago/emberapp/app/templates/register.hbs

@@ -0,0 +1 @@
+{{outlet}}

+ 15 - 0
misago/emberapp/app/templates/register/closed.hbs

@@ -0,0 +1,15 @@
+<div class="register-closed-page">
+  <div class="page-header">
+    <div class="container">
+      <h1>{{gettext "Register account"}}</h1>
+    </div>
+  </div>
+
+  <div class="container">
+
+    <p class="lead">
+      {{gettext "New member registrations are not currently accepted."}}
+    </p>
+
+  </div>
+</div>

+ 15 - 0
misago/emberapp/app/templates/register/done.hbs

@@ -0,0 +1,15 @@
+<div class="register-done-page">
+  <div class="page-header">
+    <div class="container">
+      <h1>{{gettext "Register account"}}</h1>
+    </div>
+  </div>
+
+  <div class="container">
+
+    <p class="lead">
+      WIP "what after registration?" page.
+    </p>
+
+  </div>
+</div>

+ 15 - 0
misago/emberapp/app/templates/register/form.hbs

@@ -0,0 +1,15 @@
+<div class="register-form-page">
+  <div class="page-header">
+    <div class="container">
+      <h1>{{gettext "Register account"}}</h1>
+    </div>
+  </div>
+
+  <div class="container">
+
+    <p class="lead">
+      WIP Register form template
+    </p>
+
+  </div>
+</div>

+ 0 - 63
misago/emberapp/app/utils/rpc.js

@@ -1,63 +0,0 @@
-import Ember from 'ember';
-import getCsrfToken from 'misago/utils/csrf';
-import {startsWith, endsWith} from 'misago/utils/strings';
-import ENV from '../config/environment';
-
-export function buildUrl(procedure, config) {
-  if (typeof config === 'undefined') {
-    config = ENV.APP;
-  }
-
-  var finalUrl = config.API_HOST;
-
-  if (!startsWith(procedure, '/' + config.API_NAMESPACE + '/')) {
-    finalUrl += '/' + config.API_NAMESPACE + '/';
-  }
-
-  finalUrl += procedure;
-
-  if (config.API_ADD_TRAILING_SLASHES && !endsWith(finalUrl, '/')) {
-    finalUrl += '/';
-  }
-
-  return finalUrl;
-}
-
-export function ajax(url, data) {
-  return new Ember.RSVP.Promise(function(resolve, reject){
-    function success(json) {
-      Ember.run(null, resolve, json);
-    }
-
-    function error(jqXHR) {
-      if (jqXHR.status === 200) {
-        var data = {};
-        if (typeof jqXHR.responseJSON !== 'undefined') {
-          data = jqXHR.responseJSON;
-        }
-        Ember.run(null, resolve, data);
-      } else {
-        Ember.run(null, reject, jqXHR);
-      }
-    }
-
-    Ember.$.ajax(url, {
-      type: 'POST',
-      accepts: 'application/json',
-      contentType: 'application/json;charset=UTF-8',
-      data: JSON.stringify(data || {}),
-      dataType: 'json',
-
-      headers: {
-        'X-CSRFToken': getCsrfToken()
-      },
-
-      error: error,
-      success: success
-    });
-  }, 'RPC: ' + url);
-}
-
-export default function(procedure, data, config) {
-  return ajax(buildUrl(procedure, config), data);
-}

+ 5 - 0
misago/emberapp/config/environment.js

@@ -64,6 +64,11 @@ module.exports = function(environment) {
 
 
     ENV.APP.rootElement = '#ember-testing';
     ENV.APP.rootElement = '#ember-testing';
 
 
+    // Maintain api config
+    ENV.APP.API_HOST = '';
+    ENV.APP.API_NAMESPACE = 'api';
+    ENV.APP.API_ADD_TRAILING_SLASHES = true;
+
     // Reduce toast display times for test runner
     // Reduce toast display times for test runner
     ENV.APP.TOAST_BASE_DISPLAY_TIME = 200;
     ENV.APP.TOAST_BASE_DISPLAY_TIME = 200;
     ENV.APP.TOAST_LENGTH_FACTOR = 0;
     ENV.APP.TOAST_LENGTH_FACTOR = 0;

+ 7 - 10
misago/emberapp/tests/acceptance/forgotten-password-test.js

@@ -256,8 +256,7 @@ test('no new password is entered', function(assert) {
     status: 200,
     status: 200,
     responseText: {
     responseText: {
       'user_id': 1,
       'user_id': 1,
-      'token': 'token',
-      'change_password_url': '/api/change-password-url/'
+      'token': 'token'
     }
     }
   });
   });
 
 
@@ -278,14 +277,13 @@ test('new password is invalid', function(assert) {
     status: 200,
     status: 200,
     responseText: {
     responseText: {
       'user_id': 1,
       'user_id': 1,
-      'token': 'token',
-      'change_password_url': '/api/change-password-url/'
+      'token': 'token'
     }
     }
   });
   });
 
 
   var message = 'Entered password is not allowed.';
   var message = 'Entered password is not allowed.';
   Ember.$.mockjax({
   Ember.$.mockjax({
-    url: '/api/change-password-url/',
+    url: '/api/change-password/1/token/',
     status: 400,
     status: 400,
     responseText: {
     responseText: {
       'detail': message
       'detail': message
@@ -310,15 +308,14 @@ test('new password is accepted', function(assert) {
     status: 200,
     status: 200,
     responseText: {
     responseText: {
       'user_id': 1,
       'user_id': 1,
-      'token': 'token',
-      'change_password_url': '/api/change-password-url/'
+      'token': 'token'
     }
     }
   });
   });
 
 
-  var message = 'lul';
   Ember.$.mockjax({
   Ember.$.mockjax({
-    url: '/api/change-password-url/',
-    status: 200
+    url: '/api/change-password/1/token/',
+    status: 200,
+    responseText: {'detail': 'ok'}
   });
   });
 
 
   visit('/forgotten-password/1/token/');
   visit('/forgotten-password/1/token/');

+ 14 - 0
misago/emberapp/tests/unit/routes/register-test.js

@@ -0,0 +1,14 @@
+import {
+  moduleFor,
+  test
+} from 'ember-qunit';
+
+moduleFor('route:register', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('it exists', function(assert) {
+  var route = this.subject();
+  assert.ok(route);
+});

+ 11 - 0
misago/emberapp/tests/unit/services/page-title-test.js

@@ -39,6 +39,17 @@ test('setTitle changes document title', function(assert) {
   assert.equal(document.title, 'Test Thread (page 12) | Support | Test Forum');
   assert.equal(document.title, 'Test Thread (page 12) | Support | Test Forum');
 });
 });
 
 
+test('setPlaceholderTitle changes document title to one defined for index', function(assert) {
+  assert.expect(1);
+
+  var service = this.subject();
+  service.set('forumName', 'Placeholder Test Forum');
+
+  // no index title is set
+  service.setPlaceholderTitle();
+  assert.equal(document.title, 'Placeholder Test Forum');
+});
+
 test('setIndexTitle changes document title to one defined for index', function(assert) {
 test('setIndexTitle changes document title to one defined for index', function(assert) {
   assert.expect(2);
   assert.expect(2);
 
 

+ 18 - 0
misago/emberapp/tests/unit/services/rpc-test.js

@@ -0,0 +1,18 @@
+import {
+  moduleFor,
+  test
+} from 'ember-qunit';
+
+moduleFor('service:rpc');
+
+test('unpluralizeUrlProcedure fixes urls', function(assert) {
+  assert.expect(2);
+
+  var service = this.subject();
+
+  var url = service.unpluralizeUrlProcedure('/some-words/', 'some-word');
+  assert.equal(url, '/some-word/');
+
+  url = service.unpluralizeUrlProcedure('/model/some-words/', 'some-word');
+  assert.equal(url, '/model/some-word/');
+});

+ 15 - 0
misago/emberapp/tests/unit/services/zxcvb-test.js

@@ -0,0 +1,15 @@
+import {
+  moduleFor,
+  test
+} from 'ember-qunit';
+
+moduleFor('service:zxcvb', {
+  // Specify the other units that are required for this test.
+  // needs: ['service:foo']
+});
+
+// Replace this with your real tests.
+test('it exists', function(assert) {
+  var service = this.subject();
+  assert.ok(service);
+});

+ 1 - 5
misago/emberapp/tests/unit/transforms/moment-date-test.js

@@ -3,12 +3,8 @@ import {
   test
   test
 } from 'ember-qunit';
 } from 'ember-qunit';
 
 
-moduleFor('transform:moment-date', {
-  // Specify the other units that are required for this test.
-  // needs: ['serializer:foo']
-});
+moduleFor('transform:moment-date');
 
 
-// Replace this with your real tests.
 test('it exists', function(assert) {
 test('it exists', function(assert) {
   var transform = this.subject();
   var transform = this.subject();
   assert.ok(transform);
   assert.ok(transform);

+ 0 - 79
misago/emberapp/tests/unit/utils/rpc-test.js

@@ -1,79 +0,0 @@
-import Ember from 'ember';
-import { default as rpc, buildUrl, ajax } from '../../../utils/rpc';
-import { module, test } from 'qunit';
-
-module('RPC', {
-  afterEach: function() {
-    Ember.$.mockjax.clear();
-  }
-});
-
-test('buildUrl builds valid urls', function(assert) {
-  assert.expect(3);
-
-  assert.equal(buildUrl('some/procedure', {
-    API_HOST: '',
-    API_NAMESPACE: 'api/v2',
-    API_ADD_TRAILING_SLASHES: true
-  }), '/api/v2/some/procedure/');
-
-  assert.equal(buildUrl('some/procedure', {
-    API_HOST: '',
-    API_NAMESPACE: 'api/v2',
-    API_ADD_TRAILING_SLASHES: false
-  }), '/api/v2/some/procedure');
-
-  assert.equal(buildUrl('some/procedure', {
-    API_HOST: 'https://api.testsite.com',
-    API_NAMESPACE: 'api/v2',
-    API_ADD_TRAILING_SLASHES: false
-  }), 'https://api.testsite.com/api/v2/some/procedure');
-});
-
-var conf = {
-  API_HOST: '',
-  API_NAMESPACE: 'api',
-  API_ADD_TRAILING_SLASHES: true
-};
-
-test('successfull rpc call passes', function(assert) {
-  assert.expect(1);
-  var done = assert.async();
-
-  Ember.$.mockjax({
-    url: "/api/some-rpc/",
-    status: 200,
-    responseText: {
-      'detail': 'it works'
-    }
-  });
-
-  rpc('some-rpc', {}, conf).then(function(data) {
-    assert.equal(data.detail, 'it works');
-  }, function() {
-    assert.fail("rpc call should pass");
-  }).finally(function() {
-    done();
-  });
-});
-
-test('invalid rpc call fails', function(assert) {
-  assert.expect(1);
-  var done = assert.async();
-
-  Ember.$.mockjax({
-    url: "/api/some-rpc/",
-    status: 400,
-    responseText: {
-      'detail': 'nope'
-    }
-  });
-
-  rpc('some-rpc', {}, conf).then(function() {
-    assert.fail("rpc call should fail");
-  }, function(jqXHR) {
-    assert.equal(jqXHR.responseJSON.detail, 'nope');
-  }).finally(function() {
-    done();
-  });
-});

+ 1 - 1
misago/emberapp/vendor/testutils/misago-preload-data.js

@@ -17,7 +17,7 @@ window.MisagoData = {
     "privacy_policy_title": "Polityka prywatno\u015bci",
     "privacy_policy_title": "Polityka prywatno\u015bci",
     "privacy_policy": true,
     "privacy_policy": true,
 
 
-    "authApiUrl": "/api/auth/login/",
+    "loginApiUrl": "auth/login",
 
 
     "loginUrl": "/login/",
     "loginUrl": "/login/",
     "loginRedirectUrl": "/",
     "loginRedirectUrl": "/",

+ 3 - 5
misago/users/api/changepassword.py

@@ -81,10 +81,8 @@ def send_link(request):
 @password_api_view
 @password_api_view
 def validate_token(request, user, token):
 def validate_token(request, user, token):
     return Response({
     return Response({
-        'change_password_url': reverse('misago:api:change_password', kwargs={
-            'user_id': user.id,
-            'token': token,
-        }),
+        'user_id': user.id,
+        'token': token,
         'username': user.username
         'username': user.username
     })
     })
 
 
@@ -101,4 +99,4 @@ def change_password(request, user, token):
         return Response({'detail': e.messages[0]},
         return Response({'detail': e.messages[0]},
                         status=status.HTTP_400_BAD_REQUEST)
                         status=status.HTTP_400_BAD_REQUEST)
 
 
-    return Response()
+    return Response({'detail': 'ok'})

+ 1 - 1
misago/users/migrations/0002_users_settings.py

@@ -27,7 +27,7 @@ def create_users_settings_group(apps, schema_editor):
                             ('none', _("No activation required")),
                             ('none', _("No activation required")),
                             ('user', _("Activation Token sent to User")),
                             ('user', _("Activation Token sent to User")),
                             ('admin', _("Activation by Administrator")),
                             ('admin', _("Activation by Administrator")),
-                            ('disabled', _("Don't allow new registrations"))
+                            ('closed', _("Don't allow new registrations"))
                         )
                         )
                     },
                     },
                     'is_public': True,
                     'is_public': True,