Question

My organization is currently using the https://github.com/pnp/sp-dev-fx-webparts (React-Script-Editor) webpart on several of our SharePoint online site collections to enable the functionality we had on-premise. Since the SharePoint online user profiles do not contain all the information that our on-prem profiles did I looked into the Graph API to be able to bring back some of that information.

I managed to create a webpart that uses the /me endpoint to bring back user data and inserts it in a javascript variable that's available to the page once it's loaded.

What I'm trying to do now is be able to create a javascript object or function that is available to the page that when passed in a user email will call the graph api endpoint for

https://graph.microsoft.com/beta/users('emailgoeshere@wherever.something')

and then render that to a variable on the page. I know I can hardcode a function into the webpart render method and pull back user data that way, but I'd like to be able to pass a value to a function and call the endpoint at will.

Currently on page load, you can call the myGraphMe and myGraphMeBeta variables to get user info. I'm hoping for something like

userInfo = GetUserInfoFor('someemail@org.com')

I'm just not sure if you can expose a function like that.

I'll provide my code that works for my current solution so far, but for some reason I'm drawing a blank when trying to figure out how to accomplish the second part. Any thoughts or help would be greatly appreciated.

import * as React from 'react';
import { MSGraphClient } from "@microsoft/sp-http";
import styles from './GraphUserInfo.module.scss';
import { IGraphUserInfoProps } from './IGraphUserInfoProps';
import { IGraphUserInfoState } from './IGraphUserInfoState';

export default class GraphUserInfo extends React.Component<IGraphUserInfoProps, IGraphUserInfoState> {
  constructor(props: IGraphUserInfoProps, state: IGraphUserInfoState){
     super(props);
     this.state = {
       me: ""
     };
  }

  public componentDidMount(){
    const script = document.createElement("script");
    script.id = "myGraphMeBetaTag";
    this._getMyPropertiesBeta.then(res=>{  
        script.innerHTML = "var myGraphMeBeta = " +res + ";";
        this._getMyProperties.then(res1 =>{
          script.innerHTML += "var myGraphMe = " + res1 + ";";
          document.body.appendChild(script);    
        }).catch(e => console.log(e));
        
    }).catch(e => console.log(e));
  }

  public render(): React.ReactElement<IGraphUserInfoProps> {
    return (
      this.props.isEditMode? 
      <div className={ styles.graphUserInfo }>
        <div className={ styles.container }>
          <div className={styles.row}>
            <span>
              EDIT MODE Please look for a script tag with the ID of "myGraphMe" &amp; "myGraphMeBeta" at the end of the body.
            </span>
          </div>
        </div> 
      </div>
      : 
      <div style={{display:'none'}}></div>
    );
  }
  private _getMyPropertiesBeta = new Promise<string>((resolve,reject) =>{
      console.log("Getting my user Properties");
      this.props.context.msGraphClientFactory
         .getClient()
         .then((client: MSGraphClient) => {
            client
               .api("https://graph.microsoft.com/beta/me")
               .get((err,res) =>{
                   if(err){
                     //console.log(err);
                     reject(JSON.stringify(err));
                     return; 
                   }
                   //console.log(res);
                   resolve(JSON.stringify(res));
               });
         });
    });
    private _getMyProperties = new Promise<string>((resolve,reject) =>{
      console.log("Getting my user Properties");
      this.props.context.msGraphClientFactory
         .getClient()
         .then((client: MSGraphClient) => {
            client
               .api("https://graph.microsoft.com/v1.0/me")
               .get((err,res) =>{
                   if(err){
                     //console.log(err);
                     reject(JSON.stringify(err));
                     return; 
                   }
                   //console.log(res);
                   resolve(JSON.stringify(res));
               });
         });
    });    
}
Was it helpful?

Solution

I found an answer to my question that works for what I am trying to accomplish. I dont know if this is the "best" way to handle it but it was the simplest.

declare global{
  interface Window{ _$GraphUserInfo : any;}
}

Then in my constructor

export default class GraphUserInfo extends React.Component<IGraphUserInfoProps, IGraphUserInfoState> {
  constructor(props: IGraphUserInfoProps, state: IGraphUserInfoState){
     super(props);
     window._$GraphUserInfo = this;
     this.state = {
       me: ""
     };     
  }

This allows me to call not only my 2 new functions but also the original functions without the need to drop a script tag on the page.

    public _getOtherUserPropertiesBeta = (userEmail)=>{
      return new Promise<string>((resolve,reject)=>{
        //console.log(`Getting user properties for ${userEmail}`);
        this.props.context.msGraphClientFactory
          .getClient()
          .then((client: MSGraphClient)=>{
            client
              .api(`https://graph.microsoft.com/beta/users('${userEmail}')`)
              .get((err,res)=>{
                if(err){
                  reject(JSON.stringify(err));
                  return;
                }
                resolve(JSON.stringify(res));
              });
          })
      })
    }

I'm able to access them easily now using

let userProps = await window._$GraphUserInfo._getOtherUserPropertiesBeta('email@email.com')

Again, I'm not sure this is the "best" way to approach the problem, but it does work.

OTHER TIPS

This Answer is primarily directed to users who come upon this post sometime in the future since @Brad Fortner appears to have already solved his problem.

When attempting to retrieve user info from SP using free form JS in this webpart use the ‘Enable classic _spPageContextInfo’ option in the web part’s configuration in the page’s edit mode.

Instructions:

After building and deploying the web part into your environment, add the webpart to the page and simply click on the web part. To the left of the web part the web part’s options will appear in a grey vertical rectangle. Click on the pencil icon that appears within that grey, vertical rectangle. Doing so triggers the display of the web part’s properties pane on the right hand side of the screen - just like any other modern web part.

On the property pane are three fields:

  1. The webpart’s ‘Title to show in Edit Mode’, which is a text field,

  2. The ‘Remove top/bottom padding of web part container’ toggle button (this just enables some CSS overrides built into the web part). It does what it says.

  3. And, finally the option that you need to toggle to ‘on’: The ‘Enable classic _spPageContextInfo’ toggle button. Toggle this button so it is on. (It is toggled to ‘off’ by default.)

By toggling this button to ‘on’, the ‘classic’ Sharepoint global window variable named _spPageContextInfo will be accessible to your scripts - all scripts on the page.

People other than the person who wrote this question may ask: Of what value is this variable to me? If you have had more than a little JS development in your environment, you are probably familiar with this variable and have parsed it.

The variable contains nearly all the relevant information you may need about the current user viewing the page! If you don’t believe me see here.

Enabling this variable via this web part permits you to use legacy, ‘classic’ SharePoint scripts in the script editor, so you don’t have to reinvent the wheel when your organization moves to the modern SharePoint experience.

Please up vote as answer if this post helped you!

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top