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,51 +1,46 @@
|
|||
{
|
||||
"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",
|
||||
|
|
|
@ -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; })) {
|
||||
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
debugger;
|
||||
alert(`adGroup ${ADGroupId} was not in the collection of ad groups.`);
|
||||
}
|
||||
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);
|
||||
// }
|
||||
// }
|
||||
public static findGroup(groupId: number, groups: SPSiteGroup[]): SPSiteGroup {
|
||||
return find(groups, (g) => g.id === groupId)!;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return selectedRoleAssignments;
|
||||
} catch (exception) {
|
||||
//debugger;
|
||||
console.error(exception);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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) {// its a file
|
||||
} else if (listItem.File) {
|
||||
itemToAdd.type = 'File';
|
||||
itemToAdd.title = listItem.File.Name;
|
||||
itemToAdd.serverRelativeUrl = listItem.File.ServerRelativeUrl;
|
||||
} else { // its a listitem
|
||||
} else {
|
||||
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();
|
||||
public async loadData(showHiddenLists: boolean, showCatalogs: boolean, aadHttpClient: AadHttpClient): Promise<SPSecurityInfo> {
|
||||
const securityInfo: SPSecurityInfo = new SPSecurityInfo();
|
||||
const errors: string[] = [];
|
||||
|
||||
// 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.toLocaleLowerCase() : u.Title;// switching key in react from id to upn. ensure upn is not undefined
|
||||
user.upn = upn ? upn.toLowerCase() : u.Title;
|
||||
if (u.UserId) {
|
||||
user.userId = new SPExternalUser();
|
||||
user.userId.nameId = u.UserId.NameId;
|
||||
user.userId.nameIdIssuer = u.UserId.NameIdIssuer;
|
||||
user.userId = { nameId: u.UserId.NameId, nameIdIssuer: u.UserId.NameIdIssuer };
|
||||
}
|
||||
return user;
|
||||
});
|
||||
} catch (err) {
|
||||
errors.push(`There was an error fetching site users -- ${err.message}`);
|
||||
}
|
||||
|
||||
// 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;
|
||||
});
|
||||
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) => {
|
||||
// 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
|
||||
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
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}`);
|
||||
}
|
||||
|
||||
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;
|
||||
// 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}`);
|
||||
}
|
||||
|
||||
|
||||
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[] = [];
|
||||
// Get lists with RoleAssignments, RootFolder, etc.
|
||||
try {
|
||||
const filters: string[] = [];
|
||||
if (!showHiddenLists) {
|
||||
filters.push("Hidden eq false");
|
||||
}
|
||||
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
|
||||
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.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; }),
|
||||
mylist.roleAssignments = listObject.RoleAssignments.map((roleAssignmentObject: SPRoleAssignmentObject) => ({
|
||||
roleDefinitionIds: roleAssignmentObject.RoleDefinitionBindings.map((rdb: SPRoleDefinitionBindingObject) => 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;
|
||||
} catch (err) {
|
||||
errors.push(`There was an error fetching lists -- ${err.message}`);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// execute the batch to get sp stuff
|
||||
return batch.execute().then(async () => {
|
||||
// then get the ad stuff
|
||||
|
||||
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) => {
|
||||
// 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 (var adUser of adGroup.members) {
|
||||
var siteUser = find(securityInfo.siteUsers, (su, key) => {
|
||||
return su.upn === adUser.userPrincipalName.toLowerCase();
|
||||
});
|
||||
for (const adUser of adGroup.members) {
|
||||
const siteUser = find(securityInfo.siteUsers, (su) => 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;
|
||||
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;
|
||||
}).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;
|
||||
|
||||
});
|
||||
// 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
|
||||
|
||||
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>
|
||||
);
|
||||
// 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 {
|
||||
}
|
||||
|
|
|
@ -1,616 +1,486 @@
|
|||
import { Environment, EnvironmentType } from '@microsoft/sp-core-library';
|
||||
import { SPPermission } from "@microsoft/sp-page-context";
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { SPPermission } from '@microsoft/sp-page-context';
|
||||
import { getFileTypeIconProps, initializeFileTypeIcons } from '@uifabric/file-type-icons';
|
||||
import { filter, find, findIndex } from "lodash";
|
||||
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
|
||||
import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
|
||||
import { ContextualMenuItemType, IContextualMenuItem } from "office-ui-fabric-react/lib/ContextualMenu";
|
||||
import { DetailsList, IColumn, Selection, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList';
|
||||
import { Icon } from 'office-ui-fabric-react/lib/Icon';
|
||||
import { Panel, PanelType } from "office-ui-fabric-react/lib/Panel";
|
||||
import { Spinner } from "office-ui-fabric-react/lib/Spinner";
|
||||
import * as React from "react";
|
||||
import { filter, find, findIndex } from 'lodash';
|
||||
import { Checkbox } from '@fluentui/react/lib/Checkbox';
|
||||
import { CommandBar } from '@fluentui/react/lib/CommandBar';
|
||||
import { ContextualMenuItemType, IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu';
|
||||
import { DetailsList, IColumn, Selection, SelectionMode } from '@fluentui/react/lib/DetailsList';
|
||||
import { Icon } from '@fluentui/react/lib/Icon';
|
||||
import { Panel, PanelType } from '@fluentui/react/lib/Panel';
|
||||
import { Spinner } from '@fluentui/react/lib/Spinner';
|
||||
import SPSecurityService from '../../SPSecurityService';
|
||||
import { Helpers, SPList, SPListItem, SPSiteUser } from '../../SPSecurityService';
|
||||
import SelectedPermissionsPanel from '../containers/SelectedPermissionsPanel';
|
||||
import { ISelectedPermission } from '../ISpSecurityWebPartProps';
|
||||
import { ISpSecurityProps } from './ISpSecurityProps';
|
||||
import { ISpSecurityState } from './ISpSecurityState';
|
||||
import { Legend } from './Legend';
|
||||
import styles from './SpSecurity.module.scss';
|
||||
|
||||
import SPSecurityService from "../../SPSecurityService";
|
||||
import { Helpers, SPList, SPListItem, SPSiteUser } from "../../SPSecurityService";
|
||||
import SelectedPermissionsPanel from "../containers/SelectedPermissionsPanel";
|
||||
import { ISelectedPermission } from "../ISpSecurityWebPartProps";
|
||||
import { ISpSecurityProps } from "./ISpSecurityProps";
|
||||
import { ISpSecurityState } from "./ISpSecurityState";
|
||||
import { Legend } from "./Legend";
|
||||
import styles from "./SpSecurity.module.scss";
|
||||
|
||||
/* tslint:disable */
|
||||
export default class SpSecurity extends React.Component<ISpSecurityProps, ISpSecurityState> {
|
||||
private svc: SPSecurityService = new SPSecurityService("ss");
|
||||
private userSelection = new Selection();
|
||||
private listSelection = new Selection();
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
const SpSecurity: React.FC<ISpSecurityProps> = (props) => {
|
||||
const [state, setState] = useState<ISpSecurityState>({
|
||||
securityInfo: { siteUsers: [], siteGroups: [], roleDefinitions: [], lists: [], adGroups: [] },
|
||||
//permission: this.props.permission,
|
||||
selectedPermissions: this.props.selectedPermissions,
|
||||
selectedPermissions: props.selectedPermissions,
|
||||
showUserPanel: false,
|
||||
showListPanel: false,
|
||||
showEmail: this.props.showEmail,
|
||||
showEmail: props.showEmail,
|
||||
securityInfoLoaded: false,
|
||||
showPermissionsPanel: false,
|
||||
errors: []
|
||||
errors: [],
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.expandCollapseList = this.expandCollapseList.bind(this);
|
||||
this.collapseList = this.collapseList.bind(this);
|
||||
this.collapseItem = this.collapseItem.bind(this);
|
||||
this.expandList = this.expandList.bind(this);
|
||||
this.renderTitle = this.renderTitle.bind(this);
|
||||
this.renderUserItem = this.renderUserItem.bind(this);
|
||||
this.getPermissionLevels = this.getPermissionLevels.bind(this);
|
||||
this.selectUser = this.selectUser.bind(this);
|
||||
this.parentIsExpanded = this.parentIsExpanded.bind(this);
|
||||
this.renderUserSelected = this.renderUserSelected.bind(this);
|
||||
}
|
||||
public componentDidMount(): void {
|
||||
const svc = new SPSecurityService('ss');
|
||||
const userSelection = new Selection();
|
||||
const listSelection = new Selection();
|
||||
|
||||
useEffect(() => {
|
||||
initializeFileTypeIcons();
|
||||
}
|
||||
public componentDidUpdate(): void {
|
||||
// disable postback of buttons. see https://github.com/SharePoint/sp-dev-docs/issues/492
|
||||
|
||||
if (Environment.type === EnvironmentType.ClassicSharePoint) {
|
||||
const buttons: NodeListOf<HTMLButtonElement> = this.props.domElement.getElementsByTagName('button');
|
||||
for (let i: number = 0; i < buttons.length; i++) {
|
||||
if (buttons[i]) {
|
||||
// Disable the button onclick postback
|
||||
buttons[i].onclick = function () {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public componentWillReceiveProps(newProps: ISpSecurityProps) {
|
||||
|
||||
this.setState((current) => ({
|
||||
...current,
|
||||
selectedPermissions: newProps.selectedPermissions,
|
||||
showEmail: newProps.showEmail
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
public componentWillMount(): void {
|
||||
//debugger;
|
||||
this.svc.loadData(this.props.showHiddenLists, this.props.showCatalogs, this.props.aadHttpClient, false)
|
||||
svc.loadData(props.showHiddenLists, props.showCatalogs, props.aadHttpClient)
|
||||
.then((response) => {
|
||||
const state: ISpSecurityState = {
|
||||
securityInfo: response,
|
||||
// permission: this.props.permission,
|
||||
selectedPermissions: this.props.selectedPermissions ? this.props.selectedPermissions : [],
|
||||
showUserPanel: false,
|
||||
showListPanel: false,
|
||||
showPermissionsPanel: false,
|
||||
showEmail: this.props.showEmail,
|
||||
const filteredLists = response.lists.filter((list) =>
|
||||
props.includeAdminSelectedLists
|
||||
? !!find(props.adminSelectedLists, (asl) => list.id === asl)
|
||||
: !find(props.adminSelectedLists, (asl) => list.id === asl)
|
||||
);
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
securityInfo: { ...response, lists: filteredLists },
|
||||
selectedPermissions: props.selectedPermissions ?? [],
|
||||
showEmail: props.showEmail,
|
||||
securityInfoLoaded: true,
|
||||
errors: []
|
||||
|
||||
};
|
||||
// inlclude\exclude lists selected in property pane
|
||||
//debugger;
|
||||
state.securityInfo.lists = state.securityInfo.lists.filter((list) => {
|
||||
if (this.props.includeAdminSelectedLists) { // include the lists
|
||||
|
||||
if (find(this.props.adminSelectedLists, (asl) => { return list.id === asl; })) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else { // exclude the lists
|
||||
if (find(this.props.adminSelectedLists, (asl) => { return list.id === asl; })) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
this.setState(state);
|
||||
}).catch((errors: Array<string>) => {
|
||||
this.setState((current) => ({ ...current, errors: errors, securityInfoLoaded: true }))
|
||||
//debugger;
|
||||
}));
|
||||
})
|
||||
.catch((errors: string[]) => {
|
||||
setState((prevState) => ({ ...prevState, errors, securityInfoLoaded: true }));
|
||||
});
|
||||
}, [props]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.selectedPermissions !== state.selectedPermissions || props.showEmail !== state.showEmail) {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
selectedPermissions: props.selectedPermissions,
|
||||
showEmail: props.showEmail,
|
||||
}));
|
||||
}
|
||||
public expandList(item: any): any {
|
||||
if (item instanceof SPListItem && !item.serverRelativeUrl) { // its a listitem. nothing to do
|
||||
return;
|
||||
}
|
||||
}, [props.selectedPermissions, props.showEmail]);
|
||||
|
||||
const expandList = (item: SPList | SPListItem): void => {
|
||||
if (item instanceof SPListItem && !item.serverRelativeUrl) return;
|
||||
|
||||
if (item.isFetched) {
|
||||
item.isExpanded = true;
|
||||
this.setState(this.state);
|
||||
setState({ ...state });
|
||||
} else {
|
||||
const level = item instanceof SPListItem ? item.level + 1 : 1;
|
||||
const listTitle = item instanceof SPListItem ? item.listTitle : item.title;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// get the items in the list/folder;
|
||||
let level: number;
|
||||
let listTitle: string;
|
||||
if (item instanceof SPListItem) {
|
||||
level = item.level + 1;
|
||||
listTitle = item.listTitle;
|
||||
}
|
||||
else {
|
||||
level = 1;
|
||||
listTitle = item.title;
|
||||
}
|
||||
|
||||
this.svc.loadFolderRoleAssigmentsDefinitionsMembers(listTitle, item.serverRelativeUrl, item.id, level, true).then((response) => {
|
||||
|
||||
// add them to the list after the parent
|
||||
|
||||
let position: number = findIndex(this.state.securityInfo.lists, (stateitem) => {
|
||||
return stateitem.id === item.id;
|
||||
});
|
||||
this.state.securityInfo.lists.splice(position + 1, 0, ...response);
|
||||
svc.loadFolderRoleAssignmentsDefinitionsMembers(listTitle, item.serverRelativeUrl, item.id, level)
|
||||
.then((response) => {
|
||||
const position = findIndex(state.securityInfo.lists, (stateItem) => stateItem.id === item.id);
|
||||
const updatedLists = [...state.securityInfo.lists];
|
||||
updatedLists.splice(position + 1, 0, ...response);
|
||||
item.isExpanded = true;
|
||||
item.isFetched = true;
|
||||
this.setState(this.state);
|
||||
|
||||
}).catch((error) => {
|
||||
let errors = this.state.errors;
|
||||
errors.push(`There was an error fetching site users -- ${error.message}`);
|
||||
this.setState((current) => ({ ...current, errors: errors }))
|
||||
setState((prevState) => ({ ...prevState, securityInfo: { ...prevState.securityInfo, lists: updatedLists } }));
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
const errors = [...state.errors, `There was an error fetching site users -- ${error.message}`];
|
||||
setState((prevState) => ({ ...prevState, errors }));
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
public collapseItem(itemId: string) {
|
||||
let children = filter(this.state.securityInfo.lists, (otheritem) => {
|
||||
return otheritem instanceof SPListItem && otheritem.parentId === itemId;
|
||||
});
|
||||
for (let childitem of children) {
|
||||
childitem.isExpanded = false;
|
||||
this.collapseItem(childitem.id);
|
||||
};
|
||||
|
||||
}
|
||||
public collapseList(item: any): any {
|
||||
|
||||
item.isExpanded = false;
|
||||
this.collapseItem(item.id);
|
||||
this.setState(this.state);
|
||||
|
||||
}
|
||||
|
||||
public expandCollapseList(item?: any, index?: number, column?: IColumn): any {
|
||||
if (item.itemCount === 0) {
|
||||
return;
|
||||
}
|
||||
if (item.isExpanded) {
|
||||
this.collapseList(item);
|
||||
}
|
||||
else {
|
||||
this.expandList(item);
|
||||
}
|
||||
|
||||
}
|
||||
public parentIsExpanded(item: SPListItem): boolean {
|
||||
let parent = find(this.state.securityInfo.lists, (otheritem) => {
|
||||
return otheritem.id === item.parentId;
|
||||
const collapseItem = (itemId: string) => {
|
||||
const children = filter(state.securityInfo.lists, (otherItem) => otherItem instanceof SPListItem && otherItem.parentId === itemId);
|
||||
children.forEach((childItem) => {
|
||||
childItem.isExpanded = false;
|
||||
collapseItem(childItem.id);
|
||||
});
|
||||
return parent.isExpanded;
|
||||
};
|
||||
|
||||
}
|
||||
const collapseList = (item: SPList | SPListItem): void => {
|
||||
item.isExpanded = false;
|
||||
collapseItem(item.id);
|
||||
setState({ ...state });
|
||||
};
|
||||
|
||||
public renderItemTitle(item?: any, index?: number, column?: IColumn): any {
|
||||
const extension = item.title.split('.').pop();
|
||||
debugger;
|
||||
const style = { marginLeft: item.level * 20 + 'px' }
|
||||
return (
|
||||
<div className={styles.itemTitle} style={style}>
|
||||
<Icon {...getFileTypeIconProps({ extension: extension, size: 16 })} />
|
||||
<span> {item.title}</span>
|
||||
</div>);
|
||||
const expandCollapseList = (item: SPList | SPListItem): void => {
|
||||
if (item.itemCount === 0) return;
|
||||
|
||||
if (item.isExpanded) {
|
||||
collapseList(item);
|
||||
} else {
|
||||
expandList(item);
|
||||
}
|
||||
public renderListTitle(item?: any, index?: number, column?: IColumn): any {
|
||||
const iconName = item.itemCount > 0 ?
|
||||
'FabricFormLibrary' :
|
||||
'FabricFolder';
|
||||
};
|
||||
|
||||
const renderTitle = (item: SPList | SPListItem): JSX.Element => {
|
||||
if (item instanceof SPList) {
|
||||
return renderListTitle(item);
|
||||
} else if (item.type === 'Folder') {
|
||||
return renderFolderTitle(item);
|
||||
} else {
|
||||
return renderItemTitle(item);
|
||||
}
|
||||
};
|
||||
|
||||
const renderListTitle = (item: SPList): JSX.Element => {
|
||||
const iconName = item.itemCount > 0 ? 'FabricFormLibrary' : 'FabricFolder';
|
||||
return (
|
||||
<div className={styles.itemTitle} onClick={(e) => {
|
||||
this.expandCollapseList(item);
|
||||
}}>
|
||||
<div className={styles.itemTitle} onClick={() => expandCollapseList(item)}>
|
||||
<Icon iconName={iconName} className={styles.themecolor} />
|
||||
<span> {item.title}</span>
|
||||
</div>);
|
||||
}
|
||||
|
||||
public renderFolderTitle(item?: any, index?: number, column?: IColumn): any {
|
||||
const style = { marginLeft: item.level * 20 + 'px' }
|
||||
const iconName = item.itemCount > 0 ?
|
||||
'FabricFormLibrary' :
|
||||
'FabricFolder';
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const renderFolderTitle = (item: SPListItem): JSX.Element => {
|
||||
const style = { marginLeft: `${item.level * 20}px` };
|
||||
const iconName = item.itemCount > 0 ? 'FabricFormLibrary' : 'FabricFolder';
|
||||
return (
|
||||
<div className={styles.itemTitle} onClick={(e) => {
|
||||
this.expandCollapseList(item);
|
||||
}}>
|
||||
<div className={styles.itemTitle} onClick={() => expandCollapseList(item)}>
|
||||
<Icon iconName={iconName} style={style} className={styles.themecolor} />
|
||||
<span> {item.title}</span>
|
||||
</div>);
|
||||
}
|
||||
|
||||
public renderTitle(item?: any, index?: number, column?: IColumn): any {
|
||||
if (item instanceof SPList) {
|
||||
return this.renderListTitle(item, index, column);
|
||||
} else {
|
||||
if (item.type === "Folder") {
|
||||
return this.renderFolderTitle(item, index, column);
|
||||
} else {
|
||||
return this.renderItemTitle(item, index, column);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public renderUserItem(item: any, index: number, column: IColumn, effectivePermissions: ISelectedPermission[]): any {
|
||||
|
||||
let user: SPSiteUser = find(this.state.securityInfo.siteUsers, (su) => {
|
||||
return su.upn.toString() === column.key;
|
||||
});
|
||||
// spin througg the selected permsiisopns and for the first hit, display that color. No Hit, then display empty
|
||||
|
||||
for (let selectedPermission of effectivePermissions ? effectivePermissions : []) {
|
||||
if (Helpers.doesUserHavePermission(item, user, SPPermission[selectedPermission.permission],
|
||||
this.state.securityInfo.roleDefinitions, this.state.securityInfo.siteGroups, this.state.securityInfo.adGroups)) {
|
||||
return (
|
||||
<Icon iconName={selectedPermission.iconName} style={{ color: selectedPermission.color }} onClick={(e) => {
|
||||
this.expandCollapseList(item);
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
// no hits
|
||||
};
|
||||
|
||||
const renderItemTitle = (item: SPListItem): JSX.Element => {
|
||||
const extension = item.title.split('.').pop();
|
||||
const style = { marginLeft: `${item.level * 20}px` };
|
||||
return (
|
||||
<Icon iconName={item.iconName} onClick={(e) => {
|
||||
this.expandCollapseList(item);
|
||||
}} />
|
||||
<div className={styles.itemTitle} style={style}>
|
||||
<Icon {...getFileTypeIconProps({ extension, size: 16 })} />
|
||||
<span> {item.title}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
public renderUserSelected(item?: SPSiteUser, index?: number, column?: IColumn): any {
|
||||
|
||||
return (
|
||||
<Checkbox checked={item.isSelected} />
|
||||
)
|
||||
|
||||
}
|
||||
public addUserColumns(columns: IColumn[], users: SPSiteUser[], effectivePermissions: ISelectedPermission[]): IColumn[] {
|
||||
for (let user of users) {
|
||||
const addUserColumns = (
|
||||
columns: IColumn[],
|
||||
users: SPSiteUser[],
|
||||
effectivePermissions: ISelectedPermission[]
|
||||
): IColumn[] => {
|
||||
for (const user of users) {
|
||||
if (user.isSelected) {
|
||||
if (
|
||||
((user.principalType === 1 || user.principalType === -1) && this.props.showUsers)
|
||||
||
|
||||
(user.principalType === 4 && this.props.showSecurityGroups)
|
||||
((user.principalType === 1 || user.principalType === -1) && props.showUsers) ||
|
||||
(user.principalType === 4 && props.showSecurityGroups)
|
||||
) {
|
||||
if (
|
||||
!props.showOnlyUsersWithPermission ||
|
||||
Helpers.doesUserHaveAnyPermission(
|
||||
state.securityInfo.lists,
|
||||
user,
|
||||
effectivePermissions.map(sp => {
|
||||
const permissionKey = sp.permission as keyof typeof SPPermission;
|
||||
return SPPermission[permissionKey];
|
||||
}),
|
||||
state.securityInfo.roleDefinitions,
|
||||
state.securityInfo.siteGroups,
|
||||
state.securityInfo.adGroups
|
||||
)
|
||||
if (!this.props.showOnlyUsersWithPermission || Helpers.doesUserHaveAnyPermission(this.state.securityInfo.lists, user, effectivePermissions.map((sp) => { return SPPermission[sp.permission] }), this.state.securityInfo.roleDefinitions, this.state.securityInfo.siteGroups, this.state.securityInfo.adGroups))
|
||||
) {
|
||||
columns.push({
|
||||
key: user.upn,
|
||||
name: this.state.showEmail ? user.upn : user.name,
|
||||
name: state.showEmail ? user.upn : user.name,
|
||||
fieldName: "",
|
||||
minWidth: 20,
|
||||
maxWidth: 20,
|
||||
onRender: (item?: any, index?: number, column?: IColumn) => {
|
||||
//debugger;
|
||||
return this.renderUserItem(item, index, column, effectivePermissions);
|
||||
},
|
||||
onRender: (item: SPListItem, index?: number, column?: IColumn) =>
|
||||
renderUserItem(item, index!, column!, effectivePermissions),
|
||||
headerClassName: styles.rotatedColumnHeader,
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
public getPermissionLevels(): IContextualMenuItem[] {
|
||||
|
||||
return this.props.getPermissionTypes().map(pt => {
|
||||
return {
|
||||
key: pt.text,
|
||||
name: pt.text,
|
||||
onClick: (event, item) => {
|
||||
|
||||
this.setState((current) => ({ ...current, permission: item.name }));
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
private setShowUserPanel(showPanel: boolean): () => void {
|
||||
return (): void => {
|
||||
this.setState((current) => ({ ...current, showUserPanel: showPanel }));
|
||||
|
||||
};
|
||||
}
|
||||
private selectUser(userid: number, value: boolean) {
|
||||
find(this.state.securityInfo.siteUsers, (su) => {
|
||||
return (su.id === userid)
|
||||
}).isSelected = value;
|
||||
this.setState(this.state);
|
||||
|
||||
|
||||
}
|
||||
private setShowListPanel(showPanel: boolean): () => void {
|
||||
return (): void => {
|
||||
this.setState((current) => ({ ...current, showListPanel: showPanel }));
|
||||
|
||||
};
|
||||
}
|
||||
private selectList(listid: string, value: boolean) {
|
||||
let list: SPList = find(this.state.securityInfo.lists, (su) => {
|
||||
return (su.id === listid)
|
||||
}) as SPList;
|
||||
list.isSelected = value;
|
||||
this.setState(this.state);
|
||||
|
||||
|
||||
}
|
||||
private checkUncheckPermission(perm: ISelectedPermission) {
|
||||
//debugger;
|
||||
var sps = this.state.selectedPermissions;
|
||||
const idx = findIndex(sps, (sp: ISelectedPermission) => { return sp.permission == perm.permission; });
|
||||
if (idx != -1) {
|
||||
sps[idx].isChecked = !sps[idx].isChecked
|
||||
this.setState((current) => ({ ...current, SelectedPermissions: [...sps] }));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public render(): React.ReactElement<ISpSecurityProps> {
|
||||
if (!this.state.securityInfoLoaded) {
|
||||
return (
|
||||
<div >
|
||||
<Spinner label={'Fetching security information, please wait...'} />
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
let userPanelCommands: IContextualMenuItem[] = [];
|
||||
userPanelCommands.push({
|
||||
icon: "BoxAdditionSolid",
|
||||
key: "Add All Users",
|
||||
name: "Add All Users",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
for (let item of this.state.securityInfo.siteUsers) {
|
||||
item.isSelected = true;
|
||||
}
|
||||
this.setState(this.state);
|
||||
}
|
||||
});
|
||||
userPanelCommands.push({
|
||||
icon: "BoxSubtractionSolid",
|
||||
key: "RemoveAllUsers",
|
||||
name: "Remove All Users",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
|
||||
for (let item of this.state.securityInfo.siteUsers) {
|
||||
item.isSelected = false;
|
||||
}
|
||||
this.setState(this.state);
|
||||
}
|
||||
});
|
||||
let listPanelCommands: IContextualMenuItem[] = [];
|
||||
listPanelCommands.push({
|
||||
const listPanelCommands: IContextualMenuItem[] = [
|
||||
{
|
||||
icon: "BoxAdditionSolid",
|
||||
key: "Add All Lists",
|
||||
name: "Add All Lists",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
|
||||
for (let item of this.state.securityInfo.lists) {
|
||||
onClick: () => {
|
||||
const updatedLists = state.securityInfo.lists.map((item) => {
|
||||
if (item instanceof SPList) {
|
||||
item.isSelected = true;
|
||||
}
|
||||
}
|
||||
this.setState(this.state);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
listPanelCommands.push({
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
securityInfo: {
|
||||
...prevState.securityInfo,
|
||||
lists: updatedLists,
|
||||
},
|
||||
}));
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "BoxSubtractionSolid",
|
||||
key: "Remove All Lists",
|
||||
name: "Remove All Lists",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
for (let item of this.state.securityInfo.lists) {
|
||||
onClick: () => {
|
||||
const updatedLists = state.securityInfo.lists.map((item) => {
|
||||
if (item instanceof SPList) {
|
||||
item.isSelected = false;
|
||||
}
|
||||
}
|
||||
this.setState(this.state);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
let commands: IContextualMenuItem[] = [];
|
||||
if (this.props.letUserSelectPermission) {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
securityInfo: {
|
||||
...prevState.securityInfo,
|
||||
lists: updatedLists,
|
||||
},
|
||||
}));
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
commands.push({
|
||||
icon: "AzureKeyVault",
|
||||
key: "SecurityLevel2",
|
||||
name: "Permission",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
this.setState((current) => ({ ...current, showPermissionsPanel: !current.showPermissionsPanel }));
|
||||
}
|
||||
}
|
||||
const renderUserItem = (
|
||||
item: SPListItem,
|
||||
index: number,
|
||||
column: IColumn,
|
||||
effectivePermissions: ISelectedPermission[]
|
||||
): JSX.Element => {
|
||||
const user = find(state.securityInfo.siteUsers, (su) => su.upn.toString() === column.key);
|
||||
|
||||
for (const selectedPermission of effectivePermissions) {
|
||||
const permissionKey = selectedPermission.permission as keyof typeof SPPermission;
|
||||
|
||||
if (user && Helpers.doesUserHavePermission(
|
||||
item,
|
||||
user,
|
||||
SPPermission[permissionKey],
|
||||
state.securityInfo.roleDefinitions,
|
||||
state.securityInfo.siteGroups,
|
||||
state.securityInfo.adGroups
|
||||
)) {
|
||||
return (
|
||||
<Icon
|
||||
iconName={selectedPermission.iconName}
|
||||
style={{ color: selectedPermission.color }}
|
||||
onClick={() => expandCollapseList(item)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (this.props.letUserSelectUsers) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Icon iconName={item.iconName} onClick={() => expandCollapseList(item)} />
|
||||
);
|
||||
};
|
||||
|
||||
if (!state.securityInfoLoaded) {
|
||||
return (
|
||||
<div>
|
||||
<Spinner label={'Fetching security information, please wait...'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Define the commands for users, lists, permissions, and display mode
|
||||
const commands: IContextualMenuItem[] = [];
|
||||
|
||||
// User selection command
|
||||
if (props.letUserSelectUsers) {
|
||||
commands.push({
|
||||
icon: "People",
|
||||
key: "Users",
|
||||
name: "Users",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
this.setState((current) => ({ ...current, showUserPanel: !current.showUserPanel }));
|
||||
onClick: () => {
|
||||
setState((current) => ({ ...current, showUserPanel: !current.showUserPanel }));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.props.letUserSelectLists) {
|
||||
|
||||
// List selection command
|
||||
if (props.letUserSelectLists) {
|
||||
commands.push({
|
||||
icon: "PageListSolid",
|
||||
key: "Lists",
|
||||
name: "Lists",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: (event, item) => {
|
||||
this.setState((current) => ({ ...current, showListPanel: !current.showListPanel }));
|
||||
onClick: () => {
|
||||
setState((current) => ({ ...current, showListPanel: !current.showListPanel }));
|
||||
}
|
||||
});
|
||||
}
|
||||
commands.push({
|
||||
|
||||
// Permission selection command
|
||||
if (props.letUserSelectPermission) {
|
||||
commands.push({
|
||||
icon: "AzureKeyVault",
|
||||
key: "SecurityLevel2",
|
||||
name: "Permission",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
onClick: () => {
|
||||
setState((current) => ({ ...current, showPermissionsPanel: !current.showPermissionsPanel }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Display mode command
|
||||
commands.push({
|
||||
key: "DisplayMode",
|
||||
title: "DisplayMode",
|
||||
name: this.state.showEmail ? "Show Email" : "Show Name",
|
||||
name: state.showEmail ? "Show Email" : "Show Name",
|
||||
itemType: ContextualMenuItemType.Normal,
|
||||
subMenuProps: {
|
||||
items: [{
|
||||
key: "ShowName",
|
||||
name: "Show Name",
|
||||
onClick: (event, item) => {
|
||||
this.setState((current) => ({ ...current, showEmail: false }));
|
||||
onClick: () => {
|
||||
setState((current) => ({ ...current, showEmail: false }));
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ShowEmail",
|
||||
name: "Show Email",
|
||||
onClick: (event, item) => {
|
||||
this.setState((current) => ({ ...current, showEmail: true }));
|
||||
onClick: () => {
|
||||
setState((current) => ({ ...current, showEmail: true }));
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
let effectivePermissions = this.state.selectedPermissions.filter((sp) => { return sp.isChecked; })
|
||||
let columns: Array<IColumn> = [
|
||||
|
||||
const effectivePermissions = state.selectedPermissions.filter((sp) => sp.isChecked);
|
||||
const columns: IColumn[] = [
|
||||
{
|
||||
key: "title", name: "Title", isResizable: true, fieldName: "title",
|
||||
minWidth: this.props.listTitleColumnWidth, maxWidth: this.props.listTitleColumnWidth,
|
||||
onRender: this.renderTitle, isRowHeader: true
|
||||
},
|
||||
];
|
||||
let displayColumns: IColumn[] = this.addUserColumns(columns, this.state.securityInfo.siteUsers, effectivePermissions);
|
||||
|
||||
|
||||
let displayItems: (SPList | SPListItem)[] = filter(this.state.securityInfo.lists, (item) => {
|
||||
return (
|
||||
(item instanceof SPList && item.isSelected)
|
||||
||
|
||||
((item instanceof SPListItem) && (this.parentIsExpanded(item)))
|
||||
)
|
||||
})
|
||||
|
||||
let errorMessages = [];
|
||||
for (let error of this.state.errors) {
|
||||
errorMessages.push(<li>{error}</li>)
|
||||
key: "title",
|
||||
name: "Title",
|
||||
isResizable: true,
|
||||
fieldName: "title",
|
||||
minWidth: props.listTitleColumnWidth,
|
||||
maxWidth: props.listTitleColumnWidth,
|
||||
onRender: renderTitle,
|
||||
isRowHeader: true
|
||||
}
|
||||
];
|
||||
|
||||
const displayColumns: IColumn[] = addUserColumns(columns, state.securityInfo.siteUsers, effectivePermissions);
|
||||
const displayItems: (SPList | SPListItem)[] = filter(state.securityInfo.lists, (item) => {
|
||||
return (
|
||||
(item instanceof SPList && item.isSelected) ||
|
||||
((item instanceof SPListItem) && item.parentId && find(state.securityInfo.lists, l => l.id === item.parentId)?.isExpanded)
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div >
|
||||
<ul>{errorMessages}</ul>
|
||||
<CommandBar
|
||||
items={commands}
|
||||
/>
|
||||
<>
|
||||
<CommandBar items={commands} />
|
||||
<br />
|
||||
<Legend
|
||||
selectedPermissions={this.state.selectedPermissions}
|
||||
selectedPermissions={state.selectedPermissions}
|
||||
checkUncheckPermission={(e) => {
|
||||
//debugger;
|
||||
this.checkUncheckPermission(e);
|
||||
|
||||
}
|
||||
const sps = [...state.selectedPermissions];
|
||||
const idx = findIndex(sps, (sp) => sp.permission === e.permission);
|
||||
if (idx !== -1) {
|
||||
sps[idx].isChecked = !sps[idx].isChecked;
|
||||
setState((prevState) => ({ ...prevState, selectedPermissions: sps }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<DetailsList
|
||||
items={displayItems}
|
||||
columns={displayColumns}
|
||||
selectionMode={SelectionMode.none}
|
||||
className={styles.SPFXSecurityGrid}
|
||||
|
||||
/>
|
||||
<SelectedPermissionsPanel
|
||||
isOpen={this.state.showPermissionsPanel}
|
||||
SelectedPermissions={this.props.selectedPermissions}
|
||||
onPropertyChange={(propertyName: string, oldValue: Array<ISelectedPermission>, newValue: Array<ISelectedPermission>) => {
|
||||
|
||||
this.setState((current) => ({ ...current, selectedPermissions: newValue }));
|
||||
isOpen={state.showPermissionsPanel}
|
||||
SelectedPermissions={props.selectedPermissions}
|
||||
onPropertyChange={(propertyName, oldValue, newValue) => {
|
||||
setState((prevState) => ({ ...prevState, selectedPermissions: newValue }));
|
||||
}}
|
||||
closePanel={() => {
|
||||
|
||||
this.setState((current) => ({ ...current, showPermissionsPanel: false }));
|
||||
setState((prevState) => ({ ...prevState, showPermissionsPanel: false }));
|
||||
}}
|
||||
|
||||
/>
|
||||
<Panel
|
||||
isBlocking={false}
|
||||
isOpen={state.showUserPanel}
|
||||
onDismiss={() => setState((prevState) => ({ ...prevState, showUserPanel: false }))}
|
||||
type={PanelType.medium}
|
||||
headerText="Select Users"
|
||||
closeButtonAriaLabel="Close"
|
||||
>
|
||||
|
||||
</SelectedPermissionsPanel>
|
||||
<Panel
|
||||
isBlocking={false}
|
||||
isOpen={this.state.showUserPanel}
|
||||
onDismiss={this.setShowUserPanel(false)}
|
||||
type={PanelType.medium}
|
||||
headerText='Select Users'
|
||||
closeButtonAriaLabel='Close'>
|
||||
<CommandBar items={userPanelCommands} />
|
||||
<DetailsList
|
||||
selection={this.userSelection}
|
||||
selectionMode={SelectionMode.none}
|
||||
items={this.state.securityInfo.siteUsers}
|
||||
columns={[
|
||||
{
|
||||
key: "isSelected", name: "Select", fieldName: "isSelected", minWidth: 30, onRender: (item) => <Checkbox
|
||||
checked={item.isSelected}
|
||||
onChange={(element, value) => { this.selectUser(item.id, value); }}
|
||||
/>
|
||||
},
|
||||
{ key: "id", name: "Name", fieldName: "name", minWidth: 400 },
|
||||
]}
|
||||
/>
|
||||
|
||||
</Panel>
|
||||
<Panel
|
||||
isBlocking={false}
|
||||
isOpen={this.state.showListPanel}
|
||||
onDismiss={this.setShowListPanel(false)}
|
||||
type={PanelType.medium}
|
||||
headerText='Select Lists'
|
||||
closeButtonAriaLabel='Close'>
|
||||
<CommandBar items={listPanelCommands} />
|
||||
|
||||
<DetailsList
|
||||
|
||||
selection={this.listSelection}
|
||||
selection={userSelection}
|
||||
selectionMode={SelectionMode.none}
|
||||
items={filter(this.state.securityInfo.lists, (l) => {
|
||||
return l instanceof SPList;
|
||||
})}
|
||||
items={state.securityInfo.siteUsers}
|
||||
columns={[
|
||||
{
|
||||
key: "isSelected", name: "Select", fieldName: "isSelected", minWidth: 30,
|
||||
onRender: (item) => <Checkbox
|
||||
key: "isSelected",
|
||||
name: "Select",
|
||||
fieldName: "isSelected",
|
||||
minWidth: 30,
|
||||
onRender: (item) => (
|
||||
<Checkbox
|
||||
checked={item.isSelected}
|
||||
onChange={(element, value) => { this.selectList(item.id, value); }}
|
||||
onChange={(element, value) => {
|
||||
const user = find(state.securityInfo.siteUsers, (su) => su.id === item.id);
|
||||
if (user) {
|
||||
user.isSelected = value!;
|
||||
setState((prevState) => ({ ...prevState }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
{ key: "id", name: "Title", fieldName: "title", minWidth: 500 },
|
||||
{ key: "id", name: "Name", fieldName: "name", minWidth: 400 }
|
||||
]}
|
||||
/>
|
||||
|
||||
</Panel>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
<Panel
|
||||
isBlocking={false}
|
||||
isOpen={state.showListPanel}
|
||||
onDismiss={() => setState((prevState) => ({ ...prevState, showListPanel: false }))}
|
||||
type={PanelType.medium}
|
||||
headerText="Select Lists"
|
||||
closeButtonAriaLabel="Close"
|
||||
>
|
||||
<CommandBar items={listPanelCommands} />
|
||||
<DetailsList
|
||||
selection={listSelection}
|
||||
selectionMode={SelectionMode.none}
|
||||
items={filter(state.securityInfo.lists, (l) => l instanceof SPList)}
|
||||
columns={[
|
||||
{
|
||||
key: "isSelected",
|
||||
name: "Select",
|
||||
fieldName: "isSelected",
|
||||
minWidth: 30,
|
||||
onRender: (item) => (
|
||||
<Checkbox
|
||||
checked={item.isSelected}
|
||||
onChange={(element, value) => {
|
||||
const list = find(state.securityInfo.lists, (l) => l.id === item.id);
|
||||
if (list) {
|
||||
list.isSelected = value!;
|
||||
setState((prevState) => ({ ...prevState }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{ key: "id", name: "Title", fieldName: "title", minWidth: 500 }
|
||||
]}
|
||||
/>
|
||||
</Panel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpSecurity;
|
||||
|
|
|
@ -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 null;
|
||||
}).filter(Boolean) as IDropdownOption[]; // Remove null values
|
||||
}
|
||||
return perms;
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<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,
|
||||
<Icon iconName={item.iconName} style={{ color: item.color, paddingRight: '10px' }} />
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
isColorIconSelecorDialogOpen: true,
|
||||
CurrentlySelectedPermission: item
|
||||
}));
|
||||
}}>Edit Permission</Button>
|
||||
});
|
||||
}}
|
||||
>
|
||||
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 {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
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 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();
|
||||
}
|
||||
}
|
||||
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={[{
|
||||
<CommandBar
|
||||
items={[
|
||||
{
|
||||
key: "AddColumns",
|
||||
name: "Add a Permission",
|
||||
icon: "Add",
|
||||
onClick: () => {
|
||||
this.setState((current) => ({
|
||||
...current,
|
||||
this.setState({
|
||||
isColorIconSelecorDialogOpen: true,
|
||||
CurrentlySelectedPermission: { color: null, freindlyName: null, iconName: null, permission: null }
|
||||
}));
|
||||
CurrentlySelectedPermission: { color: '', freindlyName: '', iconName: '', permission: '' }
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "ClearAllColums",
|
||||
key: "ClearAllColumns",
|
||||
name: "Remove All Permissions",
|
||||
canCheck: true,
|
||||
icon: "Delete",
|
||||
onClick: () => {
|
||||
this.removeAllColumns();
|
||||
}
|
||||
|
||||
onClick: () => this.removeAllColumns()
|
||||
},
|
||||
{
|
||||
key: "save",
|
||||
name: "Save",
|
||||
canCheck: true,
|
||||
icon: "Save",
|
||||
onClick: () => {
|
||||
|
||||
this.saveChanges();
|
||||
onClick: () => this.saveChanges()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
]} />
|
||||
{this.state.isColorIconSelecorDialogOpen &&
|
||||
]}
|
||||
/>
|
||||
{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