def get_draft(self, user=None): """Returns the draft of the review request. If a user is specified, than the draft will be returned only if owned by the user. Otherwise, None will be returned. """ if not user: return get_object_or_none(self.draft) elif user.is_authenticated(): return get_object_or_none(self.draft, review_request__submitter=user) return None
def save(self): """Save the form, creating a user if validation passes. The :py:class:`User` will be created with the provided username, e-mail address, password, and full name. If there are failures in creating this user, or there's an existing user with the given name, an error will be raisd. Subclasses that want to override this can call the parent's py:meth:`save` and modify the resulting user, if ``None`` is not returned. Returns: User: The newly-created user. """ if not self.errors: try: user = auth.models.User.objects.create_user( self.cleaned_data['username'], self.cleaned_data['email'], self.cleaned_data['password1']) user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] user.save() return user except: # We check for duplicate users here instead of clean, since # it's possible that two users could race for a name. if get_object_or_none(User, username=self.cleaned_data['username']): self.errors['username'] = forms.util.ErrorList( [_('Sorry, this username is taken.')]) else: raise
def _check(*args, **kwargs): request = _find_httprequest(args) local_site_name = kwargs.get('local_site_name', None) webapi_token = getattr(request, '_webapi_token', None) if webapi_token: restrict_to_local_site = request._webapi_token.local_site_id else: restrict_to_local_site = None if local_site_name: local_site = get_object_or_none(LocalSite, name=local_site_name) if not local_site: return DOES_NOT_EXIST elif not local_site.is_accessible_by(request.user): if request.user.is_authenticated(): return PERMISSION_DENIED else: return NOT_LOGGED_IN elif (restrict_to_local_site and restrict_to_local_site != local_site.pk): return PERMISSION_DENIED elif restrict_to_local_site is not None: return PERMISSION_DENIED return view_func(*args, **kwargs)
def _check(*args, **kwargs): request = _find_httprequest(args) local_site_name = kwargs.get('local_site_name', None) webapi_token = getattr(request, '_webapi_token', None) if webapi_token: restrict_to_local_site = request._webapi_token.local_site_id else: restrict_to_local_site = None if local_site_name: local_site = get_object_or_none(LocalSite, name=local_site_name) if not local_site: return DOES_NOT_EXIST elif not local_site.is_accessible_by(request.user): if request.user.is_authenticated(): return PERMISSION_DENIED else: return NOT_LOGGED_IN elif (restrict_to_local_site and restrict_to_local_site != local_site.pk): return PERMISSION_DENIED kwargs['local_site'] = local_site elif restrict_to_local_site is not None: return PERMISSION_DENIED else: kwargs['local_site'] = None return view_func(*args, **kwargs)
def process_view(self, request, view_func, view_args, view_kwargs): """Process the request before calling the view. This sets up a ``local_site`` attribute on the request to fetch the :py:class:`~reviewboard.site.models.LocalSite` used for this view, if any. This is based on the ``local_site_name`` key in ``view_kwargs``. Args: request (django.http.HttpRequest): The HTTP request from the client. view_func (callable): The view being called. This is unused. view_args (tuple): The positional arguments passed to the view. This is unused. view_kwargs (dict): The keyword arguments passed to the view. """ local_site_name = view_kwargs.get('local_site_name') request._local_site_name = local_site_name if request._local_site_name: request.local_site = SimpleLazyObject( lambda: get_object_or_none(LocalSite, name=local_site_name)) else: request.local_site = None
def save(self): """Save the form, creating a user if validation passes. The :py:class:`User` will be created with the provided username, e-mail address, password, and full name. If there are failures in creating this user, or there's an existing user with the given name, an error will be raisd. Subclasses that want to override this can call the parent's py:meth:`save` and modify the resulting user, if ``None`` is not returned. Returns: User: The newly-created user. """ if not self.errors: try: user = auth.models.User.objects.create_user( self.cleaned_data['username'], self.cleaned_data['email'], self.cleaned_data['password1']) user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] user.save() return user except: # We check for duplicate users here instead of clean, since # it's possible that two users could race for a name. if get_object_or_none(User, username=self.cleaned_data['username']): self.errors['username'] = form_utils.ErrorList( [_('Sorry, this username is taken.')]) else: raise
def publish(self, user): """Publishes the current draft attached to this review request. The review request will be mark as public, and signals will be emitted for any listeners. """ from reviewboard.accounts.models import LocalSiteProfile if not self.is_mutable_by(user): raise PermissionError draft = get_object_or_none(self.draft) review_request_publishing.send(sender=self.__class__, user=user, review_request_draft=draft) # Decrement the counts on everything. we lose them. # We'll increment the resulting set during ReviewRequest.save. # This should be done before the draft is published. # Once the draft is published, the target people # and groups will be updated with new values. # Decrement should not happen while publishing # a new request or a discarded request if self.public: Group.incoming_request_count.decrement(self.target_groups.all()) LocalSiteProfile.direct_incoming_request_count.decrement( LocalSiteProfile.objects.filter( user__in=self.target_people.all(), local_site=self.local_site)) LocalSiteProfile.total_incoming_request_count.decrement( LocalSiteProfile.objects.filter( Q(local_site=self.local_site) & Q( Q(user__review_groups__in=self.target_groups.all()) | Q(user__in=self.target_people.all())))) LocalSiteProfile.starred_public_request_count.decrement( LocalSiteProfile.objects.filter( profile__starred_review_requests=self, local_site=self.local_site)) if draft is not None: # This will in turn save the review request, so we'll be done. changes = draft.publish(self, send_notification=False) draft.delete() else: changes = None if not self.public and self.changedescs.count() == 0: # This is a brand new review request that we're publishing # for the first time. Set the creation timestamp to now. self.time_added = timezone.now() self.public = True self.save(update_counts=True) review_request_published.send(sender=self.__class__, user=user, review_request=self, changedesc=changes)
def publish(self, user, trivial=False, validate_fields=True): """Publishes the current draft attached to this review request. The review request will be mark as public, and signals will be emitted for any listeners. """ if not self.is_mutable_by(user): raise PermissionError draft = get_object_or_none(self.draft) old_submitter = self.submitter review_request_publishing.send(sender=self.__class__, user=user, review_request_draft=draft) # Decrement the counts on everything. we lose them. # We'll increment the resulting set during ReviewRequest.save. # This should be done before the draft is published. # Once the draft is published, the target people # and groups will be updated with new values. # Decrement should not happen while publishing # a new request or a discarded request if self.public: self._decrement_reviewer_counts() if draft is not None: # This will in turn save the review request, so we'll be done. try: changes = draft.publish(self, send_notification=False, user=user, validate_fields=validate_fields) except Exception: # The draft failed to publish, for one reason or another. # Check if we need to re-increment those counters we # previously decremented. if self.public: self._increment_reviewer_counts() raise draft.delete() else: changes = None if not self.public and self.changedescs.count() == 0: # This is a brand new review request that we're publishing # for the first time. Set the creation timestamp to now. self.time_added = timezone.now() self.public = True self.save(update_counts=True, old_submitter=old_submitter) review_request_published.send(sender=self.__class__, user=user, review_request=self, trivial=trivial, changedesc=changes)
def get_pending_reply(self, user): """Returns the pending reply owned by the specified user.""" if user.is_authenticated(): return get_object_or_none(Review, user=user, public=False, base_reply_to=self) return None
def close(self, type, user=None, description=None, rich_text=False): """Closes the review request. The type must be one of SUBMITTED or DISCARDED. """ if (user and not self.is_mutable_by(user) and not user.has_perm("reviews.can_change_status", self.local_site)): raise PermissionError if type not in [self.SUBMITTED, self.DISCARDED]: raise AttributeError("%s is not a valid close type" % type) draft = get_object_or_none(self.draft) if self.status != type: if (draft is not None and not self.public and type == self.DISCARDED): # Copy over the draft information if this is a private discard. draft.copy_fields_to_request(self) # TODO: Use the user's default for rich_text. changedesc = ChangeDescription(public=True, text=description or "", rich_text=rich_text or False) status_field = get_review_request_field('status')(self) status_field.record_change_entry(changedesc, self.status, type) changedesc.save() self.changedescs.add(changedesc) if type == self.SUBMITTED: if not self.public: raise PublishError("The draft must be public first.") else: self.commit_id = None self.status = type self.save(update_counts=True) review_request_closed.send(sender=self.__class__, user=user, review_request=self, type=type) else: # Update submission description. changedesc = self.changedescs.filter(public=True).latest() changedesc.timestamp = timezone.now() changedesc.text = description or "" changedesc.rich_text = rich_text changedesc.save() # Needed to renew last-update. self.save() # Delete the associated draft review request. if draft is not None: draft.delete()
def publish(self, user): """Publishes the current draft attached to this review request. The review request will be mark as public, and signals will be emitted for any listeners. """ from reviewboard.accounts.models import LocalSiteProfile if not self.is_mutable_by(user): raise PermissionError draft = get_object_or_none(self.draft) review_request_publishing.send(sender=self.__class__, user=user, review_request_draft=draft) # Decrement the counts on everything. we lose them. # We'll increment the resulting set during ReviewRequest.save. # This should be done before the draft is published. # Once the draft is published, the target people # and groups will be updated with new values. # Decrement should not happen while publishing # a new request or a discarded request if self.public: Group.incoming_request_count.decrement(self.target_groups.all()) LocalSiteProfile.direct_incoming_request_count.decrement( LocalSiteProfile.objects.filter( user__in=self.target_people.all(), local_site=self.local_site)) LocalSiteProfile.total_incoming_request_count.decrement( LocalSiteProfile.objects.filter( Q(local_site=self.local_site) & Q(Q(user__review_groups__in=self.target_groups.all()) | Q(user__in=self.target_people.all())))) LocalSiteProfile.starred_public_request_count.decrement( LocalSiteProfile.objects.filter( profile__starred_review_requests=self, local_site=self.local_site)) if draft is not None: # This will in turn save the review request, so we'll be done. changes = draft.publish(self, send_notification=False) draft.delete() else: changes = None if not self.public and self.changedescs.count() == 0: # This is a brand new review request that we're publishing # for the first time. Set the creation timestamp to now. self.time_added = timezone.now() self.public = True self.save(update_counts=True) review_request_published.send(sender=self.__class__, user=user, review_request=self, changedesc=changes)
def post(self, request): """Save the extension configuration. Args: request (django.http.HttpRequest): The HTTP request, including POSTed data. Returns: django.http.HttpResponse: The response. The body of the response is a JSON-encoded blob which indicates success or failure, and in the success case, includes the the current configuration. """ if not request.user.is_superuser: return HttpResponseForbidden() extension = ReviewBotExtension.instance should_save = False new_user = request.POST.get('reviewbot_user') if new_user: try: user = User.objects.get(pk=new_user) except User.DoesNotExist: # TODO: return which field was invalid return HttpResponseBadRequest(json.dumps({ 'result': 'error', 'field': 'user', 'error': 'The specified user does not exist.', }), content_type='application/json') extension.settings['user'] = user.pk should_save = True else: user = get_object_or_none(User, pk=extension.settings.get('user')) if 'reviewbot_broker_url' in request.POST: broker_url = request.POST['reviewbot_broker_url'] extension.settings['broker_url'] = broker_url should_save = True else: broker_url = extension.settings.get('broker_url', '') if should_save: extension.settings.save() return HttpResponse(json.dumps({ 'result': 'success', 'broker_url': broker_url, 'user': _serialize_user(request, user), }), content_type='application/json')
def post(self, request): """Save the extension configuration. Args: request (django.http.HttpRequest): The HTTP request, including POSTed data. Returns: django.http.HttpResponse: The response. The body of the response is a JSON-encoded blob which indicates success or failure, and in the success case, includes the the current configuration. """ if not request.user.is_superuser: return HttpResponseForbidden() extension = ReviewBotExtension.instance should_save = False new_user = request.POST.get('reviewbot_user') if new_user: try: user = User.objects.get(pk=new_user) except User.DoesNotExist: # TODO: return which field was invalid return HttpResponseBadRequest( json.dumps({ 'result': 'error', 'field': 'user', 'error': 'The specified user does not exist.', }), content_type='application/json') extension.settings['user'] = user.pk should_save = True else: user = get_object_or_none(User, pk=extension.settings.get('user')) if 'reviewbot_broker_url' in request.POST: broker_url = request.POST['reviewbot_broker_url'] extension.settings['broker_url'] = broker_url should_save = True else: broker_url = extension.settings.get('broker_url', '') if should_save: extension.settings.save() return HttpResponse( json.dumps({ 'result': 'success', 'broker_url': broker_url, 'user': _serialize_user(request, user), }), content_type='application/json')
def publish(self, user, trivial=False): """Publishes the current draft attached to this review request. The review request will be mark as public, and signals will be emitted for any listeners. """ if not self.is_mutable_by(user): raise PermissionError draft = get_object_or_none(self.draft) old_submitter = self.submitter review_request_publishing.send(sender=self.__class__, user=user, review_request_draft=draft) # Decrement the counts on everything. we lose them. # We'll increment the resulting set during ReviewRequest.save. # This should be done before the draft is published. # Once the draft is published, the target people # and groups will be updated with new values. # Decrement should not happen while publishing # a new request or a discarded request if self.public: self._decrement_reviewer_counts() if draft is not None: # This will in turn save the review request, so we'll be done. try: changes = draft.publish(self, send_notification=False, user=user) except Exception: # The draft failed to publish, for one reason or another. # Check if we need to re-increment those counters we # previously decremented. if self.public: self._increment_reviewer_counts() raise draft.delete() else: changes = None if not self.public and self.changedescs.count() == 0: # This is a brand new review request that we're publishing # for the first time. Set the creation timestamp to now. self.time_added = timezone.now() self.public = True self.save(update_counts=True, old_submitter=old_submitter) review_request_published.send(sender=self.__class__, user=user, review_request=self, trivial=trivial, changedesc=changes)
def get_user(self, user_id): """Return an existing user given a numeric user ID. Args: user_id (int): The ID of the user to retrieve. Returns: django.contrib.auth.models.User: The resulting user, or ``None`` if one could not be found. """ return get_object_or_none(User, pk=user_id)
def publish(self, user): """Publishes the current draft attached to this review request. The review request will be mark as public, and signals will be emitted for any listeners. """ if not self.is_mutable_by(user): raise PermissionError draft = get_object_or_none(self.draft) review_request_publishing.send(sender=self.__class__, user=user, review_request_draft=draft) # Decrement the counts on everything. we lose them. # We'll increment the resulting set during ReviewRequest.save. # This should be done before the draft is published. # Once the draft is published, the target people # and groups will be updated with new values. # Decrement should not happen while publishing # a new request or a discarded request if self.public: self._decrement_reviewer_counts() if draft is not None: # This will in turn save the review request, so we'll be done. changes = draft.publish(self, send_notification=False) draft.delete() else: changes = None if not self.public and self.changedescs.count() == 0: # This is a brand new review request that we're publishing # for the first time. Set the creation timestamp to now. self.time_added = timezone.now() # make a call to jira api to update status for the new request self.send_jira_transition('start') self.public = True self.save(update_counts=True) review_request_published.send(sender=self.__class__, user=user, review_request=self, changedesc=changes)
def save(self): if not self.errors: try: user = auth.models.User.objects.create_user( self.cleaned_data['username'], self.cleaned_data['email'], self.cleaned_data['password1']) user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] user.save() return user except: # We check for duplicate users here instead of clean, since it's # possible that two users could race for a name. if get_object_or_none(User, username=self.cleaned_data['username']): self.errors['username'] = forms.util.ErrorList( [_('Sorry, this username is taken.')]) else: raise
def _check(*args, **kwargs): request = _find_httprequest(args) local_site_name = kwargs.get('local_site_name', None) webapi_token = getattr(request, '_webapi_token', None) if webapi_token: restrict_to_local_site = request._webapi_token.local_site_id else: restrict_to_local_site = None if local_site_name: local_site = get_object_or_none(LocalSite, name=local_site_name) if not local_site: return DOES_NOT_EXIST elif not local_site.is_accessible_by(request.user): if request.user.is_authenticated(): logging.warning('%s %s: user %s does not have access to ' 'local site "%s".', request.method, request.path_info, request.user.username, local_site_name) return PERMISSION_DENIED else: return NOT_LOGGED_IN elif (restrict_to_local_site and restrict_to_local_site != local_site.pk): logging.warning('%s %s: API token for user %s does not have ' 'access to local site "%s".', request.method, request.path_info, request.user.username, local_site_name) return PERMISSION_DENIED kwargs['local_site'] = local_site elif restrict_to_local_site is not None: logging.warning('%s %s: API token for user %s is limited to a ' 'local site but the request was for the root.', request.method, request.path_info, request.user.username) return PERMISSION_DENIED else: kwargs['local_site'] = None return view_func(*args, **kwargs)
def __get_oauth2_token_for_request(self, request): """Return the OAuth2 token for the request. Args: request (django.http.HttpRequest): The current HTTP request. Returns: tuple: A 2-tuple of the following: * the access token, if it exists (:py:class:`~oauth2_provider.models.AccessToken`); and * whether or not the token was retrieved from the session (:py:class:`bool`). """ try: from oauth2_provider.models import AccessToken except ImportError: raise ImproperlyConfigured( 'WebAPIOAuth2TokenAuthBackend requires django-oauth-toolkit' '>=0.9,<0.9.999.' ) token = getattr(request, '_oauth2_token', None) from_session = False if not token: token_pk = request.session.get('oauth2_token_id') if token_pk: from_session = True token = get_object_or_none(AccessToken, pk=token_pk) if not token or token.is_expired(): # Either the token no longer exists or has expired, so we # must log the user out. auth.logout(request) request._oauth2_token = token return token, from_session
def _get_stored_consent_for_user(self, user): """Return stored consent data for a user. This will look up the consent data and cache it on the user, speeding up subsequent calls. Args: user (django.contrib.auth.models.User): The user to retrieve and cache stored consent data for. Returns: djblets.privacy.models.StoredConsentData: The stored consent data for the user, or ``None`` if there's no existing data on record. """ if not hasattr(user, '_djblets_stored_consent'): user._djblets_stored_consent = \ get_object_or_none(self.model, user=user) return user._djblets_stored_consent
def __get_oauth2_token_for_request(self, request): """Return the OAuth2 token for the request. Args: request (django.http.HttpRequest): The current HTTP request. Returns: tuple: A 2-tuple of the following: * the access token, if it exists (:py:class:`~oauth2_provider.models.AccessToken`); and * whether or not the token was retrieved from the session (:py:class:`bool`). """ try: from oauth2_provider.models import AccessToken except ImportError: raise ImproperlyConfigured( 'WebAPIOAuth2TokenAuthBackend requires django-oauth-toolkit' '>=0.9,<0.9.999.') token = getattr(request, '_oauth2_token', None) from_session = False if not token: token_pk = request.session.get('oauth2_token_id') if token_pk: from_session = True token = get_object_or_none(AccessToken, pk=token_pk) if not token or token.is_expired(): # Either the token no longer exists or has expired, so we # must log the user out. auth.logout(request) request._oauth2_token = token return token, from_session
def get(self, request): """Render and return the admin page. Args: request (django.http.HttpRequest): The HTTP request. Returns: django.http.HttpResponse: The response. """ if not request.user.is_superuser: # TODO: Once we move to Django 1.9+, we can switch to the new # access mixin methods instead of testing this ourselves. Here and # below in the other views in this file. return HttpResponseForbidden() extension = ReviewBotExtension.instance user = get_object_or_none(User, pk=extension.settings.get('user')) return render(request, self.template_name, { 'extension': extension, 'reviewbot_user': user, })
def __init__(self, data=None, initial={}, request=None, *args, **kwargs): """Initialize the form. Args: data (dict, optional): Posted data for the form. initial (dict, optional): Initial data for the form. request (django.http.HttpRequest, optional): The HTTP request from the client. This is used for logging of access errors, and will be passed to the underlying form if :py:attr:`form_needs_request` is ``True``. *args (tuple): Positional arguments to pass to the parent form. **kwargs (dict): Keyword arguments to pass to the parent form. Keyword Args: limit_to_local_site (reviewboard.site.models.LocalSite, optional): A specific Local Site to bind the form to. request (django.http.HttpRequest, optional): The HTTP request from the client. Raises: ValueError: An object instance was provided to the form that isn't compatible with the :py:class:`~reviewboard.site.models.LocalSite` provided in ``limit_to_local_site``. """ local_site = kwargs.pop('limit_to_local_site', None) if self.form_needs_request: kwargs['request'] = request self.limited_to_local_site = local_site local_site_field_name = self.local_site_field_name if local_site is None: if data and data.get(local_site_field_name): local_site = get_object_or_none(LocalSite, pk=data[local_site_field_name]) elif initial and initial.get(local_site_field_name): local_site = initial[local_site_field_name] if data is not None and local_site: # Always use the assigned Local Site in the model data. We # don't want to allow this to ever be overridden. We'll delete # here and then assign after the constructor does its thing. data = data.copy() data[local_site_field_name] = local_site.pk self.cur_local_site = local_site super(LocalSiteAwareModelFormMixin, self).__init__(data=data, initial=initial, *args, **kwargs) # Prepare to patch up some fields. We have a few special types we'll # be dealing with. self._conditions_fields = [] self._related_obj_fields = [] self._queryset_fields = [] for field_name, field in six.iteritems(self.fields): if isinstance(field.widget, RelatedObjectWidget): self._related_obj_fields.append(field) elif isinstance(field, ConditionsField): self._conditions_fields.append(field) field.choice_kwargs['request'] = request if getattr(field, 'queryset', None) is not None: self._queryset_fields.append(field) if self.limited_to_local_site is not None: if self.instance is not None: if self.instance.pk is None: # This is a new instance, so force its Local Site now. self.instance.local_site = local_site elif self.instance.local_site != local_site: # Something went very wrong, and an instance is now in our # form that isn't part of this Local Site. Log this and # bail out now. logger.error( 'Attempted to pass instance %r with ' 'LocalSite "%s" to form %r. Only LocalSite ' '"%s" is permitted.', self.instance, self.instance.local_site, self.__class__, local_site, request=request) raise ValueError( _('The provided instance is not associated with a ' 'LocalSite compatible with this form. Please ' 'contact support.')) # We never want to show a "Local Site" field, so let's get rid of # it. del self.fields[local_site_field_name] # Go through the fields and widgets and start limiting querysets # and other choices. local_site_name = local_site.name for field in self._related_obj_fields: field.widget.local_site_name = local_site_name for field in self._conditions_fields: field.choice_kwargs['local_site'] = local_site for field in self._queryset_fields: self._patch_field_local_site_queryset(field, local_site)
def can_publish(self): return not self.public or get_object_or_none(self.draft) is not None
def get_user(self, user_id): return get_object_or_none(User, pk=user_id)
def _on_review_request_published(self, user, review_request, changedesc, **kwargs): """Handler for when review requests are published. This will send a notification to any configured channels when a review request is published. Args: user (django.contrib.auth.models.User): The user who published the review request. review_request (reviewboard.reviews.models.ReviewRequest): The review request that was published. changedesc (reviewboard.changedescs.models.ChangeDescription): The change description for the update, if any. **kwargs (dict): Additional keyword arguments passed to the handler. """ user_link = self.get_user_text_link(user, review_request.local_site) fields = [] if changedesc: fallback_text = 'New update from %s' % user_displayname(user) pre_text = 'New update from %s' % user_link # This might be empty, which is fine. We won't show an update # at that point. body = changedesc.text else: fallback_text = 'New review request from %s' % \ user_displayname(user) pre_text = 'New review request from %s' % user_link body = None fields.append({ 'short': False, 'title': 'Description', 'value': review_request.description, }) # Link to the diff in the update, if any. diffset = review_request.get_latest_diffset() if diffset: diff_url = local_site_reverse( 'view-diff-revision', local_site=review_request.local_site, kwargs={ 'review_request_id': review_request.display_id, 'revision': diffset.revision, }) fields.append({ 'short': True, 'title': 'Diff', 'value': self.format_link(diff_url, 'Revision %s' % diffset.revision), }) if review_request.repository: fields.append({ 'short': True, 'title': 'Repository', 'value': review_request.repository.name, }) if review_request.branch: fields.append({ 'short': True, 'title': 'Branch', 'value': review_request.branch, }) # See if there are any new interesting file attachments to show. # These will only show up if the file is accessible. attachment = None if changedesc: # Only show new files added in this change. try: new_files = changedesc.fields_changed['files']['added'] except KeyError: new_files = [] for file_info in new_files: if (len(file_info) >= 3 and file_info[1].endswith(self.VALID_IMAGE_URL_EXTS)): # This one wins. Show it. attachment = get_object_or_none( review_request.file_attachments, pk=file_info[2]) break else: # This is a new review request, so show the first valid image # we can find. for attachment in review_request.file_attachments.all(): if attachment.filename.endswith(self.VALID_IMAGE_URL_EXTS): # This one wins. Show it. break else: attachment = None if attachment: image_url = attachment.get_absolute_url() else: image_url = None # Find any trophies we may want to show in the update. trophies = Trophy.objects.get_trophies(review_request) trophy_url = None if trophies: # For now, due to the need to look up resources from a stable # location, we're only supporting certain trophies. First one # wins. for trophy in trophies: try: trophy_url = self.TROPHY_URLS[trophy.category] break except KeyError: pass self.notify_review_request(review_request, fallback_text=fallback_text, body=body, pre_text=pre_text, fields=fields, thumb_url=trophy_url, image_url=image_url, local_site=review_request.local_site, event_name='review_request_published')
def get_user(self, user_id): """Get an existing user, or None if it does not exist.""" return get_object_or_none(User, pk=user_id)
def _on_review_request_published(self, user, review_request, changedesc, **kwargs): """Handler for when review requests are published. This will send a notification to any configured Slack channels when a review request is published. Args: user (django.contrib.auth.models.User): The user who published the review request. review_request (reviewboard.reviews.models.ReviewRequest): The review request that was published. changedesc (reviewboard.changedescs.models.ChangeDescription): The change description for the update, if any. **kwargs (dict): Additional keyword arguments passed to the handler. """ user_link = self.get_user_text_link(user, review_request.local_site) fields = [] if changedesc: fallback_text = 'New update from %s' % user_displayname(user) pre_text = 'New update from %s' % user_link # This might be empty, which is fine. We won't show an update # at that point. body = changedesc.text else: fallback_text = 'New review request from %s' % \ user_displayname(user) pre_text = 'New review request from %s' % user_link body = None fields.append({ 'short': False, 'title': 'Description', 'value': review_request.description, }) # Link to the diff in the update, if any. diffset = review_request.get_latest_diffset() if diffset: diff_url = local_site_reverse( 'view-diff-revision', local_site=review_request.local_site, kwargs={ 'review_request_id': review_request.display_id, 'revision': diffset.revision, }) fields.append({ 'short': True, 'title': 'Diff', 'value': self.format_link(diff_url, 'Revision %s' % diffset.revision), }) if review_request.repository: fields.append({ 'short': True, 'title': 'Repository', 'value': review_request.repository.name, }) if review_request.branch: fields.append({ 'short': True, 'title': 'Branch', 'value': review_request.branch, }) # See if there are any new interesting file attachments to show. # These will only show up if the file is accessible to Slack. attachment = None if changedesc: # Only show new files added in this change. try: new_files = changedesc.fields_changed['files']['added'] except KeyError: new_files = [] for file_info in new_files: if (len(file_info) >= 3 and file_info[1].endswith(self.VALID_IMAGE_URL_EXTS)): # This one wins. Show it. attachment = get_object_or_none( review_request.file_attachments, pk=file_info[2]) break else: # This is a new review request, so show the first valid image # we can find. for attachment in review_request.file_attachments.all(): if attachment.filename.endswith(self.VALID_IMAGE_URL_EXTS): # This one wins. Show it. break else: attachment = None if attachment: image_url = attachment.get_absolute_url() else: image_url = None # Find any trophies we may want to show in the update. trophies = Trophy.objects.get_trophies(review_request) trophy_url = None if trophies: # For now, due to the need to look up resources from a stable # location, we're only supporting certain trophies. First one # wins. for trophy in trophies: try: trophy_url = self.TROPHY_URLS[trophy.category] break except KeyError: pass self.notify_review_request(review_request, fallback_text=fallback_text, body=body, pre_text=pre_text, fields=fields, thumb_url=trophy_url, image_url=image_url, local_site=review_request.local_site, event_name='review_request_published')
def check_delete_result(self, user, app_pk): self.assertIsNone(get_object_or_none(Application, pk=app_pk))
def close(self, close_type=None, user=None, description=None, rich_text=False, **kwargs): """Closes the review request. Args: close_type (unicode): How the close occurs. This should be one of :py:attr:`SUBMITTED` or :py:attr:`DISCARDED`. user (django.contrib.auth.models.User): The user who is closing the review request. description (unicode): An optional description that indicates why the review request was closed. rich_text (bool): Indicates whether or not that the description is rich text. Raises: ValueError: The provided close type is not a valid value. PermissionError: The user does not have permission to close the review request. TypeError: Keyword arguments were supplied to the function. .. versionchanged:: 3.0 The ``type`` argument is deprecated: ``close_type`` should be used instead. This method raises :py:exc:`ValueError` instead of :py:exc:`AttributeError` when the ``close_type`` has an incorrect value. """ if close_type is None: try: close_type = kwargs.pop('type') except KeyError: raise AttributeError('close_type must be provided') warnings.warn( 'The "type" argument was deprecated in Review Board 3.0 and ' 'will be removed in a future version. Use "close_type" ' 'instead.') if kwargs: raise TypeError('close() does not accept keyword arguments.') if (user and not self.is_mutable_by(user) and not user.has_perm( "reviews.can_change_status", self.local_site)): raise PermissionError if close_type not in [self.SUBMITTED, self.DISCARDED]: raise ValueError("%s is not a valid close type" % type) review_request_closing.send(sender=type(self), user=user, review_request=self, close_type=close_type, type=deprecated_signal_argument( signal_name='review_request_closing', old_name='type', new_name='close_type', value=close_type), description=description, rich_text=rich_text) draft = get_object_or_none(self.draft) if self.status != close_type: if (draft is not None and not self.public and close_type == self.DISCARDED): # Copy over the draft information if this is a private discard. draft.copy_fields_to_request(self) # TODO: Use the user's default for rich_text. changedesc = ChangeDescription(public=True, text=description or "", rich_text=rich_text or False, user=user or self.submitter) status_field = get_review_request_field('status')(self) status_field.record_change_entry(changedesc, self.status, close_type) changedesc.save() self.changedescs.add(changedesc) if close_type == self.SUBMITTED: if not self.public: raise PublishError("The draft must be public first.") else: self.commit_id = None self.status = close_type self.save(update_counts=True) review_request_closed.send(sender=type(self), user=user, review_request=self, close_type=close_type, type=deprecated_signal_argument( signal_name='review_request_closed', old_name='type', new_name='close_type', value=close_type), description=description, rich_text=rich_text) else: # Update submission description. changedesc = self.changedescs.filter(public=True).latest() changedesc.timestamp = timezone.now() changedesc.text = description or "" changedesc.rich_text = rich_text changedesc.save() # Needed to renew last-update. self.save() # Delete the associated draft review request. if draft is not None: draft.delete()
def check_delete_result(self, user, access_token_pk): self.assertIsNone(get_object_or_none(AccessToken, pk=access_token_pk))
def check_delete_result(self, user, review_request_id): self.assertIsNone(get_object_or_none(ReviewRequest, pk=review_request_id))
def check_delete_result(self, user, review_request_id): self.assertIsNone( get_object_or_none(ReviewRequest, pk=review_request_id))
def _check(*args, **kwargs): request = _find_httprequest(args) local_site_name = kwargs.get('local_site_name', None) webapi_token = getattr(request, '_webapi_token', None) oauth_token = getattr(request, '_oauth2_token', None) if webapi_token: restrict_to_local_site = request._webapi_token.local_site_id token_type = 'API' elif oauth_token: restrict_to_local_site = oauth_token.application.local_site_id token_type = 'OAuth' else: restrict_to_local_site = None token_type = None if local_site_name: local_site = get_object_or_none(LocalSite, name=local_site_name) if not local_site: return DOES_NOT_EXIST elif not local_site.is_accessible_by(request.user): if request.user.is_authenticated(): logging.warning( 'User does not have access to local site.', request=request, ) return PERMISSION_DENIED else: return NOT_LOGGED_IN elif oauth_token and not oauth_token.application.enabled: logging.warning( 'OAuth token using disabled application "%s" (%d).', oauth_token.application.name, oauth_token.application.pk, request=request, ) return PERMISSION_DENIED elif oauth_token and not restrict_to_local_site: # OAuth tokens for applications on the global site cannot be # used on a local site. logging.warning( 'OAuth token is for root, not local site.', request=request, ) return PERMISSION_DENIED elif (restrict_to_local_site and restrict_to_local_site != local_site.pk): logging.warning( '%s token does not have access to local site.', token_type, request=request, ) return PERMISSION_DENIED kwargs['local_site'] = local_site elif restrict_to_local_site is not None: logging.warning( '%s token is limited to a local site but the request was for ' 'the root.', token_type, request=request, ) return PERMISSION_DENIED else: kwargs['local_site'] = None return view_func(*args, **kwargs)
def check_delete_result(self, user, token_id): self.assertIsNone(get_object_or_none(WebAPIToken, pk=token_id))
def check_delete_result(self, user, group_name): self.assertIsNone(get_object_or_none(Group, name=group_name))
def get_or_create_user(self, username, request): """Get an existing user, or create one if it does not exist.""" return get_object_or_none(User, username=username)
def close(self, close_type=None, user=None, description=None, rich_text=False, **kwargs): """Closes the review request. Args: close_type (unicode): How the close occurs. This should be one of :py:attr:`SUBMITTED` or :py:attr:`DISCARDED`. user (django.contrib.auth.models.User): The user who is closing the review request. description (unicode): An optional description that indicates why the review request was closed. rich_text (bool): Indicates whether or not that the description is rich text. Raises: ValueError: The provided close type is not a valid value. PermissionError: The user does not have permission to close the review request. TypeError: Keyword arguments were supplied to the function. .. versionchanged:: 3.0 The ``type`` argument is deprecated: ``close_type`` should be used instead. This method raises :py:exc:`ValueError` instead of :py:exc:`AttributeError` when the ``close_type`` has an incorrect value. """ if close_type is None: try: close_type = kwargs.pop('type') except KeyError: raise AttributeError('close_type must be provided') warnings.warn( 'The "type" argument was deprecated in Review Board 3.0 and ' 'will be removed in a future version. Use "close_type" ' 'instead.' ) if kwargs: raise TypeError('close() does not accept keyword arguments.') if (user and not self.is_mutable_by(user) and not user.has_perm("reviews.can_change_status", self.local_site)): raise PermissionError if close_type not in [self.SUBMITTED, self.DISCARDED]: raise ValueError("%s is not a valid close type" % type) review_request_closing.send( sender=type(self), user=user, review_request=self, close_type=close_type, type=deprecated_signal_argument( signal_name='review_request_closing', old_name='type', new_name='close_type', value=close_type), description=description, rich_text=rich_text) draft = get_object_or_none(self.draft) if self.status != close_type: if (draft is not None and not self.public and close_type == self.DISCARDED): # Copy over the draft information if this is a private discard. draft.copy_fields_to_request(self) # TODO: Use the user's default for rich_text. changedesc = ChangeDescription(public=True, text=description or "", rich_text=rich_text or False, user=user or self.submitter) status_field = get_review_request_field('status')(self) status_field.record_change_entry(changedesc, self.status, close_type) changedesc.save() self.changedescs.add(changedesc) if close_type == self.SUBMITTED: if not self.public: raise PublishError("The draft must be public first.") else: self.commit_id = None self.status = close_type self.save(update_counts=True) review_request_closed.send( sender=type(self), user=user, review_request=self, close_type=close_type, type=deprecated_signal_argument( signal_name='review_request_closed', old_name='type', new_name='close_type', value=close_type), description=description, rich_text=rich_text) else: # Update submission description. changedesc = self.changedescs.filter(public=True).latest() changedesc.timestamp = timezone.now() changedesc.text = description or "" changedesc.rich_text = rich_text changedesc.save() # Needed to renew last-update. self.save() # Delete the associated draft review request. if draft is not None: draft.delete()
def _check(*args, **kwargs): request = _find_httprequest(args) local_site_name = kwargs.get('local_site_name', None) webapi_token = getattr(request, '_webapi_token', None) oauth_token = getattr(request, '_oauth2_token', None) if webapi_token: restrict_to_local_site = request._webapi_token.local_site_id token_type = 'API' elif oauth_token: restrict_to_local_site = oauth_token.application.local_site_id token_type = 'OAuth' else: restrict_to_local_site = None token_type = None if local_site_name: local_site = get_object_or_none(LocalSite, name=local_site_name) if not local_site: return DOES_NOT_EXIST elif not local_site.is_accessible_by(request.user): if request.user.is_authenticated: logger.warning( 'User does not have access to local site.', request=request, ) return PERMISSION_DENIED else: return NOT_LOGGED_IN elif oauth_token and not oauth_token.application.enabled: logger.warning( 'OAuth token using disabled application "%s" (%d).', oauth_token.application.name, oauth_token.application.pk, request=request, ) return PERMISSION_DENIED elif oauth_token and not restrict_to_local_site: # OAuth tokens for applications on the global site cannot be # used on a local site. logger.warning( 'OAuth token is for root, not local site.', request=request, ) return PERMISSION_DENIED elif (restrict_to_local_site and restrict_to_local_site != local_site.pk): logger.warning( '%s token does not have access to local site.', token_type, request=request, ) return PERMISSION_DENIED kwargs['local_site'] = local_site elif restrict_to_local_site is not None: logger.warning( '%s token is limited to a local site but the request was for ' 'the root.', token_type, request=request, ) return PERMISSION_DENIED else: kwargs['local_site'] = None return view_func(*args, **kwargs)
def user(self): """The configured user.""" return get_object_or_none(User, pk=self.settings.get('user'))
def __init__(self, data=None, initial={}, *args, **kwargs): """Initialize the form. Args: data (dict, optional): Posted data for the form. initial (dict, optional): Initial data for the form. *args (tuple): Positional arguments to pass to the parent form. **kwargs (dict): Keyword arguments to pass to the parent form. Keyword Args: limit_to_local_site (reviewboard.site.models.LocalSite, optional): A specific Local Site to bind the form to. request (django.http.HttpRequest, optional): The HTTP request from the client. Raises: ValueError: An object instance was provided to the form that isn't compatible with the :py:class:`~reviewboard.site.models.LocalSite` provided in ``limit_to_local_site``. """ local_site = kwargs.pop('limit_to_local_site', None) self.request = kwargs.pop('request', None) self.limited_to_local_site = local_site local_site_field_name = self.local_site_field_name if local_site is None: if data and data.get(local_site_field_name): local_site = get_object_or_none(LocalSite, pk=data[local_site_field_name]) elif initial and initial.get(local_site_field_name): local_site = initial[local_site_field_name] if data is not None and local_site: # Always use the assigned Local Site in the model data. We # don't want to allow this to ever be overridden. We'll delete # here and then assign after the constructor does its thing. data = data.copy() data[local_site_field_name] = local_site.pk self.cur_local_site = local_site super(LocalSiteAwareModelFormMixin, self).__init__(data=data, initial=initial, *args, **kwargs) # This is always set by BaseModelForm. assert self.instance is not None if self.limited_to_local_site is not None: if self.instance.pk is None: # This is a new instance, so force its Local Site now. self.instance.local_site = local_site elif self.instance.local_site != local_site: # Something went very wrong, and an instance is now in our # form that isn't part of this Local Site. Log this and bail # out now. logger.error( 'Attempted to pass instance %r with LocalSite ' '"%s" to form %r. Only LocalSite "%s" is ' 'permitted.', self.instance, self.instance.local_site, self.__class__, local_site, request=self.request) raise ValueError( _('The provided instance is not associated with a ' 'LocalSite compatible with this form. Please contact ' 'support.')) # We never want to show a "Local Site" field, so let's get rid of # it. del self.fields[local_site_field_name] # Go through the fields and widgets and start limiting querysets # and other choices. local_site_name = local_site.name for field_name, field in six.iteritems(self.fields): if isinstance(field.widget, RelatedUserWidget): field.widget.local_site_name = local_site_name self._patch_field_local_site_queryset(field, local_site)