CartoDB/cartodb20

View on GitHub
lib/assets/javascripts/cartodb/common/dialogs/map/image_picker/upload_model.js

Summary

Maintainability
D
1 day
Test Coverage
var Backbone = require('backbone-cdb-v3');
var Utils = require('cdb.Utils');
var moment = require('moment-v3');
var _ = require('underscore-cdb-v3');

/**
 * Model that let user upload files to our endpoints.
 *
 * NOTE: this model extends Backbone.Model instead of cdb.core.Model, because it's required for the
 * vendor/backbone-model-file-upload.
 */
module.exports = Backbone.Model.extend({

  url: function(method) {
    var version = cdb.config.urlVersion('asset', method);
    return '/api/' + version + '/users/' + this.userId + '/assets'
  },

  fileAttribute: 'filename',

  defaults: {
    type: '',
    value: '',
    interval: 0,
    progress: 0,
    state: 'idle',
    option: ''
  },

  initialize: function(val, opts) {
    this.user = opts && opts.user;

    if (!opts.userId) {
      throw new Error('userId is required');
    }

    this.userId = opts.userId;

    this._initBinds();
    this._validate(this.attributes, { validate: true });
  },

  isValidToUpload: function() {
    return this.get('value') && this.get('state') !== 'error';
  },

  setFresh: function(attributes) {
    this.clear();
    this.set(attributes);
  },

  _initBinds: function() {
    this.bind('progress', function(progress) {
      this.set({
        progress: progress*100,
        state: 'uploading'
      });
    }, this);

    this.bind('change:value', function() {
      if (this.get('state') === "error") {
        this.set({ state: 'idle' })
        this.unset('get_error_text', { silent: true });
      }
    }, this);

    this.bind('error invalid', function(m, d) {
      this.set({
        state: 'error',
        error_code: (d && d.error_code) || '',
        get_error_text: {
          title: 'Invalid import',
          what_about: (d && d.msg) || ''
        }
      }, { silent: true });
      // We need this, if not validate will run again and again and again... :(
      this.trigger('change');
    }, this);
  },

  validate: function(attrs) {
    if (!attrs) return;

    if (attrs.type === "file") {
      // Number of files
      if (attrs.value && attrs.value.length) {
        return {
          msg: "Unfortunately only one file is allowed per upload"
        }
      }
      // File extension
      var name = attrs.value.name;
      var ext = name.substr(name.lastIndexOf('.') + 1);
      if (ext) {
        ext = ext.toLowerCase();
      }
      if (!_.contains(["jpg", "png", "gif", "svg"], ext)) {
        return {
          msg: "Unfortunately this file extension is not allowed"
        }
      }
    }

    if (attrs.type === "url") {
      // Valid URL?
      if (!Utils.isURL(attrs.value)) {
        return {
          msg: "Unfortunately the URL provided is not valid"
        }
      }
    }

  },

  isValid: function() {
    return this.get('value') && this.get('state') !== "error"
  },

  upload: function() {
    var self = this;

    var options = {
      kind: this.get('kind')
    };

    if (this.get('type') === "file") {
      options.filename = this.get('value');
    } else if (this.get('type') === "url") {
      options.url = this.get('value');
    }

    this.xhr = this.save(options, {
      success: function(m) {
        m.set('state', 'uploaded');
      },
      error: function(m, msg) {

        var message = 'Unfortunately there was a connection error';

        if (msg && msg.status === 429) {
          var response = JSON.parse(msg.responseText);
          message = response.error;
        } else if (msg && msg.status === 400) {
          var response = JSON.parse(msg.responseText);
          message = response.error;
        }

        self.set({
          state: 'error',
          get_error_text: { title: 'There was an error', what_about: message }
        });

      },
      complete: function() {
        delete self.xhr;
      }
    });
  },

  stopUpload: function() {
    if (this.xhr) this.xhr.abort();
  }
});