def process_reject(self): """ Reject an app. Changes status to Rejected. Creates Rejection note. """ # Hold onto the status before we change it. status = self.webapp.status self.set_files(mkt.STATUS_DISABLED, self.version.files.all(), hide_disabled_file=True) # If this app is not packaged (packaged apps can have multiple # versions) or if there aren't other versions with already reviewed # files, reject the app also. if (not self.webapp.is_packaged or not self.webapp.versions.exclude(id=self.version.id) .filter(files__status__in=mkt.REVIEWED_STATUSES).exists()): self.set_webapp(status=mkt.STATUS_REJECTED) if self.in_escalate: EscalationQueue.objects.filter(webapp=self.webapp).delete() if self.in_rereview: RereviewQueue.objects.filter(webapp=self.webapp).delete() self.create_note(mkt.LOG.REJECT_VERSION) log.info(u'Making %s disabled' % self.webapp) # Assign reviewer incentive scores. return ReviewerScore.award_points(self.request.user, self.webapp, status, in_rereview=self.in_rereview)
def hera(request): form = FlushForm(initial={'flushprefix': settings.SITE_URL}) boxes = [] configured = False # Default to not showing the form. for i in settings.HERA: hera = get_hera(i) r = {'location': urlparse(i['LOCATION'])[1], 'stats': False} if hera: r['stats'] = hera.getGlobalCacheInfo() configured = True boxes.append(r) if not configured: messages.error(request, "Hera is not (or mis-)configured.") form = None if request.method == 'POST' and hera: form = FlushForm(request.POST) if form.is_valid(): expressions = request.POST['flushlist'].splitlines() for url in expressions: num = flush_urls([url], request.POST['flushprefix'], True) msg = ("Flushed %d objects from front end cache for: %s" % (len(num), url)) log.info("[Hera] (user:%s) %s" % (request.user, msg)) messages.success(request, msg) return jingo.render(request, 'zadmin/hera.html', {'form': form, 'boxes': boxes})
def process_request(self, request): regions = mkt.regions.REGION_LOOKUP user_region = restofworld = mkt.regions.RESTOFWORLD if not getattr(request, 'API', False): request.REGION = restofworld mkt.regions.set_region(restofworld) return # Try 'region' in POST/GET data first, if it's not there try geoip. url_region = request.REQUEST.get('region') if url_region in regions: statsd.incr('z.regions.middleware.source.url') user_region = regions[url_region] log.info('Region {0} specified in URL; region set as {1}' .format(url_region, user_region.slug)) elif getattr(request, 'API_VERSION', None) == 1: # Fallback to GeoIP, but only for API version 1. statsd.incr('z.regions.middleware.source.geoip') user_region = self.region_from_request(request) log.info('Region not specified in URL; region set as {0}' .format(user_region.slug)) # Update the region on the user object if it changed. if (request.user.is_authenticated() and request.user.region != user_region.slug): request.user.region = user_region.slug request.user.save() # Persist the region on the request / local thread. self.store_region(request, user_region)
def revision_add_attachment(request, pk): """Add attachment, download if necessary """ revision = get_object_or_404(PackageRevision, pk=pk) if request.user.pk != revision.author.pk: log_msg = ("[security] Attempt to add attachment to package (%s) by " "non-owner (%s)" % (revision.package, request.user)) log.warning(log_msg) return HttpResponseForbidden( 'You are not the author of this %s' % escape( revision.package.get_type_name())) url = request.POST.get('url', None) filename = request.POST.get('filename', None) if not filename or filename == "": log.error('Trying to create an attachment without name') return HttpResponseBadRequest('Path not found.') content = '' if url: log.info(('[%s] Preparing to download %s as an attachment of ' 'PackageRevision %d') % (filename, url, revision.pk)) # validate url field = URLField(verify_exists=True) encoding = request.POST.get('force_contenttype', False) try: url = field.clean(url) except ValidationError, err: log.warning('[%s] Invalid url provided\n%s' % (url, '\n'.join(err.messages))) return HttpResponseBadRequest(("Loading attachment failed\n" "%s") % parse_validation_messages(err)) except Exception, err: log.warning('[%s] Exception raised\n%s' % (url, str(err))) return HttpResponseBadRequest(str(err))
def performance_add(request): """ A wrapper around adding in performance data that is easier than using the piston API. """ # Trigger OAuth. if not AMOOAuthAuthentication(two_legged=True).is_authenticated(request): return rc.FORBIDDEN form = PerformanceForm(request.POST) if not form.is_valid(): return form.show_error() os, created = (PerformanceOSVersion .objects.safer_get_or_create(**form.os_version)) app, created = (PerformanceAppVersions .objects.safer_get_or_create(**form.app_version)) data = form.performance data.update({'osversion': os, 'appversion': app}) # Look up on everything except the average time. result, created = Performance.objects.safer_get_or_create(**data) result.average = form.cleaned_data['average'] result.save() log.info('Performance created for add-on: %s, %s' % (form.cleaned_data['addon_id'], form.cleaned_data['average'])) return rc.ALL_OK
def cleanup_validation_results(): """Will remove all validation results. Used when the validator is upgraded and results may no longer be relevant.""" # With a large enough number of objects not using tracebacks all = FileValidation.objects.all() log.info('Removing %s old validation results.' % (all.count())) all.delete()
def emailchange(request, user_id, token, hash): user = get_object_or_404(UserProfile, id=user_id) try: _uid, newemail = EmailResetCode.parse(token, hash) except ValueError: return http.HttpResponse(status=400) if _uid != user.id: # I'm calling this a warning because invalid hashes up to this point # could be any number of things, but this is a targeted attack from # one user account to another log.warning((u"[Tampering] Valid email reset code for UID (%s) " "attempted to change email address for user (%s)") % (_uid, user)) return http.HttpResponse(status=400) user.email = newemail user.save() l = {'user': user, 'newemail': newemail} log.info(u"User (%(user)s) confirmed new email address (%(newemail)s)" % l) messages.success(request, _('Your email address was changed successfully'), _(u'From now on, please use {0} to log in.').format(newemail)) return http.HttpResponseRedirect(reverse('users.edit'))
def process_reject(self): """ Reject an app. Changes status to Rejected. Creates Rejection note/email. """ # Hold onto the status before we change it. status = self.addon.status self.set_files(amo.STATUS_DISABLED, self.version.files.all(), hide_disabled_file=True) # If this app is not packaged (packaged apps can have multiple # versions) or if there aren't other versions with already reviewed # files, reject the app also. if (not self.addon.is_packaged or not self.addon.versions.exclude(id=self.version.id) .filter(files__status__in=amo.REVIEWED_STATUSES).exists()): self.set_addon(status=amo.STATUS_REJECTED) if self.in_escalate: EscalationQueue.objects.filter(addon=self.addon).delete() if self.in_rereview: RereviewQueue.objects.filter(addon=self.addon).delete() self.create_note(amo.LOG.REJECT_VERSION) self.notify_email('pending_to_sandbox', u'Your submission has been rejected: %s') log.info(u'Making %s disabled' % self.addon) # Assign reviewer incentive scores. return ReviewerScore.award_points(self.request.amo_user, self.addon, status, in_rereview=self.in_rereview)
def cleanup_extracted_file(): log.info('Removing extracted files for file viewer.') root = os.path.join(settings.TMP_PATH, 'file_viewer') # Local storage uses local time for file modification. S3 uses UTC time. now = datetime.utcnow if storage_is_remote() else datetime.now for path in private_storage.listdir(root)[0]: full = os.path.join(root, path) age = now() - private_storage.modified_time( os.path.join(full, 'manifest.webapp')) if age.total_seconds() > (60 * 60): log.debug('Removing extracted files: %s, %dsecs old.' % (full, age.total_seconds())) for subroot, dirs, files in walk_storage(full): for f in files: private_storage.delete(os.path.join(subroot, f)) # Nuke out the file and diff caches when the file gets removed. id = os.path.basename(path) try: int(id) except ValueError: continue key = hashlib.md5() key.update(str(id)) cache.delete('%s:memoize:%s:%s' % (settings.CACHE_PREFIX, 'file-viewer', key.hexdigest()))
def edit(request): webapp = settings.APP_PREVIEW # Don't use request.amo_user since it has too much caching. amouser = UserProfile.objects.get(pk=request.user.id) if request.method == 'POST': # ModelForm alters the instance you pass in. We need to keep a copy # around in case we need to use it below (to email the user) original_email = amouser.email form = forms.UserEditForm(request.POST, request.FILES, request=request, instance=amouser, webapp=webapp) if form.is_valid(): messages.success(request, _('Profile Updated')) if amouser.email != original_email: # Temporarily block email changes. if settings.APP_PREVIEW: messages.error(request, 'Error', 'You cannot change your email on the ' 'developer preview site.') return jingo.render(request, 'users/edit.html', {'form': form, 'amouser': amouser}) l = {'user': amouser, 'mail1': original_email, 'mail2': amouser.email} log.info(u"User (%(user)s) has requested email change from" "(%(mail1)s) to (%(mail2)s)" % l) messages.info(request, _('Email Confirmation Sent'), _(u'An email has been sent to {0} to confirm your new ' 'email address. For the change to take effect, you ' 'need to click on the link provided in this email. ' 'Until then, you can keep logging in with your ' 'current email address.').format(amouser.email)) domain = settings.DOMAIN token, hash = EmailResetCode.create(amouser.id, amouser.email) url = "%s%s" % (settings.SITE_URL, reverse('users.emailchange', args=[amouser.id, token, hash])) t = loader.get_template('users/email/emailchange.ltxt') c = {'domain': domain, 'url': url} send_mail(_('Please confirm your email address ' 'change at %s' % domain), t.render(Context(c)), None, [amouser.email], use_blacklist=False, real_email=True) # Reset the original email back. We aren't changing their # address until they confirm the new one amouser.email = original_email form.save() return redirect('users.edit') else: messages.error(request, _('Errors Found'), _('There were errors in the changes ' 'you made. Please correct them and ' 'resubmit.')) else: form = forms.UserEditForm(instance=amouser, webapp=webapp) return jingo.render(request, 'users/edit.html', {'form': form, 'amouser': amouser, 'webapp': webapp})
def notify_email(self, template, subject, fresh_thread=False): """Notify the authors that their app has been reviewed.""" if waffle.switch_is_active('comm-dashboard'): # Communication dashboard uses send_mail_comm. return data = self.data.copy() data.update(self.get_context_data()) data['tested'] = '' dt, br = data.get('device_types'), data.get('browsers') if dt and br: data['tested'] = 'Tested on %s with %s' % (dt, br) elif dt and not br: data['tested'] = 'Tested on %s' % dt elif not dt and br: data['tested'] = 'Tested with %s' % br emails = list(self.addon.authors.values_list('email', flat=True)) cc_email = self.addon.get_mozilla_contacts() log.info(u'Sending email for %s' % self.addon) send_mail(subject % data['name'], 'reviewers/emails/decisions/%s.txt' % template, data, emails, perm_setting='app_reviewed', cc=cc_email, attachments=self.get_attachments())
def index_collection_counts(ids, **kw): index = kw.pop('index', None) indices = get_indices(index) es = amo.search.get_es() qs = CollectionCount.objects.filter(collection__in=ids) if qs: log.info('Indexing %s addon collection counts: %s' % (qs.count(), qs[0].date)) try: for collection_count in qs: collection = collection_count.collection_id key = '%s-%s' % (collection, collection_count.date) filters = dict(collection=collection, date=collection_count.date) data = search.extract_addon_collection( collection_count, AddonCollectionCount.objects.filter(**filters), CollectionStats.objects.filter(**filters)) for index in indices: CollectionCount.index(data, bulk=True, id=key, index=index) es.flush_bulk(forced=True) except Exception, exc: index_collection_counts.retry(args=[ids], exc=exc) raise
def _process_private(self): """Make an app private.""" if self.addon.has_incomplete_status(): # Failsafe. return self.addon.sign_if_packaged(self.version.pk) # If there are no prior PUBLIC versions we set the file status to # PUBLIC no matter what ``publish_type`` was chosen since at least one # version needs to be PUBLIC when an app is approved to set a # ``current_version``. if File.objects.filter(version__addon__pk=self.addon.pk, status=mkt.STATUS_PUBLIC).count() == 0: self.set_files(mkt.STATUS_PUBLIC, self.version.files.all()) else: self.set_files(mkt.STATUS_APPROVED, self.version.files.all()) if self.addon.status not in (mkt.STATUS_PUBLIC, mkt.STATUS_UNLISTED): self.set_addon(status=mkt.STATUS_APPROVED, highest_status=mkt.STATUS_APPROVED) self.set_reviewed() self.create_note(mkt.LOG.APPROVE_VERSION_PRIVATE) log.info(u'Making %s approved' % self.addon)
def reviewer_sign_file(self): """Sign the original file (`file_path`) with reviewer certs, then move the signed file to the reviewers-specific signed path (`reviewer_signed_file_path`) on private storage.""" if not self.extension.uuid: raise SigningError('Need uuid to be set to sign') if not self.pk: raise SigningError('Need version pk to be set to sign') ids = json.dumps({ # Reviewers get a unique 'id' so the reviewer installed add-on # won't conflict with the public add-on, and also so even multiple # versions of the same add-on can be installed side by side with # other versions. 'id': 'reviewer-{guid}-{version_id}'.format( guid=self.extension.uuid, version_id=self.pk), 'version': self.pk }) with statsd.timer('extensions.sign_reviewer'): try: # This will read the file from self.file_path, generate a # reviewer signature and write the signed file to # self.reviewer_signed_file_path. sign_app(private_storage.open(self.file_path), self.reviewer_signed_file_path, ids, reviewer=True) except SigningError: log.info( '[ExtensionVersion:%s] Reviewer Signing failed' % self.pk) if private_storage.exists(self.reviewer_signed_file_path): private_storage.delete(self.reviewer_signed_file_path) raise
def sign_file(self): """Sign the original file (`file_path`), then move signed extension file to the signed path (`signed_file_path`) on public storage. The original file remains on private storage. Return the signed file size.""" if not self.extension.uuid: raise SigningError('Need uuid to be set to sign') if not self.pk: raise SigningError('Need version pk to be set to sign') ids = json.dumps({ # 'id' needs to be an unique identifier not shared with anything # else (other extensions, langpacks, webapps...), but should not # change when there is an update. 'id': self.extension.uuid, # 'version' should be an integer and should be monotonically # increasing. 'version': self.pk }) with statsd.timer('extensions.sign'): try: # This will read the file from self.file_path, generate a # signature and write the signed file to self.signed_file_path. sign_app(private_storage.open(self.file_path), self.signed_file_path, ids) except SigningError: log.info('[ExtensionVersion:%s] Signing failed' % self.pk) self.remove_public_signed_file() # Clean up. raise return public_storage.size(self.signed_file_path)
def from_upload(cls, upload, addon, platforms): data = utils.parse_addon(upload.path, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None v = cls.objects.create(addon=addon, version=data['version'], license_id=license) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) # appversions AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type == amo.ADDON_SEARCH: # Search extensions are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: platforms = cls._make_safe_platform_files(platforms) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) v.disable_old_files() # After the upload has been copied to all # platforms, remove the upload. upload.path.unlink() version_uploaded.send(sender=v) return v
def obj_create(self, bundle, request, **kwargs): with statsd.timer('auth.browserid.verify'): profile, msg = browserid_authenticate( request, bundle.data['assertion'], browserid_audience=bundle.data['audience'], is_native=bundle.data.get('is_native', False) ) if profile is None: log.info('No profile: %s' % (msg or '')) raise http_error(http.HttpUnauthorized, 'No profile.') request.user, request.amo_user = profile.user, profile request.groups = profile.groups.all() # TODO: move this to the signal. profile.log_login_attempt(True) user_logged_in.send(sender=profile.user.__class__, request=request, user=profile.user) bundle.data = { 'error': None, 'token': self.get_token(request.amo_user.email), 'settings': { 'display_name': request.amo_user.display_name, 'email': request.amo_user.email, } } bundle.data.update(PermissionResource() .dehydrate(Bundle(request=request)).data) return bundle
def save_from_email_reply(reply_text): parser = CommEmailParser(reply_text) if hasattr(parser, "decode_error"): return False uuid = parser.get_uuid() if not uuid: return False try: tok = CommunicationThreadToken.objects.get(uuid=uuid) except CommunicationThreadToken.DoesNotExist: log.error("An email was skipped with non-existing uuid %s." % uuid) return False if user_has_perm_thread(tok.thread, tok.user) and tok.is_valid(): t, note = create_comm_note(tok.thread.addon, tok.thread.version, tok.user, parser.get_body()) log.info("A new note has been created (from %s using tokenid %s)." % (tok.user.id, uuid)) return note elif tok.is_valid(): log.error("%s did not have perms to reply to comm email thread %s." % (tok.user.email, tok.thread.id)) else: log.error("%s tried to use an invalid comm token for thread %s." % (tok.user.email, tok.thread.id)) return False
def wrapper(request, addon, *args, **kw): # If there is a 'token' in request.GET we either return 200 or 403. # Otherwise we treat it like a normal django view and redirect to a # login page or check for Apps:Review permissions. allowed = False token = request.GET.get('token') if token and Token.pop(token, data={'app_id': addon.id}): log.info('Token for app:%s was successfully used' % addon.id) allowed = True elif not token and not request.user.is_authenticated(): return redirect_for_login(request) elif acl.action_allowed(request, 'Apps', 'Review'): allowed = True if allowed: if token: log.info('Token provided for app:%s and all was happy' % addon.id) else: log.info('Apps:Review (no token) all happy for app:%s' % addon.id) return f(request, addon, *args, **kw) else: if token: log.info('Token provided for app:%s but was not valid' % addon.id) else: log.info('Apps:Review permissions not met for app:%s' % addon.id) raise PermissionDenied
def post(self, request, *args, **kwargs): form = PrepareWebAppForm(request.DATA) if not form.is_valid(): return Response(form.errors, status=status.HTTP_400_BAD_REQUEST) app = form.cleaned_data['app'] region = getattr(request, 'REGION', None) if region and region.id not in app.get_price_region_ids(): log.info('Region {0} is not in {1}' .format(region.id, app.get_price_region_ids())) return Response('Payments are limited and flag not enabled', status=status.HTTP_403_FORBIDDEN) if app.is_premium() and app.has_purchased(request._request.amo_user): log.info('Already purchased: {0}'.format(app.pk)) return Response({'reason': u'Already purchased app.'}, status=status.HTTP_409_CONFLICT) app_pay_cef.log(request._request, 'Preparing JWT', 'preparing_jwt', 'Preparing JWT for: {0}'.format(app.pk), severity=3) token = get_product_jwt( WebAppProduct(app), client_data=ClientData.get_or_create(request._request), lang=request._request.LANG, region=request._request.REGION, source=request._request.REQUEST.get('src', ''), user=request._request.amo_user, ) return Response(token, status=status.HTTP_201_CREATED)
def send_mail_comm(note): """ Email utility used globally by the Communication Dashboard to send emails. Given a note (its actions and permissions), recipients are determined and emails are sent to appropriate people. """ from mkt.reviewers.utils import send_mail if not waffle.switch_is_active("comm-dashboard"): return recipients = get_recipients(note) name = note.thread.addon.name data = {"name": name, "sender": note.author.name, "comments": note.body, "thread_id": str(note.thread.id)} subject = {comm.ESCALATION: u"Escalated Review Requested: %s" % name}.get( note.note_type, u"Submission Update: %s" % name ) log.info(u"Sending emails for %s" % note.thread.addon) for email, tok in recipients: reply_to = "{0}{1}@{2}".format(comm.REPLY_TO_PREFIX, tok, settings.POSTFIX_DOMAIN) send_mail( subject, "reviewers/emails/decisions/post.txt", data, [email], perm_setting="app_reviewed", reply_to=reply_to, )
def process_sandbox(self): """Set an addon back to sandbox.""" # Hold onto the status before we change it. status = self.addon.status if (not self.is_upgrade or not self.addon.versions.exclude(id=self.version.id) .filter(files__status__in=amo.REVIEWED_STATUSES)): self.set_addon(status=amo.STATUS_NULL) else: self.set_addon(status=amo.STATUS_LITE) self.set_files(amo.STATUS_DISABLED, self.version.files.all(), hide_disabled_file=True) self.log_action(amo.LOG.REJECT_VERSION) self.notify_email('%s_to_sandbox' % self.review_type, u'Mozilla Add-ons: %s %s Rejected') log.info(u'Making %s disabled' % (self.addon)) log.info(u'Sending email for %s' % (self.addon)) # Assign reviewer incentive scores. ReviewerScore.award_points(self.request.amo_user, self.addon, status)
def obj_update(self, bundle, request, **kwargs): data = bundle.data try: obj = self.get_object_list(bundle.request).get(**kwargs) except Webapp.DoesNotExist: raise ImmediateHttpResponse(response=http.HttpNotFound()) if not AppOwnerAuthorization().is_authorized(request, object=obj): raise ImmediateHttpResponse(response=http.HttpUnauthorized()) data['slug'] = data.get('slug', obj.app_slug) data.update(self.formset(data)) data.update(self.devices(data)) forms = [AppDetailsBasicForm(data, instance=obj, request=request), DeviceTypeForm(data, addon=obj), CategoryFormSet(data, addon=obj, request=request)] valid = all([f.is_valid() for f in forms]) if not valid: raise ValidationError(self.form_errors(forms)) forms[0].save(obj) forms[1].save(obj) forms[2].save() log.info('App updated: %s' % obj.pk) bundle.obj = obj return bundle
def vouchify(): """Synchronizes LDAP vouch info into database. This queries LDAP for users who's corresponding ``UserProfile`` has ``is_vouched`` as ``False``. It then updates ``is_vouched`` and ``vouched_by`` with up-to-date data. """ users = UserProfile.objects.filter(is_vouched=False) for user in users: person = user.get_ldap_person() if person and 'mozilliansVouchedBy' in person[1]: user.is_vouched = True voucher = (person[1]['mozilliansVouchedBy'][0].split(',')[0] .split('=')[1]) by = larper.get_user_by_uid(voucher) if by: email = by[1]['mail'][0] try: user.vouched_by = (User.objects.get(email=email) .get_profile()) except User.DoesNotExist: log.warning('No matching user for %s' % email) except UserProfile.DoesNotExist: log.warning('No matching user_profile for %s' % email) user.save() log.info('Data copied for %s' % user.user.username) log.debug('%s is still unvouched... skipping' % user.user.username)
def obj_get(self, request=None, **kwargs): obj = super(AppResource, self).obj_get(request=request, **kwargs) if not AppOwnerAuthorization().is_authorized(request, object=obj): raise ImmediateHttpResponse(response=http.HttpUnauthorized()) log.info('App retreived: %s' % obj.pk) return obj
def process_preliminary(self, auto_validation=False): """Set an addons files to preliminary.""" # Sign addon. for file_ in self.files: sign_file(file_, settings.PRELIMINARY_SIGNING_SERVER) # Hold onto the status before we change it. status = self.addon.status self.set_files(amo.STATUS_LITE, self.files, copy_to_mirror=True) self.log_action(amo.LOG.PRELIMINARY_VERSION) template = u'%s_to_preliminary' % self.review_type subject = u'Mozilla Add-ons: %s %s Preliminary Reviewed' if not self.addon.is_listed: template = u'unlisted_to_reviewed' if auto_validation: template = u'unlisted_to_reviewed_auto' subject = u'Mozilla Add-ons: %s %s signed and ready to download' self.notify_email(template, subject) log.info(u'Making %s files %s preliminary' % (self.addon, ', '.join([f.filename for f in self.files]))) log.info(u'Sending email for %s' % (self.addon)) if self.request and not auto_validation: # Assign reviewer incentive scores. ReviewerScore.award_points(self.request.user, self.addon, status)
def from_upload(cls, upload, addon, platforms, send_signal=True): data = utils.parse_addon(upload, addon) try: license = addon.versions.latest().license_id except Version.DoesNotExist: license = None max_len = cls._meta.get_field_by_name('_developer_name')[0].max_length developer = data.get('developer_name', '')[:max_len] v = cls.objects.create(addon=addon, version=data['version'], license_id=license, _developer_name=developer) log.info('New version: %r (%s) from %r' % (v, v.id, upload)) AV = ApplicationsVersions for app in data.get('apps', []): AV(version=v, min=app.min, max=app.max, application_id=app.id).save() if addon.type == amo.ADDON_SEARCH: # Search extensions are always for all platforms. platforms = [Platform.objects.get(id=amo.PLATFORM_ALL.id)] else: platforms = cls._make_safe_platform_files(platforms) for platform in platforms: File.from_upload(upload, v, platform, parse_data=data) v.disable_old_files() # After the upload has been copied to all platforms, remove the upload. storage.delete(upload.path) if send_signal: version_uploaded.send(sender=v) return v
def send_super_mail(self): self.log_action(amo.LOG.REQUEST_SUPER_REVIEW) log.info(u'Super review requested for %s' % (self.addon)) send_mail('editors/emails/super_review.ltxt', u'Super review requested: %s' % (self.addon.name), [settings.SENIOR_EDITORS_EMAIL], Context(self.get_context_data()))
def process_public(self, auto_validation=False): """Set an addons files to public.""" if self.review_type == 'preliminary': raise AssertionError('Preliminary addons cannot be made public.') # Sign addon. for file_ in self.files: sign_file(file_, settings.SIGNING_SERVER) # Hold onto the status before we change it. status = self.addon.status self.set_files(amo.STATUS_PUBLIC, self.files, copy_to_mirror=True) self.log_action(amo.LOG.APPROVE_VERSION) template = u'%s_to_public' % self.review_type subject = u'Mozilla Add-ons: %s %s Fully Reviewed' if not self.addon.is_listed: template = u'unlisted_to_reviewed' if auto_validation: template = u'unlisted_to_reviewed_auto' subject = u'Mozilla Add-ons: %s %s signed and ready to download' self.notify_email(template, subject) log.info(u'Making %s files %s public' % (self.addon, ', '.join([f.filename for f in self.files]))) log.info(u'Sending email for %s' % (self.addon)) # Assign reviewer incentive scores. if self.request and not auto_validation: ReviewerScore.award_points(self.request.user, self.addon, status)
def process_preliminary(self): """Set an addon to preliminary.""" # Hold onto the status before we change it. status = self.addon.status changes = {'status': amo.STATUS_LITE} if (self.addon.status in (amo.STATUS_PUBLIC, amo.STATUS_LITE_AND_NOMINATED)): changes['highest_status'] = amo.STATUS_LITE template = '%s_to_preliminary' % self.review_type if (self.review_type == 'preliminary' and self.addon.status == amo.STATUS_LITE_AND_NOMINATED): template = 'nominated_to_nominated' self.set_addon(**changes) self.set_files(amo.STATUS_LITE, self.version.files.all(), copy_to_mirror=True) self.log_action(amo.LOG.PRELIMINARY_VERSION) self.notify_email(template, u'Mozilla Add-ons: %s %s Preliminary Reviewed') log.info(u'Making %s preliminary' % (self.addon)) log.info(u'Sending email for %s' % (self.addon)) # Assign reviewer incentive scores. ReviewerScore.award_points(self.request.amo_user, self.addon, status)
def _delete_logs(items, **kw): log.info('[%s@%s] Deleting logs' % (len(items), _delete_logs.rate_limit)) ActivityLog.objects.filter(pk__in=items).exclude( action__in=amo.LOG_KEEP).delete()
def delete_anonymous_collections(items, **kw): log.info('[%s@%s] Deleting anonymous collections' % (len(items), delete_anonymous_collections.rate_limit)) Collection.objects.filter(type=amo.COLLECTION_ANONYMOUS, pk__in=items).delete()
def delete_stale_contributions(items, **kw): log.info('[%s@%s] Deleting stale contributions' % (len(items), delete_stale_contributions.rate_limit)) Contribution.objects.filter(transaction_id__isnull=True, pk__in=items).delete()
def refresh_manifest(request, addon_id, addon, webapp=False): log.info('Manifest %s refreshed for %s' % (addon.manifest_url, addon)) _update_manifest(addon_id, True, {}) return http.HttpResponse(status=204)
def status(request, addon_id, addon, webapp=False): form = forms.AppAppealForm(request.POST, product=addon) upload_form = NewWebappVersionForm(request.POST or None, is_packaged=True, addon=addon, request=request) if request.method == 'POST': if 'resubmit-app' in request.POST and form.is_valid(): form.save() create_comm_note(addon, addon.latest_version, request.amo_user, form.data['notes'], note_type=comm.RESUBMISSION) if addon.vip_app: handle_vip(addon, addon.current_version, request.amo_user) messages.success(request, _('App successfully resubmitted.')) return redirect(addon.get_dev_url('versions')) elif 'upload-version' in request.POST and upload_form.is_valid(): mobile_only = (addon.latest_version and addon.latest_version.features.has_qhd) ver = Version.from_upload(upload_form.cleaned_data['upload'], addon, [amo.PLATFORM_ALL]) # Update addon status now that the new version was saved. addon.update_status() res = run_validator(ver.all_files[0].file_path) validation_result = json.loads(res) # Set all detected features as True and save them. keys = [ 'has_%s' % feature.lower() for feature in validation_result['feature_profile'] ] data = defaultdict.fromkeys(keys, True) # Set "Smartphone-Sized Displays" if it's a mobile-only app. qhd_devices = (set((amo.DEVICE_GAIA, )), set( (amo.DEVICE_MOBILE, )), set(( amo.DEVICE_GAIA, amo.DEVICE_MOBILE, ))) if set(addon.device_types) in qhd_devices or mobile_only: data['has_qhd'] = True # Update feature profile for this version. ver.features.update(**data) messages.success(request, _('New version successfully added.')) log.info('[Webapp:%s] New version created id=%s from upload: %s' % (addon, ver.pk, upload_form.cleaned_data['upload'])) if addon.vip_app: handle_vip(addon, ver, request.amo_user) return redirect(addon.get_dev_url('versions.edit', args=[ver.pk])) ctx = { 'addon': addon, 'webapp': webapp, 'form': form, 'upload_form': upload_form } # Used in the delete version modal. if addon.is_packaged: versions = addon.versions.values('id', 'version') version_strings = dict((v['id'], v) for v in versions) version_strings['num'] = len(versions) ctx['version_strings'] = json.dumps(version_strings) if addon.status == amo.STATUS_REJECTED: try: entry = (AppLog.objects.filter( addon=addon, activity_log__action=amo.LOG.REJECT_VERSION.id).order_by( '-created'))[0] except IndexError: entry = None # This contains the rejection reason and timestamp. ctx['rejection'] = entry and entry.activity_log if waffle.switch_is_active('preload-apps'): test_plan = PreloadTestPlan.objects.filter(addon=addon, status=amo.STATUS_PUBLIC) if test_plan.exists(): test_plan = test_plan[0] if (test_plan.last_submission < settings.PREINSTALL_TEST_PLAN_LATEST): ctx['outdated_test_plan'] = True ctx['next_step_suffix'] = 'submit' else: ctx['next_step_suffix'] = 'home' ctx['test_plan'] = test_plan return jingo.render(request, 'developers/apps/status.html', ctx)
def ping(**kw): queue = kw['delivery_info']['routing_key'] log.info('[1@None] Checking the %s queue' % queue) QueueCheck().set('pong', queue)
def _activity_log_scrubber(items, **kw): log.info('[%s@%s] Deleting activity log items' % (len(items), _activity_log_scrubber.rate_limit)) ActivityLog.objects.filter(id__in=items).delete()
log.debug('[copy: %s] Copying started from (%s)' % (revision_id, source)) # save package try: package = source.package.copy(request.user) except IntegrityError, err: log.critical(("[copy: %s] Package copy failed") % revision_id) return HttpResponseForbidden('You already have a %s with that name' % escape(source.package.get_type_name())) # save revision with all dependencies source.save_new_revision(package) copied = source del source log.info('[copy: %s] Copied to %s, (%s)' % (revision_id, copied.pk, copied.full_name)) return render_json(request, "json/%s_copied.json" % package.get_type_name(), {'revision': copied}) @login_required def disable(request, pk): """ Disable Package and return confirmation """ package = get_object_or_404(Package, pk=pk) if request.user.pk != package.author.pk: log_msg = 'User %s wanted to disable not his own Package %s.' % ( request.user, pk) log.warning(log_msg)
pass def get_uuid(): return 'webpay:%s' % hashlib.md5(str(uuid.uuid4())).hexdigest() def sign_webpay_jwt(data): return jwt.encode(data, settings.APP_PURCHASE_SECRET) def parse_from_webpay(signed_jwt, ip): try: data = jwt.decode(signed_jwt.encode('ascii'), settings.APP_PURCHASE_SECRET, algorithms=settings.SUPPORTED_JWT_ALGORITHMS) except Exception, e: exc_type, exc_value, tb = sys.exc_info() log.info('Received invalid webpay postback from IP %s: %s' % (ip or '(unknown)', e), exc_info=True) raise InvalidSender(e), None, tb verify_claims(data) iss, aud, product_data, trans_id = verify_keys( data, ('iss', 'aud', 'request.productData', 'response.transactionID')) log.info('Received webpay postback JWT: iss:%s aud:%s ' 'trans_id:%s product_data:%s' % (iss, aud, trans_id, product_data)) return data
def transaction_refund(request, tx_uuid): contrib = get_object_or_404(Contribution, uuid=tx_uuid, type=mkt.CONTRIB_PURCHASE) refund_contribs = contrib.get_refund_contribs() refund_contrib = refund_contribs[0] if refund_contribs.exists() else None if refund_contrib: messages.error(request, _('A refund has already been processed.')) return redirect(reverse('lookup.transaction_summary', args=[tx_uuid])) form = TransactionRefundForm(request.POST) if not form.is_valid(): return render( request, 'lookup/transaction_summary.html', dict({ 'uuid': tx_uuid, 'tx_refund_form': form, 'tx_form': TransactionSearchForm() }.items() + _transaction_summary(tx_uuid).items())) data = { 'uuid': contrib.transaction_id, 'manual': form.cleaned_data['manual'] } if settings.BANGO_FAKE_REFUNDS: data['fake_response_status'] = { 'responseCode': form.cleaned_data['fake'] } try: res = client.api.bango.refund.post(data) except (HttpClientError, HttpServerError): # Either doing something not supposed to or Solitude had an issue. log.exception('Refund error: %s' % tx_uuid) messages.error( request, _('You cannot make a refund request for this transaction.')) return redirect(reverse('lookup.transaction_summary', args=[tx_uuid])) if res['status'] in [PENDING, COMPLETED]: # Create refund Contribution by cloning the payment Contribution. refund_contrib = Contribution.objects.get(id=contrib.id) refund_contrib.id = None refund_contrib.save() log.info('Creating refund transaction from: {0} ' 'with transaction_id of: {1}'.format(contrib.id, res['uuid'])) refund_contrib.update( type=mkt.CONTRIB_REFUND, related=contrib, uuid=str(uuid.uuid4()), amount=-refund_contrib.amount if refund_contrib.amount else None, transaction_id=res['uuid']) if res['status'] == PENDING: # Create pending Refund. refund_contrib.enqueue_refund( mkt.REFUND_PENDING, request.user, refund_reason=form.cleaned_data['refund_reason']) log.info('Refund pending: %s' % tx_uuid) messages.success(request, _('Refund for this transaction now pending.')) elif res['status'] == COMPLETED: # Create approved Refund. refund_contrib.enqueue_refund( mkt.REFUND_APPROVED, request.user, refund_reason=form.cleaned_data['refund_reason']) log.info('Refund approved: %s' % tx_uuid) messages.success( request, _('Refund for this transaction successfully approved.')) elif res['status'] == FAILED: # Bango no like. log.error('Refund failed: %s' % tx_uuid) messages.error(request, _('Refund request for this transaction failed.')) return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))
def register(request): if settings.APP_PREVIEW and waffle.switch_is_active('browserid-login'): messages.error(request, loc('Registrations must be through browserid.')) form = None raise http.Http404() elif request.user.is_authenticated(): messages.info(request, _('You are already logged in to an account.')) form = None elif request.method == 'POST': form = forms.UserRegisterForm(request.POST) mkt_user = UserProfile.objects.filter(email=form.data['email'], password='') if form.is_valid(): try: u = form.save(commit=False) u.set_password(form.cleaned_data['password']) u.generate_confirmationcode() u.save() u.create_django_user() log.info(u'Registered new account for user (%s)', u) log_cef('New Account', 5, request, username=u.username, signature='AUTHNOTICE', msg='User created a new account') u.email_confirmation_code() msg = _('Congratulations! Your user account was ' 'successfully created.') messages.success(request, msg) msg = _(u'An email has been sent to your address {0} to ' 'confirm your account. Before you can log in, you ' 'have to activate your account by clicking on the ' 'link provided in this email.').format(u.email) messages.info(request, _('Confirmation Email Sent'), msg) except IntegrityError, e: # I was unable to reproduce this, but I suspect it happens # when they POST twice quickly and the slaves don't have the # new info yet (total guess). Anyway, I'm assuming the # first one worked properly, so this is still a success # case to the end user so we just log it... log.error('Failed to register new user (%s): %s' % (u, e)) return http.HttpResponseRedirect(reverse('users.login')) elif mkt_user.exists(): # Handle BrowserID if (mkt_user.count() == 1 and mkt_user[0].source in amo.LOGIN_SOURCE_BROWSERIDS): messages.info(request, _('You already have an account.')) form = None else: f = PasswordResetForm() f.users_cache = [mkt_user[0]] f.save(use_https=request.is_secure(), email_template_name='users/email/pwreset.ltxt', request=request) return jingo.render(request, 'users/newpw_sent.html', {}) else: messages.error(request, _('There are errors in this form'), _('Please correct them and resubmit.'))
def edit_privacy(request, collection, username, slug): collection.listed = not collection.listed collection.save() log.info(u'%s changed privacy on collection %s' % (request.amo_user, collection.id)) return http.HttpResponseRedirect(collection.get_url_path())
def edit(request): # Don't use request.amo_user since it has too much caching. amouser = UserProfile.objects.get(pk=request.user.id) if request.method == 'POST': # ModelForm alters the instance you pass in. We need to keep a copy # around in case we need to use it below (to email the user) original_email = amouser.email form = forms.UserEditForm(request.POST, request.FILES, request=request, instance=amouser) if form.is_valid(): messages.success(request, _('Profile Updated')) if amouser.email != original_email: # Temporarily block email changes. if settings.APP_PREVIEW: messages.error( request, 'Error', 'You cannot change your email on the ' 'developer preview site.') return jingo.render(request, 'users/edit.html', { 'form': form, 'amouser': amouser }) l = { 'user': amouser, 'mail1': original_email, 'mail2': amouser.email } log.info(u"User (%(user)s) has requested email change from " "(%(mail1)s) to (%(mail2)s)" % l) messages.info( request, _('Email Confirmation Sent'), _(u'An email has been sent to {0} to confirm your new ' 'email address. For the change to take effect, you ' 'need to click on the link provided in this email. ' 'Until then, you can keep logging in with your ' 'current email address.').format(amouser.email)) token, hash_ = EmailResetCode.create(amouser.id, amouser.email) url = '%s%s' % (settings.SITE_URL, reverse('users.emailchange', args=[amouser.id, token, hash_])) t = loader.get_template('users/email/emailchange.ltxt') c = {'domain': settings.DOMAIN, 'url': url} send_mail(_('Please confirm your email address ' 'change at %s' % settings.DOMAIN), t.render(Context(c)), None, [amouser.email], use_blacklist=False, real_email=True) # Reset the original email back. We aren't changing their # address until they confirm the new one amouser.email = original_email form.save() return redirect('users.edit') else: messages.error( request, _('Errors Found'), _('There were errors in the changes ' 'you made. Please correct them and ' 'resubmit.')) else: form = forms.UserEditForm(instance=amouser) return jingo.render(request, 'users/edit.html', { 'form': form, 'amouser': amouser, 'webapp': False })
def unrestrict(self): log.info(u'User (%s: <%s>) is being unrestricted.' % (self, self.email)) GroupUser.objects.filter(user=self, group__rules='Restricted:UGC').delete()
def destroy(self, request, *args, **kwargs): obj = self.get_object() obj.delete() log.info('Price %s deleted.' % (obj, )) return Response(status=204)
def _login(request, template=None, data=None, dont_redirect=False): data = data or {} data['webapp'] = settings.APP_PREVIEW # In case we need it later. See below. get_copy = request.GET.copy() if 'to' in request.GET: request = _clean_next_url(request) if request.user.is_authenticated(): return http.HttpResponseRedirect( request.GET.get('to', settings.LOGIN_REDIRECT_URL)) limited = getattr(request, 'limited', 'recaptcha_shown' in request.POST) user = None login_status = None if 'username' in request.POST: try: # We are doing all this before we try and validate the form. user = UserProfile.objects.get(email=request.POST['username']) limited = ( (user.failed_login_attempts >= settings.LOGIN_RATELIMIT_USER) or limited) login_status = False except UserProfile.DoesNotExist: log_cef('Authentication Failure', 5, request, username=request.POST['username'], signature='AUTHFAIL', msg='The username was invalid') pass partial_form = partial(forms.AuthenticationForm, use_recaptcha=limited) r = auth.views.login(request, template_name=template, redirect_field_name='to', authentication_form=partial_form, extra_context=data) if isinstance(r, http.HttpResponseRedirect): # Django's auth.views.login has security checks to prevent someone from # redirecting to another domain. Since we want to allow this in # certain cases, we have to make a new response object here to replace # the above. if 'domain' in request.GET: request.GET = get_copy request = _clean_next_url(request) r = http.HttpResponseRedirect(request.GET['to']) # Succsesful log in according to django. Now we do our checks. I do # the checks here instead of the form's clean() because I want to use # the messages framework and it's not available in the request there. user = request.user.get_profile() if user.deleted: logout(request) log.warning(u'Attempt to log in with deleted account (%s)' % user) messages.error(request, _('Wrong email address or password!')) data.update({'form': partial_form()}) user.log_login_attempt(False) log_cef('Authentication Failure', 5, request, username=request.user, signature='AUTHFAIL', msg='Account is deactivated') return jingo.render(request, template, data) if user.confirmationcode: logout(request) log.info(u'Attempt to log in with unconfirmed account (%s)' % user) msg1 = _(u'A link to activate your user account was sent by email ' 'to your address {0}. You have to click it before you ' 'can log in.').format(user.email) url = "%s%s" % (settings.SITE_URL, reverse('users.confirm.resend', args=[user.id])) msg2 = _('If you did not receive the confirmation email, make ' 'sure your email service did not mark it as "junk ' 'mail" or "spam". If you need to, you can have us ' '<a href="%s">resend the confirmation message</a> ' 'to your email address mentioned above.') % url messages.error(request, _('Activation Email Sent'), msg1) messages.info(request, _('Having Trouble?'), msg2, title_safe=True, message_safe=True) data.update({'form': partial_form()}) user.log_login_attempt(False) return jingo.render(request, template, data) rememberme = request.POST.get('rememberme', None) if rememberme: request.session.set_expiry(settings.SESSION_COOKIE_AGE) log.debug((u'User (%s) logged in successfully with ' '"remember me" set') % user) login_status = True if dont_redirect: # We're recalling the middleware to re-initialize amo_user ACLMiddleware().process_request(request) r = jingo.render(request, template, data) if login_status is not None: user.log_login_attempt(login_status) log_cef('Authentication Failure', 5, request, username=request.POST['username'], signature='AUTHFAIL', msg='The password was incorrect') return r
def register(request): if settings.APP_PREVIEW: messages.error(request, loc('Registrations must be through browserid.')) form = None elif (settings.REGISTER_USER_LIMIT and UserProfile.objects.count() > settings.REGISTER_USER_LIMIT and not can_override_reg_limit(request)): _m = loc('Sorry, no more registrations are allowed. ' '<a href="https://developer.mozilla.org/en-US/apps">' 'Learn more</a>') messages.error(request, _m, title_safe=True, message_safe=True) form = None elif request.user.is_authenticated(): messages.info(request, _('You are already logged in to an account.')) form = None elif request.method == 'POST': form = forms.UserRegisterForm(request.POST) if form.is_valid(): try: u = form.save(commit=False) u.set_password(form.cleaned_data['password']) u.generate_confirmationcode() u.save() u.create_django_user() log.info(u'Registered new account for user (%s)', u) log_cef('New Account', 5, request, username=u.username, signature='AUTHNOTICE', msg='User created a new account') u.email_confirmation_code() if waffle.switch_is_active('zamboni-login'): # Hide these messages since prod still uses remora for # authentication, so django messages won't be displayed # until post-login. msg = _('Congratulations! Your user account was ' 'successfully created.') messages.success(request, msg) msg = _(u'An email has been sent to your address {0} to ' 'confirm your account. Before you can log in, you ' 'have to activate your account by clicking on the ' 'link provided in this email.').format(u.email) messages.info(request, _('Confirmation Email Sent'), msg) except IntegrityError, e: # I was unable to reproduce this, but I suspect it happens # when they POST twice quickly and the slaves don't have the # new info yet (total guess). Anyway, I'm assuming the # first one worked properly, so this is still a success # case to the end user so we just log it... log.error('Failed to register new user (%s): %s' % (u, e)) return http.HttpResponseRedirect(reverse('users.login')) else: messages.error(request, _('There are errors in this form'), _('Please correct them and resubmit.'))
def delete(self): log.info(u'Version deleted: %r (%s)' % (self, self.id)) amo.log(amo.LOG.DELETE_VERSION, self.addon, str(self.version)) super(Version, self).delete()
def is_authorized(self, request, object=None): if acl.action_allowed(request, self.app, self.action): log.info('Permission authorization failed') return True return False
def post_save(self, obj, created): log.info('Price %s %s.' % (obj, 'created' if created else 'updated'))
def update_collections_total(data, **kw): log.info("[%s@%s] Updating collections' download totals." % (len(data), update_collections_total.rate_limit)) for var in data: (Collection.objects.filter(pk=var['collection_id']).update( downloads=var['sum']))
def start_upgrade(minver, maxver): jetpacks = files.utils.find_jetpacks(minver, maxver) ids = [f.id for f in jetpacks if f.needs_upgrade] log.info('Starting a jetpack upgrade to %s [%s files].' % (maxver, len(ids))) files.tasks.start_upgrade.delay(ids)
def region_from_request(self, request): address = request.META.get('REMOTE_ADDR') ip_reg = self.geoip.lookup(address) log.info('Geodude lookup for {0} returned {1}'.format(address, ip_reg)) return mkt.regions.REGIONS_DICT.get(ip_reg, mkt.regions.RESTOFWORLD)
def obj_get(self, request=None, **kwargs): obj = self.get_and_check_ownership(request, allow_anon=True, **kwargs) log.info('App retreived: %s' % obj.pk) return obj
def jetpack_resend(request, file_id): maxver = JetpackUpgrader().version() log.info('Starting a jetpack upgrade to %s [1 file].' % maxver) start_upgrade_task.delay([file_id], sdk_version=maxver) return redirect('zadmin.jetpack')
def after_max_redo(msg): log.info('Completed run: %s' % msg) with establish_connection() as conn: update_to_json.apply_async(max_objs=max_objs, connection=conn)
def process_request_information(self): """Send a message to the authors.""" self.create_note(mkt.LOG.REQUEST_INFORMATION) self.version.update(has_info_request=True) log.info(u'Sending reviewer message for %s to authors' % self.addon)
def send_super_mail(self): log.info(u'Super review requested for %s' % (self.addon)) send_mail('editors/emails/super_review.ltxt', _lazy('Super review requested: %s') % (self.addon.name), [settings.SENIOR_EDITORS_EMAIL], Context(self.get_context_data()))
def post(self, request, pk, *args, **kwargs): log.info(u'Received IARC pingback for app:%s' % pk) if request.content_type != 'application/json': log.info(u'IARC pingback not of content-type "application/json"') return Response( {'detail': "Endpoint only accepts 'application/json'."}, status=http_status.HTTP_415_UNSUPPORTED_MEDIA_TYPE) app = self.get_object() data = request.DATA[0] if settings.DEBUG: log.debug(u'%s' % data) if app.iarc_token() != data.get('token'): # Verify token. log.info(u'Token mismatch in IARC pingback for app:%s' % app.id) return Response({'detail': 'Token mismatch'}, status=http_status.HTTP_400_BAD_REQUEST) if data.get('ratings'): # Double-check with IARC that it's the correct rating. if not self.verify_data(data): return Response('The ratings do not match the submission ID.', status=http_status.HTTP_400_BAD_REQUEST) log.info(u'Setting content ratings from IARC pingback for app:%s' % app.id) # We found a rating, so store the id and code for future use. if 'submission_id' in data and 'security_code' in data: app.set_iarc_info(data['submission_id'], data['security_code']) # Update status if incomplete status. # Do this before set_content_ratings to not prematurely trigger # a refresh. log.info('Checking app:%s completeness after IARC pingback.' % app.id) if (app.has_incomplete_status() and app.is_fully_complete(ignore_ratings=True)): log.info('Updating app status from IARC pingback for app:%s' % app.id) # Don't call update to prevent recursion in update_status. app.update(status=amo.STATUS_PENDING) log.info('Updated app status from IARC pingback for app:%s' % app.id) elif app.has_incomplete_status(): log.info('Reasons for app:%s incompleteness after IARC ' 'pingback: %s' % (app.id, app.completion_errors())) app.set_descriptors(data.get('descriptors', [])) app.set_interactives(data.get('interactives', [])) # Set content ratings last since it triggers a refresh on Content # Ratings page. We want descriptors and interactives visible by # the time it's refreshed. app.set_content_ratings(data.get('ratings', {})) return Response('ok')
def start_upgrade(minver, maxver): jetpacks = find_jetpacks(minver, maxver, from_builder_only=True) ids = [f.id for f in jetpacks if f.needs_upgrade] log.info('Starting a jetpack upgrade to %s [%s files].' % (maxver, len(ids))) start_upgrade_task.delay(ids, sdk_version=maxver)