1

I am working on a project to build a portal inside SharePoint online. Now i got this requirement:-

  • To build a quick links web part (similar to the built-in modern Quick Links)
  • But this quick links can contain links to internal applications that can only be accessed using VPN.
  • So if the user access the SharePoint online home page without VPN >> the quick links should hide all the links which the user does not have access to.
  • Also if the user access the SharePoint using VPN and the user does not have permission to the internal system (the user will get http 401 or will be prompted with a dialog to enter username and password) to hide the links as well.

So in other words, the SPFx web part need to send a request to the link url, and if it get http code other than "2xx success" to hide the link.

Can anyone advice how to build such a web part please?

Thanks

Now i tried to benefit from this SPFx web part @ https://github.com/clarktozer/spfx-quicklinks . where this is the QuickLinksWebPart.ts:-

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
  BaseClientSideWebPart,
  IPropertyPaneConfiguration,
  PropertyPaneDropdown,
  PropertyPaneCheckbox} from '@microsoft/sp-webpart-base';

import * as strings from 'QuickLinksWebPartStrings';
import QuickLinks from './components/QuickLinks';
import { IQuickLinksProps } from './components/IQuickLinksProps';
import { PropertyPaneLinksList } from '../../controls/PropertyPaneLinksList/PropertyPaneLinksList';
import { PropertyFieldColorPicker, PropertyFieldColorPickerStyle } from '@pnp/spfx-property-controls/lib/PropertyFieldColorPicker';
import { Link } from '../../controls/PropertyPaneLinksList/components/ILinksListState';

export interface IQuickLinksWebPartProps {
  title: string;
  type: LinkType;
  iconColor: string;
  openInNewTab?: boolean;
  fontColor: string;
  initLinks: string[];
  links: Link[];
}

export enum LinkType {
  LINK = "Link",
  FILE = "File"
}
export default class QuickLinksWebPart extends BaseClientSideWebPart<IQuickLinksWebPartProps> {

  public render(): void {
    const element: React.ReactElement<IQuickLinksProps> = React.createElement(
      QuickLinks,
      {
        title: this.properties.title,
        type: this.properties.type,
        iconColor: this.properties.iconColor,
        fontColor: this.properties.fontColor,
        openInNewTab: this.properties.openInNewTab,
        links: this.properties.links != null ? this.properties.links : [],
        displayMode: this.displayMode,
        updateProperty: (value: string) => {
          this.properties.title = value;
        }
      }
    );

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

  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: [
                PropertyPaneDropdown('type', {
                  label: strings.LinkType,
                  options: Object.keys(LinkType).map((e) => {
                    return {
                      key: LinkType[e], text: LinkType[e]
                    };
                  }),
                  selectedKey: 'link'
                }),
                PropertyPaneCheckbox('openInNewTab', {
                  text: strings.OpenInNewTab
                })
              ]
            },
            {
              groupName: strings.StylingGroup,
              groupFields: [
                PropertyFieldColorPicker('iconColor', {
                  label: strings.IconColor,
                  selectedColor: this.properties.iconColor,
                  onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
                  properties: this.properties,
                  disabled: false,
                  alphaSliderHidden: false,
                  style: PropertyFieldColorPickerStyle.Inline,
                  key: 'iconColor'
                }),
                PropertyFieldColorPicker('fontColor', {
                  label: strings.FontColor,
                  selectedColor: this.properties.fontColor,
                  onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
                  properties: this.properties,
                  disabled: false,
                  alphaSliderHidden: false,
                  style: PropertyFieldColorPickerStyle.Inline,
                  key: 'fontColor'
                })
              ]
            },
            {
              groupName: strings.LinksGroup,
              groupFields: [
                new PropertyPaneLinksList("links", {
                  key: "links",
                  links: this.properties.links
                })
              ]
            }
          ]
        }
      ]
    };
  }
}

and this is the QuikcLinks.tsx :-

import * as React from 'react';
import styles from './QuickLinks.module.scss';
import { IQuickLinksProps } from './IQuickLinksProps';
import { autobind } from '@uifabric/utilities';
import { LinkType } from '../QuickLinksWebPart';
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import Radium from 'radium';
import * as tinycolor from 'tinycolor2';

@Radium
export default class QuickLinks extends React.Component<IQuickLinksProps, {}> {
  private inlineStyles: any;
  constructor(props: IQuickLinksProps) {
    super(props);
  }

  @autobind
  private createLinkStyle(hoverColor) {
    return {
      color: this.props.fontColor,
      ':hover': {
        color: tinycolor(hoverColor).darken(25).toString()
      }
    };
  }

  @autobind
  public getIcon() {
    let icon = "";
    switch (this.props.type) {
      case LinkType.FILE:
        icon = "OpenFile";
        break;
      default:
        icon = "Link";
    }
    return icon;
  }

  public render(): React.ReactElement<IQuickLinksProps> {
    this.inlineStyles = {
      link: this.createLinkStyle(this.props.fontColor)
    };
    return (
      <div className={"ms-Grid " + styles.quickLinks}>
        <div>
          <div className="ms-Grid-row">
            <div className="ms-Grid-col ms-sm12">
              <WebPartTitle displayMode={this.props.displayMode}
                title={this.props.title}
                updateProperty={this.props.updateProperty} />
              {
                this.props.links.map((e, i) => {
                  let linkProps = {
                    key: e.key,
                    href: e.value
                  };
                  if (this.props.openInNewTab) {
                    linkProps["target"] = "_blank";
                  }
                  return <div className={styles.linkRow} key={this.props.type + "-link-" + i}>
                    <i style={{ color: this.props.iconColor }} className={styles.quickLinkIcon + " ms-Icon ms-Icon--" + this.getIcon()} aria-hidden="true"></i>
                    <a className={styles.link} {...linkProps} style={this.inlineStyles.link}>{e.label}</a>
                  </div>;
                })
              }
            </div>
          </div>
        </div>
      </div>
    );
  }
}

So where i need to do the call to the URL and check the response so i can render the link or hide it accordingly ?

1 Answer 1

2

I think you answered your own question...

...the SPFx web part need to send a request to the link url, and if it get http code other than "2xx success" to hide the link.

So, make a web part where

  • All the links are initially hidden
  • When the webpart loads (componentDidMount maybe?), send out requests to all the URLs
  • Depending on the response codes, either keep the links hidden, or show them if the user has access

You may run into CORS issues, though... but if they are all internal systems, maybe you can work with the owners of those systems to allow CORS requests from your organization's SP Online tenant.


After OP added code and this question:

So where i need to do the call to the URL and check the response so i can render the link or hide it accordingly ?

So that depends.

One thing you could do would be to add a componentDidMount to that QuickLinks component and make the calls from there, in which case you would also need to add something to that component's state to prevent the this.props.links.map() from running until after all the results are in so you know which links to render or not.

Or, if you were to do what I suggested in the comments and make each of the links a self-contained component that took care of its own call for its URL, you would need to pull all of that HTML that the this.props.links.map() function returns out and put it in it's own component, and then make the map:

this.props.links.map((linkInfo, idx) => {
    return <SelfContainedLink linkInfo={linkInfo} key={this.props.type + "-link-" + idx} />
});

And then in the SelfContainedLink component, add the componentDidMount to make the call based on props.linkInfo.value, and the state to control whether it renders an actual link or not.

6
  • I do not want to show or hide all links, this need to be done per link ... also can you post some code or links which can help me with this ?
    – John John
    Commented Nov 23, 2022 at 21:31
  • 1
    Right, so you would send out several requests, one per link, to the URL for that link, and then deal with the individual responses as appropriate to show or keep hidden the link that they represent. Commented Nov 23, 2022 at 22:14
  • ok do you have a sample code or Spfx which can help me in developing such SPFx?
    – John John
    Commented Nov 23, 2022 at 22:20
  • 1
    No, I don't, but I have a suggestion - do you use React when you make your SPFx webparts? This kind of lends itself to that, since you could have a "self hiding link" component that takes a URL as a prop, and have each of those individual components make their requests and handle showing or hiding the actual link. Then you wouldn't have to keep track of which request matches which link from a higher level, it would all be self contained. Then have a parent component that has a list of "self-hiding link" components. Commented Nov 23, 2022 at 22:23
  • yes all the SPFx web parts i developed are React based
    – John John
    Commented Nov 23, 2022 at 22:52

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.