Skip to content

Commit

Permalink
Improved error handling and added save button
Browse files Browse the repository at this point in the history
  • Loading branch information
Joseph Atkins-Turkish committed Aug 31, 2016
1 parent 18582bc commit c959dc5
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ def forwards(self, orm):
# Adding unique constraint on 'PublishedMedia', fields ['project', 'name']
db.create_unique(u'ide_publishedmedia', ['project_id', 'name'])

# Adding unique constraint on 'PublishedMedia', fields ['project', 'media_id']
db.create_unique(u'ide_publishedmedia', ['project_id', 'media_id'])


def backwards(self, orm):
# Removing unique constraint on 'PublishedMedia', fields ['project', 'media_id']
db.delete_unique(u'ide_publishedmedia', ['project_id', 'media_id'])

# Removing unique constraint on 'PublishedMedia', fields ['project', 'name']
db.delete_unique(u'ide_publishedmedia', ['project_id', 'name'])

Expand Down Expand Up @@ -78,7 +84,7 @@ def backwards(self, orm):
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'builds'", 'to': "orm['ide.Project']"}),
'started': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
'uuid': ('django.db.models.fields.CharField', [], {'default': "'8b836ddd-8bc8-4884-bdda-824481f7f05c'", 'max_length': '36'})
'uuid': ('django.db.models.fields.CharField', [], {'default': "'ff465b46-f0da-429e-85ac-6e3fa3ed20e5'", 'max_length': '36'})
},
'ide.buildsize': {
'Meta': {'object_name': 'BuildSize'},
Expand Down Expand Up @@ -111,7 +117,7 @@ def backwards(self, orm):
'app_modern_multi_js': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'app_platforms': ('django.db.models.fields.TextField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'app_short_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'app_uuid': ('django.db.models.fields.CharField', [], {'default': "'488507d8-f7d8-449e-b2d9-36c05ba88d4c'", 'max_length': '36', 'null': 'True', 'blank': 'True'}),
'app_uuid': ('django.db.models.fields.CharField', [], {'default': "'0c6dc2fb-3305-4418-9bac-3661c4773ca9'", 'max_length': '36', 'null': 'True', 'blank': 'True'}),
'app_version_label': ('django.db.models.fields.CharField', [], {'default': "'1.0'", 'max_length': '40', 'null': 'True', 'blank': 'True'}),
'github_branch': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'github_hook_build': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
Expand All @@ -129,7 +135,7 @@ def backwards(self, orm):
'sdk_version': ('django.db.models.fields.CharField', [], {'default': "'2'", 'max_length': '6'})
},
'ide.publishedmedia': {
'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'PublishedMedia'},
'Meta': {'unique_together': "(('project', 'name'), ('project', 'media_id'))", 'object_name': 'PublishedMedia'},
'glance': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'has_timeline': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
Expand Down Expand Up @@ -158,7 +164,7 @@ def backwards(self, orm):
'resource_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'space_optimisation': ('django.db.models.fields.CharField', [], {'max_length': '7', 'null': 'True', 'blank': 'True'}),
'storage_format': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}),
'target_platforms': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '30', 'null': 'True', 'blank': 'True'}),
'target_platforms': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'null': 'True', 'blank': 'True'}),
'tracking': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
},
'ide.resourcevariant': {
Expand Down Expand Up @@ -199,7 +205,7 @@ def backwards(self, orm):
'theme': ('django.db.models.fields.CharField', [], {'default': "'cloudpebble'", 'max_length': '50'}),
'use_spaces': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
'whats_new': ('django.db.models.fields.PositiveIntegerField', [], {'default': '23'})
'whats_new': ('django.db.models.fields.PositiveIntegerField', [], {'default': '24'})
}
}

Expand Down
4 changes: 3 additions & 1 deletion ide/models/published_media.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ def clean(self):
raise ValidationError(_("If glance and timeline.tiny are both used, they must be identical."))
if self.has_timeline and not (self.timeline_tiny and self.timeline_small and self.timeline_large):
raise ValidationError(_("If timeline icons are enabled, they must all be set."))
if not self.glance and not self.has_timeline:
raise ValidationError(_("Glance and Timeline cannot both be unset."))
if self.media_id < 0:
raise ValidationError(_("Published Media IDs cannot be negative."))

class Meta(IdeModel.Meta):
unique_together = (('project', 'name'),)
unique_together = (('project', 'name'), ('project', 'media_id'))
1 change: 1 addition & 0 deletions ide/static/ide/css/ide.css
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ table.build-results tr.pending .build-state {

.media-tool-buttons button {
width: initial;
min-width: 130px;
margin-right: 10px;
}

Expand Down
4 changes: 2 additions & 2 deletions ide/static/ide/js/live_settings_form.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ function make_live_settings_form(options) {
init: function() {
init();
},
save: function(element) {
return save(element);
save: function(element, event) {
return save(element, event);
}
};
}
57 changes: 39 additions & 18 deletions ide/static/ide/js/published_media.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ CloudPebble.PublishedMedia = (function() {
var pane;
this.show_error = function show_error(error) {
pane.find('.alert-error').removeClass('hide').text(error);
$('#main-pane').animate({scrollTop: 0})
};
this.hide_error = function hide_error() {
pane.find('.alert-error').addClass('hide');
Expand Down Expand Up @@ -59,7 +60,7 @@ CloudPebble.PublishedMedia = (function() {
function MediaItem() {
var self = this;
var deleting = false;
var item_form = media_template.find('form');
var item_form = media_template.find('#media-items');
var item = item_template.clone().appendTo(item_form).data('item', this);
var name_input = item.find('.edit-media-name');
var id_input = item.find('.edit-media-id');
Expand Down Expand Up @@ -201,7 +202,9 @@ CloudPebble.PublishedMedia = (function() {
});
// Set up the delete button
delete_btn.click(function() {
self.delete();
CloudPebble.Prompts.Confirm(gettext("Do you want to delete this Published Media entry?"), gettext("This cannot be undone."), function() {
self.delete();
});
});

this.setupOptions();
Expand All @@ -226,10 +229,6 @@ CloudPebble.PublishedMedia = (function() {
if (!_.every(names)) {
throw new Error(gettext('Identifiers cannot be blank'));
}
// Check that all IDs are unique
if (_.max(_.countBy(data, 'id')) > 1) {
throw new Error(gettext('Numeric IDs must be unique'));
}
// Check that all identifiers are valid
_.each(names, function(name) {
if (!REGEXES.c_identifier.test(name)) {
Expand All @@ -238,33 +237,50 @@ CloudPebble.PublishedMedia = (function() {
});
return Ajax.Post('/ide/project/' + PROJECT_ID + '/save_published_media', {
'published_media': JSON.stringify(data)
}).then(function(result) {
// TODO: use 'result' or not?
}).then(function() {
CloudPebble.ProjectInfo.published_media = data;
sync_with_ycm();
return null;
});
}

/** Save the whole form. If any names are incomplete or resources are invalid, it simply refuses to save without error. */
function save_forms() {
var items = get_media_items();
function save_forms(event) {
var data = get_form_data();
var do_cancel = !event || event.type != 'submit';
var items = get_media_items();
var identifiers = get_eligible_identifiers();
function maybe_error(text) {
if (do_cancel) return {incomplete: true};
throw new Error(text);
}
// If not all items have names, cancel saving without displaying an error
if (!_.every(_.pluck(data, 'name'))) {
return {incomplete: true};
return maybe_error(gettext("Published Media must have non-empty identifiers."))
}

// Cancel if there are any incomplete items
if (!_.every(_.map(data, function(item) {
return !!item.timeline || !!item.glance;
}))) {
return maybe_error(gettext("Published Media items must specify glance, timeline icons, or both."))
}

// Check that all IDs are unique
if (_.max(_.countBy(data, 'id')) > 1) {
return maybe_error(gettext("Published Media IDs must be unique."))
}
// Raise an error if there are any invalid selections

// Cancel (and show the 'invalid items' icon in the sidebar) if there are any invalid values
var validity = _.map(items, function(item) {return item.is_valid(identifiers);});
if (!_.every(validity)) {
toggle_sidebar_error(true);
return {incomplete: true};
return maybe_error(gettext("You cannot save Published Media items with references to resuorces which do not exist."))
}

// If we successfully saved, it implies that there were no invalid references
// so we can get rid of the sidebar error notification.
return save_pubished_media(data).then(function() {
// If we successfully saved, it implies that there were no invalid references
// so we can get rid of the sidebar error notification.
toggle_sidebar_error(false);
});
}
Expand All @@ -274,12 +290,17 @@ CloudPebble.PublishedMedia = (function() {
if (media_pane_setup) return false;
media_pane_setup = true;

var initial_data = CloudPebble.ProjectInfo.published_media;
_.each(initial_data, function(data) {
// Set up the data
_.each(CloudPebble.ProjectInfo.published_media, function(data) {
var item = new MediaItem();
item.setData(data);
});

media_template.find('form').submit(function(e) {
live_form.save(null, e);
return false;
});

media_template.find('#add-published-media').click(function() {
new MediaItem();
});
Expand All @@ -290,7 +311,7 @@ CloudPebble.PublishedMedia = (function() {
error_function: alerts.show_error,
on_progress_started: alerts.show_progress,
on_progress_complete: alerts.hide_progress,
form: media_template.find('form')
form: media_template.find('#media-items')
});
media_template.find('.media-tool-buttons').removeClass('hide');
media_template.find('.media-pane-loading').remove();
Expand Down
9 changes: 7 additions & 2 deletions ide/templates/ide/project/published_media.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@
<!-- Compilation pane -->
<div id="media-pane-template" class="hide media-pane resource-pane">
<div class="well alert alert-error hide"></div>
<form class="form-horizontal">
</form>
<form>
<div id="media-items" class="form-horizontal">

</div>
<div class="well media-pane-loading">
<h2>{% trans 'Loading...' %}</h2>
</div>

<div class="well hide media-tool-buttons">
<button type="submit" class="btn btn-affirmative" id="save-published-media">
{% trans 'Save' %}
</button>
<button type="button" class="btn" id="add-published-media">
{% trans 'Add New Published Media' %}
</button>
</div>
</form>
</div>

<div class="well hide media-item" id="media-item-template" >
Expand Down

0 comments on commit c959dc5

Please sign in to comment.