import json import os from typing import TYPE_CHECKING, Optional, Iterable, IO from .base_object import BaseObject from ..pagination.limit_offset_based_object_collection import LimitOffsetBasedObjectCollection from ..pagination.marker_based_object_collection import MarkerBasedObjectCollection from ..util.api_call_decorator import api_call if TYPE_CHECKING: from boxsdk.object.email_alias import EmailAlias from boxsdk.pagination.box_object_collection import BoxObjectCollection from boxsdk.object.folder import Folder from boxsdk.object.storage_policy_assignment import StoragePolicyAssignment class User(BaseObject): """Represents a Box user.""" _item_type = 'user' @api_call def add_email_alias(self, email: str) -> 'EmailAlias': """ Adds a new email alias to the given user's account. :param email: The email alias to add to the user. :returns: The new email alias object """ url = self.get_url('email_aliases') body = { 'email': email, } response = self._session.post(url, data=json.dumps(body)).json() return self.translator.translate( session=self._session, response_object=response, ) @api_call def get_email_aliases(self, limit: Optional[int] = None, fields: Iterable[str] = None) -> 'BoxObjectCollection': """ Gets an list of email aliases for a user. :param limit: The maximum number of users to return. If not specified, the Box API will determine an appropriate limit. :param fields: List of fields to request :returns: An iterator of the user's email aliases """ return MarkerBasedObjectCollection( session=self._session, url=self.get_url('email_aliases'), limit=limit, marker=None, fields=fields, return_full_pages=False, ) @api_call def remove_email_alias(self, email_alias: 'EmailAlias') -> bool: """ Remove an email alias from the user. :param email_alias: The email alias to remove. :returns: Whether the removal succeeded. """ url = self.get_url('email_aliases', email_alias.object_id) response = self._session.delete(url, expect_json_response=False) return response.ok @api_call def transfer_content( self, destination_user: 'User', notify: Optional[bool] = None, fields: Iterable[str] = None ) -> 'Folder': """ Move all of the items owned by a user into a new folder in another user's account. :param destination_user: The id of the user to transfer content to. :param notify: Whether the destination user should receive email notification of the transfer. :param fields: Fields to return on the resulting :class:`Folder` object :returns: A :class:`Folder` object that was transferred to another user. """ url = self.get_url('folders', '0') body = { 'owned_by': { 'id': destination_user.object_id, }, } params = {} if notify is not None: params['notify'] = notify if fields is not None: params['fields'] = ','.join(fields) response = self._session.put(url, data=json.dumps(body), params=params).json() return self.translator.translate( session=self._session, response_object=response, ) def get_storage_policy_assignment(self) -> 'StoragePolicyAssignment': """ Get the storage policy assignment assigned to the user. :returns: The :class:`StoragePolicyAssignment` object information """ url = self._session.get_url('storage_policy_assignments') additional_params = { 'resolved_for_type': self.object_type, 'resolved_for_id': self.object_id, } box_response = self._session.get(url, params=additional_params) response = box_response.json()['entries'][0] return self.translator.translate( session=self._session, response_object=response, ) @api_call def get_group_memberships( self, limit: Optional[int] = None, offset: Optional[int] = None, fields: Iterable[str] = None ) -> 'BoxObjectCollection': """ Get the entries in the user group membership using limit-offset paging. :param limit: The maximum number of entries to return per page. If not specified, then will use the server-side default. :param offset: The offset of the item at which to begin the response. :param fields: List of fields to request. :returns: An iterator of the entries in the groups """ additional_params = {} if fields is not None: additional_params['fields'] = ','.join(fields) return LimitOffsetBasedObjectCollection( session=self._session, url=self.get_url('memberships'), additional_params=additional_params, limit=limit, offset=offset, return_full_pages=False, ) @api_call def get_avatar(self) -> bytes: """ Get the avatar for the User. :returns: Avatar content as bytes. """ url = self.get_url('avatar') response = self._session.get(url, expect_json_response=False) return response.content @api_call def upload_avatar(self, image_path: str) -> str: """ Upload avatar image to user account. Supported formats are JPG, JPEG and PNG. Maximum allowed file size is 1MB and resolution 1024x1024 pixels. :param image_path: Path of the avatar image to upload :return: URLs to existing user avatars that were updated """ with open(image_path, 'rb') as image_stream: image_extension = os.path.splitext(image_path)[-1][1:] return self.upload_avatar_stream(image_stream=image_stream, image_extension=image_extension) @api_call def upload_avatar_stream(self, image_stream: IO[bytes], image_extension: str) -> str: """ Upload avatar image to user account. Supported formats are JPG, JPEG and PNG. Maximum allowed file size is 1MB and resolution 1024x1024 pixels. :param image_extension: Extension of the image. Supported: 'jpg', 'jpeg' and 'png'. :param image_stream: The file-like object containing the bytes of the image :return: URLs to existing user avatars that were updated """ url = self.get_url('avatar') files = {'pic': (f'avatar.{image_extension}', image_stream, f'image/{image_extension}')} response = self._session.post(url, files=files) return response.json()['pic_urls'] @api_call def delete_avatar(self) -> bool: """ Remove avatar from user account :return: Whether the deletion succeeded """ url = self.get_url('avatar') response = self._session.delete(url) return response.ok @api_call def delete(self, *, notify: bool = True, force: bool = False, **kwargs) -> bool: # pylint: disable=arguments-differ,arguments-renamed """ Delete a user's account. This user will no longer be able to access Box. :param notify: Whether a notification should be sent about the deletion :param force: Whether the user should be deleted even if they still own files :returns: Whether the deletion succeeded """ params = { 'notify': notify, 'force': force, } return super().delete(params=params, **kwargs)