class TranslatedModel(UncachedModelBase): name = TranslatedField() description = TranslatedField() default_locale = models.CharField(max_length=10) no_locale = TranslatedField(require_locale=False) objects = UncachedManagerBase()
class FileUpload(ModelBase): """Created when a file is uploaded for validation/submission.""" uuid = models.UUIDField(default=uuid.uuid4, editable=False) path = models.CharField(max_length=255, default='') name = models.CharField(max_length=255, default='', help_text="The user's original filename") hash = models.CharField(max_length=255, default='') user = models.ForeignKey('users.UserProfile', null=True) valid = models.BooleanField(default=False) validation = models.TextField(null=True) automated_signing = models.BooleanField(default=False) compat_with_app = models.PositiveIntegerField( choices=amo.APPS_CHOICES, db_column="compat_with_app_id", null=True) compat_with_appver = models.ForeignKey( AppVersion, null=True, related_name='uploads_compat_for_appver') # Not all FileUploads will have a version and addon but it will be set # if the file was uploaded using the new API. version = models.CharField(max_length=255, null=True) addon = models.ForeignKey('addons.Addon', null=True) objects = UncachedManagerBase() class Meta(ModelBase.Meta): db_table = 'file_uploads' def __unicode__(self): return unicode(self.uuid.hex) def save(self, *args, **kw): if self.validation: if self.load_validation()['errors'] == 0: self.valid = True super(FileUpload, self).save(*args, **kw) def add_file(self, chunks, filename, size): if not self.uuid: self.uuid = self._meta.get_field('uuid')._create_uuid() filename = force_bytes(u'{0}_{1}'.format(self.uuid.hex, filename)) loc = os.path.join(user_media_path('addons'), 'temp', uuid.uuid4().hex) base, ext = os.path.splitext(force_bytes(filename)) is_crx = False # Change a ZIP to an XPI, to maintain backward compatibility # with older versions of Firefox and to keep the rest of the XPI code # path as consistent as possible for ZIP uploads. # See: https://github.com/mozilla/addons-server/pull/2785 if ext == '.zip': ext = '.xpi' # If the extension is a CRX, we need to do some actual work to it # before we just convert it to an XPI. We strip the header from the # CRX, then it's good; see more about the CRX file format here: # https://developer.chrome.com/extensions/crx if ext == '.crx': ext = '.xpi' is_crx = True if ext in EXTENSIONS: loc += ext log.info('UPLOAD: %r (%s bytes) to %r' % (filename, size, loc)) if is_crx: hash = write_crx_as_xpi(chunks, storage, loc) else: hash = hashlib.sha256() with storage.open(loc, 'wb') as file_destination: for chunk in chunks: hash.update(chunk) file_destination.write(chunk) self.path = loc self.name = filename self.hash = 'sha256:%s' % hash.hexdigest() self.save() @classmethod def from_post(cls, chunks, filename, size, **params): upload = FileUpload(**params) upload.add_file(chunks, filename, size) return upload @property def processed(self): return bool(self.valid or self.validation) @property def validation_timeout(self): if self.processed: validation = self.load_validation() messages = validation['messages'] timeout_id = [ 'validator', 'unexpected_exception', 'validation_timeout' ] return any(msg['id'] == timeout_id for msg in messages) else: return False @property def processed_validation(self): """Return processed validation results as expected by the frontend.""" if self.validation: # Import loop. from olympia.devhub.utils import process_validation validation = self.load_validation() is_compatibility = self.compat_with_app is not None return process_validation(validation, is_compatibility, self.hash) @property def passed_all_validations(self): return self.processed and self.valid @property def passed_auto_validation(self): return self.load_validation()['passed_auto_validation'] def load_validation(self): return json.loads(self.validation) @property def pretty_name(self): parts = self.name.split('_', 1) if len(parts) > 1: return parts[1] return self.name
class FileUpload(ModelBase): """Created when a file is uploaded for validation/submission.""" uuid = UUIDField(auto=True) path = models.CharField(max_length=255, default='') name = models.CharField(max_length=255, default='', help_text="The user's original filename") hash = models.CharField(max_length=255, default='') user = models.ForeignKey('users.UserProfile', null=True) valid = models.BooleanField(default=False) validation = models.TextField(null=True) automated_signing = models.BooleanField(default=False) compat_with_app = models.PositiveIntegerField( choices=amo.APPS_CHOICES, db_column="compat_with_app_id", null=True) compat_with_appver = models.ForeignKey( AppVersion, null=True, related_name='uploads_compat_for_appver') # Not all FileUploads will have a version and addon but it will be set # if the file was uploaded using the new API. version = models.CharField(max_length=255, null=True) addon = models.ForeignKey('addons.Addon', null=True) objects = UncachedManagerBase() class Meta(ModelBase.Meta): db_table = 'file_uploads' def __unicode__(self): return self.uuid def save(self, *args, **kw): if self.validation: if self.load_validation()['errors'] == 0: self.valid = True super(FileUpload, self).save(*args, **kw) def add_file(self, chunks, filename, size): if not self.uuid: self.uuid = self._meta.get_field('uuid')._create_uuid().hex filename = smart_str(u'{0}_{1}'.format(self.uuid, filename)) loc = os.path.join(user_media_path('addons'), 'temp', uuid.uuid4().hex) base, ext = os.path.splitext(smart_path(filename)) if ext in EXTENSIONS: loc += ext log.info('UPLOAD: %r (%s bytes) to %r' % (filename, size, loc)) hash = hashlib.sha256() with storage.open(loc, 'wb') as fd: for chunk in chunks: hash.update(chunk) fd.write(chunk) self.path = loc self.name = filename self.hash = 'sha256:%s' % hash.hexdigest() self.save() @classmethod def from_post(cls, chunks, filename, size, **params): upload = FileUpload(**params) upload.add_file(chunks, filename, size) return upload @property def processed(self): return bool(self.valid or self.validation) @property def validation_timeout(self): if self.processed: validation = self.load_validation() messages = validation['messages'] timeout_id = [ 'validator', 'unexpected_exception', 'validation_timeout' ] return any(msg['id'] == timeout_id for msg in messages) else: return False @property def processed_validation(self): """Return processed validation results as expected by the frontend.""" if self.validation: # Import loop. from olympia.devhub.utils import process_validation validation = self.load_validation() is_compatibility = self.compat_with_app is not None return process_validation(validation, is_compatibility, self.hash) @property def passed_all_validations(self): return self.processed and self.valid @property def passed_auto_validation(self): return self.load_validation()['passed_auto_validation'] def load_validation(self): return json.loads(self.validation)