APIs

Show:
/**
  @module ember-flexberry
*/

import Mixin from '@ember/object/mixin';
import RSVP from 'rsvp';
import { getOwner } from '@ember/application';
import Builder from 'ember-flexberry-data/query/builder';

/**
  Mixin for {{#crossLink "EditFormRoute"}}{{/crossLink}}, which provides support locking.

  @class LockRouteMixin
  @uses <a href="https://www.emberjs.com/api/ember/release/classes/Mixin">Mixin</a>
*/
export default Mixin.create({
  /**
    @property _currentLock
    @type DS.Model
    @default null
    @private
  */
  _currentLock: null,

  /**
    @property _readonly
    @type Boolean
    @default false
    @private
  */
  _readonly: false,

  /**
    This object contains answers which will corresponding functions resolved.

    @property defaultBehaviorLock
    @type Object
    @default { openReadOnly: true, unlockObject: true }
    @for EditFormRoute
  */
  defaultBehaviorLock: {
    openReadOnly: true,
    unlockObject: true,
  },

  actions: {
    /**
      The willTransition action is fired at the beginning of any attempted transition with a Transition object as the sole argument.
      [More info](https://www.emberjs.com/api/ember/release/classes/Route/events/willTransition?anchor=willTransition).

      @method actions.willTransition
    */
    willTransition() {
      this._super(...arguments);
      this.set('_readonly', false);
      let lock = this.get('_currentLock');
      if (lock) {
        this.unlockObject().then((answer) => {
          if (answer) {
            lock.destroyRecord().then((record) => {
              // Without this next creation of lock object for this page throws an error:
              // 'modelName was saved to the server, but the response returned the new id 'id', which has already been used with another record'.
              this.store.unloadRecord(record);
              this.set('_currentLock', null);
            });
          } else {
            this.set('_currentLock', null);
          }
        });
      }
    }
  },

  /**
    This hook is the first of the route entry validation hooks called when an attempt is made to transition into a route or one of its children.
    [More info](https://www.emberjs.com/api/ember/release/classes/Route/methods/beforeModel?anchor=beforeModel).

    @method beforeModel
    @param {Transition} transition
    @return {Promise}
  */
  /* eslint-disable no-unused-vars */
  beforeModel(transition) {
    let result = this._super(...arguments);

    if (!(result instanceof RSVP.Promise)) {
      result = RSVP.resolve();
    }

    return new RSVP.Promise((resolve, reject) => {
      let params = this.paramsFor(this.routeName);
      let userService = getOwner(this).lookup('service:user');
      result.then((parentResult) => {
        if (params.id) {
          let builder = new Builder(this.store)
            .from('new-platform-flexberry-services-lock')
            .selectByProjection('LockL')
            .byId(params.id);
          this.store.queryRecord('new-platform-flexberry-services-lock', builder.build()).then((lock) => {
            if (!lock) {
              this.store.createRecord('new-platform-flexberry-services-lock', {
                id: params.id,
                userName: userService.getCurrentUserName(),
                lockDate: new Date(),
              }).save().then((lock) => {
                this.set('_currentLock', lock);
                resolve(lock);
              }).catch((reason) => {
                this.openReadOnly().then((answer) => {
                  this._openReadOnly(answer, resolve, reject, reason);
                });
              });
            } else if (lock.get('userName') === userService.getCurrentUserName()) {
              this.set('_currentLock', lock);
              resolve(lock);
            } else {
              this.openReadOnly(lock.get('userName')).then((answer) => {
                this._openReadOnly(answer, resolve, reject);
              });
            }
          }).catch((reason) => {
            reject(reason);
          });
        } else {
          resolve(parentResult);
        }
      }).catch((reason) => {
        reject(reason);
      });
    });
  },
  /* eslint-enable no-unused-vars */

  /**
    A hook you can use to setup the controller for the current route.
    [More info](https://www.emberjs.com/api/ember/release/classes/Route/methods/setupController?anchor=setupController).

    @method setupController
    @param {Controller} controller
    @param {Object} model
  */
  /* eslint-disable no-unused-vars */
  setupController(controller, model) {
    this._super(...arguments);
    if (this.get('_readonly')) {
      controller.set('readonly', true);
    }
  },
  /* eslint-enable no-unused-vars */

  /**
    This function will be called to solve open form read only or transition to parent route.
    You can override function for custom behavior.

    @example
      ```javascript
      // app/routes/user-edit.js

      import EditFormRoute from 'ember-flexberry/routes/edit-form';

      export default EditFormRoute.extend({
        ...
        openReadOnly(lockUserName) {
          return new RSVP.Promise((resolve) => {
            let answer = confirm(`This object lock user with name: '${lockUserName}'. Open read only?`);
            resolve(answer);
          });
        },
        ...
      });
      ```

    @method openReadOnly
    @param {String} lockUserName
    @return {Promise}
    @for EditFormRoute
  */
  /* eslint-disable no-unused-vars */
  openReadOnly(lockUserName) {
    return new RSVP.Promise((resolve) => {
      resolve(this.get('defaultBehaviorLock.openReadOnly'));
    });
  },
  /* eslint-enable no-unused-vars */

  /**
    This function will be called to solve unlock the object before form close.
    You can override function for custom behavior.

    @example
      ```javascript
      // app/routes/user-edit.js

      import EditFormRoute from 'ember-flexberry/routes/edit-form';

      export default EditFormRoute.extend({
        ...
        unlockObject() {
          return new RSVP.Promise((resolve) => {
            let answer = confirm(`Unlock this object?`);
            resolve(answer);
          });
        },
        ...
      });
      ```

    @method unlockObject
    @return {Promise}
    @for EditFormRoute
  */
  unlockObject() {
    return new RSVP.Promise((resolve) => {
      resolve(this.get('defaultBehaviorLock.unlockObject'));
    });
  },

  /**
    Depending on answer, open form read only or transition to parent route.

    @method _openReadOnly
    @param {Boolean} answer
    @param {function} resolve
    @param {function} reject
    @param {Object} reason
    @private
  */
  _openReadOnly(answer, resolve, reject, reason) {
    if (answer) {
      this.set('_readonly', true);
      resolve(reason);
    } else {
      this.controllerFor(this.routeName).transitionToParentRoute();
      reject();
    }
  },
});