diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a2b714d..a8d69ec1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Releases +## 1.4.0 + +**New Controls** + +- `SecurityTrimmedControl` control got added [#74](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/74) + +**Enhancements** + +- Allow the `TaxonomyPicker` to also be used in Application Customizer [#77](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/77) +- Add `npm postinstall` script to automatically add the locale config [#78](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/78) + +**Fixes** + +- Icon not showing up in the `Placeholder` control [#76](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/76) + ## 1.3.0 **New Controls** diff --git a/docs/documentation/docs/about/release-notes.md b/docs/documentation/docs/about/release-notes.md index b5a2b714d..a8d69ec1c 100644 --- a/docs/documentation/docs/about/release-notes.md +++ b/docs/documentation/docs/about/release-notes.md @@ -1,5 +1,20 @@ # Releases +## 1.4.0 + +**New Controls** + +- `SecurityTrimmedControl` control got added [#74](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/74) + +**Enhancements** + +- Allow the `TaxonomyPicker` to also be used in Application Customizer [#77](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/77) +- Add `npm postinstall` script to automatically add the locale config [#78](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/78) + +**Fixes** + +- Icon not showing up in the `Placeholder` control [#76](https://github.com/SharePoint/sp-dev-fx-controls-react/issues/76) + ## 1.3.0 **New Controls** diff --git a/docs/documentation/docs/assets/ListPicker-initial.png b/docs/documentation/docs/assets/ListPicker-initial.png new file mode 100644 index 000000000..dc54fda35 Binary files /dev/null and b/docs/documentation/docs/assets/ListPicker-initial.png differ diff --git a/docs/documentation/docs/assets/ListPicker-multi.png b/docs/documentation/docs/assets/ListPicker-multi.png new file mode 100644 index 000000000..f0b06ac7d Binary files /dev/null and b/docs/documentation/docs/assets/ListPicker-multi.png differ diff --git a/docs/documentation/docs/assets/ListPicker-single.png b/docs/documentation/docs/assets/ListPicker-single.png new file mode 100644 index 000000000..06d8afddb Binary files /dev/null and b/docs/documentation/docs/assets/ListPicker-single.png differ diff --git a/docs/documentation/docs/assets/TermPicker-selection.png b/docs/documentation/docs/assets/termPicker-tree-selection.png similarity index 100% rename from docs/documentation/docs/assets/TermPicker-selection.png rename to docs/documentation/docs/assets/termPicker-tree-selection.png diff --git a/docs/documentation/docs/assets/TermPicker-autocomplete.png b/docs/documentation/docs/assets/termpicker-input-autocomplete.png similarity index 100% rename from docs/documentation/docs/assets/TermPicker-autocomplete.png rename to docs/documentation/docs/assets/termpicker-input-autocomplete.png diff --git a/docs/documentation/docs/controls/ListPicker.md b/docs/documentation/docs/controls/ListPicker.md new file mode 100644 index 000000000..51982ff98 --- /dev/null +++ b/docs/documentation/docs/controls/ListPicker.md @@ -0,0 +1,71 @@ +# ListPicker control + +This control allows you to select one or multiple available lists/libraries of the current site. + +Here is an example of the control: + +![ListPicker initial](../assets/ListPicker-initial.png) + +`ListPicker` single selection mode: + +![ListPicker single selection](../assets/ListPicker-single.png) + +`ListPicker` multi-selection mode + +![ListPicker multi selection](../assets/ListPicker-multi.png) + +## How to use this control in your solutions + +- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../#getting-started) page for more information about installing the dependency. +- Import the control into your component: + +```TypeScript +import { ListPicker } from "@pnp/spfx-controls-react/lib/ListPicker"; +``` + +- Use the `ListPicker` control in your code as follows: + +```TypeScript + +``` + +- The `onSelectionChanged` change event returns the list(s) and can be implemented as follows: + +```TypeScript +private onListPickerChange (lists: string | string[]) { + console.log("Lists:", lists); +} +``` + +## Implementation + +The `ListPicker` control can be configured with the following properties: + +| Property | Type | Required | Description | +| ---- | ---- | ---- | ---- | +| context | WebPartContext OR ApplicationCustomizerContext | yes | The context object of the SPFx loaded webpart or customizer. | +| className | string | no | If provided, additional class name to provide on the dropdown element. | +| disabled | boolean | no | Whether or not the control is disabled. | +| baseTemplate | number | no | The SharePoint BaseTemplate ID to filter the list options by. | +| includeHidden | boolean | no | Whether or not to include hidden lists. Default is `true`. | +| orderBy | LibsOrderBy | no | How to order the lists retrieved from SharePoint. | +| selectedList | string OR string[] | no | Keys of the selected item(s). If you provide this, you must maintain selection state by observing onSelectionChanged events and passing a new value in when changed. | +| multiSelect | boolean | no | Optional mode indicates if multi-choice selections is allowed. Default to `false`. | +| label | string | no | Label to use for the control. | +| placeholder | string | no | Placeholder label to show in the dropdown. | +| onSelectionChanged | (newValue: string OR string[]): void | no | Callback function when the selected option changes. | + +Enum `LibsOrderBy` + +| Value | +| ---- | +| Id | +| Title | + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/ListPicker) diff --git a/docs/documentation/docs/controls/SecurityTrimmedControl.md b/docs/documentation/docs/controls/SecurityTrimmedControl.md new file mode 100644 index 000000000..a40bed0b5 --- /dev/null +++ b/docs/documentation/docs/controls/SecurityTrimmedControl.md @@ -0,0 +1,72 @@ +# SecurityTrimmedControl + +This control is intended to be used when you want to show or hide components based on the user its permissions. The control can be used to check the user’s permissions on the current site / list were the solution is loaded, or on a remote site / list. + +## How to use this control in your solutions + +- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../#getting-started) page for more information about installing the dependency. +- Import the following modules to your component: + +```TypeScript +import { SecurityTrimmedControl } from "@pnp/spfx-controls-react/lib/SecurityTrimmedControl"; +``` + +- You can use the `SecurityTrimmedControl` as follows in your solutions: + +**Checking permissions on the current site** + +```jsx + + {/* Specify the components to load when user has the required permissions */} + +``` + +**Checking permissions on the current list** + +```jsx + + {/* Specify the components to load when user has the required permissions */} + +``` + +**Checking permissions on remote site** + +```jsx + + {/* Specify the components to load when user has the required permissions */} + +``` + +**Checking permissions on remote list / library** + +```jsx + + {/* Specify the components to load when user has the required permissions */} + +``` + +## Implementation + +The `SecurityTrimmedControl` can be configured with the following properties: + +| Property | Type | Required | Description | +| ---- | ---- | ---- | ---- | +| context | WebPartContext or ApplicationCustomizerContext or FieldCustomizerContext or ListViewCommandSetContext | yes | Context of the web part, application customizer, field customizer, or list view command set. | +| permissions | SPPermission[] | yes | The permissions to check for the user. | +| level | PermissionLevel | yes | Specify where to check the user permissions: current site or list / remote site or list. | +| remoteSiteUrl | string | no | The URL of the remote site. Required when you want to check permissions on remote site or list. | +| relativeLibOrListUrl | string | no | The relative URL of the list or library. Required when you want to check permissions on remote list. | + + +![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/SecurityTrimmedControl) diff --git a/docs/documentation/docs/controls/TaxonomyPicker.md b/docs/documentation/docs/controls/TaxonomyPicker.md index 8244b5204..50092626e 100644 --- a/docs/documentation/docs/controls/TaxonomyPicker.md +++ b/docs/documentation/docs/controls/TaxonomyPicker.md @@ -1,6 +1,6 @@ # Taxonomy Picker -This control Allows you to select one or more Terms from a TermSet via its name or TermSet ID. You can also configure the control to select the child terms from a specific term in the TermSet by setting the AnchorId. +This control allows you to select one or more Terms from a TermSet via its name or TermSet ID. You can also configure the control to select the child terms from a specific term in the TermSet by setting the AnchorId. !!! note "Disclaimer" This control makes use of the `ProcessQuery` API end-points to retrieve the managed metadata information. This will get changed once the APIs for managing managed metadata will become available. @@ -11,7 +11,7 @@ This control Allows you to select one or more Terms from a TermSet via its name **Selecting terms** -![Selecting terms](../assets/termpicker-selection.png) +![Selecting terms](../assets/termPicker-tree-selection.png) **Selected terms in picker** @@ -19,7 +19,7 @@ This control Allows you to select one or more Terms from a TermSet via its name **Term picker: Auto Complete** -![Selected terms in the input](../assets/termpicker-autocomplete.png) +![Selected terms in the input](../assets/termpicker-input-autocomplete.png) ## How to use this control in your solutions @@ -36,7 +36,7 @@ import { TaxonomyPicker, IPickerTerms } from "@pnp/spfx-controls-react/lib/Taxon ```TypeScript =0.10.0" }, @@ -12,7 +12,8 @@ "prepublishOnly": "gulp", "versionUpdater": "gulp versionUpdater", "karma": "karma start --circle true", - "changelog": "node scripts/sync-changelogs.js" + "changelog": "node scripts/sync-changelogs.js", + "postinstall": "node postinstall/install.js" }, "dependencies": { "@pnp/common": "^1.0.1", diff --git a/postinstall/install.js b/postinstall/install.js new file mode 100755 index 000000000..b99f1a825 --- /dev/null +++ b/postinstall/install.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +console.log("INFO: Adding the required localized resource configuration to the config.json file."); + +// Get the current directory +const crntDir = path.resolve(__dirname); +// Split the whole directory path +let nesting = crntDir.split("/"); +// Windows split +if (nesting.length <= 1) { + nesting = crntDir.split("\\"); +} +// Check if correctly splitted +if (nesting.length > 0) { + // Find the first node_modules folder index + let idx = nesting.indexOf("node_modules"); + // Check if index of the folder was found + if (idx !== -1) { + // Slice unnecessary nodes + const nest = nesting.slice(idx); + if (nest && nest.length > 0) { + const paths = nest.map(m => ".."); + // Get the path of the projects root location + const rootDir = path.resolve(path.join(__dirname, paths.join('/'))); + const fileLoc = `${rootDir}/config/config.json`; + // Check if config.json file exists + if (fs.existsSync(fileLoc)) { + // Get the config file + const config = fs.readFileSync(fileLoc, "utf8"); + if (config && typeof config === "string") { + const contents = JSON.parse(config); + if (contents && contents.localizedResources && !contents.localizedResources.ControlStrings) { + contents.localizedResources["ControlStrings"] = "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js"; + // Update the file + fs.writeFileSync(fileLoc, JSON.stringify(contents, null, 2)); + console.log("INFO: Localized resource added."); + } else { + console.warn(`WARNING: it seems something is wrong with the config.json file or the "ControlStrings" reference was already set.`); + } + } else { + console.warn("WARNING: the config.json file was not correctly retrieved."); + } + } else { + console.warn("WARNING: the config.json file does not exist."); + } + } else { + console.warn("WARNING: something is wrong with the installation path."); + } + } else { + console.warn("WARNING: something when wrong during with retrieving the project its root location."); + } +} else { + console.warn("WARNING: something is wrong with the installation path."); +} diff --git a/src/SecurityTrimmedControl.ts b/src/SecurityTrimmedControl.ts new file mode 100644 index 000000000..2083f23e4 --- /dev/null +++ b/src/SecurityTrimmedControl.ts @@ -0,0 +1 @@ +export * from './controls/securityTrimmedControl'; diff --git a/src/controls/fields/fieldUserRenderer/FieldUserRenderer.tsx b/src/controls/fields/fieldUserRenderer/FieldUserRenderer.tsx index a9bd7ee8b..b897036f9 100644 --- a/src/controls/fields/fieldUserRenderer/FieldUserRenderer.tsx +++ b/src/controls/fields/fieldUserRenderer/FieldUserRenderer.tsx @@ -171,7 +171,7 @@ export class FieldUserRenderer extends React.Component
  • -
    {strings.Contact}
    +
    {strings.Contact}
    {user.email} diff --git a/src/controls/placeholder/PlaceholderComponent.tsx b/src/controls/placeholder/PlaceholderComponent.tsx index 8225bc5ce..6456a09a6 100644 --- a/src/controls/placeholder/PlaceholderComponent.tsx +++ b/src/controls/placeholder/PlaceholderComponent.tsx @@ -4,6 +4,7 @@ import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; import styles from './PlaceholderComponent.module.scss'; import * as appInsights from '../../common/appInsights'; import { IPlaceholderState } from '.'; +import { Icon } from 'office-ui-fabric-react/lib/components/Icon'; /** * Placeholder component @@ -83,15 +84,13 @@ export class Placeholder extends React.Component { - const iconName = typeof this.props.iconName !== 'undefined' && this.props.iconName !== null ? `ms-Icon--${this.props.iconName}` : ''; - return (
    { - iconName ? : '' + this.props.iconName && } {this.props.iconText}
    diff --git a/src/controls/securityTrimmedControl/ISecurityTrimmedControlProps.ts b/src/controls/securityTrimmedControl/ISecurityTrimmedControlProps.ts new file mode 100644 index 000000000..785c27ff1 --- /dev/null +++ b/src/controls/securityTrimmedControl/ISecurityTrimmedControlProps.ts @@ -0,0 +1,28 @@ +import { ApplicationCustomizerContext } from '@microsoft/sp-application-base'; +import { FieldCustomizerContext, ListViewCommandSetContext } from '@microsoft/sp-listview-extensibility'; +import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { SPPermission } from '@microsoft/sp-page-context'; +import { PermissionLevel } from '.'; + +export interface ISecurityTrimmedControlProps { + /** + * Context of the web part, application customizer, field customizer, or list view command set. + */ + context: WebPartContext | ApplicationCustomizerContext | FieldCustomizerContext | ListViewCommandSetContext; + /** + * The permissions to check for the user. + */ + permissions: SPPermission[]; + /** + * Specify where to check the user permissions: current site or list / remote site or list. + */ + level: PermissionLevel; + /** + * The URL of the remote site. Required when you want to check permissions on remote site or list. + */ + remoteSiteUrl?: string; + /** + * The relative URL of the list or library. Required when you want to check permissions on remote list. + */ + relativeLibOrListUrl?: string; +} diff --git a/src/controls/securityTrimmedControl/ISecurityTrimmedControlState.ts b/src/controls/securityTrimmedControl/ISecurityTrimmedControlState.ts new file mode 100644 index 000000000..26a92675c --- /dev/null +++ b/src/controls/securityTrimmedControl/ISecurityTrimmedControlState.ts @@ -0,0 +1,3 @@ +export interface ISecurityTrimmedControlState { + allowRender: boolean; +} diff --git a/src/controls/securityTrimmedControl/PermissionLevel.ts b/src/controls/securityTrimmedControl/PermissionLevel.ts new file mode 100644 index 000000000..907758ef6 --- /dev/null +++ b/src/controls/securityTrimmedControl/PermissionLevel.ts @@ -0,0 +1,21 @@ +/** + * Permission level enum + */ +export enum PermissionLevel { + /** + * Checks permissions on the current web + */ + currentWeb = 1, + /** + * Checks permissions in the current loaded list + */ + currentList, + /** + * Checks permissions on the specified site URL + */ + remoteWeb, + /** + * Checks permissions on the specified list/library URL in combination with the site URL + */ + remoteListOrLib +} diff --git a/src/controls/securityTrimmedControl/SecurityTrimmedControl.tsx b/src/controls/securityTrimmedControl/SecurityTrimmedControl.tsx new file mode 100644 index 000000000..ad905373c --- /dev/null +++ b/src/controls/securityTrimmedControl/SecurityTrimmedControl.tsx @@ -0,0 +1,161 @@ +import * as React from 'react'; +import { ISecurityTrimmedControlProps, ISecurityTrimmedControlState, PermissionLevel } from '.'; +import { SPHttpClient } from '@microsoft/sp-http'; +import { SPPermission } from '@microsoft/sp-page-context'; +import * as appInsights from '../../common/appInsights'; + +export class SecurityTrimmedControl extends React.Component { + constructor(props: ISecurityTrimmedControlProps) { + super(props); + + this.state = { + allowRender: false + }; + + appInsights.track('ReactPlaceholder', {}); + } + + /** + * componentDidMount lifecycle method + */ + public componentDidMount(): void { + this.checkPermissions(); + } + + /** + * componentDidUpdate lifecycle method + */ + public componentDidUpdate(prevProps: ISecurityTrimmedControlProps, prevState: ISecurityTrimmedControlState): void { + // Check permissions only if necessary + if (prevProps.level !== this.props.level || + prevProps.permissions !== this.props.permissions || + prevProps.relativeLibOrListUrl !== this.props.relativeLibOrListUrl || + prevProps.remoteSiteUrl !== this.props.remoteSiteUrl) { + this.checkPermissions(); + } + } + + /** + * Check if the user has the permissions to render the element + */ + private checkPermissions() { + const { context, level } = this.props; + // Check if the permission level needs to be checked on the current site + if (level === PermissionLevel.currentWeb || level === PermissionLevel.currentList) { + // Get the permission scope + const { permissions } = level === PermissionLevel.currentWeb ? context.pageContext.web : context.pageContext.list; + // Check the user its permissions + if (permissions.hasAllPermissions(...this.props.permissions)) { + this.setState({ + allowRender: true + }); + } else { + this.setState({ + allowRender: false + }); + } + } else if (level === PermissionLevel.remoteWeb) { + // Check permissions on remote site + this.checkRemoteSitePermissions(); + } else if (level === PermissionLevel.remoteListOrLib) { + // Check permissions on remote list/library + this.checkRemoteListOrLibPermissions(); + } + } + + /** + * Check the user its permissions on the remote site + */ + private async checkRemoteSitePermissions() { + const { context, remoteSiteUrl, permissions } = this.props; + if (remoteSiteUrl && permissions) { + for (const permission of permissions) { + const apiUrl = `${remoteSiteUrl}/_api/web/DoesUserHavePermissions(@v)?@v=${JSON.stringify(permission.value)}`; + const result = await context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1).then(data => data.json()); + // Check if a result was retrieved + if (result) { + // Check if an error was retrieved + if (result.error) { + // Do not allow rendering when there was an error + this.setState({ + allowRender: false + }); + console.error(`Error retrieved while checking user's remote site permissions.`); + return; + } + // Check the result value + if (typeof result.value !== "undefined" && result.value === false) { + this.setState({ + allowRender: false + }); + return; + } + } else { + this.setState({ + allowRender: false + }); + console.error(`No result value was retrieved when checking the user's remote site permissions.`); + return; + } + } + + // Render the controls when the permissions were OK for the user + this.setState({ + allowRender: true + }); + } + } + + /** + * Check the user its permissions on the remote list or library + */ + private async checkRemoteListOrLibPermissions() { + const { context, remoteSiteUrl, relativeLibOrListUrl, permissions } = this.props; + // Check if all properties are provided + if (remoteSiteUrl && relativeLibOrListUrl && permissions) { + const apiUrl = `${remoteSiteUrl}/_api/web/GetList(@listUrl)/EffectiveBasePermissions?@listUrl='${encodeURIComponent(relativeLibOrListUrl)}'`; + const result = await context.spHttpClient.get(apiUrl, SPHttpClient.configurations.v1).then(data => data.json()); + // Check if a result was retrieved + if (result) { + // Check if an error was retrieved + if (result.error) { + // Do not allow rendering when there was an error + this.setState({ + allowRender: false + }); + console.error(`Error retrieved while checking user's remote list or library permissions.`); + return; + } + + // Check the result high and low value are returned + if (typeof result.High !== "undefined" && typeof result.Low !== "undefined") { + // Create the permission mask + const permission = new SPPermission(result); + const hasPermissions = permission.hasAllPermissions(...permissions); + + this.setState({ + allowRender: hasPermissions + }); + return; + } + } else { + this.setState({ + allowRender: false + }); + console.error(`No result value was retrieved when checking the user's remote list or library permissions.`); + return; + } + } + } + + /** + * Default React render method + */ + public render(): React.ReactElement { + return ( + this.state.allowRender ? ( +
    {this.props.children}
    + ) : null + ); + } +} diff --git a/src/controls/securityTrimmedControl/index.ts b/src/controls/securityTrimmedControl/index.ts new file mode 100644 index 000000000..6987f2d3b --- /dev/null +++ b/src/controls/securityTrimmedControl/index.ts @@ -0,0 +1,4 @@ +export * from './ISecurityTrimmedControlProps'; +export * from './ISecurityTrimmedControlState'; +export * from './SecurityTrimmedControl'; +export * from './PermissionLevel'; diff --git a/src/controls/taxonomyPicker/ITaxonomyPicker.ts b/src/controls/taxonomyPicker/ITaxonomyPicker.ts index c114ea581..8492c6157 100644 --- a/src/controls/taxonomyPicker/ITaxonomyPicker.ts +++ b/src/controls/taxonomyPicker/ITaxonomyPicker.ts @@ -1,3 +1,4 @@ +import { ApplicationCustomizerContext } from '@microsoft/sp-application-base'; import { IPickerTerms } from './ITermPicker'; import { ITermStore, IGroup, ITermSet, ITerm } from '../../services/ISPTermStorePickerService'; import SPTermStorePickerService from '../../services/SPTermStorePickerService'; @@ -26,7 +27,7 @@ export interface ITaxonomyPickerProps { /** * WebPart's context */ - context: IWebPartContext; + context: IWebPartContext | ApplicationCustomizerContext; /** * Limit the terms that can be picked by the Term Set name or ID */ diff --git a/src/controls/taxonomyPicker/ITermPicker.ts b/src/controls/taxonomyPicker/ITermPicker.ts index ee2d6c055..67968b253 100644 --- a/src/controls/taxonomyPicker/ITermPicker.ts +++ b/src/controls/taxonomyPicker/ITermPicker.ts @@ -1,3 +1,4 @@ +import { ApplicationCustomizerContext } from '@microsoft/sp-application-base'; import { IWebPartContext } from '@microsoft/sp-webpart-base'; @@ -42,7 +43,7 @@ export interface IPropertyFieldTermPickerProps { /** * WebPart's context */ - context: IWebPartContext; + context: IWebPartContext | ApplicationCustomizerContext; /** * Limit the term sets that can be used by the group name or ID */ @@ -88,6 +89,6 @@ export interface IPropertyFieldTermPickerProps { * Default value is 200. */ deferredValidationTime?: number; - + } diff --git a/src/controls/taxonomyPicker/TermPicker.tsx b/src/controls/taxonomyPicker/TermPicker.tsx index 06914f9af..0d486a83b 100644 --- a/src/controls/taxonomyPicker/TermPicker.tsx +++ b/src/controls/taxonomyPicker/TermPicker.tsx @@ -6,6 +6,8 @@ import styles from './TaxonomyPicker.module.scss'; import { ITaxonomyPickerProps } from './ITaxonomyPicker'; import { IWebPartContext } from '@microsoft/sp-webpart-base'; import * as strings from 'ControlStrings'; +import { Icon } from 'office-ui-fabric-react'; +import { ApplicationCustomizerContext } from '@microsoft/sp-application-base'; export class TermBasePicker extends BasePicker> { @@ -18,7 +20,7 @@ export interface ITermPickerState { export interface ITermPickerProps { termPickerHostProps: ITaxonomyPickerProps; - context: IWebPartContext; + context: IWebPartContext | ApplicationCustomizerContext; disabled: boolean; value: IPickerTerms; allowMultipleSelections : boolean; @@ -70,7 +72,7 @@ export default class TermPicker extends React.Component - + ) } diff --git a/src/services/SPTermStorePickerService.ts b/src/services/SPTermStorePickerService.ts index b2592c25d..a86c29e48 100644 --- a/src/services/SPTermStorePickerService.ts +++ b/src/services/SPTermStorePickerService.ts @@ -12,6 +12,7 @@ import { ITaxonomyPickerProps } from '../controls/taxonomyPicker/ITaxonomyPicker import { IPickerTerms, IPickerTerm } from '../controls/taxonomyPicker/ITermPicker'; import { ITermStore, ITerms, ITerm, IGroup, ITermSet, ITermSets } from './ISPTermStorePickerService'; import SPTermStoreMockHttpClient from './SPTermStorePickerMockService'; +import { ApplicationCustomizerContext } from '@microsoft/sp-application-base'; /** @@ -25,7 +26,7 @@ export default class SPTermStorePickerService { /** * Service constructor */ - constructor(private props: ITaxonomyPickerProps, private context: IWebPartContext) { + constructor(private props: ITaxonomyPickerProps, private context: IWebPartContext | ApplicationCustomizerContext) { if (Environment.type !== EnvironmentType.Local) { { this.clientServiceUrl = this.context.pageContext.web.absoluteUrl + '/_vti_bin/client.svc/ProcessQuery'; diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index a916c98a6..cf1ad2c1d 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -15,6 +15,8 @@ import { TaxonomyPicker, IPickerTerms } from '../../../TaxonomyPicker'; import { ListPicker } from '../../../ListPicker'; import { IFrameDialog } from '../../../IFrameDialog'; import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; +import { SecurityTrimmedControl, PermissionLevel } from '../../../SecurityTrimmedControl'; +import { SPPermission } from '@microsoft/sp-page-context'; /** * Component that can be used to test out the React controls from this project @@ -177,6 +179,10 @@ export default class ControlsTest extends React.Component Controls testing + +

    You have permissions to view list items.

    +
    +

    File type icon control