def post(self, request, project_slug): project = get_object_or_404(Project, slug=project_slug) if not AdminPermission.is_admin(request.user, project): return HttpResponseForbidden() version_slug = request.POST.get('version_slug') version = get_object_or_404( Version, project=project, slug=version_slug, ) update_docs_task, build = trigger_build( project=project, version=version, ) if (update_docs_task, build) == (None, None): # Build was skipped messages.add_message( request, messages.WARNING, "This project is currently disabled and can't trigger new builds.", ) return HttpResponseRedirect( reverse('builds_project_list', args=[project.slug]), ) return HttpResponseRedirect( reverse('builds_detail', args=[project.slug, build.pk]), )
def test_project_admins_can_delete_subprojects_that_they_are_not_admin_of(self): self.project.users.add(self.user) self.assertFalse(AdminPermission.is_admin(self.user, self.subproject)) response = self.client.post( '/dashboard/my-mainproject/subprojects/my-subproject/delete/') self.assertEqual(response.status_code, 302) self.assertTrue(self.subproject not in [r.child for r in self.project.subprojects.all()])
def test_project_admins_can_delete_subprojects_that_they_are_not_admin_of( self, ): self.project.users.add(self.user) self.assertFalse(AdminPermission.is_admin(self.user, self.subproject)) response = self.client.post( '/dashboard/my-mainproject/subprojects/my-subproject/delete/', ) self.assertEqual(response.status_code, 302) self.assertTrue( self.subproject not in [r.child for r in self.project.subprojects.all()], )
def project_versions(request, project_slug): """ Project version list view. Shows the available versions and lets the user choose which ones to build. """ max_inactive_versions = 100 project = get_object_or_404( Project.objects.protected(request.user), slug=project_slug, ) versions = Version.internal.public( user=request.user, project=project, only_active=False, ) active_versions = versions.filter(active=True) # Limit inactive versions in case a project has a large number of branches or tags # Filter inactive versions based on the query string inactive_versions = versions.filter(active=False) version_filter = request.GET.get('version_filter', '') if version_filter: inactive_versions = inactive_versions.filter( verbose_name__icontains=version_filter) total_inactive_versions_count = inactive_versions.count() inactive_versions = inactive_versions[:max_inactive_versions] # If there's a wiped query string, check the string against the versions # list and display a success message. Deleting directories doesn't know how # to fail. :) wiped = request.GET.get('wipe', '') wiped_version = versions.filter(slug=wiped) if wiped and wiped_version.exists(): messages.success(request, 'Version wiped: ' + wiped) # Optimize project permission checks prefetch_related_objects([project], 'users') return render( request, 'projects/project_version_list.html', { 'inactive_versions': inactive_versions, 'active_versions': active_versions, 'project': project, 'is_project_admin': AdminPermission.is_admin( request.user, project), 'max_inactive_versions': max_inactive_versions, 'total_inactive_versions_count': total_inactive_versions_count, }, )
def post(self, request, project_slug): project = get_object_or_404(Project, slug=project_slug) if not AdminPermission.is_admin(request.user, project): return HttpResponseForbidden() version_slug = request.POST.get('version_slug') version = get_object_or_404( Version, project=project, slug=version_slug, ) trigger_build(project=project, version=version) return HttpResponseRedirect(reverse('builds_project_list', args=[project.slug]))
def post(self, request, project_slug): project = get_object_or_404(Project, slug=project_slug) if not AdminPermission.is_admin(request.user, project): return HttpResponseForbidden() version_slug = request.POST.get('version_slug') version = get_object_or_404( Version, project=project, slug=version_slug, ) _, build = trigger_build(project=project, version=version) return HttpResponseRedirect( reverse('builds_detail', args=[project.slug, build.pk]), )
def clean_subproject(self): """Normalize subproject field Does lookup on against :py:class:`Project` to ensure matching project exists. Return the :py:class:`Project` object instead. """ subproject_name = self.cleaned_data['subproject'] subproject_qs = Project.objects.filter(slug=subproject_name) if not subproject_qs.exists(): raise forms.ValidationError( (_("Project %(name)s does not exist") % { 'name': subproject_name })) subproject = subproject_qs.first() if not AdminPermission.is_admin(self.user, subproject): raise forms.ValidationError( _('You need to be admin of {name} in order to add it as ' 'a subproject.'.format(name=subproject_name))) return subproject
def post(self, request, project_slug, build_pk): project = get_object_or_404(Project, slug=project_slug) build = get_object_or_404(Build, pk=build_pk) if not AdminPermission.is_admin(request.user, project): return HttpResponseForbidden() # NOTE: `terminate=True` is required for the child to attend our call # immediately when it's running the build. Otherwise, it finishes the # task. However, to revoke a task that has not started yet, we don't # need it. if build.state == BUILD_STATE_TRIGGERED: # Since the task won't be executed at all, we need to update the # Build object here. terminate = False build.state = BUILD_STATE_FINISHED build.success = False build.error = BuildCancelled.message build.length = 0 build.save() else: # In this case, we left the update of the Build object to the task # itself to be executed in the `on_failure` handler. terminate = True log.warning( 'Canceling build.', project_slug=project.slug, version_slug=build.version.slug, build_id=build.pk, build_task_id=build.task_id, terminate=terminate, ) app.control.revoke(build.task_id, signal=signal.SIGINT, terminate=terminate) return HttpResponseRedirect( reverse('builds_detail', args=[project.slug, build.pk]), )
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) project = self.get_project() # Get filtered and sorted versions versions = self._get_versions(project) if settings.RTD_EXT_THEME_ENABLED: filter = ProjectVersionListFilterSet( self.request.GET, queryset=versions, ) context['filter'] = filter versions = filter.qs context['versions'] = versions protocol = 'http' if self.request.is_secure(): protocol = 'https' version_slug = project.get_default_version() context['badge_url'] = ProjectBadgeView.get_badge_url( project.slug, version_slug, protocol=protocol, ) context['site_url'] = '{url}?badge={version}'.format( url=project.get_docs_url(version_slug), version=version_slug, ) context['is_project_admin'] = AdminPermission.is_admin( self.request.user, project, ) return context
def is_admin(user, project): return AdminPermission.is_admin(user, project)
def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True # TODO: Similar logic to #1084 return AdminPermission.is_admin(request.user, obj.node.project)
def post(self, request, project_slug): commit_to_retrigger = None project = get_object_or_404(Project, slug=project_slug) if not AdminPermission.is_admin(request.user, project): return HttpResponseForbidden() version_slug = request.POST.get('version_slug') build_pk = request.POST.get('build_pk') if build_pk: # Filter over external versions only when re-triggering a specific build version = get_object_or_404( Version.external.public(self.request.user), slug=version_slug, project=project, ) build_to_retrigger = get_object_or_404( Build.objects.all(), pk=build_pk, version=version, ) if build_to_retrigger != Build.objects.filter( version=version).first(): messages.add_message( request, messages.ERROR, "This build can't be re-triggered because it's " "not the latest build for this version.", ) return HttpResponseRedirect(request.path) # Set either the build to re-trigger it or None if build_to_retrigger: commit_to_retrigger = build_to_retrigger.commit log.info( 'Re-triggering build.', project_slug=project.slug, version_slug=version.slug, build_commit=build_to_retrigger.commit, build_id=build_to_retrigger.pk, ) else: # Use generic query when triggering a normal build version = get_object_or_404( self._get_versions(project), slug=version_slug, ) update_docs_task, build = trigger_build( project=project, version=version, commit=commit_to_retrigger, ) if (update_docs_task, build) == (None, None): # Build was skipped messages.add_message( request, messages.WARNING, "This project is currently disabled and can't trigger new builds.", ) return HttpResponseRedirect( reverse('builds_project_list', args=[project.slug]), ) return HttpResponseRedirect( reverse('builds_detail', args=[project.slug, build.pk]), )