Merge pull request #1846 from Tanddant/main
This commit is contained in:
commit
68e4113213
|
@ -10,7 +10,6 @@
|
|||
>
|
||||
> DELETE THIS PARAGRAPH BEFORE SUBMITTING
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
Short summary on functionality and used technologies.
|
||||
|
@ -60,15 +59,6 @@ Short summary on functionality and used technologies.
|
|||
> Update accordingly as needed.
|
||||
> DELETE THIS PARAGRAPH BEFORE SUBMITTING
|
||||
|
||||
## Prerequisites
|
||||
|
||||
> Any special pre-requisites? Include any lists, permissions, offerings to the demo gods, or whatever else needs to be done for this web part to work.
|
||||
>
|
||||
> Please describe the steps to configure the pre-requisites. Feel free to add screen shots, but make sure that there is a text description of the steps to perform.
|
||||
>
|
||||
> DELETE THE TEXT ABOVE BEFORE SUBMITTING
|
||||
|
||||
|
||||
## Solution
|
||||
|
||||
> We use this section to recognize and promote your contributions. Please provide one author per line -- even if you worked together on it.
|
||||
|
@ -89,11 +79,13 @@ Version|Date|Comments
|
|||
1.1|September 2, 2021|Update comment
|
||||
1.0|August 29, 2021|Initial release
|
||||
|
||||
## Disclaimer
|
||||
## Prerequisites
|
||||
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
---
|
||||
> Any special pre-requisites? Include any lists, permissions, offerings to the demo gods, or whatever else needs to be done for this web part to work.
|
||||
>
|
||||
> Please describe the steps to configure the pre-requisites. Feel free to add screen shots, but make sure that there is a text description of the steps to perform.
|
||||
>
|
||||
> DELETE THE TEXT ABOVE BEFORE SUBMITTING
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
|
@ -117,4 +109,18 @@ This Web Part illustrates the following concepts on top of the SharePoint Framew
|
|||
> Note that better pictures and documentation will increase the sample usage and the value you are providing for others. Thanks for your submissions in advance! You rock ❤.
|
||||
> DELETE THIS PARAGRAPH BEFORE SUBMITTING
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/readme-template" />
|
||||
## Disclaimer
|
||||
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
## Support
|
||||
|
||||
We do not support samples, but we do use GitHub to track issues and constantly want to improve these samples.
|
||||
|
||||
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=YOUR-SOLUTION-NAME&authors=@YOURGITHUBUSERNAME&title=YOUR-SOLUTION-NAME%20-%20).
|
||||
|
||||
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=YOUR-SOLUTION-NAME&authors=@YOURGITHUBUSERNAME&title=YOUR-SOLUTION-NAME%20-%20).
|
||||
|
||||
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=suggestion.yml&sample=YOUR-SOLUTION-NAME&authors=@YOURGITHUBUSERNAME&title=YOUR-SOLUTION-NAME%20-%20).
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/YOUR-SOLUTION-NAME" />
|
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"isCreatingSolution": true,
|
||||
"environment": "spo",
|
||||
"version": "1.11.0",
|
||||
"libraryName": "sp-fx-remote-event-receiver-manager",
|
||||
"libraryId": "ad113c85-22f8-4414-aa78-4cb897d8a285",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
# Remote Event Receiver Manager
|
||||
|
||||
## Summary
|
||||
|
||||
A very simple web part that lets you add and delete remote event receivers to lists, the need came about due to remote event receivers not functioning properly when added with app only authentication and PnP.Powershell now only using that.
|
||||
|
||||
![Remote event receiver manager web part](./assets/video.gif)
|
||||
|
||||
## Compatibility
|
||||
|
||||
![SPFx 1.11](https://img.shields.io/badge/SPFx-1.11.0-green.svg)
|
||||
![Node.js LTS 10.x](https://img.shields.io/badge/Node.js-LTS%2010.x-green.svg)
|
||||
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
|
||||
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with Microsoft Teams")
|
||||
![Workbench Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench")
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
|
||||
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
None
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react remote event receiver manager | [Dan Toft](https://github.com/Tanddant) ([@tanddant](https://twitter.com/tanddant))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|May 3, 2021|Initial release
|
||||
|
||||
## Minimal Path to Awesome - please follow all the steps.
|
||||
|
||||
- Clone this repository
|
||||
- in the command line run:
|
||||
- `npm install`
|
||||
- `gulp build`
|
||||
- `gulp bundle --ship`
|
||||
- `gulp package-solution --ship`
|
||||
- Add and deploy package to AppCatalog
|
||||
|
||||
## Features
|
||||
|
||||
- See, add, and delete remote event receivers from a list
|
||||
|
||||
## Disclaimer
|
||||
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
## Support
|
||||
|
||||
We do not support samples, but we do use GitHub to track issues and constantly want to improve these samples.
|
||||
|
||||
If you encounter any issues while using this sample, [create a new issue](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=bug-report.yml&sample=react-remote-event-receiver-manager&authors=@Tanddant&title=react-remote-event-receiver-manager%20-%20).
|
||||
|
||||
For questions regarding this sample, [create a new question](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=question.yml&sample=react-remote-event-receiver-manager&authors=@Tanddant&title=react-remote-event-receiver-manager%20-%20).
|
||||
|
||||
Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/sp-dev-fx-webparts/issues/new?assignees=&labels=Needs%3A+Triage+%3Amag%3A%2Ctype%3Abug-suspected&template=suggestion.yml&sample=react-remote-event-receiver-manager&authors=@Tanddant&title=react-remote-event-receiver-manager%20-%20).
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-remote-event-receiver-manager" />
|
|
@ -0,0 +1,51 @@
|
|||
[
|
||||
{
|
||||
"name": "pnp-sp-dev-spfx-web-parts-react-remote-event-receiver-manager",
|
||||
"source": "pnp",
|
||||
"title": "Remote Event Receiver Manager",
|
||||
"shortDescription": "Lets you add and delete remote event receivers to lists",
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-remote-event-receiver-manager",
|
||||
"longDescription": [
|
||||
"A very simple web part that lets you add and delete remote event receivers to lists, the need came about due to remote event receivers not functioning properly when added with app only authentication and PnP.Powershell now only using that."
|
||||
],
|
||||
"creationDateTime": "2021-05-03",
|
||||
"updateDateTime": "2021-05-03",
|
||||
"products": [
|
||||
"SharePoint",
|
||||
"Office"
|
||||
],
|
||||
"metadata": [
|
||||
{
|
||||
"key": "CLIENT-SIDE-DEV",
|
||||
"value": "React"
|
||||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.11.0"
|
||||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
{
|
||||
"type": "image",
|
||||
"order": 100,
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-remote-event-receiver-manager/assets/video.gif",
|
||||
"alt": "Word Game"
|
||||
}
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"gitHubAccount": "Tanddant",
|
||||
"company": "",
|
||||
"pictureUrl": "https://github.com/Tanddant.png",
|
||||
"name": "Dan Toft"
|
||||
}
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"name": "Build your first SharePoint client-side web part",
|
||||
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
|
||||
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Binary file not shown.
After Width: | Height: | Size: 274 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"remote-event-receiver-manager-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/remoteEventReceiverManager/RemoteEventReceiverManagerWebPart.js",
|
||||
"manifest": "./src/webparts/remoteEventReceiverManager/RemoteEventReceiverManagerWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"RemoteEventReceiverManagerWebPartStrings": "lib/webparts/remoteEventReceiverManager/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "sp-fx-remote-event-receiver-manager",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "sp-fx-remote-event-receiver-manager-client-side-solution",
|
||||
"id": "ad113c85-22f8-4414-aa78-4cb897d8a285",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "",
|
||||
"websiteUrl": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"mpnId": ""
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/sp-fx-remote-event-receiver-manager.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
|
||||
build.initialize(require('gulp'));
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "sp-fx-remote-event-receiver-manager",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.11.0",
|
||||
"@microsoft/sp-lodash-subset": "1.11.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.11.0",
|
||||
"@microsoft/sp-property-pane": "1.11.0",
|
||||
"@microsoft/sp-webpart-base": "1.11.0",
|
||||
"@pnp/graph": "^2.4.0",
|
||||
"@pnp/sp": "^2.4.0",
|
||||
"office-ui-fabric-react": "6.214.0",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@microsoft/sp-build-web": "1.11.0",
|
||||
"@microsoft/sp-tslint-rules": "1.11.0",
|
||||
"@microsoft/sp-module-interfaces": "1.11.0",
|
||||
"@microsoft/sp-webpart-workbench": "1.11.0",
|
||||
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"@types/es6-promise": "0.0.33"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "b9b2c750-32a9-4db7-817f-fd0b23d73777",
|
||||
"alias": "RemoteEventReceiverManagerWebPart",
|
||||
"componentType": "WebPart",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
"supportedHosts": ["SharePointWebPart"],
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Remote Event Receiver Manager" },
|
||||
"description": { "default": "A simple webpart that lets you manage remote event receivers on the current site" },
|
||||
"officeFabricIconFontName": "Remote",
|
||||
"properties": {
|
||||
"description": "Remote Event Receiver Manager"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
IPropertyPaneConfiguration
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'RemoteEventReceiverManagerWebPartStrings';
|
||||
import RemoteEventReceiverManager from './components/RemoteEventReceiverManager';
|
||||
import { IRemoteEventReceiverManagerProps } from './components/RemoteEventReceiverManager';
|
||||
import { setup as pnpSetup } from "@pnp/common";
|
||||
|
||||
export interface IRemoteEventReceiverManagerWebPartProps {
|
||||
}
|
||||
|
||||
export default class RemoteEventReceiverManagerWebPart extends BaseClientSideWebPart<IRemoteEventReceiverManagerWebPartProps> {
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IRemoteEventReceiverManagerProps> = React.createElement(
|
||||
RemoteEventReceiverManager,
|
||||
{ context: this.context }
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onInit(): Promise<void> {
|
||||
|
||||
return super.onInit().then(_ => {
|
||||
|
||||
// other init code may be present
|
||||
|
||||
pnpSetup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: ""
|
||||
},
|
||||
groups: []
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.remoteEventReceiverManager {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
@include ms-fontColor-white;
|
||||
background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
import * as React from 'react';
|
||||
import styles from './RemoteEventReceiverManager.module.scss';
|
||||
import { cloneDeep, escape } from '@microsoft/sp-lodash-subset';
|
||||
import { IEventReceiver } from '../models/IEventReceiver';
|
||||
import { IList } from '../models/IList';
|
||||
import { ISharePointProvider, SharePointProvider } from '../providers/SharePointProvider';
|
||||
import { ActionButton, DialogFooter, Dropdown, IDropdownOption, PrimaryButton, Spinner, SpinnerSize, Stack, Text, TextField } from 'office-ui-fabric-react';
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
|
||||
|
||||
const SynchronizationOptions: IDropdownOption[] = [
|
||||
{ key: 0, text: "DefaultSynchronization" },
|
||||
{ key: 1, text: "Synchronous" },
|
||||
{ key: 2, text: "Asynchronous" }
|
||||
];
|
||||
|
||||
const EventTypeOptions: IDropdownOption[] = [
|
||||
{ key: 10001, text: "ItemAdded" },
|
||||
{ key: 10002, text: "ItemUpdated" },
|
||||
{ key: 10003, text: "ItemDeleted" },
|
||||
{ key: 10004, text: "ItemCheckedIn" },
|
||||
{ key: 10005, text: "ItemCheckedOut" },
|
||||
{ key: 10006, text: "ItemUncheckedOut" },
|
||||
{ key: 10007, text: "ItemAttachmentAdded" },
|
||||
{ key: 10008, text: "ItemAttachmentDeleted" },
|
||||
{ key: 10009, text: "ItemFileMoved" },
|
||||
{ key: 10010, text: "ItemFileConverted" },
|
||||
{ key: 10011, text: "ItemVersionDeleted" },
|
||||
];
|
||||
|
||||
const NewEventReciever: IEventReceiver = {
|
||||
ReceiverAssembly: "Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c",
|
||||
ReceiverClass: "Microsoft.SharePoint.Webhooks.SPWebhookItemEventReceiver",
|
||||
ReceiverId: "",
|
||||
ReceiverName: "",
|
||||
SequenceNumber: 10000,
|
||||
Synchronization: 2,
|
||||
EventType: 10001,
|
||||
ReceiverUrl: ""
|
||||
};
|
||||
|
||||
export interface IRemoteEventReceiverManagerProps {
|
||||
context: WebPartContext;
|
||||
}
|
||||
|
||||
export interface IRemoteEventReceiverManagerState {
|
||||
step: Step;
|
||||
|
||||
lists: IList[];
|
||||
selectedList: IList;
|
||||
|
||||
eventReceivers: IEventReceiver[];
|
||||
selectedEventReceiver: IEventReceiver;
|
||||
|
||||
isSaving: boolean;
|
||||
}
|
||||
|
||||
export enum Step {
|
||||
SelectList,
|
||||
SelectEventReceiver,
|
||||
EditEventReceiver
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default class RemoteEventReceiverManager extends React.Component<IRemoteEventReceiverManagerProps, IRemoteEventReceiverManagerState> {
|
||||
private provider: ISharePointProvider;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
lists: null,
|
||||
selectedList: null,
|
||||
eventReceivers: null,
|
||||
selectedEventReceiver: null,
|
||||
step: Step.SelectList,
|
||||
isSaving: false,
|
||||
};
|
||||
|
||||
this.provider = new SharePointProvider(this.props.context);
|
||||
this.loadInitialData();
|
||||
}
|
||||
|
||||
private async loadInitialData() {
|
||||
let lists = await this.provider.getLists();
|
||||
this.setState({ lists: lists });
|
||||
}
|
||||
|
||||
private async loadEventReceivers(Id: string) {
|
||||
let EventReceivers = await this.provider.getEventReceivers(Id);
|
||||
this.setState({ eventReceivers: EventReceivers });
|
||||
}
|
||||
|
||||
private async addEventReceiver() {
|
||||
this.setState({ isSaving: true });
|
||||
await this.provider.addEventReceiver(this.state.selectedEventReceiver, this.state.selectedList.Id);
|
||||
this.setState({ isSaving: false, selectedEventReceiver: null, step: Step.SelectEventReceiver });
|
||||
this.loadEventReceivers(this.state.selectedList.Id);
|
||||
}
|
||||
|
||||
|
||||
private async deleteEventReceiver() {
|
||||
this.setState({ isSaving: true });
|
||||
await this.provider.deleteEventReceiver(this.state.selectedEventReceiver, this.state.selectedList.Id);
|
||||
this.setState({ isSaving: false, selectedEventReceiver: null, step: Step.SelectEventReceiver });
|
||||
this.loadEventReceivers(this.state.selectedList.Id);
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IRemoteEventReceiverManagerProps> {
|
||||
const { selectedEventReceiver } = this.state;
|
||||
|
||||
return (
|
||||
<div className={styles.remoteEventReceiverManager}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.row}>
|
||||
<div className={styles.column}>
|
||||
|
||||
{this.state.step == Step.SelectList &&
|
||||
<div>
|
||||
<Text variant={"large"}>Please select a list</Text>
|
||||
|
||||
{this.state.lists == null &&
|
||||
<Spinner label={"Loading lists..."} size={SpinnerSize.large} />
|
||||
}
|
||||
|
||||
{this.state.lists &&
|
||||
<Stack tokens={{ childrenGap: 5 }} styles={{ root: { background: "white" } }}>
|
||||
{this.state.lists.map((list, index) => {
|
||||
return <ActionButton iconProps={{ iconName: "CustomList" }} text={list.Title} onClick={() => {
|
||||
this.setState({ selectedList: { ...list }, step: Step.SelectEventReceiver });
|
||||
this.loadEventReceivers(list.Id);
|
||||
}} />;
|
||||
})}
|
||||
</Stack>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
{this.state.step == Step.SelectEventReceiver &&
|
||||
<div>
|
||||
<ActionButton iconProps={{ iconName: "Back" }} text={"Go back"} styles={{ textContainer: { color: "white" } }} onClick={() => this.setState({ eventReceivers: null, step: Step.SelectList, selectedList: null })} />
|
||||
<br />
|
||||
<Text variant={"large"}>Please select a event receiver</Text>
|
||||
<br />
|
||||
|
||||
{this.state.eventReceivers == null &&
|
||||
<Spinner label={"Loading event receives..."} size={SpinnerSize.large} />
|
||||
}
|
||||
|
||||
{this.state.eventReceivers &&
|
||||
<div>
|
||||
<Stack tokens={{ childrenGap: 5 }} styles={{ root: { background: "white" } }}>
|
||||
{this.state.eventReceivers.map((eventReceiver, index) => {
|
||||
return <ActionButton iconProps={{ iconName: "Remote" }} text={eventReceiver.ReceiverName} onClick={() => {
|
||||
this.setState({ selectedEventReceiver: cloneDeep(eventReceiver), step: Step.EditEventReceiver });
|
||||
}} />;
|
||||
})}
|
||||
</Stack>
|
||||
<ActionButton iconProps={{ iconName: "Add" }} text={"Add new"} styles={{ textContainer: { color: "white" } }} onClick={() => this.setState({ selectedEventReceiver: cloneDeep(NewEventReciever), step: Step.EditEventReceiver })} />
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
{this.state.step == Step.EditEventReceiver &&
|
||||
|
||||
<div>
|
||||
<ActionButton iconProps={{ iconName: "Back" }} text={"Go back"} styles={{ textContainer: { color: "white" } }} onClick={() => this.setState({ selectedEventReceiver: null, step: Step.SelectEventReceiver })} />
|
||||
<br />
|
||||
<TextField disabled={selectedEventReceiver.ReceiverId != ""} label={"ReceiverAssembly"} value={selectedEventReceiver.ReceiverAssembly} />
|
||||
<TextField disabled={selectedEventReceiver.ReceiverId != ""} label={"ReceiverClass"} value={selectedEventReceiver.ReceiverClass} />
|
||||
<TextField disabled label={"ReceiverId"} value={selectedEventReceiver.ReceiverId} />
|
||||
<TextField disabled={selectedEventReceiver.ReceiverId != ""} label={"ReceiverName"} value={selectedEventReceiver.ReceiverName} onChange={(ev, val) => this.setState({ selectedEventReceiver: { ...selectedEventReceiver, ReceiverName: val } })} />
|
||||
<TextField disabled={selectedEventReceiver.ReceiverId != ""} label={"SequenceNumber"} type={"number"} value={selectedEventReceiver.SequenceNumber + ""} onChange={(ev, val) => this.setState({ selectedEventReceiver: { ...selectedEventReceiver, SequenceNumber: val as any as number } })} />
|
||||
<Dropdown disabled={selectedEventReceiver.ReceiverId != ""} label={"Synchronization"} selectedKey={selectedEventReceiver.Synchronization} onChange={(ev, val) => this.setState({ selectedEventReceiver: { ...selectedEventReceiver, Synchronization: val.key as number } })} options={SynchronizationOptions} />
|
||||
<Dropdown disabled={selectedEventReceiver.ReceiverId != ""} label={"EventType"} selectedKey={selectedEventReceiver.EventType} onChange={(ev, val) => this.setState({ selectedEventReceiver: { ...selectedEventReceiver, EventType: val.key as number } })} options={EventTypeOptions} />
|
||||
<TextField disabled={selectedEventReceiver.ReceiverId != ""} label={"ReceiverUrl"} value={selectedEventReceiver.ReceiverUrl} onChange={(ev, val) => this.setState({ selectedEventReceiver: { ...selectedEventReceiver, ReceiverUrl: val } })} />
|
||||
<DialogFooter>
|
||||
<div style={{ display: "flex", placeContent: "flex-end" }}>
|
||||
{this.state.isSaving && <Spinner size={SpinnerSize.small} styles={{ root: { marginRight: 5 } }} />}
|
||||
{selectedEventReceiver.ReceiverId == "" &&
|
||||
<PrimaryButton text={"Save"} onClick={() => this.addEventReceiver()} />
|
||||
}
|
||||
|
||||
{selectedEventReceiver.ReceiverId != "" &&
|
||||
<PrimaryButton text={"Delete"} onClick={() => this.deleteEventReceiver()} styles={{ root: { background: "red" } }} />
|
||||
}
|
||||
</div>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "Description Field"
|
||||
}
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
declare interface IRemoteEventReceiverManagerWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'RemoteEventReceiverManagerWebPartStrings' {
|
||||
const strings: IRemoteEventReceiverManagerWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
export interface IEventReceiver {
|
||||
ReceiverAssembly: string;
|
||||
ReceiverClass: string;
|
||||
ReceiverId: string;
|
||||
ReceiverName: string;
|
||||
SequenceNumber: number;
|
||||
Synchronization: number;
|
||||
EventType: number;
|
||||
ReceiverUrl: any;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export interface IList {
|
||||
Title: string;
|
||||
Id: string;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
import { sp } from "@pnp/sp/presets/all";
|
||||
import { IEventReceiver } from "../models/IEventReceiver";
|
||||
import { IList } from "../models/IList";
|
||||
import { SPHttpClient, SPHttpClientConfiguration, SPHttpClientResponse, ODataVersion, ISPHttpClientConfiguration } from '@microsoft/sp-http';
|
||||
|
||||
export interface ISharePointProvider {
|
||||
getLists(): Promise<IList[]>;
|
||||
getEventReceivers(listID: string): Promise<IEventReceiver[]>;
|
||||
addEventReceiver(eventReceiver: IEventReceiver, listId: string): Promise<void>;
|
||||
deleteEventReceiver(eventReceiver: IEventReceiver, listId: string): Promise<void>;
|
||||
}
|
||||
|
||||
export class SharePointProvider implements ISharePointProvider {
|
||||
private context: WebPartContext;
|
||||
|
||||
constructor(context: WebPartContext) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
public async getLists(): Promise<IList[]> {
|
||||
try {
|
||||
return await sp.web.lists.get();
|
||||
} catch {
|
||||
alert("Faild to get lists!");
|
||||
}
|
||||
}
|
||||
|
||||
public async getEventReceivers(listID: string): Promise<IEventReceiver[]> {
|
||||
try {
|
||||
let eventReceivers = await sp.web.lists.getById(listID).eventReceivers.get();
|
||||
//Remove all OData properties as these will cause issues when saving later!
|
||||
eventReceivers = eventReceivers.map(x => {
|
||||
delete x["odata.editLink"];
|
||||
delete x["odata.id"];
|
||||
delete x["odata.type"];
|
||||
return x;
|
||||
});
|
||||
return eventReceivers;
|
||||
} catch {
|
||||
alert("Failed to get event receivers!");
|
||||
}
|
||||
}
|
||||
|
||||
public async addEventReceiver(eventReceiver: IEventReceiver, listId: string): Promise<void> {
|
||||
delete eventReceiver.ReceiverId;
|
||||
|
||||
let url = `${this.context.pageContext.web.absoluteUrl}/_api/web/lists(guid'${listId}')/EventReceivers`;
|
||||
try {
|
||||
let result = await this.context.spHttpClient.post(url, SPHttpClient.configurations.v1, { body: JSON.stringify(eventReceiver) });
|
||||
} catch {
|
||||
alert("Failed to add event receiver");
|
||||
}
|
||||
|
||||
}
|
||||
public async deleteEventReceiver(eventReceiver: IEventReceiver, listId: string): Promise<void> {
|
||||
|
||||
let header: HeadersInit = new Headers();
|
||||
header.append("IF-MATCH", "*");
|
||||
header.append("X-HTTP-Method", "DELETE");
|
||||
|
||||
let url = `${this.context.pageContext.web.absoluteUrl}/_api/web/lists(guid'${listId}')/EventReceivers/GetById(guid'${eventReceiver.ReceiverId}')`;
|
||||
try {
|
||||
let result = await this.context.spHttpClient.post(url, SPHttpClient.configurations.v1, { body: JSON.stringify(eventReceiver), headers: header });
|
||||
} catch {
|
||||
alert("Failed to delte event receiver");
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 383 B |
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.3/includes/tsconfig-web.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "lib",
|
||||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"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