%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /proc/985914/root/data/old/home/stash/atlassian-stash/static/util/
Upload File :
Create Path :
Current File : //proc/985914/root/data/old/home/stash/atlassian-stash/static/util/error.js

define('util/error', [
    'aui',
    'jquery',
    'underscore',
    'util/function',
    'util/navbuilder',
    'model/page-state',
    'exports'
], function(
    _aui,
    $,
    _,
    fn,
    navbuilder,
    pageState,
    exports
) {

    'use strict';

    var pageIsLoaded = false,
        latestEscKeyTime = 0;
    $(document).ready(function() {
        pageIsLoaded = true;

        var oldOnBeforeUnload = window.onbeforeunload;
        window.onbeforeunload = function() {
            pageIsLoaded = false;
            return oldOnBeforeUnload ? oldOnBeforeUnload.apply(this, arguments) : undefined;
        };
    }).on('keydown', function(e) {
        if (e.keyCode === _aui.keyCode.ESCAPE) {
            latestEscKeyTime = new Date().getTime();
        }
    });

    exports.showNonFieldErrors = function(errors) {
        //TODO move to Soy and i18n it.
        var $notificationSection = $('#content > header > .notifications').empty();
        if (typeof(errors) === 'string'){
            appendError($notificationSection, errors);
        } else if (errors && errors.length) {
            var nonFieldErrors = _.filter(errors, fn.not(fn.pluck('context')));

            if (nonFieldErrors.length) {
                _.each(nonFieldErrors, function(error) {
                    var errorText = (error && error.message) ? error.message : error;
                    appendError($notificationSection, errorText);
                });
            }
        } else {
            appendError($notificationSection, AJS.I18n.getText('stash.web.unknownerror'));
        }
    };

    var appendError = function ($container, errorText) {
        $(widget.aui.message.error({
            content: _aui.escapeHtml(errorText)
        })).appendTo($container);
    };

    // TODO - our error handling needs some error codes to avoid this kind of heuristic stuff.
    // or perhaps a rawError.entityType field?
    exports.isErrorEntityWithinRepository = function(rawError) {
        return rawError && /branch|tag|repository object|changeset|revision|comment/i.test(rawError.message);
    };

    exports.level = {
        INFO : "INFO", // currently unused
        WARNING : "WARNING", // currently unused
        ERROR : "ERROR",
        SUCCESS : "SUCCESS" // currently unused
    };

    function ErrorBuilder(title, message, details, context, level) {
        this._out = {
            title : title,
            message : message,
            details : details,
            context : context,
            level : level || exports.level.ERROR,
            fallbackUrl : undefined,
            fallbackTitle : undefined,
            shouldLogin : false,
            shouldReload : false,
            canRetry : false,
            shouldRetry : false,
            retryAfterDate : undefined
        };
    }

    ErrorBuilder.prototype.title = function(title) {
        this._out.title = title;
        return this;
    };

    ErrorBuilder.prototype.message = function(message) {
        this._out.message = message;
        return this;
    };

    ErrorBuilder.prototype.details = function(details) {
        this._out.details = details;
        return this;
    };

    ErrorBuilder.prototype.context = function(context) {
        this._out.context = context;
        return this;
    };

    ErrorBuilder.prototype.level = function(level) {
        this._out.level = level || exports.level.ERROR;
        return this;
    };

    ErrorBuilder.prototype.shouldLogin = function() {
        this._out.shouldLogin = true;
        return this;
    };

    ErrorBuilder.prototype.shouldReload = function() {
        this._out.shouldReload = true;
        return this;
    };

    ErrorBuilder.prototype.shouldRetry = function() {
        this._out.shouldRetry = true;
        return this.canRetry();
    };

    ErrorBuilder.prototype.canRetry = function() {
        this._out.canRetry = true;
        return this;
    };

    ErrorBuilder.prototype.fallbackUrl = function(url, title) {
        this._out.fallbackUrl = url;
        this._out.fallbackTitle = title;
        return this;
    };

    ErrorBuilder.prototype.retryAfterDate = function(date) {
        // check for invalid date
        this._out.retryAfterDate = isNaN(date) ? undefined : date;

        if (this._out.retryAfterDate) {
            this.shouldRetry();
        }

        return this;
    };

    ErrorBuilder.prototype.doAccessDenied = function(isLoggedIn) {

        this.title(this._out.title || titleForStatusCode['401']);

        if (isLoggedIn) { // They specifically don't have permission.
            this.message(this._out.message || AJS.I18n.getText('stash.web.error.no.permission'));
            this.fallbackUrl(navbuilder.allProjects().build(), AJS.I18n.getText('stash.web.ajax.back.to.projects'));

        } else {
            this.message(this._out.message || AJS.I18n.getText('stash.web.error.anonymous.disallowed'));
            this.shouldLogin();
        }

        return this;
    };

    ErrorBuilder.prototype.doServerDown = function() {

        this.title(this._out.title || titleForStatusCode['0']);
        this.message(this._out.message || messageForStatusCode['0']);

        this.canRetry();

        return this;
    };

    ErrorBuilder.prototype.doRequestThrottled = function(retryAfterValue) {

        this.title(this._out.title || titleForStatusCode['503']);
        this.message(this._out.message || messageForStatusCode['503']);

        this.canRetry();
        this.retryAfterDate(!isNaN(Number(retryAfterValue)) ?
                            new Date( new Date().getTime() + (1000 * Number(retryAfterValue))) : // value in ms
                            new Date(Date.parse(retryAfterValue))); // date string

        return this;
    };

    ErrorBuilder.prototype.doNotFound = function(optFallbackUrl, optFallbackTitle) {

        this.title(this._out.title || titleForStatusCode['404']);
        this.message(this._out.message || messageForStatusCode['404']);

        this.fallbackUrl(
            optFallbackUrl   || this._out.fallbackUrl   || navbuilder.allProjects().build(),
            optFallbackTitle || this._out.fallbackTitle || AJS.I18n.getText('stash.web.ajax.back.to.projects'));

        return this;
    };

    ErrorBuilder.prototype.build = function() {
        if (!this._out.title) {
            this.title(AJS.I18n.getText('stash.web.dialog.ajax.error.title'));
        }

        return this._out;
    };

    var titleForStatusCode = {
        '401' : AJS.I18n.getText('stash.web.dialog.ajax.error.not.permitted.title'),
        '404' : AJS.I18n.getText('stash.web.couldnt.find.title'),
        '502' : AJS.I18n.getText('stash.web.server.unreachable.title'),
        '0'   : AJS.I18n.getText('stash.web.server.unreachable.title'),
        '503' : AJS.I18n.getText('stash.web.server.busy.title')
    };

    var messageForStatusCode = {
        '404' : AJS.I18n.getText('stash.web.couldnt.find'),
        '502' : AJS.I18n.getText('stash.web.error.server.unresponsive'),
        '0'   : AJS.I18n.getText('stash.web.error.server.unresponsive'),
        '503' : AJS.I18n.getText('stash.web.error.too.much.load')
    };

    function isSuccessHttpCodeRange(status) {
        return status >= 200 && status < 400;
    }

    // IE sucks.
    // Discussion at http://stackoverflow.com/questions/4268931/xmlhttprequest-response-has-no-headers-in-internet-explorer
    function shouldIgnoreLoggedOutUser(jqXhr, status) {
        return status === 204 && $.browser.msie && !jqXhr.getAllResponseHeaders();
    }

    function handlePermissionErrorsFromUserChanging(jqXhr, status, requestedAsUser, originalUser) {

        // IF it's an auth error or if they were redirected to login and got a 200, or there's just no other error.
        // AND their login status has changed since loading the page.
        if ((status === 401 || isSuccessHttpCodeRange(status)) && !usersEqual(requestedAsUser, originalUser)) {

            // four possible next steps:
            // 1. Just have them reload the page for consistent state
            // 2. Have them log in if they're not logged in.
            // 3. Send them Back to Projects if they don't have permission.
            // 4. Do nothing - IE sucks so we have to just assume everything went OK.
            var errorBuilder;
            if (status === 401) {
                errorBuilder = new ErrorBuilder().doAccessDenied(requestedAsUser != null);
            } else if (requestedAsUser || !shouldIgnoreLoggedOutUser(jqXhr, status)) {
                errorBuilder = new ErrorBuilder()[requestedAsUser ? "shouldReload" : "shouldLogin"]();
            } else {
                // ignoring logged out user.
                return null;
            }

            // five possible reasons/messages
            // 1. Logged in, but permission was restricted concurrently so anonymous user could see, but now logged in user can't see (rare)
            // 2. Logged in - just refresh for consistent state
            // 3. Logged out - whichever action we're taking, just use the "you've been logged out" message.
            // 4. Switched users, and new user lacks permission
            // 5. Switched users, and new user also has permission.
            if (!originalUser) { // they weren't logged in, but now they are.
                if (status === 401) { // and in doing so actually LOST permission. (perhaps a timing coincidence)
                    return errorBuilder
                        .message(AJS.I18n.getText('stash.web.error.logged.in.and.denied',
                            '"' + requestedAsUser.name + '"'))
                        .build();
                } else {
                    return errorBuilder
                        .title(AJS.I18n.getText('stash.web.error.logged.in.title'))
                        .message(AJS.I18n.getText('stash.web.error.logged.in',
                            '"' + requestedAsUser.name + '"'))
                        .build();
                }
            } else if (!requestedAsUser) { // they got logged out
                // message works whether they need to login or just reload the page.
                return errorBuilder
                    .title(AJS.I18n.getText('stash.web.error.logged.out.title'))
                    .message(AJS.I18n.getText('stash.web.error.logged.out'))
                    .build();
            } else { // switched users
                if (status === 401) {
                    return errorBuilder
                        .message(AJS.I18n.getText('stash.web.error.logged.in.as.other.and.denied',
                            '"' + originalUser.name + '"',
                            '"' + requestedAsUser.name + '"'))
                        .build();
                } else {
                    return errorBuilder
                        .title(AJS.I18n.getText('stash.web.error.user.changed.title'))
                        .message(AJS.I18n.getText('stash.web.error.logged.in.as.other',
                             '"' + originalUser.name + '"',
                             '"' + requestedAsUser.name + '"'))
                        .build();
                }
            }
        }

        return null;
    }

    function getRequestedAsUser(jqXhr) {
        var userId = jqXhr.getResponseHeader('X-AUSERID');
        var userName = jqXhr.getResponseHeader('X-AUSERNAME');
        return userName != null ? {
            id : userId != null ? parseInt(userId, 10) : null,
            name: decodeURIComponent(userName)
        } : null;
    }

    function usersEqual(user1, user2) {
        if (user1 == null || user2 == null) {
            return (user1 || null) === (user2 || null);
        } else if (user1.id != null && user2.id != null) {
            return user1.id === user2.id;
        } else {
            return user1.name === user2.name;
        }
    }

    exports.getDominantAJAXError = function(jqXhr, internalRequestedAsUser, internalOriginalUser) {
        var status = Number(jqXhr.status);

        var originalUser = internalOriginalUser !== undefined ?
                           internalOriginalUser :
                           pageState.getCurrentUser() ? pageState.getCurrentUser().toJSON() : null;
        var requestedAsUser = internalRequestedAsUser !== undefined ?
                              internalRequestedAsUser :
                              getRequestedAsUser(jqXhr);

        // because AJAX requests are redirected to login, even a 200 could potentially be a session timeout.
        var userChangedError = handlePermissionErrorsFromUserChanging(jqXhr, status, requestedAsUser, originalUser);
        if (userChangedError) {
            return userChangedError;
        }

        if (status === 0 || status >= 400 && status < 600) {

            switch(status) {
                case 401:
                    return new ErrorBuilder().doAccessDenied(!!originalUser).build();
                case 404:
                    return new ErrorBuilder().doNotFound().build();
                case 0:
                    // The server MAY be down, but first check these common alternative explanations

                    // pageIsLoaded: if the page is being unloaded, browsers will abort AJAX requests.
                    // jqXhr.statusText !== 'abort': the request wasn't manually aborted
                    // Date.now() - latestEscKeyTime > 100: in Firefox, pressing Esc will cancel AJAX requests but jQuery reports it as an error.
                    if (pageIsLoaded && jqXhr.statusText !== 'abort' && ((new Date().getTime()) - latestEscKeyTime > 100)) {
                        return new ErrorBuilder().doServerDown().build();
                    }
                    break;
                case 502:
                    return new ErrorBuilder().doServerDown().build();
                case 503:
                    return new ErrorBuilder().doRequestThrottled(jqXhr.getResponseHeader("Retry-After")).build();
                default:
                    // else we screwed up
                    return new ErrorBuilder(
                        null,
                        AJS.I18n.getText('stash.web.we.screwed.up'))
                    .shouldReload()
                    .build();
            }
        }

        return null;
    };

    exports.getDominantRESTError = function(data, jqXhr) {

        var status, requestedAsUser, originalUser;

        originalUser = pageState.getCurrentUser() ? pageState.getCurrentUser().toJSON() : null;

        // handle user-switching errors first
        if (jqXhr) {
            status = Number(jqXhr.status);

            requestedAsUser = getRequestedAsUser(jqXhr);

            var userChangedError = handlePermissionErrorsFromUserChanging(jqXhr, status, requestedAsUser, originalUser);
            if (userChangedError) {
                return userChangedError;
            }
        }

        // Then handle errors from data that may have more specific messages than what we can offer client-side.
        if (data && data.errors && data.errors.length) {
            var rawError = data.errors[0],
                builder = new ErrorBuilder(
                    rawError.title || titleForStatusCode[status],
                    rawError.message,
                    rawError.details,
                    rawError.context,
                    rawError.level || exports.level.ERROR
                );

            switch(status) {
                case 401:
                    builder.doAccessDenied(originalUser != null);
                    break;
                case 404:
                    builder.doNotFound();
                    break;
                case 409:
                    builder.shouldReload();
                    break;
                case 503:
                    builder.doRequestThrottled(jqXhr.getResponseHeader("Retry-After"));
                    break;
            }

            return builder.build();
        }

        // Finally fallback to generic HTTP error handling, if necessary
        if (jqXhr) {
            return exports.getDominantAJAXError(jqXhr, requestedAsUser, originalUser);
        }

        return null;
    };

    /**
     * Clear any existing errors in the form and add the given errors.
     *
     * @param {HTMLElement|jQuery|String} form the form to set the errors into
     * @param {Array} errors an array of errors
     */
    exports.setFormErrors = function(form, errors) {
        var $form = $(form);
        exports.clearFormErrors($form);
        var formErrors = _.chain(errors)
            .filter(function(error) {
                if (!error.message) {
                    return false;
                }
                // This is a little impure but it is very convenient.
                // While we are finding errors to apply at a form level,
                // filter out field errors and append them to the fields
                // as we go!
                if (error.context) {
                    var $field = $form.find('[name="' + error.context + '"]').closest('.field-group');
                    if ($field.length) {
                        $(document.createElement('div'))
                            .addClass('error')
                            .text(error.message)
                            .appendTo($field);
                        return false;
                    }
                }
                return true;
            })
            .pluck('message')
            // Escape at this point so that we can use br tags to concatenate
            .map(_aui.escapeHtml)
            .value()
            .join('<br>');

        if (formErrors) {
            $form.prepend(aui.message.error({
                content: formErrors
            }));
        }
    };

    /**
     * Clear all the errors in a given form.
     *
     * @param {HTMLElement|jQuery|String} form the form to clear the errors from.
     */
    exports.clearFormErrors = function(form) {
        $(form).find('.error').remove();
    };

});



Zerion Mini Shell 1.0