Upgrade to SPFx 1.20
This commit is contained in:
parent
88310656fe
commit
693640944f
|
@ -0,0 +1,5 @@
|
|||
require('@rushstack/eslint-config/patch/modern-module-resolution');
|
||||
module.exports = {
|
||||
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
|
||||
parserOptions: { tsconfigRootDir: __dirname }
|
||||
};
|
|
@ -9,6 +9,7 @@ node_modules
|
|||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
@ -30,3 +31,6 @@ obj
|
|||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
||||
|
||||
# heft folder
|
||||
.heft
|
|
@ -0,0 +1 @@
|
|||
v18.20.4
|
|
@ -3,10 +3,15 @@
|
|||
"isDomainIsolated": false,
|
||||
"isCreatingSolution": false,
|
||||
"packageManager": "npm",
|
||||
"version": "1.10.0",
|
||||
"version": "1.20.0",
|
||||
"libraryName": "spsecurity-webpart-3",
|
||||
"libraryId": "788271fb-ee9b-40df-8381-eb3dc70d1982",
|
||||
"environment": "spo",
|
||||
"componentType": "webpart"
|
||||
"componentType": "webpart",
|
||||
"nodeVersion": "18.20.4"
|
||||
},
|
||||
"sdkVersions": {
|
||||
"@microsoft/teams-js": "2.24.0",
|
||||
"@microsoft/microsoft-graph-client": "3.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,8 +87,8 @@ This is a port of an Angular 1.3 SharePoint hosted App at https://github.com/rus
|
|||
| Every SPFx version is only compatible with specific version(s) of Node.js. In order to be able to build this sample, please ensure that the version of Node on your workstation matches one of the versions listed in this section. This sample will not work on a different version of Node.|
|
||||
|Refer to <https://aka.ms/spfx-matrix> for more information on SPFx compatibility. |
|
||||
|
||||
![SPFx 1.10](https://img.shields.io/badge/SPFx-1.10.0-green.svg)
|
||||
![Node.js v10 | v8](https://img.shields.io/badge/Node.js-v10%20%7C%20v8-green.svg)
|
||||
![SPFx 1.20](https://img.shields.io/badge/SPFx-1.20.0-green.svg)
|
||||
![Node.js v18.20.4](https://img.shields.io/badge/Node.js-%20v18.20.4-green.svg)
|
||||
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
|
||||
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg)
|
||||
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
|
||||
|
@ -102,7 +102,7 @@ This is a port of an Angular 1.3 SharePoint hosted App at https://github.com/rus
|
|||
|
||||
## Prerequisites
|
||||
|
||||
> React, Office-UI-Fabric, sp-pnp-js, lodash
|
||||
> React, Fluent UI React, sp-pnp-js, lodash
|
||||
|
||||
## Contributors
|
||||
|
||||
|
@ -112,6 +112,7 @@ This is a port of an Angular 1.3 SharePoint hosted App at https://github.com/rus
|
|||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.6.0|Oct 20, 2024 | Upgrade to SPFx 1.20
|
||||
1.0.5.0|March 6, 2021 | Added webApiPermission request
|
||||
1.0.0.4|February 22, 2021 | Added support for AD groups
|
||||
1.0.0.3|October 28, 2020 | Update to office-ui-fabric-react 7.148.1, fixing icons and indentation for sub-folders
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "spsecurity-webpart-3",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"name": "spsecurity-webpart-3-client-side-solution",
|
||||
"id": "788271fb-ee9b-40df-8381-eb3dc70d1982",
|
||||
"version": "1.0.4.0",
|
||||
"webApiPermissionRequests": [{
|
||||
"webApiPermissionRequests": [
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Group.Read.All"
|
||||
},
|
||||
|
@ -14,7 +15,37 @@
|
|||
"resource": "Microsoft Graph",
|
||||
"scope": "GroupMember.Read.All"
|
||||
}
|
||||
]
|
||||
],
|
||||
"developer": {
|
||||
"name": "Russel Gove",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"websiteUrl": "https://github.com/russgove",
|
||||
"mpnId": "Undefined-1.20.0"
|
||||
},
|
||||
"metadata": {
|
||||
"shortDescription": {
|
||||
"default": "Security Grid Web Part"
|
||||
},
|
||||
"longDescription": {
|
||||
"default": "React-securitygrid is an SPFX web part that uses React and Office-UI-Fabric to render a grid showing which users have access to which lists/libraries/folders/files on a Web"
|
||||
},
|
||||
"screenshotPaths": [],
|
||||
"videoUrl": "",
|
||||
"categories": []
|
||||
},
|
||||
"features": [
|
||||
{
|
||||
"title": "Security Grid Web Part Feature",
|
||||
"description": "The feature that activates Security Grid Web Part from the react-securitygrid solution.",
|
||||
"id": "41e37f03-2ea8-4f19-b77a-f2121a1e7c45",
|
||||
"version": "1.0.4.0",
|
||||
"componentIds": [
|
||||
"41e37f03-2ea8-4f19-b77a-f2121a1e7c45"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/spsecurity-webpart-3.sppkg"
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json"
|
||||
}
|
|
@ -1,15 +1,6 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
|
||||
"port": 4321,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"https": true,
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
},
|
||||
"serveConfigurations": {
|
||||
"default": {
|
||||
"pageUrl": "https://russellwgove.sharepoint.com/sites/testazad/_layouts/15/workbench.aspx"
|
||||
}
|
||||
}
|
||||
"initialPage": "https://{tenantDomain}/_layouts/workbench.aspx",
|
||||
"https": true
|
||||
}
|
|
@ -8,4 +8,13 @@ build.addSuppression(`Warning - [sass] The local CSS class 'ms-DetailsHeader-cel
|
|||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-List-cell' is not camelCase and will not be type-safe.`);
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-DetailsRow' is not camelCase and will not be type-safe.`);
|
||||
|
||||
var getTasks = build.rig.getTasks;
|
||||
build.rig.getTasks = function () {
|
||||
var result = getTasks.call(build.rig);
|
||||
|
||||
result.set('serve', result.get('serve-deprecated'));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
build.initialize(gulp);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,55 +1,50 @@
|
|||
{
|
||||
"main": "lib/index.js",
|
||||
"name": "spsecurity-webpart-3",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.8.8"
|
||||
"node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/microsoft-graph-types": "^1.31.0",
|
||||
"@pnp/common": "1.3.3",
|
||||
"@pnp/logging": "1.3.3",
|
||||
"@pnp/odata": "1.3.3",
|
||||
"@pnp/sp": "1.3.3",
|
||||
"@pnp/spfx-property-controls": "1.0.0",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@uifabric/file-type-icons": "7.6.16",
|
||||
"@microsoft/sp-core-library": "1.20.0",
|
||||
"@microsoft/sp-adaptive-card-extension-base": "1.20.0",
|
||||
"@pnp/sp": "4.6.0",
|
||||
"@pnp/spfx-property-controls": "3.18.0",
|
||||
"@types/webpack-env": "1.15.2",
|
||||
"@uifabric/file-type-icons": "7.10.11",
|
||||
"lodash": "^4.17.4",
|
||||
"natives": "^1.1.6",
|
||||
"office-ui-fabric-react": "7.148.1",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5"
|
||||
"@fluentui/react": "8.106.4",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
"tslib": "2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/rush-stack-compiler-3.2": "0.6.8",
|
||||
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
|
||||
"@microsoft/sp-build-web": "1.10.0",
|
||||
"@microsoft/sp-core-library": "1.10.0",
|
||||
"@microsoft/sp-lodash-subset": "1.10.0",
|
||||
"@microsoft/sp-module-interfaces": "1.10.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
|
||||
"@microsoft/sp-property-pane": "1.10.0",
|
||||
"@microsoft/sp-tslint-rules": "1.10.0",
|
||||
"@microsoft/sp-webpart-base": "1.10.0",
|
||||
"@microsoft/sp-webpart-workbench": "1.10.0",
|
||||
"@microsoft/rush-stack-compiler-4.7": "0.1.0",
|
||||
"@microsoft/eslint-config-spfx": "1.20.2",
|
||||
"@microsoft/eslint-plugin-spfx": "1.20.2",
|
||||
"@microsoft/sp-build-web": "1.20.2",
|
||||
"@microsoft/sp-module-interfaces": "1.20.2",
|
||||
"@rushstack/eslint-config": "4.0.1",
|
||||
"eslint": "8.57.0",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1",
|
||||
"i": "0.3.7",
|
||||
"npm": "6.14.6",
|
||||
"tslint-microsoft-contrib": "5.0.0"
|
||||
"@types/react": "17.0.45",
|
||||
"@types/react-dom": "17.0.17",
|
||||
"@types/webpack-env": "1.15.2",
|
||||
"eslint-plugin-react-hooks": "4.3.0",
|
||||
"ajv": "6.12.5",
|
||||
"gulp": "4.0.2",
|
||||
"tslint-microsoft-contrib": "5.0.0",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"@microsoft/sp-http": "1.20.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
|
||||
// import pnp, pnp logging system, and any other selective imports needed
|
||||
import { spfi, SPFI, SPFx } from "@pnp/sp";
|
||||
import "@pnp/sp/webs";
|
||||
import "@pnp/sp/lists";
|
||||
import "@pnp/sp/items";
|
||||
import "@pnp/sp/batching";
|
||||
import "@pnp/sp/site-users/web";
|
||||
import "@pnp/sp/presets/all";
|
||||
|
||||
let _sp: SPFI = null;
|
||||
|
||||
export const getSP = (context?: WebPartContext): SPFI => {
|
||||
if (context != null) {
|
||||
_sp = spfi().using(SPFx(context));
|
||||
}
|
||||
return _sp;
|
||||
};
|
||||
|
|
@ -1,41 +1,78 @@
|
|||
import { User } from "@microsoft/microsoft-graph-types";
|
||||
import { AadHttpClient, AadHttpClientConfiguration, HttpClientResponse, IAadHttpClientConfiguration, IAadHttpClientConfigurations, IAadHttpClientOptions } from "@microsoft/sp-http";
|
||||
import { IODataUser } from "@microsoft/sp-odata-types";
|
||||
import { AadHttpClient, AadHttpClientConfiguration } from "@microsoft/sp-http";
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
//import * as MicrosoftGraph from "@microsoft/microsoft-graph-types"
|
||||
import { sp } from "@pnp/sp";
|
||||
import { SiteGroup } from "@pnp/sp/src/sitegroups";
|
||||
import { filter, find, includes, indexOf, lowerCase } from "lodash";
|
||||
import { SPFI } from '@pnp/sp';
|
||||
import { getSP } from '../pnpjs-config';
|
||||
|
||||
import { find, includes } from "lodash";
|
||||
|
||||
interface SPRoleAssignmentObject {
|
||||
PrincipalId: number;
|
||||
RoleDefinitionBindings: Array<SPRoleDefinitionBindingObject>;
|
||||
}
|
||||
|
||||
interface SPRoleDefinitionBindingObject {
|
||||
Id: number;
|
||||
}
|
||||
|
||||
interface SPListItemResponse {
|
||||
GUID: string;
|
||||
FileSystemObjectType: number;
|
||||
Title: string;
|
||||
ContentTypeId: string;
|
||||
RoleAssignments: Array<SPRoleAssignmentObject>;
|
||||
File?: {
|
||||
Name: string;
|
||||
ServerRelativeUrl: string;
|
||||
};
|
||||
Folder?: {
|
||||
Name: string;
|
||||
ServerRelativeUrl: string;
|
||||
ItemCount: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface SPUser {
|
||||
Id: number;
|
||||
LoginName: string;
|
||||
Title: string;
|
||||
PrincipalType: number;
|
||||
UserId?: {
|
||||
NameId: string;
|
||||
NameIdIssuer: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ADGroupResponse {
|
||||
value: Array<User>;
|
||||
}
|
||||
|
||||
export interface ISPSecurableObject {
|
||||
id: number;
|
||||
id: string | number;
|
||||
roleAssignments: SPRoleAssignment[];
|
||||
|
||||
}
|
||||
|
||||
export class SPBasePermissions {
|
||||
public low: number;
|
||||
public high: number;
|
||||
public constructor(high: any, low: any) {
|
||||
public constructor(high: string, low: string) {
|
||||
this.high = parseInt(high, 10);
|
||||
this.low = parseInt(low, 10);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export enum securableType {
|
||||
List
|
||||
}
|
||||
|
||||
export class ADGroupId {
|
||||
public ADId: string; // the goid id in azure
|
||||
public SPId: number; // the numeric id in the sharepoint users list
|
||||
|
||||
public ADId: string;
|
||||
public SPId: number;
|
||||
}
|
||||
|
||||
|
||||
export class ADGroup {
|
||||
public id: ADGroupId;
|
||||
public members: Array<User>;
|
||||
|
||||
}
|
||||
|
||||
export class SPSiteGroup {
|
||||
|
@ -44,23 +81,22 @@ export class SPSiteGroup {
|
|||
public isHiddenInUI: boolean;
|
||||
public isShareByEmailGuestUse: boolean;
|
||||
public isSiteAdmin: boolean;
|
||||
public userIds: number[];// to switch to ad groups need to make this a string[] with the UPN
|
||||
public adGroupIds: ADGroupId[];
|
||||
public userIds: number[] = [];
|
||||
public adGroupIds: ADGroupId[] = [];
|
||||
public constructor(id: number, title: string) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.userIds = [];
|
||||
this.adGroupIds = [];
|
||||
}
|
||||
}
|
||||
|
||||
export class SPSiteUser {
|
||||
public name: string;
|
||||
public id?: number;
|
||||
public userId?: SPExternalUser;
|
||||
public upn: string;
|
||||
public isSelected: boolean; //should user be shown in UI
|
||||
public principalType: number; //4=Security group, 1 = user, 2=DL, 8=SP Group, IN HERE A -1 MEANS AD USER
|
||||
public adId: string;//id if the user in AD
|
||||
public isSelected: boolean = false;
|
||||
public principalType: number = 0;
|
||||
public adId?: string;
|
||||
}
|
||||
|
||||
export class SPRoleDefinition {
|
||||
|
@ -76,82 +112,68 @@ export class SPRoleDefinition {
|
|||
this.hidden = hidden;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class SPSecurityInfo {
|
||||
public siteUsers: SPSiteUser[];
|
||||
public siteGroups: SPSiteGroup[];
|
||||
public roleDefinitions: SPRoleDefinition[];
|
||||
public adGroups: ADGroup[];
|
||||
public lists: (SPList | SPListItem)[];
|
||||
public constructor() {
|
||||
|
||||
this.siteUsers = new Array<SPSiteUser>();
|
||||
this.siteGroups = new Array<SPSiteGroup>();
|
||||
this.roleDefinitions = new Array<SPRoleDefinition>();
|
||||
this.siteUsers = new Array<SPSiteUser>();
|
||||
this.lists = new Array<SPList>();
|
||||
this.adGroups = new Array<ADGroup>();
|
||||
|
||||
}
|
||||
public siteUsers: SPSiteUser[] = [];
|
||||
public siteGroups: SPSiteGroup[] = [];
|
||||
public roleDefinitions: SPRoleDefinition[] = [];
|
||||
public adGroups: ADGroup[] = [];
|
||||
public lists: (SPList | SPListItem)[] = [];
|
||||
}
|
||||
|
||||
export class SPList {
|
||||
public title: string;
|
||||
public id: string;
|
||||
public hidden: boolean; // this specifies if the list is a sharepoint hidden list
|
||||
public serverRelativeUrl: string;
|
||||
public type: securableType;
|
||||
public itemCount: number;
|
||||
public roleAssignments: SPRoleAssignment[];
|
||||
public isExpanded: boolean;
|
||||
public hasBeenRetrieved: boolean;
|
||||
public isSelected: boolean; //Shoud list be shown in the UI
|
||||
|
||||
export class SPList implements ISPSecurableObject {
|
||||
public title: string = "";
|
||||
public id: string = "";
|
||||
public hidden: boolean = false;
|
||||
public serverRelativeUrl: string = "";
|
||||
public type: securableType = securableType.List;
|
||||
public itemCount: number = 0;
|
||||
public roleAssignments: SPRoleAssignment[] = [];
|
||||
public isExpanded: boolean = false;
|
||||
public isFetched: boolean = false;
|
||||
public hasBeenRetrieved: boolean = false;
|
||||
public isSelected: boolean = false;
|
||||
}
|
||||
export class SPListItem {
|
||||
public id: string;
|
||||
public parentId: string;
|
||||
public listTitle: string;
|
||||
public type: string;
|
||||
public itemCount: number;
|
||||
public title: string;
|
||||
public serverRelativeUrl: string;
|
||||
public roleAssignments: SPRoleAssignment[];
|
||||
public isExpanded: boolean;
|
||||
public hasBeenRetrieved: boolean;
|
||||
public level: number;
|
||||
|
||||
export class SPListItem implements ISPSecurableObject {
|
||||
public id: string = "";
|
||||
public parentId: string = "";
|
||||
public listTitle: string = "";
|
||||
public type: string = "";
|
||||
public itemCount: number = 0;
|
||||
public title: string = "";
|
||||
public serverRelativeUrl: string = "";
|
||||
public roleAssignments: SPRoleAssignment[] = [];
|
||||
public isExpanded: boolean = false;
|
||||
public isFetched: boolean = false;
|
||||
public isSelected: boolean = false;
|
||||
public hasBeenRetrieved: boolean = false;
|
||||
public level: number = 0;
|
||||
public iconName: string = "";
|
||||
}
|
||||
// export class ADGroup {
|
||||
// public id: string;
|
||||
// public parentId: string;
|
||||
// public listTitle: string;
|
||||
// public type: string;
|
||||
// public itemCount: number;
|
||||
// public title: string;
|
||||
// public serverRelativeUrl: string;
|
||||
// public roleAssignments: SPRoleAssignment[];
|
||||
// public isExpanded: boolean;
|
||||
// public hasBeenRetrieved: boolean;
|
||||
// public level: number;
|
||||
|
||||
// }
|
||||
export class SPExternalUser {
|
||||
public nameId: string;
|
||||
public nameIdIssuer: string;
|
||||
public nameId: string = "";
|
||||
public nameIdIssuer: string = "";
|
||||
}
|
||||
|
||||
export class SPRoleAssignment {
|
||||
public roleDefinitionIds: number[] = [];
|
||||
public principalId: number;
|
||||
|
||||
|
||||
public principalId: number = 0;
|
||||
}
|
||||
|
||||
export class Helpers {
|
||||
public static doesUserHaveAnyPermission(securableObjects: any[], user, requestedpermissions: SPPermission[], roles, siteGroups, adGroups: ADGroup[]): boolean {
|
||||
for (var securableObject of securableObjects) {
|
||||
for (var requestedpermission of requestedpermissions) {
|
||||
public static doesUserHaveAnyPermission(
|
||||
securableObjects: ISPSecurableObject[],
|
||||
user: SPSiteUser,
|
||||
requestedpermissions: SPPermission[],
|
||||
roles: SPRoleDefinition[],
|
||||
siteGroups: SPSiteGroup[],
|
||||
adGroups: ADGroup[]
|
||||
): boolean {
|
||||
for (const securableObject of securableObjects) {
|
||||
for (const requestedpermission of requestedpermissions) {
|
||||
if (Helpers.doesUserHavePermission(securableObject, user, requestedpermission, roles, siteGroups, adGroups)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -159,14 +181,19 @@ export class Helpers {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
public static doesUserHavePermission(securableObject, user, requestedpermission: SPPermission, roles, siteGroups, adGroups: ADGroup[]) {
|
||||
|
||||
|
||||
public static doesUserHavePermission(
|
||||
securableObject: ISPSecurableObject,
|
||||
user: SPSiteUser,
|
||||
requestedpermission: SPPermission,
|
||||
roles: SPRoleDefinition[],
|
||||
siteGroups: SPSiteGroup[],
|
||||
adGroups: ADGroup[]
|
||||
): boolean {
|
||||
const permissions: SPBasePermissions[] = Helpers.getUserPermissionsForObject(securableObject, user, roles, siteGroups, adGroups);
|
||||
for (const permission of permissions) {
|
||||
if (
|
||||
((permission.low & requestedpermission.value.Low) === (requestedpermission.value.Low))
|
||||
&&
|
||||
((permission.low & requestedpermission.value.Low) === (requestedpermission.value.Low)) &&
|
||||
((permission.high & requestedpermission.value.High) === (requestedpermission.value.High))
|
||||
) {
|
||||
return true;
|
||||
|
@ -175,10 +202,11 @@ export class Helpers {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static getBasePermissionsForRoleDefinitiuonIds(selectedRoleDefinitionIds: number[],
|
||||
roleDefinitions: SPRoleDefinition[]): Array<SPBasePermissions> {
|
||||
let basePermissions = [];
|
||||
public static getBasePermissionsForRoleDefinitionIds(
|
||||
selectedRoleDefinitionIds: number[],
|
||||
roleDefinitions: SPRoleDefinition[]
|
||||
): Array<SPBasePermissions> {
|
||||
const basePermissions: SPBasePermissions[] = [];
|
||||
for (const selectedRoleDefinitionId of selectedRoleDefinitionIds) {
|
||||
for (const roleDefinition of roleDefinitions) {
|
||||
if (roleDefinition.id === selectedRoleDefinitionId) {
|
||||
|
@ -186,430 +214,291 @@ export class Helpers {
|
|||
}
|
||||
}
|
||||
}
|
||||
// for (var rdx = 0; rdx < roleDefs.length; rdx++) {
|
||||
// for (var rdi = 0; rdi < roleDefinitionIds.length; rdi++) {basePermission
|
||||
// if (roleDefs[rdx].Id === roleDefinitionIds[rdi]) {
|
||||
// basePermissions.push(roleDefs[rdx].BasePermissions);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return basePermissions;
|
||||
}
|
||||
public static getUserPermissionsForObject(securableObject, user, roles: SPRoleDefinition[], siteGroups: SPSiteGroup[], adGroups: ADGroup[]) {
|
||||
|
||||
public static getUserPermissionsForObject(
|
||||
securableObject: ISPSecurableObject,
|
||||
user: SPSiteUser,
|
||||
roles: SPRoleDefinition[],
|
||||
siteGroups: SPSiteGroup[],
|
||||
adGroups: ADGroup[]
|
||||
): SPBasePermissions[] {
|
||||
const userRoleAssignments: SPRoleAssignment[] = Helpers.GetRoleAssignmentsForUser(securableObject, user, siteGroups, adGroups);
|
||||
let roleDefinitionIds: number[] = [];
|
||||
const roleDefinitionIds: number[] = [];
|
||||
|
||||
for (const roleAssignment of userRoleAssignments) {
|
||||
for (const roleDefinitionID of roleAssignment.roleDefinitionIds) {
|
||||
roleDefinitionIds.push(roleDefinitionID);
|
||||
}
|
||||
}
|
||||
var userPermissions = Helpers.getBasePermissionsForRoleDefinitiuonIds(roleDefinitionIds, roles);
|
||||
|
||||
return userPermissions;
|
||||
return Helpers.getBasePermissionsForRoleDefinitionIds(roleDefinitionIds, roles);
|
||||
}
|
||||
public static findGroup(groupId: number, groups: SPSiteGroup[]): SPSiteGroup {
|
||||
return find(groups, (g) => { return g.id === groupId; });
|
||||
|
||||
public static GetRoleAssignmentsForUser(
|
||||
securableObject: ISPSecurableObject,
|
||||
user: SPSiteUser,
|
||||
groups: SPSiteGroup[],
|
||||
adGroups: ADGroup[]
|
||||
): SPRoleAssignment[] {
|
||||
const selectedRoleAssignments: SPRoleAssignment[] = [];
|
||||
|
||||
for (const roleAssignment of securableObject.roleAssignments) {
|
||||
const group: SPSiteGroup | undefined = find(groups, (g) => g.id === roleAssignment.principalId);
|
||||
if (group) {
|
||||
if (this.userIsInGroup(user.id!, group.id, groups)) {
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
if (this.userIsInGroupsNestAdGroups(user.adId!, group.id, groups, adGroups)) {
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
} else if (user.id === roleAssignment.principalId) {
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
}
|
||||
return selectedRoleAssignments;
|
||||
}
|
||||
|
||||
public static userIsInGroup(userId: number, groupId: number, groups: SPSiteGroup[]): boolean {
|
||||
let group: SPSiteGroup = this.findGroup(groupId, groups);
|
||||
const group: SPSiteGroup = this.findGroup(groupId, groups);
|
||||
return includes(group.userIds, userId);
|
||||
}
|
||||
public static userIsInGroupsNestAdGroups(userAdId: String, groupId: number, groups: SPSiteGroup[], adGroups: ADGroup[]): boolean {
|
||||
let group: SPSiteGroup = this.findGroup(groupId, groups);
|
||||
debugger;
|
||||
for (var adGrouId of group.adGroupIds) {
|
||||
var adGroup = find(adGroups, (adg) => { return adg.id === adGrouId; });
|
||||
if (adGroup) {
|
||||
if (find(adGroup.members, (aduser) => { return aduser.id === userAdId; })) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
debugger;
|
||||
alert(`adGroup ${ADGroupId} was not in the collection of ad groups.`);
|
||||
|
||||
public static userIsInGroupsNestAdGroups(
|
||||
userAdId: string,
|
||||
groupId: number,
|
||||
groups: SPSiteGroup[],
|
||||
adGroups: ADGroup[]
|
||||
): boolean {
|
||||
const group: SPSiteGroup = this.findGroup(groupId, groups);
|
||||
for (const adGrouId of group.adGroupIds) {
|
||||
const adGroup = find(adGroups, (adg) => adg.id === adGrouId);
|
||||
if (adGroup && find(adGroup.members, (aduser) => aduser.id === userAdId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static GetRoleAssignmentsForUser(securableObject: ISPSecurableObject, user: SPSiteUser,
|
||||
groups: SPSiteGroup[], adGroups: ADGroup[]): SPRoleAssignment[] {
|
||||
try {
|
||||
let selectedRoleAssignments: SPRoleAssignment[] = [];
|
||||
|
||||
// for each role assignment, if the user is in the group, or its for this user, add it to his roleassignments
|
||||
for (const roleAssignment of securableObject.roleAssignments) {
|
||||
let group: SPSiteGroup = find(groups, (g) => { return g.id === roleAssignment.principalId; });
|
||||
if (group) {
|
||||
if (this.userIsInGroup(user.id, group.id, groups)) { // this tests if a user is directly in the SP GROUP
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
if (this.userIsInGroupsNestAdGroups(user.adId, group.id, groups, adGroups)) { // this tests if a user is in an ad group thats in the SP GROUP
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// it must be a user
|
||||
if (user.id === roleAssignment.principalId) {
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
}
|
||||
}
|
||||
debugger;
|
||||
if (user.adId) { // if user is referenced in any groups, we stored his ad id in his user record
|
||||
for (var adgroup of adGroups) {// for all adGroups
|
||||
if (find(adgroup.members, (member) => {
|
||||
|
||||
return member.id === user.adId;
|
||||
}) != -1) { // if user is in the adgroup
|
||||
// for each role assignment, if the adGroup is in the group, or its for this adgroup, add it to his roleassignments
|
||||
for (const roleAssignment of securableObject.roleAssignments) {
|
||||
if (adgroup.id.SPId === roleAssignment.principalId) {
|
||||
selectedRoleAssignments.push(roleAssignment);
|
||||
}
|
||||
// debugger;
|
||||
// let group: SPSiteGroup = find(groups, (g) => { return g.id === roleAssignment.principalId; });
|
||||
// if (group) {
|
||||
// if (this.userIsInGroup(user.id, group.id, groups)) {
|
||||
// selectedRoleAssignments.push(roleAssignment);
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// // it must be a user
|
||||
// if (user.id === roleAssignment.principalId) {
|
||||
// selectedRoleAssignments.push(roleAssignment);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return selectedRoleAssignments;
|
||||
} catch (exception) {
|
||||
//debugger;
|
||||
console.error(exception);
|
||||
}
|
||||
|
||||
public static findGroup(groupId: number, groups: SPSiteGroup[]): SPSiteGroup {
|
||||
return find(groups, (g) => g.id === groupId)!;
|
||||
}
|
||||
}
|
||||
|
||||
export default class SPSecurityService {
|
||||
public siteUrl: string;
|
||||
private sp: SPFI;
|
||||
|
||||
public constructor(siteUrl: string) {
|
||||
this.siteUrl = siteUrl;
|
||||
this.sp = getSP();
|
||||
}
|
||||
public loadFolderRoleAssigmentsDefinitionsMembers(listTitle, folderServerRelativeUrl,
|
||||
parentId: string, level: number, forceReload: boolean): Promise<SPListItem[]> {
|
||||
|
||||
// pnp.sp.web.lists.getByTitle("Config3").getItemsByCAMLQuery(caml, "RoleAssignments").then(show);
|
||||
let caml: any = {
|
||||
ViewXml: "<View Scope='RecursiveAll'>" +
|
||||
" <Query>" +
|
||||
"<Where>" +
|
||||
" <Eq>" +
|
||||
" <FieldRef Name='FileDirRef'/>" +
|
||||
" <Value Type='Lookup'>" +
|
||||
folderServerRelativeUrl +
|
||||
" </Value>" +
|
||||
" </Eq>" +
|
||||
" </Where>" +
|
||||
" </Query>" +
|
||||
// " <QueryOptions>"+
|
||||
// "<ViewAttributes Scope='RecursiveAll' />" +
|
||||
// "<OptimizeFor>FolderUrls</OptimizeFor>"+
|
||||
|
||||
// "</QueryOptions>"+
|
||||
" </View>"
|
||||
// Adjust the loadFolderRoleAssignmentsDefinitionsMembers method with proper types
|
||||
public loadFolderRoleAssignmentsDefinitionsMembers(
|
||||
listTitle: string,
|
||||
folderServerRelativeUrl: string,
|
||||
parentId: string,
|
||||
level: number
|
||||
): Promise<SPListItem[]> {
|
||||
const caml: { ViewXml: string } = {
|
||||
ViewXml: "<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='FileDirRef'/><Value Type='Lookup'>" + folderServerRelativeUrl + "</Value></Eq></Where></Query></View>"
|
||||
};
|
||||
|
||||
return sp.web.lists.getByTitle(listTitle).getItemsByCAMLQuery(caml, "ContentType", "Folder", "Folder/ParentFolder", "File",
|
||||
"File/ParentFolder", "RoleAssignments", "RoleAssignments/RoleDefinitionBindings", "RoleAssignments/Member",
|
||||
"RoleAssignments/Member/Users", "RoleAssignments/Member/Groups")
|
||||
.then((response) => {
|
||||
|
||||
|
||||
let itemsToAdd: SPListItem[] = [];
|
||||
for (let listItem of response) {
|
||||
let itemToAdd: SPListItem = new SPListItem();
|
||||
|
||||
return this.sp.web.lists
|
||||
.getByTitle(listTitle)
|
||||
.getItemsByCAMLQuery(caml, "ContentTypeId", "File", "Folder", "Folder/ParentFolder", "File/ParentFolder", "RoleAssignments", "RoleAssignments/RoleDefinitionBindings", "RoleAssignments/Member", "RoleAssignments/Member/Users", "RoleAssignments/Member/Groups")
|
||||
.then((response: SPListItemResponse[]) => { // Now typed correctly
|
||||
const itemsToAdd: SPListItem[] = response.map((listItem: SPListItemResponse) => {
|
||||
const itemToAdd: SPListItem = new SPListItem();
|
||||
itemToAdd.id = listItem.GUID;
|
||||
itemToAdd.parentId = parentId;
|
||||
itemToAdd.level = level;
|
||||
itemToAdd.listTitle = listTitle;
|
||||
itemToAdd.type = listItem.ContentType.Name;
|
||||
itemToAdd.isExpanded = false;
|
||||
itemToAdd.hasBeenRetrieved = false;
|
||||
itemToAdd.roleAssignments = [];
|
||||
if (listItem.ContentType.Name === "Folder") { // its a folder
|
||||
|
||||
// Use FileSystemObjectType to distinguish between folder and file/list item
|
||||
if (listItem.FileSystemObjectType === 1 && listItem.Folder) {
|
||||
itemToAdd.type = 'Folder';
|
||||
itemToAdd.title = listItem.Folder.Name;
|
||||
itemToAdd.serverRelativeUrl = listItem.Folder.ServerRelativeUrl;
|
||||
itemToAdd.itemCount = listItem.Folder.ItemCount;
|
||||
} else if (listItem.File) {
|
||||
itemToAdd.type = 'File';
|
||||
itemToAdd.title = listItem.File.Name;
|
||||
itemToAdd.serverRelativeUrl = listItem.File.ServerRelativeUrl;
|
||||
} else {
|
||||
if (listItem.File) {// its a file
|
||||
itemToAdd.title = listItem.File.Name;
|
||||
itemToAdd.serverRelativeUrl = listItem.File.ServerRelativeUrl;
|
||||
} else { // its a listitem
|
||||
itemToAdd.title = listItem.Title;
|
||||
}
|
||||
itemToAdd.type = 'ListItem';
|
||||
itemToAdd.title = listItem.Title;
|
||||
}
|
||||
for (let roleAssignmentObject of listItem.RoleAssignments) {
|
||||
|
||||
let roleAssignment: SPRoleAssignment = {
|
||||
roleDefinitionIds: roleAssignmentObject.RoleDefinitionBindings.map((rdb) => { return rdb.Id; }),
|
||||
principalId: roleAssignmentObject.PrincipalId
|
||||
};
|
||||
// if (roleAssignmentObject.Member.UserId) {
|
||||
// roleAssignment.userId = new SPExternalUser();
|
||||
// roleAssignment.userId.nameId = roleAssignmentObject.Member.UserId.NameId;
|
||||
// roleAssignment.userId.nameIdIssuer = roleAssignmentObject.Member.UserId.NameIdIssuer;
|
||||
// // roleAssignment.userId = roleAssignmentObject.Member.UserId;
|
||||
// }
|
||||
// if (roleAssignmentObject.Member.Users) {
|
||||
// for (let roleAssignmentMemberUser of roleAssignmentObject.Member.Users) {
|
||||
// roleAssignment.users.push(roleAssignmentMemberUser.Id);
|
||||
// }
|
||||
// }
|
||||
// if (roleAssignmentObject.Member.Groups) {
|
||||
// for (let roleAssignmentMemberGroup of roleAssignmentObject.Member.Groups) {
|
||||
// roleAssignment.groups.push(roleAssignmentMemberGroup.Id);
|
||||
// }
|
||||
// }
|
||||
// for (let roleDefinitionBinding of roleAssignmentObject.RoleDefinitionBindings) {
|
||||
// roleAssignment.roleDefinitionIds.push(roleDefinitionBinding.Id);
|
||||
// }
|
||||
itemToAdd.roleAssignments.push(roleAssignment);
|
||||
}
|
||||
itemsToAdd.push(itemToAdd);
|
||||
}
|
||||
itemToAdd.isExpanded = false;
|
||||
itemToAdd.hasBeenRetrieved = false;
|
||||
itemToAdd.roleAssignments = listItem.RoleAssignments.map((roleAssignmentObject: SPRoleAssignmentObject) => ({
|
||||
roleDefinitionIds: roleAssignmentObject.RoleDefinitionBindings.map((rdb: SPRoleDefinitionBindingObject) => rdb.Id),
|
||||
principalId: roleAssignmentObject.PrincipalId,
|
||||
}));
|
||||
|
||||
return itemToAdd;
|
||||
});
|
||||
return itemsToAdd;
|
||||
});
|
||||
}
|
||||
// public async getMembersOfAdGroup(aadHttpClient: AadHttpClient, groupName: string): Promise<any> {
|
||||
|
||||
// return aadHttpClient.get("v1.0/groups?$filter=displayName eq '" + groupName + "'&$expand=members",
|
||||
// AadHttpClient.configurations.v1).then((response) => {
|
||||
// response.json().then((data) => {
|
||||
// debugger;
|
||||
// });
|
||||
// }).catch((err) => {
|
||||
// console.log(err);
|
||||
// });
|
||||
// }
|
||||
/// Loads data for intial display
|
||||
public loadData(showHiddenLists: boolean, showCatalogs: boolean, aadHttpClient: AadHttpClient, forceReload: boolean): Promise<SPSecurityInfo> {
|
||||
let securityInfo: SPSecurityInfo = new SPSecurityInfo();
|
||||
let batch: any = sp.createBatch();
|
||||
let errors: Array<string> = [];
|
||||
debugger;
|
||||
sp.web.siteUsers.inBatch(batch).get()
|
||||
.then((response) => {
|
||||
securityInfo.siteUsers = response.map((u) => {
|
||||
var upn: string = u.LoginName.split('|')[2];
|
||||
let user: SPSiteUser = new SPSiteUser();
|
||||
user.isSelected = true;
|
||||
user.id = u.Id;
|
||||
user.name = u.Title;
|
||||
user.principalType = u.PrincipalType;
|
||||
user.upn = upn ? upn.toLocaleLowerCase() : u.Title;// switching key in react from id to upn. ensure upn is not undefined
|
||||
if (u.UserId) {
|
||||
user.userId = new SPExternalUser();
|
||||
user.userId.nameId = u.UserId.NameId;
|
||||
user.userId.nameIdIssuer = u.UserId.NameIdIssuer;
|
||||
}
|
||||
return user;
|
||||
});
|
||||
public async loadData(showHiddenLists: boolean, showCatalogs: boolean, aadHttpClient: AadHttpClient): Promise<SPSecurityInfo> {
|
||||
const securityInfo: SPSecurityInfo = new SPSecurityInfo();
|
||||
const errors: string[] = [];
|
||||
|
||||
// securityInfo.siteUsers = securityInfo.siteUsers.filter((su) => { su.upn });
|
||||
return securityInfo.siteUsers;// dont really need to return this// already set it on securityinfo
|
||||
}).catch((error) => {
|
||||
debugger;
|
||||
errors.push(`There was an error feting site users -- ${error.message}`);
|
||||
throw error;
|
||||
// Get site users
|
||||
try {
|
||||
const siteUsersResponse = await this.sp.web.siteUsers();
|
||||
securityInfo.siteUsers = siteUsersResponse.map((u: SPUser) => {
|
||||
const upn: string = u.LoginName.split('|')[2];
|
||||
const user: SPSiteUser = new SPSiteUser();
|
||||
user.isSelected = true;
|
||||
user.id = u.Id;
|
||||
user.name = u.Title;
|
||||
user.principalType = u.PrincipalType;
|
||||
user.upn = upn ? upn.toLowerCase() : u.Title;
|
||||
if (u.UserId) {
|
||||
user.userId = { nameId: u.UserId.NameId, nameIdIssuer: u.UserId.NameIdIssuer };
|
||||
}
|
||||
return user;
|
||||
});
|
||||
sp.web.siteGroups.filter(`Title ne 'Limited Access System Group'`).expand("Users").select("Title", "Id", "IsHiddenInUI", "IsShareByEmailGuestUse", "IsSiteAdmin", "IsSiteAdmin")
|
||||
.inBatch(batch).get()
|
||||
.then(async (response) => {
|
||||
|
||||
|
||||
// if group contains an ad group(PrincipalType=4) expand it
|
||||
|
||||
securityInfo.siteGroups = response.map((grp) => {
|
||||
//
|
||||
//IMPORTANT:
|
||||
//For groups created with 'Anyone in the organization with the link'
|
||||
//LoginName: "SharingLinks.cf28991a-7f40-49c8-a68e-f4fa143a094f.OrganizationEdit.2671b36d-1681-4e39-82dc-a9f11166517d",
|
||||
//
|
||||
//For groups create with SPecific People
|
||||
//LoginName: "SharingLinks.3d634d86-7136-4d59-8acf-c87d9a2c7d98.Flexible.9368eb69-6ca4-4b55-85e5-148c3e48e520",
|
||||
//
|
||||
//need to check for other options. Seems funny that the one labeled 'Flexible' is for specific people.
|
||||
//
|
||||
//So we wil need to add code one day to decipher all thes sharing groups!
|
||||
|
||||
let siteGroup: SPSiteGroup = new SPSiteGroup(grp.Id, grp.Title);
|
||||
for (let user of grp.Users) {
|
||||
if (user.PrincipalType === 4) { //4=Security group, 1 = user, 2=DL, 8=SP Group
|
||||
var adgroupid = new ADGroupId();
|
||||
adgroupid.ADId = user.LoginName.split('|')[2];//Loginname s c:0t,c|tenant|grpid for ad groups
|
||||
adgroupid.SPId = user.Id;
|
||||
siteGroup.adGroupIds.push(adgroupid);
|
||||
} else {
|
||||
siteGroup.userIds.push(user.Id);
|
||||
}
|
||||
}
|
||||
return siteGroup;
|
||||
});
|
||||
|
||||
return securityInfo.siteGroups;// don't really need to return this// already set it on securityinfo
|
||||
}).catch((error) => {
|
||||
//error fetching groups
|
||||
errors.push(`There was an error feting site Groups -- ${error.message}`);
|
||||
//debugger;
|
||||
throw error;
|
||||
});
|
||||
|
||||
|
||||
sp.web.roleDefinitions.expand("BasePermissions").inBatch(batch).get()
|
||||
.then((response) => {
|
||||
securityInfo.roleDefinitions = response.map((rd) => {
|
||||
|
||||
const bp: SPBasePermissions = new SPBasePermissions(rd.BasePermissions.High, rd.BasePermissions.Low);
|
||||
const roleDefinition: SPRoleDefinition = new SPRoleDefinition(
|
||||
parseInt(rd.Id, 10),
|
||||
bp,
|
||||
rd.Description,
|
||||
rd.Hidden,
|
||||
rd.Name);
|
||||
|
||||
return roleDefinition;
|
||||
});
|
||||
return securityInfo.roleDefinitions;
|
||||
}).catch((error) => {
|
||||
//debugger;
|
||||
//error fetching role definitions
|
||||
errors.push(`There was an error fetching role Definitions -- ${error.message}`);
|
||||
throw error;
|
||||
});
|
||||
let filters: string[] = [];
|
||||
if (!showHiddenLists) {
|
||||
filters.push("Hidden eq false");
|
||||
} catch (err) {
|
||||
errors.push(`There was an error fetching site users -- ${err.message}`);
|
||||
}
|
||||
if (!showCatalogs) {
|
||||
filters.push("IsCatalog eq false");
|
||||
}
|
||||
let subFilter: string = filters.join(" and ");
|
||||
sp.web.lists
|
||||
.expand("RootFolder", "RoleAssignments", "RoleAssignments/RoleDefinitionBindings", "RoleAssignments/Member",
|
||||
"RoleAssignments/Member/Users", "RoleAssignments/Member/Groups", "RoleAssignments/Member/UserId")
|
||||
.filter(subFilter).inBatch(batch).get()
|
||||
.then((response) => {
|
||||
securityInfo.lists = response.map((listObject) => {
|
||||
let mylist: SPList = new SPList();
|
||||
mylist.isSelected = true;// Should be shown in the UI, user can de-select it in the ui
|
||||
mylist.title = listObject.Title;
|
||||
mylist.id = listObject.Id;
|
||||
mylist.hidden = listObject.Hidden;
|
||||
mylist.serverRelativeUrl = listObject.RootFolder.ServerRelativeUrl;
|
||||
mylist.type = securableType.List;// to differentiate folders from lists
|
||||
mylist.itemCount = listObject.ItemCount;
|
||||
mylist.isExpanded = false;
|
||||
mylist.hasBeenRetrieved = false;
|
||||
mylist.roleAssignments = listObject.RoleAssignments.map((roleAssignmentObject) => {
|
||||
let roleAssignment: SPRoleAssignment = {
|
||||
roleDefinitionIds: roleAssignmentObject.RoleDefinitionBindings.map((rdb) => { return rdb.Id; }),
|
||||
principalId: roleAssignmentObject.PrincipalId
|
||||
};
|
||||
return roleAssignment;
|
||||
});
|
||||
return mylist;
|
||||
});
|
||||
|
||||
}).catch((error) => {
|
||||
//debugger;
|
||||
errors.push(`There was an error fetching lists -- ${error.message} `);
|
||||
//error fetching lists
|
||||
throw error;
|
||||
// Get site groups with users, filtering out 'Limited Access System Group'
|
||||
try {
|
||||
const siteGroupsResponse = await this.sp.web.siteGroups
|
||||
.filter(`Title ne 'Limited Access System Group'`) // Add the filter to exclude this group
|
||||
.expand("Users") // Expand users for each group
|
||||
.select("Title", "Id", "IsHiddenInUI", "IsShareByEmailGuestUse", "IsSiteAdmin", "Users/Id", "Users/LoginName", "Users/PrincipalType")(); // Select required fields
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
securityInfo.siteGroups = siteGroupsResponse.map((grp:any) => {
|
||||
const siteGroup: SPSiteGroup = new SPSiteGroup(grp.Id, grp.Title);
|
||||
for (const user of grp.Users) {
|
||||
if (user.PrincipalType === 4) { // 4 = Security group
|
||||
const adgroupid = new ADGroupId();
|
||||
adgroupid.ADId = user.LoginName.split('|')[2]; // Ensure ADId is a string
|
||||
adgroupid.SPId = user.Id.toString(); // Convert SPId to string
|
||||
siteGroup.adGroupIds.push(adgroupid);
|
||||
} else {
|
||||
siteGroup.userIds.push(user.Id); // SPId is numeric, no conversion needed here
|
||||
}
|
||||
}
|
||||
return siteGroup;
|
||||
});
|
||||
} catch (err) {
|
||||
errors.push(`There was an error fetching site groups -- ${err.message}`);
|
||||
}
|
||||
|
||||
// execute the batch to get sp stuff
|
||||
return batch.execute().then(async () => {
|
||||
// then get the ad stuff
|
||||
// Get role definitions
|
||||
try {
|
||||
const roleDefinitionsResponse = await this.sp.web.roleDefinitions.expand("BasePermissions")();
|
||||
securityInfo.roleDefinitions = roleDefinitionsResponse.map((rd) => {
|
||||
const bp: SPBasePermissions = new SPBasePermissions(rd.BasePermissions.High.toString(), rd.BasePermissions.Low.toString());
|
||||
return new SPRoleDefinition(rd.Id, bp, rd.Description, rd.Hidden, rd.Name);
|
||||
});
|
||||
} catch (err) {
|
||||
errors.push(`There was an error fetching role definitions -- ${err.message}`);
|
||||
}
|
||||
|
||||
var requests = [];
|
||||
var adPromises: Array<Promise<void>> = [];
|
||||
for (let sitegroup of securityInfo.siteGroups) {
|
||||
for (let adGroupId of sitegroup.adGroupIds) {
|
||||
// need to do this in batch
|
||||
var adPromise = this.fetchAdGroup(aadHttpClient, adGroupId)
|
||||
.then((adGroup) => {
|
||||
securityInfo.adGroups.push(adGroup);
|
||||
for (var adUser of adGroup.members) {
|
||||
var siteUser = find(securityInfo.siteUsers, (su, key) => {
|
||||
return su.upn === adUser.userPrincipalName.toLowerCase();
|
||||
});
|
||||
if (siteUser) {
|
||||
siteUser.adId = adUser.id;
|
||||
} else {
|
||||
let user: SPSiteUser = new SPSiteUser();
|
||||
user.adId = adUser.id; user.name = adUser.displayName + "*";
|
||||
user.upn = adUser.userPrincipalName ? adUser.userPrincipalName : adUser.displayName;
|
||||
user.isSelected = true;
|
||||
user.principalType = -1;
|
||||
securityInfo.siteUsers.push(user);
|
||||
}
|
||||
// Get lists with RoleAssignments, RootFolder, etc.
|
||||
try {
|
||||
const filters: string[] = [];
|
||||
if (!showHiddenLists) {
|
||||
filters.push("Hidden eq false");
|
||||
}
|
||||
if (!showCatalogs) {
|
||||
filters.push("IsCatalog eq false");
|
||||
}
|
||||
const subFilter = filters.join(" and ");
|
||||
|
||||
const listsResponse = await this.sp.web.lists
|
||||
.expand("RootFolder", "RoleAssignments", "RoleAssignments/RoleDefinitionBindings", "RoleAssignments/Member", "RoleAssignments/Member/Users", "RoleAssignments/Member/Groups", "RoleAssignments/Member/UserId")
|
||||
.filter(subFilter)();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
securityInfo.lists = listsResponse.map((listObject: any) => {
|
||||
const mylist: SPList = new SPList();
|
||||
mylist.isSelected = true;
|
||||
mylist.title = listObject.Title;
|
||||
mylist.id = listObject.Id;
|
||||
mylist.hidden = listObject.Hidden;
|
||||
mylist.serverRelativeUrl = listObject.RootFolder.ServerRelativeUrl;
|
||||
mylist.itemCount = listObject.ItemCount;
|
||||
mylist.roleAssignments = listObject.RoleAssignments.map((roleAssignmentObject: SPRoleAssignmentObject) => ({
|
||||
roleDefinitionIds: roleAssignmentObject.RoleDefinitionBindings.map((rdb: SPRoleDefinitionBindingObject) => rdb.Id),
|
||||
principalId: roleAssignmentObject.PrincipalId
|
||||
}));
|
||||
return mylist;
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
errors.push(`There was an error fetching lists -- ${err.message}`);
|
||||
}
|
||||
|
||||
// Fetch AD groups and add to securityInfo
|
||||
const adPromises: Array<Promise<void>> = [];
|
||||
for (const sitegroup of securityInfo.siteGroups) {
|
||||
for (const adGroupId of sitegroup.adGroupIds) {
|
||||
const adPromise = this.fetchAdGroup(aadHttpClient, adGroupId)
|
||||
.then((adGroup: ADGroup) => {
|
||||
securityInfo.adGroups.push(adGroup);
|
||||
for (const adUser of adGroup.members) {
|
||||
const siteUser = find(securityInfo.siteUsers, (su) => su.upn === adUser.userPrincipalName?.toLowerCase());
|
||||
if (siteUser) {
|
||||
siteUser.adId = adUser.id;
|
||||
} else {
|
||||
const user: SPSiteUser = new SPSiteUser();
|
||||
user.adId = adUser.id;
|
||||
user.name = adUser.displayName + "*";
|
||||
user.upn = adUser.userPrincipalName || adUser.displayName;
|
||||
user.isSelected = true;
|
||||
user.principalType = -1;
|
||||
securityInfo.siteUsers.push(user);
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
debugger;
|
||||
});
|
||||
adPromises.push(adPromise);
|
||||
|
||||
}
|
||||
}).catch((err: Error) => {
|
||||
errors.push(`There was an error fetching AD groups -- ${err.message}`);
|
||||
});
|
||||
adPromises.push(adPromise);
|
||||
}
|
||||
await Promise.all(adPromises);
|
||||
console.table(securityInfo.siteUsers);
|
||||
debugger;
|
||||
return securityInfo;
|
||||
}).catch((error) => {
|
||||
debugger;
|
||||
// error in batch
|
||||
throw errors;
|
||||
}
|
||||
|
||||
});
|
||||
await Promise.all(adPromises);
|
||||
|
||||
// Final error check
|
||||
if (errors.length > 0) {
|
||||
console.log("Errors encountered:", errors);
|
||||
} else {
|
||||
console.log("No errors encountered, proceeding.");
|
||||
}
|
||||
|
||||
//console.table(securityInfo.siteUsers);
|
||||
return securityInfo;
|
||||
}
|
||||
|
||||
private fetchAdGroup(aadHttpClient: AadHttpClient, adGrouId: ADGroupId): Promise<ADGroup> {
|
||||
var aadHttpClientConfiguration: AadHttpClientConfiguration = new AadHttpClientConfiguration({}, {});
|
||||
// note im not loading nested groups here. Will need to load them on the fly? or maybe here?nvrmind
|
||||
|
||||
|
||||
private fetchAdGroup(aadHttpClient: AadHttpClient, adGrouId: ADGroupId): Promise<ADGroup> {
|
||||
const aadHttpClientConfiguration: AadHttpClientConfiguration = new AadHttpClientConfiguration({}, {});
|
||||
return aadHttpClient.get(`https://graph.microsoft.com/v1.0/groups/${adGrouId.ADId}/transitiveMembers`, aadHttpClientConfiguration)
|
||||
.then((adResponse) => {
|
||||
return adResponse.json()
|
||||
.then((data) => {
|
||||
let adGroup: ADGroup = new ADGroup();
|
||||
.then((data: ADGroupResponse) => {
|
||||
const adGroup: ADGroup = new ADGroup();
|
||||
adGroup.id = adGrouId;
|
||||
adGroup.members = data.value;
|
||||
return adGroup;
|
||||
}).catch((err) => {
|
||||
debugger;
|
||||
alert(`error converting to json`);
|
||||
}).catch(() => {
|
||||
alert(`Error converting to JSON`);
|
||||
return null;
|
||||
});
|
||||
|
||||
}).catch((err) => {
|
||||
debugger;
|
||||
//if 403 show message to grant security
|
||||
alert(`grant app SharePoint Online Client Extensibility Web Application Principal graph permissions Group.Read.All & GroupMembers.Read.All`);
|
||||
}).catch(() => {
|
||||
alert(`Grant app SharePoint Online Client Extensibility Web Application Principal graph permissions Group.Read.All & GroupMembers.Read.All`);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,5 @@ export interface ISpSecurityWebPartProps {
|
|||
showEmail: boolean; //0 show name, 1 show email
|
||||
showSecurityGroups: boolean; // show PrincipalType=4
|
||||
showUsers: boolean; // show PrincipalType=1
|
||||
showOnlyUsersWithPermission;// toggle to show everyone, or justy the users who have the permissions
|
||||
|
||||
|
||||
showOnlyUsersWithPermission: boolean;// toggle to show everyone, or justy the users who have the permissions
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Version } from "@microsoft/sp-core-library";
|
||||
import { AadHttpClient, AadHttpClientConfiguration, HttpClientResponse, IAadHttpClientConfiguration, IAadHttpClientConfigurations, IAadHttpClientOptions } from "@microsoft/sp-http";
|
||||
import { AadHttpClient } from "@microsoft/sp-http";
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
import { IPropertyPaneConfiguration, IPropertyPaneDropdownOption, PropertyPaneCheckbox, PropertyPaneDropdown, PropertyPaneSlider, PropertyPaneTextField, PropertyPaneToggle } from "@microsoft/sp-property-pane";
|
||||
import { IPropertyPaneConfiguration, IPropertyPaneDropdownOption, PropertyPaneCheckbox, PropertyPaneSlider, PropertyPaneToggle } from "@microsoft/sp-property-pane";
|
||||
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
|
||||
import { sp } from "@pnp/sp";
|
||||
import { getSP } from '../../pnpjs-config';
|
||||
import { SPFI } from "@pnp/sp";
|
||||
|
||||
import { PropertyFieldListPicker, PropertyFieldListPickerOrderBy } from '@pnp/spfx-property-controls/lib/PropertyFieldListPicker';
|
||||
import * as React from "react";
|
||||
import * as ReactDom from "react-dom";
|
||||
|
@ -14,30 +15,24 @@ import { PropertyFieldSelectedPermissions } from "./containers/PropertyFieldSele
|
|||
import { ISpSecurityWebPartProps } from "./ISpSecurityWebPartProps";
|
||||
|
||||
export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurityWebPartProps> {
|
||||
private _sp: SPFI = null;
|
||||
private aadHttpClient: AadHttpClient;
|
||||
|
||||
public onInit(): Promise<void> {
|
||||
return super.onInit().then(_ => {
|
||||
sp.setup({
|
||||
spfxContext: this.context,
|
||||
defaultCachingStore: "session", // or "local"
|
||||
defaultCachingTimeoutSeconds: 30,
|
||||
globalCacheDisable: true // or true to disable caching in case of debugging/testing
|
||||
});
|
||||
}).then(_ => {
|
||||
return super.onInit().then(() => {
|
||||
this._sp = getSP(this.context);
|
||||
}).then(() => {
|
||||
this.context.aadHttpClientFactory.getClient(`https://graph.microsoft.com`)
|
||||
.then((client: AadHttpClient) => {
|
||||
// Search for the users with givenName, surname, or displayName equal to the searchFor value
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
.then((client: any) => {
|
||||
this.aadHttpClient = client;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
|
||||
const props: ISpSecurityProps = {
|
||||
//permission: this.properties.permission, // old way
|
||||
selectedPermissions: this.properties.selectedPermissions.map((spp) => { return { ...spp, isChecked: true }; }),
|
||||
selectedPermissions: this.properties.selectedPermissions.map(spp => ({ ...spp, isChecked: true })),
|
||||
showHiddenLists: this.properties.showHiddenLists,
|
||||
showCatalogs: this.properties.showCatalogs,
|
||||
showEmail: this.properties.showEmail,
|
||||
|
@ -51,11 +46,10 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
adminSelectedLists: this.properties.adminSelectedLists,
|
||||
listTitleColumnWidth: this.properties.listTitleColumnWidth,
|
||||
users: this.properties.users,
|
||||
getPermissionTypes: this.getPermissionTypes,
|
||||
aadHttpClient: this.aadHttpClient,
|
||||
domElement: this.domElement
|
||||
|
||||
getPermissionTypes: this.getPermissionTypes.bind(this),
|
||||
aadHttpClient: this.aadHttpClient
|
||||
};
|
||||
|
||||
const element: React.ReactElement<ISpSecurityProps> = React.createElement(
|
||||
SpSecurity, props
|
||||
);
|
||||
|
@ -63,14 +57,10 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse("1.0");
|
||||
}
|
||||
public getPermissionTypes(): IPropertyPaneDropdownOption[] {
|
||||
let perms = new Array();
|
||||
const perms: IPropertyPaneDropdownOption[] = [];
|
||||
for (const perm in SPPermission) {
|
||||
|
||||
if (typeof (SPPermission[perm]) === "object") {
|
||||
if (typeof SPPermission[perm as keyof typeof SPPermission] === "object") {
|
||||
perms.push({
|
||||
text: perm,
|
||||
key: perm
|
||||
|
@ -79,14 +69,11 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
}
|
||||
return perms;
|
||||
}
|
||||
private onPropertyChange(propertyPath: string, oldValue: any, newValue: any) {
|
||||
//debugger;
|
||||
// does this get oldvalue and new value?
|
||||
|
||||
private onPropertyChange(propertyPath: string, newValue: unknown): void {
|
||||
switch (propertyPath) {
|
||||
case "SelectedPermissions":
|
||||
|
||||
this.properties.selectedPermissions = newValue;
|
||||
|
||||
this.properties.selectedPermissions = newValue as ISpSecurityWebPartProps['selectedPermissions'];
|
||||
this.context.propertyPane.refresh();
|
||||
this.render();
|
||||
break;
|
||||
|
@ -106,25 +93,19 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
{
|
||||
groupName: "Permission Settings",
|
||||
groupFields: [
|
||||
|
||||
PropertyFieldSelectedPermissions("SelectedPermissions", {
|
||||
label: "Selected Permissions and Colors",
|
||||
onPropertyChange: this.onPropertyChange.bind(this),
|
||||
|
||||
getSelectedPermissions: () => {
|
||||
return this.properties.selectedPermissions || [];
|
||||
},
|
||||
getSelectedPermissions: () => this.properties.selectedPermissions || [],
|
||||
}),
|
||||
PropertyPaneCheckbox("letUserSelectPermission", {
|
||||
text: "Let user select Permission"
|
||||
}),
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: "User Settings",
|
||||
groupFields: [
|
||||
|
||||
PropertyPaneToggle("showEmail", {
|
||||
label: "Show Email or Name",
|
||||
onText: "Show Email",
|
||||
|
@ -166,22 +147,16 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
{
|
||||
groupName: "Permission Settings",
|
||||
groupFields: [
|
||||
|
||||
PropertyFieldSelectedPermissions("SelectedPermissions", {
|
||||
label: "Selected Permissions and Colors",
|
||||
onPropertyChange: this.onPropertyChange.bind(this),
|
||||
|
||||
getSelectedPermissions: () => {
|
||||
return this.properties.selectedPermissions || [];
|
||||
},
|
||||
getSelectedPermissions: () => this.properties.selectedPermissions || [],
|
||||
}),
|
||||
PropertyPaneCheckbox("letUserSelectPermission", {
|
||||
text: "Let user select Permission"
|
||||
}),
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
groupName: "Display Settings",
|
||||
groupFields: [
|
||||
|
@ -211,18 +186,15 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
PropertyPaneCheckbox("letUserSelectLists", {
|
||||
text: "Let user select Lists"
|
||||
}),
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: "Select Lists",
|
||||
groupFields: [
|
||||
PropertyPaneToggle("includeAdminSelectedLists", {
|
||||
label: "Inclued/exclude selected lists",
|
||||
label: "Include/exclude selected lists",
|
||||
onText: "Include selected lists",
|
||||
offText: "Exclude selected lists",
|
||||
|
||||
|
||||
}),
|
||||
PropertyFieldListPicker("adminSelectedLists", {
|
||||
label: 'Select lists to include/exclude',
|
||||
|
@ -230,7 +202,7 @@ export default class SpSecurityWebPart extends BaseClientSideWebPart<ISpSecurity
|
|||
includeHidden: this.properties.showHiddenLists,
|
||||
orderBy: PropertyFieldListPickerOrderBy.Title,
|
||||
disabled: false,
|
||||
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
|
||||
onPropertyChange: this.onPropertyChange.bind(this),
|
||||
properties: this.properties,
|
||||
context: this.context,
|
||||
onGetErrorMessage: null,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { SPSiteUser } from "../../SPSecurityService";
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
import { } from "@microsoft/sp-webpart-base";
|
||||
import { IPropertyPaneDropdownOption, PropertyPaneDropdown } from "@microsoft/sp-property-pane";
|
||||
import { IPropertyPaneDropdownOption } from "@microsoft/sp-property-pane";
|
||||
import { AadHttpClient } from "@microsoft/sp-http";
|
||||
import {ISelectedPermission} from "../ISpSecurityWebPartProps";
|
||||
|
||||
export interface ISpSecurityProps {
|
||||
users: SPSiteUser[];
|
||||
//permission: string;
|
||||
selectedPermissions:ISelectedPermission[];
|
||||
showHiddenLists: boolean;
|
||||
showCatalogs:boolean;
|
||||
|
@ -22,7 +21,6 @@ export interface ISpSecurityProps {
|
|||
showSecurityGroups:boolean; // show PrincipalType=4
|
||||
showUsers:boolean; // show PrincipalType=1
|
||||
showOnlyUsersWithPermission:boolean; //// toggle to show everyone, or justy the users who have the permissions
|
||||
domElement:any; // needed to disable button postback after render on classic pages
|
||||
|
||||
//domElement:any; // needed to disable button postback after render on classic pages
|
||||
|
||||
}
|
||||
|
|
|
@ -1,39 +1,28 @@
|
|||
import * as React from 'react';
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
import { Stack, StackItem, Alignment } from 'office-ui-fabric-react/lib/Stack';
|
||||
import { IconButton, DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button';
|
||||
import { Stack } from '@fluentui/react/lib/Stack';
|
||||
import { DefaultButton } from '@fluentui/react/lib/Button';
|
||||
import { ISelectedPermission } from "../ISpSecurityWebPartProps";
|
||||
|
||||
export interface ILegendProps {
|
||||
selectedPermissions: Array<ISelectedPermission>;
|
||||
selectedPermissions: ISelectedPermission[];
|
||||
checkUncheckPermission: (perm: ISelectedPermission) => void;
|
||||
}
|
||||
export function Legend(props: ILegendProps): JSX.Element {
|
||||
//debugger;
|
||||
|
||||
return (<Stack horizontal verticalFill wrap={true} gap={3}>
|
||||
{
|
||||
props.selectedPermissions.map((sp) =>
|
||||
<div>
|
||||
export function Legend(props: ILegendProps): JSX.Element {
|
||||
return (
|
||||
<Stack horizontal verticalFill wrap={true} tokens={{ childrenGap: 3 }}>
|
||||
{props.selectedPermissions.map((sp) => (
|
||||
<div key={sp.freindlyName}>
|
||||
<DefaultButton
|
||||
text={sp.freindlyName}
|
||||
onClick={(e) => {
|
||||
props.checkUncheckPermission(sp);
|
||||
}}
|
||||
onClick={() => props.checkUncheckPermission(sp)}
|
||||
checked={sp.isChecked}
|
||||
title={sp.freindlyName}
|
||||
iconProps={{ iconName: sp.iconName, style: { color: sp.color } }}>
|
||||
{sp.freindlyName}
|
||||
</DefaultButton>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
// return (<div>
|
||||
// {
|
||||
// props.selectedPermissions.map((sp)=><span><span> {sp.freindlyName} : </span><span><Icon iconName={sp.iconName} style={{color:sp.color?sp.color:"FFFFFF"}} /></span></span> )
|
||||
// }
|
||||
// </div>
|
||||
// );
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@import "~office-ui-fabric-react/dist/sass/References.scss";
|
||||
@import '~@fluentui/react/dist/sass/References.scss';
|
||||
|
||||
|
||||
.spSecurity {
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,92 +1,47 @@
|
|||
import * as React from 'react';
|
||||
import { Button, PrimaryButton } from 'office-ui-fabric-react/lib/Button';
|
||||
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
||||
import { ColorPicker } from "office-ui-fabric-react/lib/ColorPicker";
|
||||
import { IColor } from "office-ui-fabric-react/lib/Color";
|
||||
import { Dialog } from "office-ui-fabric-react/lib/Dialog";
|
||||
import { TextField } from "office-ui-fabric-react/lib/TextField";
|
||||
import { Icon } from "office-ui-fabric-react/lib/Icon";
|
||||
import { ComboBox, IComboBox, IComboBoxOption, } from "office-ui-fabric-react/lib/ComboBox";
|
||||
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||
import { Button, PrimaryButton } from '@fluentui/react/lib/Button';
|
||||
import { Dropdown, IDropdownOption } from "@fluentui/react/lib/Dropdown";
|
||||
import { ColorPicker } from "@fluentui/react/lib/ColorPicker";
|
||||
import { IColor } from "@fluentui/react/lib/Color";
|
||||
import { Dialog } from "@fluentui/react/lib/Dialog";
|
||||
import { TextField } from "@fluentui/react/lib/TextField";
|
||||
import { Icon } from "@fluentui/react/lib/Icon";
|
||||
import { ComboBox, IComboBoxOption } from "@fluentui/react/lib/ComboBox";
|
||||
import { Label } from '@fluentui/react/lib/Label';
|
||||
import { ISelectedPermission } from "../ISpSecurityWebPartProps";
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
import { findIndex } from "lodash";
|
||||
|
||||
export interface IColorIconSelectorPanelProps {
|
||||
isOpen: boolean;
|
||||
onPermissionChange(color: ISelectedPermission): void;
|
||||
closePanel(): void;
|
||||
onPermissionChange: (perm: ISelectedPermission) => void;
|
||||
closePanel: () => void;
|
||||
title: string;
|
||||
subText: string;
|
||||
currentPerm: ISelectedPermission;
|
||||
SelectedPermissions: ISelectedPermission[];
|
||||
}
|
||||
|
||||
export interface IColorIconSelectionPanelState {
|
||||
currentPerm: ISelectedPermission;
|
||||
}
|
||||
|
||||
export default class ColorIconSelectionPanel extends React.Component<IColorIconSelectorPanelProps, IColorIconSelectionPanelState> {
|
||||
|
||||
private iconNames = [
|
||||
"Add",
|
||||
"Back",
|
||||
"BlockContact",
|
||||
"Cancel",
|
||||
"Cat",
|
||||
"CheckBox",
|
||||
"CheckBoxComposite",
|
||||
"CheckMark",
|
||||
"ChevronDown",
|
||||
"ChevronUp",
|
||||
"CircleFill",
|
||||
"CircleRing",
|
||||
"Clear",
|
||||
"Contact",
|
||||
"Contrast",
|
||||
"Delete",
|
||||
"Design",
|
||||
"eDiscovery",
|
||||
"Edit",
|
||||
"EditSolid12",
|
||||
"Emoji2",
|
||||
"Error",
|
||||
"FavoriteStar",
|
||||
"FavoriteStarFill",
|
||||
"Glasses",
|
||||
"HomeSolid",
|
||||
"IncidentTriangle",
|
||||
"LaptopSecure",
|
||||
"LifeSaver",
|
||||
"LifeSaverLock",
|
||||
"Lock",
|
||||
"LockSolid",
|
||||
"Mail",
|
||||
"More",
|
||||
"PageLink",
|
||||
"People",
|
||||
"PeopleAdd",
|
||||
"Permissions",
|
||||
"PinSolid12",
|
||||
"RedEye",
|
||||
"Sad",
|
||||
"SecurityGroup",
|
||||
"Settings",
|
||||
"Share",
|
||||
"Sync",
|
||||
"TriangleSolid",
|
||||
"UnEditable",
|
||||
"UnEditable2",
|
||||
"Unlock",
|
||||
"UserWarning",
|
||||
"View",
|
||||
|
||||
|
||||
|
||||
private iconNames: string[] = [
|
||||
"Add", "Back", "BlockContact", "Cancel", "Cat", "CheckBox", "CheckBoxComposite", "CheckMark",
|
||||
"ChevronDown", "ChevronUp", "CircleFill", "CircleRing", "Clear", "Contact", "Contrast", "Delete",
|
||||
"Design", "eDiscovery", "Edit", "EditSolid12", "Emoji2", "Error", "FavoriteStar", "FavoriteStarFill",
|
||||
"Glasses", "HomeSolid", "IncidentTriangle", "LaptopSecure", "LifeSaver", "LifeSaverLock", "Lock",
|
||||
"LockSolid", "Mail", "More", "PageLink", "People", "PeopleAdd", "Permissions", "PinSolid12", "RedEye",
|
||||
"Sad", "SecurityGroup", "Settings", "Share", "Sync", "TriangleSolid", "UnEditable", "UnEditable2",
|
||||
"Unlock", "UserWarning", "View"
|
||||
];
|
||||
|
||||
constructor(props: IColorIconSelectorPanelProps) {
|
||||
super(props);
|
||||
//debugger;
|
||||
|
||||
this.state = {
|
||||
currentPerm: this.props.currentPerm
|
||||
currentPerm: { ...this.props.currentPerm } // Ensure immutability of props
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,114 +50,97 @@ export default class ColorIconSelectionPanel extends React.Component<IColorIconS
|
|||
this.onClosePanel();
|
||||
}
|
||||
|
||||
private onClosePanel(element?: any): void {
|
||||
private onClosePanel(): void {
|
||||
this.props.closePanel();
|
||||
}
|
||||
private renderIcon(props?, defaultRender?: (props?) => JSX.Element | null): JSX.Element {
|
||||
|
||||
return (<div>
|
||||
<Icon iconName={props.text} /> {props.text}
|
||||
</div>);
|
||||
private renderIcon(option?: IComboBoxOption): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<Icon iconName={option?.text} /> {option?.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private getIconOptions(): IComboBoxOption[] {
|
||||
let options: IComboBoxOption[] = [];
|
||||
for (var iconName of this.iconNames) {
|
||||
var option: IComboBoxOption = {
|
||||
key: iconName, text: iconName,
|
||||
};
|
||||
options.push(option);
|
||||
}
|
||||
return options;
|
||||
return this.iconNames.map(iconName => ({
|
||||
key: iconName,
|
||||
text: iconName
|
||||
}));
|
||||
}
|
||||
|
||||
public getPermissionTypes(): IDropdownOption[] {
|
||||
let perms = new Array<IDropdownOption>();
|
||||
for (const perm in SPPermission) {
|
||||
if (typeof (SPPermission[perm]) === "object") {
|
||||
perms.push({
|
||||
text: perm,
|
||||
key: perm,
|
||||
disabled: findIndex(this.props.SelectedPermissions, (sp: ISelectedPermission) => { return sp.permission == perm; }) !== -1
|
||||
});
|
||||
return Object.keys(SPPermission).map((perm) => {
|
||||
const isDisabled = findIndex(this.props.SelectedPermissions, sp => sp.permission === perm) !== -1;
|
||||
if (typeof SPPermission[perm as keyof typeof SPPermission] === "object") {
|
||||
return { text: perm, key: perm, disabled: isDisabled };
|
||||
}
|
||||
}
|
||||
return perms;
|
||||
return null;
|
||||
}).filter(Boolean) as IDropdownOption[]; // Remove null values
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const { currentPerm } = this.state;
|
||||
|
||||
//Renders content
|
||||
return (
|
||||
|
||||
<Dialog
|
||||
isOpen={this.props.isOpen}
|
||||
onDismiss={() => this.onClosePanel()}
|
||||
onDismiss={this.onClosePanel.bind(this)}
|
||||
title={this.props.title}
|
||||
subText={this.props.subText}
|
||||
>
|
||||
<Dropdown label="Permission"
|
||||
<Dropdown
|
||||
label="Permission"
|
||||
options={this.getPermissionTypes()}
|
||||
defaultSelectedKey={this.state.currentPerm.permission}
|
||||
onChange={(event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number) => {
|
||||
this.state.currentPerm.permission = option.text;
|
||||
this.setState((current) => ({ ...current, currentPerm: this.state.currentPerm }));
|
||||
}}>
|
||||
</Dropdown>
|
||||
<TextField label="Friendly Name"
|
||||
defaultValue={this.state.currentPerm.freindlyName}
|
||||
onChange={(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
|
||||
this.state.currentPerm.freindlyName = newValue;
|
||||
this.setState((current) => ({ ...current, currentPerm: this.state.currentPerm }));
|
||||
}}>
|
||||
</TextField>
|
||||
<Label>Color:</Label>
|
||||
<ColorPicker
|
||||
color={this.state.currentPerm.color}
|
||||
onChange={(event: React.FormEvent<HTMLDivElement>, color: IColor) => {
|
||||
this.state.currentPerm.color = color.str;
|
||||
this.setState((current) => ({ ...current, currentPerm: this.state.currentPerm }));
|
||||
}}>
|
||||
</ColorPicker>
|
||||
<ComboBox allowFreeform={true}
|
||||
|
||||
label="Select Icon(or enter name of a Fabric Icon):"
|
||||
options={this.getIconOptions()}
|
||||
onRenderOption={this.renderIcon}
|
||||
text={this.state.currentPerm.iconName}
|
||||
onChange={(event: React.FormEvent<IComboBox>, option?: IComboBoxOption, index?: number, value?: string) => {
|
||||
//debugger;
|
||||
this.state.currentPerm.iconName = value ? value : option.text;
|
||||
this.setState((current) => ({ ...current, currentPerm: this.state.currentPerm }));
|
||||
defaultSelectedKey={currentPerm.permission}
|
||||
onChange={(event, option) => {
|
||||
const updatedPerm = { ...currentPerm, permission: option?.text || '' };
|
||||
this.setState({ currentPerm: updatedPerm });
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
label="Freindly Name"
|
||||
defaultValue={currentPerm.freindlyName}
|
||||
onChange={(event, newValue) => {
|
||||
const updatedPerm = { ...currentPerm, freindlyName: newValue || '' };
|
||||
this.setState({ currentPerm: updatedPerm });
|
||||
}}
|
||||
/>
|
||||
<Label>Color:</Label>
|
||||
<ColorPicker
|
||||
color={currentPerm.color}
|
||||
onChange={(event, color: IColor) => {
|
||||
const updatedPerm = { ...currentPerm, color: color.str };
|
||||
this.setState({ currentPerm: updatedPerm });
|
||||
}}
|
||||
/>
|
||||
<ComboBox
|
||||
allowFreeform
|
||||
label="Select Icon (or enter name of a Fabric Icon):"
|
||||
options={this.getIconOptions()}
|
||||
onRenderOption={this.renderIcon.bind(this)}
|
||||
text={currentPerm.iconName}
|
||||
onChange={(event, option, index, value) => {
|
||||
const updatedPerm = { ...currentPerm, iconName: value || option?.text || '' };
|
||||
this.setState({ currentPerm: updatedPerm });
|
||||
}}
|
||||
/>
|
||||
|
||||
<Label>Display:</Label>
|
||||
<Icon iconName={this.state.currentPerm.iconName} style={{ color: this.state.currentPerm.color }} />
|
||||
<br></br>
|
||||
<br></br>
|
||||
<Button onClick={() => {
|
||||
this.onClosePanel();
|
||||
}}>Cancel</Button>
|
||||
<Icon iconName={currentPerm.iconName} style={{ color: currentPerm.color }} />
|
||||
<br />
|
||||
<Button onClick={this.onClosePanel.bind(this)}>Cancel</Button>
|
||||
<PrimaryButton
|
||||
disabled={
|
||||
this.state.currentPerm.color == null ||
|
||||
this.state.currentPerm.freindlyName == null ||
|
||||
this.state.currentPerm.iconName == null ||
|
||||
this.state.currentPerm.permission == null
|
||||
!currentPerm.color ||
|
||||
!currentPerm.freindlyName ||
|
||||
!currentPerm.iconName ||
|
||||
!currentPerm.permission
|
||||
}
|
||||
onClick={() => {
|
||||
//debugger;
|
||||
this.saveChanges();
|
||||
}}
|
||||
>Save</PrimaryButton>
|
||||
|
||||
onClick={this.saveChanges.bind(this)}
|
||||
>
|
||||
Save
|
||||
</PrimaryButton>
|
||||
</Dialog>
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,56 +6,55 @@ import PropertyFieldSelectedPermissionsHost, { IPropertyFieldSelectedPermissions
|
|||
|
||||
export interface IPropertyFieldSelectedPermissionsProps {
|
||||
label: string;
|
||||
initialValue?: Array<ISelectedPermission>;
|
||||
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
|
||||
getSelectedPermissions: () => Array<ISelectedPermission>;
|
||||
initialValue?: ISelectedPermission[]; // Updated type here
|
||||
onPropertyChange(propertyPath: string, oldValue: ISelectedPermission[], newValue: ISelectedPermission[]): void; // Updated type here
|
||||
getSelectedPermissions: () => ISelectedPermission[];
|
||||
}
|
||||
|
||||
export interface IPropertyFieldSelectedPermissionsPropsInternal extends IPropertyPaneCustomFieldProps {
|
||||
label: string;
|
||||
initialValue?: Array<ISelectedPermission>;
|
||||
initialValue?: ISelectedPermission[];
|
||||
targetProperty: string;
|
||||
onRender(elem: HTMLElement): void;
|
||||
onDispose(elem: HTMLElement): void;
|
||||
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
|
||||
SelectedPermissions: Array<ISelectedPermission>;
|
||||
onPropertyChange(propertyPath: string, oldValue: ISelectedPermission[], newValue: ISelectedPermission[]): void; // Updated type here
|
||||
SelectedPermissions: ISelectedPermission[];
|
||||
}
|
||||
|
||||
class PropertyFieldSelectedPermissionsBuilder implements IPropertyPaneField<IPropertyFieldSelectedPermissionsPropsInternal> {
|
||||
//Properties defined by IPropertyPaneField
|
||||
public type = 1;//IPropertyPaneFieldType.Custom;
|
||||
public type = 1; // IPropertyPaneFieldType.Custom
|
||||
public targetProperty: string;
|
||||
public properties: IPropertyFieldSelectedPermissionsPropsInternal;
|
||||
//Custom properties
|
||||
|
||||
private label: string;
|
||||
private onPropertyChange: (propertyPath: string, oldValue: any, newValue: any) => void;
|
||||
private customProperties: any;
|
||||
private onPropertyChange: (propertyPath: string, oldValue: ISelectedPermission[], newValue: ISelectedPermission[]) => void; // Updated type here
|
||||
private customProperties: ISelectedPermission[];
|
||||
|
||||
public constructor(_targetProperty: string, _properties: IPropertyFieldSelectedPermissionsPropsInternal) {
|
||||
|
||||
this.render = this.render.bind(this);
|
||||
this.properties = _properties;
|
||||
this.label = _properties.label;
|
||||
this.properties.onDispose = this.dispose;
|
||||
this.properties.onRender = this.render;
|
||||
this.onPropertyChange = _properties.onPropertyChange;
|
||||
this.customProperties = _properties.SelectedPermissions ? _properties.SelectedPermissions : [];
|
||||
}
|
||||
|
||||
private render(elem: HTMLElement): void {
|
||||
// what other args does this get? does it get ctx and a callback?
|
||||
const element: React.ReactElement<IPropertyFieldSelectedPermissionsHostProps> = React.createElement(PropertyFieldSelectedPermissionsHost, {
|
||||
label: this.label,
|
||||
onPropertyChange: this.onPropertyChange,
|
||||
SelectedPermissions: this.customProperties,
|
||||
|
||||
});
|
||||
ReactDom.render(element, elem);
|
||||
}
|
||||
private dispose(elem: HTMLElement): void {
|
||||
}
|
||||
|
||||
}
|
||||
export function PropertyFieldSelectedPermissions(targetProperty: string, properties: IPropertyFieldSelectedPermissionsProps): IPropertyPaneField<IPropertyFieldSelectedPermissionsPropsInternal> {
|
||||
//Create an internal properties object from the given properties
|
||||
var newProperties: IPropertyFieldSelectedPermissionsPropsInternal = {
|
||||
|
||||
export function PropertyFieldSelectedPermissions(
|
||||
targetProperty: string,
|
||||
properties: IPropertyFieldSelectedPermissionsProps
|
||||
): IPropertyPaneField<IPropertyFieldSelectedPermissionsPropsInternal> {
|
||||
const newProperties: IPropertyFieldSelectedPermissionsPropsInternal = {
|
||||
label: properties.label,
|
||||
targetProperty: targetProperty,
|
||||
key: targetProperty,
|
||||
|
@ -65,9 +64,6 @@ export function PropertyFieldSelectedPermissions(targetProperty: string, propert
|
|||
onDispose: null,
|
||||
onRender: null,
|
||||
};
|
||||
//Calles the PropertyFieldSelectedPermissions builder object
|
||||
//This object will simulate a PropertyFieldCustom to manage his rendering process
|
||||
|
||||
return new PropertyFieldSelectedPermissionsBuilder(targetProperty, newProperties);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
|
||||
|
||||
import { ISelectedPermission } from "../ISpSecurityWebPartProps";
|
||||
import * as React from 'react';
|
||||
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||
import { Button } from 'office-ui-fabric-react/lib/Button';
|
||||
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
|
||||
import { DetailsList, IColumn, DetailsListLayoutMode, SelectionMode } from "office-ui-fabric-react/lib/DetailsList";
|
||||
import { IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
||||
|
||||
|
||||
import { Label } from '@fluentui/react/lib/Label';
|
||||
import { Button } from '@fluentui/react/lib/Button';
|
||||
import { Icon } from '@fluentui/react/lib/Icon';
|
||||
import { DetailsList, IColumn, DetailsListLayoutMode, SelectionMode } from "@fluentui/react/lib/DetailsList";
|
||||
import { IDropdownOption } from "@fluentui/react/lib/Dropdown";
|
||||
import { IPropertyPaneDropdownOption } from "@microsoft/sp-property-pane";
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
|
||||
import SelectedPermissionsPanel from "./SelectedPermissionsPanel";
|
||||
|
||||
export interface IPropertyFieldSelectedPermissionsHostProps {
|
||||
label: string;
|
||||
initialValue?: Array<ISelectedPermission>;
|
||||
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
|
||||
SelectedPermissions: Array<ISelectedPermission>;
|
||||
initialValue?: ISelectedPermission[]; // Updated type here
|
||||
onPropertyChange(propertyPath: string, oldValue: ISelectedPermission[], newValue: ISelectedPermission[]): void; // Updated type here
|
||||
SelectedPermissions: ISelectedPermission[];
|
||||
}
|
||||
|
||||
export interface IPropertyFieldSelectedPermissionsHostState {
|
||||
openPanel?: boolean;
|
||||
SelectedPermissions: Array<ISelectedPermission>;
|
||||
SelectedPermissions: ISelectedPermission[];
|
||||
}
|
||||
|
||||
export default class PropertyFieldSelectedPermissionsHost extends React.Component<IPropertyFieldSelectedPermissionsHostProps, IPropertyFieldSelectedPermissionsHostState> {
|
||||
public panelColumns: IColumn[] = [
|
||||
{
|
||||
|
@ -33,10 +30,10 @@ export default class PropertyFieldSelectedPermissionsHost extends React.Componen
|
|||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
isResizable: true,
|
||||
onRender: (item?: any, index?: number, column?: IColumn) => {
|
||||
onRender: (item?: ISelectedPermission) => { // Updated type here
|
||||
return (
|
||||
<div>
|
||||
{item.permission}
|
||||
{item?.permission}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -48,10 +45,10 @@ export default class PropertyFieldSelectedPermissionsHost extends React.Componen
|
|||
minWidth: 90,
|
||||
maxWidth: 90,
|
||||
isResizable: true,
|
||||
onRender: (item?: any, index?: number, column?: IColumn) => {
|
||||
onRender: (item?: ISelectedPermission) => { // Updated type here
|
||||
return (
|
||||
<div>
|
||||
{item.freindlyName}
|
||||
{item?.freindlyName}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -63,13 +60,14 @@ export default class PropertyFieldSelectedPermissionsHost extends React.Componen
|
|||
minWidth: 50,
|
||||
maxWidth: 50,
|
||||
isResizable: false,
|
||||
onRender: (item?: ISelectedPermission, index?: number, column?: IColumn) => {
|
||||
onRender: (item?: ISelectedPermission) => { // Updated type here
|
||||
return (
|
||||
<Icon iconName={item.iconName} style={{ color: item.color }} />
|
||||
<Icon iconName={item?.iconName} style={{ color: item?.color }} />
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
constructor(props: IPropertyFieldSelectedPermissionsHostProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
@ -77,10 +75,11 @@ export default class PropertyFieldSelectedPermissionsHost extends React.Componen
|
|||
openPanel: false
|
||||
};
|
||||
}
|
||||
|
||||
public getPermissionTypes(): IDropdownOption[] {
|
||||
let perms = new Array();
|
||||
const perms: IPropertyPaneDropdownOption[] = [];
|
||||
for (const perm in SPPermission) {
|
||||
if (typeof (SPPermission[perm]) === "object") {
|
||||
if (typeof SPPermission[perm as keyof typeof SPPermission] === "object") {
|
||||
perms.push({
|
||||
text: perm,
|
||||
key: perm
|
||||
|
@ -89,16 +88,16 @@ export default class PropertyFieldSelectedPermissionsHost extends React.Componen
|
|||
}
|
||||
return perms;
|
||||
}
|
||||
private onOpenPanel(element?: any): void {
|
||||
this.setState((current) => ({ ...current, openPanel: true }));
|
||||
|
||||
private onOpenPanel(): void {
|
||||
this.setState({ openPanel: true });
|
||||
}
|
||||
private onClosePanel(element?: any): void {
|
||||
//debugger;
|
||||
this.setState((current) => ({ ...current, openPanel: false }));
|
||||
|
||||
private onClosePanel(): void {
|
||||
this.setState({ openPanel: false });
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
//debugger;
|
||||
//This Details list Renders the short list of permissions in the panel
|
||||
return (
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
<Label>{this.props.label}</Label>
|
||||
|
@ -108,26 +107,20 @@ export default class PropertyFieldSelectedPermissionsHost extends React.Componen
|
|||
layoutMode={DetailsListLayoutMode.justified}
|
||||
selectionMode={SelectionMode.none}
|
||||
/>
|
||||
<Button
|
||||
onClick={(e) => this.onOpenPanel()}>
|
||||
<Button onClick={() => this.onOpenPanel()}>
|
||||
Edit Permissions and Colors
|
||||
</Button>
|
||||
</Button>
|
||||
|
||||
<SelectedPermissionsPanel
|
||||
isOpen={this.state.openPanel}
|
||||
onPropertyChange={(prop, oldval, newval) => {
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...newval] }));
|
||||
onPropertyChange={(prop: string, oldval: ISelectedPermission[], newval: ISelectedPermission[]) => {
|
||||
this.setState({ SelectedPermissions: [...newval] });
|
||||
this.props.onPropertyChange("SelectedPermissions", this.props.SelectedPermissions, newval);
|
||||
|
||||
|
||||
}}
|
||||
closePanel={() => { this.onClosePanel(); }}
|
||||
SelectedPermissions={this.props.SelectedPermissions}
|
||||
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,32 +1,29 @@
|
|||
|
||||
import { findIndex, filter, first } from "underscore";
|
||||
import { findIndex, filter } from "lodash";
|
||||
import { ISelectedPermission } from "../ISpSecurityWebPartProps";
|
||||
import * as React from 'react';
|
||||
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||
import { Button } from 'office-ui-fabric-react/lib/Button';
|
||||
import { Panel, PanelType } from 'office-ui-fabric-react/lib/Panel';
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
import { IconButton } from 'office-ui-fabric-react/lib/Button';
|
||||
import { DetailsList, IColumn, DetailsListLayoutMode, SelectionMode, Selection } from "office-ui-fabric-react/lib/DetailsList";
|
||||
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
||||
import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
|
||||
import { Label } from '@fluentui/react/lib/Label';
|
||||
import { Button, IconButton } from '@fluentui/react/lib/Button';
|
||||
import { Panel, PanelType } from '@fluentui/react/lib/Panel';
|
||||
import { Icon } from '@fluentui/react/lib/Icon';
|
||||
import { DetailsList, IColumn, DetailsListLayoutMode, SelectionMode, Selection } from "@fluentui/react/lib/DetailsList";
|
||||
import { Dropdown, IDropdownOption } from "@fluentui/react/lib/Dropdown";
|
||||
import { CommandBar } from "@fluentui/react/lib/CommandBar";
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
import ColorIconSelectorDialog from "./ColorIconSelectorDialog";
|
||||
import { disableBodyScroll } from "@uifabric/utilities";
|
||||
|
||||
export interface ISelectedPemissionPanelProps {
|
||||
isOpen: boolean;
|
||||
onPropertyChange(propertyPath: string, oldValue: any, newValue: any): void;
|
||||
closePanel(): void;
|
||||
|
||||
SelectedPermissions: Array<ISelectedPermission>;
|
||||
onPropertyChange(propertyPath: string, oldValue: ISelectedPermission[], newValue: ISelectedPermission[]): void;
|
||||
closePanel: () => void;
|
||||
SelectedPermissions: ISelectedPermission[];
|
||||
}
|
||||
export interface ISelectedPemissionPanelState {
|
||||
|
||||
SelectedPermissions: Array<ISelectedPermission>;
|
||||
export interface ISelectedPemissionPanelState {
|
||||
SelectedPermissions: ISelectedPermission[];
|
||||
CurrentlySelectedPermission?: ISelectedPermission;
|
||||
isColorIconSelecorDialogOpen: boolean;
|
||||
}
|
||||
|
||||
export default class SelectedPermissionsPanel extends React.Component<ISelectedPemissionPanelProps, ISelectedPemissionPanelState> {
|
||||
private selection: Selection;
|
||||
private columns: IColumn[] = [
|
||||
|
@ -37,19 +34,17 @@ export default class SelectedPermissionsPanel extends React.Component<ISelectedP
|
|||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
isResizable: true,
|
||||
onRender: (item?: any, index?: number, column?: IColumn) => {
|
||||
|
||||
onRender: (item: ISelectedPermission) => {
|
||||
return (
|
||||
<Dropdown
|
||||
options={this.getPermissionTypes()}
|
||||
defaultSelectedKey={item.permission}
|
||||
onChange={(event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, ix?: number) => {
|
||||
var sps = this.state.SelectedPermissions;
|
||||
item.permission = option.text;
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...this.state.SelectedPermissions] }));
|
||||
|
||||
}}>
|
||||
</Dropdown>
|
||||
onChange={(event, option) => {
|
||||
const sps = [...this.state.SelectedPermissions];
|
||||
item.permission = option?.text ?? '';
|
||||
this.setState({ SelectedPermissions: [...sps] });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -59,34 +54,35 @@ export default class SelectedPermissionsPanel extends React.Component<ISelectedP
|
|||
fieldName: 'freindlyName',
|
||||
minWidth: 150,
|
||||
maxWidth: 150,
|
||||
isResizable: true
|
||||
|
||||
isResizable: true,
|
||||
onRender: (item: ISelectedPermission) => {
|
||||
return <div>{item.freindlyName}</div>;
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'color',
|
||||
name: 'Display',
|
||||
fieldName: 'color',
|
||||
minWidth: 100,
|
||||
maxWidth: 100,
|
||||
minWidth: 150,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
onRender: (item?: ISelectedPermission, index?: number, column?: IColumn) => {
|
||||
onRender: (item: ISelectedPermission) => {
|
||||
return (
|
||||
<div>
|
||||
<Icon iconName={item.iconName} style={{ color: item.color }} />
|
||||
|
||||
<Button onClick={(e) => {
|
||||
this.setState((current) => ({
|
||||
...current,
|
||||
isColorIconSelecorDialogOpen: true,
|
||||
CurrentlySelectedPermission: item
|
||||
}));
|
||||
}}>Edit Permission</Button>
|
||||
<Icon iconName={item.iconName} style={{ color: item.color, paddingRight: '10px' }} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
isColorIconSelecorDialogOpen: true,
|
||||
CurrentlySelectedPermission: item
|
||||
});
|
||||
}}
|
||||
>
|
||||
Edit Permission
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
key: 'commands',
|
||||
|
@ -95,184 +91,146 @@ export default class SelectedPermissionsPanel extends React.Component<ISelectedP
|
|||
minWidth: 50,
|
||||
maxWidth: 50,
|
||||
isResizable: false,
|
||||
onRender: (item?: any, index?: number, column?: IColumn) => {
|
||||
onRender: (item: ISelectedPermission, index: number) => {
|
||||
return (
|
||||
<div>
|
||||
<IconButton
|
||||
iconProps={{ iconName: 'Up', }}
|
||||
style={{ display: index === 0 ? "none" : "normal" }}
|
||||
onClick={(e) => {
|
||||
this.moveColumnUp(item);
|
||||
}}>
|
||||
</IconButton>
|
||||
|
||||
iconProps={{ iconName: 'Up' }}
|
||||
disabled={index === 0}
|
||||
onClick={() => this.moveColumnUp(item)}
|
||||
/>
|
||||
<IconButton
|
||||
iconProps={{ iconName: 'Down', }}
|
||||
style={{ display: index === this.state.SelectedPermissions.length - 1 ? "none" : "normal" }}
|
||||
onClick={(e) => {
|
||||
this.moveColumnDown(item);
|
||||
}}>
|
||||
</IconButton>
|
||||
|
||||
iconProps={{ iconName: 'Down' }}
|
||||
disabled={index === this.state.SelectedPermissions.length - 1}
|
||||
onClick={() => this.moveColumnDown(item)}
|
||||
/>
|
||||
<IconButton
|
||||
iconProps={{ iconName: 'Delete', }}
|
||||
onClick={(e) => {
|
||||
this.removeColumn(item);
|
||||
}}>
|
||||
</IconButton>
|
||||
iconProps={{ iconName: 'Delete' }}
|
||||
onClick={() => this.removeColumn(item)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
];
|
||||
|
||||
constructor(props: ISelectedPemissionPanelProps) {
|
||||
super(props);
|
||||
this.selection = new Selection({
|
||||
onSelectionChanged: () => console.log("onSelectionChanged...")
|
||||
});
|
||||
this.selection = new Selection();
|
||||
this.state = {
|
||||
SelectedPermissions: this.props.SelectedPermissions,
|
||||
isColorIconSelecorDialogOpen: false,
|
||||
|
||||
SelectedPermissions: props.SelectedPermissions,
|
||||
isColorIconSelecorDialogOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
public getPermissionTypes(): IDropdownOption[] {
|
||||
let perms = new Array<IDropdownOption>();
|
||||
const perms: IDropdownOption[] = [];
|
||||
for (const perm in SPPermission) {
|
||||
if (typeof (SPPermission[perm]) === "object") {
|
||||
if (typeof SPPermission[perm as keyof typeof SPPermission] === "object") {
|
||||
perms.push({
|
||||
text: perm,
|
||||
key: perm,
|
||||
disabled: findIndex(this.state.SelectedPermissions, (sp: ISelectedPermission) => { return sp.permission == perm; }) !== -1
|
||||
disabled: findIndex(this.state.SelectedPermissions, sp => sp.permission === perm) !== -1
|
||||
});
|
||||
}
|
||||
}
|
||||
return perms;
|
||||
}
|
||||
|
||||
// private addColumn(): void {
|
||||
// let unusedPermission = first(filter(this.getPermissionTypes(), (pt) => { return !pt.disabled }));
|
||||
// if (unusedPermission) {
|
||||
|
||||
// const col: ISelectedPermission = {
|
||||
// "permission": unusedPermission.text,
|
||||
// "freindlyName": unusedPermission.text,
|
||||
// color: "FFFFFF",
|
||||
// iconName: "Blocked"
|
||||
// };
|
||||
// var sp = this.state.SelectedPermissions;
|
||||
// sp.push(col);
|
||||
// this.setState((current) => ({ ...current, SelectedPermissions: [...sp] }));
|
||||
// }
|
||||
// }
|
||||
private removeColumn(column: ISelectedPermission): void {
|
||||
var sps = filter(this.state.SelectedPermissions, (o: ISelectedPermission) => { return o.permission !== column.permission; });
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...sps] }));
|
||||
const sps = filter(this.state.SelectedPermissions, (o: ISelectedPermission) => o.permission !== column.permission);
|
||||
this.setState({ SelectedPermissions: sps });
|
||||
}
|
||||
|
||||
private removeAllColumns(): void {
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [] }));
|
||||
this.setState({ SelectedPermissions: [] });
|
||||
}
|
||||
|
||||
private moveColumnUp(column: ISelectedPermission): void {
|
||||
|
||||
var sps: ISelectedPermission[] = this.state.SelectedPermissions;
|
||||
const index = findIndex(sps, (sp: ISelectedPermission) => { return sp.permission == column.permission; });
|
||||
if (index != -1) {
|
||||
sps[index] = sps.splice(index - 1, 1, sps[index])[0];
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...sps] }));
|
||||
const sps = [...this.state.SelectedPermissions];
|
||||
const index = findIndex(sps, sp => sp.permission === column.permission);
|
||||
if (index > 0) {
|
||||
[sps[index - 1], sps[index]] = [sps[index], sps[index - 1]];
|
||||
this.setState({ SelectedPermissions: sps });
|
||||
}
|
||||
}
|
||||
|
||||
private moveColumnDown(column: ISelectedPermission): void {
|
||||
const sps = [...this.state.SelectedPermissions];
|
||||
const index = findIndex(sps, sp => sp.permission === column.permission);
|
||||
if (index < sps.length - 1) {
|
||||
[sps[index], sps[index + 1]] = [sps[index + 1], sps[index]];
|
||||
this.setState({ SelectedPermissions: sps });
|
||||
}
|
||||
}
|
||||
|
||||
var sps: ISelectedPermission[] = this.state.SelectedPermissions;
|
||||
const index = findIndex(sps, (sp: ISelectedPermission) => { return sp.permission == column.permission; });
|
||||
if (index != -1) {
|
||||
sps[index] = sps.splice(index + 1, 1, sps[index])[0];
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...sps] }));
|
||||
}
|
||||
}
|
||||
private saveChanges(): void {
|
||||
if (this.props.onPropertyChange) {
|
||||
this.props.onPropertyChange("SelectedPermissions", this.props.SelectedPermissions, this.state.SelectedPermissions);
|
||||
this.onClosePanel();
|
||||
}
|
||||
this.props.onPropertyChange("SelectedPermissions", this.props.SelectedPermissions, this.state.SelectedPermissions);
|
||||
this.onClosePanel();
|
||||
}
|
||||
private onOpenPanel(element?: any): void {
|
||||
this.setState((current) => ({ ...current, openPanel: true }));
|
||||
}
|
||||
private onClosePanel(element?: any): void {
|
||||
|
||||
private onClosePanel(): void {
|
||||
this.props.closePanel();
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
|
||||
//Renders content
|
||||
return (
|
||||
|
||||
<Panel
|
||||
isOpen={this.props.isOpen} hasCloseButton={true}
|
||||
isOpen={this.props.isOpen}
|
||||
hasCloseButton={true}
|
||||
onDismiss={() => this.onClosePanel()}
|
||||
isLightDismiss={true} type={PanelType.largeFixed}
|
||||
headerText="Select Permissions" >
|
||||
isLightDismiss={true}
|
||||
type={PanelType.largeFixed}
|
||||
headerText="Select Permissions"
|
||||
>
|
||||
<Label>The grid will display the color of the first match, so list permissions from most restricted to least restricted (i.e. manageLists, then deleteListItems, then viewListItems)</Label>
|
||||
<CommandBar items={[{
|
||||
key: "AddColumns",
|
||||
name: "Add a Permission",
|
||||
icon: "Add",
|
||||
onClick: () => {
|
||||
this.setState((current) => ({
|
||||
...current,
|
||||
isColorIconSelecorDialogOpen: true,
|
||||
CurrentlySelectedPermission: { color: null, freindlyName: null, iconName: null, permission: null }
|
||||
}));
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ClearAllColums",
|
||||
name: "Remove All Permissions",
|
||||
canCheck: true,
|
||||
icon: "Delete",
|
||||
onClick: () => {
|
||||
this.removeAllColumns();
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
key: "save",
|
||||
name: "Save",
|
||||
canCheck: true,
|
||||
icon: "Save",
|
||||
onClick: () => {
|
||||
|
||||
this.saveChanges();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
]} />
|
||||
{this.state.isColorIconSelecorDialogOpen &&
|
||||
<CommandBar
|
||||
items={[
|
||||
{
|
||||
key: "AddColumns",
|
||||
name: "Add a Permission",
|
||||
icon: "Add",
|
||||
onClick: () => {
|
||||
this.setState({
|
||||
isColorIconSelecorDialogOpen: true,
|
||||
CurrentlySelectedPermission: { color: '', freindlyName: '', iconName: '', permission: '' }
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ClearAllColumns",
|
||||
name: "Remove All Permissions",
|
||||
icon: "Delete",
|
||||
onClick: () => this.removeAllColumns()
|
||||
},
|
||||
{
|
||||
key: "save",
|
||||
name: "Save",
|
||||
icon: "Save",
|
||||
onClick: () => this.saveChanges()
|
||||
}
|
||||
]}
|
||||
/>
|
||||
{this.state.isColorIconSelecorDialogOpen && (
|
||||
<ColorIconSelectorDialog
|
||||
isOpen={this.state.isColorIconSelecorDialogOpen}
|
||||
SelectedPermissions={this.state.SelectedPermissions}
|
||||
title={`Edit Icon and color for ${this.state.CurrentlySelectedPermission.permission}`}
|
||||
subText={`Edit Icon and color for ${this.state.CurrentlySelectedPermission.permission}`}
|
||||
title={`Edit Icon and color for ${this.state.CurrentlySelectedPermission?.permission}`}
|
||||
subText={`Edit Icon and color for ${this.state.CurrentlySelectedPermission?.permission}`}
|
||||
currentPerm={this.state.CurrentlySelectedPermission}
|
||||
closePanel={() => {
|
||||
this.setState((current) => ({ ...current, isColorIconSelecorDialogOpen: false }));
|
||||
}}
|
||||
closePanel={() => this.setState({ isColorIconSelecorDialogOpen: false })}
|
||||
onPermissionChange={(perm: ISelectedPermission) => {
|
||||
//debugger;
|
||||
var sps = this.state.SelectedPermissions;
|
||||
const idx = findIndex(sps, (sp: ISelectedPermission) => { return sp.permission == perm.permission; });
|
||||
const sps = [...this.state.SelectedPermissions];
|
||||
const idx = findIndex(sps, sp => sp.permission === perm.permission);
|
||||
if (idx === -1) {
|
||||
sps.push(perm);
|
||||
} else {
|
||||
sps[idx] = perm;
|
||||
}
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...sps] }));
|
||||
this.setState({ SelectedPermissions: sps });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
<DetailsList
|
||||
items={this.state.SelectedPermissions}
|
||||
columns={this.columns}
|
||||
|
@ -281,9 +239,6 @@ export default class SelectedPermissionsPanel extends React.Component<ISelectedP
|
|||
selection={this.selection}
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||
{ "extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json",
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
|
@ -26,13 +22,14 @@
|
|||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
"es2015.collection",
|
||||
"es2015.promise"
|
||||
],
|
||||
"noImplicitAny": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
"forin": false,
|
||||
"label-position": false,
|
||||
"member-access": true,
|
||||
"no-arg": false,
|
||||
"no-console": false,
|
||||
"no-construct": false,
|
||||
"no-duplicate-variable": true,
|
||||
"no-eval": false,
|
||||
"no-function-expression": true,
|
||||
"no-internal-module": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-unnecessary-semicolons": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue