Update Profile Picture Using Graph API In SPFx

Introduction

 
Microsoft 365 has a different application in its suite. To combine all these data for developers Microsoft came up with Graph API. This has helped developers to access data across applications in Microsoft 365. One such example which we would leverage today is to save profile picture which would reflect across all the applications in Microsoft 365.
 
In this article, we will create a SPFx webpart where user can select their profile picture and on click of the save button, the profile picture will be updated for all apps in Microsoft 365.
 
Step 1 - Create a SPFx Solution
 
To create SPFx solution, use the below command:
 
yo @microsoft/sharepoint
 
Provide values for different questions which are asked while creating the solution as displayed in the image below:
 
Update Profile Picture using Graph API SPFx
 
Step 2 - Install fluent UI
 
To use the icon for the file selection and deleting the file, we use the fluent UI icon. Use the below command to install it.
 
npm install @fluentui/react --save
 
Step 3 - Add Graph API Scopes
 
Open the solution in your choice of code editor.

Open the file name package-solution.json which would be present inside the config folder.
 
Add the below code after the line "isDomainIsolated": true:
  1. "webApiPermissionRequests": [    
  2.    {    
  3.       "resource""Microsoft Graph",    
  4.       "scope""User.ReadWrite"    
  5.    }    
  6. ]    
Step 4 - Add the Code for Uploading the Image as a Profile Picture
 
To start updating the code, we are required to access context in our component. To do so, we need to update two files:
  1. UpdateProfilePictureWebPart.ts
  2. IUpdateProfilePictureProps.ts
Below is the code update that we need to do in the above-mentioned files:
 
In IUpdateProfilePictureProps.ts, replace the below code in the file:
  1. import { WebPartContext } from "@microsoft/sp-webpart-base";    
  2.     
  3. export interface IUpdateProfilePictureProps {    
  4.    description: string;    
  5.    context:WebPartContext;    
  6. }   
In UpdateProfilePictureWebPart.ts, update the render method pass additional parameter named context to UpdateProfilePicture component. Hence, the final render method will look as mentioned below:
  1. public render(): void {  
  2.     const element: React.ReactElement < IUpdateProfilePictureProps > = React.createElement(UpdateProfilePicture, {  
  3.         description: this.properties.description,  
  4.         context: this.context  
  5.     });  
  6.     ReactDom.render(element, this.domElement);  
  7. }  
Now let us update the component. To update the component, we need to update the file named UpdateProfilePicture.tsx.
 
Replace it with the below code. In the below code we are using:
  • HTML file picker which is hidden but is called in click on the icon button
  • On Selection of the file, we would display a delete icon and display the selected image as with the help of URL.createObjectURL() which would help us get the blob value of the file
  • On a click of "Save as Profile Picture," we are uploading the file with the help of MSGraphClient
For displaying a success or error message in the dialog box:
  1. import * as React from 'react';  
  2. import styles from './UpdateProfilePicture.module.scss';  
  3. import { IUpdateProfilePictureProps } from './IUpdateProfilePictureProps';  
  4. import { IconButton, PrimaryButton } from '@fluentui/react/lib/Button';  
  5. import { MSGraphClient } from "@microsoft/sp-http";  
  6. import { Dialog, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';  
  7.   
  8. export interface IUpdateProfilePictureState {  
  9.   imageFile: FileList;  
  10.   open: boolean;  
  11.   savedMessage: string;  
  12.   savedMessageTitle: string;  
  13. }  
  14.   
  15. export default class UpdateProfilePicture extends React.Component<IUpdateProfilePictureProps, IUpdateProfilePictureState> {  
  16.   
  17.   constructor(props: IUpdateProfilePictureProps) {  
  18.     super(props);  
  19.     this.state = {  
  20.       imageFile: null,  
  21.       open: true,  
  22.       savedMessage: "",  
  23.       savedMessageTitle: "",  
  24.     };  
  25.   }  
  26.   
  27.   
  28.   private saveAsProfile = () => {  
  29.     var reader = new FileReader();  
  30.     var tempfile = this.state.imageFile[0];  
  31.     this.props.context.msGraphClientFactory  
  32.       .getClient().then((client: MSGraphClient) => {  
  33.         client  
  34.           .api("me/photo/$value")  
  35.           .version("v1.0").header("Content-Type", tempfile.type).put(tempfile, (err, res) => {  
  36.             if (!err) {  
  37.               this.setState({  
  38.                 open: false, savedMessage: "Your profile picture is updated",  
  39.                 savedMessageTitle: "Profile Picture Saved"  
  40.               });  
  41.             } else {  
  42.               this.setState({  
  43.                 open: false, savedMessage: err.message,  
  44.                 savedMessageTitle: "Error while saving Profile Picture"  
  45.               });  
  46.             }  
  47.           });  
  48.       });  
  49.   }  
  50.   
  51.   private closeDialog = () => {  
  52.     this.setState({ open: true });  
  53.   }  
  54.   
  55.   
  56.   public render(): React.ReactElement<IUpdateProfilePictureProps> {  
  57.     return (  
  58.       <div className={styles.updateProfilePicture}>  
  59.         <div className={styles.container}>  
  60.           <div className={styles.row}>  
  61.             <label>Select File</label>  
  62.             <input id="fileUpload" type="file" accept="image/*" style={{ display: "none" }}  
  63.               onChange={(ev) => {  
  64.                 console.log(ev.target.files);  
  65.                 if (ev.target.files.length > 0) {  
  66.                   this.setState({ imageFile: ev.target.files });  
  67.                 } else {  
  68.                   this.setState({ imageFile: null });  
  69.                 }  
  70.               }}></input>  
  71.             <IconButton iconProps={{ iconName: 'PictureFill' }} title="PictureFill" ariaLabel="PictureFill"  
  72.               style={{ display: this.state.imageFile ? "none" : "block" }}  
  73.               onClick={() => {  
  74.                 document.getElementById('fileUpload').click();  
  75.               }} />  
  76.           </div>  
  77.           {this.state.imageFile && <div className={styles.row}>  
  78.             <div style={{  
  79.               height: 250, width: 250,  
  80.               display: "inline-block",  
  81.               backgroundSize: "Cover", backgroundRepeat: "no-repeat",  
  82.               backgroundPosition: "center",  
  83.               backgroundImage: `url("${URL.createObjectURL(this.state.imageFile[0])}")`  
  84.             }}></div>  
  85.             <IconButton iconProps={{ iconName: 'Delete' }} title="Delete" ariaLabel="Delete"  
  86.               onClick={() => {  
  87.                 this.setState({ imageFile: null });  
  88.               }} />  
  89.             <div className={styles.row}>  
  90.               <PrimaryButton text="Save as Profile Picture" onClick={() => { this.saveAsProfile(); }} allowDisabledFocus />  
  91.             </div>  
  92.           </div>}  
  93.           <Dialog  
  94.             hidden={this.state.open}  
  95.             onDismiss={this.closeDialog}  
  96.             dialogContentProps={{ title: this.state.savedMessageTitle, subText: this.state.savedMessage }}  
  97.           >  
  98.             <DialogFooter>  
  99.               <PrimaryButton onClick={this.closeDialog} text="close" />  
  100.             </DialogFooter>  
  101.           </Dialog>  
  102.         </div>  
  103.       </div>  
  104.     );  
  105.   }  
  106. }   
Step 5 - Approve the Graph Request in API Management
 
To use Graph API, we need to approve the scope that we are using from SharePoint Admin Portal to do so.
  1. Navigate to SharePoint Online Admin Portal
  2. On the left Navigation open Advance section and click on API Access.
Click on Approve, as displayed in the image below:
 
 
Outcome
 
 

Conclusion

 
To update images across all the applications in Microsoft 365, graph API comes to rescue and we can create an a SPFx webpart which can be accessed by all the users in the organization and update their profile picture.