コード例 #1
0
ファイル: nodes.js プロジェクト: 545zhou/osf.io
    }
});


var NodeClient = oop.extend(base.BaseClient, {
    /**
     * Return a promise that resolves to an Array of Node objects.
     * @param {object} params
     *  {number} pageSize
     */
    list: function(params) {
        params = params || {};
        var ret = $.Deferred();
        // TODO: page numbber, filtering etc.
        var query = params.pageSize != null ? 'page[size]=' + params.pageSize : '';
        this._request({url: '/nodes/',
                      query: query})
            .done(function(resp) {
                var nodes = $.map(resp.data, function(nodeData) {
                    return new Node(nodeData);
                });
                ret.resolve(nodes);
            }).fail(base.captureError('Could not fetch nodes list.'));
        return ret.promise();
    }
    // TODO: detail(nodeID)
});

module.exports = {
    Node: Node,
    NodeClient: NodeClient
コード例 #2
0
var NodeCategorySettings = oop.extend(
    ChangeMessageMixin,
    {
        constructor: function(category, categories, updateUrl, disabled) {
            this.super.constructor.call(this);

            var self = this;

            self.disabled = disabled || false;

            self.UPDATE_SUCCESS_MESSAGE = 'Category updated successfully';
            self.UPDATE_ERROR_MESSAGE = 'Error updating category, please try again. If the problem persists, email ' +
                '<a href="mailto:support@osf.io">support@osf.io</a>.';
            self.UPDATE_ERROR_MESSAGE_RAVEN = 'Error updating Node.category';

            self.INSTANTIATION_ERROR_MESSAGE = 'Trying to instatiate NodeCategorySettings view model without an update URL';

            self.MESSAGE_SUCCESS_CLASS = 'text-success';
            self.MESSAGE_ERROR_CLASS = 'text-danger';

            if (!updateUrl) {
                throw new Error(self.INSTANTIATION_ERROR_MESSAGE);
            }

            self.categories = categories;
            self.category = ko.observable(category);
            self.updateUrl = updateUrl;

            self.selectedCategory = ko.observable(category);
            self.dirty = ko.observable(false);
            self.selectedCategory.subscribe(function(value) {
                if (value !== self.category()) {
                    self.dirty(true);
                }
            });
        },
        updateSuccess: function(newcategory) {
            var self = this;
            self.changeMessage(self.UPDATE_SUCCESS_MESSAGE, self.MESSAGE_SUCCESS_CLASS);
            self.category(newcategory);
            self.dirty(false);
        },
        updateError: function(xhr, status, error) {
            var self = this;
            self.changeMessage(self.UPDATE_ERROR_MESSAGE, self.MESSAGE_ERROR_CLASS);
            Raven.captureMessage(self.UPDATE_ERROR_MESSAGE_RAVEN, {
                url: self.updateUrl,
                textStatus: status,
                err: error
            });
        },
        updateCategory: function() {
            var self = this;
            return $osf.putJSON(self.updateUrl, {
                    category: self.selectedCategory()
                })
                .then(function(response) {
                    return response.updated_fields.category;
                })
                .done(self.updateSuccess.bind(self))
                .fail(self.updateError.bind(self));
        },
        cancelUpdateCategory: function() {
            var self = this;
            self.selectedCategory(self.category());
            self.dirty(false);
            self.resetMessage();
        }
    });
コード例 #3
0
var TokenDetailViewModel = oop.extend(ChangeMessageMixin, {
    constructor: function (urls) {
        this.super.constructor.call(this);
        var placeholder = new TokenData();
        this.tokenData = ko.observable(placeholder);

        // Track whether data has changed, and whether user is allowed to leave page anyway
        this.originalValues = ko.observable(placeholder.serialize());
        this.dirty = ko.computed(function(){
            return JSON.stringify(this.originalValues()) !== JSON.stringify(this.tokenData().serialize());
        }.bind(this));
        this.allowExit = ko.observable(false);

        // Set up data access client
        this.webListUrl = urls.webListUrl;
        this.client = new TokenDataClient(urls.apiListUrl);

        // Toggle hiding token id (in detail view)
        this.showToken = ko.observable(false);
        // Toggle display of validation messages
        this.showMessages = ko.observable(false);

        // // If no detail url provided, render view as though it was a creation form. Otherwise, treat as READ/UPDATE.
        this.apiDetailUrl = ko.observable(urls.apiDetailUrl);
        this.isCreateView = ko.computed(function () {
            return !this.apiDetailUrl();
        }.bind(this));
    },
    init: function () {
        if (!this.isCreateView()) {
            // Add listener to prevent user from leaving page if there are unsaved changes
            $(window).on('beforeunload', function () {
                if (this.dirty() && !this.allowExit()) {
                    return 'There are unsaved changes on this page.';
                }
            }.bind(this));

            var request = this.client.fetchOne(this.apiDetailUrl());
            request.done(function (dataObj) {
                this.tokenData(dataObj);
                this.originalValues(dataObj.serialize());
            }.bind(this));
            request.fail(function(xhr, status, error) {
                $osf.growl('Error',
                             language.apiOauth2Token.dataFetchError,
                            'danger');

                Raven.captureMessage('Error fetching token data', {
                    extra: {
                        url: this.apiDetailUrl(),
                        status: status,
                        error: error
                    }
                });
            }.bind(this));
        }
    },
    updateToken: function () {
        if (!this.dirty()){
            // No data needs to be sent to the server, but give the illusion that form was submitted
            this.changeMessage(
                language.apiOauth2Token.dataUpdated,
                'text-success',
                5000);
            return;
        }

        var request = this.client.updateOne(this.tokenData());
        request.done(function (dataObj) {
            this.tokenData(dataObj);
            this.originalValues(dataObj.serialize());
            this.changeMessage(
                language.apiOauth2Token.dataUpdated,
                'text-success',
                5000);
        }.bind(this));

        request.fail(function (xhr, status, error) {
            $osf.growl('Error',
                       language.apiOauth2Token.dataSendError,
                       'danger');

            Raven.captureMessage('Error updating instance', {
                extra: {
                    url: this.apiDetailUrl,
                    status: status,
                    error: error
                }
            });
        }.bind(this));
        return request;
    },
    createToken: function () {
        var request = this.client.createOne(this.tokenData());
        request.done(function (dataObj) {
            this.tokenData(dataObj);
            this.originalValues(dataObj.serialize());
            this.showToken(true);
            this.changeMessage(language.apiOauth2Token.creationSuccess, 'text-success');
            this.apiDetailUrl(dataObj.apiDetailUrl); // Toggle ViewModel --> act like a display view now.
            historyjs.replaceState({}, '', dataObj.webDetailUrl);  // Update address bar to show new detail page
        }.bind(this));

        request.fail(function (xhr, status, error) {
            $osf.growl('Error',
                       language.apiOauth2Token.dataSendError,
                       'danger');

            Raven.captureMessage('Error registering new OAuth2 personal access token', {
                extra: {
                    url: this.apiDetailUrl,
                    status: status,
                    error: error
                }
            });
        }.bind(this));
    },
    submit: function () {
        // Validate and dispatch form to correct handler based on view type
        if (!this.tokenData().isValid()) {
            // Turn on display of validation messages
            this.showMessages(true);
        } else {
            this.showMessages(false);
            if (this.isCreateView()) {
                this.createToken();
            } else {
                this.updateToken();
            }
        }
    },
    deleteToken: function () {
        var tokenData = this.tokenData();
        bootbox.confirm({
            title: 'Deactivate token?',
            message: language.apiOauth2Token.deactivateConfirm,
            callback: function (confirmed) {
                if (confirmed) {
                    var request = this.client.deleteOne(tokenData );
                    request.done(function () {
                        this.allowExit(true);
                        // Don't let user go back to a deleted token page
                        historyjs.replaceState({}, '', this.webListUrl);
                        this.visitList();
                    }.bind(this));
                    request.fail(function () {
                            $osf.growl('Error',
                                       language.apiOauth2Token.deactivateError,
                                       'danger');
                    }.bind(this));
                }
            }.bind(this),
            buttons:{
                confirm:{
                    label:'Deactivate',
                    className:'btn-danger'
                }
            }
        });
    },
    visitList: function () {
        window.location = this.webListUrl;
    },
    cancelChange: function () {
        if (!this.dirty()) {
            this.visitList();
        } else {
            bootbox.confirm({
                title: 'Discard changes?',
                message: language.apiOauth2Token.discardUnchanged,
                callback: function(confirmed) {
                    if (confirmed) {
                        this.allowExit(true);
                        this.visitList();
                    }
                }.bind(this),
                buttons: {
                    confirm: {
                        label:'Discard',
                        className:'btn-danger'
                    }
                }
            });
        }
    },
});
コード例 #4
0
ファイル: accountSettings.js プロジェクト: XTech2K/osf.io
var UserProfileViewModel = oop.extend(ChangeMessageMixin, {
    constructor: function() {
        this.super.constructor.call(this);
        this.client = new UserProfileClient();
        this.profile = ko.observable(new UserProfile());
        this.emailInput = ko.observable('');

    },
    init: function () {
        this.client.fetch().done(
            function(profile) { this.profile(profile); }.bind(this)
        );
    },
    addEmail: function () {
        this.changeMessage('', 'text-info');
        var newEmail = this.emailInput().toLowerCase().trim();
        if(newEmail){

            var email = new UserEmail({
                address: newEmail
            });

            // ensure email isn't already in the list
            for (var i=0; i<this.profile().emails().length; i++) {
                if (this.profile().emails()[i].address() === email.address()) {
                    this.changeMessage('Duplicate Email', 'text-warning');
                    this.emailInput('');
                    return;
                }
            }

            this.profile().emails.push(email);

            this.client.update(this.profile()).done(function (profile) {
                this.profile(profile);

                var emails = profile.emails();
                for (var i=0; i<emails.length; i++) {
                    if (emails[i].address() === email.address()) {
                        this.emailInput('');
                        var addrText = $osf.htmlEscape(email.address());
                        $osf.growl('<em>' + addrText  + '</em> added to your account.','You will receive a confirmation email at <em>' + addrText  + '</em>. Please check your email and confirm.', 'success');
                        return;
                    }
                }
            }.bind(this)).fail(function(){
                this.profile().emails.remove(email);
            }.bind(this));
        } else {
            this.changeMessage('Email cannot be empty.', 'text-danger');
        }
    },
    resendConfirmation: function(email){
        var self = this;
        self.changeMessage('', 'text-info');
        var addrText = $osf.htmlEscape(email.address());
        bootbox.confirm({
            title: 'Resend Email Confirmation?',
            message: 'Are you sure that you want to resend email confirmation to ' + '<em>' + addrText + '</em>?',
            callback: function (confirmed) {
                if (confirmed) {
                    self.client.update(self.profile(), email).done(function () {
                        $osf.growl(
                            'Email confirmation resent to <em>' + addrText + '</em>',
                            'You will receive a new confirmation email at <em>' + addrText  + '</em>. Please check your email and confirm.',
                            'success');
                    });
                }
            },
            buttons:{
                confirm:{
                    label:'Resend'
                }
            }
        });
    },
    removeEmail: function (email) {
        var self = this;
        self.changeMessage('', 'text-info');
        if (self.profile().emails().indexOf(email) !== -1) {
            var addrText = $osf.htmlEscape(email.address());
            bootbox.confirm({
                title: 'Remove Email?',
                message: 'Are you sure that you want to remove ' + '<em>' + addrText + '</em>' + ' from your email list?',
                callback: function (confirmed) {
                    if (confirmed) {
                        self.profile().emails.remove(email);
                        self.client.update(self.profile()).done(function () {
                            $osf.growl('Email Removed', '<em>' + addrText + '</em>', 'success');
                        });
                    }
                },
                buttons:{
                    confirm:{
                        label:'Remove',
                        className:'btn-danger'
                    }
                }
            });
        } else {
            $osf.growl('Error', 'Please refresh the page and try again.', 'danger');
        }
    },
    makeEmailPrimary: function (email) {
        this.changeMessage('', 'text-info');
        if (this.profile().emails().indexOf(email) !== -1) {
            this.profile().primaryEmail().isPrimary(false);
            email.isPrimary(true);
            this.client.update(this.profile()).done(function () {
                var addrText = $osf.htmlEscape(email.address());
                $osf.growl('Made Primary', '<em>' + addrText + '<em>', 'success');
            });
        } else {
            $osf.growl('Error', 'Please refresh the page and try again.', 'danger');
        }
    }
});
コード例 #5
0
ファイル: pointers.js プロジェクト: adlius/osf.io
var AddPointerViewModel = oop.extend(Paginator, {
    constructor: function(nodeTitle) {
        this.super.constructor.call(this);
        var self = this;
        this.nodeTitle = nodeTitle;
        this.submitEnabled = ko.observable(true);
        this.searchAllProjectsSubmitText = ko.observable(SEARCH_ALL_SUBMIT_TEXT);
        this.searchMyProjectsSubmitText = ko.observable(SEARCH_MY_PROJECTS_SUBMIT_TEXT);

        this.query = ko.observable();
        this.results = ko.observableArray();
        this.selection = ko.observableArray();
        this.errorMsg = ko.observable('');
        this.totalPages = ko.observable(0);
        this.includePublic = ko.observable(true);
        this.searchWarningMsg = ko.observable('');
        this.submitWarningMsg = ko.observable('');
        this.loadingResults = ko.observable(false);

        this.foundResults = ko.pureComputed(function() {
            return self.query() && self.results().length;
        });

        this.noResults = ko.pureComputed(function() {
            return self.query() && !self.results().length;
        });
    },
    searchAllProjects: function() {
        this.includePublic(true);
        this.pageToGet(0);
        this.searchAllProjectsSubmitText('Searching...');
        this.fetchResults();
    },
    searchMyProjects: function() {
        this.includePublic(false);
        this.pageToGet(0);
        this.searchMyProjectsSubmitText('Searching...');
        this.fetchResults();
    },
    fetchResults: function() {
        var self = this;
        self.errorMsg('');
        self.searchWarningMsg('');

        if (self.query()) {
            self.results([]); // clears page for spinner
            self.loadingResults(true); // enables spinner

            osfHelpers.postJSON(
                '/api/v1/search/node/', {
                    query: self.query(),
                    nodeId: nodeId,
                    includePublic: self.includePublic(),
                    page: self.pageToGet()
                }
            ).done(function(result) {
                if (!result.nodes.length) {
                    self.errorMsg('No results found.');
                } else {
                    result.nodes.forEach(function(each) {
                        if (each.isRegistration) {
                            each.dateRegistered = new osfHelpers.FormattableDate(each.dateRegistered);
                        } else {
                            each.dateCreated = new osfHelpers.FormattableDate(each.dateCreated);
                            each.dateModified = new osfHelpers.FormattableDate(each.dateModified);
                        }
                    });
                }
                self.results(result.nodes);
                self.currentPage(result.page);
                self.numberOfPages(result.pages);
                self.addNewPaginators();
            }).fail(function(xhr) {
                self.searchWarningMsg(xhr.responseJSON && xhr.responseJSON.message_long);
            }).always( function (){
                self.searchAllProjectsSubmitText(SEARCH_ALL_SUBMIT_TEXT);
                self.searchMyProjectsSubmitText(SEARCH_MY_PROJECTS_SUBMIT_TEXT);
                self.loadingResults(false);
            });
        } else {
            self.results([]);
            self.currentPage(0);
            self.totalPages(0);
            self.searchAllProjectsSubmitText(SEARCH_ALL_SUBMIT_TEXT);
            self.searchMyProjectsSubmitText(SEARCH_MY_PROJECTS_SUBMIT_TEXT);
        }
    },
    addTips: function(elements, data) {
        elements.forEach(function(element) {
            var titleText = '';
            if (data.isRegistration) {
                titleText = 'Registered: ' + data.dateRegistered.local;
            } else {
                titleText = 'Created: ' + data.dateCreated.local + '\nModified: ' + data.dateModified.local;
            }
            $(element).tooltip({
                title: titleText
            });
        });
    },
    add: function(data) {
        this.selection.push(data);
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.pointer-row').tooltip();
    },
    remove: function(data) {
        var self = this;
        self.selection.splice(
            self.selection.indexOf(data), 1
        );
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.pointer-row').tooltip();
    },
    addAll: function() {
        var self = this;
        $.each(self.results(), function(idx, result) {
            if (self.selection().indexOf(result) === -1) {
                self.add(result);
            }
        });
    },
    removeAll: function() {
        var self = this;
        $.each(self.selection(), function(idx, selected) {
            self.remove(selected);
        });
    },
    selected: function(data) {
        var self = this;
        for (var idx = 0; idx < self.selection().length; idx++) {
            if (data.id === self.selection()[idx].id) {
                return true;
            }
        }
        return false;
    },
    submit: function() {
        var self = this;
        self.submitEnabled(false);
        self.submitWarningMsg('');

        var nodeIds = osfHelpers.mapByProperty(self.selection(), 'id');

        osfHelpers.postJSON(
            nodeApiUrl + 'pointer/', {
                nodeIds: nodeIds
            }
        ).done(function() {
            window.location.reload();
        }).fail(function(data) {
            self.submitEnabled(true);
            self.submitWarningMsg(data.responseJSON && data.responseJSON.message_long);
        });
    },
    clear: function() {
        this.query('');
        this.results([]);
        this.selection([]);
        this.searchWarningMsg('');
        this.submitWarningMsg('');
    },
    authorText: function(node) {
        var rv = node.firstAuthor;
        if (node.etal) {
            rv += ' et al.';
        }
        return rv;
    }
});
コード例 #6
0
ファイル: owncloudNodeConfig.js プロジェクト: adlius/osf.io
var ViewModel = oop.extend(OauthAddonFolderPicker,{
    constructor: function(addonName, url, selector, folderPicker, opts, tbOpts) {
        var self = this;
        // TODO: [OSF-7069]
        self.super.super.constructor.call(self, addonName, url, selector, folderPicker, tbOpts);
        self.super.construct.call(self, addonName, url, selector, folderPicker, opts, tbOpts);
        // Non-Oauth fields:
        self.username = ko.observable('');
        self.password = ko.observable('');
        self.hosts = ko.observableArray([]);
        self.selectedHost = ko.observable();    // Host specified in select element
        self.customHost = ko.observable();      // Host specified in input element
        self.savedHost = ko.observable();       // Configured host

        var otherString = 'Other (Please Specify)';
        // Designated host, specified from select or input element
        self.host = ko.pureComputed(function() {
            return self.useCustomHost() ? self.customHost() : self.selectedHost();
        });
        // Hosts visible in select element. Includes presets and 'Other' option
        self.visibleHosts = ko.pureComputed(function() {
            return self.hosts().concat([otherString]);
        });
        // Whether to use select element or input element for host designation
        self.useCustomHost = ko.pureComputed(function() {
            return (self.selectedHost() === otherString || !self.hasDefaultHosts());
        });
        self.credentialsChanged = ko.pureComputed(function() {
            return self.nodeHasAuth() && !self.validCredentials();
        });
        self.showCredentialInput = ko.pureComputed(function() {
            return (self.credentialsChanged() && self.userIsOwner()) ||
                (!self.userHasAuth() && !self.nodeHasAuth() && self.loadedSettings());
        });
        self.hasDefaultHosts = ko.pureComputed(function() {
            return Boolean(self.hosts().length);
        });
    },
    _updateCustomFields: function(settings) {
        var self = this;
        self.hosts(settings.hosts);
    },
    clearModal : function() {
        var self = this;
        self.selectedHost(null);
        self.customHost(null);
    },
    connectAccount : function() {
        var self = this;
        if( self.hasDefaultHosts() && !self.selectedHost() ){
            if (self.useCustomHost()){
                self.changeMessage('Please enter an ownCloud server.', 'text-danger');
            } else {
                self.changeMessage('Please select an ownCloud server.', 'text-danger');            
            }
            return;
        }
        if ( !self.useCustomHost() && !self.username() && !self.password() ){
            self.changeMessage('Please enter a username and password.', 'text-danger');
            return;
        }
        if ( self.useCustomHost() && ( !self.customHost() || !self.username() || !self.password() ) )  {
            self.changeMessage('Please enter an ownCloud host and credentials.', 'text-danger');
            return;
        }
        var url = self.urls().auth;
        return osfHelpers.postJSON(
            url,
            ko.toJS({
                host: self.host,
                password: self.password,
                username: self.username
            })
        ).done(function() {
            self.clearModal();
            $modal.modal('hide');
            self.updateAccounts().then(function() {
                try{
                    $osf.putJSON(
                        self.urls().importAuth, {
                            external_account_id: self.accounts()[0].id
                        }
                    ).done(self.onImportSuccess.bind(self)
                    ).fail(self.onImportError.bind(self));
                    self.changeMessage(self.messages.connectAccountSuccess(), 'text-success', 3000);
                }
                catch(err){
                    self.changeMessage(self.messages.connectAccountDenied(), 'text-danger', 6000);
                }
            });
        }).fail(function(xhr, textStatus, error) {
            var errorMessage = (xhr.status === 401) ? language.authInvalid : language.authError;
            self.changeMessage(errorMessage, 'text-danger');
            Raven.captureMessage('Could not authenticate with ownCloud', {
                url: url,
                textStatus: textStatus,
                error: error
            });
        });
    },
   formatExternalName: function(item) {
        return {
            text: $osf.htmlEscape(item.name) + ' - ' + $osf.htmlEscape(item.profile),
            value: item.id
        };
    }
});
コード例 #7
0
ファイル: pointers.js プロジェクト: GageGaskins/osf.io
var AddPointerViewModel = oop.extend(Paginator, {
    constructor: function(nodeTitle) {
        this.super.constructor.call(this);
        var self = this;
        this.nodeTitle = nodeTitle;
        this.submitEnabled = ko.observable(true);

        this.query = ko.observable();
        this.results = ko.observableArray();
        this.selection = ko.observableArray();
        this.errorMsg = ko.observable('');
        this.totalPages = ko.observable(0);
        this.includePublic = ko.observable(true);

        this.foundResults = ko.pureComputed(function() {
            return self.query() && self.results().length;
        });

        this.noResults = ko.pureComputed(function() {
            return self.query() && !self.results().length;
        });
    },
    searchAllProjects: function() {
        this.includePublic(true);
        this.fetchResults();
    },
    searchMyProjects: function() {
        this.includePublic(false);
        this.fetchResults();
    },
    fetchResults: function() {
        var self = this;
        self.errorMsg('');
        if (self.query()) {
            osfHelpers.postJSON(
                '/api/v1/search/node/', {
                    query: self.query(),
                    nodeId: nodeId,
                    includePublic: self.includePublic(),
                    page: self.currentPage()
                }
            ).done(function(result) {
                if (!result.nodes.length) {
                    self.errorMsg('No results found.');
                }
                self.results(result.nodes);
                self.currentPage(result.page);
                self.numberOfPages(result.pages);
                self.addNewPaginators();
            }).fail(
                osfHelpers.handleJSONError
            );
        } else {
            self.results([]);
            self.currentPage(0);
            self.totalPages(0);
        }
    },
    addTips: function(elements) {
        elements.forEach(function(element) {
            $(element).find('.contrib-button').tooltip();
        });
    },
    add: function(data) {
        this.selection.push(data);
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.contrib-button').tooltip();
    },
    remove: function(data) {
        var self = this;
        self.selection.splice(
            self.selection.indexOf(data), 1
        );
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.contrib-button').tooltip();
    },
    addAll: function() {
        var self = this;
        $.each(self.results(), function(idx, result) {
            if (self.selection().indexOf(result) === -1) {
                self.add(result);
            }
        });
    },
    removeAll: function() {
        var self = this;
        $.each(self.selection(), function(idx, selected) {
            self.remove(selected);
        });
    },
    selected: function(data) {
        var self = this;
        for (var idx = 0; idx < self.selection().length; idx++) {
            if (data.id === self.selection()[idx].id) {
                return true;
            }
        }
        return false;
    },
    submit: function() {
        var self = this;
        self.submitEnabled(false);
        var nodeIds = osfHelpers.mapByProperty(self.selection(), 'id');
        osfHelpers.postJSON(
            nodeApiUrl + 'pointer/', {
                nodeIds: nodeIds
            }
        ).done(function() {
            window.location.reload();
        }).fail(function(data) {
            self.submitEnabled(true);
            osfHelpers.handleJSONError(data);
        });
    },
    clear: function() {
        this.query('');
        this.results([]);
        this.selection([]);
    },
    authorText: function(node) {
        var rv = node.firstAuthor;
        if (node.etal) {
            rv += ' et al.';
        }
        return rv;
    }
});
コード例 #8
0
ファイル: contribAdder.js プロジェクト: adlius/osf.io
AddContributorViewModel = oop.extend(Paginator, {
    constructor: function (title, nodeId, parentId, parentTitle, options) {
        this.super.constructor.call(this);
        var self = this;

        self.title = title;
        self.nodeId = nodeId;
        self.nodeApiUrl = '/api/v1/project/' + self.nodeId + '/';
        self.parentId = parentId;
        self.parentTitle = parentTitle;
        self.async = options.async || false;
        self.callback = options.callback || function () {
            };
        self.nodesOriginal = {};
        //state of current nodes
        self.childrenToChange = ko.observableArray();
        self.nodesState = ko.observable();
        self.canSubmit = ko.observable(true);
        //nodesState is passed to nodesSelectTreebeard which can update it and key off needed action.
        self.nodesState.subscribe(function (newValue) {
            //The subscribe causes treebeard changes to change which nodes will be affected
            var childrenToChange = [];
            for (var key in newValue) {
                newValue[key].changed = newValue[key].checked !== self.nodesOriginal[key].checked;
                if (newValue[key].changed && key !== self.nodeId) {
                    childrenToChange.push(key);
                }
            }
            self.childrenToChange(childrenToChange);
            m.redraw(true);
        });

        //list of permission objects for select.
        self.permissionList = [
            {value: 'read', text: 'Read'},
            {value: 'write', text: 'Read + Write'},
            {value: 'admin', text: 'Administrator'}
        ];

        self.page = ko.observable('whom');
        self.pageTitle = ko.computed(function () {
            return {
                whom: 'Add Contributors',
                which: 'Select Components',
                invite: 'Add Unregistered Contributor'
            }[self.page()];
        });
        self.query = ko.observable();
        self.results = ko.observableArray([]);
        self.contributors = ko.observableArray([]);
        self.selection = ko.observableArray();

        self.contributorIDsToAdd = ko.pureComputed(function () {
            return self.selection().map(function (user) {
                return user.id;
            });
        });

        self.notification = ko.observable('');
        self.inviteError = ko.observable('');
        self.doneSearching = ko.observable(false);
        self.parentImport = ko.observable(false);
        self.totalPages = ko.observable(0);
        self.childrenToChange = ko.observableArray();

        self.emailSearch = ko.pureComputed(function () {
            var emailRegex = new RegExp('[^\\s]+@[^\\s]+\\.[^\\s]');
            if (emailRegex.test(String(self.query()))) {
                return true;
            } else {
                return false;
            }
        });

        self.foundResults = ko.pureComputed(function () {
            return self.query() && self.results().length && !self.parentImport();
        });

        self.parentPagination = ko.pureComputed(function () {
            return self.doneSearching() && self.parentImport();
        });

        self.noResults = ko.pureComputed(function () {
            return self.query() && !self.results().length && self.doneSearching();
        });

        self.showLoading = ko.pureComputed(function () {
            return !self.doneSearching() && !!self.query();
        });

        self.addAllVisible = ko.pureComputed(function () {
            var selected_ids = self.selection().map(function (user) {
                return user.id;
            });
            var contributors = self.contributors();
            return ($osf.any(
                $.map(self.results(), function (result) {
                    return contributors.indexOf(result.id) === -1 && selected_ids.indexOf(result.id) === -1;
                })
            ));
        });

        self.removeAllVisible = ko.pureComputed(function () {
            return self.selection().length > 0;
        });

        self.inviteName = ko.observable();
        self.inviteEmail = ko.observable();

        self.addingSummary = ko.computed(function () {
            var names = $.map(self.selection(), function (result) {
                return result.fullname;
            });
            return names.join(', ');
        });
    },
    hide: function () {
        $('.modal').modal('hide');
    },
    selectWhom: function () {
        this.page('whom');
    },
    selectWhich: function () {
        //when the next button is hit by the user, the nodes to change and disable are decided
        var self = this;
        var nodesState = self.nodesState();
        for (var key in nodesState) {
            var i;
            var node = nodesState[key];
            var enabled = nodesState[key].isAdmin;
            var checked = nodesState[key].checked;
            if (enabled) {
                var nodeContributors = [];
                for (i = 0; i < node.contributors.length; i++) {
                    nodeContributors.push(node.contributors[i].id);
                }
                for (i = 0; i < self.contributorIDsToAdd().length; i++) {
                    if (nodeContributors.indexOf(self.contributorIDsToAdd()[i]) < 0) {
                        enabled = true;
                        break;
                    }
                    else {
                        checked = true;
                        enabled = false;
                    }
                }
            }
            nodesState[key].enabled = enabled;
            nodesState[key].checked = checked;
        }
        self.nodesState(nodesState);
        this.page('which');
    },
    gotoInvite: function () {
        var self = this;
        self.inviteName(self.query());
        self.inviteError('');
        self.inviteEmail('');
        self.page('invite');
    },
    goToPage: function (page) {
        this.page(page);
    },
    /**
     * A simple Contributor model that receives data from the
     * contributor search endpoint. Adds an additional displayProjectsinCommon
     * attribute which is the human-readable display of the number of projects the
     * currently logged-in user has in common with the contributor.
     */
    startSearch: function () {
        this.parentImport(false);
        this.pageToGet(0);
        this.fetchResults();
    },
    fetchResults: function () {
        if (this.parentImport()){
            this.importFromParent();
        } else {
            var self = this;
            self.doneSearching(false);
            self.notification(false);
            if (self.query()) {
                return $.getJSON(
                    '/api/v1/user/search/', {
                        query: self.query(),
                        page: self.pageToGet
                    },
                    function (result) {
                        var contributors = result.users.map(function (userData) {
                            userData.added = (self.contributors().indexOf(userData.id) !== -1);
                            return new Contributor(userData);
                        });
                        self.doneSearching(true);
                        self.results(contributors);
                        self.currentPage(result.page);
                        self.numberOfPages(result.pages);
                        self.addNewPaginators(false);
                    }
                );
            } else {
                self.results([]);
                self.currentPage(0);
                self.totalPages(0);
                self.doneSearching(true);
            }
        }
    },
    getContributors: function () {
        var self = this;
        self.notification(false);
        var url = $osf.apiV2Url('nodes/' + window.contextVars.node.id + '/contributors/');

        return $.ajax({
            url: url,
            type: 'GET',
            dataType: 'json',
            contentType: 'application/vnd.api+json;',
            crossOrigin: true,
            xhrFields: {withCredentials: true},
            processData: false
        }).done(function (response) {
            var contributors = response.data.map(function (contributor) {
                // contrib ID has the form <nodeid>-<userid>
                return contributor.id.split('-')[1];
            });
            self.contributors(contributors);
        });
    },
    startSearchParent: function () {
        this.parentImport(true);
        this.importFromParent();
    },
    importFromParent: function () {
        var self = this;
        self.doneSearching(false);
        self.notification(false);
        return $.getJSON(
            self.nodeApiUrl + 'get_contributors_from_parent/', {},
            function (result) {
                var contributors = result.contributors.map(function (user) {
                    var added = (self.contributors().indexOf(user.id) !== -1);
                    var updatedUser = $.extend({}, user, {added: added});
                    return updatedUser;
                });
                var pageToShow = [];
                var startingSpot = (self.pageToGet() * 5);
                if (contributors.length > startingSpot + 5){
                    for (var iterate = startingSpot; iterate < startingSpot + 5; iterate++) {
                        pageToShow.push(contributors[iterate]);
                    }
                } else {
                    for (var iterateTwo = startingSpot; iterateTwo < contributors.length; iterateTwo++) {
                        pageToShow.push(contributors[iterateTwo]);
                    }
                }
                self.doneSearching(true);
                self.results(pageToShow);
                self.currentPage(self.pageToGet());
                self.numberOfPages(Math.ceil(contributors.length/5));
                self.addNewPaginators(true);
            }
        );
    },
    addTips: function (elements) {
        elements.forEach(function (element) {
            $(element).find('.contrib-button').tooltip();
        });
    },
    afterRender: function (elm, data) {
        var self = this;
        self.addTips(elm, data);
    },
    makeAfterRender: function () {
        var self = this;
        return function (elm, data) {
            return self.afterRender(elm, data);
        };
    },
    /** Validate the invite form. Returns a string error message or
     *   true if validation succeeds.
     */
    validateInviteForm: function () {
        var self = this;
        // Make sure Full Name is not blank
        if (!self.inviteName().trim().length) {
            return 'Full Name is required.';
        }
        if (self.inviteEmail() && !$osf.isEmail(self.inviteEmail())) {
            return 'Not a valid email address.';
        }
        // Make sure that entered email is not already in selection
        for (var i = 0, contrib; contrib = self.selection()[i]; ++i) {
            if (contrib.email) {
                var contribEmail = contrib.email.toLowerCase().trim();
                if (contribEmail === self.inviteEmail().toLowerCase().trim()) {
                    return self.inviteEmail() + ' is already in queue.';
                }
            }
        }
        return true;
    },
    postInvite: function () {
        var self = this;
        self.inviteError('');
        self.canSubmit(false);

        var validated = self.validateInviteForm();
        if (typeof validated === 'string') {
            self.inviteError(validated);
            return false;
        }
        return self.postInviteRequest(self.inviteName(), self.inviteEmail());
    },
    add: function (data) {
        var self = this;
        data.permission = ko.observable(self.permissionList[1]); //default permission write
        // All manually added contributors are visible
        data.visible = true;
        this.selection.push(data);
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.contrib-button').tooltip();
    },
    remove: function (data) {
        this.selection.splice(
            this.selection.indexOf(data), 1
        );
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.contrib-button').tooltip();
    },
    addAll: function () {
        var self = this;
        var selected_ids = self.selection().map(function (user) {
            return user.id;
        });
        $.each(self.results(), function (idx, result) {
            if (selected_ids.indexOf(result.id) === -1 && self.contributors().indexOf(result.id) === -1) {
                self.add(result);
            }
        });
    },
    removeAll: function () {
        var self = this;
        $.each(self.selection(), function (idx, selected) {
            self.remove(selected);
        });
    },
    selected: function (data) {
        var self = this;
        for (var idx = 0; idx < self.selection().length; idx++) {
            if (data.id === self.selection()[idx].id) {
                return true;
            }
        }
        return false;
    },
    selectAllNodes: function () {
        //select all nodes to add a contributor to.  THe changed variable is set here for timing between
        // treebeard and knockout
        var self = this;
        var nodesState = ko.toJS(self.nodesState());
        for (var key in nodesState) {
            if (nodesState[key].enabled) {
                nodesState[key].checked = true;
            }
        }
        self.nodesState(nodesState);
    },
    selectNoNodes: function () {
        //select no nodes to add a contributor to.  THe changed variable is set here for timing between
        // treebeard and knockout
        var self = this;
        var nodesState = ko.toJS(self.nodesState());
        for (var key in nodesState) {
            if (nodesState[key].enabled && nodesState[key].checked) {
                nodesState[key].checked = false;
            }
        }
        self.nodesState(nodesState);
    },
    submit: function () {
        var self = this;
        $osf.block();
        var url = self.nodeApiUrl + 'contributors/';
        return $osf.postJSON(
            url, {
                users: ko.utils.arrayMap(self.selection(), function (user) {
                    var permission = user.permission().value; //removes the value from the object
                    var tUser = JSON.parse(ko.toJSON(user)); //The serialized user minus functions
                    tUser.permission = permission; //shoving the permission value into permission
                    return tUser; //user with simplified permissions
                }),
                node_ids: self.childrenToChange()
            }
        ).done(function (response) {
            if (self.async) {
                self.contributors($.map(response.contributors, function (contrib) {
                    return contrib.id;
                }));
                self.hide();
                $osf.unblock();
                if (self.callback) {
                    self.callback(response);
                }
            } else {
                window.location.reload();
            }
        }).fail(function (xhr, status, error) {
            self.hide();
            $osf.unblock();
            var errorMessage = lodashGet(xhr, 'responseJSON.message') || ('There was a problem trying to add contributors.' + osfLanguage.REFRESH_OR_SUPPORT);
            $osf.growl('Could not add contributors', errorMessage);
            Raven.captureMessage('Error adding contributors', {
                extra: {
                    url: url,
                    status: status,
                    error: error
                }
            });
        });
    },
    clear: function () {
        var self = this;
        self.page('whom');
        self.parentImport(false);
        self.query('');
        self.results([]);
        self.selection([]);
        self.childrenToChange([]);
        self.notification(false);
    },
    postInviteRequest: function (fullname, email) {
        var self = this;
        return $osf.postJSON(
            self.nodeApiUrl + 'invite_contributor/', {
                'fullname': fullname,
                'email': email
            }
        ).done(
            self.onInviteSuccess.bind(self)
        ).fail(
            self.onInviteError.bind(self)
        );
    },
    onInviteSuccess: function (result) {
        var self = this;
        self.query('');
        self.results([]);
        self.page('whom');
        self.add(result.contributor);
        self.canSubmit(true);
    },
    onInviteError: function (xhr) {
        var self = this;
        var response = JSON.parse(xhr.responseText);
        // Update error message
        self.inviteError(response.message);
        self.canSubmit(true);
    },
    hasChildren: function() {
        var self = this;
        return (Object.keys(self.nodesOriginal).length > 1);
    },
    /**
     * get node tree for treebeard from API V1
     */
    fetchNodeTree: function (treebeardUrl) {
        var self = this;
        return $.ajax({
            url: treebeardUrl,
            type: 'GET',
            dataType: 'json'
        }).done(function (response) {
            self.nodesOriginal = projectSettingsTreebeardBase.getNodesOriginal(response[0], self.nodesOriginal);
            var nodesState = $.extend(true, {}, self.nodesOriginal);
            var nodeParent = response[0].node.id;
            //parent node is changed by default
            nodesState[nodeParent].checked = true;
            //parent node cannot be changed
            nodesState[nodeParent].isAdmin = false;
            self.nodesState(nodesState);
        }).fail(function (xhr, status, error) {
            $osf.growl('Error', 'Unable to retrieve project settings');
            Raven.captureMessage('Could not GET project settings.', {
                extra: {
                    url: treebeardUrl, status: status, error: error
                }
            });
        });
    }
});
コード例 #9
0
var $osf = require('js/osfHelpers');

var FolderPickerNodeConfigVM = require('js/folderPickerNodeConfig');
var FolderPicker = require('js/folderpicker');
var testUtils = require('./folderPickerTestUtils.js');

var onPickFolderSpy = new sinon.spy();
var resolveLazyloadUrlSpy = new sinon.spy();
var TestSubclassVM = oop.extend(FolderPickerNodeConfigVM, {
    constructor: function(addonName, url, selector, folderPicker) {
        this.super.constructor.call(this, addonName, url, selector, folderPicker);
        this.customField = ko.observable('');

        this.messages.submitSettingsSuccess = ko.pureComputed(function(){
            return 'SUCCESS';
        });
    },
    _updateCustomFields: function(settings) {
        this.customField(settings.customField);
    },
    _serializeSettings: function(settings) {
        return this.folder().name.toUpperCase();
    }
});

describe('FolderPickerNodeConfigViewModel', () => {

    var settingsUrl = '/api/v1/12345/addon/config/';
    var endpoints = [{
        method: 'GET',
        url: settingsUrl,
        response: {
コード例 #10
0
ファイル: contribAdder.js プロジェクト: huangginny/osf.io
var AddContributorViewModel = oop.extend(Paginator, {
    constructor: function(title, parentId, parentTitle) {
        this.super.constructor();
        var self = this;

        self.permissions = ['read', 'write', 'admin'];

        self.title = title;
        self.parentId = parentId;
        self.parentTitle = parentTitle;

        self.page = ko.observable('whom');
        self.pageTitle = ko.computed(function() {
            return {
                whom: 'Add Contributors',
                which: 'Select Components',
                invite: 'Add Unregistered Contributor'
            }[self.page()];
        });
        self.query = ko.observable();
        self.results = ko.observableArray([]);
        self.selection = ko.observableArray();
        self.notification = ko.observable('');
        self.inviteError = ko.observable('');
        self.totalPages = ko.observable(0);
        self.nodes = ko.observableArray([]);
        self.nodesToChange = ko.observableArray();
        $.getJSON(
            nodeApiUrl + 'get_editable_children/', {},
            function(result) {
                $.each(result.children || [], function(idx, child) {
                    child.margin = NODE_OFFSET + child.indent * NODE_OFFSET + 'px';
                });
                self.nodes(result.children);
            }
        );
        self.foundResults = ko.computed(function() {
            return self.query() && self.results().length;
        });

        self.noResults = ko.computed(function() {
            return self.query() && !self.results().length;
        });

        self.inviteName = ko.observable();
        self.inviteEmail = ko.observable();

        self.addingSummary = ko.computed(function() {
            var names = $.map(self.selection(), function(result) {
                return result.fullname;
            });
            return names.join(', ');
        });
    },
    selectWhom: function() {
        this.page('whom');
    },
    selectWhich: function() {
        this.page('which');
    },
    gotoInvite: function() {
        var self = this;
        self.inviteName(self.query());
        self.inviteError('');
        self.inviteEmail('');
        self.page('invite');
    },
    goToPage: function(page) {
        this.page(page);
    },
    /**
     * A simple Contributor model that receives data from the
     * contributor search endpoint. Adds an addiitonal displayProjectsinCommon
     * attribute which is the human-readable display of the number of projects the
     * currently logged-in user has in common with the contributor.
     */
    startSearch: function() {
        this.currentPage(0);
        this.search();
    },
    search: function() {
        var self = this;
        self.notification(false);
        if (self.query()) {
            return $.getJSON(
                '/api/v1/user/search/', {
                    query: self.query(),
                    excludeNode: nodeId,
                    page: self.currentPage
                },
                function(result) {
                    var contributors = result.users.map(function(userData) {
                        return new Contributor(userData);
                    });
                    self.results(contributors);
                    self.currentPage(result.page);
                    self.numberOfPages(result.pages);
                    self.addNewPaginators();
                }
            );
        } else {
            self.results([]);
            self.currentPage(0);
            self.totalPages(0);
        }
    },
    importFromParent: function() {
        self.notification(false);
        $.getJSON(
            nodeApiUrl + 'get_contributors_from_parent/', {},
            function(result) {
                if (!result.contributors.length) {
                    self.notification({
                        'message': 'All contributors from parent already included.',
                        'level': 'info'
                    });
                }
                self.results(result.contributors);
            }
        );
    },
    recentlyAdded: function() {
        var self = this;
        self.notification(false);
        var url = nodeApiUrl + 'get_recently_added_contributors/?max=' + MAX_RECENT.toString();
        return $.getJSON(
            url, {},
            function(result) {
                if (!result.contributors.length) {
                    self.notification({
                        'message': 'All recent collaborators already included.',
                        'level': 'info'
                    });
                }
                var contribs = [];
                var numToDisplay = result.contributors.length;
                for (var i = 0; i < numToDisplay; i++) {
                    contribs.push(new Contributor(result.contributors[i]));
                }
                self.results(contribs);
                self.numberOfPages(1);
            }
        ).fail(function(xhr, textStatus, error) {
            self.notification({
                'message': 'OSF was unable to resolve your request. If this issue persists, ' +
                    'please report it to <a href="mailto:support@osf.io">support@osf.io</a>.',
                'level': 'warning'
            });
            Raven.captureMessage('Could not GET recentlyAdded contributors.', {
                url: url,
                textStatus: textStatus,
                error: error
            });
        });
    },
    mostInCommon: function() {
        var self = this;
        self.notification(false);
        var url = nodeApiUrl + 'get_most_in_common_contributors/?max=' + MAX_RECENT.toString();
        return $.getJSON(
            url, {},
            function(result) {
                if (!result.contributors.length) {
                    self.notification({
                        'message': 'All frequent collaborators already included.',
                        'level': 'info'
                    });
                }
                var contribs = [];
                var numToDisplay = result.contributors.length;
                for (var i = 0; i < numToDisplay; i++) {
                    contribs.push(new Contributor(result.contributors[i]));
                }
                self.results(contribs);
                self.numberOfPages(1);
            }
        ).fail(function(xhr, textStatus, error) {
            self.notification({
                'message': 'OSF was unable to resolve your request. If this issue persists, ' +
                    'please report it to <a href="mailto:support@osf.io">support@osf.io</a>.',
                'level': 'warning'
            });
            Raven.captureMessage('Could not GET mostInCommon contributors.', {
                url: url,
                textStatus: textStatus,
                error: error
            });
        });
    },
    addTips: function(elements) {
        elements.forEach(function(element) {
            $(element).find('.contrib-button').tooltip();
        });
    },
    setupEditable: function(elm, data) {
        var $elm = $(elm);
        var $editable = $elm.find('.permission-editable');
        $editable.editable({
            showbuttons: false,
            value: 'admin',
            source: [{
                value: 'read',
                text: 'Read'
            }, {
                value: 'write',
                text: 'Read + Write'
            }, {
                value: 'admin',
                text: 'Administrator'
            }],
            success: function(response, value) {
                data.permission(value);
            }
        });
    },
    afterRender: function(elm, data) {
        var self = this;
        self.addTips(elm, data);
        self.setupEditable(elm, data);
    },
    makeAfterRender: function() {
        var self = this;
        return function(elm, data) {
            return self.afterRender(elm, data);
        };
    },
    /** Validate the invite form. Returns a string error message or
     *   true if validation succeeds.
     */
    validateInviteForm: function() {
        var self = this;
        // Make sure Full Name is not blank
        if (!self.inviteName().trim().length) {
            return 'Full Name is required.';
        }
        if (self.inviteEmail() && !$osf.isEmail(self.inviteEmail())) {
            return 'Not a valid email address.';
        }
        // Make sure that entered email is not already in selection
        for (var i = 0, contrib; contrib = self.selection()[i]; ++i) {
            var contribEmail = contrib.email.toLowerCase().trim();
            if (contribEmail === self.inviteEmail().toLowerCase().trim()) {
                return self.inviteEmail() + ' is already in queue.';
            }
        }
        return true;
    },
    postInvite: function() {
        var self = this;
        self.inviteError('');
        var validated = self.validateInviteForm();
        if (typeof validated === 'string') {
            self.inviteError(validated);
            return false;
        }
        return self.postInviteRequest(self.inviteName(), self.inviteEmail());
    },
    add: function(data) {
        data.permission = ko.observable('admin');
        // All manually added contributors are visible
        data.visible = true;
        this.selection.push(data);
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.contrib-button').tooltip();
    },
    remove: function(data) {
        this.selection.splice(
            this.selection.indexOf(data), 1
        );
        // Hack: Hide and refresh tooltips
        $('.tooltip').hide();
        $('.contrib-button').tooltip();
    },
    addAll: function() {
        $.each(this.results(), function(idx, result) {
            if (this.selection().indexOf(result) === -1) {
                this.add(result);
            }
        });
    },
    removeAll: function() {
        $.each(this.selection(), function(idx, selected) {
            this.remove(selected);
        });
    },
    cantSelectNodes: function() {
        return this.nodesToChange().length === this.nodes().length;
    },
    cantDeselectNodes: function() {
        return this.nodesToChange().length === 0;
    },
    selectNodes: function() {
        this.nodesToChange($osf.mapByProperty(this.nodes(), 'id'));
    },
    deselectNodes: function() {
        this.nodesToChange([]);
    },
    selected: function(data) {
        for (var idx = 0; idx < this.selection().length; idx++) {
            if (data.id === this.selection()[idx].id) {
                return true;
            }
        }
        return false;
    },
    submit: function() {
        var self = this;
        $osf.block();
        return $osf.postJSON(
            nodeApiUrl + 'contributors/', {
                users: self.selection().map(function(user) {
                    return ko.toJS(user);
                }),
                node_ids: self.nodesToChange()
            }
        ).done(function() {
            window.location.reload();
        }).fail(function() {
            $('.modal').modal('hide');
            $osf.unblock();
            $osf.growl('Error', 'Add contributor failed.');
        });
    },
    clear: function() {
        var self = this;
        self.page('whom');
        self.query('');
        self.results([]);
        self.selection([]);
        self.nodesToChange([]);
        self.notification(false);
    },
    postInviteRequest: function(fullname, email) {
        var self = this;
        return $osf.postJSON(
            nodeApiUrl + 'invite_contributor/', {
                'fullname': fullname,
                'email': email
            }
        ).done(
            self.onInviteSuccess.bind(self)
        ).fail(
            self.onInviteError.bind(self)
        );
    },
    onInviteSuccess: function(result) {
        var self = this;
        self.query('');
        self.results([]);
        self.page('whom');
        self.add(result.contributor);
    },
    onInviteError: function(xhr) {
        var response = JSON.parse(xhr.responseText);
        // Update error message
        this.inviteError(response.message);
    }
});
コード例 #11
0
ファイル: s3NodeConfig.js プロジェクト: atelic/osf.io
var s3FolderPickerViewModel = oop.extend(OauthAddonFolderPicker, {
    bucketLocations: s3Settings.bucketLocations,

    constructor: function(addonName, url, selector, folderPicker, opts, tbOpts) {
        var self = this;
        self.super.constructor(addonName, url, selector, folderPicker, tbOpts);
        // Non-OAuth fields
        self.accessKey = ko.observable('');
        self.secretKey = ko.observable('');
        // Treebeard config
        self.treebeardOptions = $.extend(
            {},
            OauthAddonFolderPicker.prototype.treebeardOptions,
            {   // TreeBeard Options
                columnTitles: function() {
                    return [{
                        title: 'Buckets',
                        width: '75%',
                        sort: false
                    }, {
                        title: 'Select',
                        width: '25%',
                        sort: false
                    }];
                },
                resolveToggle: function(item) {
                    return '';
                },
                resolveIcon: function(item) {
                    return m('i.fa.fa-folder-o', ' ');
                },
            },
            tbOpts
        );
    },

    connectAccount: function() {
        var self = this;
        if( !self.accessKey() && !self.secretKey() ){
            self.changeMessage('Please enter both an API access key and secret key.', 'text-danger');
            return;
        }

        if (!self.accessKey() ){
            self.changeMessage('Please enter an API access key.', 'text-danger');
            return;
        }

        if (!self.secretKey() ){
            self.changeMessage('Please enter an API secret key.', 'text-danger');
            return;
        }
        $osf.block();

        return $osf.postJSON(
            self.urls().create, {
                secret_key: self.secretKey(),
                access_key: self.accessKey()
            }
        ).done(function(response) {
            $osf.unblock();
            self.clearModal();
            $('#s3InputCredentials').modal('hide');
            self.changeMessage('Successfully added S3 credentials.', 'text-success', null, true);
            self.updateFromData(response);
            self.importAuth();
        }).fail(function(xhr, status, error) {
            $osf.unblock();
            var message = '';
            var response = JSON.parse(xhr.responseText);
            if (response && response.message) {
                message = response.message;
            }
            self.changeMessage(message, 'text-danger');
            Raven.captureMessage('Could not add S3 credentials', {
                url: self.urls().importAuth,
                textStatus: status,
                error: error
            });
        });
    },
    /**
     * Tests if the given string is a valid Amazon S3 bucket name.  Supports two modes: strict and lax.
     * Strict is for bucket creation and follows the guidelines at:
     *
     *   http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html#bucketnamingrules
     *
     * However, the US East (N. Virginia) region currently permits much laxer naming rules.  The S3
     * docs claim this will be changed at some point, but to support our user's already existing
     * buckets, we provide the lax mode checking.
     *
     * Strict checking is the default.
     *
     * @param {String} bucketName user-provided name of bucket to validate
     * @param {Boolean} laxChecking whether to use the more permissive validation
     */
    isValidBucketName: function(bucketName, laxChecking) {
        if (laxChecking === true) {
            return /^[a-zA-Z0-9.\-_]{1,255}$/.test(bucketName);
        }
        var label = '[a-z0-9]+(?:[a-z0-9\-]*[a-z0-9])?';
        var strictBucketName = new RegExp('^' + label + '(?:\\.' + label + ')*$');
        var isIpAddress = /^[0-9]+(?:\.[0-9]+){3}$/;
        return bucketName.length >= 3 && bucketName.length <= 63 &&
            strictBucketName.test(bucketName) && !isIpAddress.test(bucketName);
    }, 

    /** Reset all fields from S3 credentials input modal */
    clearModal: function() {
        var self = this;
        self.message('');
        self.messageClass('text-info');
        self.secretKey(null);
        self.accessKey(null);
    },

    createBucket: function(self, bucketName, bucketLocation) {
        $osf.block();
        bucketName = bucketName.toLowerCase();
        return $osf.postJSON(
            self.urls().createBucket, {
                bucket_name: bucketName,
                bucket_location: bucketLocation
            }
        ).done(function(response) {
            $osf.unblock();
            self.loadedFolders(false);
            self.activatePicker();
            var msg = 'Successfully created bucket "' + $osf.htmlEscape(bucketName) + '". You can now select it from the list.';
            var msgType = 'text-success';
            self.changeMessage(msg, msgType, null, true);
        }).fail(function(xhr) {
            var resp = JSON.parse(xhr.responseText);
            var message = resp.message;
            var title = resp.title || 'Problem creating bucket';
            $osf.unblock();
            if (!message) {
                message = 'Looks like that name is taken. Try another name?';
            }
            bootbox.confirm({
                title: $osf.htmlEscape(title),
                message: $osf.htmlEscape(message),
                callback: function(result) {
                    if (result) {
                        self.openCreateBucket();
                    }
                },
                buttons:{
                    confirm:{
                        label:'Try again'
                    }
                }
            });
        });
    },

    openCreateBucket: function() {
        var self = this;

        // Generates html options for key-value pairs in BUCKET_LOCATION_MAP
        function generateBucketOptions(locations) {
            var options = '';
            for (var location in locations) {
                if (self.bucketLocations.hasOwnProperty(location)) {
                    options = options + ['<option value="', location, '">', $osf.htmlEscape(locations[location]), '</option>', '\n'].join('');
                }
            }
            return options;
        }

        bootbox.dialog({
            title: 'Create a new bucket',
            message:
                    '<div class="row"> ' +
                        '<div class="col-md-12"> ' +
                            '<form class="form-horizontal" onsubmit="return false"> ' +
                                '<div class="form-group"> ' +
                                    '<label class="col-md-4 control-label" for="bucketName">Bucket Name</label> ' +
                                    '<div class="col-md-8"> ' +
                                        '<input id="bucketName" name="bucketName" type="text" placeholder="Enter bucket name" class="form-control" autofocus> ' +
                                        '<div>' +
                                            '<span id="bucketModalErrorMessage" ></span>' +
                                        '</div>'+
                                    '</div>' +
                                '</div>' +
                                '<div class="form-group"> ' +
                                    '<label class="col-md-4 control-label" for="bucketLocation">Bucket Location</label> ' +
                                    '<div class="col-md-8"> ' +
                                        '<select id="bucketLocation" name="bucketLocation" class="form-control"> ' +
                                            generateBucketOptions(self.bucketLocations) +
                                        '</select>' +
                                    '</div>' +
                                '</div>' +
                            '</form>' +
                            '<span>For more information on locations, click ' +
                                '<a href="http://www.bucketexplorer.com/documentation/amazon-s3--amazon-s3-buckets-and-regions.html">here</a>' + 
                            '</span>' +
                        '</div>' +
                    '</div>',
            buttons: {
                cancel: {
                    label: 'Cancel',
                    className: 'btn-default'
                },
                confirm: {
                    label: 'Create',
                    className: 'btn-success',
                    callback: function () {
                        var bucketName = $('#bucketName').val();
                        var bucketLocation = $('#bucketLocation').val();

                        if (!bucketName) {
                            var errorMessage = $('#bucketModalErrorMessage');
                            errorMessage.text('Bucket name cannot be empty');
                            errorMessage[0].classList.add('text-danger');
                            return false;
                        } else if (!self.isValidBucketName(bucketName, false)) {
                            bootbox.confirm({
                                title: 'Invalid bucket name',
                                message: 'Amazon S3 buckets can contain lowercase letters, numbers, and hyphens separated by' +
                                ' periods.  Please try another name.',
                                callback: function (result) {
                                    if (result) {
                                        self.openCreateBucket();
                                    }
                                },
                                buttons: {
                                    confirm: {
                                        label: 'Try again'
                                    }
                                }
                            });
                        } else {
                            self.createBucket(self, bucketName, bucketLocation);
                        }
                    }
                }
            }
        });
    }
});
コード例 #12
0
var ProjectSettings = oop.extend(
    ChangeMessageMixin,
    {
        constructor: function(params) {
            this.super.constructor.call(this);
            var self = this;

            self.title = ko.observable(params.currentTitle).extend({
                required: {
                    params: true,
                    message: 'Title cannot be blank.'
                }});
            self.description = ko.observable(params.currentDescription);
            self.titlePlaceholder = params.currentTitle;
            self.descriptionPlaceholder = params.currentDescription;

            self.categoryOptions = params.categoryOptions;
            self.categoryPlaceholder = params.category;
            self.selectedCategory = ko.observable(params.category);

            self.disabled = params.disabled || false;

            if (!params.updateUrl) {
                throw new Error(language.instantiationErrorMessage);
            }

            self.updateUrl = params.updateUrl;
            self.node_id = params.node_id;

            self.originalProjectSettings = ko.observable(self.serialize());
            self.dirty = ko.pureComputed(function(){
                return JSON.stringify(self.originalProjectSettings()) !== JSON.stringify(self.serialize());
            });
        },
        /*error handler*/
        updateError: function(xhr, status, error) {
            var self = this;
            var errorMessage;
            if (error === 'BAD REQUEST') {
                self.changeMessage(language.updateErrorMessage400, 'text-danger');
                errorMessage = language.updateErrorMessage400;
            }
            else {
                self.changeMessage(language.updateErrorMessage, 'text-danger');
                errorMessage = language.updateErrorMessage;
            }
            Raven.captureMessage(errorMessage, {
                url: self.updateUrl,
                textStatus: status,
                err: error
            });
        },
        /*update handler*/
        updateAll: function() {
            var self = this;
            if (!self.dirty()){
                self.changeMessage(language.updateSuccessMessage, 'text-success');
                return;
            }
            var requestPayload = self.serialize();
            var request = $osf.ajaxJSON('patch',
                self.updateUrl,
                { data: requestPayload,
                isCors: true });
            request.done(function(response) {
                self.categoryPlaceholder = response.data.attributes.category;
                self.titlePlaceholder = response.data.attributes.title;
                self.descriptionPlaceholder = response.data.attributes.description;
                self.selectedCategory(self.categoryPlaceholder);
                self.title(self.titlePlaceholder);
                self.description(self.descriptionPlaceholder);
                self.originalProjectSettings(self.serialize());
                self.changeMessage(language.updateSuccessMessage, 'text-success');
            });
            request.fail(self.updateError.bind(self));
            return request;
        },
        /*cancel handler*/
        cancelAll: function() {
            var self = this;
            self.selectedCategory(self.categoryPlaceholder);
            self.title(self.titlePlaceholder);
            self.description(self.descriptionPlaceholder);
            self.resetMessage();
        },
        serialize: function() {
            var self = this;
            return {
                data: {
                    type: 'nodes',
                    id:   self.node_id,
                    attributes: {
                        title: self.title(),
                        category: self.selectedCategory(),
                        description: self.description(),
                    }
                }
            };
        }
    });
コード例 #13
0
var RegistrationRetractionViewModel = oop.extend(
    ChangeMessageMixin,
    {
        constructor: function(submitUrl, registrationTitle) {
            this.super.constructor.call(this);

            var self = this;

            self.submitUrl = submitUrl;
            self.registrationTitle = $osf.htmlDecode(registrationTitle);
            // Truncate title to around 50 chars
            var parts = self.registrationTitle.slice(0, 50).split(' ');
            if (parts.length > 1) {
                self.truncatedTitle = parts.slice(0, -1).join(' ');
            }
            else {
                self.truncatedTitle = parts[0];
            }

            self.justification = ko.observable('').extend({
                maxLength: 2048
            });
            self.confirmationText = ko.observable().extend({
                required: true,
                mustEqual: self.truncatedTitle
            });
            self.disableSave = ko.observable(false);
            self.valid = ko.computed(function(){
                return !self.disableSave() && self.confirmationText.isValid();
            });
        },
        SUBMIT_ERROR_MESSAGE: 'Error submitting your retraction request, please try again. If the problem ' +
                'persists, email <a href="mailto:support@osf.iop">support@osf.io</a>',
        CONFIRMATION_ERROR_MESSAGE: 'Please enter the registration title before clicking Retract Registration',
        JUSTIFICATON_ERROR_MESSAGE: 'Your justification is too long, please enter a justification with no more ' +
            'than 2048 characters long.',
        MESSAGE_ERROR_CLASS: 'text-danger',
        onSubmitSuccess: function(response) {            
            window.location = response.redirectUrl;
        },
        onSubmitError: function(xhr, status, errorThrown) {
            var self = this;
            self.disableSave(false);
            self.changeMessage(self.SUBMIT_ERROR_MESSAGE, self.MESSAGE_ERROR_CLASS);
            Raven.captureMessage('Could not submit registration retraction.', {
                xhr: xhr,
                status: status,
                error: errorThrown
            });
        },
        submit: function() {
            var self = this;
            self.disableSave(true);
            // Show errors if invalid
            if (!self.confirmationText.isValid()) {
                self.changeMessage(self.CONFIRMATION_ERROR_MESSAGE, self.MESSAGE_ERROR_CLASS);
                return false;
            } else if (!self.justification.isValid()) {
                self.changeMessage(self.JUSTIFICATON_ERROR_MESSAGE, self.MESSAGE_ERROR_CLASS);
                return false;
            } else {
                // Else Submit
                return $osf.postJSON(self.submitUrl, ko.toJS(self))
                    .done(self.onSubmitSuccess.bind(self))
                    .fail(self.onSubmitError.bind(self));
            }
        }
});
コード例 #14
0
ファイル: logFeed.js プロジェクト: PatrickEGorman/osf.io
var LogsViewModel = oop.extend(Paginator, {
    constructor: function(logs, url) {
        this.super.constructor.call(this);
        var self = this;
        self.loading = ko.observable(false);
        self.logs = ko.observableArray(logs);
        self.url = url;

        self.tzname = ko.pureComputed(function() {
            var logs = self.logs();
            if (logs.length) {
                var tz =  moment(logs[0].date.date).format('ZZ');
                return tz;
            }
            return '';
        });
    },
    //send request to get more logs when the more button is clicked
    fetchResults: function(){
        var self = this;
        self.loading(true);
        return $.ajax({
            type: 'get',
            url: self.url,
            data:{
                page: self.currentPage()
            },
            cache: false
        }).done(function(response) {
            self.loading(false);
            // Initialize LogViewModel
            self.logs.removeAll();
            var logModelObjects = createLogs(response.logs); // Array of Log model objects
            for (var i=0; i<logModelObjects.length; i++) {
                self.logs.push(logModelObjects[i]);
            }
            self.currentPage(response.page);
            self.numberOfPages(response.pages);
            self.addNewPaginators();
        }).fail(
            $osf.handleJSONError
        ).fail(function() {
            self.loading(false);
        });

    }
});
コード例 #15
0
ファイル: formViewModel.js プロジェクト: 545zhou/osf.io
/*
 * Maintains the base class for knockoutJS form ViewModels
 */
'use strict';

var ko = require('knockout');

var $osf = require('js/osfHelpers');
var oop = require('js/oop');

var ValidationError = oop.extend(Error, {
    constructor: function (messages, header, level) {
        this.super.constructor.call(this);
        this.messages = messages || [];
        this.header = header || 'Error';
        this.level = level || 'warning';
    }
});

/**
* Base class KO viewmodel based forms should inherit from.
*
* Note: isValid needs to be implemented by subclasses and onError can
* optionally be implemented by subclasses to handle ValidationError(s) as desired.
*/
var FormViewModel = oop.defclass({
    constructor: function() {},
    isValid: function() {
        throw new Error('FormViewModel subclass must implement isValid');
    },
    onError: function(validationError) {
コード例 #16
0
ファイル: statistics.js プロジェクト: adlius/osf.io
var ChartUniqueVisits = oop.extend(UserFacingChart, {
    constructor: function(params) {
        this.super.constructor.call(this, params);
    },
    baseQuery: function() {
        var self = this;
        return {
            type: 'count_unique',
            params: {
                event_collection: 'pageviews',
                interval: 'daily',
                target_property: 'anon.id'
            }
        };
    },
    _initDataviz: function() {
        var self = this;
        return self.super._initDataviz.call(self)
            .chartType('line')
            .dateFormat('%b %d')
            .chartOptions({
                tooltip: {
                    format: {
                        title: function(x) { return x.toDateString(); },
                        name: function() { return 'Visits'; }
                    }
                },
                axis: {
                    y: {
                        tick: {
                            format: self._helpers.hideNonIntegers,
                        }
                    },
                    x: {
                        tick: {
                            fit: false,
                        },
                    },
                },
            });
    },
});
コード例 #17
0
/*global describe, it, expect, example, before, after, beforeEach, afterEach, mocha, sinon*/
'use strict';

var Paginator = require('js/paginator');
var oop = require('js/oop');
var assert = require('chai').assert;
sinon.assert.expose(assert, {prefix: ''});

var spy = new sinon.spy();

var TestPaginator = oop.extend(Paginator, {
    constructor: function(){
        this.super.constructor();
    },
    fetchResults: spy,
    configure: function(config){
        config(this);
    }
});


describe.skip('Paginator', () => {
    var paginator;
    var numberOfPages;
    var currentPage;
    var pageToGet;

    beforeEach(() => {
        paginator = new TestPaginator();
    });
コード例 #18
0
var AddonFolderPickerViewModel = oop.extend(FolderPickerViewModel, {
    constructor: function(addonName, url, selector, folderPicker, opts) {
        var self = this;
        self.super.constructor.call(self, addonName, url, selector, folderPicker);
        // externalAccounts
        self.accounts = ko.observable([]);
        self.selectedFolderType = ko.pureComputed(function() {
            var userHasAuth = self.userHasAuth();
            var selected = self.selected();
            return (userHasAuth && selected) ? selected.type : '';
        });
        self.messages.submitSettingsSuccess =  ko.pureComputed(function() {
            var name = self.options.decodeFolder($osf.htmlEscape(self.folder().name));
            return 'Successfully linked "' + name + '". Go to the <a href="' +
                self.urls().files + '">Files page</a> to view your content.';
        });
        // Overrides
        var defaults = {
            onPickFolder: function(evt, item) {
                evt.preventDefault();
                var name = item.data.path !== '/' ? item.data.path : '/ (Full ' + self.addonName + ')';
                self.selected({
                    name: name,
                    path: item.data.path,
                    id: item.data.id
                });
                return false; // Prevent event propagation
            },
            connectAccount: function() {
                window.location.href = this.urls().auth;
            },
            decodeFolder: function(folder_name) {
                return folder_name;
            }
        };
        // Overrides
        self.options = $.extend({}, defaults, opts);
        // Treebeard config
        self.treebeardOptions = $.extend(
            {}, 
            FolderPickerViewModel.prototype.treebeardOptions,
            {
                onPickFolder: function(evt, item) {
                    return this.options.onPickFolder.call(this, evt, item);
                }.bind(this),
                resolveLazyloadUrl: function(item) {
                    return item.data.urls.folders;
                },
                decodeFolder: function(item) {
                    return this.options.decodeFolder.call(this, item);
                }.bind(this)

            }
        );

        self.folderName = ko.pureComputed(function () {
            var nodeHasAuth = self.nodeHasAuth();
            var folder = self.folder();
            var folder_name = self.options.decodeFolder((nodeHasAuth && folder && folder.name) ? folder.name.trim() : '');
            return folder_name;
        });
        self.selectedFolderName = ko.pureComputed(function() {
            var userIsOwner = self.userIsOwner();
            var selected = self.selected();
            var name = selected.name || 'None';
            var folder_name = self.options.decodeFolder(userIsOwner ? name : '');
            return folder_name;
        });

    },
    afterUpdate: function() {
        var self = this;
        if (self.nodeHasAuth() && !self.validCredentials()) {
            var message;
            if (self.userIsOwner()) {
                message = self.messages.invalidCredOwner();
            }
            else {
                message = self.messages.invalidCredNotOwner();
            }
            self.changeMessage(message, 'text-danger');
        }
    },
    _updateCustomFields: function(settings){
        var self = this;
        self.validCredentials(settings.validCredentials);
    },
    /**
     * Allows a user to create an access token from the nodeSettings page
     */
    connectAccount: function() {
        this.options.connectAccount.call(this);
    }
});
コード例 #19
0
ファイル: citationsNodeConfig.js プロジェクト: XTech2K/osf.io
var CitationsFolderPickerViewModel = oop.extend(FolderPickerViewModel, {
    constructor: function(addonName, url, selector, folderPicker) {
        var self = this;
        self.super.constructor.call(self, addonName, url, selector, folderPicker);
        self.userAccountId = ko.observable('');
        // externalAccounts
        self.accounts = ko.observable([]);

        self.messages.submitSettingsSuccess = ko.pureComputed(function(){
            return 'Successfully linked "' + $osf.htmlEscape(self.folder().name) + '". Go to the <a href="' +
                self.urls().files + '">Overview page</a> to view your citations.';
        });

        self.treebeardOptions = $.extend(
            {}, 
            FolderPickerViewModel.prototype.treebeardOptions,
            {
                /** Callback for chooseFolder action.
                 *   Just changes the ViewModel's self.selected observable to the selected
                 *   folder.
                 */
                onPickFolder: function(evt, item){
                    evt.preventDefault();
                    this.selected({
                        name: item.data.name,
                        id: item.data.id
                    });
                    return false; // Prevent event propagation     
                }.bind(this),
                lazyLoadPreprocess: function(data) {    
                    return data.contents.filter(function(item) {
                    return item.kind === 'folder';
                    });
                },
                resolveLazyloadUrl: function(item) {
                    return this.urls().folders + item.data.id + '/?view=folders';
                }.bind(this)
            });
    },
    fetchAccounts: function() {
        var self = this;
        var ret = $.Deferred();
        var request = $.get(self.urls().accounts);
        request.then(function(data) {
            ret.resolve(data.accounts);
        });
        request.fail(function(xhr, textStatus, error) {
            self.changeMessage(self.messages.updateAccountsError(), 'text-danger');
            Raven.captureMessage('Could not GET ' + self.addonName + ' accounts for user', {
                url: self.url,
                textStatus: textStatus,
                error: error
            });
            ret.reject(xhr, textStatus, error);
        });
        return ret.promise();
    },
    updateAccounts: function() {
        var self = this;
        return self.fetchAccounts()
            .done(function(accounts) {
                self.accounts(
                    $.map(accounts, function(account) {
                        return {
                            name: account.display_name,
                            id: account.id
                        };
                    })
                );
            });
    },
    /**
     * Allows a user to create an access token from the nodeSettings page
     */
    connectAccount: function() {
        var self = this;

        window.oauthComplete = function(res) {
            // Update view model based on response
            self.changeMessage(self.messages.connectAccountSuccess(), 'text-success', 3000);
            self.userHasAuth(true);
            self.importAuth.call(self);
        };
        window.open(self.urls().auth);
    },
    connectExistingAccount: function(account_id) {
        var self = this;

        return $osf.putJSON(
            self.urls().importAuth, {
                external_account_id: account_id
            }
        ).then(self.onImportSuccess.bind(self), self.onImportError.bind(self));
    },
    _updateCustomFields: function(settings){
        this.userAccountId(settings.userAccountId);
    },
    _serializeSettings: function(){
        return {
            external_account_id: this.userAccountId(),
            external_list_id: this.selected().id,
            external_list_name: this.selected().name
        };
    },
    importAuth: function() {
        var self = this;
        self.updateAccounts()
            .then(function(){
                if (self.accounts().length > 1) {
                    bootbox.prompt({
                        title: 'Choose ' + self.addonName + ' Access Token to Import',
                        inputType: 'select',
                        inputOptions: ko.utils.arrayMap(
                            self.accounts(),
                            function(item) {
                                return {
                                    text: item.name,
                                    value: item.id
                                };
                            }
                        ),
                        value: self.accounts()[0].id,
                        callback: function(accountId) {
                            if (accountId) {
                                self.connectExistingAccount.call(self, (accountId));
                            }
                        },
                        buttons:{
                            confirm:{
                                label: 'Import'
                            }
                        }
                    });
                } else {
                    bootbox.confirm({
                        title: 'Import ' + self.addonName + ' access token',
                        message: self.messages.confirmAuth(),
                        callback: function(confirmed) {
                            if (confirmed) {
                                self.connectExistingAccount.call(self, (self.accounts()[0].id));
                            }
                        },
                        buttons:{
                            confirm:{
                                label:'Import'
                            }
                        }
                    });
                }
            });
    }
});
コード例 #20
0
var OauthAddonFolderPickerViewModel = oop.extend(FolderPickerViewModel, {
    constructor: function(addonName, url, selector, folderPicker, opts) {
        var self = this;
        self.super.constructor.call(self, addonName, url, selector, folderPicker);
        // externalAccounts
        self.accounts = ko.observableArray();
        self.selectedFolderType = ko.pureComputed(function() {
            var userHasAuth = self.userHasAuth();
            var selected = self.selected();
            return (userHasAuth && selected) ? selected.type : '';
        });
        self.messages.submitSettingsSuccess =  ko.pureComputed(function() {
            return 'Successfully linked "' + $osf.htmlEscape(self.options.decodeFolder(self.folder().name)) + '". Go to the <a href="' +
                self.urls().files + '">Files page</a> to view your content.';
        });
        var defaults = {
            onPickFolder: function(evt, item) {
                evt.preventDefault();
                var name = item.data.path !== '/' ? item.data.path : '/ (Full ' + self.addonName + ')';
                self.selected({
                    name: name,
                    path: item.data.path,
                    id: item.data.id
                });
                return false; // Prevent event propagation
            },
            /**
             * Allows a user to create an access token from the nodeSettings page
             */
            connectAccount: function() {
                var self = this;

                window.oauthComplete = function(res) {
                    // Update view model based on response
                    self.updateAccounts().then(function() {
                        try{
                            $osf.putJSON(
                                self.urls().importAuth, {
                                    external_account_id: self.accounts()[0].id
                                }
                            ).done(self.onImportSuccess.bind(self)
                            ).fail(self.onImportError.bind(self));

                            self.changeMessage(self.messages.connectAccountSuccess(), 'text-success', 3000);
                        }
                        catch(err){
                            self.changeMessage(self.messages.connectAccountDenied(), 'text-danger', 6000);
                        }
                    });
                };
                window.open(self.urls().auth);
            },
            decodeFolder: function(folder_name) {
                return folder_name;
            }
        };
        // Overrides
        self.options = $.extend({}, defaults, opts);
        // Treebeard config
        self.treebeardOptions = $.extend(
            {},
            FolderPickerViewModel.prototype.treebeardOptions,
            {
                onPickFolder: function(evt, item) {
                    return this.options.onPickFolder.call(this, evt, item);
                }.bind(this),
                resolveLazyloadUrl: function(item) {
                    return item.data.urls.folders;
                }
            }
        );
    },
    afterUpdate: function() {
        var self = this;
        if (self.nodeHasAuth() && !self.validCredentials()) {
            var message;
            if (self.userIsOwner()) {
                message = self.messages.invalidCredOwner();
            }
            else {
                message = self.messages.invalidCredNotOwner();
            }
            self.changeMessage(message, 'text-danger');
        }
    },
    _updateCustomFields: function(settings){
        this.validCredentials(settings.validCredentials);
    },
     /**
     * Allows a user to create an access token from the nodeSettings page
     */
    connectAccount: function() {
        this.options.connectAccount.call(this);
    },
    /**
    * Imports addon settings from user's account. If multiple addon accounts are connected, allow user to pick between them.
    */
    importAuth: function(){
        var self = this;

        self.updateAccounts().then(function () {
            if (self.accounts().length > 1) {
                bootbox.prompt({
                    title: 'Choose ' + $osf.htmlEscape(self.addonName) + ' Account to Import',
                    inputType: 'select',
                    inputOptions: ko.utils.arrayMap(
                        self.accounts(),
                        function(item) {
                            return {
                                text: $osf.htmlEscape(item.name),
                                value: item.id
                            };
                        }
                    ),
                    value: self.accounts()[0].id,
                    callback: (self.connectExistingAccount.bind(self)),
                    buttons: {
                        confirm:{
                            label:'Import',
                        }
                    }
                });
            } else {
                bootbox.confirm({
                    title: 'Import ' + self.addonName + ' Account?',
                    message: self.messages.confirmAuth(),
                    callback: function(confirmed) {
                        if (confirmed) {
                            self.connectExistingAccount.call(self, (self.accounts()[0].id));
                        }
                    },
                    buttons: {
                        confirm: {
                            label:'Import',
                        }
                    }
                });
            }
        });
    },
    /**
    * Associates selected external account with this node, or handles error.
    */
    connectExistingAccount: function(account_id) {
        var self = this;
        if (account_id !== null) {
            return $osf.putJSON(
                self.urls().importAuth, {
                    external_account_id: account_id
                }
            ).done(self.onImportSuccess.bind(self)
            ).fail(self.onImportError.bind(self));
        }
        return;
    },
    updateAccounts: function() {
        var self = this;
        var request = $.get(self.urls().accounts);
        return request.done(function(data) {
            self.accounts(data.accounts.map(function(account) {
                return {
                    name: account.display_name,
                    id: account.id
                };
            }));
        }).fail(function(xhr, textStatus, error) {
            self.changeMessage(self.messages.UPDATE_ACCOUNTS_ERROR(), 'text-warning');
            Raven.captureMessage('Could not GET ' + self.addonName + ' accounts for user', {
                extra: {
                    url: self.url,
                    textStatus: textStatus,
                    error: error
                }
            });
        });
    },
});
コード例 #21
0
ファイル: forgotPassword.js プロジェクト: 545zhou/osf.io
require('knockout.validation');

var $osf = require('js/osfHelpers');
var oop = require('js/oop');
var formViewModel = require('js/formViewModel');

var ForgotPasswordViewModel = oop.extend(formViewModel.FormViewModel, {
    constructor: function() {
        var self = this;
        self.super.constructor.call(self);
        self.username = ko.observable('').extend({
            required: true,
            email: true
        });
    },
    isValid: function() {
        var validationError = new formViewModel.ValidationError();
        if (!this.username.isValid()) {
            validationError.messages.push('Please enter a valid email address.');
            throw validationError;
        } else {
            return true;
        }
    }
});


var ForgotPassword = function(selector) {
    this.viewModel = new ForgotPasswordViewModel();
    $osf.applyBindings(this.viewModel, selector);
};