def check_stats_permission(request, addon, for_contributions=False, no_raise=False): """ Check if user is allowed to view stats for ``addon``. no_raise -- if enabled function returns true or false else function raises PermissionDenied if user is not allowed. """ # If public, non-contributions: everybody can view. if addon.public_stats and not for_contributions: return True # Everything else requires an authenticated user. if not request.user.is_authenticated(): if no_raise: return False raise PermissionDenied if not for_contributions: # Only authors and Stats Viewers allowed. if (addon.has_author(request.amo_user) or acl.action_allowed(request, 'Stats', 'View')): return True else: # For contribution stats. # Only authors and Contribution Stats Viewers. if (addon.has_author(request.amo_user) or acl.action_allowed(request, 'RevenueStats', 'View')): return True if no_raise: return False raise PermissionDenied
def get_list(self, request=None, **kwargs): form = ApiSearchForm(request.GET if request else None) if not form.is_valid(): raise self.form_errors(form) # Pluck out status first since it forms part of the base query, but # only for privileged users. status = form.cleaned_data['status'] if status != amo.STATUS_PUBLIC and not ( acl.action_allowed(request, 'Apps', 'Review') or acl.action_allowed(request, 'Admin', '%')): return http.HttpUnauthorized( content=json.dumps( {'reason': 'Unauthorized to filter by status.'})) # Search specific processing of the results. region = getattr(request, 'REGION', mkt.regions.WORLDWIDE) qs = _get_query(region, gaia=request.GAIA, mobile=request.MOBILE, tablet=request.TABLET, status=status) qs = _filter_search(request, qs, form.cleaned_data, region=region) paginator = self._meta.paginator_class(request.GET, qs, resource_uri=self.get_resource_list_uri(), limit=self._meta.limit) page = paginator.page() # Rehydrate the results as per tastypie. objs = [self.build_bundle(obj=obj, request=request) for obj in page['objects']] page['objects'] = [self.full_dehydrate(bundle) for bundle in objs] # This isn't as quite a full as a full TastyPie meta object, # but at least it's namespaced that way and ready to expand. return self.create_response(request, page)
def wrapper(request, *args, **kw): from access import acl if (acl.action_allowed(request, '*', '*') or not acl.action_allowed(request, 'Restricted', 'UGC')): return f(request, *args, **kw) else: return http.HttpResponseForbidden()
def test_tools_developer_and_admin(self): # Make them a developer. user = self.login('admin') AddonUser.objects.create(user=user, addon=Addon.objects.all()[0]) r = self.client.get(self.url, follow=True) request = r.context['request'] eq_(request.amo_user.is_developer, True) eq_(acl.action_allowed(request, 'Editors', '%'), True) eq_(acl.action_allowed(request, 'Localizer', '%'), True) eq_(acl.action_allowed(request, 'Admin', '%'), True) expected = [ ('Tools', '#'), ('Manage My Add-ons', reverse('devhub.addons')), ('Submit a New Add-on', reverse('devhub.submit.1')), ('Manage My Apps', reverse('devhub.apps')), ('Submit a New App', reverse('devhub.submit_apps.1')), ('Submit a New Persona', reverse('devhub.personas.submit')), ('Developer Hub', reverse('devhub.index')), ('Editor Tools', reverse('editors.home')), ('Localizer Tools', '/localizers'), ('Admin Tools', reverse('zadmin.home')), ] check_links(expected, pq(r.content)('#aux-nav .tools a'))
def setup_viewer(request, file_obj): data = {'file': file_obj, 'version': file_obj.version, 'addon': file_obj.version.addon, 'status': False, 'selected': {}, 'validate_url': ''} if (acl.action_allowed(request, 'Editors', '%') or acl.check_addon_ownership(request, file_obj.version.addon, viewer=True, ignore_disabled=True)): data['validate_url'] = reverse('devhub.json_file_validation', args=[file_obj.version.addon.slug, file_obj.id]) if acl.action_allowed(request, 'Editors', '%'): data['file_link'] = {'text': _('Back to review'), 'url': reverse('editors.review', args=[data['addon'].slug])} else: data['file_link'] = {'text': _('Back to addon'), 'url': reverse('addons.detail', args=[data['addon'].pk])} return data
def wrapper(request, *args, **kw): from access import acl if (acl.action_allowed(request, '*', '*') or not acl.action_allowed(request, 'Restricted', 'UGC')): return f(request, *args, **kw) else: raise PermissionDenied
def global_settings(request): """ Storing standard AMO-wide information used in global headers, such as account links and settings. """ account_links = [] tools_links = [] context = {} tools_title = _('Tools') is_reviewer = False if request.user.is_authenticated(): amo_user = request.amo_user profile = request.user is_reviewer = acl.check_reviewer(request) account_links.append({'text': _('My Profile'), 'href': profile.get_url_path()}) if not settings.APP_PREVIEW: account_links.append({ 'text': _('My Collections'), 'href': reverse('collections.user', args=[amo_user.username])}) account_links.append({ 'text': _('Log out'), 'href': remora_url('/users/logout?to=' + urlquote(request.path)), }) if request.amo_user.is_developer: tools_links.append({'text': _('Manage My Submissions'), 'href': reverse('devhub.addons')}) tools_links += [ {'text': _('Submit a New Add-on'), 'href': reverse('devhub.submit.1')}, {'text': _('Developer Hub'), 'href': reverse('devhub.index')}, ] if is_reviewer: tools_links.append({'text': _('Editor Tools'), 'href': reverse('editors.home')}) if (acl.action_allowed(request, 'Admin', '%') or acl.action_allowed(request, 'AdminTools', 'View')): tools_links.append({'text': _('Admin Tools'), 'href': reverse('zadmin.home')}) context['amo_user'] = request.amo_user else: context['amo_user'] = AnonymousUser() context.update({'account_links': account_links, 'settings': settings, 'amo': amo, 'tools_links': tools_links, 'tools_title': tools_title, 'ADMIN_MESSAGE': get_config('site_notice'), 'collect_timings_percent': get_collect_timings(), 'is_reviewer': is_reviewer}) return context
def check_stats_permission(request, addon, for_contributions=False): """ Check if user is allowed to view stats for ``addon``. Raises PermissionDenied if user is not allowed. """ # If public, non-contributions: everybody can view. if addon.public_stats and not for_contributions: return # Everything else requires an authenticated user. if not request.user.is_authenticated(): raise PermissionDenied if not for_contributions: # Only authors and Stats Viewers allowed. if addon.has_author(request.amo_user) or acl.action_allowed(request, "Stats", "View"): return else: # For contribution stats. # Only authors and Contribution Stats Viewers. if addon.has_author(request.amo_user) or acl.action_allowed(request, "RevenueStats", "View"): return raise PermissionDenied
def profile(request, user_id): webapp = settings.APP_PREVIEW user = get_object_or_404(UserProfile, id=user_id) # Get user's own and favorite collections, if they allowed that. own_coll = fav_coll = [] if not webapp: if user.display_collections: own_coll = (Collection.objects.listed().filter(author=user).order_by("-created"))[:10] if user.display_collections_fav: fav_coll = (Collection.objects.listed().filter(following__user=user).order_by("-following__created"))[:10] edit_any_user = acl.action_allowed(request, "Admin", "EditAnyUser") own_profile = request.user.is_authenticated() and request.amo_user.id == user.id personas = [] if user.is_developer: if webapp: items = user.apps_listed else: items = user.addons_listed.exclude(type=amo.ADDON_PERSONA) personas = user.addons_listed.filter(type=amo.ADDON_PERSONA) addons = amo.utils.paginate(request, items.order_by("-weekly_downloads")) else: addons = [] def get_addons(reviews): if not reviews: return qs = Addon.objects.filter(id__in=set(r.addon_id for r in reviews)) addons = dict((addon.id, addon) for addon in qs) for review in reviews: review.addon = addons.get(review.addon_id) reviews = user.reviews.transform(get_addons) data = { "profile": user, "own_coll": own_coll, "reviews": reviews, "fav_coll": fav_coll, "edit_any_user": edit_any_user, "addons": addons, "own_profile": own_profile, "webapp": webapp, "personas": personas, } if not own_profile: data["abuse_form"] = AbuseForm(request=request) data["review_perms"] = {} # See reviews.views.review_list for more if ( own_profile or acl.action_allowed(request, "Reviews", "Edit") or acl.action_allowed(request, "Admin", "EditAnyUser") ): data["review_perms"] = dict(can_delete=True) return jingo.render(request, "users/profile.html", data)
def app_header(context, app, page_type=''): t = env.get_template('lookup/helpers/app_header.html') is_admin = acl.action_allowed(context['request'], 'Users', 'Edit') is_staff = acl.action_allowed(context['request'], 'Apps', 'Configure') is_reviewer = acl.check_reviewer(context['request']) return jinja2.Markup(t.render(app=app, page_type=page_type, is_admin=is_admin, is_staff=is_staff, is_reviewer=is_reviewer))
def motd(request): form = None if acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit'): form = forms.MOTDForm( initial={'motd': get_config('editors_review_motd')}) motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit') data = context(request, form=form, motd_editable=motd_editable) return render(request, 'editors/motd.html', data)
def global_settings(request): """Store global Marketplace-wide info. used in the header.""" account_links = [] tools_links = [] context = {} tools_title = _('Tools') if request.user.is_authenticated() and hasattr(request, 'amo_user'): amo_user = request.amo_user account_links = [] context['is_reviewer'] = acl.check_reviewer(request) if getattr(request, 'can_view_consumer', True): account_links = [ {'text': _('Account History'), 'href': reverse('account.purchases')}, {'text': _('Account Settings'), 'href': reverse('account.settings')}, ] account_links += [ {'text': _('Change Password'), 'href': 'https://browserid.org/signin'}, {'text': _('Log out'), 'href': reverse('users.logout')}, ] if '/developers/' not in request.path: if amo_user.is_app_developer: tools_links.append({'text': _('My Submissions'), 'href': reverse('mkt.developers.apps')}) else: tools_links.append({'text': _('Developers'), 'href': reverse('ecosystem.landing')}) if '/reviewers/' not in request.path and context['is_reviewer']: tools_links.append({'text': _('Reviewer Tools'), 'href': reverse('reviewers.home')}) if acl.action_allowed(request, 'Localizers', '%'): tools_links.append({'text': _('Localizer Tools'), 'href': '/localizers'}) if acl.action_allowed(request, 'AccountLookup', '%'): tools_links.append({'text': _('Lookup Tool'), 'href': reverse('lookup.home')}) if acl.action_allowed(request, 'Admin', '%'): tools_links.append({'text': _('Admin Tools'), 'href': reverse('zadmin.home')}) context['amo_user'] = amo_user else: context['amo_user'] = AnonymousUser() context.update(account_links=account_links, settings=settings, amo=amo, mkt=mkt, APP=amo.FIREFOX, tools_links=tools_links, tools_title=tools_title, ADMIN_MESSAGE=get_config('site_notice'), collect_timings_percent=get_collect_timings(), is_admin=acl.action_allowed(request, 'Addons', 'Edit')) return context
def global_settings(request): """ Storing standard AMO-wide information used in global headers, such as account links and settings. """ account_links = [] tools_links = [] context = {} if request.user.is_authenticated(): # TODO(jbalogh): reverse links amo_user = request.amo_user account_links.append({ 'text': _('View Profile'), 'href': request.user.get_profile().get_url_path(), }) account_links.append({'text': _('Edit Profile'), 'href': reverse('users.edit')}) if request.amo_user.is_developer: account_links.append({'text': _('My Add-ons'), 'href': '/developers/addons'}) account_links.append({ 'text': _('My Collections'), 'href': reverse('collections.user', args=[amo_user.username])}) if amo_user.favorite_addons: account_links.append( {'text': _('My Favorites'), 'href': reverse('collections.detail', args=[amo_user.username, 'favorites'])}) account_links.append({ 'text': _('Log out'), 'href': remora_url('/users/logout?to=' + urlquote(request.path)), }) tools_links.append({'text': _('Developer Hub'), 'href': '/developers'}) if acl.action_allowed(request, 'Editors', '%'): tools_links.append({'text': _('Editor Tools'), 'href': '/editors'}) if acl.action_allowed(request, 'Localizers', '%'): tools_links.append({'text': _('Localizer Tools'), 'href': '/localizers'}) if acl.action_allowed(request, 'Admin', '%'): tools_links.append({'text': _('Admin Tools'), 'href': reverse('zadmin.home')}) context['amo_user'] = request.amo_user else: context['amo_user'] = AnonymousUser() context.update({'account_links': account_links, 'settings': settings, 'amo': amo, 'tools_links': tools_links, 'ADMIN_MESSAGE': get_config('site_notice')}) return context
def queue_tabnav_themes(context): """Similar to queue_tabnav, but for themes.""" tabs = [] if acl.action_allowed(context["request"], "Personas", "Review"): tabs.append(("editors.themes.list", "pending_themes", _("Pending"))) if acl.action_allowed(context["request"], "SeniorPersonasTools", "View"): tabs.append(("editors.themes.list_flagged", "flagged_themes", _("Flagged"))) tabs.append(("editors.themes.list_rereview", "rereview_themes", _("Updates"))) return tabs
def wrapper(request, *args, **kw): admin = (action_allowed(request, 'Admin', '%') or action_allowed(request, 'AdminTools', 'View')) if reviewers == True: admin = (admin or action_allowed(request, 'ReviewerAdminTools', 'View')) if admin: return f(request, *args, **kw) raise PermissionDenied
def queue_tabnav_themes_interactive(context): """Tabnav for the interactive shiny theme queues.""" tabs = [] if acl.action_allowed(context["request"], "Personas", "Review"): tabs.append(("editors.themes.queue_themes", "pending", _("Pending"))) if acl.action_allowed(context["request"], "SeniorPersonasTools", "View"): tabs.append(("editors.themes.queue_flagged", "flagged", _("Flagged"))) tabs.append(("editors.themes.queue_rereview", "rereview", _("Updates"))) return tabs
def wrapper(request, *args, **kw): admin = (action_allowed(request, 'Admin', '%') or action_allowed(request, 'AdminTools', 'View')) if reviewers == True: admin = (admin or action_allowed(request, 'ReviewerAdminTools', 'View')) if admin: return f(request, *args, **kw) return http.HttpResponseForbidden()
def wrapper(request, *args, **kw): admin = action_allowed(request, "Admin", "%") or action_allowed(request, "AdminTools", "View") if reviewers == True: admin = admin or action_allowed(request, "ReviewerAdminTools", "View") if theme_reviewers == True: admin = admin or action_allowed(request, "SeniorPersonasTools", "View") if admin: return f(request, *args, **kw) raise PermissionDenied
def _permission_to_edit_locale(request, locale=''): """If locale is empty, it checks global permissions.""" if acl.action_allowed(request, 'Admin', 'EditAnyLocale'): return True if locale and acl.action_allowed(request, 'Localizers', locale): return True return False
def _permission_to_edit_locale(request, locale=''): """If locale is empty, it checks global permissions.""" if acl.action_allowed(request, 'Locales', 'Edit'): return True if locale and acl.action_allowed(request, 'Locale.%s' % locale, 'Edit'): return True return False
def home(request): if (not acl.action_allowed(request, 'Addons', 'Review') and acl.action_allowed(request, 'Personas', 'Review')): return http.HttpResponseRedirect(reverse('editors.themes.home')) motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit') durations = (('new', _('New Add-ons (Under 5 days)')), ('med', _('Passable (5 to 10 days)')), ('old', _('Overdue (Over 10 days)'))) progress, percentage = _editor_progress() unlisted_progress, unlisted_percentage = _editor_progress(unlisted=True) reviews_max_display = getattr(settings, 'EDITOR_REVIEWS_MAX_DISPLAY', 5) reviews_total = ActivityLog.objects.total_reviews()[:reviews_max_display] reviews_monthly = ( ActivityLog.objects.monthly_reviews()[:reviews_max_display]) reviews_total_count = ActivityLog.objects.user_approve_reviews( request.user).count() reviews_monthly_count = ( ActivityLog.objects.current_month_user_approve_reviews( request.user).count()) # Try to read user position from retrieved reviews. # If not available, query for it. reviews_total_position = ( ActivityLog.objects.user_position(reviews_total, request.user) or ActivityLog.objects.total_reviews_user_position(request.user)) reviews_monthly_position = ( ActivityLog.objects.user_position(reviews_monthly, request.user) or ActivityLog.objects.monthly_reviews_user_position(request.user)) data = context( request, reviews_total=reviews_total, reviews_monthly=reviews_monthly, reviews_total_count=reviews_total_count, reviews_monthly_count=reviews_monthly_count, reviews_total_position=reviews_total_position, reviews_monthly_position=reviews_monthly_position, new_editors=EventLog.new_editors(), eventlog=ActivityLog.objects.editor_events()[:6], progress=progress, unlisted_progress=unlisted_progress, percentage=percentage, unlisted_percentage=unlisted_percentage, durations=durations, reviews_max_display=reviews_max_display, motd_editable=motd_editable, queue_counts_total=queue_counts(admin_reviewer=True), unlisted_queue_counts_total=queue_counts(admin_reviewer=True, unlisted=True), ) return render(request, 'editors/home.html', data)
def check_acls(self, request, obj, acl_type): if acl_type == 'moz_contact': return request.user.email == obj.addon.mozilla_contact elif acl_type == 'admin': return acl.action_allowed(request, 'Admin', '%') elif acl_type == 'reviewer': return acl.action_allowed(request, 'Apps', 'Review') elif acl_type == 'senior_reviewer': return acl.action_allowed(request, 'Apps', 'ReviewEscalated') else: raise 'Invalid ACL lookup.'
def performance(request, user_id=False): user = request.amo_user editors = _recent_editors() is_admin = (acl.action_allowed(request, 'Admin', '%') or acl.action_allowed(request, 'ReviewerAdminTools', 'View')) if is_admin and user_id: try: user = UserProfile.objects.get(pk=user_id) except UserProfile.DoesNotExist: pass # Use request.amo_user from above. monthly_data = _performance_by_month(user.id) performance_total = _performance_total(monthly_data) # Incentive point breakdown. today = date.today() month_ago = today - timedelta(days=30) year_ago = today - timedelta(days=365) point_total = ReviewerScore.get_total(user) totals = ReviewerScore.get_breakdown(user) months = ReviewerScore.get_breakdown_since(user, month_ago) years = ReviewerScore.get_breakdown_since(user, year_ago) def _sum(iter, types): return sum(s.total for s in iter if s.atype in types) breakdown = { 'month': { 'addons': _sum(months, amo.GROUP_TYPE_ADDON), 'apps': _sum(months, amo.GROUP_TYPE_WEBAPP), 'themes': _sum(months, amo.GROUP_TYPE_THEME), }, 'year': { 'addons': _sum(years, amo.GROUP_TYPE_ADDON), 'apps': _sum(years, amo.GROUP_TYPE_WEBAPP), 'themes': _sum(years, amo.GROUP_TYPE_THEME), }, 'total': { 'addons': _sum(totals, amo.GROUP_TYPE_ADDON), 'apps': _sum(totals, amo.GROUP_TYPE_WEBAPP), 'themes': _sum(totals, amo.GROUP_TYPE_THEME), } } data = context(monthly_data=json.dumps(monthly_data), performance_month=performance_total['month'], performance_year=performance_total['year'], breakdown=breakdown, point_total=point_total, editors=editors, current_user=user, is_admin=is_admin, is_user=(request.amo_user.id == user.id)) return render(request, 'editors/performance.html', data)
def queue_tabnav_themes(context): """Similar to queue_tabnav, but for themes.""" tabs = [] if acl.action_allowed(context['request'], 'Personas', 'Review'): tabs.append(( 'themes', 'pending', 'queue_themes', _('Pending'), )) if acl.action_allowed(context['request'], 'ReviewerAdminTools', 'View'): tabs.append(( 'themes', 'flagged', 'queue_flagged', _('Flagged'), )) return tabs
def app_header(context, app, page_type=''): t = env.get_template('lookup/helpers/app_header.html') is_author = acl.check_ownership(context['request'], app) is_operator = any(g.name == 'Operators' for g in context['request'].groups) is_admin = acl.action_allowed(context['request'], 'Users', 'Edit') is_staff = acl.action_allowed(context['request'], 'Apps', 'Configure') is_reviewer = acl.check_reviewer(context['request']) return jinja2.Markup(t.render(app=app, page_type=page_type, is_admin=is_admin, is_staff=is_staff, is_reviewer=is_reviewer, is_author=is_author, is_operator=is_operator))
def home(request): if not acl.action_allowed(request, "Addons", "Review") and acl.action_allowed(request, "Personas", "Review"): return http.HttpResponseRedirect(reverse("editors.themes.home")) motd_editable = acl.action_allowed(request, "AddonReviewerMOTD", "Edit") durations = ( ("new", _("New Add-ons (Under 5 days)")), ("med", _("Passable (5 to 10 days)")), ("old", _("Overdue (Over 10 days)")), ) progress, percentage = _editor_progress() unlisted_progress, unlisted_percentage = _editor_progress(unlisted=True) reviews_max_display = getattr(settings, "EDITOR_REVIEWS_MAX_DISPLAY", 5) reviews_total = ActivityLog.objects.total_reviews()[:reviews_max_display] reviews_monthly = ActivityLog.objects.monthly_reviews()[:reviews_max_display] reviews_total_count = ActivityLog.objects.user_approve_reviews(request.user).count() reviews_monthly_count = ActivityLog.objects.current_month_user_approve_reviews(request.user).count() # Try to read user position from retrieved reviews. # If not available, query for it. reviews_total_position = ActivityLog.objects.user_position( reviews_total, request.user ) or ActivityLog.objects.total_reviews_user_position(request.user) reviews_monthly_position = ActivityLog.objects.user_position( reviews_monthly, request.user ) or ActivityLog.objects.monthly_reviews_user_position(request.user) data = context( request, reviews_total=reviews_total, reviews_monthly=reviews_monthly, reviews_total_count=reviews_total_count, reviews_monthly_count=reviews_monthly_count, reviews_total_position=reviews_total_position, reviews_monthly_position=reviews_monthly_position, new_editors=EventLog.new_editors(), eventlog=ActivityLog.objects.editor_events()[:6], progress=progress, unlisted_progress=unlisted_progress, percentage=percentage, unlisted_percentage=unlisted_percentage, durations=durations, reviews_max_display=reviews_max_display, motd_editable=motd_editable, queue_counts_total=queue_counts(admin_reviewer=True), unlisted_queue_counts_total=queue_counts(admin_reviewer=True, unlisted=True), ) return render(request, "editors/home.html", data)
def queue_tabnav(context): """ Returns tuple of tab navigation for the queue pages. Each tuple contains three elements: (named_url. tab_code, tab_text) """ request = context['request'] counts = context['queue_counts'] apps_reviewing = AppsReviewing(request).get_apps() # Apps. if acl.action_allowed(request, 'Apps', 'Review'): rv = [ ('reviewers.apps.queue_pending', 'pending', _('Apps ({0})', counts['pending']).format(counts['pending'])), ('reviewers.apps.queue_rereview', 'rereview', _('Re-reviews ({0})', counts['rereview']).format( counts['rereview'])), ('reviewers.apps.queue_updates', 'updates', _('Updates ({0})', counts['updates']).format(counts['updates'])), ] if acl.action_allowed(request, 'Apps', 'ReviewEscalated'): rv.append(('reviewers.apps.queue_escalated', 'escalated', _('Escalations ({0})', counts['escalated']).format( counts['escalated']))) rv.extend([ ('reviewers.apps.queue_moderated', 'moderated', _('Moderated Reviews ({0})', counts['moderated']) .format(counts['moderated'])), ('reviewers.apps.apps_reviewing', 'reviewing', _('Reviewing ({0})').format(len(apps_reviewing))), ]) else: rv = [] # Themes. if (acl.action_allowed(request, 'Personas', 'Review') and waffle.switch_is_active('mkt-themes')): rv.append(('reviewers.themes.list', 'themes', _('Themes ({0})').format(counts['themes']),)) if waffle.switch_is_active('buchets') and 'pro' in request.GET: device_srch = device_queue_search(request) rv.append(('reviewers.apps.queue_device', 'device', _('Device ({0})').format(device_srch.count()),)) return rv
def queue_tabnav(context): """ Returns tuple of tab navigation for the queue pages. Each tuple contains three elements: (url, tab_code, tab_text) """ request = context['request'] counts = context['queue_counts'] apps_reviewing = AppsReviewing(request).get_apps() # Apps. if acl.action_allowed(request, 'Apps', 'Review'): rv = [ (reverse('reviewers.apps.queue_pending'), 'pending', _('Apps ({0})', counts['pending']).format(counts['pending'])), (reverse('reviewers.apps.queue_rereview'), 'rereview', _('Re-reviews ({0})', counts['rereview']).format( counts['rereview'])), (reverse('reviewers.apps.queue_updates'), 'updates', _('Updates ({0})', counts['updates']).format(counts['updates'])), ] if acl.action_allowed(request, 'Apps', 'ReviewEscalated'): rv.append((reverse('reviewers.apps.queue_escalated'), 'escalated', _('Escalations ({0})', counts['escalated']).format( counts['escalated']))) rv.extend([ (reverse('reviewers.apps.queue_moderated'), 'moderated', _('Moderated Reviews ({0})', counts['moderated']) .format(counts['moderated'])), (reverse('reviewers.apps.apps_reviewing'), 'reviewing', _('Reviewing ({0})').format(len(apps_reviewing))), ]) if acl.action_allowed(request, 'Apps', 'ReviewRegionCN'): url_ = reverse('reviewers.apps.queue_region', args=[mkt.regions.CN.slug]) rv.append((url_, 'region', _('China ({0})').format(counts['region_cn']))) else: rv = [] if waffle.switch_is_active('buchets') and 'pro' in request.GET: device_srch = device_queue_search(request) rv.append((reverse('reviewers.apps.queue_device'), 'device', _('Device ({0})').format(device_srch.count()),)) return rv
def queue_tabnav(context): """ Returns tuple of tab navigation for the queue pages. Each tuple contains three elements: (named_url. tab_code, tab_text) """ counts = context["queue_counts"] apps_reviewing = AppsReviewing(context["request"]).get_apps() # Apps. if acl.action_allowed(context["request"], "Apps", "Review"): rv = [ ("reviewers.apps.queue_pending", "pending", _("Apps ({0})", counts["pending"]).format(counts["pending"])), ( "reviewers.apps.queue_rereview", "rereview", _("Re-reviews ({0})", counts["rereview"]).format(counts["rereview"]), ), ( "reviewers.apps.queue_updates", "updates", _("Updates ({0})", counts["updates"]).format(counts["updates"]), ), ] if acl.action_allowed(context["request"], "Apps", "ReviewEscalated"): rv.append( ( "reviewers.apps.queue_escalated", "escalated", _("Escalations ({0})", counts["escalated"]).format(counts["escalated"]), ) ) rv.extend( [ ( "reviewers.apps.queue_moderated", "moderated", _("Moderated Reviews ({0})", counts["moderated"]).format(counts["moderated"]), ), ("reviewers.apps.apps_reviewing", "reviewing", _("Reviewing ({0})").format(len(apps_reviewing))), ] ) else: rv = [] # Themes. if acl.action_allowed(context["request"], "Personas", "Review") and waffle.switch_is_active("mkt-themes"): rv.append(("reviewers.themes.list", "themes", _("Themes ({0})").format(counts["themes"]))) return rv
def queue_tabnav_themes_interactive(context): """Tabnav for the interactive shiny theme queues.""" tabs = [] if acl.action_allowed(context['request'], 'Personas', 'Review'): tabs.append(( 'editors.themes.queue_themes', 'pending', _('Pending'), )) if acl.action_allowed(context['request'], 'SeniorPersonasTools', 'View'): tabs.append(( 'editors.themes.queue_flagged', 'flagged', _('Flagged'), )) tabs.append(( 'editors.themes.queue_rereview', 'rereview', _('Updates'), )) return tabs
def _review(request, addon): version = addon.latest_version if (not settings.DEBUG and addon.authors.filter(user=request.user).exists()): amo.messages.warning(request, _('Self-reviews are not allowed.')) return redirect(reverse('editors.queue')) form = forms.get_review_form(request.POST or None, request=request, addon=addon, version=version) queue_type = (form.helper.review_type if form.helper.review_type != 'preliminary' else 'prelim') redirect_url = reverse('editors.queue_%s' % queue_type) num = request.GET.get('num') paging = {} if num: try: num = int(num) except (ValueError, TypeError): raise http.Http404 total = queue_counts(queue_type) paging = { 'current': num, 'total': total, 'prev': num > 1, 'next': num < total, 'prev_url': '%s?num=%s' % (redirect_url, num - 1), 'next_url': '%s?num=%s' % (redirect_url, num + 1) } if request.method == 'POST' and form.is_valid(): form.helper.process() if form.cleaned_data.get('notify'): EditorSubscription.objects.get_or_create(user=request.amo_user, addon=addon) amo.messages.success(request, _('Review successfully processed.')) return redirect(redirect_url) canned = CannedResponse.objects.all() is_admin = acl.action_allowed(request, 'Admin', 'EditAnyAddon') actions = form.helper.actions.items() statuses = [ amo.STATUS_PUBLIC, amo.STATUS_LITE, amo.STATUS_LITE_AND_NOMINATED ] try: show_diff = (addon.versions.exclude(id=version.id).filter( files__isnull=False, created__lt=version.created, files__status__in=statuses).latest()) except Version.DoesNotExist: show_diff = None # The actions we should show a minimal form from. actions_minimal = [k for (k, a) in actions if not a.get('minimal')] # We only allow the user to check/uncheck files for "pending" allow_unchecking_files = form.helper.review_type == "pending" versions = (Version.objects.filter(addon=addon).exclude( files__status=amo.STATUS_BETA).order_by('-created').transform( Version.transformer_activity).transform(Version.transformer)) pager = amo.utils.paginate(request, versions, 10) num_pages = pager.paginator.num_pages count = pager.paginator.count ctx = context(version=version, addon=addon, pager=pager, num_pages=num_pages, count=count, flags=Review.objects.filter(addon=addon, flag=True), form=form, paging=paging, canned=canned, is_admin=is_admin, status_types=amo.STATUS_CHOICES, show_diff=show_diff, allow_unchecking_files=allow_unchecking_files, actions=actions, actions_minimal=actions_minimal) return jingo.render(request, 'editors/review.html', ctx)
def has_curate_permission(self, request): return acl.action_allowed(request, 'Collections', 'Curate')
def addons_section(request, addon_id, addon, section, editable=False, webapp=False): basic = AppFormBasic if webapp else addon_forms.AddonFormBasic models = { 'basic': basic, 'media': AppFormMedia, 'details': AppFormDetails, 'support': AppFormSupport, 'technical': AppFormTechnical, 'admin': forms.AdminSettingsForm } is_dev = acl.check_addon_ownership(request, addon, dev=True) if section not in models: raise http.Http404() version = addon.current_version or addon.latest_version tags, previews, restricted_tags = [], [], [] cat_form = appfeatures = appfeatures_form = None # Permissions checks. # Only app owners can edit any of the details of their apps. # Users with 'Apps:Configure' can edit the admin settings. if (section != 'admin' and not is_dev) or ( section == 'admin' and not acl.action_allowed(request, 'Apps', 'Configure') and not acl.action_allowed(request, 'Apps', 'ViewConfiguration')): raise PermissionDenied if section == 'basic': cat_form = CategoryForm(request.POST or None, product=addon, request=request) elif section == 'media': previews = PreviewFormSet(request.POST or None, prefix='files', queryset=addon.get_previews()) elif section == 'technical': # Only show the list of features if app isn't packaged. if (waffle.switch_is_active('buchets') and not addon.is_packaged and section == 'technical'): appfeatures = version.features formdata = request.POST if request.method == 'POST' else None appfeatures_form = AppFeaturesForm(formdata, instance=appfeatures) elif section == 'admin': tags = addon.tags.not_blacklisted().values_list('tag_text', flat=True) restricted_tags = addon.tags.filter(restricted=True) # Get the slug before the form alters it to the form data. valid_slug = addon.app_slug if editable: if request.method == 'POST': if (section == 'admin' and not acl.action_allowed(request, 'Apps', 'Configure')): raise PermissionDenied form = models[section](request.POST, request.FILES, instance=addon, request=request) all_forms = [form, previews] if appfeatures_form: all_forms.append(appfeatures_form) if cat_form: all_forms.append(cat_form) if all(not f or f.is_valid() for f in all_forms): if cat_form: cat_form.save() addon = form.save(addon) if appfeatures_form: appfeatures_form.save() if 'manifest_url' in form.changed_data: addon.update( app_domain=addon.domain_from_url(addon.manifest_url)) update_manifests([addon.pk]) if previews: for preview in previews.forms: preview.save(addon) editable = False if section == 'media': amo.log(amo.LOG.CHANGE_ICON, addon) else: amo.log(amo.LOG.EDIT_PROPERTIES, addon) valid_slug = addon.app_slug else: form = models[section](instance=addon, request=request) else: form = False data = { 'addon': addon, 'webapp': webapp, 'version': version, 'form': form, 'editable': editable, 'tags': tags, 'restricted_tags': restricted_tags, 'cat_form': cat_form, 'preview_form': previews, 'valid_slug': valid_slug, } if appfeatures_form and appfeatures: data.update({ 'appfeatures': appfeatures, 'feature_list': [unicode(f) for f in appfeatures.to_list()], 'appfeatures_form': appfeatures_form }) return jingo.render(request, 'developers/apps/edit/%s.html' % section, data)
def get_actions(self): public = { 'method': self.handler.process_public, 'minimal': False, 'label': _lazy(u'Push to public'), 'details': _lazy(u'This will approve the sandboxed app so it ' u'appears on the public side.') } reject = { 'method': self.handler.process_sandbox, 'label': _lazy(u'Reject'), 'minimal': False, 'details': _lazy(u'This will reject the app and remove it from ' u'the review queue.') } info = { 'method': self.handler.request_information, 'label': _lazy(u'Request more information'), 'minimal': True, 'details': _lazy(u'This will send the author(s) an email ' u'requesting more information.') } escalate = { 'method': self.handler.process_escalate, 'label': _lazy(u'Escalate'), 'minimal': True, 'details': _lazy(u'Flag this app for an admin to review.') } comment = { 'method': self.handler.process_comment, 'label': _lazy(u'Comment'), 'minimal': True, 'details': _lazy(u'Make a comment on this app. The author won\'t ' u'be able to see this.') } clear_escalation = { 'method': self.handler.process_clear_escalation, 'label': _lazy(u'Clear Escalation'), 'minimal': True, 'details': _lazy(u'Clear this app from the escalation queue. The ' u'author will get no email or see comments ' u'here.') } clear_rereview = { 'method': self.handler.process_clear_rereview, 'label': _lazy(u'Clear Re-review'), 'minimal': True, 'details': _lazy(u'Clear this app from the re-review queue. The ' u'author will get no email or see comments ' u'here.') } disable = { 'method': self.handler.process_disable, 'label': _lazy(u'Disable app'), 'minimal': True, 'details': _lazy(u'Disable the app, removing it from public ' u'results. Sends comments to author.') } actions = SortedDict() if not self.version: # Return early if there is no version, this app is incomplete. actions['info'] = info actions['comment'] = comment return actions file_status = self.version.files.values_list('status', flat=True) multiple_versions = (File.objects.exclude(version=self.version).filter( version__addon=self.addon, status__in=amo.REVIEWED_STATUSES).exists()) # Public. if ((self.addon.is_packaged and amo.STATUS_PUBLIC not in file_status) or (not self.addon.is_packaged and self.addon.status != amo.STATUS_PUBLIC)): actions['public'] = public # Reject. if self.addon.is_packaged: # Packaged apps reject the file only, or the app itself if there's # only a single version. if (not multiple_versions and self.addon.status not in [amo.STATUS_REJECTED, amo.STATUS_DISABLED]): actions['reject'] = reject elif multiple_versions and amo.STATUS_DISABLED not in file_status: actions['reject'] = reject else: # Hosted apps reject the app itself. if self.addon.status not in [ amo.STATUS_REJECTED, amo.STATUS_DISABLED ]: actions['reject'] = reject # Disable. if (acl.action_allowed(self.handler.request, 'Addons', 'Edit') and (self.addon.status != amo.STATUS_DISABLED or amo.STATUS_DISABLED not in file_status)): actions['disable'] = disable # Clear escalation. if self.handler.in_escalate: actions['clear_escalation'] = clear_escalation # Clear re-review. if self.handler.in_rereview: actions['clear_rereview'] = clear_rereview # Escalate. if not self.handler.in_escalate: actions['escalate'] = escalate # Request info and comment are always shown. actions['info'] = info actions['comment'] = comment return actions
def global_settings(request): """ Storing standard AMO-wide information used in global headers, such as account links and settings. """ account_links = [] tools_links = [] context = {} tools_title = _('Tools') if request.user.is_authenticated() and hasattr(request, 'amo_user'): amo_user = request.amo_user account_links.append({ 'text': _('My Profile'), 'href': request.user.get_profile().get_url_path(), }) account_links.append({ 'text': _('Account Settings'), 'href': reverse('users.edit') }) if not settings.APP_PREVIEW: account_links.append({ 'text': _('My Collections'), 'href': reverse('collections.user', args=[amo_user.username]) }) if amo_user.favorite_addons: account_links.append({ 'text': _('My Favorites'), 'href': reverse('collections.detail', args=[amo_user.username, 'favorites']) }) if waffle.switch_is_active('marketplace'): account_links.append({ 'text': _('My Purchases'), 'href': reverse('users.purchases') }) if waffle.flag_is_active(request, 'allow-pre-auth'): account_links.append({ 'text': loc('Payment Profile'), 'href': reverse('users.payments') }) account_links.append({ 'text': _('Log out'), 'href': remora_url('/users/logout?to=' + urlquote(request.path)), }) if not settings.APP_PREVIEW: if request.amo_user.is_developer: tools_links.append({ 'text': _('Manage My Add-ons'), 'href': reverse('devhub.addons') }) tools_links.append({ 'text': _('Submit a New Add-on'), 'href': reverse('devhub.submit.1') }) if waffle.flag_is_active(request, 'accept-webapps'): if settings.APP_PREVIEW or request.amo_user.is_developer: tools_links.append({ 'text': _('Manage My Apps'), 'href': reverse('devhub.apps') }) tools_links.append({ 'text': _('Submit a New App'), 'href': reverse('devhub.submit_apps.1') }) if waffle.flag_is_active(request, 'submit-personas'): # TODO(cvan)(fligtar): Do we want this here? tools_links.append({ 'text': 'Submit a New Persona', 'href': reverse('devhub.personas.submit') }) if not settings.APP_PREVIEW: tools_links.append({ 'text': _('Developer Hub'), 'href': reverse('devhub.index') }) if acl.action_allowed(request, 'Editors', '%'): tools_links.append({ 'text': _('Editor Tools'), 'href': reverse('editors.home') }) if acl.action_allowed(request, 'Localizers', '%'): tools_links.append({ 'text': _('Localizer Tools'), 'href': '/localizers' }) if acl.action_allowed(request, 'Admin', '%'): tools_links.append({ 'text': _('Admin Tools'), 'href': reverse('zadmin.home') }) context['amo_user'] = request.amo_user else: context['amo_user'] = AnonymousUser() # The flag has to be enabled for everyone and then we'll use that # percentage in the pages. percent = 0 try: flag = waffle.models.Flag.objects.get(name='collect-timings') if flag.everyone and flag.percent: percent = float(flag.percent) / 100.0 except waffle.models.Flag.DoesNotExist: pass context.update({ 'account_links': account_links, 'settings': settings, 'amo': amo, 'tools_links': tools_links, 'tools_title': tools_title, 'ADMIN_MESSAGE': get_config('site_notice'), 'collect_timings_percent': percent }) return context
def performance(request, user_id=False): user = request.amo_user editors = _recent_editors() is_admin = (acl.action_allowed(request, 'Admin', '%') or acl.action_allowed(request, 'ReviewerAdminTools', 'View')) if is_admin and user_id: try: user = UserProfile.objects.get(pk=user_id) except UserProfile.DoesNotExist: pass # Use request.amo_user from above. motd_editable = acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit') monthly_data = _performance_by_month(user.id) performance_total = _performance_total(monthly_data) # Incentive point breakdown. today = date.today() month_ago = today - timedelta(days=30) year_ago = today - timedelta(days=365) point_total = ReviewerScore.get_total(user) totals = ReviewerScore.get_breakdown(user) months = ReviewerScore.get_breakdown_since(user, month_ago) years = ReviewerScore.get_breakdown_since(user, year_ago) def _sum(iter, types, exclude=False): """Sum the `total` property for items in `iter` that have an `atype` that is included in `types` when `exclude` is False (default) or not in `types` when `exclude` is True.""" return sum(s.total for s in iter if (s.atype in types) == (not exclude)) breakdown = { 'month': { 'addons': _sum(months, amo.GROUP_TYPE_ADDON), 'themes': _sum(months, amo.GROUP_TYPE_THEME), 'other': _sum(months, amo.GROUP_TYPE_ADDON + amo.GROUP_TYPE_THEME, exclude=True) }, 'year': { 'addons': _sum(years, amo.GROUP_TYPE_ADDON), 'themes': _sum(years, amo.GROUP_TYPE_THEME), 'other': _sum(years, amo.GROUP_TYPE_ADDON + amo.GROUP_TYPE_THEME, exclude=True) }, 'total': { 'addons': _sum(totals, amo.GROUP_TYPE_ADDON), 'themes': _sum(totals, amo.GROUP_TYPE_THEME), 'other': _sum(totals, amo.GROUP_TYPE_ADDON + amo.GROUP_TYPE_THEME, exclude=True) } } data = context(request, monthly_data=json.dumps(monthly_data), performance_month=performance_total['month'], performance_year=performance_total['year'], breakdown=breakdown, point_total=point_total, editors=editors, current_user=user, is_admin=is_admin, is_user=(request.amo_user.id == user.id), motd_editable=motd_editable) return render(request, 'editors/performance.html', data)
def get_tags(self, addon): if acl.action_allowed(self.request, 'Apps', 'Edit'): return list(addon.tags.values_list('tag_text', flat=True)) else: return list(addon.tags.filter(restricted=False) .values_list('tag_text', flat=True))
def is_authorized(self, request, object=None): if acl.action_allowed(request, self.app, self.action): return True log.info('Permission authorization failed') return False
def has_permission(self, request, view): return request.method in SAFE_METHODS and acl.action_allowed( request, 'Apps', 'Review')
def has_permission(self, request, view): return acl.action_allowed(request, self.app, self.action)
def can_view_stats(self, request): if request and request.amo_user: return (self.publishable_by(request.amo_user) or acl.action_allowed(request, 'CollectionStats', 'View')) return False
def wrapper(request, *args, **kw): if acl.action_allowed(request, 'Editors', '%'): return func(request, *args, **kw) else: return http.HttpResponseForbidden()
def get_tags(self, addon): if acl.action_allowed(self.request, 'Addons', 'Edit'): return [t.tag_text for t in addon.tags.all()] else: return [t.tag_text for t in addon.tags.filter(restricted=False)]
def review(request, addon): if not addon.is_listed and not acl.check_unlisted_addons_reviewer(request): raise http.Http404 version = addon.latest_version if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.amo_user): amo.messages.warning(request, _('Self-reviews are not allowed.')) return redirect(reverse('editors.queue')) form = forms.get_review_form(request.POST or None, request=request, addon=addon, version=version) queue_type = (form.helper.review_type if form.helper.review_type != 'preliminary' else 'prelim') if addon.is_listed: redirect_url = reverse('editors.queue_%s' % queue_type) else: redirect_url = reverse('editors.unlisted_queue_%s' % queue_type) is_admin = acl.action_allowed(request, 'Addons', 'Edit') if request.method == 'POST' and form.is_valid(): form.helper.process() if form.cleaned_data.get('notify'): EditorSubscription.objects.get_or_create(user=request.amo_user, addon=addon) if form.cleaned_data.get('adminflag') and is_admin: addon.update(admin_review=False) amo.messages.success(request, _('Review successfully processed.')) return redirect(redirect_url) # Kick off validation tasks for any files in this version which don't have # cached validation, since editors will almost certainly need to access # them. But only if we're not running in eager mode, since that could mean # blocking page load for several minutes. if not getattr(settings, 'CELERY_ALWAYS_EAGER', False): for file_ in version.files.all(): if not file_.has_been_validated: devhub.tasks.validate(file_) canned = AddonCannedResponse.objects.all() actions = form.helper.actions.items() statuses = [ amo.STATUS_PUBLIC, amo.STATUS_LITE, amo.STATUS_LITE_AND_NOMINATED ] try: show_diff = (addon.versions.exclude(id=version.id).filter( files__isnull=False, created__lt=version.created, files__status__in=statuses).latest()) except Version.DoesNotExist: show_diff = None # The actions we should show a minimal form from. actions_minimal = [k for (k, a) in actions if not a.get('minimal')] versions = (Version.objects.filter(addon=addon).exclude( files__status=amo.STATUS_BETA).order_by('-created').transform( Version.transformer_activity).transform(Version.transformer)) class PseudoVersion(object): def __init__(self): self.all_activity = [] all_files = () approvalnotes = None compatible_apps_ordered = () releasenotes = None status = 'Deleted', @property def created(self): return self.all_activity[0].created @property def version(self): return (self.all_activity[0].activity_log.details.get( 'version', '[deleted]')) # Grab review history for deleted versions of this add-on comments = (CommentLog.objects.filter( activity_log__action__in=amo.LOG_REVIEW_QUEUE, activity_log__versionlog=None, activity_log__addonlog__addon=addon).order_by( 'created').select_related('activity_log')) comment_versions = defaultdict(PseudoVersion) for c in comments: c.version = c.activity_log.details.get('version', c.created) comment_versions[c.version].all_activity.append(c) all_versions = comment_versions.values() all_versions.extend(versions) all_versions.sort(key=lambda v: v.created, reverse=True) pager = amo.utils.paginate(request, all_versions, 10) num_pages = pager.paginator.num_pages count = pager.paginator.count try: flags = ViewQueue.objects.get(id=addon.id).flags except ViewQueue.DoesNotExist: flags = [] user_changes_actions = [ amo.LOG.ADD_USER_WITH_ROLE.id, amo.LOG.CHANGE_USER_WITH_ROLE.id, amo.LOG.REMOVE_USER_WITH_ROLE.id ] user_changes_log = AddonLog.objects.filter( activity_log__action__in=user_changes_actions, addon=addon).order_by('id') ctx = context(request, version=version, addon=addon, pager=pager, num_pages=num_pages, count=count, flags=flags, form=form, canned=canned, is_admin=is_admin, show_diff=show_diff, actions=actions, actions_minimal=actions_minimal, whiteboard_form=forms.WhiteboardForm(instance=addon), user_changes=user_changes_log, unlisted=not addon.is_listed) return render(request, 'editors/review.html', ctx)
def global_settings(request): """ Storing standard AMO-wide information used in global headers, such as account links and settings. """ account_links = [] tools_links = [] context = {} tools_title = _('Tools') is_reviewer = False if request.user.is_authenticated(): amo_user = request.amo_user profile = request.user is_reviewer = (acl.check_addons_reviewer(request) or acl.check_personas_reviewer(request)) account_links.append({ 'text': _('My Profile'), 'href': profile.get_url_path() }) if amo_user.is_artist: account_links.append({ 'text': _('My Themes'), 'href': profile.get_user_url('themes') }) account_links.append({ 'text': _('Account Settings'), 'href': reverse('users.edit') }) account_links.append({ 'text': _('My Collections'), 'href': reverse('collections.user', args=[amo_user.username]) }) if amo_user.favorite_addons: account_links.append({ 'text': _('My Favorites'), 'href': reverse('collections.detail', args=[amo_user.username, 'favorites']) }) account_links.append({ 'text': _('Log out'), 'href': remora_url('/users/logout?to=' + urlquote(request.path)), }) if request.amo_user.is_developer: tools_links.append({ 'text': _('Manage My Submissions'), 'href': reverse('devhub.addons') }) links = [ { 'text': _('Submit a New Add-on'), 'href': reverse('devhub.submit.1') }, { 'text': _('Submit a New Theme'), 'href': reverse('devhub.themes.submit') }, { 'text': _('Developer Hub'), 'href': reverse('devhub.index') }, ] if waffle.switch_is_active('signing-api'): links.append({ 'text': _('Manage API Keys'), 'href': reverse('devhub.api_key') }) tools_links += links if is_reviewer: tools_links.append({ 'text': _('Editor Tools'), 'href': reverse('editors.home') }) if acl.action_allowed(request, 'L10nTools', 'View'): tools_links.append({ 'text': _('Localizer Tools'), 'href': '/localizers' }) if (acl.action_allowed(request, 'Admin', '%') or acl.action_allowed(request, 'AdminTools', 'View')): tools_links.append({ 'text': _('Admin Tools'), 'href': reverse('zadmin.home') }) context['amo_user'] = request.amo_user else: context['amo_user'] = AnonymousUser() context.update({ 'account_links': account_links, 'settings': settings, 'amo': amo, 'tools_links': tools_links, 'tools_title': tools_title, 'ADMIN_MESSAGE': get_config('site_notice'), 'collect_timings_percent': get_collect_timings(), 'is_reviewer': is_reviewer }) return context
def motd(request): form = None if acl.action_allowed(request, 'AddonReviewerMOTD', 'Edit'): form = forms.MOTDForm() data = context(form=form) return render(request, 'editors/motd.html', data)
def _review(request, addon, version): if (not settings.ALLOW_SELF_REVIEWS and not acl.action_allowed(request, 'Admin', '%') and addon.has_author(request.amo_user)): messages.warning(request, _('Self-reviews are not allowed.')) return redirect(reverse('reviewers.home')) if (addon.status == amo.STATUS_BLOCKED and not acl.action_allowed(request, 'Apps', 'ReviewEscalated')): messages.warning( request, _('Only senior reviewers can review blocklisted apps.')) return redirect(reverse('reviewers.home')) attachment_formset = forms.AttachmentFormSet(data=request.POST or None, files=request.FILES or None, prefix='attachment') form = forms.get_review_form(data=request.POST or None, files=request.FILES or None, request=request, addon=addon, version=version, attachment_formset=attachment_formset) postdata = request.POST if request.method == 'POST' else None all_forms = [form, attachment_formset] if waffle.switch_is_active('buchets') and version: features_list = [unicode(f) for f in version.features.to_list()] appfeatures_form = AppFeaturesForm(data=postdata, instance=version.features) all_forms.append(appfeatures_form) else: appfeatures_form = None features_list = None queue_type = form.helper.review_type redirect_url = reverse('reviewers.apps.queue_%s' % queue_type) is_admin = acl.action_allowed(request, 'Addons', 'Edit') if request.method == 'POST' and all(f.is_valid() for f in all_forms): old_types = set(o.id for o in addon.device_types) new_types = set(form.cleaned_data.get('device_override')) if waffle.switch_is_active('buchets'): old_features = set(features_list) new_features = set(unicode(f) for f in appfeatures_form.instance.to_list()) if form.cleaned_data.get('action') == 'public': if old_types != new_types: # The reviewer overrode the device types. We need to not # publish this app immediately. if addon.make_public == amo.PUBLIC_IMMEDIATELY: addon.update(make_public=amo.PUBLIC_WAIT) # And update the device types to what the reviewer set. AddonDeviceType.objects.filter(addon=addon).delete() for device in form.cleaned_data.get('device_override'): addon.addondevicetype_set.create(device_type=device) # Log that the reviewer changed the device types. added_devices = new_types - old_types removed_devices = old_types - new_types msg = _(u'Device(s) changed by ' 'reviewer: {0}').format(', '.join( [_(u'Added {0}').format(unicode(amo.DEVICE_TYPES[d].name)) for d in added_devices] + [_(u'Removed {0}').format( unicode(amo.DEVICE_TYPES[d].name)) for d in removed_devices])) amo.log(amo.LOG.REVIEW_DEVICE_OVERRIDE, addon, addon.current_version, details={'comments': msg}) if (waffle.switch_is_active('buchets') and old_features != new_features): # The reviewer overrode the requirements. We need to not # publish this app immediately. if addon.make_public == amo.PUBLIC_IMMEDIATELY: addon.update(make_public=amo.PUBLIC_WAIT) appfeatures_form.save(mark_for_rereview=False) # Log that the reviewer changed the minimum requirements. added_features = new_features - old_features removed_features = old_features - new_features fmt = ', '.join( [_(u'Added {0}').format(f) for f in added_features] + [_(u'Removed {0}').format(f) for f in removed_features]) # L10n: {0} is the list of requirements changes. msg = _(u'Requirements changed by reviewer: {0}').format(fmt) amo.log(amo.LOG.REVIEW_FEATURES_OVERRIDE, addon, addon.current_version, details={'comments': msg}) form.helper.process() if form.cleaned_data.get('notify'): EditorSubscription.objects.get_or_create(user=request.amo_user, addon=addon) messages.success(request, _('Review successfully processed.')) return redirect(redirect_url) canned = AppCannedResponse.objects.all() actions = form.helper.actions.items() try: if not version: raise Version.DoesNotExist show_diff = (addon.versions.exclude(id=version.id) .filter(files__isnull=False, created__lt=version.created, files__status=amo.STATUS_PUBLIC) .latest()) except Version.DoesNotExist: show_diff = None # The actions we should show a minimal form from. actions_minimal = [k for (k, a) in actions if not a.get('minimal')] # We only allow the user to check/uncheck files for "pending" allow_unchecking_files = form.helper.review_type == "pending" versions = (Version.with_deleted.filter(addon=addon) .order_by('-created') .transform(Version.transformer_activity) .transform(Version.transformer)) product_attrs = { 'product': json.dumps( product_as_dict(request, addon, False, 'reviewer'), cls=JSONEncoder), 'manifest_url': addon.manifest_url, } pager = paginate(request, versions, 10) num_pages = pager.paginator.num_pages count = pager.paginator.count ctx = context(request, version=version, product=addon, pager=pager, num_pages=num_pages, count=count, flags=Review.objects.filter(addon=addon, flag=True), form=form, canned=canned, is_admin=is_admin, status_types=amo.STATUS_CHOICES, show_diff=show_diff, allow_unchecking_files=allow_unchecking_files, actions=actions, actions_minimal=actions_minimal, tab=queue_type, product_attrs=product_attrs, attachment_formset=attachment_formset, appfeatures_form=appfeatures_form, default_visibility=DEFAULT_ACTION_VISIBILITY) if features_list is not None: ctx['feature_list'] = features_list return jingo.render(request, 'reviewers/review.html', ctx)
def _get_themes(request, reviewer, flagged=False, rereview=False): """Check out themes. :param flagged: Flagged themes (amo.STATUS_REVIEW_PENDING) :param rereview: Re-uploaded themes (RereviewQueueTheme) """ num = 0 themes = [] locks = [] status = (amo.STATUS_REVIEW_PENDING if flagged else amo.STATUS_PUBLIC if rereview else amo.STATUS_PENDING) if rereview: # Rereview themes. num, themes, locks = _get_rereview_themes(reviewer) else: # Pending and flagged themes. locks = ThemeLock.objects.no_cache().filter( reviewer=reviewer, theme__addon__status=status) num, themes = _calc_num_themes_checkout(locks) if themes: return themes themes = Persona.objects.no_cache().filter(addon__status=status, themelock=None) # Don't allow self-reviews. if (not settings.ALLOW_SELF_REVIEWS and not acl.action_allowed(request, 'Admin', '%')): if rereview: themes = themes.exclude(theme__addon__addonuser__user=reviewer) else: themes = themes.exclude(addon__addonuser__user=reviewer) # Check out themes by setting lock. themes = list(themes)[:num] expiry = get_updated_expiry() for theme in themes: if rereview: theme = theme.theme ThemeLock.objects.create(theme=theme, reviewer=reviewer, expiry=expiry) # Empty pool? Go look for some expired locks. if not themes: expired_locks = ThemeLock.objects.filter( expiry__lte=datetime.datetime.now(), theme__addon__status=status)[:rvw.THEME_INITIAL_LOCKS] # Steal expired locks. for lock in expired_locks: lock.reviewer = reviewer lock.expiry = expiry lock.save() if expired_locks: locks = expired_locks if rereview: return RereviewQueueTheme.objects.filter( theme__themelock__reviewer=reviewer) # New theme locks may have been created, grab all reviewer's themes again. return [lock.theme for lock in locks]
def wrapper(request, *args, **kw): from access import acl for app, action in pairs: if acl.action_allowed(request, app, action): return f(request, *args, **kw) return http.HttpResponseForbidden()
def themes_single(request, slug): """ Like a detail page, manually review a single theme if it is pending and isn't locked. """ reviewer = request.user.get_profile() reviewable = True # Don't review an already reviewed theme. theme = get_object_or_404(Persona, addon__slug=slug) if (theme.addon.status != amo.STATUS_PENDING and not theme.rereviewqueuetheme_set.all()): reviewable = False if (not settings.ALLOW_SELF_REVIEWS and not acl.action_allowed(request, 'Admin', '%') and theme.addon.has_author(request.amo_user)): reviewable = False else: # Don't review a locked theme (that's not locked to self). try: lock = theme.themelock if (lock.reviewer.id != reviewer.id and lock.expiry > datetime.datetime.now()): reviewable = False elif (lock.reviewer.id != reviewer.id and lock.expiry < datetime.datetime.now()): # Steal expired lock. lock.reviewer = reviewer lock.expiry = get_updated_expiry() lock.save() else: # Update expiry. lock.expiry = get_updated_expiry() lock.save() except ThemeLock.DoesNotExist: # Create lock if not created. ThemeLock.objects.create(theme=theme, reviewer=reviewer, expiry=get_updated_expiry()) ThemeReviewFormset = formset_factory(forms.ThemeReviewForm) formset = ThemeReviewFormset(initial=[{'theme': theme.id}]) # Since we started the review on the single page, we want to return to the # single page rather than get shot back to the queue. request.session['theme_redirect_url'] = reverse('editors.themes.single', args=[theme.addon.slug]) rereview = (theme.rereviewqueuetheme_set.all()[0] if theme.rereviewqueuetheme_set.exists() else None) return jingo.render( request, 'editors/themes/single.html', context( **{ 'formset': formset, 'theme': rereview if rereview else theme, 'theme_formsets': zip([rereview if rereview else theme], formset), 'theme_reviews': paginate( request, ActivityLog.objects.filter( action=amo.LOG.THEME_REVIEW.id, _arguments__contains=theme.addon.id)), 'actions': get_actions_json(), 'theme_count': 1, 'rereview': rereview, 'reviewable': reviewable, 'reject_reasons': rvw.THEME_REJECT_REASONS.items(), 'action_dict': rvw.REVIEW_ACTIONS, 'tab': ('flagged' if theme.addon.status == amo.STATUS_REVIEW_PENDING else 'rereview' if rereview else 'pending') }))
def is_admin_reviewer(request): return acl.action_allowed(request, 'ReviewerAdminTools', 'View')
def wrapper(request, *args, **kw): from access import acl for app, action in pairs: if acl.action_allowed(request, app, action): return f(request, *args, **kw) raise PermissionDenied
def global_settings(request): """ Storing standard AMO-wide information used in global headers, such as account links and settings. """ account_links = [] tools_links = [] context = {} if request.user.is_authenticated(): # TODO(jbalogh): reverse links amo_user = request.amo_user account_links.append({ 'text': _('View Profile'), 'href': request.user.get_profile().get_url_path(), }) account_links.append({ 'text': _('Edit Profile'), 'href': reverse('users.edit') }) if request.amo_user.is_developer: account_links.append({ 'text': _('My Add-ons'), 'href': '/developers/addons' }) account_links.append({ 'text': _('My Collections'), 'href': reverse('collections.user', args=[amo_user.username]) }) if amo_user.favorite_addons: account_links.append({ 'text': _('My Favorites'), 'href': reverse('collections.detail', args=[amo_user.username, 'favorites']) }) account_links.append({ 'text': _('Log out'), 'href': remora_url('/users/logout?to=' + urlquote(request.path)), }) tools_links.append({'text': _('Developer Hub'), 'href': '/developers'}) if acl.action_allowed(request, 'Editors', '%'): tools_links.append({'text': _('Editor Tools'), 'href': '/editors'}) if acl.action_allowed(request, 'Localizers', '%'): tools_links.append({ 'text': _('Localizer Tools'), 'href': '/localizers' }) if acl.action_allowed(request, 'Admin', '%'): tools_links.append({ 'text': _('Admin Tools'), 'href': reverse('zadmin.home') }) context['amo_user'] = request.amo_user else: context['amo_user'] = AnonymousUser() context.update({ 'account_links': account_links, 'settings': settings, 'amo': amo, 'tools_links': tools_links, 'ADMIN_MESSAGE': get_config('site_notice') }) return context
def has_permission(self, request, view): if (request.method == 'GET' or acl.action_allowed(request, 'Apps', 'Publisher')): return True log.info('Publisher authorization failed') return False
def check_contrib_stats_perms(context, addon): request = context['request'] if addon.has_author(request.amo_user) or acl.action_allowed( request, 'RevenueStats', 'View'): return True
def global_settings(request): """ Storing standard AMO-wide information used in global headers, such as account links and settings. """ account_links = [] tools_links = [] context = {} tools_title = _('Developer') if request.user.is_authenticated() and hasattr(request, 'amo_user'): amo_user = request.amo_user account_links.append({ 'text': _('View Profile'), 'href': request.user.get_profile().get_url_path(), }) account_links.append({'text': _('Edit Profile'), 'href': reverse('users.edit')}) account_links.append({ 'text': _('My Collections'), 'href': reverse('collections.user', args=[amo_user.username])}) if amo_user.favorite_addons: account_links.append( {'text': _('My Favorites'), 'href': reverse('collections.detail', args=[amo_user.username, 'favorites'])}) if waffle.switch_is_active('marketplace'): account_links.append({'text': _('My Purchases'), 'href': reverse('users.purchases')}) account_links.append({ 'text': _('Log out'), 'href': remora_url('/users/logout?to=' + urlquote(request.path)), }) if request.amo_user.is_developer: tools_links.append({'text': _('Manage My Add-ons'), 'href': reverse('devhub.addons')}) tools_links.append({'text': _('Submit a New Add-on'), 'href': reverse('devhub.submit.1')}) if waffle.flag_is_active(request, 'accept-webapps'): tools_links.append({'text': _('Submit a New Web App'), 'href': reverse('devhub.submit_apps.1')}) tools_links.append({'text': _('Developer Hub'), 'href': reverse('devhub.index')}) if acl.action_allowed(request, 'Editors', '%'): tools_title = _('Tools') tools_links.append({'text': _('Editor Tools'), 'href': reverse('editors.home')}) if acl.action_allowed(request, 'Localizers', '%'): tools_title = _('Tools') tools_links.append({'text': _('Localizer Tools'), 'href': '/localizers'}) if acl.action_allowed(request, 'Admin', '%'): tools_title = _('Tools') tools_links.append({'text': _('Admin Tools'), 'href': reverse('zadmin.home')}) context['amo_user'] = request.amo_user else: context['amo_user'] = AnonymousUser() context.update({'account_links': account_links, 'settings': settings, 'amo': amo, 'tools_links': tools_links, 'tools_title': tools_title, 'ADMIN_MESSAGE': get_config('site_notice')}) return context
def global_settings(request): """Store global Marketplace-wide info. used in the header.""" account_links = [] tools_links = [] footer_links = [] context = {} tools_title = _('Tools') if request.user.is_authenticated() and getattr(request, 'amo_user', None): amo_user = request.amo_user context['is_reviewer'] = acl.check_reviewer(request) account_links = [ # TODO: Coming soon with payments. # {'text': _('Account History'), # 'href': reverse('account.purchases')}, { 'text': _('Account Settings'), 'href': '/settings' }, { 'text': _('Change Password'), 'href': 'https://login.persona.org/signin' }, { 'text': _('Sign out'), 'href': reverse('users.logout') }, ] if '/developers/' not in request.path: tools_links.append({ 'text': _('Developer Hub'), 'href': reverse('ecosystem.landing') }) if amo_user.is_app_developer: tools_links.append({ 'text': _('My Submissions'), 'href': reverse('mkt.developers.apps') }) if '/reviewers/' not in request.path and context['is_reviewer']: footer_links.append({ 'text': _('Reviewer Tools'), 'href': reverse('reviewers.apps.queue_pending'), }) if acl.action_allowed(request, 'Localizers', '%'): footer_links.append({ 'text': _('Localizer Tools'), 'href': 'https://addons.mozilla.org/localizers/' }) if acl.action_allowed(request, 'AccountLookup', '%'): footer_links.append({ 'text': _('Lookup Tool'), 'href': reverse('lookup.home') }) if acl.action_allowed(request, 'Admin', '%'): footer_links.append({ 'text': _('Admin Tools'), 'href': reverse('zadmin.home') }) tools_links += footer_links context['amo_user'] = amo_user logged = True else: context['amo_user'] = AnonymousUser() logged = False DESKTOP = (getattr(request, 'TABLET', None) or not getattr(request, 'MOBILE', None)) context.update(account_links=account_links, settings=settings, amo=amo, mkt=mkt, APP=amo.FIREFOX, tools_links=tools_links, tools_title=tools_title, footer_links=footer_links, ADMIN_MESSAGE=get_config('site_notice'), collect_timings_percent=get_collect_timings(), is_admin=acl.action_allowed(request, 'Addons', 'Edit'), DESKTOP=DESKTOP, logged=logged) return context
def is_authorized(self, request, object=None): if request.method == 'GET' and acl.action_allowed(request, 'Apps', 'Review'): return True
def _review(request, addon): version = addon.latest_version if not settings.ALLOW_SELF_REVIEWS and addon.has_author(request.amo_user): amo.messages.warning(request, _('Self-reviews are not allowed.')) return redirect(reverse('editors.queue')) form = forms.get_review_form(request.POST or None, request=request, addon=addon, version=version) queue_type = (form.helper.review_type if form.helper.review_type != 'preliminary' else 'prelim') redirect_url = reverse('editors.queue_%s' % queue_type) num = request.GET.get('num') paging = {} if num: try: num = int(num) except (ValueError, TypeError): raise http.Http404 total = queue_counts(queue_type) paging = { 'current': num, 'total': total, 'prev': num > 1, 'next': num < total, 'prev_url': '%s?num=%s' % (redirect_url, num - 1), 'next_url': '%s?num=%s' % (redirect_url, num + 1) } is_admin = acl.action_allowed(request, 'Addons', 'Edit') if request.method == 'POST' and form.is_valid(): form.helper.process() if form.cleaned_data.get('notify'): EditorSubscription.objects.get_or_create(user=request.amo_user, addon=addon) if form.cleaned_data.get('adminflag') and is_admin: addon.update(admin_review=False) amo.messages.success(request, _('Review successfully processed.')) return redirect(redirect_url) canned = AddonCannedResponse.objects.all() actions = form.helper.actions.items() statuses = [ amo.STATUS_PUBLIC, amo.STATUS_LITE, amo.STATUS_LITE_AND_NOMINATED ] try: show_diff = (addon.versions.exclude(id=version.id).filter( files__isnull=False, created__lt=version.created, files__status__in=statuses).latest()) except Version.DoesNotExist: show_diff = None # The actions we should show a minimal form from. actions_minimal = [k for (k, a) in actions if not a.get('minimal')] # We only allow the user to check/uncheck files for "pending" allow_unchecking_files = form.helper.review_type == "pending" versions = (Version.objects.filter(addon=addon).exclude( files__status=amo.STATUS_BETA).order_by('-created').transform( Version.transformer_activity).transform(Version.transformer)) class PseudoVersion(object): def __init__(self): self.all_activity = [] all_files = () approvalnotes = None compatible_apps_ordered = () releasenotes = None status = 'Deleted', @property def created(self): return self.all_activity[0].created @property def version(self): return (self.all_activity[0].activity_log.details.get( 'version', '[deleted]')) # Grab review history for deleted versions of this add-on comments = (CommentLog.objects.filter( activity_log__action__in=amo.LOG_REVIEW_QUEUE, activity_log__versionlog=None, activity_log__addonlog__addon=addon).order_by( 'created').select_related('activity_log')) comment_versions = defaultdict(PseudoVersion) for c in comments: c.version = c.activity_log.details.get('version', c.created) comment_versions[c.version].all_activity.append(c) all_versions = comment_versions.values() all_versions.extend(versions) all_versions.sort(key=lambda v: v.created, reverse=True) pager = amo.utils.paginate(request, all_versions, 10) num_pages = pager.paginator.num_pages count = pager.paginator.count try: flags = ViewQueue.objects.get(id=addon.id).flags except ViewQueue.DoesNotExist: flags = [] ctx = context(version=version, addon=addon, pager=pager, num_pages=num_pages, count=count, flags=flags, form=form, paging=paging, canned=canned, is_admin=is_admin, show_diff=show_diff, allow_unchecking_files=allow_unchecking_files, actions=actions, actions_minimal=actions_minimal) return render(request, 'editors/review.html', ctx)
def report_menu(context, request, addon, report): report_tree = [ { 'name': 'overview', 'url': '', 'title': _('Overview'), }, { 'name': 'downloads', 'url': 'downloads/', 'title': _('Downloads'), 'children': [ { 'name': 'sources', 'url': 'downloads/sources/', 'title': _('by Source'), }, ] }, { 'name': 'usage', 'url': 'usage/', 'title': _('Daily Users'), 'children': [ { 'name': 'versions', 'url': 'usage/versions/', 'title': _('by Add-on Version') }, { 'name': 'apps', 'url': 'usage/applications/', 'title': _('by Application') }, { 'name': 'locales', 'url': 'usage/languages/', 'title': _('by Language') }, { 'name': 'os', 'url': 'usage/os/', 'title': _('by Platform') }, { 'name': 'statuses', 'url': 'usage/status/', 'title': _('by Add-on Status') }, ] }, ] if (request.user.is_authenticated() and (acl.action_allowed(request, 'Admin', 'ViewAnyStats') or addon.has_author(request.amo_user))): report_tree.append({ 'name': 'contributions', 'url': 'contributions/', 'title': _('Contributions') }) base_url = reverse('stats.overview', args=[addon.slug]) """Reports Menu. navigation for the various statistic reports.""" c = {'report': report, 'base_url': base_url, 'report_tree': report_tree} return c