diff --git a/Dockerfile b/Dockerfile index ef54ecc7..3b04fda6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,8 +55,8 @@ RUN mkdir /sdk2 && \ curl -L "https://s3.amazonaws.com/assets.getpebble.com/sdk3/sdk-core/sdk-core-${SDK_TWO_VERSION}.tar.bz2" | \ tar --strip-components=1 -xj -C /sdk2 -ENV SDK_THREE_CHANNEL=beta -ENV SDK_THREE_VERSION=4.0-beta16 +ENV SDK_THREE_CHANNEL=release +ENV SDK_THREE_VERSION=4.0 # Install SDK 3 RUN mkdir /sdk3 && \ diff --git a/bin/post_compile b/bin/post_compile index ba99e9ca..5b44c74c 100644 --- a/bin/post_compile +++ b/bin/post_compile @@ -9,7 +9,7 @@ echo "Downloading SDK 2" curl -o /tmp/pebblesdk2.tar.gz -L https://s3.amazonaws.com/assets.getpebble.com/sdk3/sdk-core/sdk-core-2.9.tar.bz2 echo "Downloading SDK 3" -curl -o /tmp/pebblesdk3.tar.gz -L https://s3.amazonaws.com/assets.getpebble.com/sdk3/beta/sdk-core-4.0-rc20.tar.bz2 +curl -o /tmp/pebblesdk3.tar.gz -L https://s3.amazonaws.com/assets.getpebble.com/sdk3/release/sdk-core-4.0.tar.bz2 echo "Downloading the toolchain" curl -o /tmp/arm-cs-tools.tar https://cloudpebble-vagrant.s3.amazonaws.com/arm-cs-tools-stripped.tar diff --git a/ide/api/project.py b/ide/api/project.py index e0a24c39..3ed62b2f 100644 --- a/ide/api/project.py +++ b/ide/api/project.py @@ -50,6 +50,7 @@ def project_info(request, project_id): 'app_platforms': project.app_platforms, 'app_modern_multi_js': project.app_modern_multi_js, 'menu_icon': project.menu_icon.id if project.menu_icon else None, + 'published_media': project.get_published_media(), 'source_files': [{ 'name': f.file_name, 'id': f.id, @@ -257,11 +258,26 @@ def save_project_dependencies(request, project_id): try: project.set_dependencies(json.loads(request.POST['dependencies'])) project.set_interdependencies([int(x) for x in json.loads(request.POST['interdependencies'])]) + except (IntegrityError, ValueError) as e: + raise BadRequest(str(e)) + else: + send_td_event('cloudpebble_save_dependencies', request=request, project=project) return {'dependencies': project.get_dependencies()} + + +@require_POST +@login_required +@json_view +def save_published_media(request, project_id): + project = get_object_or_404(Project, pk=project_id, owner=request.user) + try: + project.set_published_media(json.loads(request.POST['published_media'])) except (IntegrityError, ValueError) as e: raise BadRequest(str(e)) else: - send_td_event('cloudpebble_save_project_settings', request=request, project=project) + send_td_event('cloudpebble_save_published_media', request=request, project=project) + return {'published_media': project.get_published_media()} + @require_POST @login_required diff --git a/ide/api/ycm.py b/ide/api/ycm.py index f0a17f86..891f6138 100644 --- a/ide/api/ycm.py +++ b/ide/api/ycm.py @@ -7,8 +7,10 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import get_object_or_404 from django.views.decorators.http import require_POST +from django.utils.translation import ugettext as _ from urlparse import urlparse + from ide.models.project import Project from utils.jsonview import json_view @@ -38,7 +40,8 @@ def init_autocomplete(request, project_id): 'sdk': request.POST.get('sdk', '2'), 'messagekeys': appkey_names, 'resources': identifiers, - 'dependencies': project.get_dependencies() + 'dependencies': project.get_dependencies(), + 'published_media': [x.name for x in project.published_media.all()] } # Let's go! return _spin_up_server(request) @@ -66,6 +69,7 @@ def _spin_up_server(request): 'server': ws_server, 'secure': secure, 'libraries': response.get('libraries', {}), + 'resources': response.get('resources', []), 'npm_error': response.get('npm_error', None) } diff --git a/ide/migrations/0051_auto__chg_field_resourceidentifier_target_platforms.py b/ide/migrations/0051_auto__chg_field_resourceidentifier_target_platforms.py new file mode 100644 index 00000000..6d6b6e1d --- /dev/null +++ b/ide/migrations/0051_auto__chg_field_resourceidentifier_target_platforms.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'ResourceIdentifier.target_platforms' + db.alter_column(u'ide_resourceidentifier', 'target_platforms', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)) + + def backwards(self, orm): + + # Changing field 'ResourceIdentifier.target_platforms' + db.alter_column(u'ide_resourceidentifier', 'target_platforms', self.gf('django.db.models.fields.CharField')(max_length=30, null=True)) + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'ide.buildresult': { + 'Meta': {'object_name': 'BuildResult'}, + 'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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': "'db7a8764-aaf8-453b-ba1a-a7a0da137336'", 'max_length': '36'}) + }, + 'ide.buildsize': { + 'Meta': {'object_name': 'BuildSize'}, + 'binary_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sizes'", 'to': "orm['ide.BuildResult']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'resource_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'total_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'worker_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ide.dependency': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Dependency'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'dependencies'", 'to': "orm['ide.Project']"}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '2000'}) + }, + 'ide.project': { + 'Meta': {'object_name': 'Project'}, + 'app_capabilities': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'app_company_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'app_is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'app_is_shown_on_communication': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'app_is_watchface': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'app_jshint': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'app_keys': ('django.db.models.fields.TextField', [], {'default': "'{}'"}), + 'app_keywords': ('django.db.models.fields.TextField', [], {'default': "'[]'"}), + 'app_long_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + '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': "'72886d2e-9a66-47fc-a852-4044128bacbe'", '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'}), + 'github_hook_uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'null': 'True', 'blank': 'True'}), + 'github_last_commit': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'github_last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'github_repo': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'optimisation': ('django.db.models.fields.CharField', [], {'default': "'s'", 'max_length': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'project_dependencies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ide.Project']", 'symmetrical': 'False'}), + 'project_type': ('django.db.models.fields.CharField', [], {'default': "'native'", 'max_length': '10'}), + 'sdk_version': ('django.db.models.fields.CharField', [], {'default': "'2'", 'max_length': '6'}) + }, + 'ide.resourcefile': { + 'Meta': {'unique_together': "(('project', 'file_name'),)", 'object_name': 'ResourceFile'}, + 'file_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_menu_icon': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'kind': ('django.db.models.fields.CharField', [], {'max_length': '9'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'resources'", 'to': "orm['ide.Project']"}) + }, + 'ide.resourceidentifier': { + 'Meta': {'object_name': 'ResourceIdentifier'}, + 'character_regex': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'compatibility': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'memory_format': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'resource_file': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'identifiers'", 'to': "orm['ide.ResourceFile']"}), + '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': '100', 'null': 'True', 'blank': 'True'}), + 'tracking': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ide.resourcevariant': { + 'Meta': {'unique_together': "(('resource_file', 'tags'),)", 'object_name': 'ResourceVariant'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_legacy': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'resource_file': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variants'", 'to': "orm['ide.ResourceFile']"}), + 'tags': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'ide.sourcefile': { + 'Meta': {'unique_together': "(('project', 'file_name', 'target'),)", 'object_name': 'SourceFile'}, + 'file_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'folded_lines': ('django.db.models.fields.TextField', [], {'default': "'[]'"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'source_files'", 'to': "orm['ide.Project']"}), + 'target': ('django.db.models.fields.CharField', [], {'default': "'app'", 'max_length': '10'}) + }, + 'ide.templateproject': { + 'Meta': {'object_name': 'TemplateProject', '_ormbases': ['ide.Project']}, + u'project_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ide.Project']", 'unique': 'True', 'primary_key': 'True'}), + 'template_kind': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'ide.usergithub': { + 'Meta': {'object_name': 'UserGithub'}, + 'avatar': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'nonce': ('django.db.models.fields.CharField', [], {'max_length': '36', 'null': 'True', 'blank': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'github'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['auth.User']"}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + 'ide.usersettings': { + 'Meta': {'object_name': 'UserSettings'}, + 'accepted_terms': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'autocomplete': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'keybinds': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '20'}), + 'tab_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '2'}), + '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': '24'}) + } + } + + complete_apps = ['ide'] \ No newline at end of file diff --git a/ide/migrations/0052_auto__add_publishedmedia__add_unique_publishedmedia_project_name__add_.py b/ide/migrations/0052_auto__add_publishedmedia__add_unique_publishedmedia_project_name__add_.py new file mode 100644 index 00000000..1ae34753 --- /dev/null +++ b/ide/migrations/0052_auto__add_publishedmedia__add_unique_publishedmedia_project_name__add_.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'PublishedMedia' + db.create_table(u'ide_publishedmedia', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('project', self.gf('django.db.models.fields.related.ForeignKey')(related_name='published_media', to=orm['ide.Project'])), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('media_id', self.gf('django.db.models.fields.IntegerField')()), + ('glance', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('has_timeline', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('timeline_tiny', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('timeline_small', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('timeline_large', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + )) + db.send_create_signal('ide', ['PublishedMedia']) + + # 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']) + + # Deleting model 'PublishedMedia' + db.delete_table(u'ide_publishedmedia') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'ide.buildresult': { + 'Meta': {'object_name': 'BuildResult'}, + 'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + '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': "'ff465b46-f0da-429e-85ac-6e3fa3ed20e5'", 'max_length': '36'}) + }, + 'ide.buildsize': { + 'Meta': {'object_name': 'BuildSize'}, + 'binary_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sizes'", 'to': "orm['ide.BuildResult']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'platform': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'resource_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'total_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'worker_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ide.dependency': { + 'Meta': {'unique_together': "(('project', 'name'),)", 'object_name': 'Dependency'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'dependencies'", 'to': "orm['ide.Project']"}), + 'version': ('django.db.models.fields.CharField', [], {'max_length': '2000'}) + }, + 'ide.project': { + 'Meta': {'object_name': 'Project'}, + 'app_capabilities': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'app_company_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'app_is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'app_is_shown_on_communication': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'app_is_watchface': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'app_jshint': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'app_keys': ('django.db.models.fields.TextField', [], {'default': "'{}'"}), + 'app_keywords': ('django.db.models.fields.TextField', [], {'default': "'[]'"}), + 'app_long_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + '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': "'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'}), + 'github_hook_uuid': ('django.db.models.fields.CharField', [], {'max_length': '36', 'null': 'True', 'blank': 'True'}), + 'github_last_commit': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'github_last_sync': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'github_repo': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'optimisation': ('django.db.models.fields.CharField', [], {'default': "'s'", 'max_length': '1'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}), + 'project_dependencies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ide.Project']", 'symmetrical': 'False'}), + 'project_type': ('django.db.models.fields.CharField', [], {'default': "'native'", 'max_length': '10'}), + 'sdk_version': ('django.db.models.fields.CharField', [], {'default': "'2'", 'max_length': '6'}) + }, + 'ide.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'}), + 'media_id': ('django.db.models.fields.IntegerField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'published_media'", 'to': "orm['ide.Project']"}), + 'timeline_large': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'timeline_small': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'timeline_tiny': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'ide.resourcefile': { + 'Meta': {'unique_together': "(('project', 'file_name'),)", 'object_name': 'ResourceFile'}, + 'file_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_menu_icon': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'kind': ('django.db.models.fields.CharField', [], {'max_length': '9'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'resources'", 'to': "orm['ide.Project']"}) + }, + 'ide.resourceidentifier': { + 'Meta': {'object_name': 'ResourceIdentifier'}, + 'character_regex': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'compatibility': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'memory_format': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'resource_file': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'identifiers'", 'to': "orm['ide.ResourceFile']"}), + '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': '100', 'null': 'True', 'blank': 'True'}), + 'tracking': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'ide.resourcevariant': { + 'Meta': {'unique_together': "(('resource_file', 'tags'),)", 'object_name': 'ResourceVariant'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_legacy': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'resource_file': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variants'", 'to': "orm['ide.ResourceFile']"}), + 'tags': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'ide.sourcefile': { + 'Meta': {'unique_together': "(('project', 'file_name', 'target'),)", 'object_name': 'SourceFile'}, + 'file_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'folded_lines': ('django.db.models.fields.TextField', [], {'default': "'[]'"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'source_files'", 'to': "orm['ide.Project']"}), + 'target': ('django.db.models.fields.CharField', [], {'default': "'app'", 'max_length': '10'}) + }, + 'ide.templateproject': { + 'Meta': {'object_name': 'TemplateProject', '_ormbases': ['ide.Project']}, + u'project_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ide.Project']", 'unique': 'True', 'primary_key': 'True'}), + 'template_kind': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'ide.usergithub': { + 'Meta': {'object_name': 'UserGithub'}, + 'avatar': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'nonce': ('django.db.models.fields.CharField', [], {'max_length': '36', 'null': 'True', 'blank': 'True'}), + 'token': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'github'", 'unique': 'True', 'primary_key': 'True', 'to': u"orm['auth.User']"}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + 'ide.usersettings': { + 'Meta': {'object_name': 'UserSettings'}, + 'accepted_terms': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'autocomplete': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'keybinds': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '20'}), + 'tab_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '2'}), + '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': '24'}) + } + } + + complete_apps = ['ide'] \ No newline at end of file diff --git a/ide/models/__init__.py b/ide/models/__init__.py index af9e197d..f9a32907 100644 --- a/ide/models/__init__.py +++ b/ide/models/__init__.py @@ -5,3 +5,4 @@ from ide.models.project import * from ide.models.user import * from ide.models.dependency import * +from ide.models.published_media import * diff --git a/ide/models/files.py b/ide/models/files.py index b24482b5..dabf0216 100644 --- a/ide/models/files.py +++ b/ide/models/files.py @@ -175,7 +175,7 @@ class ResourceIdentifier(IdeModel): character_regex = models.CharField(max_length=100, blank=True, null=True) tracking = models.IntegerField(blank=True, null=True) compatibility = models.CharField(max_length=10, blank=True, null=True) - target_platforms = models.CharField(max_length=30, null=True, blank=True, default=None) + target_platforms = models.CharField(max_length=100, null=True, blank=True, default=None) MEMORY_FORMATS = ( ('Smallest', _('Smallest')), diff --git a/ide/models/project.py b/ide/models/project.py index 63b58f1b..e72ec4d7 100644 --- a/ide/models/project.py +++ b/ide/models/project.py @@ -10,6 +10,7 @@ from ide.models.files import ResourceFile, ResourceIdentifier, SourceFile, ResourceVariant from ide.models.dependency import Dependency from ide.models.meta import IdeModel +from ide.models.published_media import PublishedMedia from ide.utils import generate_half_uuid from ide.utils.regexes import regexes from ide.utils.version import version_to_semver, semver_to_version, parse_sdk_version @@ -90,12 +91,25 @@ def __init__(self, *args, **kwargs): if self.sdk_version == '2': self.app_modern_multi_js = False + def set_published_media(self, published_media): + """ Set the project's publishedMedia entry + :param published_media: A list of publishedMedia dictionaries. + """ + with transaction.atomic(): + self.published_media.all().delete() + for item in published_media: + media = PublishedMedia.from_dict(self, item) + media.save() + + def get_published_media(self): + return [item.to_dict() for item in self.published_media.all()] + def set_dependencies(self, dependencies): """ Set the project's dependencies from a dictionary. :param dependencies: A dictionary of dependency->version """ with transaction.atomic(): - Dependency.objects.filter(project=self).delete() + self.dependencies.all().delete() for name, version in dependencies.iteritems(): dep = Dependency.objects.create(project=self, name=name, version=version) dep.save() diff --git a/ide/models/published_media.py b/ide/models/published_media.py new file mode 100644 index 00000000..2ab62c0a --- /dev/null +++ b/ide/models/published_media.py @@ -0,0 +1,59 @@ +from django.db import models +from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ + +from ide.utils.regexes import regexes +from ide.models.meta import IdeModel + + +class PublishedMedia(IdeModel): + project = models.ForeignKey('Project', related_name='published_media') + name = models.CharField(max_length=100, validators=regexes.validator('c_identifier', _('Invalid identifier'))) + media_id = models.IntegerField() + glance = models.CharField(max_length=100, blank=True, validators=regexes.validator('c_identifier', _('Invalid identifier'))) + has_timeline = models.BooleanField(default=False) + timeline_tiny = models.CharField(max_length=100, blank=True, validators=regexes.validator('c_identifier', _('Invalid identifier'))) + timeline_small = models.CharField(max_length=100, blank=True, validators=regexes.validator('c_identifier', _('Invalid identifier'))) + timeline_large = models.CharField(max_length=100, blank=True, validators=regexes.validator('c_identifier', _('Invalid identifier'))) + + @classmethod + def from_dict(cls, project, data): + return cls.objects.create( + project=project, + name=data['name'], + media_id=data.get('id', None), + glance=data.get('glance', ''), + has_timeline=('timeline' in data), + timeline_tiny=data.get('timeline', {}).get('tiny', ''), + timeline_small=data.get('timeline', {}).get('small', ''), + timeline_large=data.get('timeline', {}).get('large', '') + ) + + + def to_dict(self): + obj = { + 'name': self.name, + 'id': self.media_id + } + if self.glance: + obj['glance'] = self.glance + if self.has_timeline: + obj['timeline'] = { + 'tiny': self.timeline_tiny, + 'small': self.timeline_small, + 'large': self.timeline_large + } + return obj + + def clean(self): + if self.has_timeline and self.glance and self.glance != self.timeline_tiny: + 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'), ('project', 'media_id')) diff --git a/ide/static/ide/css/ide.css b/ide/static/ide/css/ide.css index d534f89f..102b199f 100644 --- a/ide/static/ide/css/ide.css +++ b/ide/static/ide/css/ide.css @@ -527,16 +527,22 @@ table.kv-table .kv-remove { margin-left: 110px; } -.compilation-pane #run-on-phone .btn, -.compilation-pane #run-on-qemu .btn, -.compilation-pane .build-buttons .btn { +.compilation-pane .compilation-buttons.two-buttons .btn { + width: 287px; + margin: 0 4px; +} + +.compilation-pane .compilation-buttons.three-buttons .btn{ + width: 188px; + margin-right: 9px; +} + +.compilation-pane .compilation-buttons.four-buttons .btn{ width: 137px; margin: 0 4px; } -.compilation-pane #run-on-phone .btn:last-of-type, -.compilation-pane #run-on-qemu .btn:last-of-type, -.compilation-pane .build-buttons .btn:last-child { +.compilation-pane .compilation-buttons .btn:last-child { margin-right: 0; } @@ -641,7 +647,7 @@ table.build-results tr.pending .build-state { clear: both; } -.resource-pane input:not([type=checkbox]):not([type=submit]) { +.resource-pane input:not([type=checkbox]):not([type=submit]):not([type=number]) { width: 435px; } @@ -676,6 +682,36 @@ table.build-results tr.pending .build-state { width: 188px; } +/* Published media pane */ + +.media-item:not(:first-child) .help-block { + display: none; +} + +.media-tool-buttons button { + width: initial; + min-width: 130px; + margin-right: 10px; +} + +.sidebar-error::after { + content: "!"; +} + +.sidebar-error { + background: #e5171f; + display: inline-block; + color: white; + width: 22px; + text-align: center; + font-family: serif; + border-radius: 20px; + padding-right: 1px; + margin-top: -1px; +} +.sidebar-error.hide { + display: none; +} /* Dependancies pane */ @@ -1188,7 +1224,7 @@ button#add-filter { display: table; } -.edit-resource-target-platforms-enabled:not(:checked) ~ .edit-resource-targets { +.form-section-toggle:not(:checked) ~ .form-toggleable-section { display: none; } diff --git a/ide/static/ide/js/KVtable.js b/ide/static/ide/js/KVtable.js index 3ec12d2d..7897e64d 100644 --- a/ide/static/ide/js/KVtable.js +++ b/ide/static/ide/js/KVtable.js @@ -52,7 +52,12 @@ CloudPebble.KVTable = function(table_elm, options) { .addClass('kv-value') .attr('type', opts.value_type) .attr('placeholder', key ? null : opts.value_placeholder)), - $('