API Docs for: 1.0.0
Show:

File: Resources/public/js/views/services/ez-contenteditviewservice.js

/*
 * Copyright (C) eZ Systems AS. All rights reserved.
 * For full copyright and license information view LICENSE file distributed with this source code.
 */
YUI.add('ez-contenteditviewservice', function (Y) {
    'use strict';
    /**
     * Provides the view service component for the content edit view
     *
     * @module ez-contenteditviewservice
     */
    Y.namespace('eZ');

    var getNewUser = function () {
            return new Y.eZ.User();
        };

    /**
     * Content edit view service.
     *
     * Loads the models needed by the content edit view
     *
     * @namespace eZ
     * @class ContentEditViewService
     * @constructor
     * @extends eZ.ViewService
     */
    Y.eZ.ContentEditViewService = Y.Base.create('contentEditViewService', Y.eZ.ViewService, [], {
        initializer: function () {
            this.after('*:requestChange', function () {
                this._setLanguageCode();
                this._setBaseLanguageCode();
            });
            this.on('*:changeLanguage', this._selectLanguage);

            this.after('*:closeView', this._redirectAfterClose);
            this.after('discardedDraft', this._redirectAfterDiscard);
            this.after('publishedDraft', this._redirectAfterPublish);

            this._setLanguageCode();
            this._setBaseLanguageCode();
        },

        /**
         * Loads the content, the main location, the content type and the owner
         * of the currently edited content, after that it sets version fields
         *
         * @method _load
         * @protected
         * @param {Function} next
         */
        _load: function (next) {
            var request = this.get('request'),
                service = this,
                languageCode = this.get('languageCode'),
                baseLanguageCode = this.get('baseLanguageCode'),
                endVersionLoading;

            this.get('version').reset();
            this._loadContentInfo(request.params.id, function () {
                var resources = service.get('contentInfo').get('resources'),
                    tasks = new Y.Parallel(),
                    content = service.get('content');

                service._loadOwner(resources.Owner, tasks.add());
                if ( resources.MainLocation ) {
                    service._loadLocation(resources.MainLocation, tasks.add());
                }
                service._loadContentType(resources.ContentType, tasks.add());

                if ( baseLanguageCode ) {
                    service._loadContent(request.params.id, baseLanguageCode, tasks.add());
                } else {
                    content.set('id', request.params.id);
                    content.load({api: service.get('capi'), languageCode: languageCode}, tasks.add(function (error) {
                        if ( error ) {
                            // this is the first time we translate the content
                            // into languageCode, so it's not really an error,
                            // in this case, the content is initialized from the
                            // contentInfo.
                            content.setAttrs(service.get('contentInfo').getAttrs());
                        }
                    }));
                }
                if ( request.params.versionId ) {
                    endVersionLoading = tasks.add();
                    service._loadVersion(request.params.versionId, languageCode, function () {
                        if ( service._canEditVersion() ) {
                            endVersionLoading();
                        }
                    });
                }

                tasks.done(function () {
                    if ( baseLanguageCode && !content.hasTranslation(baseLanguageCode) ) {
                        // this can happen if the content is always
                        // available and the user manipulated with URI to pass
                        // any language code.
                        service._error(
                            Y.eZ.trans('failed.loading.content.id.language', {
                                contentId: request.params.id,
                                languageCode: baseLanguageCode,
                            }, 'contentedit')
                        );
                        return;
                    }
                    if ( !request.params.versionId ) {
                        service._setVersionFields();
                    }
                    next(service);
                });
            });
        },

        /**
         * Checks whether the version can be edited. A version can be edited if:
         *   - it's a draft
         *   - it's a version of the content which id is passed in the request
         *   - it's translated into the language code passed in the request
         *   - it was created by the currently logged in user
         *
         * @method _canEditVersion
         * @protected
         * @return {Boolean}
         */
        _canEditVersion: function () {
            var version = this.get('version'),
                versionId = version.get('id'),
                contentId = this.get('contentInfo').get('id');

            if ( !version.isDraft() ) {
                this._error(Y.eZ.trans('version.not.a.draft', {versionId: versionId}, 'contentedit'));
                return false;
            }
            if ( version.get('resources').Content !== contentId ) {
                this._error(Y.eZ.trans('version.not.a.draft.of.content', {versionId: versionId, contentId: contentId}, 'contentedit'));
                return false;
            }
            if ( !version.hasTranslation(this.get('languageCode')) ) {
                this._error(Y.eZ.trans('version.not.exist.in', {versionId: versionId, languageCode: this.get('languageCode')}, 'contentedit'));
                return false;
            }
            if ( !version.createdBy(this.get('app').get('user')) ) {
                this._error(Y.eZ.trans('version.not.belong.to.you', {versionId: versionId}, 'contentedit'));
                return false;
            }
            return true;
        },

        /**
         * Sets fields of edited version
         *
         * @method _setVersionFields
         * @private
         */
        _setVersionFields: function () {
            this.get('version').setFieldsIn(this._getFieldsForEdit(), this.get('languageCode'));
        },

        /**
         * Returns the fields for the newly created version. Depending on the
         * loaded content, it creates the fields from the content type or from
         * the content.
         *
         * @method _getFieldsForEdit
         * @private
         * @return {Object}
         */
        _getFieldsForEdit: function () {
            var languageCode = this.get('baseLanguageCode') || this.get('languageCode'),
                languageFields = this.get('content').getFieldsIn(languageCode),
                fields;

            if ( Y.Object.isEmpty(languageFields) ) {
                fields = this._getDefaultFields(languageCode);
            } else {
                fields = Y.clone(languageFields);
            }

            return fields;
        },

        /**
         * Returns collection of default fields from the ContentType of edited
         * content and sets for them given languageCode
         *
         * @method _getDefaultFields
         * @private
         * @param {String} languageCode
         * @return {Object}
         */
        _getDefaultFields: function (languageCode) {
            var contentType = this.get('contentType'),
                defaultFields = {};

            Y.Object.each(contentType.get('fieldDefinitions'), function (fieldDef, identifier) {
                defaultFields[identifier] = {
                    fieldDefinitionIdentifier: identifier,
                    fieldValue: fieldDef.defaultValue,
                    languageCode: languageCode
                };
            });

            return defaultFields;
        },

        /**
         * Loads a content by its id and language code
         *
         * @method _loadContent
         * @protected
         * @param {String} id
         * @param {String} languageCode
         * @param {Function} callback
         */
        _loadContent: function (id, languageCode, callback) {
            this._loadModel(
                'content',
                id,
                {languageCode: languageCode},
                Y.eZ.trans(
                    'failed.loading.content.in',
                    {contentId: id, languageCode: languageCode}, 'contentedit'
                ),
                callback
            );
         },

        /**
         * Loads a version by its id and language code
         *
         * @method _loadVersion
         * @protected
         * @param {String} id
         * @param {String} languageCode
         * @param {Function} callback
         */
        _loadVersion: function (id, languageCode, callback) {
            this._loadModel(
                'version',
                id,
                {languageCode: languageCode},
                Y.eZ.trans(
                    'failed.loading.version.in',
                    {versionId: id, languageCode: languageCode}, 'contentedit'
                ),
                callback
            );
        },

        /**
         * Loads a content info by its id
         *
         * @method _loadContentInfo
         * @protected
         * @param {String} id
         * @param {Function} callback
         */
        _loadContentInfo: function (id, callback) {
            this._loadModel(
                'contentInfo', id, {},
                Y.eZ.trans(
                    'failed.finding.content',
                    {id: id}, 'contentedit'
                ),
                callback
            );
        },


        /**
         * Loads a content type by its id
         *
         * @method _loadContentType
         * @protected
         * @param {String} id
         * @param {Function} callback
         */
        _loadContentType: function (id, callback) {
            this._loadModel(
                'contentType', id, {},
                Y.eZ.trans(
                    'failed.loading.content.type',
                    {id: id}, 'contentedit'
                ),
                callback
            );
        },

        /**
         * Loads a location type by its id
         *
         * @method _loadLocation
         * @protected
         * @param {String} id
         * @param {Function} callback
         */
        _loadLocation: function (id, callback) {
            this._loadModel(
                'location', id, {},
                Y.eZ.trans(
                    'failed.loading.location',
                    {id: id}, 'contentedit'
                ),
                callback
            );
        },

        /**
         * Loads a user by its id
         *
         * @method _loadOwner
         * @protected
         * @param {String} id
         * @param {Function} callback
         */
        _loadOwner: function (id, callback) {
            var owner = this.get('owner'),
                loadOptions = {api: this.get('capi')};

            if ( !owner ) {
                owner = getNewUser();
                this.set('owner', owner);
            }
            owner.set('id', id);
            owner.load(loadOptions, Y.bind(function (error) {
                if (error) {
                    this.set('owner', null);
                }
                callback();
            }, this));
        },

        /**
         * Utility method to load a model by its id in a given attribute
         *
         * @method _loadModel
         * @protected
         * @param {String} attr
         * @param {String} id
         * @param {Object} options
         * @param {String} errorMsg
         * @param {Function} callback
         */
        _loadModel: function (attr, modelId, options, errorMsg, callback) {
            var model = this.get(attr),
                loadOptions = {api: this.get('capi')};

            model.set('id', modelId);
            model.load(Y.merge(loadOptions, options), Y.bind(function (error) {
                if (!error) {
                    callback();

                    return;
                }
                this._error(errorMsg);
            }, this));
        },

        /**
         * Returns the view parameters of the content edit view
         *
         * @method _getViewParameters
         * @protected
         * @return {Object}
         */
        _getViewParameters: function () {
            return {
                content: this.get('content'),
                version: this.get('version'),
                mainLocation: this.get('location'),
                contentType: this.get('contentType'),
                owner: this.get('owner'),
                config: this.get('config'),
                languageCode: this.get('languageCode'),
                user: this.get('app').get('user'),
            };
        },

        /**
         * `publishedDraft` event handler. It redirects the user according to
         * the `publishRedirectionUrl` attribute value.
         *
         * @method _redirectAfterPublish
         * @protected
         */
        _redirectAfterPublish: function () {
            this._redirectToAttribute('publishRedirectionUrl');
        },

        /**
         * `discardedDraft` event handler. It redirects the user according to
         * the `discardRedirectionUrl` attribute.
         *
         * @method _redirectAfterDiscard
         * @protected
         */
        _redirectAfterDiscard: function () {
            this._redirectToAttribute('discardRedirectionUrl');
        },

        /**
         * `*:closeView` event handler. It redirects the user according to the
         * `closeRedirectionUrl` attribute.
         *
         * @method _redirectAfterClose
         * @protected
         */
        _redirectAfterClose: function () {
            this._redirectToAttribute('closeRedirectionUrl');
        },

        /**
         * Navigates to view the Location. If the location is not loaded, it is
         * first loaded.
         *
         * @method _navigateToViewLocation
         * @private
         */
        _navigateToViewLocation: function () {
            var locationId = this.get('content').get('resources').MainLocation,
                doRedirectToViewLocation = Y.bind(function () {
                    this.get('app').navigateTo('viewLocation', {
                        id: locationId,
                        languageCode: this.get('location').get('contentInfo').get('mainLanguageCode'),
                    });
                }, this);

            if ( this.get('location').isNew() ) {
                this._loadLocation(locationId, doRedirectToViewLocation);
            } else {
                doRedirectToViewLocation();
            }
        },

        /**
         * Redirects the user according the redirection `attr`. If the attribute
         * is filled, its value is used to redirect the user, otherwise, the
         * main location of the content is used and in last resort, the user is
         * redirected to the 'dashboard'.
         *
         * @method _redirectToAttribute
         * @param {String} attr one of the redirection URL attribute name
         * @private
         */
        _redirectToAttribute: function (attr) {
            var attrRedirectionUrl = this.get(attr);

            if ( attrRedirectionUrl ) {
                return this.get('app').navigate(attrRedirectionUrl);
            }
            if ( this.get('content').get('resources').MainLocation ) {
                this._navigateToViewLocation();
            } else {
                // last option, we don't know where to redirect the user
                this.get('app').navigateTo('dashboard');
            }
        },

        /**
         * Set languageCode attribute based on parameter from request
         *
         * @method _setLanguageCode
         * @protected
         */
        _setLanguageCode: function () {
            var languageCode = this.get('content').get('mainLanguageCode');

            if (this.get('request').params.languageCode) {
                languageCode = this.get('request').params.languageCode;
            }

            this.set('languageCode', languageCode);
        },

        /**
         * Set baseLanguageCode attribute based on parameter from request
         *
         * @method _setBaseLanguageCode
         * @protected
         */
        _setBaseLanguageCode: function () {
            if (this.get('request').params.baseLanguageCode) {
                this.set('baseLanguageCode', this.get('request').params.baseLanguageCode);
            } else {
                this.reset('baseLanguageCode');
            }
        },

        /**
         * Returns uri for user redirection.
         *
         * @method _redirectionUrl
         * @protected
         * @return {String}
         */
        _redirectionUrl: function (value) {
            if ( typeof value === 'function' ) {
                return value.call(this);
            }
            return value;
        },

        /**
         * changeLanguage event handler. It opens languageSelectionBox for selecting
         * language of edited content
         *
         * @method _selectLanguage
         * @private
         * @param {EventFacade} e
         */
        _selectLanguage: function (e) {
            var that = this;

            e.preventDefault();
            this.fire('languageSelect', {
                config: {
                    title: Y.eZ.trans('change.language.to', {}, 'contentedit'),
                    languageSelectedHandler: Y.bind(this._changeContentLanguage, this),
                    cancelLanguageSelectionHandler: null,
                    canBaseTranslation: false,
                    translationMode: false,
                    referenceLanguageList: that.get('content').get('currentVersion').getTranslationsList()
                },
            });
        },

        /**
         * Changes language of edited content
         *
         * @method _changeContentLanguage
         * @private
         * @param {EventFacade} e
         * @param {String} e.selectedLanguageCode language code to which edited ontent will be switched
         */
        _changeContentLanguage: function (e) {
            var app = this.get('app'),
                service = this;

            app.navigateTo('editContent', {
                id: service.get('content').get('id'),
                languageCode: e.selectedLanguageCode
            });
        }
    }, {
        ATTRS: {
            /**
             * The content to be loaded
             *
             * @attribute content
             * @type Y.eZ.Content
             */
            content: {
                valueFn: function () {
                    return new Y.eZ.Content();
                }
            },

            /**
             * The content info the content being edited
             *
             * @attribute contentInfo
             * @type Y.eZ.ContentInfo
             */
            contentInfo: {
                valueFn: function () {
                    return new Y.eZ.ContentInfo();
                },
            },

            /**
             * The main location of the content
             *
             * @attribute location
             * @type Y.eZ.Location
             */
            location: {
                valueFn: function () {
                    return new Y.eZ.Location();
                }
            },

            /**
             * The owner of the content
             *
             * @attribute owner
             * @type Y.eZ.User
             */
            owner: {
                valueFn: getNewUser
            },

            /**
             * The version that will be edited
             *
             * @attribute version
             * @type eZ.Version
             */
            version: {
                valueFn: function () {
                    return new Y.eZ.Version();
                }
            },

            /**
             * The content type of the content
             *
             * @attribute contentType
             * @type Y.eZ.ContentType
             */
            contentType: {
                valueFn: function () {
                    return new Y.eZ.ContentType();
                }
            },

            /**
             * The URL user will be redirected to after closing the edit view
             *
             * @attribute closeRedirectionUrl
             * @type {Object}
             */
            closeRedirectionUrl: {
                getter: '_redirectionUrl'
            },

            /**
             * The URL user will be redirected to after discarding changes
             *
             * @attribute discardRedirectionUrl
             * @type {Object}
             */
            discardRedirectionUrl: {
                getter: '_redirectionUrl'
            },

            /**
             * The url user will be redirected to after publishing the content
             *
             * @attribute closeRedirectionUrl
             * @type {Object}
             */
            publishRedirectionUrl: {
                getter: '_redirectionUrl',
            },

            /**
             * The language code in which the content is edited.
             *
             * @attribute languageCode
             * @type String
             */
            languageCode: {},

            /**
             * The language code on which new translation is based.
             *
             * @attribute baseLanguageCode
             * @default null
             * @type String
             */
            baseLanguageCode: {
                value: null
            }
        }
    });
});