0

we need to get Site collection/Webs under tenant in a Webpart Here is the TSX file code

import * as React from 'react';
import styles from './GetSpWebs.module.scss';
import { IGetSpWebsProps } from './IGetSpWebsProps';
//import { escape } from '@microsoft/sp-lodash-subset';
import * as jquery from 'jquery';

export interface IRGetSpWebsWPState { getlistitems: [{ "Title": "" }] }

export default class GetSpWebs extends React.Component<IGetSpWebsProps, IRGetSpWebsWPState> {
  static siteurl: string = "";

  public constructor(props: IGetSpWebsProps, state: IRGetSpWebsWPState) {
    super(props);
    this.state = {
      getlistitems: [{ "Title": "" }]
    };
    GetSpWebs.siteurl = this.props.webSiteUrl;
  }

  public componentDidMount() {
    let reactcontexthandler = this;
    jquery.ajax({
      url: `https://4spq36-admin.sharepoint.com/_api/web/lists/getbytitle('DO_NOT_DELETE_SPLIST_TENANTADMIN_AGGREGATED_SITECOLLECTIONS')/items?$select=Title,SiteUrl`,
      type: "GET",
      headers: { 'Accept': 'application/json; odata =verbose;' },
      success: function (resultData) {
        reactcontexthandler.setState({

          getlistitems: resultData.d.results
        });
      },
      error: function (jqXHR, textStatus, errorthrown) {

      }
    });
  }

  public render(): React.ReactElement<IGetSpWebsProps> {

    return (
      <div>
        <h3 className={styles.h3}>SiteCollection webs List Using ReactJS</h3>

        <table className={styles.table}>
          <tr>
            <th className={styles.th}>{"Title"}</th>
          </tr>
          {
            this.state.getlistitems.map(function (listitem, listitemkey) {
              return (
                <tr>
                  <td className={styles.td}> {listitem.Title}</td>
                </tr>
              )
            })
          }</table>
      </div>
    );
  }
}
     

the above piece of code shows 403 error please find the screen shot below enter image description here

If i try to access the REST API in the browser its working fine i can able to get required data, but i use the same API in the SPFX webpart which is showing 403 status error. Any assistance please ?

3 Answers 3

0
  /***** This is Webpart.tsx file *****/  
import * as React from 'react';
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
import { WebPartContext } from '@microsoft/sp-webpart-base';
/*** use the context in this file ****/
export interface IExpProps {
  context: WebPartContext;

}
export default class Exp extends React.Component<IExpProps, any> {
  constructor(props: any) {
    super(props)
    this.state = {
      Sites: [],
    }
  }
  componentDidMount(): void {
    debugger;
    const webUrl: string = this.props.context.pageContext.site.absoluteUrl
    const requestUrl = `${webUrl}/_api/search/query?querytext='contentclass:STS_Site contentclass:STS_Web'&selectproperties='Title,Path'&rowlimit=500
    `;
    let siteUrl :any=[]
    this.props.context.spHttpClient.get(requestUrl, SPHttpClient.configurations.v1)
      .then((response: SPHttpClientResponse) => {
        if (response.ok) {
          response.json().then((data) => {
            const sites: any[] = data.PrimaryQueryResult.RelevantResults.Table.Rows
            if (sites){
            for (let i = 0; i <= sites.length; i++) {
              const url=sites[i].Cells.find((cell: any) => cell.Key === 'Path').Value
             console.log("sites and Subsites",url)
             siteUrl.push({
              Url:url
             })
             this.setState({Sites:siteUrl})
            }
          }
            console.log(this.state.Sites)

          });
        } else {
          console.log(`Error: ${response.statusText}`);
        }
      })
      .catch((error: any) => {
        console.log(`Error: ${error}`);
      });
  }
  public render(): React.ReactElement<IExpProps> {
    return (
      <div>Sites:
        <ol>
          {this.state.Sites.map((items:any) => {
            return [
              <li>{items.Url}</li>
            ]
          })}
        </ol>
      </div>
    );
  }
}
/******* This is webpart.ts file *****/
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
  IPropertyPaneConfiguration,
  PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { IReadonlyTheme } from '@microsoft/sp-component-base';

import * as strings from 'ExpWebPartStrings';
import Exp from './components/Exp';
import { IExpProps } from './components/IExpProps';

export interface IExpWebPartProps {
  description: string;
}

export default class ExpWebPart extends BaseClientSideWebPart<IExpWebPartProps> {

  private _isDarkTheme: boolean = false;
  private _environmentMessage: string = '';

  public render(): void {
    const element: React.ReactElement<IExpProps> = React.createElement(
      Exp,
      {
        description: this.properties.description,
        isDarkTheme: this._isDarkTheme,
        environmentMessage: this._environmentMessage,
        hasTeamsContext: !!this.context.sdks.microsoftTeams,
        userDisplayName: this.context.pageContext.user.displayName,
        context:this.context /**** Mention the context here ***/
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected onInit(): Promise<void> {
    return this._getEnvironmentMessage().then(message => {
      this._environmentMessage = message;
    });
  }



  private _getEnvironmentMessage(): Promise<string> {
    if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook
      return this.context.sdks.microsoftTeams.teamsJs.app.getContext()
        .then(context => {
          let environmentMessage: string = '';
          switch (context.app.host.name) {
            case 'Office': // running in Office
              environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
              break;
            case 'Outlook': // running in Outlook
              environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
              break;
            case 'Teams': // running in Teams
              environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
              break;
            default:
              throw new Error('Unknown host');
          }

          return environmentMessage;
        });
    }

    return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
  }

  protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
    if (!currentTheme) {
      return;
    }

    this._isDarkTheme = !!currentTheme.isInverted;
    const {
      semanticColors
    } = currentTheme;

    if (semanticColors) {
      this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
      this.domElement.style.setProperty('--link', semanticColors.link || null);
      this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
    }

  }

  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

  protected get dataVersion(): Version {
    return Version.parse('1.0');
  }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPaneTextField('description', {
                  label: strings.DescriptionFieldLabel
                })
              ]
            }
          ]
        }
      ]
    };
  }
}
/******* This is Props.ts file *****/
import { WebPartContext } from "@microsoft/sp-webpart-base";

export interface IExpProps {
  description: string;
  isDarkTheme: boolean;
  environmentMessage: string;
  hasTeamsContext: boolean;
  userDisplayName: string;
  context:WebPartContext; /****  Declare here context *****/
}
2
  • Really appreciated, Solution is working fine. this is code is really helps me a lot, thanks for help Commented Aug 3, 2023 at 9:22
  • Hi @Vaishnavi, is there a way to loop subsites as well? means what if Site collection has subSites ? Is there way to loop subsites under a siteCollection ? Commented Aug 4, 2023 at 8:19
0

I would consider using the Graph API to return all sites in the tenant:

List sites

Here is some additional information on consuming the Microsoft Graph API from SPFx:

Consume the Microsoft Graph in the SharePoint Framework

2
  • May be but bit odds to me any way please help me if there is any link on how to call Graph API call from SPFX please assist me. Commented Jul 28, 2023 at 10:47
  • I've updated the answer. Please remember to upvote/mark as answer where appropriate to help the community. Commented Jul 28, 2023 at 12:11
0

Try using this code:

import * as React from 'react';
import { IWebPartContext } from '@microsoft/sp-webpart-base';
import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions } from '@microsoft/sp-http';

export interface ISiteCollection {
  siteUrl: string;
  siteTitle: string;
}

export interface ISiteCollectionsWebPartProps {
  context: IWebPartContext;
}

export default class SiteCollectionsWebPart extends React.Component<ISiteCollectionsWebPartProps, any> {
  constructor(props: ISiteCollectionsWebPartProps) {
    super(props);
    this.state = {
      siteCollections: []
    };
  }

  public componentDidMount(): void {
    this.getSiteCollections();
  }

  private getSiteCollections(): void {
    const endpointUrl = `${this.props.context.pageContext.web.absoluteUrl}/_api/search/query?querytext='contentclass:STS_Site'`;

    this.props.context.spHttpClient.get(endpointUrl, SPHttpClient.configurations.v1)
      .then((response: SPHttpClientResponse) => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error(`Error ${response.status}: ${response.statusText}`);
        }
      })
      .then(data => {
        if (data && data.PrimaryQueryResult && data.PrimaryQueryResult.RelevantResults &&
          data.PrimaryQueryResult.RelevantResults.Table && data.PrimaryQueryResult.RelevantResults.Table.Rows) {
          const siteCollections: ISiteCollection[] = data.PrimaryQueryResult.RelevantResults.Table.Rows.map(row => {
            const siteUrl = row.Cells.find(cell => cell.Key === 'Path').Value;
            const siteTitle = row.Cells.find(cell => cell.Key === 'Title').Value;
            return { siteUrl, siteTitle };
          });
          this.setState({ siteCollections });
        }
      })
      .catch(error => {
        console.log('Error fetching site collections:', error);
      });
  }

  public render(): React.ReactElement<ISiteCollectionsWebPartProps> {
    const { siteCollections } = this.state;

    return (
      <div>
        <h1>Site Collections</h1>
        <ul>
          {siteCollections.map((site: ISiteCollection) => (
            <li key={site.siteUrl}>
              <a href={site.siteUrl}>{site.siteTitle}</a>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}
2
  • Hi @Vinothkumar Really appreciate for piece of code Can I request you to update code in-between ``` your code ``` this will align properly Commented Jul 31, 2023 at 2:24
  • Hi @Vinoth the code which you posted also working fine, but there is an error from my solution that is at ISiteCollection[] and row => and cell =>, after I carefully these are very minor issues, but after compare with above solution both are working fine. thanks for help Commented Aug 3, 2023 at 9:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.